Data Access Services

Data Access Services

Microsoft Data Access Components (MDAC)

The Microsoft® Data Access Components documentation includes the following sections:

  • MDAC SDK Overview
  • ActiveX® Data Objects (ADO)
  • OLE DB
  • Open Database Connectivity (ODBC)
  • Technical Articles

For the latest news about MDAC technologies, see the Microsoft Universal Data Access Web site.

Extensible Markup Language (XML)

The XML documentation includes developer's guides for XML and XSLT, reference sections, tutorials, and samples.

For the latest news about the Microsoft XML Parser (MSXML), see the XML Developer Center on MSDN Online.


Microsoft Data Access Components (MDAC)

MDAC Start Page

MDAC
Version 2.6

Purpose

The Microsoft® Data Access Components (MDAC) SDK documents the key technologies that enable Universal Data Access, Microsoft's strategy for providing access to information across the enterprise.

Microsoft Data Access Components include ActiveX® Data Objects (ADO), OLE DB, and Open Database Connectivity (ODBC). Data-driven client/server applications deployed over the Web or a LAN can use these components to easily integrate information from a variety of sources, both relational (SQL) and nonrelational.

The MDAC SDK is the primary source of information and instruction for using data access technologies. Its tools, samples, and documentation are designed to help developers create solutions for their data access needs.

For the latest news about MDAC technologies, see the Microsoft Universal Data Access Web site.

If you have questions or need detailed information about properly redistributing MDAC, see "MDAC Distribution, Past and Present" for a description of the contents and distribution requirements of each important MDAC release version.

ActiveX Data Objects (ADO)

Microsoft ActiveX Data Objects (ADO) provides consistent, high-performance access to data and supports a variety of development needs, including the creation of front-end database clients and middle-tier business objects that use applications, tools, languages, or Internet browsers. The primary benefits of ADO are ease of use, high speed, low memory overhead, and a small disk footprint.

ADO provides an easy-to-use interface to OLE DB, which provides the underlying access to data. It uses a familiar metaphor—the COM Automation interface—available from all leading Rapid Application Development (RAD) tools, database tools, and languages.

OLE DB

Microsoft OLE DB is a set of interfaces that expose data from a variety of relational and nonrelational sources by using the 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 store, enabling the data store to share its data.

OLE DB comprises a programmatic model consisting of data providers, which contain and expose data; data consumers, which use data; and service components, which process and transport data (such as query processors and cursor engines). In addition, OLE DB includes a bridge to ODBC to enable continued support for the broad range of ODBC relational database drivers.

Open Database Connectivity (ODBC)

The Microsoft Open Database Connectivity (ODBC) interface makes it possible for applications to access data from a variety of DBMSs. ODBC permits maximum interoperability—an application can access data in diverse DBMSs through a single interface. Furthermore, that application will be independent of any DBMS from which it accesses data. Users of the application can add software components called drivers, which create an interface between an application and a specific DBMS.

Main Components of MDAC 2.6

Microsoft ActiveX Data Objects (ADO)

This section contains Programmer's References for ADO, ADOX, ADO MD, and Remote Data Service (RDS), as well as samples documentation.

Microsoft OLE DB

This section documents the OLE DB and OLE DB for OLAP interfaces, including a Programmer's Reference, documentation for various OLE DB providers, and samples.

Microsoft Open Database Connectivity (ODBC)

This section documents the ODBC interface, including a Programmer's Reference, documentation for various ODBC drivers, and samples.

Microsoft Data Access Technical Articles

Microsoft Data Access Technical Articles

This section addresses cross-technology and overview topics, including the latest information about MDAC SDK component technologies.

Component Checker

The Component Checker is designed to determine and diagnose installation issues with MDAC components. It is available from www.microsoft.com/data. The self-extracting .exe file contains all installation files for the tool to operate properly. Furthermore, it is customizable and performs the following tasks:

  • Identifies the current MDAC installation on a computer.
  • Creates a series of reports about the files identified for the current MDAC installation.
  • Removes existing MDAC installations after listing .dll conflicts and identifying programs that reference a given .dll.

Feedback

You can send feedback about the MDAC documentation or samples directly to the MDAC documentation team.


What's New in the MDAC SDK

The Microsoft Data Access Components are now integrated into Microsoft Windows® 2000 as system-level components. This means that beginning with Windows 2000, MDAC is part of the base operating system installation.

The following topics highlight important changes in the MDAC documentation since the last version (MDAC 2.5) release, provide general information about overall areas of interest, and refer you to documents and Web pages where you can get more detailed information about Microsoft's data access strategies:

  • Changes and Enhancements in MDAC 2.6 is an overview with links to more specific information about what has changed since the MDAC 2.5 SDK.
  • Do You Have Feedback About MDAC Documentation? offers tips on how to send feedback directly to the MDAC documentation team.
  • Microsoft Data Access Web Site provides links to technologies that work with MDAC.


Changes and Enhancements in MDAC 2.6

This release of MDAC 2.6 introduces several changes and enhancements to the SDK. See individual technology sections, listed at the end of this topic, for more detailed information about what's new.

Enhanced ADO Programmer's Guide Documentation

Conceptual and tutorial topics for ADO have been written or reorganized into a comprehensive ADO developer's guide.

Enhanced ADO Code Example Topics

The examples have been expanded to contain additional examples written in VBScript and JScript.

Additional ADO Samples

Several new ADO samples have been added with this release.

Changes to OLE DB Samples

The Rowset Viewer sample has been updated for the row object and direct binding interfaces. It also now handles multidimensional data functions for developers using OLE DB for OLAP.

The OLE DB Query Demo sample has been deleted from this release of MDAC.

Changes to OLE DB Test Tools

The Trace Provider tool has been removed from MDAC pending further development. It was offered as a beta preview for MDAC 2.5.

ODBC Desktop Database Drivers

The ODBC Desktop Database Drivers have been removed from the MDAC SDK and the MDAC Redist.

Details About Changes by Technology

For more detailed information about what is new and changed in specific MDAC technologies, see the following topics:

  • What's New in ADO
  • What's New in OLE DB
  • What's New in ODBC


MDAC Release Manifest

All versions of the Microsoft Data Access Components (MDAC) and the MDAC SDK now include a release manifest, which contains important details specific to each version.

The MDAC release manifests describe each version in detail, including its features, ship vehicles, and file lists. In addition, release manifests contain details about compatibility issues, known problems, and workarounds for each version of MDAC.

To view current and former release manifests, as well as find information about installing, uninstalling, upgrading, and downgrading MDAC, see the Downloads page of the Universal Data Access Web site.


Navigating the MDAC Documentation

This chart is designed to guide you through the Microsoft® Data Access Components (MDAC) documentation. Look on the chart to find MDAC information about what you want to learn, the problem you want to solve, or an activity you want to do. For a better understanding of MDAC technologies, we recommend that everyone read the "What Are the MDAC Technologies?" technical article at the Microsoft Universal Data Access Web site.

Tasks | Go To | Language
Learn about data access technologies |  |
Novice | Microsoft Data Access Components overview "MDAC Technology Features" technical article "Choosing MDAC Components" technical article ActiveX Data Objects (ADO) overview OLE DB overview Open Database Connectivity (ODBC) overview |
Experienced with RDO/DAO or PowerBuilder/Delphi Tools | "Migrating from DAO to ADO" technical article |
Use Visual Basic to create an application that uses data accessed from different data stores | ADO overview | Visual Basic
Use a browser to create an application that uses data accessed from different data stores | ADO overview | Active Server Pages
Expose multidimensional data using ADO | ADO MD | Visual Basic, Visual C++, Visual J++
Expose data and services through OLE DB |  |
OLE DB novice | OLE DB overview OLE DB Samples OLE DB Programmer's Reference OLE DB Simple Provider Toolkit | Visual Basic, Visual C++, Visual J++
OLE DB expert | "OLE DB Minimum Levels of Consumer and Provider Functionality" technical article OLE DB Providers OLE DB Programmer's Reference OLE DB Testing | Visual C++
Use multidimensional data | OLE DB for Online Analytical Processing (OLAP) |
Expose SQL data through ODBC | ODBC overview "OLE DB for the ODBC Programmer" technical article |


Locating MDAC Components

Installing the Platform SDK from MSDN Online

MDAC 2.6 components are now part of the Platform SDK. For information about installing the Platform SDK, see the Products page of the MSDN Online Web site at http://msdn.microsoft.com. If you customize your installation of the Platform SDK, be aware that links to some components might not function.

Installing MDAC 2.6

Beginning with MDAC 2.5, core components were integrated with the Windows® 2000 operating system. Overwriting some of these system files is prohibited. However, installing this Beta Release of MDAC 2.6 on Windows® 2000 operating systems is specific to this release. Consult with the Beta Coordinator for instructions.

The MDAC redistribution process overwrites any existing MDAC files on computers running Microsoft Windows NT® 4.0 or Windows 95/98. For more information, see "MDAC Distribution, Past and Present."

Directory Structure

The Platform SDK setup process installs MDAC files on your computer in the following subdirectories of the Platform SDK installation directory. Depending on the components you select during installation, your directory structure might vary.

Directory | Description
Bin | Binary files for all MDAC SDK components.
Help | Documentation and Help files.
Include | Header files for MDAC SDK components.
Lib | Library files for MDAC SDK components.
Misc | Miscellaneous files including conformance tests.
Samples | Subdirectories of DataAccess contain code for all MDAC samples.


Using MDAC Resources

This section discusses a variety of resources for finding information about the MDAC SDK:

  • MDAC Documentation
  • User Interface Help
  • MDAC Samples
  • Web Sites
  • Microsoft Knowledge Base Articles
  • Printed Resources


MDAC Documentation

The MDAC documentation includes ActiveX Data Objects (ADO), OLE DB, ODBC, and MDAC technical articles. Each section is included within the Platform SDK and also is available through MSDN Online.

For late-breaking information about the MDAC components and documentation, see the MDAC SDK Readme. To view this file, point to Start, Programs, Platform SDK, Data Access, and then click MDAC SDK Readme, or search for MDACSDKReadme.htm on your computer.

Documentation for MDAC is not available in print.

HTML Help

MDAC documentation is presented in HTML Help. The following illustration describes some of the features of HTML Help and provides suggestions for its use. For more information about using the Search feature, see the advanced full-text search document.


User Interface Help

Detailed help for the following components is available from their individual Help menus or dialog boxes. The following sections of the MDAC documentation include general information about each component:

  • ODBC Data Source Administrator
  • ODBC Desktop Database Drivers
  • ODBC Driver for Oracle
  • Visual FoxPro ODBC Driver
  • ODBC Test
  • Data Link API
  • Local Test Manager


MDAC Samples

As MDAC technologies change to reflect business and developer demands, the number and type of samples also change. The following sections of the MDAC documentation discuss in detail some sample applications using MDAC technologies:

  • ADO Samples
  • OLE DB Samples
  • ODBC Samples


Web Sites

Universal Data Access

The Universal Data Access Web site at http://www.microsoft.com/data contains current information specific to the MDAC technologies: ADO, OLE DB, and ODBC. On this site, you will find discussions of current issues, release manifests, downloads, and links to related technologies and sites.

MSDN Online

The MSDN Online Web site at http://msdn.microsoft.com offers a wide range of developer resources for finding help on specific topics. This site also contains the Platform SDK.

Using the MSDN Online Search feature, you can access the Knowledge Base, Web Workshop, product information, documentation, and other resources.


Microsoft Knowledge Base Articles

Microsoft Knowledge Base articles, available at http://msdn.microsoft.com, contain specific solutions, workarounds, and pertinent information about the MDAC SDK. The following table includes helpful keywords to use when searching the Knowledge Base for assistance with MDAC technologies.

Technology | Keywords
MDAC MDAC SDK | kbgrpMDAC kbMDAC kbMDAC200 kbMDAC210 kbMDAC250 kbSDKDataAc kbDatabase
ADO RDS | kbADO kbADO150 kbADO200 kbADO210 kbADO250 kbRDS
OLE DB | kbOLEDB kbOLEDB200 kbOLEDB210 kbOLEDB250
OLE DB providers | kbProvider
OSP Toolkit | kbOSP
ODBC | kbODBC odbcAPI kbODBC200 kbODBC250 kbODBC351 kb32bitonly odbcGPF
ODBC Desktop Database Drivers | kbDriver kbJet odbcExcel
ODBC Driver for Oracle | kbOracle kb3rdParty odbcOracle kbOracle200
Visual FoxPro ODBC Driver | kbgrpFox
SQL ODBC Driver and OLE DB Provider for SQL Server | kbSQL kbSQLServ kbSQLServ650 kbSQLServ700


Printed Resources

Microsoft Press publishes a number of guides and reference books that are useful when developing applications using MDAC technologies. Other printed resources are available from technical book publishers.

For a complete list of publications, see the Microsoft Press Web site at http://mspress.microsoft.com or see the Resources section of the MSDN Online Web site at http://msdn.microsoft.com.


MDAC Distribution, Past and Present

Summary: The Microsoft® Data Access Components (MDAC) library and the MDAC SDK have grown and changed with each release. This article details the contents for each important release version and provides detailed information about redistributing MDAC.

Introduction

This article provides details about each release of MDAC. Some releases were incorporated into releases of other programs or updates, and some were made available individually. Some releases were complete, and some were not. Understanding the details of each release may help diagnose and correct configuration problems with data access technologies.

If you must redistribute the components in MDAC, be sure to use the redistributable program. Microsoft cannot provide effective support if you distribute them any other way. See the section in this article titled Redistributing MDAC for more information.

Note The Microsoft Universal Data Access Web site has release manifests that detail the contents and installation instructions for the current versions of MDAC.

For specific information about MDAC history and distribution, see the following topics in this article:

  • MDAC Background
  • How MDAC Is Distributed
  • MDAC Component Breakdown
  • MDAC Version History
  • MDAC Release Timeline
  • Redistributing MDAC


MDAC Background

Microsoft's data access technology before 1996 consisted of several technologies, including the Open Database Connectivity (ODBC) library and software development kit (SDK), and the Data Access Objects (DAO) library. In 1996, two major new data access technologies, OLE DB and Microsoft® ActiveX® Data Objects (ADO) were released. The Active Data Connector, later to become Remote Data Services for ADO, was also released. In 1998, OLE DB for Online Analytical Processing (OLAP) was released.

Each technology is implemented using several dynamic-link Libraries (DLLs). The SDKs through which each technology was released included information on redistributing the DLLs. However, many application vendors and information systems groups installed the redistributable versions of these DLLs in a variety of locations on a workstation, rather than in one consistent location for all workstations. Their intention for doing this was to be sure that the correct version of a DLL was indeed installed on the workstation with the application, but unfortunately it caused a bigger problem instead.

This situation created a problem because of the way Microsoft Windows® (except Microsoft Windows 2000 and Microsoft Windows 98 Second Edition—see below) manages DLLs. Once Windows loads a particular DLL into memory, it is used by all applications that need a DLL with the same name, regardless of the DLL's version. Thus, the wrong version of a DLL was sometimes used even if the right version of the DLL was installed where the setup program put it, causing applications to crash or run incorrectly. This problem was difficult to correct because the exact cause of the problem was difficult to detect.

To illustrate this problem, assume that two versions of a DLL are installed on a workstation in two different folders, as follows:

  • Msador15.dll version 2.10.3513.2 is installed in C:\Program Files\Common Files\System\Ado\.
  • Msador15.dll version 2.0.3002.11 is installed in D:\DataClient\.

Now suppose that Program1.exe in D:\DataClient began running and consequently loaded Msador15.dll version 2.0.3002.11 into memory. The module name for this DLL is ADOR15. Later, Program2.exe, in a different folder, begins to run and attempts to load the latest version of Msador15.dll. Windows has the module ADOR15 already in memory. It can't unload the module, and it can't load a different version of Msador.dll in memory. Therefore, it returns a handle to the 2.0.3002.11 version to Program2.exe, which crashes when it calls Msador15.dll because it is expecting the newer version 2.10.3513.2.

To conquer part of this problem, Microsoft collected data access technologies and created a single installation program. The intention was to prevent multiple versions of the same DLL from being installed at different locations on the workstation, as well as making sure that all versions that were installed had been designed and tested to work with each other. This installation program is known as the Microsoft Data Access Components (MDAC) setup program.

Unfortunately, this approach was not consistently applied by all of the Microsoft products, which aggravated the problem. Nor did this approach solve problems with earlier installations. Since MDAC was introduced, software installation programs have not uniformly installed the data access technologies in MDAC. Only since mid-1999 has MDAC been uniformly installed, and with the release of Windows 2000, MDAC 2.5 became part of the operating system, further reducing the likelihood that this problem will occur.

By allowing multiple versions of the same DLL to be loaded at the same time, Windows 2000 and Windows 98 Second Edition eliminate the DLL-version problem entirely. This feature is called side-by-side component sharing. Read the MSDN technical article "Implementing Side-by-Side Component Sharing in Applications" for details on this feature.

You may be interested in tools that assist in diagnosing configuration problems with MDAC. One tool, formerly called ClsidView, has now become the tool called Component Checker. It is available for free download from the Microsoft Universal Data Access Download Page. This tool compares the configuration of a workstation to a database of known good configurations and reports any errors or discrepancies. Among other useful features, Component Checker can completely remove an installation of MDAC.


How MDAC Is Distributed

As of version 2.5, MDAC is mainly distributed in two significant ways. The first is as a component of the Windows 2000 operating system. The second is a stand-alone distribution program downloadable from the Web, intended for operating systems that predate Windows 2000, such as Windows NT 4.0, Windows 95, or Windows 98.

The MDAC software development kit (SDK) is distributed as part of the Microsoft Windows Platform SDK. The SDK includes a full set of documentation, header files, libraries, samples, configuration files, and other support files. Early versions of MDAC did not have redistributable programs. Application developers had to incorporate the components into their own installation programs, using instructions in the SDK. Later versions of the MDAC SDK did include a redistributable program. However, beginning with MDAC 2.5, the MDAC SDK is now a part of the Platform SDK and does not include a redistributable program. MDAC should only be redistributed using the free downloadable redistributable program found at the Microsoft Universal Data Access Download Page.


MDAC Component Breakdown

The tables in the following sections list all of the major components of MDAC. For each major component, all of the executable modules that are or ever were part of the component are listed. Demos and SDK tools are not included in this list; they are generally not redistributable without the MDAC SDK. The description of each module is also listed.

  • ODBC
  • OLE DB
  • ActiveX Data Objects (ADO)
  • Other Components

Note Do not use the redistributable files (listed in the Shipped in column of the component tables) to create a substitute for the redistributable setup program. Microsoft does not recommend or support redistribution of the MDAC files without using the redistributable setup program. Additionally, redistribution of some of the files is restricted in accordance with the terms of the end-user license agreement (EULA) provided with each version of MDAC. See the section in this article titled Redistributing MDAC for more information.


ODBC

In the File version resource description column below, two values have special meanings:

  • (empty) means the file would normally have a description string contained in the file version resource (Portable Executable format), but no description was found in this file.
  • (N/A) means the file does not normally contain a file version resource. This usually applies to source code files, help files, text files, and so on.

ODBC Driver Manager

File name | Description | File version resource description | Shipped in
ds16gt.dll | 16-bit driver setup generic thunking | Microsoft ODBC Driver Setup Generic Thunk | MDAC 1.x/2.0/2.1/2.5
ds32gt.dll | 32-bit driver setup generic thunking | Microsoft ODBC Driver Setup Generic Thunk | MDAC 1.x/2.0/2.1/2.5
mtxdm.dll | ODBC Connection Pooling manager | (empty) | MDAC 2.0/2.1/2.5
odbc16gt.dll | 16-bit ODBC generic thunking | Microsoft ODBC Driver Generic Thunk | MDAC 1.x/2.0/2.1/2.5
odbc32.dll | 32-bit Driver Manager | Microsoft ODBC Driver Manager | MDAC 1.x/2.0/2.1/2.5
odbc32gt.dll | 32-bit ODBC generic thunking | Microsoft ODBC Driver Generic Thunk | MDAC 1.x/2.0/2.1/2.5
odbcad32.exe | 32-bit Administrator program | Microsoft ODBC Administrator | MDAC 2.0/2.1/2.5
odbccp32.cpl | 32-bit Administrator Control Panel program | Microsoft ODBC Control Panel | MDAC 1.x/2.0/2.1/2.5
odbccp32.dll | 32-bit installer | Microsoft ODBC Installer | MDAC 1.x/2.0/2.1/2.5
odbccr32.dll | ANSI Cursor Library | Microsoft ODBC Cursor Library | MDAC 1.x/2.0/2.1/2.5
odbccu32.dll | Unicode Cursor Library | Microsoft ODBC Cursor Library | MDAC 2.0/2.1/2.5
odbcinst.chm | Installer Help file | (N/A) | MDAC 2.5
odbcinst.cnt | Installer Help Table of Contents file | (N/A) | MDAC 1.x/2.0/2.1
odbcinst.hlp | Installer Help file | (N/A) | MDAC 1.x/2.0/2.1
odbcint.dll | 16-bit installer | Microsoft ODBC Resource DLL | MDAC 1.x/2.0/2.1/2.5
odbctrac.dll | ODBC Tracing library | Microsoft ODBC Driver Manager | MDAC 1.x/2.0/2.1/2.5
odbcinst.h | SDK header file | (N/A) | MDAC 2.0
sql.h | SDK header file | (N/A) | MDAC 2.0
sqlext.h | SDK header file | (N/A) | MDAC 2.0
sqltypes.h | SDK header file | (N/A) | MDAC 2.0
sqlucode.h | SDK header file | (N/A) | MDAC 2.0
odbc.cnt | Help Table of Contents file | (N/A) | MDAC 2.0
odbc.hlp | Help file | (N/A) | MDAC 2.0
sdkguide.cnt | SDK Help Table of Contents file | (N/A) | MDAC 2.0
sdkguide.hlp | SDK Help file | (N/A) | MDAC 2.0

Code Page

File name | Description | File version resource description | Shipped in
12520437.cpx | Translation Code Page | (N/A) | MDAC 2.0/2.1/2.5
12520850.cpx | Translation Code Page | (N/A) | MDAC 2.0/2.1/2.5
mscpxl32.dll | ANSI Code Page Translator | Microsoft ODBC Code Page Translator | MDAC 2.0/2.1/2.5

Microsoft SQL Server ODBC Driver

File name | Description | File version resource description | Shipped in
cliconf.hlp | SQL Server client configuration Help file | (N/A) | MDAC 2.1/2.5
cliconfg.dll | SQL Server client configuration library | SQL Client Configuration Utility | MDAC 2.1/2.5
cliconfg.exe | SQL Server client configuration executable | SQL Client Configuration Utility | MDAC 2.1/2.5
ntwdblib.dll | DB-Lib library | SQL Server Client Library | Not redistributable
odbcbcp.dll | SQL Server BCP library | Microsoft BCP for ODBC | MDAC 2.0/2.1/2.5
sqlsodbc.hlp | SQL Server ODBC Help | (N/A) | MDAC 2.0/2.1/2.5
sqlsrdme.txt | Release notes | (N/A) | MDAC 2.0/2.1
sqlsrv32.dll | SQL Server ODBC Driver | Microsoft SQL Server ODBC Driver | MDAC 2.0/2.1/2.5
windbver.exe | SQL Server Client Configuration Utility | SQL Client Configuration Utility | MDAC 2.0/2.1

ODBC Driver for Desktop Databases

File name | Description | File version resource description | Shipped in
odbcjet.chm | Jet ODBC Help | (N/A) | MDAC 2.5
odbcjet.cnt | Jet ODBC Help contents | (N/A) | MDAC 1.x/2.0/2.1
odbcjet.hlp | Jet ODBC Help | (N/A) | MDAC 1.x/2.0/2.1
odbcji32.dll | ODBC Driver for Desktop Databases resource library | Microsoft ODBC Desktop Driver Pack | MDAC 1.x/2.0/2.1/2.5
odbcjt32.dll | ODBC Driver for Desktop Databases | Microsoft ODBC Desktop Driver Pack | MDAC 2.0/2.1/2.5
odbcjtnw.cnt | Help file table of contents | (N/A) | MDAC 1.x
odbcjtnw.hlp | DBC Desktop Database Drivers Help file | (N/A) | MDAC 1.x
odbctl32.dll | Utility routines | ODBC Helper Function DLL | MDAC 1.x/2.0/2.1
oddbse32.dll | Installable dBase IISAM | ODBC (3.0) Driver for DBase | MDAC 1.x/2.0/2.1/2.5
odexl32.dll | Installable Excel IISAM | ODBC (3.0) Driver for Excel | MDAC 1.x/2.0/2.1/2.5
odfox32.dll | Installable FoxPro IISAM | ODBC (3.0) Driver for FoxPro | MDAC 1.x/2.0/2.1/2.5
odpdx32.dll | Installable Paradox IISAM | ODBC (3.0) Driver for Paradox | MDAC 1.x/2.0/2.1/2.5
odtext32.dll | Installable Text IISAM | ODBC (3.0) Driver for Text files | MDAC 1.x/2.0/2.1/2.5

Oracle ODBC Driver

Note These files are restricted as specified in the End-User License Agreement.

File name | Description | File version resource description | Shipped in
msorcl32.dll | Oracle ODBC Driver | Microsoft ODBC for Oracle | MDAC 2.0/2.1/2.5 (limited redistribution license)
msorcl32.chm | Oracle ODBC Help | (N/A) | MDAC 2.5
msorcl32.hlp | Oracle ODBC Help | (N/A) | MDAC 2.0/2.1
msorcl32.cnt | Oracle ODBC Help Contents | (N/A) | MDAC 2.0/2.1
msoracle32readme.txt | Release notes | (N/A) | MDAC 2.0/2.1
msorcrdm.txt | Oracle ODBC Driver release notes | (N/A) | MDAC 2.1

Visual FoxPro ODBC Driver

File name | Description | File version resource description | Shipped in
vfpodbc.dll | Visual FoxPro ODBC Driver | Microsoft Visual Foxpro ODBC Driver | MDAC 2.0/2.1/2.5
vfpodbc.txt | VFPODBC release notes | (N/A) | MDAC 2.0/2.1
drvvfp.chm | Visual FoxPro ODBC Driver Help | (N/A) | MDAC 2.5
drvvfp.hlp | Visual FoxPro ODBC Driver Help | (N/A) | MDAC 2.0/2.1
drvvfp.cnt | Visual FoxPro ODBC Driver Help contents | (N/A) | MDAC 2.0/2.1


OLE DB

In the File version resource description column below, two values have special meanings:

  • (empty) means the file would normally have a description string contained in the file version resource (Portable Executable format), but no description was found in this file.
  • (N/A) means the file does not normally contain a file version resource. This usually applies to source code files, help files, text files, and so on.

OLE DB Core Components

File name | Description | File version resource description | Shipped in
msdadc.dll | OLE DB data conversion library | Microsoft OLE DB Data Conversion Service | MDAC 1.x/2.0/2.1/2.5
msdadc.h | OLE DB data conversion header file | (N/A) | MDAC 1.x
msdaenum.dll | OLE DB services enumerator | Microsoft OLE DB Root Enumerator | MDAC 1.x/2.0/2.1/2.5
msdaer.dll | OLE DB error object | Microsoft OLE DB Error Collection Service | MDAC 1.x/2.0/2.1/2.5
msdaerr.dll | OLE DB error object resource library | Microsoft OLE DB Error Collection Localized Strings | MDAC 1.x/2.0/2.1
msdaguid.h | Header file for additional SDK components | (N/A) | MDAC 1.x
msdaps.dll | OLE DB interface remote | Microsoft OLE DB Proxies/Stubs | MDAC 2.0/2.1/2.5
msdarem.dll | RDS remoting | Microsoft OLE DB Remoting Provider | MDAC 2.1/2.5
msdaremr.dll | RDS remoting resource library | MS Remote Resources | MDAC 2.1/2.5
msdasql.h | ODBC Provider header file | (N/A) | MDAC 1.x
msdatl.dll | Memory/array manager | Microsoft OLE DB Implementation Support Library | MDAC 1.x
msdatl2.dll | OLE DB tools library | Microsoft OLE DB Implementation Support Library | MDAC 2.0/2.1/2.5
msdatt.dll | OLE DB Temporary Table Service Provider | Microsoft OLE DB Temporary Table Service Provider | MDAC 1.x/2.0/2.1/2.5
msxactps.dll | OLE DB transactions proxies/stubs | Microsoft Transaction Proxies/Stubs | MDAC 2.1/2.5
oledb.h | OLE DB header file | (N/A) | MDAC 1.x
oledb.lib | OLE DB library | (N/A) | MDAC 1.x
oledb15spec.cnt | Help contents file for the OLE DB Programmer's Reference | (N/A) | MDAC 1.x
oledb15spec.hh | Context IDs for the OLE DB Programmer's Reference Help file | (N/A) | MDAC 1.x
oledb15spec.hlp | OLE DB Programmer's Reference Help file | (N/A) | MDAC 1.x
oledb32.dll | OLE DB core services library | OLE DB Core DLL | MDAC 2.1/2.5
oledb32r.dll | OLE DB resource library | OLE DB Core Localized Strings DLL | MDAC 2.1/2.5
oledb32x.dll | OLE DB extensions library | Microsoft OLE DB Extensions DLL | MDAC 2.1/2.5
oledbd.lib | OLE DB debug library | (N/A) | MDAC 1.x
oledberr.h | OLE DB error object header file | (N/A) | MDAC 1.x
oledbjvs.inc | OLE DB error constant include file for JavaScript | (N/A) | MDAC 1.x/2.5
oledbvbs.inc | OLE DB error constant include file for VBScript | (N/A) | MDAC 1.x/2.5
transact.h | OLE DB transactions header file | (N/A) | MDAC 1.x

Service Components

File name | Description | File version resource description | Shipped in
msdasc.dll | OLE DB service components library | Microsoft OLE DB Service Components | MDAC 2.0/2.1/2.5
msdasc.chm | OLE DB service components Help | (N/A) | MDAC 2.5
msdasc.hlp | OLE DB service components Help | (N/A) | MDAC 2.0/2.1
msdasc.cnt | OLE DB service components Help contents | (N/A) | MDAC 2.0/2.1
msdasc.txt | Release notes for OLE DB service components | (N/A) | MDAC 2.0/2.1

OLE DB for Online Analytical Processing (OLAP)

File name | Description | File version resource description | Shipped in
dtstvwr.exe | Microsoft OLE DB for Olap Dataset Viewer | Microsoft OLE DB for Olap Dataset Viewer | Not redistributable
msmdcube.dll | PivotTable Services DLL | PivotTable Services DLL | Not redistributable
msmdgdrv.dll | Microsoft Decision Support Services driver | Microsoft Decision Support Services driver | Not redistributable
msmdint.dll | Microsoft Decision Support Services driver interfaces | Microsoft Decision Support Services driver interfaces | Not redistributable
msolap.dll | Microsoft OLE DB Provider for DSS | Microsoft OLE DB Provider for DSS | Not redistributable
msolapfm.dll | Microsoft OLAP format object | Microsoft OLAP format object | Not redistributable
msolapr.dll | Microsoft OLE DB Provider for DSS | Microsoft OLE DB Provider for DSS | Not redistributable
msolapsl.dll | Slicer user interface | Slicer user interface | Not redistributable
msolapui.dll | Microsoft OLE DB for Decision Support Services Provider connection dialog | Microsoft OLE DB for Decision Support Services Provider connection dialog | Not redistributable
olapcnsl.exe | OLAP Console Demo | (N/A) | Not redistributable

OLE DB Provider for ODBC

File name | Description | File version resource description | Shipped in
msdasql.cnt | Help contents file for the ODBC Provider Help file | (N/A) | MDAC 1.x
msdasql.dll | OLE DB Provider for ODBC | Microsoft OLE DB Provider for ODBC Drivers | MDAC 1.x/2.0/2.1/2.5
msdasql.hlp | ODBC Provider Help file | (N/A) | MDAC 1.x
msdasqlr.dll | OLE DB Provider for ODBC resource library | Microsoft OLE DB Provider for ODBC Drivers (Localized Strings) | MDAC 1.x/2.0/2.1/2.5
msdasqlreadme.txt | ODBC Provider release notes | (N/A) | MDAC 2.0/2.1
msdsqlrm.txt | OLE DB Provider for ODBC release notes | (N/A) | MDAC 2.1

OLE DB Provider for SQL Server

File name | Description | File version resource description | Shipped in
sqloledb.dll | SQL Server OLE DB Provider | Microsoft OLE DB Provider for SQL Server | MDAC 2.0/2.1/2.5
instcat.sql | Updated system stored procedure catalog | (N/A) | MDAC 2.0/2.1/2.5
sqlsoldb.hlp | Help file | (N/A) | MDAC 2.0/2.1
sqloledb.txt | Release notes | (N/A) | MDAC 2.1

OLE DB Provider for Microsoft Jet

Note These files are restricted as specified in the End-User License Agreement.

File name | Description | File version resource description | Shipped in
msjetoledb40.dll | OLE DB Provider for Jet | Jet Engine Library for Jolt | MDAC 2.1/2.5 (limited redistribution license)
msjt4jlt.dll | Jet for OLE DB | OLE DB Provider for Jet | MDAC 2.0 (limited redistribution license)
msjtor35.dll | Jet OLE DB Provider | Jet Engine Library for Jolt | MDAC 2.0 (limited redistribution license)
joltreadme.txt | Jet OLE DB Provider release notes | (N/A) | MDAC 2.0

OLE DB Provider for Oracle

Note These files are restricted as specified in the End-User License Agreement.

File name | Description | File version resource description | Shipped in
msdaora.dll | Microsoft OLE DB Provider for Oracle | Microsoft OLE DB Provider for Oracle | MDAC 2.0/2.1/2.5 (limited redistribution license)
msorcloledbreadme.txt | Release notes | (N/A) | MDAC 2.0/2.1
msorcolr.txt | Oracle OLE DB Provider release notes | (N/A) | MDAC 2.1

OLE DB Simple Provider (OSP) Toolkit

File name | Description | File version resource description | Shipped in
msdaosp.dll | OLE DB Simple Provider | Microsoft OLE DB Simple Provider | MDAC 2.0/2.1/2.5
simpdata.tlb | OLE DB Simple Provider type library | Microsoft OLE DB Simple Provider 1.5 Library | MDAC 2.1


ActiveX Data Objects (ADO)

In the File version resource description column below, two values have special meanings:

  • (empty) means the file would normally have a description string contained in the file version resource (Portable Executable format), but no description was found in this file.
  • (N/A) means the file does not normally contain a file version resource. This usually applies to source code files, help files, text files, and so on.

ADO Core

File name | Description | File version resource description | Shipped in
adoapt15.reg | Apartment-threading registry entries | (N/A) | MDAC 2.0/2.1
adofre15.reg | Free-threading registry entries | (N/A) | MDAC 2.0/2.1
adoid.h | Internal header file | (N/A) | MDAC 1.x
adoint.h | Internal header file for calling ADO from C++ | (N/A) | MDAC 1.x
adojavas.inc | Include file for JavaScript | (N/A) | MDAC 1.x/2.0/2.1/2.5
adoreadme.txt | Release notes | (N/A) | MDAC 2.0/2.1
adovbs.inc | Include file for VBScript | (N/A) | MDAC 1.x/2.0/2.1/2.5
makapt15.bat | Batch file for apartment threading | (N/A) | MDAC 2.0/2.1
makfre15.bat | Batch file for free threading | (N/A) | MDAC 2.0/2.1
msader10.dll | ADO error string library | Microsoft ADO Error Lookup Service | MDAC 1.x
msader15.dll | ADO error string library | Microsoft ADO Error Resource | MDAC 1.x/2.0/2.1/2.5
msado10.cnt | Help contents file for ADO | (N/A) | MDAC 1.x
msado10.dll | ADO library | Microsoft ActiveX Data Objects Library | MDAC 1.x
msado10.hlp | Help file for ADO | (N/A) | MDAC 1.x
msado15.dll | ADO library | Microsoft ActiveX Data Objects Library | MDAC 1.x/2.0/2.1/2.5
msador10.dll | ADO Recordset-only implementation | Microsoft ActiveX Data Objects Recordset Library | MDAC 1.x
msador15.dll | ADO Recordset object | Microsoft ActiveX Data Objects Library | MDAC 2.0/2.1/2.5
msadrh15.dll | Rowset helper DLL | Microsoft ADO Rowset Helper Library | MDAC 2.0/2.1/2.5

ADO Extensions for DDL and Security (ADOX)

File name | Description | File version resource description | Shipped in
msadox.dll | Microsoft ADO Extensions Library | Microsoft ADO Extensions Library | MDAC 2.1/2.5

Remote Data Service (RDS)

Note RDS was previously known as ADO Recordset Implementation (ADO/R) and as Advanced Data Connector (ADC).

File name | Description | File version resource description | Shipped in
adcjavas.inc | JavaScript include file for RDS/ADC | (N/A) | MDAC 2.0/2.1/2.5
adcvbs.inc | VBScript include file for RDS/ADC | (N/A) | MDAC 2.0/2.1/2.5
handler.reg | Register and include file for the DataFactory handler core | (N/A) | MDAC 2.0/2.1/2.5
handsafe.reg | Handler settings for safe mode/restricted access | (N/A) | MDAC 2.1/2.5
handunsf.reg | Handler settings for unrestricted access | (N/A) | MDAC 2.1/2.5
mdac11.cab | RDS 1.1 client cab | (N/A) | MDAC 2.0/2.1
msadc10.exe | Server setup program | Microsoft Active Data Connector OLE DB Provider | MDAC 1.x
msadcb10.dll | Microsoft Advanced Data Connector Bindmanager | Microsoft Advanced Data Connector Bindmanager | MDAC 1.x
msadcb11.dll | Microsoft Advanced Data Connector Bindmanager | Microsoft Advanced Data Connector Bindmanager | MDAC 1.x
msadcc10.dll | Microsoft Advanced Data Connector Wrapping Control | Microsoft Advanced Data Connector Wrapping Control | MDAC 1.x
msadcc11.dll | Microsoft Advanced Data Connector Wrapping Control | Microsoft Advanced Data Connector Wrapping Control | MDAC 1.x
msadce.dll | RDS client-side cursor engine | Microsoft Remote Data Services 2.0 | MDAC 2.0/2.1/2.5
msadcer.dll | RDS client-side cursor engine resource library | Microsoft Remote Data Services 2.0 Resource DLL | MDAC 2.0/2.1/2.5
msadcf.dll | RDS DataFactory object | Microsoft Remote Data Services Data Factory | MDAC 2.0/2.1/2.5
msadcf10.dll | Microsoft Advanced Data Factory | Microsoft Advanced Data Factory | MDAC 1.x
msadcfr.dll | RDS DataFactory object resource library | Microsoft Remote Data Services Data Factory Resources | MDAC 2.0/2.1/2.5
msadco.dll | RDS data binding HTML control | Microsoft Remote Data Services Data Control | MDAC 2.0/2.1/2.5
msadco10.dll | Microsoft Active Data Connector OLE DB Provider | Microsoft Active Data Connector OLE DB Provider | MDAC 1.x
msadcor.dll | RDS data binding HTML control resource library | Microsoft Remote Data Services Data Control Resources | MDAC 2.0/2.1/2.5
msadcs.dll | RDS ISAPI library | Microsoft Remote Data Services Server ISAPI Component | MDAC 2.0/2.1/2.5
msadcs10.dll | Microsoft Advanced Data Connector Serverside | Microsoft Advanced Data Connector Serverside | MDAC 1.x
msadds.dll | Shape Provider (hierarchical rowsets) | Microsoft Data Shape OLE DB Provider 2.0 | MDAC 2.0/2.1/2.5
msaddsr.dll | Shape Provider resource library | Microsoft Data Shape OLE DB Provider 2.0 Resource DLL | MDAC 2.0/2.1/2.5
msadrs10.dll | Microsoft ActiveX Data Recordset Library | Microsoft ActiveX Data Recordset Library | MDAC 1.x
msdaprsr.dll | RDS proxies/stubs resource library | Microsoft Persistence OLE DB Provider 2.0 Resource DLL | MDAC 2.1/2.5
msdaprst.dll | RDS proxies/stubs library | Microsoft Persistence OLE DB Provider 2.0 | MDAC 2.0/2.1/2.5
msdatsrc.tlb | RDS data binding type library | Microsoft Data Source Interfaces | MDAC 2.1/2.5
msdfmap.dll | DataFactory handler core | Microsoft Data Factory Handler | MDAC 2.0/2.1/2.5
msdfmap.ini | Initializes the DataFactory handler core | (N/A) | MDAC 2.0/2.1/2.5
msjtes40.dll | Jet expression service | Microsoft Jet Expression Service | MDAC 2.1/2.5
readme.txt | Product description and contents for ADC | (N/A) | MDAC 1.x

ADO Multidimensional (ADO MD)

File name | Description | File version resource description | Shipped in
msadomd.dll | Microsoft ActiveX Data Objects (Multidimensional) Library | Microsoft ActiveX Data Objects (Multidimensional) Library | MDAC 2.5


Other Components

In the File version resource description column below, two values have special meanings:

  • (empty) means the file would normally have a description string contained in the file version resource (Portable Executable format), but no description was found in this file.
  • (N/A) means the file does not normally contain a file version resource. This usually applies to source code files, help files, text files, and so on.

Jet and Replication Objects (JRO)

File name | Description | File version resource description | Shipped in
msjro.dll | Microsoft Jet and Replication Objects | Microsoft Jet and Replication Objects | MDAC 2.1/2.5

Remote Data Objects (RDO)

Note The Remote Data Objects library supports the ODBCDirect services for Microsoft Jet.

File name | Description | File version resource description | Shipped in
msrdc20.ocx | Remote Data Control | MSRDC20 | MDAC 2.1
msrdo20.dll | Remote Data Objects library | MSRDO20 rdoEngine control | MDAC 2.1
rdocurs.dll | Microsoft RDO Batch Client Cursor library | Microsoft RDO Client Cursor DLL | MDAC 2.1

Jet Engine

Note These files are restricted as specified in the End-User License Agreement.

File name | Description | File version resource description | Shipped in
msjet35.dll | Microsoft Jet Database Engine | Microsoft Jet Engine Library | MDAC 1.x/2.0/2.1 (limited redistribution license)
msjet40.dll | Jet Engine | Microsoft Jet Engine Library | MDAC 2.1/2.5 (limited redistribution license)
msjint32.dll | International resource file | Microsoft Jet Database Engine International DLL | MDAC 2.0/2.1 (limited redistribution license)
msjint35.dll | Microsoft Jet Database Engine International DLL | Microsoft Jet Database Engine International DLL | MDAC 1.x (limited redistribution license)
msjint40.dll | Jet international library | Microsoft Jet Database Engine International DLL | MDAC 2.1/2.5 (limited redistribution license)
msjt3032.dll | Microsoft Jet Engine Library | Microsoft Jet Engine Library | MDAC 1.x (limited redistribution license)
msjter32.dll | Microsoft Jet Database Engine Error DLL | Microsoft Jet Database Engine Error DLL | MDAC 1.x (limited redistribution license)
msjter35.dll | Error services file | Microsoft Jet Database Engine Error DLL | MDAC 2.0/2.1 (limited redistribution license)
msjter40.dll | Jet error library | Microsoft Jet Database Engine Error DLL | MDAC 2.1/2.5 (limited redistribution license)
msrclr40.dll | Replication reconciler | Microsoft Jet Briefcase Reconciler Library | MDAC 2.1
msrecr40.dll | Replication reconciler resource library | Microsoft Jet Briefcase Reconciler Resource Library | MDAC 2.1/2.5
msrepl35.dll | Microsoft Replication Library | Microsoft Replication Library | MDAC 2.0 (limited redistribution license)
msrepl40.dll | Jet replication library | Microsoft Replication Library | MDAC 2.0/2.1/2.5 (limited redistribution license)
msrpjt40.dll | Microsoft Replication Provider for Jet | Microsoft Replication Provider for Jet | MDAC 2.1
mswdat10.dll | Jet sorting data library | Microsoft Jet Sort Tables | MDAC 2.1/2.5
mswstr10.dll | Jet sorting library | Microsoft Jet Sort Library | MDAC 2.1/2.5

Microsoft Jet Installable ISAMs

Note These files are restricted as specified in the End-User License Agreement.

File name | Description | File version resource description | Shipped in
msexch35.dll | Exchange installable ISAM | Microsoft Jet Exchange ISAM | MDAC 2.0/2.1 (limited redistribution license)
msexch40.dll | Microsoft Jet Exchange ISAM | Microsoft Jet Exchange ISAM | MDAC 2.1/2.5 (limited redistribution license)
msexcl35.dll | Excel installable ISAM | Microsoft Jet Excel ISAM | MDAC 1.x/2.0/2.1 (limited redistribution license)
msexcl40.dll | Microsoft Jet Excel ISAM | Microsoft Jet Excel ISAM | MDAC 2.1/2.5 (limited redistribution license)
msltus35.dll | Lotus installable ISAM | Microsoft Jet Lotus 1-2-3 ISAM | MDAC 2.0/2.1 (limited redistribution license)
msltus40.dll | Lotus IISAM | Microsoft Jet Lotus 1-2-3 ISAM | MDAC 2.1/2.5 (limited redistribution license)
mspbde40.dll | Paradox IISAM | Microsoft Jet Paradox ISAM | MDAC 2.1/2.5
mspdox35.dll | Paradox installable ISAM | Microsoft Jet Paradox ISAM | MDAC 1.x/2.0/2.1 (limited redistribution license)
mspx3032.dll | Microsoft Jet Paradox ISAM | Microsoft Jet Paradox ISAM | MDAC 1.x (limited redistribution license)
msrd2x32.dll | IISAM for Jet 2.0 databases | Microsoft Red ISAM | MDAC 1.x (limited redistribution license)
msrd2x35.dll | IISAM for Jet 2.0 databases | Microsoft Red ISAM | MDAC 1.x/2.0/2.1 (limited redistribution license)
msrd2x40.dll | ISAM for Jet 2.0 databases | Microsoft Red ISAM | MDAC 2.1/2.5 (limited redistribution license)
msrd3x40.dll | ISAM for Jet 3.0 databases | Microsoft Red ISAM | MDAC 2.1/2.5 (limited redistribution license)
mstext35.dll | Text installable ISAM | Microsoft Jet Text ISAM | MDAC 1.x/2.0/2.1 (limited redistribution license)
mstext40.dll | Text IISAM | Microsoft Jet Text ISAM | MDAC 2.1/2.5 (limited redistribution license)
mstx3032.dll | Text IISAM | Microsoft Jet Text ISAM | MDAC 1.x
msxb3032.dll | Xbase installable ISAM | Microsoft Jet xBase ISAM | MDAC 1.x (limited redistribution license)
msxbde40.dll | dBase IISAM | Microsoft Jet xBASE ISAM | MDAC 2.1/2.5 (limited redistribution license)
msxbse35.dll | Xbase installable ISAM | Microsoft Jet xBase ISAM | MDAC 1.x/2.0/2.1 (limited redistribution license)
msxl3032.dll | Excel installable ISAM | Microsoft Jet Excel ISAM | MDAC 1.x (limited redistribution license)

SQL Server Support Library

File name | Description | File version resource description | Shipped in
dbmsadsn.dll | SQL Server AppleTalk Net-Library | Named Pipes Net DLL for SQL Clients | MDAC 2.1/2.5
dbmsrpcn.dll | SQL Server RPC Net-Library | ConnectTo RPC Net Library | MDAC 2.1/2.5
dbmsshrn.dll | SQL Server shared-memory Net-Library | ConnectTo RPC Net Library | MDAC 2.0/2.1/2.5
dbmssocn.dll | SQL Server TCP/IP sockets Net-Library | Winsock Net DLL for SQL Server | MDAC 2.0/2.1/2.5
dbmsspxn.dll | SQL Server SPX Net-Library | Client-Side NWLINK SPX WINSOCK Net Library | MDAC 2.1/2.5
dbmsvinn.dll | SQL Server vines networking Net-Library | ConnectTo VINES Net Library | MDAC 2.1/2.5
dbnmpntw.dll | SQL Server named pipes Net-Library | Named Pipes Net DLL for SQL Clients | MDAC 2.0/2.1/2.5
sqlstr.dll | SQL Server string function library | String Function .DLL for SQL Enterprise Components | MDAC 2.1/2.5
sqlwid.dll | SQL Server UNICODE function library | Unicode Function .DLL for SQL Enterprise Components | MDAC 2.1/2.5
sqlwoa.dll | SQL Server ANSI/UNICODE function library | Unicode/ANSI Function .DLL for SQL Enterprise Components | MDAC 2.1/2.5

Oracle Support Library

File name | Description | File version resource description | Shipped in
mtxoci.dll | Oracle database support library | (empty) | MDAC 2.0/2.1/2.5

OLE

File name | Description | File version resource description | Shipped in
asycfilt.dll | Automation system library | Microsoft OLE for Windows NT and Windows 95 Operating Systems | MDAC 2.1/2.5
comcat.dll | Microsoft Component Category Manager Library | Microsoft Component Category Manager Library | MDAC 2.0
oleaut32.dll | Automation system library | Microsoft OLE for Windows NT and Windows 95 Operating Systems | MDAC 2.1/2.5
olepro32.dll | Automation system library | Microsoft OLE Property Support DLL | MDAC 2.1/2.5
stdole2.tlb | Automation system library | Microsoft OLE for Windows NT and Windows 95 Operating Systems | MDAC 2.1/2.5

Visual Basic

File name | Description | File version resource description | Shipped in
expsrv.dll | Express service | Visual Basic for Applications Runtime-Expression Service | MDAC 2.0/2.1/2.5 (limited redistribution license)
msvbvm50.dll | Visual Basic Virtual Machine | Visual Basic Virtual Machine | MDAC 1.x/2.0
vbajet32.dll | VBA expression service loader | Visual Basic for Applications Development Environment-Expression Service Loader | MDAC 1.x/2.0/2.1/2.5 (limited redistribution license)
vbar2232.dll | A VBA run-time file | Visual Basic for Applications Runtime-Expression Service | MDAC 1.x
vbar332.dll | A VBA run-time file | Visual Basic for Applications Runtime-Expression Service | MDAC 1.x/2.1

Visual C++, Microsoft Foundation Classes (MFC), and ActiveX Template Library (ATL)

File name | Description | File version resource description | Shipped in
atl.dll | Active Template Library | ATL Module for Windows | Only with Visual Studio license
mfc40.dll | MFCDLL Shared Library—Retail Version | MFCDLL Shared Library—Retail Version | Only with Visual Studio license
mfc42.dll | MFCDLL Shared Library—Retail Version | MFCDLL Shared Library—Retail Version | Only with Visual Studio license
mfc42d.dll | MFCDLL Shared Library—Debug Version | MFCDLL Shared Library—Debug Version | Not redistributable
mfc42u.dll | MFCDLL Shared Library—Retail Version | MFCDLL Shared Library—Retail Version | Only with Visual Studio license
mfco42d.dll | MFCOLE Shared Library—Debug Version | MFCOLE Shared Library—Debug Version | Not redistributable
mspdb41.dll | Microsoft Program Database | Microsoft Program Database | Not redistributable
msvcirt.dll | Microsoft C run-time library | Microsoft C++ Runtime Library | MDAC 2.1/2.5
msvcprj.dll | Microsoft Visual C++ Project Package | Microsoft Visual C++ Project Package | Not redistributable
msvcrt.dll | Microsoft C run-time library | Microsoft C Runtime Library | MDAC 2.1/2.5
msvcrt20.dll | Microsoft C Runtime Library | Microsoft C Runtime Library | Only with Visual Studio license
msvcrt40.dll | Microsoft C run-time library | Microsoft C Runtime Library | MDAC 1.x/2.0/2.1
msvcrtd.dll | Microsoft C Runtime Library | Microsoft C Runtime Library | Not redistributable
msvcshl.dll | Microsoft Visual C++ Shell | Microsoft Visual C++ Shell | Not redistributable
msvcsrc.dll | Microsoft Visual C++ Src/Dbg Package | Microsoft Visual C++ Src/Dbg Package | Not redistributable

Setup

File name | Description | File version resource description | Shipped in
common98.dll | Setup library | Setup | MDAC 2.1/2.5
mdac.cmd | ACLs script batch file | (N/A) | MDAC 2.1
msdamg9x.dll | Windows 95/98 setup migration library | Microsoft Data Access—Win98 Migration DLL | MDAC 2.1/2.5
odbcconf.exe | Setup library | ODBC Driver Configuration Program | MDAC 2.1/2.5
odbckey.inf | Setup library | (N/A) | MDAC 2.1/2.5
odbcstf.dll | Setup library | ODBC Version 3.0 Custom Action Setup DLL | MDAC 2.1/2.5

System

File name | Description | File version resource description | Shipped in
comctl32.ocx | Windows Common Controls ActiveX Control DLL | Windows Common Controls ActiveX Control DLL | Only with Visual Studio license
comdlg32.ocx | CMDialog ActiveX Control DLL | CMDialog ActiveX Control DLL | Only with Visual Studio license
ctl3d32.dll | Ctl3D 3D Windows Controls | Ctl3D 3D Windows Controls | Only with Visual Studio license
regsvr32.exe | Microsoft Register Server | Microsoft Register Server | Only with Visual Studio license


MDAC Version History

The following sections cover the details of each important release of MDAC since version 1.0.

Major Releases

MDAC has had five major releases since it was first published. Each of these major releases has been followed by minor releases. A minor release improves functionality but does not usually introduce new features.

  • MDAC 1.0
    • OLE DB 1.0 SDK
    • OLE DB 1.1 SDK
  • MDAC 1.5
    • MDAC 1.5 (IE)
    • MDAC 1.5 (PDC)
    • MDAC 1.5a
    • MDAC 1.5b
    • MDAC 1.5c
    • MDAC 1.5d
  • MDAC 2.0
    • MDAC 2.0.3002.20 (GA)
    • MDAC 2.0.3002.23 (Windows NT 4 SP4)
    • MDAC 2.0.3002.28 (GA)
  • MDAC 2.1
    • MDAC 2.10.3513.2 (SQL)
    • MDAC 2.1 SDK Updater
    • Subset of MDAC 2.1.1.3711.6 (GA)
    • MDAC 2.1.1.3711.11 (GA)
    • MDAC 2.1.2.4202.3 (GA)
  • MDAC 2.5


MDAC 1.0

The MDAC 1.0 release is now obsolete and is no longer generally available.

MDAC 1.0 Release Details

The following table describes two important releases of the OLE DB 1.x SDK.

Release Release date Major components
OLE DB 1.0 SDK August 1996
  • ODBC 2.5
  • ODBC Drivers
    • Access
    • Excel
    • Paradox
    • SQL Server
    • Text
    • Visual FoxPro
    • xBase
  • Jet ISAM Drivers
    • Excel
    • Paradox
    • Red (Jet)
    • Text
    • xBase
  • OLE DB 1.0 (new)
  • OLE DB Providers
    • ODBC (new)
  • ADO (preview)
  • ADOR (new)
  • OLE DB SDK (includes ADO and ADO/R SDK)
OLE DB 1.1 SDK December 1996
  • ODBC 3.0
  • ODBC Drivers
    • Access
    • Excel
    • Paradox
    • SQL Server
    • Text
    • Visual FoxPro
    • xBase
  • Jet ISAM Drivers
    • Excel
    • Paradox
    • Red (Jet)
    • Text
    • xBase
  • OLE DB 1.1
  • OLE DB Providers
    • ODBC
  • ADO 1.0 (new)
  • ADC (was ADOR)
  • OLE DB SDK (includes ADO and ADC)

MDAC 1.0 Release Manifests

The following table lists all of the modules that were distributed with each release, not including setup modules, sample files, and SDK tools. Version numbers in boldface indicate an update or new component. Empty cells in the table indicate that module did not ship with that release of MDAC. This is not a complete list of the files that must be redistributed; see the section in this article titled Redistributing MDAC for more information.

File Versions

Module | OLE DB 1.0 SDK | OLE DB 1.1 SDK
ctl3d32.dll | 2.30.0.0 |
dbnmpntw.dll | 1996.7.15.0 | 1996.11.1.0
ds16gt.dll | 2.10.23.23 | 3.0.23.1
ds32gt.dll | 2.50.30.6 | 3.0.23.1
mfc40.dll | 4.1.0.6038 | 4.1.0.6038
mfc42.dll |  | 4.2.0.6068
msadc10.exe |  | 4.71.121.0
msadcb10.dll |  | 1.0.1211.0
msadcc10.dll |  | 1.0.1211.0
msadcf10.dll |  | 1.0.1211.0
msadco10.dll |  | 1.0.1211.0
msadcs10.dll |  | 1.0.1211.0
msader10.dll | 0.99.0.826 | 1.0.0.994
msado10.dll | 0.99.0.826 | 1.0.0.994
msador10.dll |  | 1.0.0.995
msadrs10.dll | 0.99.0.819 |
mscpxl32.dll | 2.65.2.1 | 2.65.2.1
msdadc.dll | 1.0.2026.0 | 1.10.2326.0
msdaenum.dll | 1.0.2026.0 | 1.10.2326.0
msdaer.dll | 1.0.2026.0 | 1.10.2326.0
msdaerr.dll | 1.0.2026.0 | 1.10.2326.0
msdasql.dll | 1.0.2026.0 | 1.10.2326.0
msdasqlr.dll | 1.0.2026.0 | 1.10.2326.0
msdatl.dll | 1.0.2026.0 | 1.10.2326.0
msdatt.dll | 1.0.2026.0 | 1.10.2326.0
msexcl35.dll |  | 3.50.3428.0
msjet35.dll |  | 3.50.3428.0
msjint32.dll | 3.0.0.2712 |
msjint35.dll |  | 3.50.3428.0
msjt3032.dll | 3.0.0.2809 |
msjter32.dll | 3.0.0.2712 |
msjter35.dll |  | 3.50.3428.0
mspdb41.dll | 4.10.0.6038 | 4.10.0.6038
mspdox35.dll |  | 3.50.3428.0
mspx3032.dll | 3.0.0.2504 |
msrd2x32.dll | 3.0.0.2504 |
msrd2x35.dll |  | 3.50.3428.0
mstext35.dll |  | 3.50.3428.0
mstx3032.dll | 3.0.0.2504 |
msvcirt.dll |  | 4.20.0.6164
msvcprj.dll | 4.10.0.6038 | 4.10.0.6038
msvcrt.dll |  | 4.20.0.6164
msvcrt20.dll | 2.11.0.0 |
msvcrt40.dll | 4.10.0.6038 | 4.10.0.6038
msvcshl.dll | 4.10.0.6038 | 4.10.0.6038
msvcsrc.dll | 4.10.0.6038 | 4.10.0.6038
msxb3032.dll | 3.0.0.2504 |
msxbse35.dll |  | 3.50.3428.0
msxl3032.dll | 3.0.0.2517 |
mtxdm.dll |  | 1996.11.27.137
odbc16gt.dll | 2.50.30.6 | 3.0.23.1
odbc32.dll | 2.50.30.6 | 3.0.23.1
odbc32gt.dll | 2.50.30.6 | 3.0.23.1
odbcad32.exe | 2.50.30.6 | 3.0.23.1
odbccp32.cpl | 2.50.30.6 | 3.0.23.1
odbccp32.dll | 2.50.30.6 | 3.0.23.1
odbccr32.dll | 2.50.30.6 | 3.0.23.1
odbcint.dll | 2.50.30.6 | 3.0.23.1
odbcji32.dll | 3.40.28.29 | 3.50.3428.0
odbcjt32.dll | 3.40.28.29 | 3.50.3428.0
odbcstf.dll | 3.0.21.27 | 3.0.23.1
odbctl32.dll | 3.40.28.29 | 3.50.3428.0
odbctrac.dll |  | 3.0.23.1
oddbse32.dll | 3.40.28.29 | 3.50.3428.0
odexl32.dll | 3.40.28.29 | 3.50.3428.0
odfox32.dll | 3.40.28.29 | 3.50.3428.0
odpdx32.dll | 3.40.28.29 | 3.50.3428.0
odtext32.dll | 3.40.28.29 | 3.50.3428.0
oleaut32.dll | 2.20.4049.1 | 2.20.4054.1
regsvr32.exe |  | 4.0.0.0
sqlsrv32.dll | 2.65.2.13 | 2.65.2.36
vbajet32.dll | 2.0.0.5924 | 3.0.0.6824
vbar2232.dll | 2.0.0.5924 |
vbar332.dll |  | 3.0.0.6824
ven2232.olb | 2.0.0.5924 |

MDAC 1.0 Release Notes

The first major release of MDAC wasn't exactly a major release. It wasn't even called MDAC, and it wasn't a single release. Before MDAC 1.0, the ODBC SDK had existed for several versions and over several years. The first version shipped in September 1992. The ODBC SDK continued to be a separate product until version 3.5 in April 1998. Meanwhile, Microsoft released the OLE DB 1.0 SDK in August 1996, which began to point the way toward an integrated MDAC version. Later in 1996, the MDAC 1.0 stack was released through a variety of distribution mechanisms. However, centralized and coordinated MDAC did not arrive until version 1.5.

ODBC 3.0 shipped with the ODBC 3.0 SDK (available at the time by Web download) as well as with Internet Information Server 3.0 and the OLE DB SDK. The ODBC SDK was not included with the OLE DB SDK, although redistributable files from the ODBC SDK were included to support the OLE DB Provider for ODBC. The only ODBC driver installed by the ODBC 3.0 SDK was the SQL Server Driver. ODBC drivers for Microsoft® Access or Oracle were available only as part of development products such as Microsoft Visual Basic®, Microsoft Visual C++®, and Microsoft Access 97.

The OLE DB SDK contained the earliest versions of most of the components that form the current version of MDAC: ODBC, OLE DB, and ADO. OLE DB 1.1 and ADO 1.0 shipped with the OLE DB 1.1 SDK, which was available with Microsoft Visual Studio® 97 or by Web download, and with Internet Information Server 3.0. The Advanced Data Connector (ADC), which is now part of ADO, shipped with Internet Information Server 3.0 as well as being available on the Web as a downloadable cabinet (.cab) file. The ADC used a subset of the ADO Recordset and was generally independent of ADO.

ODBC 3.0 was tested and supported on Microsoft Windows NT® 3.51 and 4.0 and on Microsoft Windows® 95. OLE DB 1.1, ADO 1.0, and ADC 1.0 all were tested and supported on Windows NT 4.0 and Windows 95. However, these products have dependencies that prevent them from being supported on Windows NT 3.51.

Besides being distributed on the Web, MDAC 1.0 was also distributed through two other Microsoft products: Microsoft Active Server Pages (ASP), which was included with Microsoft Internet Information Server (IIS) 3.0 and Microsoft Visual InterDev® 1.0, and through Microsoft Visual Studio 97. These products integrated the MDAC components into their own setup programs.

In Microsoft Visual Studio 97, individual products installed different versions of ODBC and potentially OLE DB and ADO. Visual Interdev, for example, included ASP, which installed ODBC 3.0, OLE DB 1.1, and ADO/ADC 1.0. However, Microsoft Visual Basic 5.0 installed ODBC 2.65 and did not install OLE DB or ADO/ADC components. Microsoft Visual C++ 5.0 installed ODBC 2.65 and included the option to install the OLE DB 1.5 SDK, which provided the entire MDAC 1.0 stack.

Redistribution of ADC 1.0, and later 1.1 components, was accomplished by a .cab file installation. However, in some scenarios it was impossible to upgrade from ADC 1.0 to 1.1 because of registry problems with the .cab files.

MDAC 1.0 Redistribution Notes

Neither release of the OLE DB SDK contained a redistributable setup program. Redistribution instructions were partially documented in the installed doc folder, in the file sdkguide.hlp under the topic titled "Redistributable Components." These instructions did not make it clear enough that if you distribute either OLE DB or ADO, you must also distribute the other.


MDAC 1.5

The MDAC 1.5 release is now obsolete and is no longer generally available.

The Microsoft® Data Access Components version 1.5 had a series of separate releases, each including different versions of ODBC 3.5, OLE DB 1.5, ADO 1.5, and RDS 1.5 components as well as ODBC drivers and OLE DB providers. The first two releases, other than file version stamping and bugs fixed in the setup, had no significant differences.

MDAC 1.5 Release Details

There were six important releases of MDAC 1.5, which are described in the following table.

Release Release date Major components
MDAC 1.5 (IE 4.0)

Shipped with Microsoft Internet Explorer 4.0 and Internet Client SDK 4.0. Integrated within the setup of both products.

 September 1997
  • ADO 1.5 (light)
  • RDS 1.5 (was ADC)
MDAC 1.5 (PDC)

Updated the components shipping with Internet Explorer. Available only on the 1997 Professional Developers Conference CD within the MDAC stand-alone setup program.

 September 1997
  • ODBC 3.5
  • ODBC Drivers
    • Access
    • Excel
    • Oracle® (new)
    • Paradox
    • SQL Server
    • Text
    • Visual FoxPro
    • xBase
  • Jet ISAM Drivers
    • Excel
    • Exchange (new)
    • Lotus 1-2-3 (new)
    • Paradox
    • Red (Jet)
    • Text
    • xBase
  • OLE DB 1.5
  • OLE DB Providers
    • ODBC
  • ADO 1.5
  • RDS 1.5
MDAC 1.5a

Available on the Web.

 November 1997
  • ODBC 3.5
  • ODBC Drivers
    • Access
    • Oracle
    • SQL Server
  • Jet ISAM Drivers
    • Red (Jet)
  • OLE DB 1.5
  • OLE DB Providers
    • ODBC
  • ADO 1.5
  • RDS 1.5
MDAC 1.5b

Available with Microsoft Windows NT® 4.0 Option Pack, the MDAC stand-alone was actually integrated within the Option Pack and was not available as a separate setup.

 November 1997
  • ODBC 3.5
  • ODBC Drivers
    • Access
    • Excel
    • Oracle
    • Paradox
    • SQL Server
    • Text
    • Visual FoxPro
    • xBase
  • Jet ISAM Drivers
    • Excel
    • Exchange
    • Lotus 1-2-3
    • Paradox
    • Red (Jet)
    • Text
    • xBase
  • OLE DB 1.5
  • OLE DB Providers
    • ODBC
  • ADO 1.5
  • RDS 1.5
MDAC 1.5c

Available on the Web after December 20, 1997, both with the MDAC stand-alone and as a smaller MDAC redistribution that just installed the run-time components.

 December 1997
  • ODBC 3.5
  • ODBC Drivers
    • Access
    • Excel
    • Oracle
    • Paradox
    • SQL Server
    • Text
    • Visual FoxPro
    • xBase
  • Jet ISAM Drivers
    • Excel
    • Exchange
    • Lotus 1-2-3
    • Paradox
    • Red (Jet)
    • Text
    • xBase
  • OLE DB 1.5
  • OLE DB Providers
    • Jet (new)
    • ODBC
  • ADO 1.5
    • RDS 1.5
MDAC 1.5d

A final 1.5d release of MDAC can be found within Microsoft Windows® 98 and Internet Explorer 4.01 Service Pack 1. Each product contains an incomplete subset of MDAC components, but both contain the only complete and most current set of ADO and RDS 1.5d components.

 March 1998
  • ADO 1.5
  • RDS 1.5

MDAC 1.5 Release Manifests

The following table lists all of the modules that were distributed with each release, not including setup modules, sample files, and SDK tools. Version numbers in boldface indicate an update or new component. Version numbers in italic indicate an out-of-date component. Empty cells in the table indicate that module did not ship with that release of MDAC. This is not a complete list of the files that must be redistributed; see the section in this article titled Redistributing MDAC for more information.

File Versions

Module | MDAC 1.5 (IE 4.0) | MDAC 1.5 (PDC) | MDAC 1.5a | MDAC 1.5b | MDAC 1.5c Windows 95 | MDAC 1.5c Windows NT | MDAC 1.5d
asycfilt.dll |  |  |  |  | 2.20.4122.1 | 2.20.4122.1 |
atl.dll |  |  |  |  | 2.0.7024.0 | 2.0.7024.0 |
comctl32.ocx |  |  |  |  | 5.0.37.14 | 5.0.38.28 |
comdlg32.ocx |  |  |  |  | 5.0.37.14 | 5.0.37.14 |
dbmssocn.dll |  | 1997.8.11.0 | 1997.8.11.0 | 1997.8.11.0 | 1997.8.11.0 | 1997.8.11.0 |
dbnmpntw.dll |  | 1997.8.11.0 | 1997.8.11.0 | 1997.8.11.0 | 1997.8.11.0 | 1997.8.11.0 |
ds16gt.dll |  | 3.50.32.14 | 3.50.33.30 | 3.50.33.30 | 3.50.36.9 | 3.50.36.9 |
ds32gt.dll |  | 3.50.32.14 | 3.50.33.30 | 3.50.33.30 | 3.50.36.9 | 3.50.36.9 |
mfc42.dll |  | 4.21.0.7022 |  | 4.21.0.7022 | 4.21.0.7022 | 4.21.0.7022 |
msadce.dll | 1.5.2007.0 | 1.5.2014.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2125.0
msadcer.dll | 1.5.2007.0 | 1.5.2014.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2125.0
msadcf.dll |  | 1.5.2014.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2125.0 |
msadco.dll | 1.5.2007.0 | 1.5.2014.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2603.0
msadcor.dll | 1.5.2007.0 | 1.5.2014.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2603.0
msadcs.dll |  | 1.5.2014.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2125.0 | 1.5.2125.0 |
msader15.dll | 1.50.2.2007 | 1.50.2.2021 | 1.50.2.2207 | 1.50.2.2207 | 1.50.2.2404 | 1.50.2.2404 | 1.50.2.2603
msado15.dll |  | 1.50.2.2021 | 1.50.2.2207 | 1.50.2.2207 | 1.50.2.2404 | 1.50.2.2404 | 1.50.2.2603
msador15.dll | 1.50.2.2007 | 1.50.2.2021 | 1.50.2.2207 | 1.50.2.2207 | 1.50.2.2404 | 1.50.2.2404 | 1.50.2.2603
msadrh15.dll |  | 1.50.2.2021 | 1.50.2.2207 | 1.50.2.2207 | 1.50.2.2404 | 1.50.2.2404 | 1.50.2.2603
mscpxl32.dll |  | 3.50.3.0 | 3.50.3.0 | 3.50.3.0 | 3.50.3.0 | 3.50.3.0 |
msdadc.dll | 1.50.3206.0 | 1.50.3221.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3401.0
msdaenum.dll |  | 1.50.3221.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 |
msdaer.dll |  | 1.50.3221.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 |
msdaerr.dll |  | 1.50.3221.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 |
msdaps.dll | 1.50.3206.0 | 1.50.3221.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3401.0
msdasql.dll |  | 1.50.3221.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 |
msdasqlr.dll |  | 1.50.3221.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 |
msdatl.dll |  | 1.50.3221.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 |
msdatsrc.tlb | No Version Info | No Version Info | No Version Info | No Version Info | No Version Info | No Version Info |
msdatt.dll |  | 1.50.3221.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 | 1.50.3506.0 |
msexch35.dll |  | 3.51.623.0 |  | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 |
msexcl35.dll |  | 3.51.623.2 |  | 3.51.623.2 | 3.51.623.2 | 3.51.623.2 |
msjet35.dll |  | 3.51.623.4 | 3.51.623.4 | 3.51.623.4 | 3.51.623.4 | 3.51.623.4 |
msjint35.dll |  | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 |
msjter35.dll |  | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 |
msjtor35.dll |  |  |  |  | 3.51.1014.4 | 3.51.1014.4 |
msltus35.dll |  | 3.51.623.0 |  | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 |
msorcl32.dll |  | 2.73.7233.0 | 2.73.7283.1 | 2.73.7283.3 | 2.73.7283.3 | 2.73.7283.3 |
mspdox35.dll |  | 3.51.623.0 |  | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 |
msrd2x35.dll |  | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 |
msrepl35.dll |  | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 |
mstext35.dll |  | 3.51.623.0 |  | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 |
msvbvm50.dll |  |  |  | 5.0.37.24 | 5.0.37.24 | 5.0.37.24 |
msvcirt.dll |  |  |  |  | 5.0.0.7022 | 5.0.0.7022 |
msvcrt.dll |  | 5.0.0.7022 |  | 5.0.0.7022 | 5.0.0.7022 | 5.0.0.7022 |
msvcrt40.dll |  |  |  |  | 4.10.0.6038 | 4.10.0.6038 |
msxbse35.dll |  | 3.51.623.0 |  | 3.51.623.0 | 3.51.623.0 | 3.51.623.0 |
mtxdm.dll |  | 1997.8.463.0 | 1997.6.394.0 | 1997.6.394.0 | 1997.6.394.0 | 1997.6.394.0 |
ntwdblib.dll |  | 1997.8.11.0 |  |  | 1997.8.11.0 | 1997.8.11.0 |
odbc16gt.dll |  | 3.50.32.14 | 3.50.33.30 | 3.50.33.30 | 3.50.36.9 | 3.50.36.9 |
odbc32.dll |  | 3.50.32.14 | 3.50.33.30 | 3.50.33.30 | 3.50.36.9 | 3.50.36.9 |
odbc32gt.dll |  | 3.50.32.14 | 3.50.33.30 | 3.50.33.30 | 3.50.36.9 | 3.50.36.9 |
odbcad32.exe |  | 3.50.32.14 | 3.50.33.30 | 3.50.33.30 | 3.50.36.9 | 3.50.36.9 |
odbcbcp.dll |  | 3.50.3.0 | 3.50.3.4 | 3.50.3.4 | 3.50.3.4 | 3.50.3.4 |
odbccp32.cpl |  | 3.50.32.14 | 3.50.33.30 | 3.50.33.30 | 3.50.36.9 | 3.50.36.9 |
odbccp32.dll |  | 3.50.32.14 | 3.50.33.30 | 3.50.33.30 | 3.50.36.9 | 3.50.36.9 |
odbccr32.dll |  | 3.50.32.14 | 3.50.33.30 | 3.50.33.30 | 3.50.36.9 | 3.50.36.9 |
odbccu32.dll |  | 3.50.32.14 | 3.50.33.30 | 3.50.33.30 | 3.50.36.9 | 3.50.36.9 |
odbcint.dll |  | 3.50.32.14 | 3.50.33.30 | 3.50.33.30 | 3.50.36.9 | 3.50.36.9 |
odbcji32.dll |  | 3.50.3907.0 | 3.51.1029.0 | 3.51.1029.0 | 3.51.1029.0 | 3.51.1029.0 |
odbcjt32.dll |  | 3.50.3907.0 | 3.51.1029.0 | 3.51.1029.0 | 3.51.1029.0 | 3.51.1029.0 |
odbctl32.dll |  | 3.50.3907.0 | 3.51.1029.0 | 3.51.1029.0 | 3.51.1029.0 | 3.51.1029.0 |
odbctrac.dll |  | 3.50.32.14 | 3.50.33.30 | 3.50.33.30 | 3.50.36.9 | 3.50.36.9 |
oddbse32.dll |  | 3.50.3907.0 |  | 3.51.1029.0 | 3.51.1029.0 | 3.51.1029.0 |
odexl32.dll |  | 3.50.3907.0 |  | 3.51.1029.0 | 3.51.1029.0 | 3.51.1029.0 |
odfox32.dll |  | 3.50.3907.0 |  | 3.51.1029.0 | 3.51.1029.0 | 3.51.1029.0 |
odpdx32.dll |  | 3.50.3907.0 |  | 3.51.1029.0 | 3.51.1029.0 | 3.51.1029.0 |
odtext32.dll |  | 3.50.3907.0 |  | 3.51.1029.0 | 3.51.1029.0 | 3.51.1029.0 |
oleaut32.dll |  |  |  |  | 2.20.4122.1 | 2.20.4122.1 |
olepro32.dll |  |  |  |  | 5.0.4122.1 | 5.0.4122.1 |
regsvr32.exe |  | 4.0.1381.0 |  | 4.0.1381.0 | 4.0.1381.0 | 4.0.1381.0 |
simpdata.tlb |  |  |  |  | No Version Info | No Version Info |
sqlsrv32.dll |  | 3.50.3.0 | 3.50.3.5 | 3.50.3.5 | 3.50.3.5 | 3.50.3.5 |
stdole2.tlb |  |  |  |  | 2.20.4122.1 | 2.20.4122.1 |
vbajet32.dll |  | 3.0.0.6908 | 3.0.0.6908 | 3.0.0.6908 | 3.0.0.6908 | 3.0.0.6908 |
vbar332.dll |  | 3.0.0.6908 | 3.0.0.6908 | 3.0.0.6908 | 3.0.0.6908 | 3.0.0.6908 |
windbver.exe |  | 1997.8.11.0 | 1997.8.11.0 | 1997.8.11.0 | 1997.8.11.0 | 1997.8.11.0 |

MDAC 1.5 Release Notes

With MDAC 1.5, more coordination was brought to both the setup of MDAC components and redistribution.

The Remote Data Service succeeded the Advanced Data Connector. It was renamed to reflect that it was increasingly a feature of ADO.

ODBC 3.5 was tested and supported on Windows NT 3.51, NT 4.0, and Windows 95. OLE DB 1.5, ADO 1.5, and RDS 1.5 were tested and supported on Windows NT 4.0 and Windows 95. However, dependencies prevented these three components from being supported or tested on Windows NT 3.51.

MDAC 1.5c matched bits included with the OLE DB 1.5 and ODBC 3.5 SDKs. The actual bug fixes between 1.5b and 1.5c were incremental but significant, fixing problems with ADO threading and ODBC connection pooling.

Windows 98 shipped a pre-MDAC subset of the Access ODBC Driver, a subset of the MDAC 1.5a OLE DB Provider for ODBC, and 1.5d components for both ADO and RDS.

Internet Explorer 4.01 SP1 shipped ODBC 3.0 (MDAC 1.0), pre-MDAC 1.5 Access driver, a single file of the MDAC 1.0 SQL Server ODBC Driver, a subset of the MDAC 1.5a OLE DB Provider for ODBC, and the latest ADO and RDS 1.5d components.

Windows 98 and Internet Explorer 4.01 did contain the elements of MDAC 1.5d. However, their versions of ODBC and OLE DB are incomplete and older than those in MDAC 1.5c. You could ensure you had the latest bits by applying Windows 98 or Internet Explorer 4.01 Service Pack 1 and then installing MDAC 1.5c. The changes for MDAC 1.5d were incremental; they were of benefit only if you used the Remote Data Service, and then only if you marshaled by using the DCOM protocol.

Because MDAC 1.5d was released by both products so close to the release of MDAC 2.0, the MDAC product group did not release either a stand-alone or a redistribution version of the MDAC setup programs. The single significant change in 1.5d consisted of removing the dependency of RDS to mark a DCOM-based business object as safe for scripting and initialization.

The MDAC stand-alone setup really comprised half an SDK, providing SDK elements for the MDAC 1.5 stack. However, two other SDKs—the ODBC 3.5 SDK (released in the spring of 1998) and the OLE DB 1.5 SDK (released in the fall of 1997)—complemented the MDAC setup. If you installed all three setups, you had a complete set of samples, documentation, headers, and libs for the MDAC 1.5 stack. This was, as much as anything, the reason the MDAC SDK was created: Developers had to install only one setup to get the entire SDK for MDAC 2.0.

A library file that had been distributed with prior versions of MDAC was no longer included in ADO 1.5. The library, adoid.lib, shipped with ADO 1.0 and ADO 1.1. For ADO 1.5 and later, programmers should include initguid.h instead.

MDAC 1.5 Redistribution Notes

Redistribution and dependencies for MDAC 1.5 became both simpler and more complex. It was simple to use the MDAC 1.5 stand-alone setup to install the entire MDAC 1.5 stack, even though the setup program itself was operating system–dependent: The MDAC stand-alone for Windows 95 could not be run on Windows NT, and vice versa. However, the MDAC 1.5 redistribution program provided an operating system–independent setup that ran equally well on Windows NT 3.51 and 4.0, as well as on Windows 95.

MDAC 1.5 also provided a rich feature set for installing specific components or drivers and providers from the command line.


MDAC 2.0

The MDAC 2.0 release contains the complete MDAC SDK and a few new technologies. MDAC 2.0 is available on the Microsoft Universal Data Access Web site. You can download MDAC 2.0 and then install the latest MDAC 2.1 Service Pack (currently SP2) to get the latest version of all components. You should also get the Data Access SDK 2.1 Updater to update the documentation at the Microsoft Universal Data Access Web site.

The MDAC 2.0 redistribution program is distributed within the MDAC 2.0 SDK. The MDAC 2.0 SDK combines and updates the contents of MDAC 1.5, the ODBC 3.5 SDK and the OLE DB 1.5 SDK, and the OLE DB for OLAP specification. The MDAC SDK is a single set of components; ADO/RDS cannot be run separately.

MDAC 2.0 Release Details

There were three important releases of MDAC 2.0, which are described in the following table.

Release Release date Major components
MDAC 2.0.3002.20 (GA)

(Also known as MDAC 2.0)

 July 1998
  • ODBC 3.51
  • ODBC Drivers
    • Access
    • Excel
    • Paradox
    • Oracle
    • SQL Server
    • Text
    • Visual FoxPro
    • xBase
  • Jet ISAM Drivers
    • Excel
    • Exchange
    • Lotus 1-2-3
    • Paradox
    • Red (Jet)
    • Text
    • xBase
  • OLE DB 2.0
  • OLE DB Providers
    • Jet 3.5
    • ODBC
    • Oracle (new)
    • SQL Server
  • OLE DB for OLAP (new)
  • ADO 2.0
  • ADO MD 2.0 (new)
  • RDS 2.0
  • MDAC SDK
MDAC 2.0.3002.23 (Microsoft® Windows NT® 4 SP4)

(Also known as MDAC 2.0 SP1)

 October 1998
  • ODBC 3.51
  • ODBC Drivers
    • Access
    • Excel
    • Paradox
    • Oracle
    • SQL Server
    • Text
    • Visual FoxPro
    • xBase
  • Jet ISAM Drivers
    • Excel
    • Exchange
    • Lotus 1-2-3
    • Paradox
    • Red (Jet)
    • Text
    • xBase
  • OLE DB 2.0
  • OLE DB Providers
    • Jet 3.5
    • ODBC
    • Oracle
    • SQL Server
  • ADO 2.0
  • RDS 2.0
MDAC 2.0.3002.28 (GA)

(Also known as MDAC 2.0 SP2)

 January 1999
  • ODBC 3.51
  • ODBC Drivers
    • Access
    • Excel
    • Paradox
    • Oracle
    • SQL Server
    • Text
    • Visual FoxPro
    • xBase
  • Jet ISAM Drivers
    • Excel
    • Exchange
    • Lotus 1-2-3
    • Paradox
    • Red (Jet)
    • Text
    • xBase
  • OLE DB 2.0
  • OLE DB Providers
    • Jet 3.5
    • ODBC
    • Oracle
    • SQL Server
  • ADO 2.0
  • RDS 2.0

MDAC 2.0 Release Manifests

The following table lists all of the modules that were distributed with each release, not including setup modules, sample files, and SDK tools. Version numbers in boldface indicate an update or new component. Empty cells in the table indicate that module did not ship with that release of MDAC. This is not a complete list of the files that must be redistributed; see the section in this article titled Redistributing MDAC for more information.

File Versions

Module | MDAC 2.0.3002.20 (GA) | MDAC 2.0.3002.23 (Windows NT 4 SP4) | MDAC 2.0.3002.28 (GA)
asycfilt.dll | 2.30.4261.1 | 2.30.4261.1 | 2.30.4265.1
atl.dll | 2.0.7024.0 |  |
comcat.dll | 4.71.1441.1 |  |
comctl32.ocx | 5.1.43.19 |  |
comdlg32.ocx | 5.1.43.19 |  |
dbmsshrn.dll | 1998.6.5.0 | 1998.6.5.0 | 1998.11.13.0
dbmssocn.dll | 1998.6.5.0 | 1998.6.5.0 | 1998.11.13.0
dbnmpntw.dll | 1998.6.5.0 | 1998.6.5.0 | 1998.11.13.0
ds16gt.dll | 3.510.3002.13 | 3.510.3002.23 | 3.510.3002.23
ds32gt.dll | 3.510.3002.13 | 3.510.3002.23 | 3.510.3002.23
dtstvwr.exe | 2.0.0.0 |  |
expsrv.dll | 6.0.0.8167 | 6.0.0.8167 | 6.0.0.8167
mfc42.dll | 4.21.0.7022 |  |
mfc42d.dll | 4.21.0.7022 |  |
mfc42u.dll | 4.21.0.7022 |  |
mfco42d.dll | 4.21.0.7022 |  |
msadce.dll | 2.0.3002.12 | 2.0.3002.12 | 2.0.3002.12
msadcer.dll | 2.0.3002.12 | 2.0.3002.12 | 2.0.3002.12
msadcf.dll | 2.0.3002.12 | 2.0.3002.12 | 2.0.3002.12
msadcfr.dll | 2.0.3002.12 | 2.0.3002.12 | 2.0.3002.12
msadco.dll | 2.0.3002.12 | 2.0.3002.12 | 2.0.3002.12
msadcor.dll | 2.0.3002.12 | 2.0.3002.12 | 2.0.3002.12
msadcs.dll | 2.0.3002.12 | 2.0.3002.12 | 2.0.3002.12
msadds.dll | 2.0.3002.12 | 2.0.3002.12 | 2.0.3002.12
msaddsr.dll | 2.0.3002.12 | 2.0.3002.12 | 2.0.3002.12
msader15.dll | 2.0.3002.11 | 2.0.3002.23 | 2.0.3002.23
msado15.dll | 2.0.3002.11 | 2.0.3002.23 | 2.0.3002.23
msadomd.dll | 2.0.2922.1 |  |
msador15.dll | 2.0.3002.11 | 2.0.3002.23 | 2.0.3002.23
msadrh15.dll | 2.0.3002.11 | 2.0.3002.23 | 2.0.3002.23
mscpxl32.dll | 3.50.3.0 | 3.60.3.19 | 3.60.3.19
msdadc.dll | 2.0.3002.4 | 2.0.3002.23 | 2.0.3002.23
msdaenum.dll | 2.0.3002.4 | 2.0.3002.23 | 2.0.3002.23
msdaer.dll | 2.0.3002.4 | 2.0.3002.23 | 2.0.3002.23
msdaerr.dll | 2.0.3002.4 | 2.0.3002.23 | 2.0.3002.23
msdaora.dll | 2.0.3002.15 |  | 2.0.3002.15
msdaosp.dll | 2.0.30.2 | 2.0.30.2 | 2.0.30.2
msdaprst.dll | 2.0.3002.12 | 2.0.3002.12 | 2.0.3002.12
msdaps.dll | 2.0.3002.4 | 2.0.3002.23 | 2.0.3002.23
msdasc.dll | 2.0.3002.0 | 2.0.3002.0 | 2.0.3002.0
msdasql.dll | 2.0.3002.11 | 2.0.3002.23 | 2.0.3002.23
msdasqlr.dll | 2.0.3002.11 | 2.0.3002.23 | 2.0.3002.23
msdatl2.dll | 2.0.3002.11 | 2.0.3002.23 | 2.0.3002.23
msdatsrc.tlb |  | No Version Info |
msdatt.dll | 2.0.3002.4 | 2.0.3002.23 | 2.0.3002.23
msdfmap.dll | 2.0.3002.12 | 2.0.3002.12 | 2.0.3002.12
msexch35.dll | 3.51.623.0 | 3.51.623.0 | 3.51.623.0
msexcl35.dll | 3.51.623.2 | 3.51.623.2 | 3.51.623.2
msjet35.dll | 3.51.623.4 | 3.51.2026.0 | 3.51.2026.0
msjint35.dll | 3.51.623.0 | 3.51.623.0 | 3.51.623.0
msjt4jlt.dll | 3.52.1527.3 | 3.52.2026.0 | 3.52.2026.0
msjter35.dll | 3.51.623.0 | 3.51.623.0 | 3.51.623.0
msjtes40.dll | 4.0.1718.5 | 4.0.1718.11 | 4.0.1718.11
msjtor35.dll | 3.52.1527.4 | 3.52.1527.4 | 3.52.1527.4
msltus35.dll | 3.51.623.0 | 3.51.623.0 | 3.51.623.0
msmdcube.dll | 1.0.612.606 |  |
msmdgdrv.dll | 1.0.612.606 |  |
msmdint.dll | 1.0.0.612 |  |
msolap.dll | 1.0.612.606 |  |
msolapfm.dll | 1.0.0.612 |  |
msolapr.dll | 1.0.612.606 |  |
msolapui.dll | 1.0.0.612 |  |
msorcl32.dll | 2.573.2927.0 | 2.573.2927.0 | 2.573.2927.0
mspdox35.dll | 3.51.623.0 | 3.51.623.0 | 3.51.623.0
msrd2x35.dll | 3.51.623.0 | 3.51.623.0 | 3.51.623.0
msrepl35.dll | 3.51.623.0 | 3.51.2010.0 | 3.51.2010.0
mstext35.dll | 3.51.623.0 | 3.51.623.0 | 3.51.623.0
msvbvm50.dll | 5.1.43.19 |  |
msvcirt.dll | 5.0.0.7022 |  |
msvcrt.dll | 5.0.0.7022 | 5.0.0.7022 | 5.0.0.7128
msvcrt40.dll | 4.10.0.6038 |  |
msvcrtd.dll | 5.0.0.7022 |  |
mswstr10.dll | 4.0.1711.0 |  |
msxbse35.dll | 3.51.623.0 | 3.51.623.0 | 3.51.623.0
mtxdm.dll | 1997.11.532.0 | 1997.11.532.0 | 1997.11.532.0
mtxoci.dll | 1998.2.715.0 | 1998.2.715.0 | 1998.2.715.0
odbc16gt.dll | 3.510.3002.13 | 3.510.3002.23 | 3.510.3002.23
odbc32.dll | 3.510.3002.13 | 3.510.3002.23 | 3.510.3002.23
odbc32gt.dll | 3.510.3002.13 | 3.510.3002.23 | 3.510.3002.23
odbcad32.exe | 3.510.3002.13 | 3.510.3002.23 | 3.510.3002.23
odbcbcp.dll | 3.60.3.19 | 3.60.3.19 | 3.60.3.19
odbcconf.exe | 3.60.3017.0 | 3.60.3122.0 | 3.60.3419.0
odbccp32.cpl | 3.510.3002.13 | 3.510.3002.23 | 3.510.3002.23
odbccp32.dll | 3.510.3002.13 | 3.510.3002.23 | 3.510.3002.23
odbccr32.dll | 3.510.3002.13 | 3.510.3002.23 | 3.510.3002.23
odbccu32.dll | 3.510.3002.13 | 3.510.3002.23 | 3.510.3002.23
odbcint.dll | 3.510.3002.13 | 3.510.3002.23 | 3.510.3002.23
odbcji32.dll | 3.51.1713.0 | 3.51.1713.0 | 3.51.1713.0
odbcjt32.dll | 3.51.1713.0 | 3.51.1713.0 | 3.51.1713.0
odbcsql.exe | No Version Info |  |
odbcstf.dll | 3.510.26.27 | 3.510.26.27 | 3.510.3419.8
odbctl32.dll | 3.51.1713.0 | 3.51.1713.0 | 3.51.1713.0
odbctrac.dll | 3.510.3002.13 | 3.510.3002.23 | 3.510.3002.23
oddbse32.dll | 3.51.1713.0 | 3.51.1713.0 | 3.51.1713.0
odexl32.dll | 3.51.1713.0 | 3.51.1713.0 | 3.51.1713.0
odfox32.dll | 3.51.1713.0 | 3.51.1713.0 | 3.51.1713.0
odpdx32.dll | 3.51.1713.0 | 3.51.1713.0 | 3.51.1713.0
odtext32.dll | 3.51.1713.0 | 3.51.1713.0 | 3.51.1713.0
olapcnsl.exe | No Version Info |  |
oleaut32.dll | 2.30.4261.1 | 2.30.4261.1 | 2.30.4265.1
oledb32.dll | 2.0.1706.0 | 2.0.1706.0 | 2.0.1706.0
oledb32r.dll | 2.0.1706.0 | 2.0.1706.0 | 2.0.1706.0
oledb32x.dll | 2.0.1706.0 | 2.0.1706.0 | 2.0.1706.0
olepro32.dll | 5.0.4261.1 | 5.0.4261.1 | 5.0.4265.1
sampprov.dll | 2.0.2905.0 |  |
selfreg.dll | 8.0.0.3214 | 8.0.0.3214 | 8.0.0.3214
sqloledb.dll | 7.0.5.3 |  | 7.1.6.23
sqlsrv32.dll | 3.60.3.19 | 3.60.3.19 | 3.60.3.19
sqlstr.dll | 1998.5.29.0 |  |
sqlwid.dll | 1998.5.29.0 |  |
sqlwoa.dll | 1998.5.29.0 |  |
stdole2.tlb | 2.30.4261.1 | 2.30.4261.1 | 2.30.4265.1
vbajet32.dll | 6.0.1.8167 | 6.0.1.8167 | 6.0.1.8167
vbar332.dll | 3.0.0.6908 | 3.0.0.6908 | 3.0.0.6908
vfpodbc.dll | 6.0.8167.0 | 6.0.8167.0 | 6.0.8167.0
windbver.exe | 1997.8.11.0 | 1997.8.11.0 | 1997.8.11.0

MDAC 2.0 Release Notes

ODBC 3.51 was tested and supported on Microsoft Windows NT 3.51 and 4.0 and on Microsoft Windows® 95. OLE DB 2.0, ADO 2.0, and RDS 2.0 were tested and supported on Windows NT 4.0 and Windows 95. However, dependencies in these three components prevent their being supported or tested on Windows NT 3.51.

The MDAC SDK was released for the Intel platform but contains Alpha platform versions of both the MDAC 2.0 redistribution program and the RDS 2.0 client components .cab file.

MDAC 2.0 installs three .cab files on your machine: Mdac11.cab, Mdac20.cab, and Mdac20_a.cab. Msadc11.cab is for older versions of Microsoft Internet Explorer and Web-based applications that need RDS 1.1 client components (which work with RDS 2.0 server components, just as they did for 1.5). Mdac20.cab and Mdac20_a.cab provide RDS 2.0 client components for Intel and Alpha platforms, respectively.

ADO MD and the underlying OLAP provider, Microsoft PivotTable® Service, are included in MDAC 2.0. ADO MD, the high-level object model for exposing multidimensional data, is contained in a single DLL (Msadomd.dll). However, the PivotTable Service shipping with the MDAC 2.0 SDK is a beta technology and should not be redistributed.

MDAC 2.0 Redistribution Notes

The MDAC SDK combines all of the run-time components, documentation, and samples and provides an integrated redistribution program in the <Drive>:\Msdasdk\Redist directory. Previously, the MDAC 1.5 stand-alone and redistribution program shipped separately, occasionally creating confusion. However, the MDAC SDK is completely self-contained and integrated. The 40-MB SDK is easier to distinguish from the 7.9-MB MDAC 2.0 redistribution program.

The MDAC 2.0 redistribution program comes in one version and installs all of the MDAC 2.0 components as well as ODBC drivers and OLE DB providers for Access, Microsoft Jet, Oracle, and SQL Server. The Microsoft Visual FoxPro® ODBC Driver is also included in the redistribution. Previously, this driver was available only by Web download.

The one MDAC 2.0 component dependency not handled by the MDAC 2.0 redistribution is for DCOM95 components. OLE DB 2.0 uses definitions found in an update to Ole32.dll that is available in the DCOM95 patch. Ole32.dll is only one DLL out of several that make up the OLE library. Simply installing the new version of this DLL does not guarantee MDAC 2.0 will work. You have to apply the entire DCOM95 patch, available from http://www.microsoft.com/com/resources/downloads.asp. The MDAC redistribution program detects when DCOM95 is not installed on a machine, in which case a dialog box instructs the user to install DCOM95.


MDAC 2.1

This Web release is the first generally available (GA) version of Microsoft® Data Access Components (MDAC) 2.1. Each version is now referenced using a version-specific notation. The current version is referred to as "MDAC 2.1.2.4202.3 (GA)."

MDAC 2.1 Release Details

There are five releases of MDAC 2.1, which are described in the following table.

Release Release date Major components
MDAC 2.10.3513.2 (SQL)

(Also known as MDAC 2.1)

 November 1998
  • ODBC 3.51
  • ODBC Drivers
    • Access
    • Excel
    • Paradox
    • Oracle
    • SQL Server
    • Text
    • Visual FoxPro
    • xBase
  • Jet ISAM Drivers
    • Excel
    • Exchange
    • Lotus 1-2-3
    • Paradox
    • Red (Jet)
    • Text
    • xBase
  • OLE DB 2.1
  • OLE DB Providers
    • ODBC
    • Oracle
    • SQL Server
  • ADO 2.1
    • ADO MD 2.1
    • ADOX 2.1 (new)
    • RDS 2.1
  • JRO 2.1 (new)
  • RDO (new)
    • RDC (new)
MDAC 2.1 SDK Updater January 1999
  • MDAC SDK
Subset of MDAC 2.1.1.3711.6 (GA)

(Also known as MDAC 2.1 SP1 Microsoft Internet Explorer 5.0)

 March 1999
  • ODBC 3.51
  • OLE DB 2.1
  • OLE DB Providers
    • ODBC
  • ADO 2.1
    • ADO MD 2.1
    • ADOX 2.1
    • RDS 2.1
  • JRO 2.1
MDAC 2.1.1.3711.11 (GA)

(Also known as MDAC 2.1 SP1)

 April 1999
  • ODBC 3.51
  • ODBC Drivers
    • Access
    • Excel
    • Paradox
    • Oracle
    • SQL Server
    • Text
    • Visual FoxPro
    • xBase
  • Jet ISAM Drivers
    • Excel
    • Exchange
    • Lotus 1-2-3
    • Paradox
    • Red (Jet)
    • Text
    • xBase
  • OLE DB 2.1
  • OLE DB Providers
    • Jet 4.0
    • ODBC
    • Oracle
    • SQL Server
  • ADO 2.1
    • ADO MD 2.1
    • ADOX 2.1
    • RDS 2.1
  • JRO 2.1
MDAC 2.1.2.4202.3 (GA)

(Also known as MDAC 2.1 SP2)

 July 1999
  • ODBC 3.51
  • ODBC Drivers
    • Access
    • Excel
    • Paradox
    • Oracle
    • SQL Server
    • Text
    • Visual FoxPro
    • xBase
  • Jet ISAM Drivers
    • Excel
    • Exchange
    • Lotus 1-2-3
    • Paradox
    • Red (Jet)
    • Text
    • xBase
  • OLE DB 2.1
  • OLE DB Providers
    • Jet 4.0
    • ODBC
    • Oracle
    • SQL Server
  • ADO 2.1
    • ADO MD 2.1
    • ADOX 2.1
    • RDS 2.1
  • JRO 2.1

MDAC 2.1 Release Manifests

Full release manifests started to accompany MDAC releases beginning with MDAC 2.1.1.3711.11 (GA). Each release manifest provides the following information:

  • Version information (release version number, date, function, ship vehicles, localized versions, new features, important notes)
  • A complete file list
  • Known issues and significant fixes

The following MDAC 2.1 release manifests are available on the Microsoft Universal Data Access Web site:

  • MDAC 2.1.1.3711.11 (GA) at http://www.microsoft.com/data/mdac21info/mdac21gamanifest.htm
  • MDAC 2.1.2.4202.3 (GA) at http://www.microsoft.com/data/mdac21info/MDAC21sp2manifest.htm

The following table lists all of the modules that were distributed with each release, not including setup modules, sample files, and SDK tools. Version numbers in boldface indicate an update or new component. Empty cells in the table indicate that module did not ship with that release of MDAC. This is not a complete list of the files that must be redistributed; see the section in this article titled Redistributing MDAC for more information.

Module | MDAC 2.10.3513.2 (SQL Server 7.0) | Subset of MDAC 2.1.1.3711.6 (GA) | MDAC 2.1.1.3711.11 (GA) | MDAC 2.1.2.4202.3 (GA)
asycfilt.dll | 2.30.4265.1 | 2.40.4275.1 | 2.40.4275.1 | 2.40.4275.1
cliconfg.dll | 1998.11.13.0 |  | 1998.11.13.0 | 1999.4.5.0
cliconfg.exe | 1998.11.13.0 |  | 1998.11.13.0 | 1999.4.5.0
dbmsadsn.dll | 1998.11.13.0 |  | 1998.11.13.0 | 1999.4.5.0
dbmsrpcn.dll | 1998.11.13.0 |  | 1998.11.13.0 | 1999.4.5.0
dbmsshrn.dll | 1998.11.13.0 |  | 1998.11.13.0 | 1999.4.5.0
dbmssocn.dll | 1998.11.13.0 |  | 1998.11.13.0 | 1999.4.5.0
dbmsspxn.dll | 1998.11.13.0 |  | 1998.11.13.0 | 1999.4.5.0
dbmsvinn.dll | 1998.11.13.0 |  | 1998.11.13.0 | 1999.4.5.0
dbnmpntw.dll | 1998.11.13.0 |  | 1998.11.13.0 | 1999.4.5.0
ds16gt.dll | 3.510.3513.0 | 3.510.3711.0 | 3.510.3711.0 | 3.510.4202.0
ds32gt.dll | 3.510.3513.0 | 3.510.3711.0 | 3.510.3711.0 | 3.510.4202.0
expsrv.dll | 6.0.0.8246 | 6.0.0.8268 | 6.0.0.8268 | 6.0.0.8268
msadce.dll | 2.10.3513.2 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.1
msadcer.dll | 2.10.3513.2 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.1
msadcf.dll | 2.10.3513.2 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.1
msadcfr.dll | 2.10.3513.2 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.1
msadco.dll | 2.10.3513.2 | 2.10.3711.1 | 2.10.3711.1 | 2.10.4202.1
msadcor.dll | 2.10.3513.2 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.1
msadcs.dll | 2.10.3513.2 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.2
msadds.dll | 2.10.3513.2 | 2.10.3711.2 | 2.10.3711.2 | 2.10.4202.1
msaddsr.dll | 2.10.3513.2 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.1
msader15.dll | 2.10.3513.2 | 2.10.3711.3 | 2.10.3711.3 | 2.10.4202.1
msado15.dll | 2.10.3513.2 | 2.10.3711.9 | 2.10.3711.9 | 2.10.4202.1
msadomd.dll | 2.10.3513.0 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.0
msador15.dll | 2.10.3513.2 | 2.10.3711.3 | 2.10.3711.3 | 2.10.4202.1
msadox.dll | 2.10.3513.0 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.0
msadrh15.dll | 2.10.3513.2 | 2.10.3711.3 | 2.10.3711.3 | 2.10.4202.1
mscpxl32.dll | 3.60.3.20 |  | 3.60.3.20 | 3.60.3.20
msdadc.dll | 2.10.3513.0 | 2.10.3711.2 | 2.10.3711.2 | 2.10.4202.0
msdaenum.dll | 2.10.3513.0 | 2.10.3711.2 | 2.10.3711.2 | 2.10.4202.0
msdaer.dll | 2.10.3513.0 | 2.10.3711.2 | 2.10.3711.2 | 2.10.4202.0
msdamg9x.dll |  |  |  | 2.50.4202.0
msdaora.dll | 2.10.3513.0 |  | 2.10.3711.0 | 2.10.4202.0
msdaosp.dll | 2.10.3513.0 |  | 2.10.3711.0 | 2.10.4202.1
msdaprsr.dll | 2.10.3513.2 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.1
msdaprst.dll | 2.10.3513.2 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.1
msdaps.dll | 2.10.3513.0 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.0
msdarem.dll | 2.10.3513.2 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.1
msdaremr.dll | 2.10.3513.2 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.1
msdasc.dll | 2.10.3513.0 | 2.10.3711.2 | 2.10.3711.2 | 2.10.4202.0
msdasql.dll | 2.10.3513.0 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.0
msdasqlr.dll | 2.10.3513.0 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.0
msdatl2.dll | 2.10.3513.0 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.0
msdatt.dll | 2.10.3513.0 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.0
msdfmap.dll | 2.10.3513.2 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.1
msexch40.dll | 4.0.2115.25 |  | 4.0.2521.8 | 4.0.2927.2
msexcl40.dll | 4.0.2115.25 |  | 4.0.2521.8 | 4.0.2927.2
msjet40.dll | 4.0.2115.25 |  | 4.0.2521.8 | 4.0.2927.2
msjetoledb40.dll |  |  | 4.0.2521.8 | 4.0.2927.2
msjint40.dll | 4.0.2115.25 |  | 4.0.2521.8 | 4.0.2927.2
msjro.dll | 2.10.3513.0 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.0
msjter40.dll | 4.0.2115.25 |  | 4.0.2521.8 | 4.0.2927.2
msjtes40.dll | 4.0.2115.25 | 4.0.2521.8 | 4.0.2521.8 | 4.0.2927.2
msltus40.dll | 4.0.2115.25 |  | 4.0.2521.8 | 4.0.2927.2
msorcl32.dll | 2.573.3513.0 |  | 2.573.3711.0 | 2.573.4202.0
mspbde40.dll | 4.0.2115.25 |  | 4.0.2521.8 | 4.0.2927.2
msrclr40.dll | 4.0.2115.25 |  | 4.0.2521.8 | 4.0.2927.2
msrd2x40.dll | 4.0.2115.25 |  | 4.0.2521.9 | 4.0.2927.2
msrd3x40.dll | 4.0.2115.25 |  | 4.0.2521.8 | 4.0.2927.2
msrdc20.ocx | 6.0.81.40 |  |  |
msrdo20.dll | 6.0.81.40 |  |  |
msrecr40.dll | 4.0.2115.25 |  | 4.0.2521.8 | 4.0.2927.2
msrepl40.dll | 4.0.2115.25 |  | 4.0.2521.9 | 4.0.2927.2
msrpjt40.dll | 4.0.2115.31 |  |  |
mstext40.dll | 4.0.2115.25 |  | 4.0.2521.8 | 4.0.2927.2
msvcirt.dll | 5.0.0.7022 |  | 6.0.8168.0 | 6.0.8168.0
msvcrt.dll | 5.0.0.7128 |  | 6.0.8397.0 | 6.0.8397.0
mswdat10.dll | 4.0.2115.30 |  | 4.0.2521.8 | 4.0.2927.2
mswstr10.dll | 4.0.2115.30 |  | 4.0.2521.8 | 4.0.2927.2
msxactps.dll | 2.10.3513.0 | 2.10.3711.0 | 2.10.3711.0 | 2.10.4202.0
msxbde40.dll | 4.0.2115.25 |  | 4.0.2521.8 | 4.0.2927.2
mtxdm.dll | 1997.11.532.0 | 1997.11.532.0 | 1997.11.532.0 | 1997.11.532.0
mtxoci.dll | 1998.2.715.0 |  | 1998.2.715.0 | 1998.2.715.0
odbc16gt.dll | 3.510.3513.0 | 3.510.3711.0 | 3.510.3711.0 | 3.510.4202.0
odbc32.dll | 3.510.3513.0 | 3.510.3711.0 | 3.510.3711.0 | 3.510.4202.0
odbc32gt.dll | 3.510.3513.0 | 3.510.3711.0 | 3.510.3711.0 | 3.510.4202.0
odbcad32.exe | 3.510.3513.0 | 3.510.3711.0 | 3.510.3711.0 | 3.510.4202.0
odbcbcp.dll | 3.70.6.23 |  | 3.70.6.23 | 3.70.6.90
odbcconf.exe | 3.60.3419.0 | 3.510.3903.0 | 3.510.3904.0 | 3.510.4002.0
odbccp32.cpl | 3.510.3513.0 | 3.510.3711.0 | 3.510.3711.0 | 3.510.4202.0
odbccp32.dll | 3.510.3513.0 | 3.510.3711.0 | 3.510.3711.0 | 3.510.4202.0
odbccr32.dll | 3.510.3513.0 | 3.510.3711.0 | 3.510.3711.0 | 3.510.4202.0
odbccu32.dll | 3.510.3513.0 | 3.510.3711.0 | 3.510.3711.0 | 3.510.4202.0
odbcint.dll | 3.510.3513.0 | 3.510.3711.0 | 3.510.3711.0 | 3.510.4202.0
odbcji32.dll | 4.0.3513.0 |  | 4.0.3711.6 | 4.0.4202.0
odbcjt32.dll | 4.0.3513.0 |  | 4.0.3711.8 | 4.0.4202.0
odbcstf.dll | 3.510.3419.8 | 3.510.3513.0 | 3.510.3904.0 | 3.510.4002.0
odbctrac.dll | 3.510.3513.0 | 3.510.3711.0 | 3.510.3711.0 | 3.510.4202.0
oddbse32.dll | 4.0.3513.0 |  | 4.0.3711.6 | 4.0.4202.0
odexl32.dll | 4.0.3513.0 |  | 4.0.3711.6 | 4.0.4202.0
odfox32.dll | 4.0.3513.0 |  | 4.0.3711.6 | 4.0.4202.0
odpdx32.dll | 4.0.3513.0 |  | 4.0.3711.6 | 4.0.4202.0
odtext32.dll | 4.0.3513.0 |  | 4.0.3711.6 | 4.0.4202.0
oleaut32.dll | 2.30.4265.1 | 2.40.4275.1 | 2.40.4275.1 | 2.40.4275.1
oledb32.dll | 2.10.3513.0 | 2.10.3711.9 | 2.10.3711.9 | 2.10.4202.0
oledb32r.dll | 2.10.3513.0 | 2.10.3711.2 | 2.10.3711.2 | 2.10.4202.0
oledb32x.dll | 2.10.3513.0 | 2.10.3711.2 | 2.10.3711.2 | 2.10.4202.0
olepro32.dll | 5.0.4265.1 | 5.0.4275.1 | 5.0.4275.1 | 5.0.4275.1
rdocurs.dll | 5.0.81.40 |  |  |
selfreg.dll | 8.0.0.3214 | 8.0.0.3214 | 8.0.0.3214 | 8.0.0.3214
sqloledb.dll | 7.1.6.23 |  | 7.1.6.23 | 7.1.6.90
sqlsrv32.dll | 3.70.6.23 |  | 3.70.6.23 | 3.70.6.90
sqlstr.dll | 1998.11.13.0 |  | 1998.11.13.0 | 1999.4.5.0
sqlwid.dll | 1998.11.13.0 |  | 1998.11.13.0 | 1999.4.5.0
sqlwoa.dll | 1998.11.13.0 |  | 1998.11.13.0 | 1999.4.5.0
vbajet32.dll | 6.0.1.8246 | 6.0.1.8268 | 6.0.1.8268 | 6.0.1.8268
vfpodbc.dll | 6.0.8281.0 |  | 6.0.8428.0 | 6.0.8440.0

MDAC 2.1 Release Notes

The initial release of Microsoft Internet Explorer 5.0, containing MDAC 2.1.1.3711.6 (IE5), might generate an application violation when an ActiveX® Data Objects (ADO) consumer application attempts to connect or to test a connection by using Microsoft Data Links. Specifically, this issue affects those OLE DB providers that do not have the optional OLEDB_SERVICES registry entry. The Microsoft OLE DB Provider for DB2 and the Microsoft OLE DB Provider for AS/400 and VSAM are adversely affected by this issue. This problem is resolved in Internet Explorer 5.0a and MDAC 2.1.1.3711.11 (GA).

MDAC 2.10.3513.2 also included the first and last release in MDAC of the Remote Data Objects (RDO) and Remote Data Control (RDC). RDO is a COM wrapper of ODBC. Microsoft Jet uses RDO in its ODBCDirect technology. RDC was a data source control that could be used in Microsoft Visual Basic and Microsoft Visual C++ to bind controls on a form or a dialog to an ODBC data source through RDO.

Internet Explorer 5.0a installs a subset of MDAC 2.1.1.3511.11. This subset is a limited client installation that contains only core ADO, OLE DB, and ODBC. It does not contain Microsoft Jet or any drivers or providers found in MDAC 2.1.1.3711.11 (GA) and later.

There are no changes in the header files in different released versions of MDAC 2.1.x. Continue using the headers from the MDAC 2.1 SDK Update and the MDAC 2.0 SDK stand-alone.


MDAC 2.5

This release marks the first time that MDAC is considered part of the Windows family of operating systems: Windows 2000, Windows NT 4.0, Windows 98, and Windows 95. MDAC 2.5 shipped as part of Windows 2000; for all earlier operating systems it is available as a download from the Microsoft Universal Data Access web site.

MDAC 2.5 Release Details

There is currently only one release of MDAC 2.5, which is described in the following table.

Release Release date Major components
MDAC 2.5 RTM (2.50.4403.12) February 2000
  • ODBC 3.52
  • ODBC Drivers
    • Access
    • Excel
    • Paradox
    • Oracle
    • SQL Server
    • Text
    • Visual FoxPro
    • xBase
  • Jet ISAM Drivers
    • Excel
    • Exchange
    • Lotus 1-2-3
    • Paradox
    • Red (Jet)
    • Text
    • xBase
  • OLE DB 2.5
  • OLE DB Providers
    • ODBC
    • Oracle
    • SQL Server
  • ADO 2.5
    • ADO MD 2.5
    • ADOX 2.5
    • RDS 2.5
  • JRO 2.5

MDAC 2.5 Release Manifest

Each release manifest provides the following information:

  • Version information (release version number, date, function, ship vehicles, localized versions, new features, important notes)
  • A complete file list
  • Known issues and significant fixes

http://www.microsoft.com/data/The MDAC 2.5 release manifest is available on the Microsoft Universal Data Access Web site:

  • MDAC 2.5 at http://www.microsoft.com/data/MDAC25info/MDAC25rtmmanifest.htm

The following table lists all of the modules that were distributed with each release, not including setup modules, sample files, and SDK tools. Version numbers in boldface indicate an update or new component. Empty cells in the table indicate that module did not ship with that release of MDAC. This is not a complete list of the files that must be redistributed; see the section in this article titled Redistributing MDAC for more information.

Module | MDAC 2.5
asycfilt.dll | 2.40.4277.1
cliconfg.dll | 1999.10.20.0
cliconfg.exe | 1999.10.20.0
dbmsadsn.dll | 1999.10.20.0
dbmsrpcn.dll | 1999.10.20.0
dbmsshrn.dll | 1999.10.20.0
dbmssocn.dll | 1999.10.20.0
dbmsspxn.dll | 1999.10.20.0
dbmsvinn.dll | 1999.10.20.0
dbnmpntw.dll | 1999.10.20.0
ds16gt.dll | 3.510.3711.0
ds32gt.dll | 3.520.4403.2
expsrv.dll | 6.0.0.8540
msadce.dll | 2.50.4403.6
msadcer.dll | 2.50.4403.6
msadcf.dll | 2.50.4403.7
msadcfr.dll | 2.50.4403.7
msadco.dll | 2.50.4403.0
msadcor.dll | 2.50.4403.0
msadcs.dll | 2.50.4403.0
msadds.dll | 2.50.4403.6
msaddsr.dll | 2.50.4403.6
msader15.dll | 2.50.4403.4
msado15.dll | 2.50.4403.9
msado20.tlb | 2.50.4403.4
msado21.tlb | 2.50.4403.4
msadomd.dll | 2.50.4403.4
msador15.dll | 2.50.4403.4
msadox.dll | 2.50.4403.4
msadrh15.dll | 2.50.4403.4
mscpxl32.dll | 3.60.3.20
msdadc.dll | 2.50.4403.0
msdaenum.dll | 2.50.4403.0
msdaer.dll | 2.50.4403.0
msdamg9x.dll | 2.50.4403.6
msdaora.dll | 2.50.4403.0
msdaosp.dll | 2.50.4403.0
msdaprsr.dll | 2.50.4403.6
msdaprst.dll | 2.50.4403.6
msdaps.dll | 2.50.4403.2
msdarem.dll | 2.50.4403.6
msdaremr.dll | 2.50.4403.6
msdart32.dll | 2.50.4403.0
msdasc.dll | 2.50.4403.0
msdasql.dll | 2.50.4403.6
msdasqlr.dll | 2.50.4403.6
msdatl2.dll | 2.50.4403.6
msdatsrc.tlb | 9.0.4403.6
msdatt.dll | 2.50.4403.6
msdaurl.dll | 9.1.4403.0
msdfmap.dll | 2.50.4403.0
msexch40.dll | 4.0.2927.2
msexcl40.dll | 4.0.2927.9
msjet40.dll | 4.0.2927.4
msjetoledb40.dll | 4.0.2927.2
msjint40.dll | 4.0.2927.2
msjro.dll | 2.50.4403.4
msjter40.dll | 4.0.2927.2
msjtes40.dll | 4.0.2927.8
msltus40.dll | 4.0.2927.9
msorcl32.dll | 2.573.4403.0
mspbde40.dll | 4.0.2927.2
msrd2x40.dll | 4.0.2927.2
msrd3x40.dll | 4.0.2927.4
msrepl40.dll | 4.0.2927.2
mstext40.dll | 4.0.2927.11
msvcirt.dll | 6.0.8168.0
msvcrt.dll | 6.0.8397.0
mswdat10.dll | 4.0.2927.10
mswstr10.dll | 4.0.2927.10
msxactps.dll | 2.50.4403.0
msxbde40.dll | 4.0.2927.2
mtxdm.dll | 1999.6.902.0
mtxoci.dll | 1999.6.854.0
odbc16gt.dll | 3.510.3711.0
odbc32.dll | 3.520.4403.2
odbc32gt.dll | 3.520.4403.2
odbcad32.exe | 3.520.4403.2
odbcbcp.dll | 3.70.8.20
odbcconf.dll | 3.520.4403.2
odbcconf.exe | 3.520.4403.6
odbccp32.cpl | 3.520.4403.2
odbccp32.dll | 3.520.4403.2
odbccr32.dll | 3.520.4403.2
odbccu32.dll | 3.520.4403.2
odbcint.dll | 3.520.4403.2
odbcji32.dll | 4.0.4403.2
odbcjt32.dll | 4.0.4403.2
odbctrac.dll | 3.520.4403.2
oddbse32.dll | 4.0.4403.2
odexl32.dll | 4.0.4403.2
odfox32.dll | 4.0.4403.2
odpdx32.dll | 4.0.4403.2
odtext32.dll | 4.0.4403.2
oleaut32.dll | 2.40.4277.1
oledb32.dll | 2.50.4403.8
oledb32r.dll | 2.50.4403.2
oledb32x.dll | 2.50.4403.0
olepro32.dll | 5.0.4277.1
sqloledb.dll | 7.1.8.19
sqlsrv32.dll | 3.70.8.20
sqlstr.dll | 1999.10.20.0
sqlwid.dll | 1999.10.20.0
sqlwoa.dll | 1999.10.20.0
stdole2.tlb | 2.40.4277.1
vbajet32.dll | 6.0.1.8268
vfpodbc.dll | 6.1.8629.1

1 The versions of ds16gt.dll and odbc16gt.dll that shipped with MDAC 2.5 are earlier than the versions that shipped with MDAC 2.1.2.4202.3 (GA). However, no significant changes have been made to those DLLs between these versions.

MDAC 2.5 Release Notes

The Web release is the first release of MDAC 2.5 RTM. This download installs the same version of the MDAC components that are installed by Windows 2000. There is no MDAC 2.5 version for Alpha machines. Alpha installations should continue using MDAC 2.1.2.4202.3 (GA).


MDAC Release Timeline

The following table lists each important MDAC release date, including minor version releases, in chronological order.

Release | Date | Primary ship vehicle
OLE DB 1.0 SDK | August 1996 | Stand-alone product
OLE DB 1.1 SDK | December 1996 | Stand-alone product Microsoft® Visual Studio® 97 development environment
MDAC 1.5 (IE) | September 1997 | Microsoft Internet Explorer 4.0 Microsoft Internet Client SDK 4.0
MDAC 1.5 (PDC) | September 1997 | Microsoft 1997 Professional Developers Conference CD
MDAC 1.5a | November 1997 | Microsoft Universal Data Access Web site
MDAC 1.5b | November 1997 | Microsoft Windows NT® 4.0 Option Pack
MDAC 1.5c | December 1997 | Microsoft Universal Data Access Web site
MDAC 1.5d | March 1998 | Microsoft Windows® 98 Microsoft Internet Explorer 4.01 Service Pack 1
MDAC 2.0.3002.20 (GA) (also known as MDAC 2.0) | July 1998 | Microsoft Universal Data Access Web site
MDAC 2.0.3002.23 (Windows NT 4 SP4) (also known as MDAC 2.0 SP1) | October 1998 | Microsoft Windows NT 4.0 SP4
MDAC 2.10.3513.2 (SQL) (also known as MDAC 2.1) | November 1998 | Microsoft SQL Server 7.0 Microsoft SQL Server 6.5 Service Pack 5
MDAC 2.0.3002.28 (GA) (also known as MDAC 2.0 SP2) | January 1999 | Microsoft Universal Data Access Web site
MDAC 2.1 SDK Updater | January 1999 | Microsoft Universal Data Access Web site
Subset of MDAC 2.1.1.3711.6 (GA) (also known as MDAC 2.1 Internet Explorer 5 SP1) | March 15, 1999 | Microsoft Internet Explorer 5.0
MDAC 2.1.1.3711.11 (GA) (also known as MDAC 2.1 SP1) | April 1, 1999 | Microsoft Universal Data Access Web site Microsoft Office 2000 Microsoft Back Office 4.5 Microsoft Visual Studio 98 Service Pack 3 Microsoft Internet Explorer 5.0a
MDAC 2.1.2.4202.3 (GA) (also known as MDAC 2.1 SP2) | June 30, 1999 | Microsoft Universal Data Access Web site
MDAC 2.5 RTM (2.50.4403.12) | February 17, 2000 | Microsoft Universal Data Access Web site Microsoft Windows 2000


Redistributing MDAC

Each release of MDAC includes a matched set of modules. These modules were tested together when they were released by Microsoft. The interdependencies of the modules are not simple; they must all be distributed as a set. Although it's tempting to try to distribute only the modules for the components that you think your application may need, there is no supported way to do that. Other applications will use MDAC components, and if they aren't part of the same released set, the applications will probably crash.

Note Each release of MDAC includes a license agreement, which gives you specific rights to redistribute some of the files in MDAC. This article does not change the terms of the license agreements in any way.

The current version of MDAC includes a redistributable MDAC setup, in the file mdac_typ.exe. This program is a self-extracting compressed file that installs the run-time components onto a workstation.

The ADO components (including RDS) and some OLE DB modules require administrator privileges on Microsoft Windows NT® and Microsoft Windows® 2000 to install and register correctly.

As of MDAC 2.1 and later, the stand-alone and the redistributable MDAC setup programs will not update files on a Windows 2000 system.

For MDAC 2.5, the redistributable MDAC setup program was designed to install on these platforms:

  • Windows 95 with DCOM95 installed (see the Knowledge Base article "Q191094: MDAC 2.0 Requires DCOM95 to Install Under Windows 95"
  • Windows 98 (all editions)
  • Windows NT 4.0 with Internet Explorer 4.01 or later installed

See the MDAC 2.5 Release Manifest at the Microsoft Universal Data Access Web site (http://www.microsoft.com/data) for more information on redistributing MDAC 2.5.

Avoiding a Restart

The redistributable MDAC setup will sometimes require the workstation to be restarted to complete its installation. In some situations, however, it may be possible to avoid restarting the workstation. Shutting down applications and services that are using MDAC components will minimize the likelihood that the redistributable setup will try to restart the workstation to complete the installation.

The procedures for shutting down applications are generally obvious and simple. However, shutting down system services, which are applications that run in a special context for the operating system, is not as obvious or intuitive. Use the Services applet in the Windows Control Panel to shut down running services. There are several services that may use MDAC components and should be stopped in order to install the MDAC components without restarting the workstation. For example, Internet Information Services (IIS), Component Services (formerly Microsoft Transaction Services MTS), and Microsoft® SQL Server™ are all services that may use MDAC components.

If the redistributable MDAC setup is being run as part of a larger installation, see the section below titled "Silent Setup," which shows the command switches used to prevent the MDAC setup from restarting the workstation immediately. Note that this does not eliminate the potential need to restart the workstation; if you do prevent the MDAC setup from restarting the workstation, you should assume that the installation will not be complete until the workstation is restarted.

MDAC setup has several modes to customize installation.

Standard Setup

Standard setup is the recommended mode. This mode emphasizes the fact that the user's system is being upgraded and offers the option to abort the installation if necessary. A setup may also involve restarting the machine, and this method gives users maximum control over the timing of the restart.

To run MDAC setup in standard mode, simply execute mdac_typ.exe with no command-line parameters. This will bring up all necessary user interface dialogs to allow the user to install MDAC.

MDAC setup will extract all of the files in mdac_typ.exe to the user's TEMP directory. From there, it will run the setup programs setup.exe and acmsetup.exe. The setup program will display the license agreement, which the user must accept in order to proceed. Then the user will have two options: either select a complete installation of MDAC or exit the setup program. After the files are copied, the user will be notified if the workstation must be restarted. The user can choose to restart immediately or wait until later.

Automatic Setup

This mode completely installs MDAC with no prompting. To run MDAC setup in automatic mode, use the following syntax at the command prompt:

   mdac_typ /Q

MDAC setup will perform a complete installation and will restart the computer if necessary. It will also create a detailed log file, mdacset.log, in the Windows root directory.

This is not a "silent" setup. The user will not have to accept the license agreement, but the user will see windows showing that files are being copied onto the workstation.

This mode is most appropriate when MDAC is being installed as part of a setup program for a larger application.

File Extraction

In this mode, MDAC setup merely extracts all of the files in mdac_typ.exe to a folder and then exits. It does not install MDAC. To run MDAC setup in file extraction mode, use the following syntax at the command prompt:

   mdac_typ /C /T:<full path>

For example, to extract all of the files in mdac_typ.exe to the folder "C:\MDAC FILES", use the following command:

   mdac_typ /C /T:"C:\MDAC FILES"

You do not have to create the folder before extracting files to it. MDAC setup will create the folder for you if it doesn't exist.

This is also not a "silent" setup; the user will have to accept the license agreement to extract the files. Once accepted, all of the files will be copied to the folder provided. The user will see a window indicating that the files are being copied. After the files are all copied, no further user interaction will be required.

Logging

In the standard mode, MDAC setup creates a summary log. This log is not as detailed as the log created in the automatic mode. To create a detailed log in standard mode, use the following syntax at the command prompt:

   mdac_typ /C:"setup /GC <file name>"

For example, to create a detailed log in the file "C:\MDAC_LOG.TXT", use the following command:

   mdac_typ /C:"setup /GC C:\MDAC_LOG.TXT"

Silent Setup

This mode installs MDAC with almost no visible user interface at all. This mode should be used only as part of a larger application setup program that can handle any problems that may arise during installation. To run MDAC setup in silent mode, use the following command:

   mdac_typ /Q:A /C:"setup /QNT"

If you need to prevent a the MDAC setup from restarting the workstation, use this command:

   mdac_typ /Q:A /C:"setup /QN1"

This will completely install MDAC but will not restart the workstation. A restart may be required to complete the installation, so the larger application setup program should restart when it has completed its setup.

The Knowledge Base article "Q192009 - HOWTO: Invoke Silent MDAC or DA SDK 2.0 Redistribution" also has information about options you can use with the MDAC setup program.

Manual Setup

Microsoft does not recommend or support manual setup of the MDAC files. Use only the redistributable program mdac_typ.exe to distribute MDAC, in accordance with the license agreement.

ODBC Notes

In general, it is safe to mix and match ODBC drivers with ODBC Driver Manager/Core Components. You should make sure the ODBC Driver Manager is more recent than the ODBC drivers, even though the product group attempts to ensure that the ODBC Driver Manager and Microsoft ODBC drivers are backward compatible.


Troubleshooting

When you have trouble with MDAC technologies, try the following resources to get help before calling Microsoft Product Support Services.

  • MDAC Technical Articles You can find a number of technical articles pertaining to MDAC in the Platform SDK.
  • Web Sites The Universal Data Access Web site and MSDN Library provide articles and technical information that may help you.
  • Microsoft Knowledge Base The articles in this database provide specific solutions, workarounds, and pertinent information about the Microsoft Data Access Components.
  • Public newsgroups: microsoft.public.data.ado and others.


Getting Help from Microsoft Product Support Services

If you can't find the answers you need in the Microsoft Data Access Components (MDAC) documentation, you can resolve problems quickly by accessing the Microsoft Product Support Services Web site at http://www.microsoft.com/support.

For additional technical resources and information about developing with Microsoft products and technologies, visit MSDN Online at http://msdn.microsoft.com.


Do You Have Feedback About MDAC Documentation?

The MDAC documentation team welcomes your suggestions and comments about our documentation and samples. You can send feedback quickly and directly to us via e-mail or the Universal Data Access Web site. Many of your suggestions have been implemented in this release of the documentation.

Note If you need technical support, see "Getting Help from Microsoft Product Support Services." The MDAC documentation team cannot answer technical support questions, but we welcome your feedback concerning the presentation, accuracy, and thoroughness of the documentation, as well as your ideas for improving it.

E-mail

The MDAC documentation now includes a Documentation Feedback button at the top of every topic. Clicking this button offers an easy way to send e-mail feedback to the MDAC documentation team. Addresses for each section of the MDAC documentation also appear below.

Documentation | Address
MDAC Overview | mdacdoc@microsoft.com
ADO | adodoc@microsoft.com
OLE DB and providers | oledbdoc@microsoft.com
ODBC and drivers | odbcdoc@microsoft.com
MDAC technical articles | datech@microsoft.com
MDAC samples | dacode@microsoft.com

Universal Data Access Web site

For product or documentation feedback, use the form at the Universal Data Access Web site. This form is specifically intended for feedback about the Web site, but any documentation and samples issues will be forwarded to the appropriate team.


MDAC Copyright and Legal Information

Information in this document is subject to change without notice. The names of companies, products, people, characters, and/or data mentioned herein are fictitious and are in no way intended to represent any real individual, company, product, or event, unless otherwise noted. Complying with all applicable copyright laws is the responsibility of the user. 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. If, however, your only means of access is electronic, permission to print one copy is hereby granted.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property.

Microsoft, ActiveX, BackOffice, Developer Studio, FoxPro, JScript, Microsoft Press, MSDN, MS-DOS, PivotTable, Visual Basic, Visual C++, Visual InterDev, Visual J++, Visual Studio, Win32, Windows, and Windows NT are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries.

Apple and Macintosh are registered trademarks of Apple Computer, Inc.

Intel is a registered trademark of Intel Corporation.

Other product and company names mentioned herein may be the trademarks of their respective owners.


Microsoft Active X Data Objects (ADO) Start Page

ActiveX Data Objects Start Page

ADO
Version 2.6

Purpose

Microsoft® ActiveX® Data Objects (ADO) enable your client applications to access and manipulate data from a database server through an OLE DB provider. Its primary benefits are ease of use, high speed, low memory overhead, and a small disk footprint. ADO supports key features for building client/server and Web-based applications.

RDS

ADO also features Remote Data Service (RDS), by which you can move data from a server to a client application or Web page, manipulate the data on the client, and return updates to the server in a single round trip.

ADO MD

Microsoft ActiveX Data Objects (Multidimensional) (ADO MD) provides easy access to multidimensional data from languages such as Microsoft Visual Basic®, Microsoft Visual C++®, and Microsoft Visual J++®. ADO MD extends Microsoft ActiveX Data Objects (ADO) to include objects specific to multidimensional data, such as the CubeDef and Cellset objects. With ADO MD you can browse multidimensional schema, query a cube, and retrieve the results.

Like ADO, ADO MD uses an underlying OLE DB provider to gain access to data. To work with ADO MD, the provider must be a multidimensional data provider (MDP) as defined by the OLE DB for OLAP specification. MDPs present data in multidimensional views as opposed to tabular data providers (TDPs) that present data in tabular views. Refer to the documentation for your OLAP OLE DB provider for more detailed information on the specific syntax and behaviors supported by your provider.

ADOX

Microsoft ActiveX Data Objects Extensions for Data Definition Language and Security (ADOX) is an extension to the ADO objects and programming model. ADOX includes objects for schema creation and modification, as well as security. Because it is an object-based approach to schema manipulation, you can write code that will work against various data sources regardless of differences in their native syntaxes.

ADOX is a companion library to the core ADO objects. It exposes additional objects for creating, modifying, and deleting schema objects, such as tables and procedures. It also includes security objects to maintain users and groups and to grant and revoke permissions on objects.

Main Components of ADO 2.6

Programmer's Guide

An introduction to using ADO, RDS, ADO MD, and ADOX.

Programmer's Reference

This section of the ADO documentation contains topics for each ADO, RDS, ADO MD, and ADOX object, collection, property, dynamic property, method, event, and enumeration.

Samples

Sample applications and code examples written in Visual Basic, Visual Basic Scripting Edition, Visual C++, and Visual J++.

Feedback

You can send feedback about ADO documentation or samples directly to the ADO documentation team.

See Also

  • ADO Technical Articles
  • JRO
  • OLE DB


ADO Programmer's Guide

ADO Programmer's Guide

For an introduction to the Microsoft® ActiveX® Data Objects (ADO) Programmer's Guide, see the following topics:

  • Introduction
  • What's New in ADO
  • Prerequisites
  • The ADO Family of Libraries
  • The Role of ADO in Universal Data Access
  • Referencing the ADO Libraries
  • Installing and Redistributing ADO
  • ADO Task Table
  • ADO Technology Table


Introduction

The ADO Programmer's Guide has been created to assist developers who are new to ADO by giving them a thorough introduction to the technology. This guide describes the libraries of the ADO family and uses sample code in a variety of languages to explain how to use the libraries, best practices for using the libraries, and tips and tricks for maximizing the performance of your ADO application.

The ADO Programmer's Guide contains the following sections and chapters:

  • Section I: ActiveX Data Objects (ADO)
    • Chapter 1: ADO Fundamentals
    • Chapter 2: Getting Data
    • Chapter 3: Examining Data
    • Chapter 4: Editing Data
    • Chapter 5: Updating and Persisting Data
    • Chapter 6: Error Handling
    • Chapter 7: Handling ADO Events
    • Chapter 8: Understanding Cursors and Locks
    • Chapter 9: Data Shaping
    • Chapter 10: Records and Streams
  • Section II: Remote Data Service (RDS)
    • Chapter 11: RDS Fundamentals
    • Chapter 12: RDS Tutorial
    • Chapter 13: RDS Usage and Security
  • Section III: ActiveX Data Objects (Multidimensional) (ADO MD)
    • Chapter 14: ADO MD Fundamentals
  • Section IV: ActiveX Data Objects Extensions for Data Definition Language and Security (ADOX)
    • Chapter 15: ADOX Fundamentals
  • Section V: Appendixes
    • Appendix A: Providers
    • Appendix B: ADO Errors
    • Appendix C: Programming with ADO
    • Appendix D: ADO Samples
  • ADO Glossary


What's New in ADO

The following new features and enhanced documentation are included in the ADO 2.6 release. This list covers ADO, ADO MD, and ADOX.

New Feature | Description
ADO MD CubeDef.GetSchemaObject Method | ADO MD objects can now be retrieved using unique names, as specified by the UniqueName property. The names of parent objects do not need to be known, and parent collections do not need to be populated to retrieve a schema object. See GetSchemaObject Method.
ADOX Group and User Properties | ADOX Group and User objects now surface a Properties collection, which can be populated with provider-specific properties. Specifically, the OLEDB:Trustee PIN property can be specified.
Command Streams | The Command object supports commands in stream format as an alternative to using the CommandText property. The CommandStream property can be used to specify XML Templates or updategrams as the Command input with the Microsoft OLE DB Provider for SQL Server.
Dialect Property | Dialect is a new property that defines the syntax and general rules that the provider uses to parse the string or stream.
Execute Method (ADO Command) | The Execute method of the Command object has been enhanced to use streams for input and output.
Field Status Values | If the user encounters a DB_E_ERRORSOCCURRED error when modifying a Field of a Recordset, ADO will now fill the Field.Status property with the appropriate status information so that the user will have more information about what went wrong. See Status Property (ADO Field).
NamedParameters Property | NamedParameters is a new property of the Command object that indicates that the provider should use named parameters.
Resultsets in Streams | ADO can return resultsets from a data source in a Stream, rather than a Recordset object. Using the latest version of the Microsoft OLE DB Provider for SQL Server, you can get XML results from the provider by executing a "For XML" query. A Stream that receives the resultset can be opened with a "For XML" command as the source. See Retrieving Resultsets Into Streams.
Single Row Resultset | The ADO Record object can now be opened on a command string or Command object that returns one row of data from the provider. This results in improved performance with MDAC 2.6 providers. See Open Method (ADO Record).

Enhanced Programmer's Guide Documentation

ADO Programmer's Guide

Conceptual and tutorial topics for ADO have been written or reorganized into a comprehensive ADO developer's guide.

ADO Code Examples

The examples have been expanded to contain additional examples written in VBScript and JScript.

ADO Samples

The ADO samples have been revised and new samples have been added.


Prerequisites

The ADO Programmer's Guide will prove useful to developers with a wide variety of backgrounds. At a minimum, readers should have an intermediate level of experience in developing applications with Microsoft® Visual Basic, because most of the examples in the guide are written in this language. Other examples are written in Microsoft® Visual C++®; Java; Visual Basic, Scripting Edition (VBScript); and Microsoft® JScript®.

Because ADO is used for accessing data from a variety of sources, readers might also need some understanding of fundamental relational database management system concepts, online analytical processing (OLAP) concepts, and basic familiarity with the Internet and Internet protocols.

ADO is a part of the Microsoft Universal Data Access (UDA) strategy. (For more information about UDA, see "The Role of ADO in Universal Data Access," later in this chapter.) As such, it interoperates with the OLE DB technology. OLE DB is based on the Microsoft Component Object Model (COM). Therefore, familiarity with COM can also be useful for understanding some of the more advanced concepts in ADO.


The ADO Family of Libraries

Three major libraries make up the ADO family: ADO (including RDS), ADO MD, and ADOX.

ADO

ADO enables your client applications to access and manipulate data from a database server through an OLE DB provider. The primary benefits of ADO are ease of use, high speed, low memory overhead, and a small disk footprint. ADO supports key features for building client/server and Web-based applications.

ADO also features Remote Data Service (RDS), by which you can move data from a server to a client application or Web page, manipulate the data on the client, and return updates to the server in a single round trip.

ADO MD

Microsoft ActiveX Data Objects (Multidimensional) (ADO MD) provides easy access to multidimensional data from languages such as Microsoft Visual Basic, Microsoft Visual C++, and Microsoft Visual J++™. ADO MD extends ADO to include objects specific to multidimensional data, such as the CubeDef and Cellset objects. With ADO MD you can browse multidimensional schema, query a cube, and retrieve the results.

Like ADO, ADO MD uses an underlying OLE DB provider to gain access to data. To work with ADO MD, the provider must be a multidimensional data provider (MDP) as defined by the OLE DB for OLAP specification. MDPs present data in multidimensional views as opposed to tabular data providers (TDPs) that present data in tabular views. Refer to the documentation for your OLE DB for OLAP provider for more detailed information about the specific syntax and behaviors supported by your provider.

ADOX

Microsoft ActiveX Data Objects Extensions for Data Definition Language and Security (ADOX) is an extension to the ADO objects and programming model. ADOX includes objects for schema creation and modification as well as security. Because it is an object-based approach to schema manipulation, you can write code that will work against various data sources regardless of differences in their native syntaxes.

ADOX is a companion library to the core ADO objects. It exposes additional objects for creating, modifying, and deleting schema objects, such as tables and procedures. It also includes security objects to maintain users and groups, and to grant and revoke permissions on objects.

See Also

Section I: ActiveX Data Objects (ADO) | Section II: Remote Data Service (RDS) | Section III: ADO (Multidimensional) (ADO MD) | Section IV: ADO Extensions for Data Definition Language and Security (ADOX)


The Role of ADO in Universal Data Access

Universal Data Access is Microsoft's strategy for providing access to information across the enterprise. The basic idea behind UDA is to enable you to efficiently access any data from where it is normally found, rather than moving the data to large, centralized data stores.

UDA is independent of data stores, tools, and languages. It provides a high-level, easy-to-use interface, and a low-level, high-performance interface to practically any data store available. You can use this flexibility to integrate diverse data stores and use your choice of tools, applications, and platform services to create the right solutions for your needs.

The Microsoft Data Access Components (MDAC) comprise the technologies that make UDA work. These technologies provide the basic framework for general-purpose data access in Microsoft® Windows® operating systems.

There are three primary technologies in MDAC. ActiveX Data Objects (ADO) is a high-level, easy-to-use interface to OLE DB. OLE DB is a low-level, high-performance interface to a variety of data stores. ADO and OLE DB both can work with relational (tabular) and nonrelational (hierarchical or stream) data. Finally, Open Database Connectivity (ODBC) is another low-level, high-performance interface that is designed specifically for relational data stores.

ADO provides a layer of abstraction between your client or middle-tier application and the low-level OLE DB interfaces. ADO uses a small set of Automation objects to provide a simple and efficient interface to OLE DB. This interface makes ADO the perfect choice for developers in higher level languages, such as Visual Basic and even VBScript, who want to access data without having to learn the intricacies of COM and OLE DB.


Referencing the ADO Libraries

As with any library that provides a type library, you must set a reference to the ADO library you want to use in your Visual Basic project. The type library for the current version of ADO is contained within the ADO DLL, msado15.dll. (To meet the needs of certain applications written with older versions, older versions of the type library can be referenced as well through the provided msado20.tlb, msado21.tlb, and msado25.tlb files.)

To reference the ADO library in your Visual Basic project, open the Project menu and click References. Scroll down and select Microsoft ActiveX Data Objects 2.6 Library. If an application compiled on a system with ADO 2.6 must run on a computer with a previous version of ADO installed, it needs to use one of the backwards compatibility type libraries. Add a reference to the appropriate .tlb file instead of to msado15.dll.


Installing and Redistributing ADO

Beginning with Windows 2000, MDAC is now an integrated part of the operating system. Future OS releases will contain MDAC components. The MDAC SDK can be found within the Platform SDK. The Microsoft Data Access Components redistribution in English and 18 other languages is available for download on the Microsoft Universal Data Access Web site.

To update Windows 95, Windows 98, Windows NT® 4.0, Windows 2000®, or Windows® Millennium Edition to MDAC 2.6, the redistributable setup program Mdac_typ.exe is available on the UDA Web site. MDAC 2.6 is also installed as part of SQL Server 2000. The Mdac_typ.exe redistributable file is available on the SQL Server 2000 CD. This program installs all the files that your applications need to use the technologies in MDAC. The redistributable program does not offer an option to selectively install components; it installs all of them together or none at all. This way, all of the components are installed in the same configuration in which they were tested before leaving Microsoft, which decreases problems with mismatched versions of DLLs.

MDAC is a system component, and installing MDAC should be considered similar to a system upgrade. You cannot remove MDAC without replacing operating system files that were upgraded by the MDAC installer. Many applications, including Microsoft Internet Explorer, Visual Studio®, SQL Server, and Office require MDAC for data binding and other application functions. Removing MDAC will negatively impact or break these applications. However, under certain circumstances, Microsoft Product Support Services can assist you in solving installation and behavior problems. For more information, see Installing MDAC Q&A at the Microsoft Universal Data Access site.


ADO Task Table

The following table lists programming tasks contained in the ADO Programmer's Guide and provides references for each task. These references can be textual descriptions or code examples in which you can find information about the ADO feature that performs the task.

ADO Task | References
Connecting to a data provider | Making a Connection
Executing commands or calling stored procedures | Using the Command Object
Opening a Recordset | The Recordset Object Open Method
Determining the size of a Recordset | Counting Rows and The Limits of a Recordset
Moving to a specific record | Navigating Through the Data
Accessing column values | The Fields Collection
Searching for data | Finding a Specific Record
Modifying data and changing values | Editing Existing Records
Adding new data | Adding Records
Deleting or removing data | Deleting Records Using the Delete Method
Posting changes to the data source | Updating Data
Beginning, committing, and rolling back transactions | Transaction Processing
Saving records to a file (XML or binary) | Persisting Data
Handling errors | ADO Errors
Handling events, asynchronous programming | ADO Event Handler Summary
Choosing cursor location and type | Types of Cursors
Choosing lock types | Types of Locks
Returning related records in a Recordset | Data Shaping Summary
Accessing semi-structured data | Chapter 10: Records and Streams
Using XML for queries or command input | Command Streams
Returning XML data from a provider | Retrieving Resultsets into Streams
Publishing to IIS | Using ADO for Internet Publishing


ADO Technology Table

The following table lists the Microsoft products, tools, and technologies discussed in the ADO Programmer's Guide. It provides links, wherever possible, to relevant topics in the guide.

Microsoft Product or Technology | References
Access/Jet | Finding a Specific Record Chapter 15: ADOX Fundamentals Microsoft Jet and Replication Objects OLE DB Provider for Microsoft Jet
Active Directory Service Interfaces | Microsoft OLE DB Provider for Microsoft Active Directory Service
COM/DCOM | Prerequisites Marking Business Objects as Safe for Scripting Registering Business Objects on the Client for Use with DCOM Setting DCOM Stream Marshaling Format Enabling a DLL to Run on DCOM Running Business Objects in Component Services
FrontPage® | Microsoft OLE DB Provider for Internet Publishing
Indexing Service | Microsoft OLE DB Provider for Microsoft Indexing Service
Internet Explorer | Internet Explorer Error Codes
Internet Information Services | Chapter 10: Records and Streams Streams and Persistence Command Streams Using ADO for Internet Publishing Solutions for Remote Data Access RDS Scenario Configuring Virtual Servers on IIS Specifying Threads Per Processor on IIS Securing RDS Applications "Internet Server Error: Access Denied" Microsoft OLE DB Provider for Internet Publishing Internet Information Services Error Codes
JScript | Handling Errors in Other Languages JScript ADO Programming ADO Code Examples in Microsoft JScript
ODBC | The Role of ADO in Universal Data Access Using the Connection Object Using RDS with ODBC Connection Pooling Microsoft OLE DB Provider for ODBC
OLE DB | The Role of ADO in Universal Data Access OLE DB Providers Appendix A: Providers Provider Errors
Oracle | Microsoft OLE DB Provider for Oracle
SQL Server 2000 | Controlling Transactions Calling a Stored Procedure with a Command Counting Rows Forward-Only Cursors Command Streams Retrieving Resultsets into Streams Running the Address Book SQL Script Ensuring Sufficient TempDB Space Minimizing Log File Space Usage Microsoft OLE DB Provider for SQL Server
Transaction Server | Running Business Objects in Component Services
VBScript | Handling Errors in Other Languages Visual Basic for Applications Functions Command Streams Solutions for Remote Data Access RDS Scenario RDS Tutorial (VBScript) VBScript ADO Programming ADO Code Examples in Microsoft Visual Basic Scripting Edition
Visual Basic | Errors ADO Errors ADO Event Instantiation By Language Visual Basic for Applications Functions Chapter 12: RDS Tutorial Using ADO with Microsoft Visual Basic ADO Code Examples in Microsoft Visual Basic
Visual C++ | Handling Errors in Other Languages ADO Event Instantiation By Language Using ADO with Microsoft Visual C++ ADO Code Examples in Microsoft Visual C++
Visual J++ | Handling Errors in Other Languages ADO Event Instantiation By Language RDS Tutorial (Visual J++) Using ADO with Microsoft Visual J++ ADO Code Examples in Microsoft Visual J++
Visual Studio | Appendix D: ADO Samples
Windows 2000 | System Requirements for the Address Book Application Granting Guest Privileges to a Web Server Computer Registering a Custom Business Object Securing RDS Applications Configuring RDS on Windows 2000
XML | Persisting Records in XML Format Chapter 10: Records and Streams Command Streams Retrieving Resultsets into Streams


Section I: ActiveX Data Objects (ADO)

This section contains the following chapters:

  • Chapter 1: ADO Fundamentals
  • Chapter 2: Getting Data
  • Chapter 3: Examining Data
  • Chapter 4: Editing Data
  • Chapter 5: Updating and Persisting Data
  • Chapter 6: Error Handling
  • Chapter 7: Handling ADO Events
  • Chapter 8: Understanding Cursors and Locks
  • Chapter 9: Data Shaping
  • Chapter 10: Records and Streams


Chapter 1: ADO Fundamentals

This chapter is an introduction to the ADO library. It discusses what you can do with ADO, reviews the objects in the ADO hierarchy, and presents a simple ADO application that uses many of the ADO objects to retrieve, edit, and update data from a data source. Finally, this chapter covers two issues that are important to understand for writing ADO applications: OLE DB providers and errors.


What You Can Do With ADO

ADO is designed to provide developers with a powerful, logical object model for programmatically accessing, editing, and updating a wide variety of data sources through OLE DB system interfaces. The most common usage of ADO is to query a table or tables in a relational database, retrieve and display the results in an application, and perhaps allow users to make and save changes to the data. Other things that can be done programmatically with ADO include:

  • Querying a database using SQL and displaying the results.
  • Accessing information in a file store over the Internet.
  • Manipulating messages and folders in an e-mail system.
  • Saving data from a database into an XML file.
  • Executing commands described with XML and retrieving an XML stream.
  • Saving data into a binary or XML stream.
  • Allowing a user to review and make changes to data in database tables.
  • Creating and reusing parameterized database commands.
  • Executing stored procedures.
  • Dynamically creating a flexible structure, called a Recordset, to hold, navigate, and manipulate data.
  • Performing transactional database operations.
  • Filtering and sorting local copies of database information based on run-time criteria.
  • Creating and manipulating hierarchical results from databases.
  • Binding database fields to data-aware components.
  • Creating remote, disconnected Recordsets.

ADO must expose a wide variety of options and settings in order to provide such flexibility. Therefore it's important to take a methodical approach to learning how to use ADO in an application, breaking down each of your goals into manageable pieces.

Four primary operations are involved in most ADO programs: getting data, examining data, editing data, and updating data. The next four chapters examine each of these operations in more detail.

Before proceeding, familiarize yourself with the objects in the ADO Object Model. Then review HelloData: A Simple ADO Application. This application is written in Visual Basic and performs each of the four primary ADO operations.


The ADO Object Model

ADO requires only nine objects and four collections to provide its entire functionality. The following table introduces them.

Object or Collection | Description
Connection object | Represents a unique session with a data source. In the case of a client/server database system, it may be equivalent to an actual network connection to the server. Depending on the functionality supported by the provider, some collections, methods, or properties of a Connection object may not be available.
Command object | Used to define a specific command, such as a SQL query, intended to run against a data source.
Recordset object | Represents the entire set of records from a base table or the results of an executed command. All Recordset objects consist of records (rows) and fields (columns).
Record object | Represents a single row of data, either from a Recordset or from the provider. This record could represent a database record or some other type of object such as a file or directory, depending upon your provider.
Stream object | Represents a stream of binary or text data. For example, an XML document can be loaded into a stream for command input or returned from certain providers as the results of a query. A Stream object can be used to manipulate fields or records containing these streams of data.
Parameter object | Represents a parameter or argument associated with a Command object, based on a parameterized query or stored procedure.
Field object | Represents a column of data with a common data type. Each Field object corresponds to a column in the Recordset.
Property object | Represents a characteristic of an ADO object that is defined by the provider. ADO objects have two types of properties: built-in and dynamic. Built-in properties are those properties implemented in ADO and immediately available to any new object. The Property object is a container for dynamic properties, defined by the underlying provider.
Error object | Contains details about data access errors that pertain to a single operation involving the provider.
Fields collection | Contains all the Field objects of a Recordset or Record object.
Properties collection | Contains all the Property objects for a specific instance of an object.
Parameters collection | Contains all the Parameter objects of a Command object.
Errors collection | Contains all the Error objects created in response to a single provider-related failure.

The following figures show the ADO objects and their collections. Click an object or collection for more information from the ADO Programmer's Reference.


HelloData: A Simple ADO Application

To lay the groundwork for an exploration of the ADO library, consider a simple ADO application called "HelloData." HelloData steps through each of the four major ADO operations (getting, examining, editing, and updating data). In order to focus on the fundamentals of ADO and prevent code clutter, minimal error handling is done in the example.

The application queries the Northwind sample database that is included with Microsoft® SQL Server 2000.

To run HelloData

  1. Create a new Standard Executable Visual Basic Project that references the ADO 2.6 library. For more information, see Referencing the ADO Libraries.
  2. Create four command buttons at the top of the form, setting the Name and Caption properties to the values shown in the table below.
  3. Below the buttons, create a Microsoft DataGrid Control (Msdatgrd.ocx). You must add the "Microsoft DataGrid Control 6.0 (SP3) (OLEDB)" to your toolbox first.
  4. Create a TextBox on the form below the grid and set its properties as shown in the table. The form should look similar to the following figure when you are finished.
  5. Finally, copy the code listed in "HelloData Code" and paste it into the code editor window of the form. Press F5 to run the code.

Note In the following example, and throughout the guide, the user id "MyId" with a password of "123aBc" is used to authenticate against the server. You should substitute these values with valid logon credentials for your server. Also, substitute the "MyServer" value with the name of your server.

For a detailed description of the code, see "HelloData Details."

Control Type | Property | Value
Form | Name | Form1
 | Height | 6500
 | Width | 6500
MS DataGrid | Name | grdDisplay1
TextBox | Name | txtDisplay1
 | Multiline | true
Command Button | Name | cmdGetData
 | Caption | Get Data
Command Button | Name | cmdExploreData
 | Caption | Examine Data
Command Button | Name | cmdEditData
 | Caption | Edit Data
Command Button | Name | cmdUpdateData
 | Caption | Update Data


Hello Data Details

ADO

HelloData Details

The HelloData application steps through the basic operations of a typical ADO application: getting, examining, editing, and updating data. When you start the application, click the first button, Get Data. This will run the GetData() subroutine.

GetData

GetData places a valid connection string into a module-level variable, m_sConnStr. For more information about connection strings, see Creating the Connection String.

Assign an error handler using a Visual Basic OnError statement. For more information about error handling in ADO, see Chapter 6: Error Handling. A new Connection object is created, and the CursorLocation property is set to adUseClient because the HelloData example creates a disconnected Recordset. This means that once the data has been fetched from the data source, the physical connection with the data source is broken, but you can still work with the data that is cached locally in your Recordset object.

After the connection has been opened, assign a SQL string to a variable (sSQL). Then instantiate a new Recordset object, m_oRecordset1. In the next line of code, open the Recordset over the existing Connection, passing in sSQL as the source of the Recordset. You assist ADO in making the determination that the SQL string you have passed as the source for the Recordset is a textual definition of a command by passing adCmdText in the final argument to the Recordset Open method. This line also sets the LockType and CursorType associated with the Recordset.

The next line of code sets the MarshalOptions property equal to adMarshalModifiedOnly. MarshalOptions indicates which records should be marshaled to the middle tier (or server). For more information about marshaling, see the COM documentation. When using adMarshalModifiedOnly with a client-side cursor (CursorLocation = adUseClient), only records that have been modified on the client are written back to the middle tier. Setting MarshalOptions to adMarshalModifiedOnly can improve performance because fewer rows are marshaled.

Next, disconnect the Recordset by setting its ActiveConnection property equal to Nothing. For more information, see Disconnecting and Reconnecting the Recordset in Chapter 5: Updating and Persisting Data.

Close the connection to the data source and destroy the existing Connection object, thereby releasing the resources it consumed.

The final step is to set the Recordset as the DataSource for the Microsoft DataBound Grid Control on the form so that you can easily display the data from the Recordset on the form.

Click the second button, Examine Data. This runs the ExamineData subroutine.

ExamineData

ExamineData uses various methods and properties of the Recordset object to display information about the data in the Recordset. It reports the number of records by using the RecordCount property. It loops through the Recordset and prints the value of the AbsolutePosition property in the display text box on the form. Also while in the loop, the value of the Bookmark property for the third record is placed into a variant variable, vBookmark, for later use.

The routine navigates directly back to the third record using the bookmark variable that it stored earlier. The routine calls the WalkFields subroutine, which loops through the Fields collection of the Recordset and displays details about each Field in the collection.

Finally, ExamineData uses the Filter property of the Recordset to screen for only those records with a CategoryId equal to 2. The result of applying this filter is immediately visible in the display grid on the form.

For more information about the functionality shown in the ExamineData subroutine, see Chapter 3: Examining Data.

Next, click the third button, Edit Data. This will run the EditData subroutine.

EditData

When the code enters the EditData subroutine, the Recordset is still filtered on CategoryId equal to 2, so only those items that meet the filter criteria are visible. It first loops through the Recordset and increases the price of each visible item in the Recordset by 10 percent. The value of the Price field is changed by setting the Value property for that field equal to a new, valid amount.

Remember that the Recordset is disconnected from the data source. The changes made in EditData are made only to the locally cached copy of the data. For more information, see Chapter 4: Editing Data.

The changes will not be made on the data source until you click the fourth button, Update Data. This will run the UpdateData subroutine.

UpdateData

UpdateData first removes the filter that has been applied to the Recordset. The code removes and resets m_oRecordset1 as the DataSource for the Microsoft Bound DataGrid on the form so that the unfiltered Recordset appears in the grid.

The code then checks to see whether you can move backward in the Recordset by using the Supports method with the adMovePrevious argument.

The routine moves to the first record using the MoveFirst method and displays the field's original and current values, using the OriginalValue and Value properties of the Field object. These properties, along with the UnderlyingValue property (not used here), are discussed in Chapter 5: Updating and Persisting Data.

Next, a new Connection object is created and used to reestablish a connection to the data source. You reconnect the Recordset to the data source by setting the new Connection as the ActiveConnection for the Recordset. To send the updates to the server, the code calls UpdateBatch on the Recordset.

If the batch update succeeds, a module-level flag variable, m_flgPriceUpdated, is set to True. This will remind you later to clean up all changes made to the database.

Finally, the code moves back to the first record in the Recordset and displays the original and current values. The values are the same after the call to UpdateBatch.

For more detailed information about updating data, including what to do when data on the server changes while your Recordset is disconnected, see Chapter 5: Updating and Persisting Data.

Form_Unload

The Form_Unload subroutine is important for several reasons. First, because this is a sample application, Form_Unload cleans up the changes made to the database before the application exits. Second, the code shows how a command can be executed directly from an open Connection object using the Execute method. Finally, it shows an example of executing a non-row–returning query (an UPDATE query) against the data source.


Hello Data Code

ADO

HelloData Code

'BeginHelloData
Option Explicit

Dim m_oRecordset1 As ADODB.Recordset
Dim m_sConnStr As String
Dim m_flgPriceUpdated As Boolean

Private Sub cmdEditData_Click()
   EditData
End Sub

Private Sub cmdExamineData_Click()
   ExamineData
End Sub

Private Sub cmdGetData_Click()
   GetData
End Sub

Private Sub cmdUpdateData_Click()
   UpdateData
End Sub

Private Sub GetData()
   Dim sSQL As String
   Dim oConnection1 As ADODB.Connection

   m_sConnStr = "Provider=SQLOLEDB;Data Source=MySrvr;" & _
               "Initial Catalog=Northwind;User Id=MyId;Password=123aBc;"

   On Error GoTo GetDataError

   ' Create and Open the Connection object.
   Set oConnection1 = New ADODB.Connection
   oConnection1.CursorLocation = adUseClient
   oConnection1.Open m_sConnStr

   sSQL = "SELECT ProductID, ProductName, CategoryID, UnitPrice " & _
            "FROM Product"

   ' Create and Open the Recordset object.
   Set m_oRecordset1 = New ADODB.Recordset
   m_oRecordset1.Open sSQL, oConnection1, adOpenStatic, _
                       adLockBatchOptimistic, adCmdText

   m_oRecordset1.MarshalOptions = adMarshalModifiedOnly

   ' Disconnect the Recordset.
   Set m_oRecordset1.ActiveConnection = Nothing
   oConnection1.Close
   Set oConnection1 = Nothing

   ' Bind Recordset to the DataGrid for display.
   Set grdDisplay1.DataSource = m_oRecordset1

   Exit Sub

GetDataError:
   If oConnection1 Is Nothing Then
      HandleErrs "GetData", m_oRecordset1.ActiveConnection
   Else
      HandleErrs "GetData", oConnection1
   End If
End Sub

Private Sub ExamineData()
   Dim iNumRecords As Integer
   Dim vBookmark As Variant

   On Err GoTo ExamineDataErr

   iNumRecords = m_oRecordset1.RecordCount

   DisplayMsg "There are " & CStr(iNumRecords) & _
               " records in the current Recordset."

   ' Loop through the Recordset and print the
   ' value of the AbsolutePosition property.
   DisplayMsg "****** Start AbsolutePosition Loop ******"

   Do While Not m_oRecordset1.EOF
      ' Store the bookmark for the 3rd record,
      ' for demo purposes.
      If m_oRecordset1.AbsolutePosition = 3 Then _
          vBookmark = m_oRecordset1.Bookmark

      DisplayMsg m_oRecordset1.AbsolutePosition

      m_oRecordset1.MoveNext
   Loop

   DisplayMsg "****** End AbsolutePosition Loop ******" & vbCrLf

   ' Use our bookmark to move back to 3rd record.
   m_oRecordset1.Bookmark = vBookmark
   MsgBox vbCr & "Moved back to position " & _
           m_oRecordset1.AbsolutePosition & " using bookmark.", , _
           "Hello Data"

   ' Display meta-data about each field. See WalkFields() sub.
   Call WalkFields

   ' Apply a filter on the type field.
   MsgBox "Filtering on type field. (CategoryID=2)", _
           vbOKOnly, "Hello Data"

   m_oRecordset1.Filter = "CategoryID=2"

   Exit Sub

ExamineDataErr:
   HandleErrs "ExamineData", m_oRecordset1.ActiveConnection
End Sub

Private Sub EditData()
   On Error GoTo EditDataErr

   'Recordset still filtered on CategoryID=2.
   'Increase price by 10% for filtered records.
   MsgBox "Increasing unit price by 10%" & vbCr & _
           "for all records with CategoryID = 2.", , "Hello Data"

   m_oRecordset1.MoveFirst

   Dim cVal As Currency
   Do While Not m_oRecordset1.EOF
      cVal = m_oRecordset1.Fields("UnitPrice").Value
      m_oRecordset1.Fields("UnitPrice").Value = (cVal * 1.1)
      m_oRecordset1.MoveNext
   Loop

   Exit Sub

EditDataErr:
   HandleErrs "EditData", m_oRecordset1.ActiveConnection
End Sub

Private Sub UpdateData()
   Dim oConnection2 As New ADODB.Connection

   On Error GoTo UpdateDataErr

   MsgBox "Removing Filter (adFilterNone).", , "Hello Data"
   m_oRecordset1.Filter = adFilterNone

   Set grdDisplay1.DataSource = Nothing
   Set grdDisplay1.DataSource = m_oRecordset1

   MsgBox "Applying Filter (adFilterPendingRecords).", , "Hello Data"
   m_oRecordset1.Filter = adFilterPendingRecords

   Set grdDisplay1.DataSource = Nothing
   Set grdDisplay1.DataSource = m_oRecordset1

   DisplayMsg "*** PRE-UpdateBatch values for 'UnitPrice' field. ***"

   ' Display Value, UnderlyingValue, and OriginalValue for
   ' type field in first record.
   If m_oRecordset1.Supports(adMovePrevious) Then
      m_oRecordset1.MoveFirst
      DisplayMsg "OriginalValue   = " & _
          m_oRecordset1.Fields("UnitPrice").OriginalValue
      DisplayMsg "Value           = " & _
          m_oRecordset1.Fields("UnitPrice").Value
   End If

   oConnection2.ConnectionString = m_sConnStr
   oConnection2.Open

   Set m_oRecordset1.ActiveConnection = oConnection2
   m_oRecordset1.UpdateBatch

   m_flgPriceUpdated = True

   DisplayMsg "*** POST-UpdateBatch values for 'UnitPrice' field ***"

   If m_oRecordset1.Supports(adMovePrevious) Then
      m_oRecordset1.MoveFirst
      DisplayMsg "OriginalValue   = " & _
          m_oRecordset1.Fields("UnitPrice").OriginalValue
      DisplayMsg "Value           = " & _
          m_oRecordset1.Fields("UnitPrice").Value
   End If

   MsgBox "See value comparisons in txtDisplay.", , _
           "Hello Data"

   Exit Sub

UpdateDataErr:
   HandleErrs "UpdateData", oConnection2
End Sub

Private Sub WalkFields()
   Dim iFldCnt As Integer
   Dim oFields As ADODB.Fields
   Dim oField As ADODB.Field
   Dim sMsg As String

   Set oFields = m_oRecordset1.Fields

   DisplayMsg "****** BEGIN FIELDS WALK ******"

   For iFldCnt = 0 To (oFields.Count - 1)
      Set oField = oFields(iFldCnt)
      sMsg = ""
      sMsg = sMsg & oField.Name
      sMsg = sMsg & vbTab & "Type: " & GetTypeAsString(oField.Type)
      sMsg = sMsg & vbTab & "Defined Size: " & oField.DefinedSize
      sMsg = sMsg & vbTab & "Actual Size: " & oField.ActualSize

      grdDisplay1.SelStartCol = iFldCnt
      grdDisplay1.SelEndCol = iFldCnt
      DisplayMsg sMsg
      MsgBox sMsg, , "Hello Data"
   Next iFldCnt

   DisplayMsg "****** END FIELDS WALK ******" & vbCrLf

   Set oField = Nothing
   Set oFields = Nothing
End Sub

Private Function GetTypeAsString(dtType As ADODB.DataTypeEnum) As String
   ' To save space, we are only checking for data types
   ' that we know are present.
   Select Case dtType
      Case adChar
         GetTypeAsString = "adChar"
      Case adVarChar
         GetTypeAsString = "adVarChar"
      Case adCurrency
         GetTypeAsString = "adCurrency"
      Case adInteger
         GetTypeAsString = "adInteger"
   End Select
End Function

Private Sub Form_Load()
   grdDisplay1.AllowAddNew = False
   grdDisplay1.AllowDelete = False
   grdDisplay1.AllowUpdate = False
   m_flgPriceUpdated = False
End Sub

Private Sub Form_Unload(Cancel As Integer)
   Dim oConnection3 As New ADODB.Connection
   Dim sSQL As String
   Dim lAffected As Long

   If Not m_oRecordset1 Is Nothing Then
      Set m_oRecordset1 = Nothing
   End If

   ' Undo the changes we've made to the database on the server.
   If m_flgPriceUpdated Then
      sSQL = "UPDATE Products SET UnitPrice=(UnitPrice/1.1) " & _
             "WHERE CategoryID=2"
      oConnection3.Open m_sConnStr
      oConnection3.Execute sSQL, lAffected, adCmdText

      MsgBox "Restored prices for " & CStr(lAffected) & _
              " records affected.", , "Hello Data"
   End If

   If oConnection3.State = adStateOpen Then
      oConnection3.Close
      Set oConnection3 = Nothing
   End If
End Sub

Private Sub HandleErrs(sSource As String, ByRef oConnection1 As ADODB.Connection)
   DisplayMsg "ADO (OLE) ERROR IN " & sSource
   DisplayMsg vbTab & "Error: " & Err.Number
   DisplayMsg vbTab & "Description: " & Err.Description
   DisplayMsg vbTab & "Source: " & Err.Source

   If Not oConnection1 Is Nothing Then
      If oConnection1.Errors.Count <> 0 Then
         DisplayMsg "PROVIDER ERROR"
         Dim oError1 As ADODB.Error
         For Each oError1 In oConnection1.Errors
            DisplayMsg vbTab & "Error: " & oError1.Number
            DisplayMsg vbTab & "Description: " & oError1.Description
            DisplayMsg vbTab & "Source: " & oError1.Source
            DisplayMsg vbTab & "Native Error:" & oError1.NativeError
            DisplayMsg vbTab & "SQL State: " & oError1.SQLState
         Next oError1
         oConnection1.Errors.Clear
         Set oError1 = Nothing
      End If
   End If

   MsgBox "Error(s) occurred. See txtDisplay1 for specific information.", , _
           "Hello Data"

   Err.Clear
End Sub

Private Sub DisplayMsg(sText As String)
   txtDisplay1.Text = (txtDisplay1.Text & vbCrLf & sText)
End Sub

Private Sub Form_Resize()
   grdDisplay1.Move 100, 700, Me.ScaleWidth - 200, (Me.ScaleHeight - 800) / 2
   txtDisplay1.Move 100, grdDisplay1.Top + grdDisplay1.Height + 100, _
                    Me.ScaleWidth - 200, (Me.ScaleHeight - 1000) / 2
End Sub

'EndHelloData


OLE DB Providers

The ADO Programmer's Guide Introduction discusses the relationship between ADO and the rest of the Microsoft Universal Data Access architecture. OLE DB defines a set of COM interfaces to provide applications with uniform access to data that is stored in diverse information sources. This approach allows a data source to share its data through the interfaces that support the amount of DBMS functionality appropriate to the data source. By design, the high-performance architecture of OLE DB is based on its use of a flexible, component-based services model. Rather than having a prescribed number of intermediary layers between the application and the data, OLE DB requires only as many components as are needed to accomplish a particular task.

For example, suppose a user wants to run a query. Consider the following scenarios:

  • The data resides in a relational database for which there currently exists an ODBC driver but no native OLE DB provider: The application uses ADO to talk to the OLE DB Provider for ODBC, which then loads the appropriate ODBC driver. The driver passes the SQL statement to the DBMS, which retrieves the data.
  • The data resides in Microsoft SQL Server for which there is a native OLE DB provider: The application uses ADO to talk directly to the OLE DB Provider for Microsoft SQL Server. No intermediaries are required.
  • The data resides in Microsoft Exchange Server, for which there is an OLE DB provider but which does not expose an engine to process SQL queries: The application uses ADO to talk to the OLE DB Provider for Microsoft Exchange and calls upon an OLE DB query processor component to handle the querying.
  • The data resides in the Microsoft NTFS file system in the form of documents: Data is accessed by using a native OLE DB provider over Microsoft Indexing Service, which indexes the content and properties of documents in the file system to enable efficient content searches.

In all of the preceding examples, the application can query the data. The user's needs are met with a minimum number of components. In each case, additional components are used only if needed, and only the required components are invoked. This demand-loading of reusable and shareable components greatly contributes to high performance when OLE DB is used.

Providers fall into two categories: those providing data and those providing services. A data provider owns its own data and exposes it in tabular form to your application. A service provider encapsulates a service by producing and consuming data, augmenting features in your ADO applications. A service provider may also be further defined as a service component, which must work in conjunction with other service providers or components.

ADO provides a consistent, higher level interface to the various OLE DB providers.


Data Providers

Data providers represent diverse sources of data such as SQL databases, indexed-sequential files, spreadsheets, document stores, and mail files. Providers expose data uniformly using a common abstraction called the rowset.

ADO is powerful and flexible because it can connect to any of several different data providers and still expose the same programming model, regardless of the specific features of any given provider. However, because each data provider is unique, how your application interacts with ADO will vary by data provider.

For example, the capabilities and features of the OLE DB Provider for SQL Server, which is used to access Microsoft SQL Server databases, are considerably different from those of the Microsoft OLE DB Provider for Internet Publishing, which is used to access file stores on a Web server.


Service Providers and Components

Service providers are components that extend the functionality of data providers by implementing extended interfaces that are not natively supported by the data store.

Universal Data Access provides a component architecture that allows individual, specialized components to implement discrete sets of database functionality, or "services," on top of less capable stores. Thus, rather than forcing each data store to provide its own implementation of extended functionality or forcing generic applications to implement database functionality internally, service components provide a common implementation that any application can use when accessing any data store. The fact that some functionality is implemented natively by the data store and some through generic components is transparent to the application.

For example, a cursor engine, such as the Microsoft Cursor Service for OLE DB, is a service component that can consume data from a sequential, forward-only data store to produce scrollable data. Other service providers commonly used by ADO include the Microsoft OLE DB Persistence Provider (for saving data to a file), the Microsoft Data Shaping Service for OLE DB (for hierarchical Recordsets), and the Microsoft OLE DB Remoting Provider (for invoking data providers on a remote computer).

For more information about service and data providers, see Appendix A: Providers.


Errors

Any operation involving ADO objects can generate one or more provider errors. As each error occurs, one or more Error objects are placed in the Errors collection of the Connection object. For details about handling warnings and errors in your ADO application, see Chapter 6: Error Handling.

Application errors can be raised by a separate mechanism. For example, in Visual Basic, the Err object will contain application-level errors.


Chapter 2: Getting Data

The preceding chapter introduced four primary operations involved in creating an ADO application: getting data, examining data, editing data, and updating data. This chapter will focus on the details of the concepts relevant to the first operation: getting data.

Several ADO objects can play a role in this operation. First you connect to the data source using an ADO Connection object (which at times will be created implicitly). Then you pass instructions to the data source about what you want to do using an ADO Command object (which also can be created implicitly). The result of passing a command to a data source and receiving its response usually will be represented in an ADO Recordset object.

To get data, your application must be in communication with a data source, such as a DBMS, a file store, or a comma-delimited text file. This communication represents a connection—the environment necessary for exchanging data.

The ADO object model represents the concept of a connection with the Connection object—the foundation upon which much ADO functionality is built. The purpose of a Connection object is to:

  • Define the information ADO needs to communicate with data sources and create sessions.
  • Define the transactional capabilities of the session.
  • Allow you to create and execute commands against the data source.
  • Provide information about the design of the underlying data source in the form of schema rowsets. For more information about schema rowsets, see OpenSchema Method.


Making a Connection

To connect to a data source, you must specify a connection string, the parameters of which might differ for each provider and data source. For more information, see Creating the Connection String.

ADO most commonly opens a connection by using the Connection object Open method. The syntax for the Open method is shown here:

Dim connection as New ADODB.Connection
connection.Open ConnectionString, UserID, Password, OpenOptions

Alternatively, you can invoke a shortcut technique, Recordset.Open, to open an implicit connection and issue a command over that connection in one operation. Do this by passing in a valid connection string as the ActiveConnection argument to the Open method. Here is the syntax for each method in Visual Basic:

Dim recordset as ADODB.Recordset
Set recordset = New ADODB.Recordset
recordset.Open Source, ActiveConnection, CursorType, LockType, Options

Note When should you use a Connection object vs. the Recordset.Open shortcut? Use the Connection object if you plan to open more than one Recordset, or when executing multiple commands. A connection is still created by ADO implicitly when you use the Recordset.Open shortcut.


Using the Connection Object

A Connection object represents a unique session with a data source. In the case of a client/server database system, it can be equivalent to an actual network connection to the server. Depending on the functionality supported by the provider, some collections, methods, or properties of a Connection object might not be available.

Before opening a Connection object, you must define certain information about the data source and type of connection. The ConnectionString parameter of the Connection object Open method—or the ConnectionString property on the Connection object—usually contains most of this information. A connection string is a string of characters that defines a variable number of arguments. The arguments—some required by ADO, but others provider-specific—contain information that the Connection object must have to carry out its work. The arguments that make up the ConnectionString parameter are separated with semicolons (;).

Note You can also specify an ODBC Data Source Name (DSN) or a Data Link (UDL) file in a connection string. For more information about DSNs, see Data Sources in Part 1 of the ODBC Programmer's Reference. For more information about UDLs, see Data Link API Overview in the OLE DB Programmer's Reference.


Creating the Connection String

ADO directly supports five arguments in a connection string. Other arguments are passed to the provider that is named in the Provider argument without any processing by ADO.

Argument | Description
Provider | Specifies the name of a provider to use for the connection.
File Name | Specifies the name of a provider-specific file (for example, a persisted data source object) containing preset connection information.
URL | Specifies the connection string as an absolute URL identifying a resource, such as a file or directory.
Remote Provider | Specifies the name of a provider to use when opening a client-side connection. (Remote Data Service only.)
Remote Server | Specifies the path name of the server to use when opening a client-side connection. (Remote Data Service only.)

Note In the following examples and throughout the ADO Programmer's Guide, the user id "MyId" with a password of "123aBc" is used to authenticate against the server. You should substitute these values with valid login credentials for your server. Also, substitute the name of your server for "MyServer".

The HelloData application in Chapter 1 used the following connection string:

m_sConnStr = "Provider=SQLOLEDB;Data Source=MyServer;" & _
             "Initial Catalog=Northwind;User Id=MyId;Password=123aBc;"

The only ADO parameter supplied in this connection string was "Provider=SQLOLEDB", which indicated the Microsoft OLE DB Provider for SQL Server. Other valid parameters that can be passed in the connection string can be determined by referring to individual providers' documentation. According to the OLE DB Provider for SQL Server documentation, you can substitute "Server" for the Data Source parameter and "Database" for the Initial Catalog parameter. Thus, the following connection string would produce results identical to the first:

m_sConnStr = "Provider=SQLOLEDB;Server=MyServer;" & _
             "Database=Northwind;User Id=MyId;Password=123aBc;"

To open the connection, simply pass the connection string as the first argument in the Connection object Open method:

objConn.Open m_sConnStr

It is also possible to supply much of this information by setting properties of the Connection object before opening the connection. For example, you could achieve the same effect as the connection string above by using the following code:

With objConn
.Provider = "SQLOLEDB"
.DefaultDatabase = "Northwind"
.Properties("Data Source") = "MyServer"
.Properties("User Id") = "MyId"
.Properties("Password") = "123aBc"
.Open
End With


Controlling Transactions

A transaction delimits the beginning and end of a series of data access operations that transpire across a connection. Subject to the transactional capabilities of your data source, the Connection object also allows you to create and manage transactions. For example, using the Microsoft OLE DB Provider for SQL Server to access a database on Microsoft SQL Server 2000, you can create multiple nested transactions for the commands you execute.

ADO ensures that changes to a data source resulting from operations in a transaction occur successfully together or not at all.

If you cancel the transaction, or if one of its operations fails, the ultimate result will be as if none of the operations in the transaction occurred. The data source will remain as it was before the transaction began.

The ADO object model does not explicitly include transactions, but represents them with a set of Connection object methods (BeginTrans, CommitTrans, and RollbackTrans).

For more information about transactions, see Chapter 5: Updating and Persisting Data.


Using the Command Object

After connecting to a data source, you need to execute requests against it to obtain result sets. ADO encapsulates this type of command functionality in the Command object.

You can use the Command object to request any type of operation from the provider, assuming that the provider can interpret the command string properly. A common operation for data providers is to query a database and return records in a Recordset object. Recordsets will be discussed later in this and other chapters; for now, think of them as tools to hold and view result sets. As with many ADO objects, depending on the functionality of the provider, some Command object collections, methods, or properties might generate errors when referenced.

It is not always necessary to create a Command object to execute a command against a data source. You can use the Execute method on the Connection object or the Open method on the Recordset object. However, you should use a Command object if you need to reuse a command in your code or if you need to pass detailed parameter information with your command. These scenarios are covered in more detail later in this chapter.

Note Certain Commands can return a result set as a binary stream or as a single Record rather than as a Recordset, if this is supported by the provider. Also, some Commands are not intended to return any result set at all (for example, a SQL Update query). This chapter will cover the most typical scenario, however: executing Commands that return results into a Recordset object. For more information about returning results into Records or Streams, see Chapter 10: Records and Streams.


Command Object Overview

With the collections, methods, and properties of a Command object, you can do the following:

  • Define the executable text of the command (for example, a SQL statement or a stored procedure) by using the CommandText property.
  • Define parameterized queries or stored procedure arguments by using Parameter objects and the Parameters collection.
  • Execute a command and return a Recordset object, if appropriate, by using the Execute method.
  • Specify the type of command by using the CommandType property prior to execution to optimize performance.
  • Specify specific information about the command text by using the Dialect property of the Command object.
  • Control whether the provider saves a prepared (or compiled) version of the command prior to execution by using the Prepared property.
  • Set the number of seconds that a provider will wait for a command to execute by using the CommandTimeout property.
  • Associate an open connection with a Command object by setting its ActiveConnection property.
  • Set the Name property to identify the Command object as a method on the associated Connection object.
  • Pass a Command object to the Source property of a Recordset in order to obtain data.
  • Pass a Stream object containing a command (for example, an XML command) to a provider that supports it.


Creating and Executing a Simple Command

Though not a typical usage of the Command object, the following code shows the basic method of using the Command object to execute a command against a data source. In this case, it is a row-returning command, so it returns the results of the command execution into a Recordset object.

'BeginBasicCmd
    Dim objConn As ADODB.Connection
    Dim objCmd As New ADODB.Command
    Dim objRs As ADODB.Recordset

    objCmd.CommandText = "SELECT OrderID, OrderDate, " & _
                         "RequiredDate, ShippedDate " & _
                         "FROM Orders " & _
                         "WHERE CustomerID = 'ALFKI' " & _
                         "ORDER BY OrderID"
    objCmd.CommandType = adCmdText

    ' Connect to the data source.
    Set objConn = GetNewConnection
    objCmd.ActiveConnection = objConn

    ' Execute once and display...
    Set objRs = objCmd.Execute

    Debug.Print "ALFKI"
    Do While Not objRs.EOF
        Debug.Print vbTab & objRs(0) & vbTab & objRs(1) & vbTab & _
                    objRs(2) & vbTab & objRs(3)
        objRs.MoveNext
    Loop

    'clean up
    objRs.Close
    objConn.Close
    Set objConn = Nothing
    Set objCmd = Nothing
'EndBasicCmd

The command to be executed is specified with the CommandText property.

Note Several examples in this section call a utility function, GetNewConnection, to establish a connection with the data provider. To avoid redundancy, it is listed only once, here:

'BeginNewConnection
Private Function GetNewConnection() As ADODB.Connection
    Dim oCn As New ADODB.Connection
    Dim sCnStr As String

    sCnStr = "Provider=SQLOLEDB;Data Source=MySrvr;" & _
             "User Id=MyId;Password=123aBc;Database=Northwind;"
    oCn.Open sCnStr

    Set GetNewConnection = oCn
End Function
'EndNewConnection


Command Object Parameters

A more interesting use for the Command object is shown in the next example, in which the text of the SQL command has been modified to make it parameterized. This makes it possible to reuse the command, passing in a different value for the parameter each time. Because the Prepared property on the Command object is set equal to True, ADO will require the provider to compile the command specified in CommandText before executing it for the first time. It also will retain the compiled command in memory. This slows the execution of the command slightly the first time it is executed because of the overhead required to prepare it, but results in a performance gain each time the command is called thereafter. Thus, commands should be prepared only if they will be used more than once.

'BeginManualParamCmd
' Set the CommandText as a parameterized SQL query.
objCmd.CommandText = "SELECT OrderID, OrderDate, " & _
                         "RequiredDate, ShippedDate " & _
                         "FROM Orders " & _
                         "WHERE CustomerID = ? " & _
                         "ORDER BY OrderID"
objCmd.CommandType = adCmdText

' Prepare command since we will be executing it more than once.
objCmd.Prepared = True

' Create new parameter for CustomerID. Initial value is ALFKI.
Set objParm1 = objCmd.CreateParameter("CustId", adChar, _
                adParamInput, 5, "ALFKI")
objCmd.Parameters.Append objParm1

' Connect to the data source.
Set objConn = GetNewConnection
objCmd.ActiveConnection = objConn

' Execute once and display...
Set objRs = objCmd.Execute

Debug.Print objParm1.Value
Do While Not objRs.EOF
    Debug.Print vbTab & objRs(0) & vbTab & objRs(1) & vbTab & _
                objRs(2) & vbTab & objRs(3)
    objRs.MoveNext
Loop

' ...then set new param value, re-execute command, and display.
objCmd("CustId") = "CACTU"
Set objRs = objCmd.Execute

Debug.Print objParm1.Value
Do While Not objRs.EOF
    Debug.Print vbTab & objRs(0) & vbTab & objRs(1) & vbTab & _
                objRs(2) & vbTab & objRs(3)
    objRs.MoveNext
Loop

objRs.Close
objConn.Close
'EndManualParamCmd

Not all providers support prepared commands. If the provider does not support command preparation, it might return an error as soon as this property is set to True. If it does not return an error, it ignores the request to prepare the command and sets the Prepared property to False.


Calling a Stored Procedure with a Command

You can also use a command when calling a stored procedure. The following code calls a stored procedure in the Northwind sample database, called CustOrdersOrders, which is defined as follows:

CREATE PROCEDURE CustOrdersOrders @CustomerID nchar(5) AS
SELECT OrderID, OrderDate, RequiredDate, ShippedDate
FROM Orders
WHERE CustomerID = @CustomerID
ORDER BY OrderID

This stored procedure is similar to the command used in Command Object Parameters, in that it takes a customer ID parameter and returns information about that customer's orders. The code below uses this stored procedure as the source for an ADO Recordset.

Using the stored procedure allows you to access another capability of ADO: the Parameters collection Refresh method. By using this method, ADO can automatically fill in all information about the parameters required by the command at run time. There is a performance penalty in using this technique, because ADO must query the data source for the information about the parameters.

Other important differences exist between the code below and the code in Command Object Parameters, where the parameters were entered manually. First, this code does not set the Prepared property to True because it is a SQL Server stored procedure and is precompiled by definition. Second, the CommandType property of the Command object changed to adCmdStoredProc in the second example to inform ADO that the command was a stored procedure.

Finally, in the second example the parameter must be referred to by index when setting the value, because you might not know the name of the parameter at design time. If you do know the name of the parameter, you can set the new NamedParameters property of the Command object to True and refer to the property's name. You might wonder why the position of the first parameter mentioned in the stored procedure (@CustomerID) is 1 instead of 0 (objCmd(1) = "ALFKI"). This is because parameter 0 contains a return value from the SQL Server stored procedure.

'BeginAutoParamCmd
' Set CommandText equal to the stored procedure name.
objCmd.CommandText = "CustOrdersOrders"
objCmd.CommandType = adCmdStoredProc

' Connect to the data source.
Set objConn = GetNewConnection
objCmd.ActiveConnection = objConn

' Automatically fill in parameter info from stored procedure.
objCmd.Parameters.Refresh

' Set the param value.
objCmd(1) = "ALFKI"

' Execute once and display...
Set objRs = objCmd.Execute

Debug.Print objParm1.Value
Do While Not objRs.EOF
    Debug.Print vbTab & objRs(0) & vbTab & objRs(1) & vbTab & _
                objRs(2) & vbTab & objRs(3)
    objRs.MoveNext
Loop

' ...then set new param value, re-execute command, and display.
objCmd(1) = "CACTU"
Set objRs = objCmd.Execute

Debug.Print objParm1.Value
Do While Not objRs.EOF
    Debug.Print vbTab & objRs(0) & vbTab & objRs(1) & vbTab & _
                objRs(2) & vbTab & objRs(3)
    objRs.MoveNext
Loop

objRs.Close
objConn.Close
'EndAutoParamCmd


Named Commands

You can set the Name property on a Command object and then execute the command by calling it as if it were a method on the Command object ActiveConnection property. This is illustrated in the following example, in which the command is named GetCustomers. Notice that the code passes in a declared and instantiated Recordset object to the GetCustomers "method." You can also pass in parameters to the "method" if they are required by the Command.

'BeginNamedCmd
    Dim objConn As ADODB.Connection
    Dim objCmd As New ADODB.Command
    Dim objRs As New ADODB.Recordset

    ' Connect to the data source.
    Set objConn = GetNewConnection

    objCmd.CommandText = "SELECT CustomerID, CompanyName FROM Customers"
    objCmd.CommandType = adCmdText

    'Name the command.
    objCmd.Name = "GetCustomers"

    objCmd.ActiveConnection = objConn

    ' Execute using Command.Name from the Connection.
    objConn.GetCustomers objRs

    ' Display.
    Do While Not objRs.EOF
        Debug.Print objRs(0) & vbTab & objRs(1)
        objRs.MoveNext
    Loop

    'Clean up.
    objRs.Close
    objConn.Close
    Set objConn = Nothing
    Set objCmd = Nothing
'EndNamedCmd


Adding Data to a Recordset

The Recordset is probably the most used of the ADO objects. In ADO a Recordset is best thought of as the combination of a result set from a data source and its associated cursor behaviors. Thus, you can put data into a Recordset and then use the Recordset methods and properties to navigate through the rows of data, view the values in the rows, and otherwise manipulate the result set.

This section will focus on adding data to the Recordset. For information about navigating through the data or updating the data, see Chapter 4: Editing Data and Chapter 5: Updating and Persisting Data. You do not always need the advanced capabilities of a Command object to add your result set to a Recordset. Often, you can execute your command by setting the Source property on the Recordset or passing a command string to the Recordset object Open method.

There are a variety of ways to add data from your data source to a Recordset. The technique you use depends on the needs of your application and the capabilities of your provider.


The Recordset Object Open Method

Everything you need to open an ADO Recordset is built into the Open method. You can use it without explicitly creating any other objects. The syntax of this method is as follows:

recordset.Open Source, ActiveConnection, CursorType, LockType, Options

All arguments are optional because the information they pass can be communicated to ADO in other ways. However, understanding each argument will help you to understand many important ADO concepts. The following topics will examine each argument of this method in more detail.


Source and Options Arguments

The Source and Options arguments appear in the same topic because they are closely related.

recordset.Open Source, ActiveConnection, CursorType, LockType, Options

The Source argument is a Variant that evaluates to a valid Command object, a text command (e.g., a SQL statement), a table name, a stored procedure call, a URL, or the name of a file or Stream object containing a persistently stored Recordset. If Source is a file path name, it can be a full path ("C:\dir\file.rst"), a relative path ("..\file.rst"), or a URL ("http://files/file.rst"). You can also specify this information in the Recordset object Source property and leave the Source argument blank.

The Options argument is a Long value that indicates either or both of the following:

  • How the provider should evaluate the Source argument if it represents something other than a Command object.
  • That the Recordset should be restored from a file where it was previously saved.

This argument can contain a bitmask of CommandTypeEnum or ExecuteOptionEnum values. A CommandTypeEnum passed in the Options argument sets the CommandType property of the Recordset.

Note The ExecuteOpenEnum values of adExecuteNoRecords and adExecuteStream cannot be used with Open.

If the CommandType property value equals adCmdUnknown (the default value), you might experience diminished performance, because ADO must make calls to the provider to determine whether the CommandText property is a SQL statement, a stored procedure, or a table name. If you know what type of command you are using, setting the CommandType property instructs ADO to go directly to the relevant code. If the CommandType property does not match the type of command in the CommandText property, an error occurs when you call the Open method.

For more information about using these enumerated constants for Options and with other ADO methods and properties, see CommandTypeEnum and ExecuteOptionEnum.


ActiveConnection Argument

You can pass in either a Connection object or a connection string as the ActiveConnection argument.

recordset.Open Source, ActiveConnection, CursorType, LockType, Options

The ActiveConnection argument corresponds to the ActiveConnection property and specifies in which connection to open the Recordset object. If you pass a connection definition for this argument, ADO opens a new connection using the specified parameters. After opening the Recordset with a client-side cursor (CursorLocation = adUseClient), you can change the value of this property to send updates to another provider. Or you can set this property to Nothing (in Microsoft Visual Basic) or NULL to disconnect the Recordset from any provider. Changing ActiveConnection for a server-side cursor generates an error, however.

If you pass a Command object in the Source argument and also pass an ActiveConnection argument, an error occurs because the ActiveConnection property of the Command object must already be set to a valid Connection object or connection string.


CursorType Argument

recordset.Open Source, ActiveConnection, CursorType, LockType, Options

As discussed in The Significance of Cursor Location, the type of cursor that your application uses will determine which capabilities are available to the resultant Recordset (if any). For a detailed examination of cursor types, see Chapter 8: Understanding Cursors and Locks.

The CursorType argument can accept any of the CursorTypeEnum values.


LockType Argument

recordset.Open Source, ActiveConnection, CursorType, LockType, Options

Set the LockType argument to specify what type of locking the provider should use when opening the Recordset. The different types of locking are discussed in Chapter 8: Understanding Cursors and Locks.

The LockType argument can accept any of the LockTypeEnum values.


Retrieving Multiple Recordsets

You might occasionally need to execute a command that will return more than one result set. A common example is a stored procedure that runs against a SQL Server database, as in the following example. The stored procedure contains a COMPUTE clause to return the average price of all products in the table. The definition of the stored procedure is as follows:

CREATE PROCEDURE ProductsWithAvgPrice
AS
SELECT ProductID, ProductName, UnitPrice
  FROM PRODUCTS
  COMPUTE AVG(UnitPrice)

The Microsoft OLE DB Provider for SQL Server returns multiple result sets to ADO when the command contains a COMPUTE clause. Therefore, the ADO code must use the NextRecordset method to access the data in the second result set, as shown here:

'BeginNextRs
    objCmd.CommandText = "ProductsWithAvgPrice"
    objCmd.CommandType = adCmdStoredProc

    Set objRs = objCmd.Execute

    Do While Not objRs.EOF
        Debug.Print objRs(0) & vbTab & objRs(1) & vbTab & _
                    objRs(2)
        objRs.MoveNext
    Loop

    Set objRs = objRs.NextRecordset

    Debug.Print "AVG. PRICE = $ " & objRs(0)

    objRs.Close
'EndNextRs

For more information, see NextRecordset.


Chapter 3: Examining Data

Chapter 2 explained how to retrieve data from a data source as a Recordset object. This chapter will discuss the Recordset in more detail, including how to navigate through the Recordset and view its data.

The following diagram illustrates the object model of the Recordset object. Click an object or collection for more information.

Recordsets have methods and properties designed to make it easy to move through them and examine their contents. Depending on the functionality supported by the provider, some Recordset methods or properties might not be available. To continue exploring the Recordset object, consider a Recordset that would be returned from the Northwind sample database on Microsoft SQL Server 2000, using the following code:

'BeginRsTour
Public Sub RecordsetTour()
    Dim objRs As ADODB.Recordset
    Dim strSQL As String
    Dim strConnStr As String

    Set objRs = New ADODB.Recordset

    strSQL = "SELECT ProductID, ProductName, UnitPrice FROM Products " & _
           "WHERE CategoryID = 7"          '7 = Produce

    strConnStr = "Provider=SQLOLEDB;Initial Catalog=NorthWind;" & _
             "Data Source=MySrv;User Id=MyId;Password=123aBc;"

    objRs.Open strSQL, strConnStr, adOpenForwardOnly, _
             adLockReadOnly, adCmdText

    ' work with Recordset...
'EndRsTour

This SQL query returns a Recordset with five rows (records) and three columns (fields). The values for each row are shown in the following table.

FIELD 0 Name = ProductID | FIELD 1 Name = ProductName | FIELD 2 Name = UnitPrice
7 | Uncle Bob's Organic Dried Pears | 30.0000
14 | Tofu | 23.2500
28 | Rössle Sauerkraut | 45.6000
51 | Manjimup Dried Apples | 53.0000
74 | Longlife Tofu | 10.0000

The next section will explain how to locate the current position of the cursor in this sample Recordset.


Locating the Current Record

The current position of the cursor in the Recordset delineates the current record position. Assuming that the command issued returns results, the cursor is automatically placed at the first record when the Recordset Open method is called. So, with the sample Recordset, the cursor would be on the first record, "Uncle Bob's Organic Dried Pears."


Counting Rows

The RecordCount property returns a Long value that indicates the number of records in the Recordset. Use the RecordCount property to find out how many records are in a Recordset object. The property returns -1 when ADO cannot determine the number of records or if the provider or cursor type does not support RecordCount. Reading the RecordCount property on a closed Recordset causes an error.

The RecordCount property depends on the capabilities of the provider and the type of cursor. The RecordCount property will return -1 for a forward-only cursor, the actual count for a static or keyset cursor, and either -1 or the actual count for a dynamic cursor, depending on the data source.

The sample Recordset introduced in Examining Data would return –1 because a forward-only cursor was opened. In order to use the RecordCount property, you would need to open the Recordset with a more sophisticated cursor (static or keyset).

In certain cases, your provider or cursor might be unable to provide the RecordCount value without first fetching all records from the data source. To force this type of fetch, call the Recordset MoveLast method before calling RecordCount.

If you were to replace the line of code that calls the Recordset Open method with the following:

oRs.Open sSQL, sCnStr, adOpenStatic, adLockOptimistic, adCmdText

you would be able to use the RecordCount property because static cursors with the Microsoft OLE DB Provider for SQL Server support RecordCount. For example, the following code would print out the number of records returned by the command to the debug window, assuming the cursor supports the RecordCount property:

Debug.Print oRs.RecordCount   ' Output: 4

From this point forward, assume that these more capable (but more expensive) cursor and lock type settings are used.


The Limits of a Recordset

Use the BOF and EOF properties to determine whether a Recordset object contains records or whether you've gone beyond the limits of a Recordset object when you move from record to record. Think of BOF and EOF as "phantom" records that are positioned at the beginning and end of the Recordset. Building on the sample Recordset from Examining Data, it would now look like this:

ProductID | ProductName | UnitPrice
BOF |  |
7 | Uncle Bob's Organic Dried Pears | 30.0000
14 | Tofu | 23.2500
28 | Rössle Sauerkraut | 45.6000
51 | Manjimup Dried Apples | 53.0000
74 | Longlife Tofu | 10.0000
EOF |  |

The BOF property returns True (-1) if the current record position is before the first record and False (0) if the current record position is on or after the first record.

The EOF property returns True if the current record position is after the last record and False if the current record position is on or before the last record.

If either the BOF or EOF property is True, there is no current record, as shown in the following code:

If oRs.BOF And oRs.EOF Then
    ' Command returned no records.
End If

If you open a Recordset object containing no records, the BOF and EOF properties are both set to True and the value of the Recordset object's RecordCount property setting depends on the cursor type. -1 will be returned for dynamic cursors (CursorType = adOpenDynamic) and 0 will be returned for other cursors.

When you open a Recordset object that contains at least one record, the first record is the current record and the BOF and EOF properties are False.

If you delete the last remaining record in the Recordset object, the cursor is left in an indeterminate state. The BOF and EOF properties may remain False until you attempt to reposition the current record, depending upon the provider.


Navigating Through the Data

Now that you have executed a command against the data source and determined that the result set contains data, you can move through the results by using the navigation methods and properties provided by the Recordset object. The following topics describe how to use these methods and properties on the sample Recordset:

  • Jumping to a Record
  • More Ways to Move in a Recordset
  • Using Bookmarks
  • Using Pages
  • Recordset Positioning


Jumping to a Record

The Move method allows you to move forward or backward in the Recordset a specified number of records by using the following syntax:

oRs.Move NumRecords, Start

The Move method is supported on all Recordset objects.

If the NumRecords argument is greater than zero, the current record position moves forward (toward the end of the Recordset). If NumRecords is less than zero, the current record position moves backward (toward the beginning of the Recordset).

If the Move call would move the current record position to a point before the first record, ADO sets the current record to the position before the first record in the Recordset (BOF is True). An attempt to move backward when the BOF property is already True generates an error.

If the Move call would move the current record position to a point after the last record, ADO sets the current record to the position after the last record in the Recordset (EOF is True). An attempt to move forward when the EOF property is already True generates an error.

Calling the Move method from an empty Recordset object generates an error.

If you pass a bookmark in the Start argument, the move is relative to the record with this bookmark, assuming the Recordset object supports bookmarks. A bookmark is obtained by using the Bookmark property. If not specified, the move is relative to the current record.

If you are using the CacheSize property to locally cache records from the provider, passing a NumRecords argument that moves the current record position outside the current group of cached records forces ADO to retrieve a new group of records, starting from the destination record. The CacheSize property determines the size of the newly retrieved group, and the destination record is the first record retrieved.


More Ways to Move in a Recordset

The following four methods are used to move around, or scroll, in the Recordset: MoveFirst, MoveLast, MoveNext, and MovePrevious. (Some of these methods are unavailable on forward-only cursors.)

MoveFirst changes the current record position to the first record in the Recordset. MoveLast changes the current record position to the last record in the Recordset. To use MoveFirst or MoveLast, the Recordset object must support bookmarks or backward cursor movement; otherwise, the method call will generate an error.

MoveNext moves the current record position one place forward. If you are on the last record when you call MoveNext, EOF will be set to True. MovePrevious moves the current record position one place backward. If you are on the first record when you call MovePrevious, BOF will be set to True. It is wise to check the EOF and BOF properties when using these methods and to move the cursor back to a valid current record position if you move off either end of the Recordset, as shown here:

. . .
oRs.MoveNext
If oRs.EOF Then oRs.MoveLast
. . .

Or, in the case of the MovePrevious method:

. . .
oRs.MovePrevious
If oRs.BOF Then oRs.MoveFirst
. . .

In cases where the Recordset has been filtered or sorted and the current record's data is changed, the position may also change. In such cases the MoveNext method works normally, but be aware that the position is moved one record forward from the new position, not the old position. For example, changing the data in the current record, such that the record is moved to the end of the sorted Recordset, would mean that calling MoveNext results in ADO setting the current record to the position after the last record in the Recordset (EOF = True).

The behavior of the various Move methods of the Recordset object depends, to some extent, on the data within the Recordset. New records added to the Recordset are initially added in a particular order, which is defined by the data source and may be dependent implicitly or explicitly on the data in the new record. For example, if a sort or a join is done within the query that populates the Recordset, the new record will be inserted in the appropriate place within the Recordset. If ordering is not explicitly specified when creating the Recordset, changes in the data source implementation may cause the ordering of the returned rows to change inadvertently. In addition, the sorting, filtering, and editing functions of the Recordset can affect the order and possibly which rows in the recordset will be visible.

Therefore, MoveNext, MovePrevious, MoveFirst, MoveLast, and Move are all sensitive to other operations performed on the same Recordset. ADO will always try to maintain your current position until you explicitly move it, but sometimes intervening changes make it difficult to understand the effects of a subsequent move. For example, if you call MoveFirst to position on the first row of a sorted Recordset and you change the sort from ascending order to descending order, you are still on the same row—but now it is the last row in the Recordset. MoveFirst will take you to a different row (the new first row).

As another example, if you are positioned on a particular row in the middle of a Recordset and you call Delete and then call MoveNext, you are now on the record immediately following the deleted record. But calling MovePrevious makes the record preceding the one you deleted the current record, because the deleted record is no longer counted in the active membership of the Recordset.

It is particularly difficult to define consistent move semantics across all providers for methods that move relative to the current record—MovePrevious, MoveNext, and Move—in the face of changing data in the current record. For example, if you are working with a sorted, filtered Recordset, and you change the data in the current record so that it would precede all other records, but your changed data also no longer matches the filter, it is not clear where a MoveNext operation should take you. The safest conclusion is that relative movement within a Recordset is riskier than absolute movement (such as using MoveFirst or MoveLast) when the data is changing while records are being edited, added, or deleted. Sorting and filtering should be based on a primary key or ID, because this type of value should not change.


Using Bookmarks

It is often useful to return directly to a specific record after having moved around in the Recordset without having to scroll through every record and compare values. For example, if you attempt to search for a record using the Find method but the search returns no records, you are automatically placed at either end of the Recordset. If your provider supports them, bookmarks can be used to mark your place before using the Find method so you can return to your location. A bookmark is a Variant type value that uniquely identifies a record in a Recordset object.

You can also use a variant array of bookmarks with the Recordset Filter method to filter on a selected set of records. For details about this technique, see Filtering the Results later in this chapter.

You can use the Bookmark property to get a bookmark for a record, or set the current record in a Recordset object to the record identified by a valid bookmark. The following code uses the Bookmark property to set a bookmark and then return to the bookmarked record after moving on to other records. To determine if your Recordset supports bookmarks, use the Supports method.

'BeginBookmarkEg
    Dim varBookmark As Variant
    Dim blnCanBkmrk As Boolean

    objRs.Open strSQL, strConnStr, adOpenStatic, adLockOptimistic, adCmdText
    objRs.Move 5                       ' move to the sixth record
    blnCanBkmrk = objRs.Supports(adBookmark)
    If blnCanBkmrk Then
        varBookmark = objRs.Bookmark   ' record the bookmark
        objRs.MoveLast                 ' move to a different record
        objRs.Bookmark = varBookmark   ' return to the bookmarked (sixth) record
    End If
'EndBookmarkEg

The Supports method is covered in more detail later.

Except for the case of cloned Recordsets, bookmarks are unique to the Recordset in which they were created, even if the same command is used. This means that you cannot use a Bookmark obtained from one Recordset to move to the same record in a second Recordset opened with the same command.


Using Pages

Use the PageCount property to determine how many pages of data are in the Recordset object. Pages are groups of records whose size equals the PageSize property setting. Even if the last page is incomplete because there are fewer records than the PageSize value, it counts as an additional page in the PageCount value. If the Recordset object does not support this property, PageCount will be -1 to indicate that the PageCount is indeterminable.

Use the PageSize property to determine how many records make up a logical page of data. Establishing a page size allows you to use the AbsolutePage property to move to the first record of a particular page. This is useful in Web-server scenarios when you want to allow the user to page through data, viewing a certain number of records at a time.

This property can be set at any time, and its value will be used for calculating the location of the first record of a particular page.

Use the AbsolutePage property to identify the page number on which the current record is located. Again, the provider must support the appropriate functionality for this property to be available.

AbsolutePage is 1-based and equals 1 when the current record is the first record in the Recordset. Set this property to move to the first record of a particular page. Obtain the total number of pages from the PageCount property.


Recordset Positioning

Use the AbsolutePosition property to move to a record, based on its ordinal position in the Recordset object, or to determine the ordinal position of the current record. The provider must support the appropriate functionality for this property to be available.

AbsolutePosition is 1-based and equals 1 when the current record is the first record in the Recordset. As mentioned previously, you can obtain the total number of records in the Recordset object from the RecordCount property.

When you set the AbsolutePosition property, even if it is to a record in the current cache, ADO reloads the cache with a new group of records starting with the record you specified. The CacheSize property determines the size of this group.

Note You should not use the AbsolutePosition property as a surrogate record number. The position of a given record changes when you delete a preceding record. There also is no assurance that a given record will have the same AbsolutePosition if the Recordset object is requeried or reopened. Bookmarks are the recommended way of retaining and returning to a given position and are the only way of positioning across all types of Recordset objects.


Understanding Recordset Structure

Every Recordset has a Fields collection consisting of one or more Field objects. A Field object usually represents a table column. The following topics will explain how to navigate through the Fields collection and get information about each field. Then they will discuss what kind of information is available to you via the Field object and how to use it.

  • The Fields Collection
  • The Field Object
  • Working with Recordsets


The Fields Collection

The Fields collection is one of ADO's intrinsic collections. A collection is an ordered set of items that can be referred to as a unit. For more information about ADO collections, see The ADO Object Model in Chapter 1.

The Fields collection contains a Field object for every field (column) in the Recordset. Like all ADO collections, it has Count and Item properties, as well as Append and Refresh methods. It also has CancelUpdate, Delete, Resync, and Update methods, which are not available to other ADO collections.


Examining the Fields Collection

Consider the Fields collection of the sample Recordset introduced in this chapter. The sample Recordset was derived from the SQL statement

SELECT ProductID, ProductName, UnitPrice FROM Products WHERE CategoryID = 7

Thus, you should find that the Recordset Fields collection contains three fields.

'BeginWalkFields
    Dim objFields As ADODB.Fields

    objRs.Open strSQL, strConnStr, adOpenForwardOnly, adLockReadOnly, adCmdText

    Set objFields = objRs.Fields

    For intLoop = 0 To (objFields.Count - 1)
        Debug.Print objFields.Item(intLoop).Name
    Next
'EndWalkFields

This code simply determines the number of Field objects in the Fields collection using the Count property and loops through the collection, returning the value of the Name property for each Field object. You can use many more Field properties to get information about a field. For more information about querying a Field, see The Field Object.


Counting Columns

As you might expect, the Count property returns the actual number of Field objects in the Fields collection. Because numbering for members of a collection begins with zero, you should always code loops starting with the zero member and ending with the value of the Count property minus 1. If you are using Microsoft Visual Basic and want to loop through the members of a collection without checking the Count property, use the For Each...Next command.

If the Count property is zero, there are no objects in the collection.


Getting to the Field

As with any ADO collection, the Item property is the default property of the collection. It returns the individual Field object specified by the name or index passed to it. Therefore, the following statements are equivalent for the sample Recordset:

objField = objRecordset.Fields.Item("ProductID")
objField = objRecordset.Fields("ProductID")
objField = objRecordset.Fields.Item(0)
objField = objRecordset.Fields(0)

If these methods are equivalent, which is best? It depends. Using an index to retrieve a Field from the collection is faster because it accesses the Field directly without having to perform a string lookup. On the other hand, the order of Fields within the collection must be known, and if the order changes, the reference to the Field's index will have to be changed wherever it occurs. Although slightly slower, using the name of the Field is more flexible because it doesn't depend on the order of the Fields in the collection.


Using the Refresh Method

Unlike some other ADO collections, using the Refresh method on the Fields collection has no visible effect. To retrieve changes from the underlying database structure, you must use either the Requery method, or if the Recordset object does not support bookmarks, the MoveFirst method, which will cause the command to be executed against the provider again.


Adding Fields to a Recordset

The Append method is used to add fields to a Recordset.

You can use the Append method to fabricate a Recordset programmatically without opening a connection to a data source. A run-time error will occur if the Append method is called on the Fields collection of an open Recordset or on a Recordset where the ActiveConnection property has been set. You can append fields only to a Recordset that is not open and has not yet been connected to a data source. However, to specify values for the newly appended Fields, the Recordset must first be opened.

Developers often need a place to temporarily store some data, or want some data to act as if it came from a server so it can participate in data binding in a user interface. ADO (in conjunction with the Microsoft Cursor Service for OLE DB) enables the developer to build an empty Recordset object by specifying column information and calling Open. In the following example, three new fields are appended to a new Recordset object. Then the Recordset is opened, two new records are added, and the Recordset is persisted to a file. (For more information about Recordset persistence, see Chapter 5: Updating and Persisting Data.)

'BeginFabricate
    Dim objRs As ADODB.Recordset
    Set objRs = New ADODB.Recordset

    With objRs.Fields
        .Append "StudentID", adChar, 11, adFldUpdatable
        .Append "FullName", adVarChar, 50, adFldUpdatable
        .Append "PhoneNmbr", adVarChar, 20, adFldUpdatable
    End With

    With objRs
        .Open

        .AddNew
        .Fields(0) = "123-45-6789"
        .Fields(1) = "John Doe"
        .Fields(2) = "(425) 555-5555"
        .Update

        .AddNew
        .Fields(0) = "123-45-6780"
        .Fields(1) = "Jane Doe"
        .Fields(2) = "(615) 555-1212"
        .Update
    End With

    objRs.Save App.Path & "FabriTest.adtg", adPersistADTG

    objRs.Close
'EndFabricate

The usage of the Fields Append method differs between the Recordset object and the Record object. For more information about the Record object, see Chapter 10: Records and Streams.

See Also

Fabricating Hierarchical Recordsets


The Field Object

Each Field object usually corresponds to a column in a database table. However, a Field can also represent a pointer to another Recordset, called a chapter. Exceptions, such as chapter columns, will be covered later in this guide.

Use the Value property of Field objects to set or return data for the current record. Depending on the functionality the provider exposes, some collections, methods, or properties of a Field object may not be available.

With the collections, methods, and properties of a Field object, you can do the following:

  • Return the name of a field by using the Name property.
  • View or change the data in the field by using the Value property. Value is the default property of the Field object.
  • Return the basic characteristics of a field by using the Type, Precision, and NumericScale properties.
  • Return the declared size of a field by using the DefinedSize property.
  • Return the actual size of the data in a given field by using the ActualSize property.
  • Determine what types of functionality are supported for a given field by using the Attributes property and Properties collection.
  • Manipulate the values of fields containing long binary or long character data by using the AppendChunk and GetChunk methods.

Resolve discrepancies in field values during batch updating by using the OriginalValue and UnderlyingValue properties, if the provider supports batch updates.


Describing a Field

The topics that follow will discuss properties of the Field object that represent information that describes the Field object itself—that is, metadata about the field. This information can be used to determine much about the schema of the Recordset. These properties include Type, DefinedSize and ActualSize, Name, and NumericScale and Precision.


Discovering the Data Type

The Type property indicates the data type of the field. The data type enumerated constants that are supported by ADO are described in DataTypeEnum in the ADO Programmer's Reference.

For floating point numeric types such adNumeric, you can obtain more information. The NumericScale property indicates how many digits to the right of the decimal point will be used to represent values for the Field. The Precision property specifies the maximum number of digits used to represent values for the Field.


Determining Field Size

Use the DefinedSize property to determine the data capacity of a Field object.

Use the ActualSize property to return the actual length of a Field object's value. For all fields, the ActualSize property is read-only. If ADO cannot determine the length of the Field object's value, the ActualSize property returns adUnknown.

The DefinedSize and ActualSize properties have different purposes. For example, consider a Field object with a declared type of adVarChar and a DefinedSize property value of 50, containing a single character. The ActualSize property value it returns is the length in bytes of the single character.


Determining Field Contents

The identifier of the column from the data source is represented by the Name property of the Field. The Value property of the Field object returns or sets the actual data content of the field. This is the default property.

To change the data in a field, set the Value property equal to a new value of the correct type. Your cursor type must support updates to change the contents of a field. Database validation is not done here in batch mode, so you will need to check for errors when you call UpdateBatch in such a case. Some providers also support the ADO Field object's UnderlyingValue and OriginalValue properties to assist you with resolving conflicts when you attempt to perform batch updates. For details about how to resolve such conflicts, see Chapter 4: Editing Data.

Note Recordset Field values cannot be set when appending new Fields to a Recordset. Rather, new Fields can be appended to a closed Recordset. Then the Recordset must be opened, and only then can values be assigned to these Fields.


Getting More Field Information

ADO objects have two types of properties: built-in and dynamic. To this point, only the built-in properties of the Field object have been discussed.

Built-in properties are those properties implemented in ADO and immediately available to any new object, using the MyObject.Property syntax. They do not appear as Property objects in an object's Properties collection.

Dynamic properties are defined by the underlying data provider, and appear in the Properties collection for the appropriate ADO object. For example, a property specific to the provider may indicate if a Recordset object supports transactions or updating. These additional properties will appear as Property objects in that Recordset object's Properties collection. Dynamic properties can be referenced only through the collection, using the syntax MyObject.Properties(0) or MyObject.Properties("Name").

You cannot delete either kind of property.

A dynamic Property object has four built-in properties of its own:

  • The Name property is a string that identifies the property.
  • The Type property is an integer that specifies the property data type.
  • The Value property is a variant that contains the property setting. Value is the default property for a Property object.
  • The Attributes property is a Long value that indicates characteristics of the property specific to the provider.

The Properties collection for the Field object contains additional metadata about the field. The contents of this collection vary depending upon the provider. The following code example examines the Properties collection of the sample Recordset introduced at the beginning of this chapter. It first looks at the contents of the collection. This code uses the OLE DB Provider for SQL Server, so the Properties collection contains information relevant to that provider.

'BeginFieldProps
    Dim objProp As ADODB.Property

    For intLoop = 0 To (objFields.Count - 1)
        Debug.Print objFields.Item(intLoop).Name

        For Each objProp In objFields(intLoop).Properties
            Debug.Print vbTab & objProp.Name & " = " & objProp.Value
        Next objProp
    Next intLoop
'EndFieldProps


Dealing with Binary Data

Use the AppendChunk method on a Field object to fill it with long binary or character data. In situations where system memory is limited, you can use the AppendChunk method to manipulate long values in portions rather than in their entirety.

If the adFldLong bit in the Attributes property of a Field object is set to True, you can use the AppendChunk method for that field.

The first AppendChunk call on a Field object writes data to the field, overwriting any existing data. Subsequent AppendChunk calls add to existing data. If you are appending data to one field and then you set or read the value of another field in the current record, ADO assumes that you are finished appending data to the first field. If you call the AppendChunk method on the first field again, ADO interprets the call as a new AppendChunk operation and overwrites the existing data. Accessing fields in other Recordset objects that are not clones of the first Recordset object will not disrupt AppendChunk operations.

Use the GetChunk method on a Field object to retrieve part or all of its long binary or character data. In situations where system memory is limited, you can use the GetChunk method to manipulate long values in portions, rather than in their entirety.

The data that a GetChunk call returns is assigned to variable. If Size is greater than the remaining data, the GetChunk method returns only the remaining data without padding variable with empty spaces. If the field is empty, the GetChunk method returns a null value.

Each subsequent GetChunk call retrieves data starting from where the previous GetChunk call left off. However, if you are retrieving data from one field and then set or read the value of another field in the current record, ADO assumes you have finished retrieving data from the first field. If you call the GetChunk method on the first field again, ADO interprets the call as a new GetChunk operation and starts reading from the beginning of the data. Accessing fields in other Recordset objects that are not clones of the first Recordset object will not disrupt GetChunk operations.

If the adFldLong bit in the Attributes property of a Field object is set to True, you can use the GetChunk method for that field.

If there is no current record when you use the GetChunk or AppendChunk method on a Field object, error 3021 (no current record) occurs.

For an example of using these methods to manipulate binary data, see the AppendChunk Method and GetChunk Method examples in the ADO Programmer's Reference.


Working with Recordsets

The Recordset object has built-in features that make it possible for you to rearrange the order of the data in the result set, to search for a specific record based on criteria that you supply, and even to optimize those search operations using indexes. Whether these features are available for use depends on the provider and in some cases—such as that of the Index property—the structure of the data source itself.


Arranging Data

Often, the most efficient way to order the data in your Recordset is by specifying an ORDER BY clause in the SQL command used to return results to it. However, you might need to change the order of the data in a Recordset that has already been created. You can use the Sort property to establish the order in which rows of a Recordset are traversed. Furthermore, the Filter property determines which rows are accessible when traversing rows.

The Sort property sets or returns a String value that indicates the field names in the Recordset on which to sort. Each name is separated by a comma and is optionally followed by a space and the keyword ASC (which sorts the field in ascending order) or DESC (which sorts the field in descending order). By default, if no keyword is specified, the field is sorted in ascending order.

The sort operation is efficient because data is not physically rearranged but is simply accessed in the order specified by an index.

The Sort property requires the CursorLocation property to be set to adUseClient. A temporary index will be created for each field specified in the Sort property if an index does not already exist.

Setting the Sort property to an empty string will reset the rows to their original order and delete temporary indexes. Existing indexes will not be deleted.

Suppose a Recordset contains three fields named firstName, middleInitial, and lastName. Set the Sort property to the string "lastName DESC, firstName ASC", which will order the Recordset by last name in descending order and then by first name in ascending order. The middle initial is ignored.

No field referenced in a sort criteria string can be named "ASC" or "DESC" because those names conflict with the keywords ASC and DESC. Give a field with a conflicting name an alias by using the AS keyword in the query that returns the Recordset.

For more details about Recordset filtering, see Filtering the Results.


Finding a Specific Record

ADO provides the Find and Seek methods for locating a particular record in a Recordset. The Find method is supported by a variety of providers but is limited to a single search criterion. The Seek method supports searching on multiple criteria, but is not supported by many providers.

Indexes on fields can greatly enhance the performance of the Recordset object's Find method and Sort and Filter properties. You can create an internal index for a Field object by setting its dynamic Optimize property. This dynamic property is added to the Field object's Properties collection when you set the CursorLocation property to adUseClient. Remember that this index is internal to ADO—you cannot gain access to it or use it for any other purpose. Also, this index is distinct from the Recordset object's Index property.

The Find method quickly locates a value within a column (field) of a Recordset. You can often improve the speed of the Find method's operation on a column by using the Optimize property to create an index on it.

The Find method limits your search to the contents of one field. The Seek method requires that you have an index and has other limitations as well. If you need to search on multiple fields that aren't the basis of an index, or if your provider does not support indexes, you can limit your results using the Filter property of the Recordset object.

Find

The Find method searches a Recordset for the row that satisfies a specified criterion. Optionally, the direction of the search, starting row, and offset from the starting row may be specified. If the criterion is met, the current row position is set on the found record; otherwise, the position is set to the end (or start) of the Recordset, depending on search direction.

Only a single-column name may be specified for the criterion. In other words, this method does not support multi-column searches.

The comparison operator for the criterion may be ">" (greater than), "<" (less than), "=" (equal), ">=" (greater than or equal), "<=" (less than or equal), "<>" (not equal), or "LIKE" (pattern matching).

The criterion value may be a string, floating-point number, or date. String values are delimited with single quotes or "#" (number sign) marks (for example, "state = 'WA'" or "state = #WA#"). Date values are delimited with "#" (number sign) marks (for example, "start_date > #7/22/97#").

If the comparison operator is "like", the string value may contain an asterisk (*) to find one or more occurrences of any character or substring. For example, "state like 'M*'" matches Maine and Massachusetts. You can also use leading and trailing asterisks to find a substring contained within the values. For example, "state like '*as*'" matches Alaska, Arkansas, and Massachusetts.

Asterisks can be used only at the end of a criteria string or together at both the beginning and end of a criteria string, as shown above. You cannot use the asterisk as a leading wildcard ('*str') or embedded wildcard ('s*r'). This will cause an error.

Seek and Index

Use the Seek method in conjunction with the Index property if the underlying provider supports indexes on the Recordset object. Use the Supports (adSeek) method to determine whether the underlying provider supports Seek, and the Supports(adIndex) method to determine whether the provider supports indexes. (For example, the OLE DB Provider for Microsoft Jet supports Seek and Index.)

If Seek does not find the desired row, no error occurs, and the row is positioned at the end of the Recordset. Set the Index property to the desired index before executing this method.

This method is supported only with server-side cursors. Seek is not supported when the Recordset object's CursorLocation property value is adUseClient.

This method can be used only when the Recordset object has been opened with a CommandTypeEnum value of adCmdTableDirect.


Filtering the Results

The Find method limits your search to the contents of one field. The Seek method requires that you have an index and has other limitations as well. If you need to search on multiple fields that are not the basis of an index or if your provider does not support indexes, you can limit your results using the Filter property of the Recordset object.

Use the Filter property to selectively screen out records in a Recordset object. The filtered Recordset becomes the current cursor, which means that records that do not satisfy the Filter criteria are not available in the Recordset until the Filter is removed. Other properties that return values based on the current cursor are affected, such as AbsolutePosition, AbsolutePage, RecordCount, and PageCount. This is because setting the Filter property to a specific value will move the current record to the first record that satisfies the new value.

The Filter property takes a variant argument. This value represents one of three methods for using the Filter property: a criteria string, a FilterGroupEnum constant, or an array of bookmarks. For more information, see Filtering with a Criteria String, Filtering with a Constant, and Filtering with Bookmarks.

Note When you know the data you want to select, it is usually more efficient to open a Recordset with a SQL statement that effectively filters the result set, rather than relying on the Filter property.

To remove a filter from a Recordset, use the adFilterNone constant. Setting the Filter property to a zero-length string ("") has the same effect as using the adFilterNone constant.


Filtering with a Criteria String

The criteria string is made up of clauses in the form FieldName Operator Value (for example, "LastName = 'Smith'"). You can create compound clauses by concatenating individual clauses with AND (for example, "LastName = 'Smith' AND FirstName = 'John'") and OR (for example, "LastName = 'Smith' OR LastName = 'Jones'"). Use the following guidelines for criteria strings:

  • FieldName must be a valid field name from the Recordset. If the field name contains spaces, you must enclose the name in square brackets.
  • Operator must be one of the following: <, >, <=, >=, <>, =, or LIKE.
  • Value is the value with which you will compare the field values (for example, 'Smith', #8/24/95#, 12.345, or $50.00). Use single quotation marks (') with strings and pound signs (#) with dates. For numbers, you can use decimal points, dollar signs, and scientific notation. If Operator is LIKE, Value can use wildcards. Only the asterisk (*) and percent sign (%) wildcards are allowed, and they must be the last character in the string. Value cannot be null.

    Note To include single quotation marks (') in the filter Value, use two single quotation marks to represent one. For example, to filter on O'Malley, the criteria string should be "col1 = 'O''Malley'". To include single quotation marks at both the beginning and the end of the filter value, enclose the string in pound signs (#). For example, to filter on '1', the criteria string should be "col1 = #'1'#".

There is no precedence between AND and OR. Clauses can be grouped within parentheses. However, you cannot group clauses joined by an OR and then join the group to another clause with an AND, like this:

(LastName = 'Smith' OR LastName = 'Jones') AND FirstName = 'John'

Instead, you would construct this filter as:

(LastName = 'Smith' AND FirstName = 'John') OR (LastName = 'Jones' AND FirstName = 'John')

In a LIKE clause, you can use a wildcard at the beginning and end of the pattern (for example, LastName Like '*mit*') or only at the end of the pattern (for example, LastName Like 'Smit*').


Filtering with a Constant

The following constants are available for filtering Recordsets.

Constant | Description
adFilterAffectedRecords
 Filters for viewing only records effected by the last Delete, Resync, UpdateBatch, or CancelBatch call.
adFilterConflictingRecords | Filters for viewing the records that failed the last batch update.
adFilterFetchedRecords | Filters for viewing the records in the current cache—that is, the results of the last call to retrieve records from the database.
adFilterNone | Removes the current filter and restores all records for viewing.
adFilterPendingRecords | Filters for viewing only records that have changed but have not yet been sent to the server. Applicable only for batch update mode.

The filter constants make it easier to resolve individual record conflicts during batch update mode by allowing you to view, for example, only those records that were effected during the last UpdateBatch method call, as shown in the following example:

'BeginDeleteGroup
    'add some bogus records
    With objRs1
        For i = 0 To 8
            .AddNew
            .Fields("CompanyName") = "Shipper Number " & i + 1
            .Fields("Phone") = "(425) 555-000" & (i + 1)
            .Update
        Next i

    're-connect & update
        .ActiveConnection = GetNewConnection
        .UpdateBatch

    'filter on newly added records
        .Filter = adFilterAffectedRecords
        Debug.Print "Deleting the " & .RecordCount & _
                    " records you just added."

    'delete the newly added bogus records
        .Delete adAffectGroup
        .Filter = adFilterNone
        Debug.Print .RecordCount & " records remain."

        .Close
    End With
'EndDeleteGroup


Filtering with Bookmarks

Finally, you can pass a variant array of bookmarks to the Filter property. The resulting cursor will contain only those records whose bookmark was passed in to the property. The following code example creates an array of bookmarks from the records in a Recordset which have a "B" in the ProductName field. It then passes the array to the Filter property and displays information about the resulting filtered Recordset.

'BeginFilterBkmk
Dim vBkmkArray() As Variant

Dim i As Integer
i = 0

'Recordset created using "SELECT * FROM Products" as command.
'So, we will check to see if ProductName has a capital B, and
'if so, add to the array.
Do While Not objRs.EOF
    If InStr(1, objRs("ProductName"), "B") Then
        ReDim Preserve vBkmkArray(i)
        vBkmkArray(i) = objRs.Bookmark
        i = i + 1
        Debug.Print objRs("ProductName")
    End If
    objRs.MoveNext
Loop

'Filter using the array of bookmarks.
objRs.Filter = vBkmkArray

objRs.MoveFirst
Do While Not objRs.EOF
    Debug.Print objRs("ProductName")
    objRs.MoveNext
Loop
'EndFilterBkmk


Creating a Clone of a Recordset

Use the Clone method to create multiple, duplicate Recordset objects, particularly if you want to maintain more than one current record in a given set of records. Using the Clone method is more efficient than creating and opening a new Recordset object with the same definition as the original.

The current record of a newly created clone is originally set to the first record. The current record pointer in a cloned Recordset is not synchronized with the original or vice versa. You can navigate independently in each Recordset.

Changes you make to one Recordset object are visible in all of its clones regardless of cursor type. However, after you execute Requery on the original Recordset, the clones will no longer be synchronized to the original.

Closing the original Recordset does not close its copies, nor does closing a copy close the original or any of the other copies.

You can clone a Recordset object only if it supports bookmarks. Bookmark values are interchangeable; that is, a bookmark reference from one Recordset object refers to the same record in any of its clones.


Chapter 4: Editing Data

The preceding two chapters explained how use ADO to connect to a data source, execute a command, get the results in a Recordset object, and navigate within the Recordset. This chapter focuses on the next fundamental ADO operation: editing data.

This chapter continues to use the sample Recordset introduced in Chapter 3—with one important change. The following code is used to open the Recordset:

    . . .
'BeginEditIntro
    Dim strSQL As String
    Dim objRs1 As ADODB.Recordset

    strSQL = "SELECT * FROM Shippers"

    Set objRs1 = New ADODB.Recordset

    objRs1.Open strSQL, GetNewConnection, adOpenStatic, _
                adLockBatchOptimistic, adCmdText

    ' Disconnect the Recordset from the Connection object.
    Set objRs1.ActiveConnection = Nothing
'EndEditIntro

    . . .

The important change to the code involves setting the Connection object's CursorLocation property equal to adUseClient in the GetNewConnection function (shown below), which indicates the use of a client cursor. For more information about the differences between client-side and server-side cursors, see Chapter 8: Understanding Cursors and Locks.

The CursorLocation property's adUseClient setting moves the location of the cursor from the data source (the SQL Server, in this case) to the location of the client code (the desktop workstation). This setting forces ADO to invoke the Client Cursor Engine for OLE DB on the client in order to create and manage the cursor.

You might also have noticed that the LockType parameter of the Open method changed to adLockBatchOptimistic. This opens the cursor in batch mode. (The provider caches multiple changes and writes them to the underlying data source only when you call the UpdateBatch method.) Changes made to the Recordset will not be updated in the database until the UpdateBatch method is called.

Finally, the code in this chapter uses a modified version of the GetNewConnection function, introduced in Chapter 2. This version of the function now returns a client-side cursor. The function looks like this:

'BeginNewConnection
Public Function GetNewConnection() As ADODB.Connection
    Dim objConn1 As ADODB.Connection
    Set objConn1 = New ADODB.Connection

    strConnStr = "Provider=SQLOLEDB;Initial Catalog=Northwind;" & _
                 "Data Source=MySrvr;User Id=MyId;Password=123aBc;"

    objConn1.ConnectionString = strConnStr
    objConn1.CursorLocation = adUseClient
    objConn1.Open

    Set GetNewConnection = objConn1
End Function
'EndNewConnection


Editing Existing Records

To edit existing records, move to the row you want to edit and change the Value property of the fields you want to change. For more information about the Field object's Value property, see Chapter 3: Examining Data. Depending on your cursor type, you will use Update or UpdateBatch to send changes back to the data source. For more information, see Chapter 5: Updating and Persisting Data.

It is usually more efficient to use a stored procedure with a command object to perform updates, as well as to perform other operations, because a stored procedure does not require the creation of a cursor. For more information about cursors, see Chapter 8: Understanding Cursors and Locks.


Adding Records

Use the AddNew method to create and initialize a new record in an existing Recordset. You can use the Supports method with a CursorOptionEnum value of adAddNew to verify whether you can add records to the current Recordset object.

After you call the AddNew method, the new record becomes the current record and remains current after you call the Update method. If the Recordset object does not support bookmarks, you might not be able to access the new record once you move to another record. Therefore, depending on your cursor type, you might need to call the Requery method to make the new record accessible.

If you call AddNew while editing the current record or while adding a new record, ADO calls the Update method to save any changes and then creates the new record.


Adding Records Using AddNew

This is the basic syntax of the AddNew method:

recordset.AddNew FieldList, Values

The FieldList and Values arguments are optional. FieldList is either a single name or an array of names or ordinal positions of the fields in the new record.

The Values argument is either a single value or an array of values for the fields in the new record.

Typically, when you intend to add a single record, you will call the AddNew method without any arguments. Specifically, you will call AddNew, set the Value of each field in the new record, and then call Update and/or UpdateBatch. You can ensure that your Recordset supports adding new records by using the Supports property with the adAddNew enumerated constant.

The following code uses this technique to add a new Shipper to the sample Recordset. The ShipperID field value is supplied automatically by SQL Server, so the code does not attempt to supply a field value for the new records.

'BeginAddNew1.1
    If objRs1.Supports(adAddNew) Then
        With objRs1
            .AddNew
            .Fields("CompanyName") = "Sample Shipper"
            .Fields("Phone") = "(931) 555-6334"
            .Update
        End With
    End If
'EndAddNew1.1

Because this code uses a disconnected Recordset with a client-side cursor in batch mode, you must reconnect the Recordset to the data source with a new Connection object before you can call the UpdateBatch method to post changes to the database. This is easily done by using the new function GetNewConnection.

'BeginAddNew1.2
    'Re-establish a Connection and update
    Set objRs1.ActiveConnection = GetNewConnection
    objRs1.UpdateBatch
'EndAddNew1.2


Adding Multiple Fields

Occasionally, it might be more efficient to pass in an array of fields and their corresponding values to the AddNew method, rather than setting Value multiple times for each new field. If FieldList is an array, Values must also be an array with the same number of members; otherwise, an error occurs. The order of field names must match the order of field values in each array. The following code passes an array of fields and an array of values to the AddNew method.

'BeginAddNew2
    Dim avarFldNames As Variant
    Dim avarFldValues As Variant

    avarFldNames = Array("CompanyName", "Phone")
    avarFldValues = Array("Sample Shipper 2", "(931) 555-6334")

    If objRs1.Supports(adAddNew) Then
        objRs1.AddNew avarFldNames, avarFldValues
    End If

    'Re-establish a Connection and update
    Set objRs1.ActiveConnection = GetNewConnection
    objRs1.UpdateBatch
'EndAddNew2


Determining Edit Mode

ADO maintains an editing buffer associated with the current record. The EditMode property indicates whether changes have been made to this buffer or whether a new record has been created. Use EditMode to determine the editing status of the current record. You can test for pending changes if an editing process has been interrupted and determine whether you need to use the Update or CancelUpdate method.

EditMode returns one of the EditModeEnum constants, which are listed in the following table.

Constant | Description
adEditNone | Indicates that no editing operation is in progress.
adEditInProgress | Indicates that data in the current record has been modified but not saved.
adEditAdd | Indicates that the AddNew method has been called, and the current record in the copy buffer is a new record that has not been saved to the database.
adEditDelete | Indicates that the current record has been deleted.

EditMode can return a valid value only if there is a current record. EditMode will return an error if BOF or EOF is True or if the current record has been deleted.


Using AddNew in Immediate and Batch Modes

The behavior of the AddNew method depends on the updating mode of the Recordset object and whether you pass the FieldList and Values arguments.

In immediate update mode (in which the provider writes changes to the underlying data source once you call the Update method), calling the AddNew method without arguments sets the EditMode property to adEditAdd. The provider caches any field value changes locally. Calling the Update method posts the new record to the database and resets the EditMode property to adEditNone. If you pass the FieldList and Values arguments, ADO immediately posts the new record to the database (no Update call is necessary); the EditMode property value does not change (adEditNone).

In batch update mode, calling the AddNew method without arguments sets the EditMode property to adEditAdd. The provider caches any field value changes locally. Calling the Update method adds the new record to the current Recordset and resets the EditMode property to adEditNone, but the provider does not post the changes to the underlying database until you call the UpdateBatch method. If you pass the FieldList and Values arguments, ADO sends the new record to the provider for storage in a cache; you need to call the UpdateBatch method to post the new record to the underlying database. For more information about Update and UpdateBatch, see Chapter 5: Updating and Persisting Data.

If the Unique Table dynamic property is set and the Recordset is the result of executing a JOIN operation on multiple tables, the AddNew method can insert fields only into the table named in the Unique Table property.


Determining What is Supported

The Supports method is used to determine whether a specified Recordset object supports a particular type of functionality. It has the following syntax:

boolean = recordset.Supports( CursorOptions )

Supports returns a Boolean value that indicates whether all of the features identified by the CursorOptions argument are supported by the provider. You can use the Supports method to determine what types of functionality a Recordset object supports. If the Recordset object supports the features whose corresponding constants are in CursorOptions, the Supports method returns True. Otherwise, it returns False.

Using the Supports method, you can check for the ability of the Recordset object to add new records, use bookmarks, use the Find method, use scrolling, use the Index property, and to perform batch updates. For a complete list of constants and their meanings, see CursorOptionEnum.

Although the Supports method may return True for a given functionality, it does not guarantee that the provider can make the feature available under all circumstances. The Supports method simply returns whether the provider can support the specified functionality, assuming certain conditions are met. For example, the Supports method may indicate that a Recordset object supports updates, even though the cursor is based on a multiple table join—some columns of which are not updatable.


Deleting Records Using the Delete Method

Using the Delete method marks the current record or a group of records in a Recordset object for deletion. If the Recordset object does not allow record deletion, an error occurs. If you are in immediate update mode, deletions occur in the database immediately. If a record cannot be successfully deleted (due to database integrity violations, for example), the record will remain in edit mode after the call to Update. This means that you must cancel the update using CancelUpdate before moving off the current record (for example, using Close, Move, or NextRecordset).

If you are in batch update mode, the records are marked for deletion from the cache and the actual deletion happens when you call the UpdateBatch method. (To view the deleted records, set the Filter property to adFilterAffectedRecords after Delete is called.)

Attempting to retrieve field values from the deleted record generates an error. After deleting the current record, the deleted record remains current until you move to a different record. Once you move away from the deleted record, it is no longer accessible.

If you nest deletions in a transaction, you can recover deleted records by using the RollbackTrans method. If you are in batch update mode, you can cancel a pending deletion or group of pending deletions by using the CancelBatch method.

If the attempt to delete records fails because of a conflict with the underlying data (for example, a record has already been deleted by another user), the provider returns warnings to the Errors collection but does not halt program execution. A run-time error occurs only if there are conflicts on all the requested records.

If the Unique Table dynamic property is set and the Recordset is the result of executing a JOIN operation on multiple tables, the Delete method will delete rows only from the table named in the Unique Table property.

The Delete method takes an optional argument that allows you to specify which records are affected by the Delete operation. The only valid values for this argument are either of the following ADO AffectEnum enumerated constants:

  • adAffectCurrent Affects only the current record.
  • adAffectGroup Affects only records that satisfy the current Filter property setting. The Filter property must be set to a FilterGroupEnum value or an array of Bookmarks to use this option.

The following code shows an example of specifying adAffectGroup when calling the Delete method. This example adds some records to the sample Recordset and updates the database. Then it filters the Recordset using the adFilterAffectedRecords filter enumerated constant, which leaves only the newly added records visible in the Recordset. Finally, it calls the Delete method and specifies that all of the records that satisfy the current Filter property setting (the new records) should be deleted.

'BeginDeleteGroup
    'add some bogus records
    With objRs1
        For i = 0 To 8
            .AddNew
            .Fields("CompanyName") = "Shipper Number " & i + 1
            .Fields("Phone") = "(425) 555-000" & (i + 1)
            .Update
        Next i

    're-connect & update
        .ActiveConnection = GetNewConnection
        .UpdateBatch

    'filter on newly added records
        .Filter = adFilterAffectedRecords
        Debug.Print "Deleting the " & .RecordCount & _
                    " records you just added."

    'delete the newly added bogus records
        .Delete adAffectGroup
        .Filter = adFilterNone
        Debug.Print .RecordCount & " records remain."

        .Close
    End With
'EndDeleteGroup


Alternatives: Using SQL Statements

ADO also allows using commands as alternatives to its built-in properties and methods for editing data. Depending upon your provider, all operations mentioned in this chapter could also be accomplished by passing commands to your data source. For example, SQL UPDATE statements can be used to modify data without using the Value property of a Field. SQL INSERT statements can be used to add new records to a data source, rather than the ADO method AddNew. For more information about SQL or the data-manipulation language of your provider, see the documentation of your data source.

For example, you can pass a SQL string containing a DELETE statement to a database, as shown in the following code:

'BeginSQLDelete
strSQL = "DELETE FROM Shippers WHERE ShipperID = " & intId
objConn.Execute strSQL, , adCmdText + adExecuteNoRecords
'EndSQLDelete


Chapter 5: Updating and Persisting Data

The preceding chapters have discussed how to use ADO to get to data in a data source, how to move around in the data, and even how to edit the data. Of course, if the goal of your application is to allow users to make changes to the data, you will need to understand how to save those changes. You can either persist the Recordset changes to a file using the Save method, or you can send the changes back to the data source for storage using the Update or UpdateBatch methods.

In the preceding chapters, you changed the data in several rows of the Recordset. ADO supports two basic notions relating to the addition, deletion, and modification of rows of data.

The first notion is that changes aren't immediately made to the Recordset; instead, they are made to an internal copy buffer. If you decide you don't want the changes, the modifications in the copy buffer are discarded. If you decide to keep the changes, the changes in the copy buffer are applied to the Recordset.

The second notion is that changes are either propagated to the data source as soon as you declare the work on a row complete (that is, immediate mode), or all changes to a set of rows are collected until you declare the work for the set complete (that is, batch mode). The LockType property determines when the changes are made to the underlying data source. adLockOptimistic or adLockPessimistic specifies immediate mode, while adLockBatchOptimistic specifies batch mode. The CursorLocation property can affect which LockType settings are available. For instance, the adLockPessimistic setting is not supported if the CursorLocation property is set to adUseClient.

In immediate mode, each invocation of the Update method propagates the changes to the data source. In batch mode, each invocation of Update or movement of the current row position saves the changes to the copy buffer, but only the UpdateBatch method propagates the changes to the data source.


Updating Data

Update behavior and functionality is largely dependent upon update mode (lock type), cursor type, and cursor location.

Use the Update method to save any changes you have made to the current record of a Recordset object since calling the AddNew method or since changing any field values in an existing record. The Recordset object must support updates.

If the Recordset object supports batch updating, you can cache multiple changes to one or more records locally until you call the UpdateBatch method. If you are editing the current record or adding a new record when you call the UpdateBatch method, ADO will automatically call the Update method to save any pending changes to the current record before transmitting the batched changes to the provider.

The current record remains current after you call the Update or UpdateBatch methods.


Immediate Mode

Immediate mode is in effect when the LockType property is set to adLockOptimistic or adLockPessimistic. In immediate mode, changes to a record are propagated to the data source as soon as you declare the work on a row complete by calling the Update method.

Calling Update

If you move from the record you are adding or editing before calling the Update method, ADO will automatically call Update to save the changes. You must call the CancelUpdate method before navigation if you want to cancel any changes made to the current record or discard a newly added record.

The current record remains current after you call the Update method.

CancelUpdate

Use the CancelUpdate method to cancel any changes made to the current row or to discard a newly added row. You cannot cancel changes to the current row or a new row after you call the Update method, unless the changes are either part of a transaction that you can roll back with the RollbackTrans method or part of a batch update. In the case of a batch update, you can cancel the Update with the CancelUpdate or CancelBatch method.

If you are adding a new row when you call the CancelUpdate method, the current row becomes the row that was current before the AddNew call.

If you have not changed the current row or added a new row, calling the CancelUpdate method generates an error.


Batch Mode

Batch mode is in effect when the LockType property is set to adLockBatchOptimistic and batch updating is supported by the provider. Certain lock type settings are not available depending on cursor location. For instance, a pessimistic lock type is not available when the CursorLocation is set to adUseClient. Conversely, a provider may not support a batch optimistic lock when the cursor location is on the server. You should use batch updating with either a keyset or static cursor only.

The UpdateBatch method is used to send Recordset changes held in the copy buffer to the server to update the data source. In the following section, we will open a Recordset in batch mode, make changes to the copy buffer, and then send our changes to the data source using a call to UpdateBatch.


Sending the Updates: UpdateBatch

ADO

Sending the Updates: UpdateBatch Method

The following code opens a Recordset in batch mode by setting the LockType property to adLockBatchOptimistic and the CursorLocation to adUseClient. It adds two new records and changes the value of a field in an existing record, saving the original values, and then calls UpdateBatch to send the changes back to the data source.

'BeginBatchUpdate
    strConn = "Provider=SQLOLEDB;Initial Catalog=Northwind;" & _
              "Data Source=dhale1;User Id=MyId;Password=123aBc;"

    strSQL = "SELECT ShipperId, CompanyName, Phone FROM Shippers"

    Set objRs1 = New ADODB.Recordset
    objRs1.CursorLocation = adUseClient
    objRs1.Open strSQL, strConn, adOpenStatic, adLockBatchOptimistic, adCmdText

    ' Change value of Phone field for first record in Recordset, saving value
    ' for later restoration.
    intId = objRs1("ShipperId")
    sPhone = objRs1("Phone")

    objRs1("Phone") = "(111) 555-1111"

    'Add two new records
    For i = 0 To 1
        objRs1.AddNew
        objRs1(1) = "New Shipper #" & CStr((i + 1))
        objRs1(2) = "(nnn) 555-" & i & i & i & i
    Next i

    ' Send the updates
    objRs1.UpdateBatch
'EndBatchUpdate

If you are editing the current record or adding a new record when you call the UpdateBatch method, ADO will automatically call the Update method to save any pending changes to the current record before transmitting the batched changes to the provider.


Filtering for Updated Records

Before you call UpdateBatch, you can use the Recordset Filter property to view only those records which have been changed since the Recordset was opened or the last call to UpdateBatch. To do this, set Filter equal to adFilterPendingRecords to determine how many records will be updated, as shown below.

This example extends the previous UpdateBatch example by filtering the Recordset just before calling the UpdateBatch, showing the user which records will change and allowing her to cancel the update (using the CancelBatch method).

'BeginFilterPend
    '...
    objRs1.Open strSQL, strConn, adOpenStatic, adLockBatchOptimistic, adCmdText

    ' Change value of Phone field for first record in Recordset, saving value
    ' for later restoration.
    intId = objRs1("ShipperId")
    sPhone = objRs1("Phone")

    objRs1("Phone") = "(111) 555-1111"

    'Add two new records
    For i = 0 To 1
        objRs1.AddNew
        objRs1(1) = "New Shipper #" & CStr((i + 1))
        objRs1(2) = "(nnn) 555-" & i & i & i & i
    Next i

    objRs1.Filter = adFilterPendingRecords

    MsgBox "There are " & objRs1.RecordCount & " records pending.", _
            , "Filter Pending"

    ' Cancel the updates
    objRs1.CancelBatch
'EndFilterPend


Dealing with Failed Updates

When an update concludes with errors, how you resolve the errors depends on the nature and severity of the errors and the logic of your application. However, if the database is shared with other users, a typical error is that someone else modifies the field before you do. This type of error is called a conflict. ADO detects this situation and reports an error.

If there are update errors, they will be trapped in an error-handling routine. Filter the Recordset with the adFilterConflictingRecords constant so that only the conflicting rows are visible. In this example, the error-resolution strategy is merely to print the author's first and last names (au_fname and au_lname).

The code to alert the user to the update conflict looks like this:

objRs.Filter = adFilterConflictingRecords
objRs.MoveFirst
Do While Not objRst.EOF
   Debug.Print "Conflict: Name =  "; objRs!au_fname; " "; objRs!au_lname
   objRs.MoveNext
Loop


Detecting and Resolving Conflicts

If you are dealing with your Recordset in immediate mode, there is much less chance for concurrency problems to arise. On the other hand, if your application uses batch mode updating, there may be a good chance that one user will change a record before changes made by another user editing the same record are saved. In such a case, you will want your application to gracefully handle the conflict. It may be your wish that the last person to send an update to the server "wins." Or you may want to let the most recent user to decide which update should take precedence by providing him with a choice between the two conflicting values.

Whatever the case, ADO provides the UnderlyingValue and OriginalValue properties of the Field object in order to handle these types of conflicts. Use these properties in combination with the Resync method and Filter property of the Recordset.

Detecting Errors

When ADO encounters a conflict during a batch update, a warning will be placed in the Errors collection. Therefore, you should always check for errors immediately after calling BatchUpdate, and if you find them, begin testing the assumption that you have encountered a conflict. The first step is to set the Filter property on the Recordset equal to adFilterConflictingRecords (the Filter property is discussed in the preceding chapter). This limits the view on your Recordset to only those records that are in conflict. If the RecordCount property is equal to zero after this step, you know the error was raised by something other than a conflict.

When you call BatchUpdate, ADO and the provider are generating SQL statements to perform updates on the data source. Remember that certain data sources have limitations on which types of columns can be used in a WHERE clause.

Next, call the Resync method on the Recordset with the AffectRecords argument set equal to adAffectGroup and the ResyncValues argument set equal to adResyncUnderlyingValues. The Resync method refreshes the data in the current Recordset object from the underlying database. By using adAffectGroup, you are ensuring that only the records visible with the current filter setting, that is, only the conflicting records, are resynchronized with the database. This could make a significant performance difference if you are dealing with a large Recordset. By setting the ResyncValues argument to adResyncUnderlyingValues when calling Resync, you ensure that the UnderlyingValue property will contain the (conflicting) value from the database, that the Value property will maintain the value entered by the user, and that the OriginalValue property will hold the original value for the field (the value it had before the last successful UpdateBatch call was made). You can then use these values to resolve the conflict programmatically or require the user to choose the value that will be used.

This technique is shown in the code example below. The example artificially creates a conflict by using a separate Recordset to change a value in the underlying table before UpdateBatch is called.

'BeginConflicts
    strConn = "Provider=SQLOLEDB;Initial Catalog=Northwind;" & _
              "Data Source=dhale1;User Id=MyId;Password=123aBc;"

    strSQL = "SELECT * FROM Shippers WHERE ShipperID = 2"

    'Open Rs and change a value
    Set objRs1 = New ADODB.Recordset
    objRs1.CursorLocation = adUseClient
    objRs1.Open strSQL, strConn, adOpenStatic, adLockBatchOptimistic, adCmdText
    objRs1("Phone") = "(111) 555-1111"

    'Introduce a conflict at the db...
    objRs2.Open strSQL, strConn, adOpenKeyset, adLockOptimistic, adCmdText
    objRs2("Phone") = "(999) 555-9999"
    objRs2.Update
    objRs2.Close
    Set objRs2 = Nothing

    On Error Resume Next
    objRs1.UpdateBatch

    If objRs1.ActiveConnection.Errors.Count <> 0 Then
        Dim intConflicts As Integer

        intConflicts = 0

        objRs1.Filter = adFilterConflictingRecords

        intConflicts = objRs1.RecordCount

        'Resync so we can see the UnderlyingValue and offer user a choice.
        'This sample only displays all three values and resets to original.
        objRs1.Resync adAffectGroup, adResyncUnderlyingValues

        If intConflicts > 0 Then
            strMsg = "A conflict occurred with updates for " & intConflicts & _
                     " record(s)." & vbCrLf & "The values will be restored" & _
                     " to their original values." & vbCrLf & vbCrLf

            objRs1.MoveFirst
            While Not objRs1.EOF
              strMsg = strMsg & "SHIPPER = " & objRs1("CompanyName") & vbCrLf
              strMsg = strMsg & "Value = " & objRs1("Phone").Value & vbCrLf
              strMsg = strMsg & "UnderlyingValue = " & _
                                 objRs1("Phone").UnderlyingValue & vbCrLf
              strMsg = strMsg & "OriginalValue = " & _
                                 objRs1("Phone").OriginalValue & vbCrLf
              strMsg = strMsg & vbCrLf & "Original value has been restored."

              MsgBox strMsg, vbOKOnly, _
                    "Conflict " & objRs1.AbsolutePosition & _
                    " of " & intConflicts

              objRs1("Phone").Value = objRs1("Phone").OriginalValue
              objRs1.MoveNext
            Wend

            objRs1.UpdateBatch adAffectGroup
        Else
            'Other error occurred. Minimal handling in this example.
             strMsg = "Errors occurred during the update. " & _
                        objRs1.ActiveConnection.Errors(0).Number & " " & _
                        objRs1.ActiveConnection.Errors(0).Description
        End If

        On Error GoTo 0
    End If

    objRs1.MoveFirst
    objRs1.Close
    Set objRs1 = Nothing
'EndConflicts

You can use the Status property of the current Record or of a specific Field to determine what kind of a conflict has occurred.

For more detailed information on error handling, see Chapter 6: Error Handling.


Disconnecting and Reconnecting the Recordset

One of the most powerful features found in ADO is the capability to open a client-side Recordset from a data source and then disconnect the Recordset from the data source. Once the Recordset has been disconnected, the connection to the data source can be closed, thereby releasing the resources on the server used to maintain it. You can continue to view and edit the data in the Recordset while it is disconnected and later reconnect to the data source and send your updates in batch mode.

To disconnect a Recordset, open it with a cursor location of adUseClient, and then set the ActiveConnection property equal to Nothing. (C++ users should set the ActiveConnection equal to NULL to disconnect.)

We will use a disconnected Recordset later in this chapter when we discuss Recordset persistence to address a scenario in which we need to have the data in a Recordset available to an application while the client computer is not connected to a network.


Updating JOINed Results: Unique Table

ADO enables you to closely control modifications to a particular base table in a Recordset that was formed by a JOIN operation on multiple base tables using the Unique Table dynamic property. For details on using Unique Table, refer to the ADO Programmer's Reference topics on the Unique Table and Update Resynch dynamic properties.


Transaction Processing

ADO provides the following methods for controlling transactions: BeginTrans, CommitTrans, and RollbackTrans. Use these methods with a Connection object when you want to save or cancel a series of changes made to the source data as a single unit. For example, to transfer money between accounts, you subtract an amount from one and add the same amount to the other. If either update fails, the accounts no longer balance. Making these changes within an open transaction ensures that either all or none of the changes go through.

Note Not all providers support transactions. Verify that the provider-defined property "Transaction DDL" appears in the Connection object's Properties collection, indicating that the provider supports transactions. If the provider does not support transactions, calling one of these methods will return an error.

After you call the BeginTrans method, the provider will no longer instantaneously commit changes you make until you call CommitTrans or RollbackTrans to end the transaction.

Calling the CommitTrans method saves changes made within an open transaction on the connection and ends the transaction. Calling the RollbackTrans method reverses any changes made within an open transaction and ends the transaction. Calling either method when there is no open transaction generates an error.

Depending on the Connection object's Attributes property, calling either the CommitTrans or RollbackTrans method may automatically start a new transaction. If the Attributes property is set to adXactCommitRetaining, the provider automatically starts a new transaction after a CommitTrans call. If the Attributes property is set to adXactAbortRetaining, the provider automatically starts a new transaction after a RollbackTrans call.

Transaction Isolation Level

Use the IsolationLevel property to set the isolation level of a transaction on a Connection object. The setting does not take effect until the next time you call the BeginTrans method. If the level of isolation you request is unavailable, the provider may return the next greater level of isolation. Refer to the IsolationLevel property in the ADO Programmer's Reference for more details on valid values.

Nested Transactions

For providers that support nested transactions, calling the BeginTrans method within an open transaction starts a new, nested transaction. The return value indicates the level of nesting: a return value of "1" indicates you have opened a top-level transaction (that is, the transaction is not nested within another transaction), "2" indicates that you have opened a second-level transaction (a transaction nested within a top-level transaction), and so forth. Calling CommitTrans or RollbackTrans affects only the most recently opened transaction; you must close or roll back the current transaction before you can resolve any higher-level transactions.


Persisting Data

Portable computing (for example, using laptops) has generated the need for applications that can run in both a connected and disconnected state. ADO has added support for this by giving the developer the ability to save a client cursor Recordset to disk and reload it later.

There are several scenarios in which you could use this type of feature, including the following:

  • Traveling: When taking the application on the road, it is vital to supply the ability to make changes and add new records that can then be reconnected to the database later and committed.
  • Infrequently updated lookups: Often in an application, tables are used as lookups—for example, state tax tables. They are infrequently updated and are read-only. Instead of rereading this data from the server each time the application is started, the application can simply load the data from a locally persisted Recordset.

In ADO, to save and load Recordsets, use the Recordset.Save and Recordset.Open(,,,,adCmdFile) methods on the ADO Recordset object.

You can use the Recordset Save method to persist your ADO Recordset to a file on a disk. (You can also save a Recordset to an ADO Stream object. Stream objects are discussed later in the guide.) Later, you can use the Open method to reopen the Recordset when you are ready to use it. By default, ADO saves the Recordset into the proprietary Microsoft Advanced Data TableGram (ADTG) format. This binary format is specified using the adPersistADTG PersistFormatEnum value. Alternatively, you may choose to save your Recordset out as XML instead using adPersistXML. For more information about saving Recordsets as XML, see Persisting Records in XML Format.

The syntax of the Save method is as follows:

recordset.Save Destination, PersistFormat

The first time you save the Recordset, it is optional to specify Destination. If you omit Destination, a new file will be created with a name set to the value of the Source property of the Recordset.

Omit Destination when you subsequently call Save after the first save or a run-time error will occur. If you subsequently call Save with a new Destination, the Recordset is saved to the new destination. However, the new destination and the original destination will both be open.

Save does not close the Recordset or Destination, so you can continue to work with the Recordset and save your most recent changes. Destination remains open until the Recordset is closed, during which time other applications can read but not write to Destination.

For reasons of security, the Save method permits only the use of low and custom security settings from a script executed by Microsoft Internet Explorer. For a more detailed explanation of security issues, see "ADO and RDS Security Issues in Microsoft Internet Explorer" under ActiveX Data Objects (ADO) Technical Articles in Microsoft Data Access Technical Articles.

If the Save method is called while an asynchronous Recordset fetch, execute, or update operation is in progress, Save waits until the asynchronous operation is complete.

Records are saved beginning with the first row of the Recordset. When the Save method is finished, the current row position is moved to the first row of the Recordset.

For best results, set the CursorLocation property to adUseClient with Save. If your provider does not support all of the functionality necessary to save Recordset objects, the Cursor Service will provide that functionality.

When a Recordset is persisted with the CursorLocation property set to adUseServer, the update capability for the Recordset is limited. Typically, only single-table updates, insertions, and deletions are allowed (dependent on provider functionality). The Resync method is also unavailable in this configuration.

Because the Destination parameter can accept any object that supports the OLE DB IStream interface, you can save a Recordset directly to the ASP Response object.

In the following example, the Save and Open methods are used to persist a Recordset and later reopen it:

'BeginPersist
   conn.ConnectionString = _
   "Provider=SQLOLEDB;Data Source=(local);" _
      & "User ID=sa;Password='';Initial Catalog=pubs"
   conn.Open

   conn.Execute "create table testtable (dbkey int " & _
      "primary key, field1 char(10))"
   conn.Execute "insert into testtable values (1, 'string1')"

   Set rst.ActiveConnection = conn
   rst.CursorLocation = adUseClient

   rst.Open "select * from testtable", conn, adOpenStatic, _
      adLockBatchOptimistic

   'Change the row on the client
   rst!field1 = "NewValue"

   'Save to a file--the .dat extension is an example; choose
   'your own extension. The changes will be saved in the file
   'as well as the original data.
   MyFile = Dir("c:\temp\temptbl.dat")
   If MyFile <> "" Then
       Kill "c:\temp\temptbl.dat"
   End If

   rst.Save "c:\temp\temptbl.dat", adPersistADTG
   Set rst = Nothing

   'Now reload the data from the file
   Set rst = New ADODB.Recordset
   rst.Open "c:\temp\temptbl.dat", , adOpenStatic, _
      adLockBatchOptimistic, adCmdFile

   Debug.Print "After Loading the file from disk"
   Debug.Print "   Current Edited Value: " & rst!field1.Value
   Debug.Print "   Value Before Editing: " & rst!field1.OriginalValue

   'Note that you can reconnect to a connection and
   'submit the changes to the data source
   Set rst.ActiveConnection = conn
   rst.UpdateBatch
'EndPersist


More About Recordset Persistence

The Microsoft OLE DB Persistence Provider supports storing a Recordset object in a file with the Recordset object's Save method. The persistently stored file may exist on a local drive, network server, or as a URL on a Web site. Later, the file can be restored with either the Recordset object's Open method or the Connection object's Execute method.

In addition, the GetString method converts a Recordset object to a form in which the columns and rows are delimited with characters you specify.

To persist a Recordset, begin by converting it to a form that can be stored in a file. Recordset objects can be stored in the proprietary Advanced Data TableGram (ADTG) format or the open Extensible Markup Language (XML) format. ADTG examples are shown below. For more information about XML persistence, see Persisting Records in XML format.

Save any pending changes in the persisted file. Doing this allows you to issue a query that returns a Recordset object, edits the Recordset, saves it and the pending changes, later restores the Recordset, and then updates the data source with the saved pending changes.

For information about persistently storing Stream objects, see Streams and Persistence in Chapter 10.

For an example of Recordset persistence, see the XML Recordset Persistence Scenario.

Example

Save a Recordset:

Dim rs as New ADODB.Recordset
rs.Save "c:\yourFile.adtg", adPersistADTG

Open a persisted file with Recordset.Open:

Dim rs as New ADODB.Recordset
rs.Open "c:\yourFile.adtg", "Provider=MSPersist",,,adCmdFile

Optionally, if the Recordset does not have an active connection, you can accept all the defaults and simply code the following:

Dim rs as New ADODB.Recordset
rs.Open "c:\yourFile.adtg"

Open a persisted file with Connection.Execute:

Dim conn as New ADODB.Connection
Dim rs as ADODB.Recordset
conn.Open "Provider=MSPersist"
Set rs = conn.execute("c:\yourFile.adtg")

Open a persisted file with RDS.DataControl:

In this case, the Server property is not set.

Dim dc as New RDS.DataControl
dc.Connection = "Provider=MSPersist"
dc.SQL = "c:\yourFile.adtg"
dc.Refresh

See Also

GetString Method | Microsoft OLE DB Persistence Provider | Recordset Object | Streams and Persistence


Persisting Filtered and Hierarchical Recordsets

If the Filter property is in effect for the Recordset, only the rows accessible under the filter are saved. If the Recordset is hierarchical, the current child Recordset and its children are saved, including the parent Recordset. If the Save method of a child Recordset is called, the child and all its children are saved, but the parent is not. For more information about hierarchical Recordsets, see Chapter 9: Data Shaping.

Note Some limitations apply when saving hierarchical Recordsets (data shapes) in XML format. For more information, see Hierarchical Recordsets in XML.


Persisting Records in XML Format

Like ADTG format, Recordset persistence in XML format is implemented with the Microsoft OLE DB Persistence Provider. This provider generates a forward-only, read-only rowset from a saved XML file or stream that contains the schema information generated by ADO. Similarly, it can take an ADO Recordset, generate XML, and save it to a file or any object that implements the COM IStream interface. (In fact, a file is just another example of an object that supports IStream.) For versions 2.5 and later, ADO relies on the Microsoft XML Parser (MSXML) to load the XML into the Recordset; therefore msxml.dll is required. For version 2.5, MSXML shipped with Internet Explorer 5. For version 2.6, MSXML shipped with SQL Server 2000.

Note Some limitations apply when saving hierarchical Recordsets (data shapes) to XML format. You cannot save to XML if the hierarchical Recordset contains pending updates, and you cannot save a parameterized hierarchical Recordset. For more information, see Hierarchical Recordsets in XML.

The easiest way to persist data into XML and load it back again through ADO is with the Save and Open methods, respectively. The following ADO code example demonstrates saving the data in the Titles table to a file named titles.sav.

Dim rs as new Recordset
Dim rs2 as new Recordset
Dim c as new Connection
Dim s as new Stream

' Query the Titles table.
c.Open "provider=sqloledb;data source=mydb;initial catalog=pubs;user id=sa;password="
rs.cursorlocation = adUseClient
rs.Open "select * from titles", c, adOpenStatic

' Save to the file in the XML format. Note that if you don't specify
' adPersistXML, a binary format (ADTG) will be used by default.
rs.Save "titles.sav", adPersistXML

' Save the Recordset into the ADO Stream object.
rs.save s, adPersistXML
rs.Close
c.Close

set rs = nothing

' Reopen the file.
rs.Open "titles.sav",,,,adCmdFile
' Open the Stream back into a Recordset.
rs2.open s

ADO always persists the entire Recordset object. If you wish to only persist a subset of rows of the Recordset object, use the Filter method to narrow down the rows or change your selection clause. However, you must open a Recordset object with a client-side cursor (CursorLocation = adUseClient) to use the Filter method for saving a subset of rows. For example, to retrieve titles that start with the letter "b," you can apply a filter to an open Recordset object:

rs.Filter "title_id like 'B*'"
rs.Save "btitles.sav", adPersistXML

ADO always uses the Client Cursor Engine rowset to produce a scrollable, bookmarkable Recordset object on top of the forward-only data generated by the Persistence Provider.


XML Persistence Format

ADO uses UTF-8 encoding for the XML stream it persists.

The ADO XML format is broken into two sections, a schema section followed by the data section. The following is an example XML file for the Shippers table from the Northwind database. Various parts of the XML are discussed following the example.

<xml xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
xmlns:rs="urn:schemas-microsoft-com:rowset"
xmlns:z="#RowsetSchema">
  <s:Schema id="RowsetSchema">
    <s:ElementType name="row" content="eltOnly" rs:updatable="true">
      <s:AttributeType name="ShipperID" rs:number="1"
        rs:basetable="shippers" rs:basecolumn="ShipperID"
        rs:keycolumn="true">
        <s:datatype dt:type="int" dt:maxLength="4" rs:precision="10"
          rs:fixedlength="true" rs:maybenull="false"/>
      </s:AttributeType>
      <s:AttributeType name="CompanyName" rs:number="2"
        rs:nullable="true" rs:write="true" rs:basetable="shippers"
        rs:basecolumn="CompanyName">
        <s:datatype dt:type="string" dt:maxLength="40" />
      </s:AttributeType>
      <s:AttributeType name="Phone" rs:number="3" rs:nullable="true"
        rs:write="true" rs:basetable="shippers"
        rs:basecolumn="Phone">
        <s:datatype dt:type="string" dt:maxLength="24"/>
      </s:AttributeType>
      <s:extends type="rs:rowbase"/>
    </s:ElementType>
  </s:Schema>

  <rs:data>
    <z:row ShipperID="1" CompanyName="Speedy Express"
      Phone="(503) 555-9831"/>
    <z:row ShipperID="2" CompanyName="United Package"
      Phone="(503) 555-3199"/>
    <z:row ShipperID="3" CompanyName="Federal Shipping"
      Phone="(503) 555-9931"/>
  </rs:data>
</xml>

The schema shows the declarations of namespaces, the schema section, and the data section. The schema section contains definitions for row, ShipperID, CompanyName, and Phone.

Schema definitions conform to the XML-Data specification and are able to be fully validated (though validation will not occur in Internet Explorer 5). You can view this specification at http://www.w3.org/TR/1998/NOTE-XML-data/. XML-Data is the only supported schema format for Recordset persistence currently.

The data section has three rows containing information about shippers. For an empty rowset, the data section may be empty, but the <rs:data> tags must be present. With no data, you could write the tag shorthand as simply <rs:data/>. Any tag prefixed with "rs" indicates that it is in the namespace defined by urn:schemas-microsoft-com:rowset. The full definition of this schema is defined in the appendix to this document.


Namespaces

The XML persistence format in ADO uses the following four namespaces.

Prefix | Description
s | Refers to the "XML-Data" namespace containing the elements and attributes that define the schema of the current Recordset.
dt | Refers to the data type definitions specification.
rs | Refers to the namespace containing elements and attributes specific to ADO Recordset properties and attributes.
z | Refers to the schema of the current rowset.

A client should not add its own tags to these namespaces, as defined by the specification. For example, a client should not define a namespace as "urn:schemas-microsoft-com:rowset" and then write out something such as "rs:MyOwnTag." To learn more about namespaces, see http://www.w3.org/TR/REC-xml-names/.

Important The ID for the schema tag must be "RowsetSchema," and the namespace used to refer to the schema of the current rowset must point to "#RowsetSchema."

Note that the prefix of the namespace, that part to the right of the colon and to the left of the equal sign, is arbitrary.

xmlns:rs="urn:schemas-microsoft-com:rowset"

The user can define this to be any name as long as this name is consistently used throughout the XML document. ADO always writes out "s," "rs," "dt," and "z," but these prefix names are not hard-coded into the loading component.


Schema Section

The schema section is required. As the previous example shows, ADO writes out detailed metadata about each column to preserve the semantics of the data values as much as possible for updating. However, to load in the XML, ADO only requires the names of the columns and the rowset to which they belong. Here is an example of a minimal schema:

<xml xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
    xmlns:rs="urn:schemas-microsoft-com:rowset"
    xmlns:z="#RowsetSchema">
  <s:Schema id="RowsetSchema">
    <s:ElementType name="row" content="eltOnly">
      <s:AttributeType name="ShipperID"/>
      <s:AttributeType name="CompanyName"/>
      <s:AttributeType name="Phone"/>
      <s:Extends type="rs:rowbase"/>
    </s:ElementType>
  </s:Schema>
  <rs:data>
...
  </rs:data>
</xml>

In the case above, ADO will treat the data as variable length strings because no type information is included in the schema.

Creating Aliases for Column Names

The rs:name attribute allows you to create an alias for a column name so that a friendly name may appear in the column information exposed by the rowset and a shorter name may be used in the data section. For example, the schema above could be modified to map ShipperID to s1, CompanyName to s2, and Phone to s3 as follows:

<s:Schema id="RowsetSchema">
<s:ElementType name="row" content="eltOnly" rs:updatable="true">
<s:AttributeType name="s1" rs:name="ShipperID" rs:number="1" ...>
...
</s:AttributeType>
<s:AttributeType name="s2" rs:name="CompanyName" rs:number="2" ...>
...
</s:AttributeType>
<s:AttributeType name="s3" rs:name="Phone" rs:number="3" ...>
...
</s:AttributeType>
...
</s:ElementType>
</s:Schema>

Then, in the data section, the row would use the name attribute (not rs:name) to refer to that column:

"<row s1="1" s2="Speedy Express" s3="(503) 555-9831"/>

Creating aliases for column names is required whenever a column name is not a legal attribute or tag name in XML. For example, "LastName" must have an alias because names with embedded spaces are illegal attributes. The following line won't be correctly handled by the XML parser, so you must create an alias to some other name that does not have an embedded space:

<row last name="Jones"/>

Whatever value you use for the name attribute must be used consistently in each place that the column is referenced in both the schema and data sections of the XML document. The following example shows the consistent use of s1:

<s:Schema id="RowsetSchema">
  <s:ElementType name="row" content="eltOnly">
    <s:attribute type="s1"/>
    <s:attribute type="CompanyName"/>
    <s:attribute type="s3"/>
    <s:extends type="rs:rowbase"/>
  </s:ElementType>
  <s:AttributeType name="s1" rs:name="ShipperID" rs:number="1"
    rs:maydefer="true" rs:writeunknown="true">
    <s:datatype dt:type="i4" dt:maxLength="4" rs:precision="10"
      rs:fixedlength="true" rs:maybenull="true"/>
  </s:AttributeType>
</s:Schema>
<rs:data>
  <z:row s1="1" CompanyName="Speedy Express" s3="(503) 555-9831"/>
</rs:data>

Similarly, because there is no alias defined for CompanyName above, CompanyName must be used consistently throughout the document.

Data Types

You can apply a data type to a column with the dt:type attribute. For the definitive guide to allowable XML types, see http://www.w3.org/TR/1998/NOTE-XML-data-0105/#Datatypes. You can specify a data type in two ways: either specify the dt:type attribute directly on the column definition itself or use the s:datatype construct as a nested element of the column definition. For example,

<s:AttributeType name="Phone" >
  <s:datatype dt:type="string"/>
</s:AttributeType>

is equivalent to

<s:AttributeType name="Phone" dt:type="string"/>

If you omit the dt:type attribute entirely from the row definition, by default, the column's type will be a variable length string.

If you have more type information than simply the type name (for example, dt:maxLength), it makes it more readable to use the s:datatype child element. This is merely a convention, however, and not a requirement.

The following examples show further how to include type information in your schema:

<!-- 1. String with no max length -->
<s:AttributeType name="title_id"/>
<!—or -->
<s:AttributeType name="title_id" dt:type="string"/>

<!—- 2. Fixed length string with max length of 6 -->
<s:AttributeType name="title_id">
    <s:datatype dt:type="string" dt:maxLength="6" rs:fixedlength="true" />
</s:AttributeType>

<!—- 3. Variable length string with max length of 6 -->
<s:AttributeType name="title_id">
    <s:datatype dt:type="string" dt:maxLength="6" />
</s:AttributeType>

<!—- 4. Integer -->
<s:AttributeType name="title_id" dt:type="int"/>

There is a subtle use of the rs:fixedlength attribute in the second example. A column with the rs:fixedlength attribute set to true means that the data must have the length defined in the schema. In this case, a legal value for title_id is "123456," as is "123 ." However, "123" would not be valid because its length is 3, not 6. See the OLE DB Programmer's Guide for a more complete description of the fixedlength property.

Handling Nulls

Null values are handled by the rs:maybenull attribute. If this attribute is set to true, the contents of the column may contain a null value. Furthermore, if the column is not found in a row of data, the user reading the data back from the rowset will get a null status from IRowset::GetData(). Consider the following column definitions from the Shippers table:

<s:AttributeType name="ShipperID">
  <s:datatype dt:type="int" dt:maxLength="4"/>
</s:AttributeType>
<s:AttributeType name="CompanyName">
  <s:datatype dt:type="string" dt:maxLength="40" rs:maybenull="true"/>
</s:AttributeType>

The definition allows CompanyName to be null, but ShipperID cannot contain a null value. If the data section contained the following row, the Persistence Provider would set the status of the data for the CompanyName column to the OLE DB status constant DBSTATUS_S_ISNULL:

<z:row ShipperID="1"/>

If the row was entirely empty, as follows, the Persistence Provider would return an OLE DB status of DBSTATUS_E_UNAVAILABLE for ShipperID and DBSTATUS_S_ISNULL for CompanyName.

<z:row/>

Note that a zero-length string is not the same as null.

<z:row ShipperID="1" CompanyName=""/>

For the preceding row, the Persistence Provider will return an OLE DB status of DBSTATUS_S_OK for both columns. The CompanyName in this case is simply "" (a zero-length string).

For further information about the OLE DB constructs available for use within the schema of an XML document for OLE DB, see the definition of "urn:schemas-microsoft-com:rowset" and the OLE DB Programmer's Guide.


Data Section

The data section defines the data of the rowset along with any pending updates, insertions, or deletions. The data section may contain zero or more rows. It may only contain data from one rowset where the row is defined by the schema. Also, as noted before, columns without any data may be omitted. If an attribute or subelement is used in the data section and that construct has not been defined in the schema section, it is silently ignored.

String

Reserved XML characters in text data must be replaced with appropriate character entities. For example, in the company name "Joe's Garage," the single quote character must be replaced by an entity. The actual row would look like:

<z:row CompanyName="Joe&apos;s Garage"/>

The following characters are reserved in XML and must be replaced by character entities: {',",&,<,>}.

Binary

Binary data is bin.hex encoded (that is, one byte maps to two characters, one character per nibble).

DateTime

The variant VT_DATE format is not directly supported by XML-Data data types. The correct format for dates with both a data and time component is yyyy-mm-ddThh:mm:ss.

For more information about date formats specified by XML, refer to http://www.w3.org/TR/1998/NOTE-XML-data-0105/#Datatypes.

When the XML-Data specification defines two equivalent data types (for example, i4 == int), ADO will write out the friendly name but read in both.

Managing Pending Changes

A Recordset can be opened in immediate or batch update mode. When opened in batch update mode with client-side cursors, all changes made to the Recordset are in a pending state until the UpdateBatch method is called. Pending changes are also persisted when the Recordset is saved. In XML, they are represented by the use of the "update" elements defined in urn:schemas-microsoft-com:rowset. In addition, if a rowset can be updated, the updatable property must be set to true in the definition of the row. For example, to define that the Shippers table contains pending changes, the row definition would look like the following:

<s:ElementType name="row" content="eltOnly" updatable="true">
  <s:attribute type="ShipperID"/>
  <s:attribute type="CompanyName"/>
  <s:attribute type="Phone"/>
  <s:extends type="rs:rowbase"/>
</s:ElementType>

This tells the Persistence Provider to surface data so that ADO can construct an updatable Recordset object.

The following sample data shows how insertions, changes, and deletions look in the persisted file:

<rs:data>
  <z:row ShipperID="2" CompanyName="United Package"
    Phone="(503) 555-3199"/>
<rs:update>
  <rs:original>
    <z:row ShipperID="3" CompanyName="Federal Shipping"
      Phone="(503) 555-9931"/>
  </rs:original>
  <z:row Phone="(503) 552-7134"/>
</rs:update>
<rs:insert>
  <z:row ShipperID="12" CompanyName="Lightning Shipping"
    Phone="(505) 111-2222"/>
  <z:row ShipperID="13" CompanyName="Thunder Overnight"
    Phone="(505) 111-2222"/>
  <z:row ShipperID="14" CompanyName="Blue Angel Air Delivery"
    Phone="(505) 111-2222"/>
</rs:insert>
<rs:delete>
  <z:row ShipperID="1" CompanyName="Speedy Express" Phone="(503) 555-9831"/>
</rs:delete>
</rs:data>

An update always contains the entire original row data followed by the changed row data. The changed row may contain all of the columns or only those columns that have actually changed. In the previous example, the row for Shipper 2 is not changed, while only the Phone column has changed values for Shipper 3 and is therefore the only column included in the changed row. The inserted rows for Shippers 12, 13, and 14 are batched together under one rs:insert tag. Note that deleted rows may also be batched together, although this is not shown above.


Hierarchical Recordsets in XML

ADO allows persistence of hierarchical Recordset objects into XML. With hierarchical Recordset objects, the value of a field in the parent Recordset is another Recordset. Such fields are represented as child elements in the XML stream rather than an attribute. The following example demonstrates this case:

Rs.Open "SHAPE {select stor_id, stor_name, state from stores} APPEND ({select stor_id, ord_num, ord_date, qty from sales} AS rsSales RELATE stor_id TO stor_id)", "Provider=MSDataShape;DSN=pubs;UID=sa;PWD=;"

The following is the XML format of the persisted Recordset:

<xml xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"     xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"     xmlns:rs="urn:schemas-microsoft-com:rowset"
    xmlns:z="#RowsetSchema">
  <s:Schema id="RowsetSchema">
    <s:ElementType name="row" content="eltOnly" rs:updatable="true">
      <s:AttributeType name="stor_id" rs:number="1"
        rs:writeunknown="true">
        <s:datatype dt:type="string" dt:maxLength="4"
          rs:fixedlength="true" rs:maybenull="false"/>
      </s:AttributeType>
      <s:AttributeType name="stor_name" rs:number="2" rs:nullable="true"
        rs:writeunknown="true">
          <s:datatype dt:type="string" dt:maxLength="40"/>
      </s:AttributeType>
      <s:AttributeType name="state" rs:number="3" rs:nullable="true"
        rs:writeunknown="true">
        <s:datatype dt:type="string" dt:maxLength="2"
          rs:fixedlength="true"/>
      </s:AttributeType>
      <s:ElementType name="rsSales" content="eltOnly"
        rs:updatable="true" rs:relation="010000000100000000000000">
        <s:AttributeType name="stor_id" rs:number="1"
          rs:writeunknown="true">
          <s:datatype dt:type="string" dt:maxLength="4"
            rs:fixedlength="true" rs:maybenull="false"/>
        </s:AttributeType>
        <s:AttributeType name="ord_num" rs:number="2"
          rs:writeunknown="true">
          <s:datatype dt:type="string" dt:maxLength="20"
            rs:maybenull="false"/>
        </s:AttributeType>
        <s:AttributeType name="ord_date" rs:number="3"
          rs:writeunknown="true">
            <s:datatype dt:type="dateTime" dt:maxLength="16"
              rs:scale="3" rs:precision="23" rs:fixedlength="true"
              rs:maybenull="false"/>
        </s:AttributeType>
        <s:AttributeType name="qty" rs:number="4" rs:writeunknown="true">
          <s:datatype dt:type="i2" dt:maxLength="2" rs:precision="5"
            rs:fixedlength="true" rs:maybenull="false"/>
        </s:AttributeType>
        <s:extends type="rs:rowbase"/>
      </s:ElementType>
      <s:extends type="rs:rowbase"/>
    </s:ElementType>
  </s:Schema>
  <rs:data>
    <z:row stor_id="6380" stor_name="Eric the Read Books" state="WA">
      <rsSales stor_id="6380" ord_num="6871"
        ord_date="1994-09-14T00:00:00" qty="5"/>
      <rsSales stor_id="6380" ord_num="722a"
        ord_date="1994-09-13T00:00:00" qty="3"/>
    </z:row>
    <z:row stor_id="7066" stor_name="Barnum's" state="CA">
      <rsSales stor_id="7066" ord_num="A2976"
        ord_date="1993-05-24T00:00:00" qty="50"/>
      <rsSales stor_id="7066" ord_num="QA7442.3"
        ord_date="1994-09-13T00:00:00" qty="75"/>
    </z:row>
    <z:row stor_id="7067" stor_name="News & Brews" state="CA">
      <rsSales stor_id="7067" ord_num="D4482"
        ord_date="1994-09-14T00:00:00" qty="10"/>
      <rsSales stor_id="7067" ord_num="P2121"
        ord_date="1992-06-15T00:00:00" qty="40"/>
      <rsSales stor_id="7067" ord_num="P2121"
        ord_date="1992-06-15T00:00:00" qty="20"/>
      <rsSales stor_id="7067" ord_num="P2121"
        ord_date="1992-06-15T00:00:00" qty="20"/>
    </z:row>
...
  </rs:data>
</xml>

The exact order of the columns in the parent Recordset is not obvious when it is persisted in this manner. Any field in the parent may contain a child Recordset. The Persistence Provider persists out all scalar columns first as attributes and then persists out all child Recordset "columns" as child elements of the parent row. The ordinal position of the field in the parent Recordset can be obtained by looking at the schema definition of the Recordset. Every field has an OLE DB property, rs:number, defined in the Recordset schema namespace that contains the ordinal number for that field.

The names of all fields in the child Recordset are concatenated with the name of the field in the parent Recordset that contains this child. This is to ensure that there are no name collisions in cases where parent and child Recordsets both contain a field that is obtained from two different tables but is named singularly.

When saving hierarchical Recordsets into XML, you should be aware of the following restrictions in ADO:

  • A hierarchical Recordset with pending updates cannot be persisted into XML.
  • A hierarchical Recordset created with a parameterized shape command cannot be persisted (in either XML or ADTG format).
  • ADO currently saves the relationship between the parent and the child Recordsets as a binary large object (BLOB). XML tags to describe this relationship have not yet been defined in the rowset schema namespace.
  • When a hierarchical Recordset is saved, all child Recordsets are saved along with it. If the current Recordset is a child of another Recordset, its parent is not saved. All child Recordsets that form the subtree of the current Recordset are saved.

When a hierarchical Recordset is reopened from its XML-persisted format, you must be aware of the following limitations:

  • If the child record contains records for which there are no corresponding parent records, these rows are not written out in the XML representation of the hierarchical Recordset. Thus, these rows will be lost when the Recordset is reopened from its persisted location.
  • If a child record has references to more than one parent record, then on reopening the Recordset, the child Recordset may contain duplicate records. However, these duplicates will only be visible if the user works directly with the underlying child rowset. If a chapter is used to navigate the child Recordset (that is the only way to navigate through ADO), the duplicates are not visible.


Recordset Dynamic Properties in XML

The following Recordset provider-specific properties (from the Client Cursor Engine) are currently persisted into the XML format:

  • Update Resync
  • Unique Table
  • Unique Schema
  • Unique Catalog
  • Resync Command
  • IRowsetChange
  • IRowsetUpdate
  • CommandTimeout
  • BatchSize
  • UpdateCriteria
  • Reshape Name
  • AutoRecalc

These properties are saved in the schema section as attributes of the element definition for the Recordset being persisted. These attributes are defined in the rowset schema namespace and must have the prefix "rs:".


XSL Transformations

ADO

XSLT Transformations

XSLT can be applied to the generated XML to transform it into another format. Understanding the XML format in ADO helps in developing XSLT templates that can transform it into a more user-friendly form.

For example, you know that each row of the Recordset is saved as the z:row element inside the rs:data element. Similarly, each field of the Recordset is saved as an attribute-value pair for this element.

The following XSLT script can be applied to the XML shown in the previous section to transform it into an HTML table to be displayed in the browser:

<?xml version="1.0" encoding="ISO-8859-1"?>
<html xmlns:xsl="http://www.w3.org/TR/WD-xsl">
<body STYLE="font-family:Arial, helvetica, sans-serif; font-size:12pt; background-color:white">
<table border="1" style="table-layout:fixed" width="600">
  <col width="200"></col>
  <tr bgcolor="teal">
    <th><font color="white">CustomerId</font></th>
    <th><font color="white">CompanyName</font></th>
    <th><font color="white">ContactName</font></th>
  </tr>
<xsl:for-each select="xml/rs:data/z:row">
  <tr bgcolor="navy">
    <td><font color="white"><xsl:value-of select="@CustomerID"/></font></td>
    <td><font color="white"><xsl:value-of select="@CompanyName"/></font></td>
    <td><font color="white"><xsl:value-of select="@ContactName"/></font></td>
  </tr>
</xsl:for-each>
</table>
</body>
</html>

The XSLT converts the XML stream generated by the ADO Save method into an HTML table which displays each field of the Recordset along with a table heading. Table headings and rows also are assigned different fonts and colors.


Saving to the XML DOM

ADO

Saving to the XML DOM Object

You can save a Recordset in XML format to an instance of an MSXML DOM object, as shown in the following Visual Basic code:

Dim xDOM As New MSXML.DOMDocument
Dim rsXML As New ADODB.Recordset
Dim sSQL As String, sConn As String

sSQL = "SELECT customerid, companyname, contactname FROM customers"
sConn="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\Program Files" & _
        "\Common Files\System\msadc\samples\NWind.mdb"
rsXML.Open sSQL, sConn
rsXML.Save xDOM, adPersistADO   'Save Recordset directly into a DOM tree.
...


XML Security Considerations

The ADO Save and Open methods on the Recordset object are not considered safe operations to run in Internet Explorer. Thus, if these methods are used in a script code that is running in an application or control that is hosted in a browser, the security configuration of the browser will have an effect on its behavior.

Internet Explorer 5 provides security restrictions for such operations by default in the Internet zones. Under that configuration, the Recordset cannot make any access to the local file system on the client or access any data sources outside the domain of the server from which the page has been downloaded. Specifically, when running inside the browser host, a Recordset can be saved back to a file only if it is on the same server from which the page was downloaded. Similarly, you can open a Recordset by loading it from a file only if that file is on the same server from which the page was downloaded.

For more information about security in Internet Explorer, see the technical article "ADO and RDS Security Issues in Microsoft Internet Explorer."


XML Recordset Persistence Scenario

In this scenario, you will create an Active Server Pages (ASP) application that saves the contents of a Recordset object directly to the ASP Response object.

Note This scenario requires that your server have Internet Information Server 5.0 (IIS) or later installed.

The returned Recordset is displayed in Internet Explorer using an RDS.DataControl.

The following steps are necessary to create this scenario:

  1. Set up the application.
  2. Get the data.
  3. Send the data.
  4. Receive and display the data.

Next Step 1: Set Up the Application

See Also

Save Method


Step 1: Set Up the Application

Create an IIS virtual directory named "XMLPersist" with script permissions. Create two new text files in the folder to which the virtual directory points, one named "XMLResponse.asp," the other named "Default.htm."

Next Step 2: Get the Data


Step 2: Get the Data

In this step you will write the code to open an ADO Recordset and prepare to send it to the client. Open the file XMLResponse.asp with a text editor, such as Windows Notepad, and insert the following code:

<%@ codepage="65001" %>
<object id="rstCustomers" progid="ADODB.Recordset" runat="Server">
</object>
<!-- metadata name="Microsoft ActiveX Data Objects 2.5 Library"
     type="TypeLib" uuid="{00000205-0000-0010-8000-00AA006D2EA4}" -->
<%
  Option Explicit
  Response.Buffer = True

  Dim strSQL, strConnection

  Response.ContentType = "text/xml"
  Response.Expires = 0
  Response.Buffer = False

  strSQL="SELECT title_id, title, type FROM titles" & _
         " ORDER By title"

  strConnection = "Provider=MSDataShape;Data Provider=SQLOLEDB;" & _
                      "Database=Pubs;Data Source=SRV;User Id=sa;" & _
                        "Password=;"

  rstCustomers.Open strSQL, strConnection, adOpenStatic,
      adLockBatchOptimistic

Be sure to change the value of the Data Source parameter in strConnection to the name of your Microsoft SQL Server computer. The codepage for the page is set to 65001 (Unicode) so that it will return XML.

Keep the file open and go on to the next step.

Next Step 3: Send the Data


Step 3: Send the Data

Now that you have a Recordset, you need to send it to the client by saving it as XML to the ASP Response object. Add the following code to the bottom of XMLResponse.asp:

  Response.Write "<?xml version='1.0' encoding='ISO-8859-1'?>" & vbCRLF
  rstCustomers.save Response, adPersistXML
  rstCustomers.Close
%>

Notice that the ASP Response object is specified as the destination for the Recordset Save method. The destination of the Save method can be any object that supports the IStream interface, such as an ADO Stream object, or a file name that includes the complete path to which the Recordset is to be saved.

Save and close XMLResponse.asp before going to the next step.

Next Step 4: Receive the Data


Step 4: Receive and Display the Data

In this step you will create an HTML file with an embedded RDS.DataControl object that points at the XMLResponse.asp file to get the Recordset. Open default.htm with a text editor, such as Windows Notepad, and add the code below. Replace "yourserver" in the URL with the name of your server computer.

<HTML>
<HEAD><TITLE>ADO Recordset Persistence Sample</TITLE></HEAD>
<BODY>

<OBJECT CLASSID="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33" ID="RDC1"
   <PARAM NAME="URL"
          VALUE="http://yourserver/XMLPersist/XMLResponse.asp">
</OBJECT>

<TABLE DATASRC="#RDC1">
  <TR>
<TD><SPAN DATAFLD="title_id"></SPAN></TD>
<TD><SPAN DATAFLD="title"></SPAN></TD>
<TD><SPAN DATAFLD="type"></SPAN></TD>
  </TR>
</TABLE>
</BODY>
</HTML>

Save and close default.htm. Using Internet Explorer 4.0 or later, open the URL http://yourserver/XMLPersist/default.htm and observe the results. The data is displayed in a bound DHTML table. Now open the URL http://yourserver/XMLPersist/XMLResponse.asp and observe the results. The XML is displayed.


Chapter 6: Error Handling

ADO uses several different methods to notify an application of errors that occur. This chapter discusses the types of errors that can occur when you are using ADO and how your application is notified. It concludes by making suggestions about how to handle those errors.

How Does ADO Report Errors?

ADO notifies you about errors in several ways:

  • ADO errors generate a run-time error. Handle an ADO error the same way you would any other run-time error, such as using an On Error statement in Visual Basic.
  • Your program can receive errors from OLE DB. An OLE DB error generates a run-time error as well.
  • If the error is specific to your data provider, one or more Error objects are placed in the Errors collection of the Connection object that was used to access the data store when the error occurred.
  • If the process that raised an event also produced an error, error information is placed in an Error object and passed as a parameter to the event. See Chapter 7: Handling ADO Events for more information about events.
  • Problems that occur when processing batch updates or other bulk operations involving a Recordset can be indicated by the Status property of the Recordset. For example, schema constraint violations or insufficient permissions can be specified by RecordStatusEnum values.
  • Problems that occur involving a particular Field in the current record are also indicated by the Status property of each Field in the Fields collection of the Record or Recordset. For example, updates that could not be completed or incompatible data types can be specified by FieldStatusEnum values.

The following sections describe each of these notification methods in more detail.


ADO Errors

ADO Errors are reported to your program as run-time errors. You can use the error-trapping mechanism of your programming language to trap and handle them. For example, in Visual Basic, use the On Error statement. In Visual J++, use a try-catch block. In Visual C++, it depends on the method you are using to access the ADO libraries. With #import, use a try-catch block. Otherwise, C++ programmers need to explicitly retrieve the error object by calling GetErrorInfo. The following Visual Basic sub procedure demonstrates trapping an ADO error:

' BeginErrorHandlingVB01
Private Sub Form_Load()
' Turn on error handling
On Error GoTo FormLoadError

'Open the database and the recordset for processing.
'
Dim strCnn As String
strCnn = "Provider=sqloledb;" & _
    "Data Source=a-iresmi2000;" & _
    "Initial Catalog=Northwind;User Id=sa;Password=;"

' cnn is a Public Connection Object because
' it was defined WithEvents
Set cnn = New ADODB.Connection
cnn.Open strCnn

' The next line of code intentionally causes
' an error by trying to open a connection
' that has already been opened.
cnn.Open strCnn

' rst is a Public Recordset because it
' was defined WithEvents
Set rst = New ADODB.Recordset
rst.Open "Customers", cnn

Exit Sub

' Error handler
FormLoadError:
    Dim strErr As String
    Select Case Err
        Case adErrObjectOpen
            strErr = "Error #" & Err.Number & ": " & Err.Description & vbCrLf
            strErr = strErr & "Error reported by: " & Err.Source & vbCrLf
            strErr = strErr & "Help File: " & Err.HelpFile & vbCrLf
            strErr = strErr & "Topic ID: " & Err.HelpContext
            MsgBox strErr
            Debug.Print strErr
            Err.Clear
            Resume Next
        ' If some other error occurs that
        ' has nothing to do with ADO, show
        ' the number and description and exit.
        Case Else
            strErr = "Error #" & Err.Number & ": " & Err.Description & vbCrLf
            MsgBox strErr
            Debug.Print strErr
            Unload Me
    End Select
End Sub
' EndErrorHandlingVB01

This Form_Load event procedure intentionally creates an error by trying to open the same Connection object twice. The second time the Open method is called, the error handler is activated. In this case the error is of type adErrObjectOpen, so the error handler displays the following message before resuming program execution:

Error #3705: Operation is not allowed when the object is open.
Error reported by: ADODB.Connection
Help File: E:\WINNT\HELP\ADO260.CHM Topic ID: 1003705

The error message includes each piece of information provided by the Visual Basic Err object except for the LastDLLError value, which does not apply here. The error number tells you which error has occurred. The description is useful in cases in which you do not want to handle the error yourself. You can simply pass it along to the user. Although you will usually want to use messages customized for your application, you cannot anticipate every error; the description gives some clue as to what went wrong. In the sample code, the error was reported by the Connection object. You will see the object's type or programmatic ID here—not a variable name.

Note The Visual Basic Err object only contains information about the most recent error. The ADO Errors collection of the Connection object contains one Error object for each error raised by the most recent ADO operation. Use the Errors collection rather than the Err object to handle multiple errors. For more information about the Errors collection, see Provider Errors. However, if there is no valid Connection object, the Err object is the only source for information about ADO errors.

What kinds of operations are likely to cause ADO errors? Common ADO errors can involve opening an object such as a Connection or Recordset, attempting to update data, or calling a method or property that is not supported by your provider.

OLE DB errors can also be passed to your application as run-time errors in the Errors collection. For more information about OLE DB error numbers, see Chapter 16 of the OLE DB Programmer's Reference.


ADO Error Reference

The ErrorValueEnum constant describes the ADO error values. For a complete listing of these enumerated constants, including values, see Appendix B: Error Lists. This section will examine some of the more interesting errors and explain some specific situations that can raise them, or solutions to fix the problem. Both the ErrorValueEnum constant and the short positive decimal number are listed.

Number | ErrorValueEnum constant | Description/Possible causes
3000 | adErrProviderFailed | Provider failed to perform the requested operation.
3001 | adErrInvalidArgument | Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another. This error is often caused by a typographical error in an SQL SELECT statement. For example, a misspelled field name or table name can generate this error. This error can also occur when a field or table named in a SELECT statement does not exist in the data store.
3002 | adErrOpeningFile | File could not be opened. A misspelled file name was specified, or a file has been moved, renamed, or deleted. Over a network, the drive might be temporarily unavailable or network traffic might be preventing a connection.
3003 | adErrReadFile | File could not be read. The name of the file is specified incorrectly, the file might have been moved or deleted, or the file might have become corrupted.
3004 | adErrWriteFile | Write to file failed. You might have closed a file and then tried to write to it, or the file might be corrupted. If the file is located on a network drive, transient network conditions might prevent writing to a network drive.
3021 | adErrNoCurrentRecord | Either BOF or EOF is True, or the current record has been deleted. Requested operation requires a current record. An attempt was made to update records by using Find or Seek to move the record pointer to the desired record. If the record is not found, EOF will be True. This error can also occur after a failed AddNew or Delete because there is no current record when these methods fail.
3219 | adErrIllegalOperation | Operation is not allowed in this context.
3220 | adErrCantChangeProvider | Supplied provider is different from the one already in use.
3246 | adErrInTransaction | Connection object cannot be explicitly closed while in a transaction. A Recordset or Connection object that is currently participating in a transaction cannot be closed. Call either RollbackTrans or CommitTrans before closing the object.
3251 | adErrFeatureNotAvailable | The object or provider is not capable of performing the requested operation. Some operations depend on a particular provider version.
3265 | adErrItemNotFound | Item cannot be found in the collection corresponding to the requested name or ordinal. An incorrect field or table name has been specified.
3367 | adErrObjectInCollection | Object is already in collection. Cannot append. An object cannot be added to the same collection twice.
3420 | adErrObjectNotSet | Object is no longer valid.
3421 | adErrDataConversion | Application uses a value of the wrong type for the current operation. You might have supplied a string to an operation that expects a stream, for example.
3704 | adErrObjectClosed | Operation is not allowed when the object is closed. The Connection or Recordset has been closed. For example, some other routine might have closed a global object. You can prevent this error by checking the State property before you attempt an operation.
3705 | adErrObjectOpen | Operation is not allowed when the object is open. An object that is open cannot be opened. Fields cannot be appended to an open Recordset.
3706 | adErrProviderNotFound | Provider cannot be found. It may not be properly installed. The name of the provider might be incorrectly specified, the specified provider might not be installed on the computer where your code is being executed, or the installation might have become corrupted.
3707 | adErrBoundToCommand | The ActiveConnection property of a Recordset object, which has a Command object as its source, cannot be changed. The application attempted to assign a new Connection object to a Recordset that has a Command object as its source.
3708 | adErrInvalidParamInfo | Parameter object is improperly defined. Inconsistent or incomplete information was provided.
3709 | adErrInvalidConnection | The connection cannot be used to perform this operation. It is either closed or invalid in this context.
3710 | adErrNotReentrant | Operation cannot be performed while processing event. An operation cannot be performed within an event handler that causes the event to fire again. For example, navigation methods should not be called from within a WillMove event handler.
3711 | adErrStillExecuting | Operation cannot be performed while executing asynchronously.
3712 | adErrOperationCancelled | Operation has been canceled by the user. The application has called the CancelUpdate or CancelBatch method and the current operation has been canceled.
3713 | adErrStillConnecting | Operation cannot be performed while connecting asynchronously.
3714 | adErrInvalidTransaction | Coordinating transaction is invalid or has not started.
3715 | adErrNotExecuting | Operation cannot be performed while not executing.
3716 | adErrUnsafeOperation | Safety settings on this computer prohibit accessing a data source on another domain.
3717 | adWrnSecurityDialog | For internal use only. Don't use. (Entry was included for the sake of completeness. This error should not appear in your code.)
3718 | adWrnSecurityDialogHeader | For internal use only. Don't use. (Entry included for the sake of completeness. This error should not appear in your code.)
3719 | adErrIntegrityViolation | Data value conflicts with the integrity constraints of the field. A new value for a Field would cause a duplicate key. A value that forms one side of a relationship between two records might not be updatable.
3720 | adErrPermissionDenied | Insufficient permission prevents writing to the field. The user named in the connection string does not have the proper permissions to write to a Field.
3721 | adErrDataOverflow | Data value is too large to be represented by the field data type. A numeric value that is too large for the intended field was assigned. For example, a long integer value was assigned to a short integer field.
3722 | adErrSchemaViolation | Data value conflicts with the data type or constraints of the field. The data store has validation constraints that differ from the Field value.
3723 | adErrSignMismatch | Conversion failed because the data value was signed and the field data type used by the provider was unsigned.
3724 | adErrCantConvertvalue | Data value cannot be converted for reasons other than sign mismatch or data overflow. For example, conversion would have truncated data.
3725 | adErrCantCreate | Data value cannot be set or retrieved because the field data type was unknown, or the provider had insufficient resources to perform the operation.
3726 | adErrColumnNotOnThisRow | Record does not contain this field. An incorrect field name was specified or a field not in the Fields collection of the current record was referenced.
3727 | adErrURLDoesNotExist | Either the source URL or the parent of the destination URL does not exist. There is a typographical error in either the source or destination URL. You might have http://mysite/photo/myphoto.jpg when you should actually have http://mysite/photos/myphoto.jpg instead. The typographical error in the parent URL (in this case, photo instead of photos) has caused the error.
3728 | adErrTreePermissionDenied | Permissions are insufficient to access tree or subtree. The user named in the connection string does not have the appropriate permissions.
3729 | adErrInvalidURL | URL contains invalid characters. Make sure the URL is typed correctly. The URL follows the scheme registered to the current provider (for example, Internet Publishing Provider is registered for http).
3730 | adErrResourceLocked | Object represented by the specified URL is locked by one or more other processes. Wait until the process has finished and attempt the operation again. The object you are trying to access has been locked by another user or by another process in your application. This is most likely to arise in a multi-user environment.
3731 | adErrResourceExists | Copy operation cannot be performed. Object named by destination URL already exists. Specify adCopyOverwrite to replace the object. If you do not specify adCopyOverwrite when copying the files in a directory, the copy fails when you try to copy an item that already exists in the destination location.
3732 | adErrCannotComplete | The server cannot complete the operation. This might be because the server is busy with other operations or it might be low on resources.
3733 | adErrVolumeNotFound | Provider cannot locate the storage device indicated by the URL. Make sure the URL is typed correctly. The URL of the storage device might be incorrect, but this error can occur for other reasons. The device might be offline or a large volume of network traffic might prevent the connection from being made.
3734 | adErrOutOfSpace | Operation cannot be performed. Provider cannot obtain enough storage space. There might not be enough RAM or hard-drive space for temporary files on the server.
3735 | adErrResourceOutOfScope | Source or destination URL is outside the scope of the current record.
3736 | adErrUnavailable | Operation failed to complete and the status is unavailable. The field may be unavailable or the operation was not attempted. Another user might have changed or deleted the field you are trying to access.
3737 | adErrURLNamedRowDoesNotExist | Record named by this URL does not exist. While attempting to open a file using a Record object, either the file name or the path to the file was misspelled.
3738 | adErrDelResOutOfScope | The URL of the object to be deleted is outside the scope of the current record.
3747 | adErrCatalogNotSet | Operation requires a valid ParentCatalog.
3748 | adErrCantChangeConnection | Connection was denied. The new connection you requested has different characteristics than the one already in use.
3749 | adErrFieldsUpdateFailed | Fields update failed. For further information, examine the Status property of individual field objects. This error can occur in two situations: when changing a Field object's value in the process of changing or adding a record to the database; and when changing the properties of the Field object itself. The Record or Recordset update failed due to a problem with one of the fields in the current record. Enumerate the Fields collection and check the Status property of each field to determine the cause of the problem.
3750 | adErrDenyNotSupported | Provider does not support sharing restrictions. An attempt was made to restrict file sharing and your provider does not support the concept.
3751 | adErrDenyTypeNotSupported | Provider does not support the requested kind of sharing restriction. An attempt was made to establish a particular type of file-sharing restriction that is not supported by your provider. See the provider's documentation to determine what file-sharing restrictions are supported.


Provider Errors

When a provider error occurs, a run-time error of -2147467259 is returned. When you receive this error, check the active Connection object's Errors collection, which will contain one or more errors describing what happened.

The ADO Errors Collection

Because a particular ADO operation can produce multiple provider errors, ADO exposes a collection of error objects through the Connection object. This collection contains no objects if an operation concludes successfully and contains one or more Error objects if something went wrong and the provider raised one or more errors. Examine each individual error object in order to determine the exact cause of the error.

Once you have finished handling any errors that have occurred, you can clear the collection by calling the Clear method. It is particularly important to explicitly clear the Errors collection before you call the Resync, UpdateBatch, or CancelBatch method on a Recordset object, the Open method on a Connection object, or set the Filter property on a Recordset object. By clearing the collection explicitly, you can be certain that any Error objects in the collection are not left over from a previous operation.

Some operations can generate warnings as well as errors. Warnings are also represented by Error objects in the Errors collection. When a provider adds a warning to the collection, it does not generate a run-time error. Check the Count property of the Errors collection to determine if a warning was produced by a particular operation. If the count is one or greater, an Error object has been added to the collection. Once you have determined that the Errors collection contains errors or warnings, you can iterate through the collection and retrieve information about each of the Error objects it contains. The following short Visual Basic example demonstrates this:

' BeginErrorHandlingVB02
Private Function DeleteCustomer(ByVal CompanyName As String) As Long
    On Error GoTo DeleteCustomerError

    rst.Find "CompanyName='" & CompanyName & "'"
DeleteCustomerError:
Dim objError As ADODB.Error
Dim strError As String

    If cnn.Errors.Count > 0 Then
        For Each objError In cnn.Errors
            strError = strError & "Error #" & objError.Number & _
                " " & objError.Description & vbCrLf & _
                "NativeError: " & objError.NativeError & vbCrLf & _
                "SQLState: " & objError.SQLState & vbCrLf & _
                "Reported by: " & objError.Source & vbCrLf & _
                "Help file: " & objError.HelpFile & vbCrLf & _
                "Help Context ID: " & objError.HelpContext
        Next
        MsgBox strError
    End If
End Function
' EndErrorHandlingVB02

The error-handling routine includes a For Each loop that examines each object in the Errors collection. In this example, it simply accumulates a message for display. In a working program, you would write code to perform an appropriate task for each error, such as closing all open files and shutting down the program in an orderly fashion.

The Error Object

By examining an Error object you can determine what error occurred, and more importantly, what application or what object caused the error. The Error object has the following properties:

Property name | Description
Description | A text description of the error that occurred.
HelpContext, HelpFile | Refers to the help topic and help file that contain a description of the error that occurred.
NativeError | The provider-specific error number.
Number | A Long Integer that represents the number (listed in the ErrorValueEnum) of the error that occurred.
Source | Indicates the name of the object or application that generated an error.
SQLState | A five-character error code that the provider returns during the process of a SQL statement.

The ADO Error object is quite similar to the standard Visual Basic Err object. Its properties describe the error that occurred. In addition to the number of the error, you also receive two related pieces of information. The NativeError property contains an error number specific to the provider you are using. In the previous example, the provider is the Microsoft OLE DB Provider for SQL Server, so NativeError will contain errors specific to SQL Server. The SQLState property has a five-letter code that describes an error in a SQL statement.

Event-Related Errors

The Error object is also used when event-related errors occur. You can determine if an error occurred in the process that raised an ADO event by checking the Error object passed as an event parameter.

If the operation that causes an event is concluded successfully, the adStatus parameter of the event handler will be set to adStatusOK. On the other hand, if the operation that raised the event was unsuccessful, the adStatus parameter is set to adStatusErrorsOccurred. In that case, the pError parameter will contain an Error object that describes the error.


Field-Related Error Information

If an error is directly related to a field—for example, if the data is missing or if it is the wrong type for the field—you can retrieve more information about the cause of the problem by examining the Field object's Status property. This property has been enhanced to provide specific information about the problem. So, for example, when a call to UpdateBatch fails, the cause of the problem can be determined by examining the Status property of the Fields in each of the effected records. The property will contain one of the values in the FieldStatusEnum constant. The following table includes those values that are of particular interest when an error occurs.

Constant | Value | Description
adFieldCantConvertValue | 2 | Indicates that the field cannot be retrieved or stored without loss of data.
adFieldDataOverflow | 6 | Indicates that the data returned from the provider overflowed the data type of the field.
adFieldDefault | 13 | Indicates that the default value for the field was used when setting data.
adFieldIgnore | 15 | Indicates that this field was skipped when setting data values in the source. No value was set by the provider.
adFieldIntegrityViolation | 10 | Indicates that the field cannot be modified because it is a calculated or derived entity.
adFieldIsNull | 3 | Indicates that the provider returned a null value.
adFieldOutOfSpace | 22 | Indicates that the provider is unable to obtain enough storage space to complete a move or copy operation.


Recordset-Related Error Information

During batch processing, the Status property of the Recordset object provides information about the individual records in the Recordset. Before a batch update takes place, the Status property of the Recordset reflects information about records to be added, changed and deleted. After UpdateBatch has been called, the Status property indicates the success or failure of the operation. As you move from record to record in the Recordset, the value of the Status property changes to describe the status of the current record.


Handling Errors in other Languages

So far, this chapter has discussed errors from a Microsoft® Visual Basic® point of view. The remainder of this section will give you an overview of error handling in languages other than Visual Basic.


Handling Errors in VBScript

There is little difference between the methods used in Visual Basic and those used with VBScript. The primary difference is that VBScript does not support the concept of error handling by continuing execution at a label. In other words, you cannot use On Error GoTo in VBScript. Instead, use On Error Resume Next and then check both Err.Number and the Count property of the Errors collection, as shown in the following example:

<!-- BeginErrorExampleVBS -->
<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
<TITLE>Error Handling Example (VBScript)</TITLE>
</HEAD>
<BODY>

<h1>Error Handling Example (VBScript)</h1>

<%

   Dim cnn1
   Dim errLoop
   Dim strError

   On Error Resume Next

   ' Intentionally trigger an error.
   Set cnn1 = Server.CreateObject("ADODB.Connection")
   cnn1.Open "nothing"

   If cnn1.Errors.Count > 0 Then
      ' Enumerate Errors collection and display
      ' properties of each Error object.
      For Each errLoop In cnn1.Errors
         strError = "Error #" & errLoop.Number & "<br>" & _
            "   " & errLoop.Description & "<br>" & _
            "   (Source: " & errLoop.Source & ")" & "<br>" & _
            "   (SQL State: " & errLoop.SQLState & ")" & "<br>" & _
            "   (NativeError: " & errLoop.NativeError & ")" & "<br>"
         If errLoop.HelpFile = "" Then
            strError = strError & _
               "   No Help file available" & _
               "<br><br>"
         Else
            strError = strError & _
               "   (HelpFile: " & errLoop.HelpFile & ")" & "<br>" & _
               "   (HelpContext: " & errLoop.HelpContext & ")" & _
               "<br><br>"
         End If

         Response.Write("<p>" & strError & "</p>")
      Next
   End If

%>

</BODY>
</HTML>
<!-- EndErrorExampleVBS -->


Handling Errors in Visual C++

In COM, most operations return an HRESULT return code that indicates whether a function completed successfully. The #import directive generates wrapper code around each "raw" method or property and checks the returned HRESULT. If the HRESULT indicates failure, the wrapper code throws a COM error by calling _com_issue_errorex() with the HRESULT return code as an argument. COM error objects can be caught in a try-catch block. (For efficiency's sake, catch a reference to a _com_error object.)

Remember, these are ADO errors: they result from the ADO operation failing. Errors returned by the underlying provider appear as Error objects in the Connection object's Errors collection.

The #import directive only creates error-handling routines for methods and properties declared in the ADO .dll. However, you can take advantage of this same error-handling mechanism by writing your own error-checking macro or inline function. See the topic Visual C++® Extensions for examples.


Handline Errors in Visual J++

ADO

Handling Errors in Visual J++

Handle ADO errors in your Microsoft® Visual J++® applications using a try catch block. Once an error has been thrown, you can iterate through the collection, successively handling each error. The following Visual J++ example shows a console application that deliberately causes an error.

When the catch block is activated, it calls the PrintProviderError function to display the errors. The PrintProviderError function iterates through the Errors collection and sends a line to the standard output device that describes each error in the collection.

// BeginErrorExampleVJ
// The WFC class includes the ADO objects.

import com.ms.wfc.data.*;
import java.io.* ;

public class ErrorExample
{
   /**
    * The main entry point for the application.
    *
    * @param args Array of parameters passed to the application
    * via the command line.
    */
   public static void main (String[] args)
   {
      ForceError();
      System.exit(0);
   }

   static void DescriptionX()
   {
      BufferedReader in = new
         BufferedReader(new InputStreamReader(System.in));

     // Define ADO Objects.
      Connection cnConn1 = null;

      try
      {
         // Create an error by trying to
       // Open a database that doesn't exist.
         cnConn1 = new Connection();
         cnConn1.open("nothing");
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.
         PrintProviderError(cnConn1);
      }

     try
     {
       System.out.println("\nPress <Enter> key to continue.");
       in.readLine();
     }
      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }

   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndErrorExampleVJ


Handling Errors in JScript

Your Microsoft® JScript® code must check the Count property of the Connection object's Errors collection. If the value is greater than 0, iterate through the collection and print the values as you would in any of the other languages.

<!-- BeginErrorExampleJS -->
<%@ Language=JScript %>
<HTML>
<HEAD>
<title>Error Handling Example (JScript)</title>
</HEAD>
<BODY>
<h1>Error Handling Example (JScript)</h1>
<%
   var cnn1 = Server.CreateObject("ADODB.Connection");
   var errLoop = Server.CreateObject("ADODB.Error");
   var strError = new String;

   // Intentionally trigger an error.
   cnn1.Open("nothing");

   if (cnn1.Errors.Count > 0) {
      // Enumerate Errors collection and display
      // properties of each Error object.
      for (var i = 1; i < cnn1.Errors.Count; i++) {
         errLoop = cnn1.Errors(i);
         strError = "Error #" & errLoop.Number + "<br>" +
            "   " + errLoop.Description + "<br>" +
            "   (Source: " & errLoop.Source & ")" + "<br>" +
            "   (SQL State: " & errLoop.SQLState + ")" + "<br>" +
            "   (NativeError: " & errLoop.NativeError + ")" + "<br>";
         if (errLoop.HelpFile == "")
            strError = strError +
               "   No Help file available" +
               "<br><br>";
         else
            strError = strError +
               "   (HelpFile: " & errLoop.HelpFile & ")" & "<br>" +
               "   (HelpContext: " & errLoop.HelpContext & ")" +
               "<br><br>";
         Response.Write("<p>" & strError & "</p>");
      }
   }
%>

</BODY>
</HTML>
<!-- EndErrorExampleJS -->


Anticipating Errors

Error prevention is at least as important as error handling. This final section contains a short list of precautions your application can take to help make errors less likely to occur.

Check the state of objects by checking the value in the State property before trying to perform an operation using those objects. For example, if your application uses a global Connection, check its State property to see if it is already open before calling the Open method.

  • Any program that accepts data from a user must include code to validate that data before sending it to the data store. You cannot rely on the data store, the provider, ADO, or even your programming language to notify you of problems. You must check every byte entered by your users, making sure that data is the correct type for its field and that required fields are not empty.

Check the data before you try to write any data to the data store. The easiest way to do so is to handle the WillMove event or the WillUpdateRecordset event. For a more complete discussion of handling ADO events, see Chapter 7: Handling ADO Events.

Make sure that Recordset objects are not beyond the boundaries of the Recordset before attempting to move the record pointer. If you try to MoveNext when EOF is True or MovePrev when BOF is True, an error will occur. If you perform any of the Move methods when both EOF and BOF are True, an error will be generated.

Errors also will occur if you try to perform operations such as Seek and Find on an empty Recordset.


Chapter 7: Handling ADO Events

The ADO event model supports certain synchronous and asynchronous ADO operations that issue events, or notifications, before the operation starts or after it completes. An event is actually a call to an event-handler routine that you define in your application.

If you provide handler functions or procedures for the group of events that occur before the operation starts, you can examine or modify the parameters that were passed to the operation. Because it has not been executed yet, you can either cancel the operation or allow it to complete.

The group of events that occur after an operation completes are especially important if you use ADO asynchronously. For example, an application that starts an asynchronous Recordset.Open operation is notified by an execution complete event when the operation concludes.

Using the ADO event model adds some overhead to your application but provides far more flexibility than other methods of dealing with asynchronous operations, such as monitoring the State property of an object with a loop.

See Also

ADO Event Handler Summary | ADO Event Instantiation by Language | ADO Events | Event Parameters | Types of Events


ADO Event Handler Summary

Two ADO objects can raise events: the Connection object and the Recordset object. The ConnectionEvent family pertains to operations on the Connection object, and the RecordsetEvent family pertains to operations on the Recordset object.

  • Connection Events: Events are issued when a transaction on a connection begins, is committed, or is rolled back; when a Command executes; when a warning occurs during a Connection Event operation; or when a Connection starts or ends.
  • Recordset Events: Events are issued around asynchronous fetch operations as well as when you navigate through the rows of a Recordset object, change a field in a row of a Recordset, change a row in a Recordset, open a Recordset with a server-side cursor, close a Recordset, or make any change whatsoever in the Recordset.

The following tables summarize the events and their descriptions.

ConnectionEvent | Description
BeginTransComplete, CommitTransComplete, RollbackTransComplete | Transaction Management—Notification that the current transaction on the connection has started, committed, or rolled back.
WillConnect, ConnectComplete, Disconnect | Connection Management—Notification that the current connection will start, has started, or has ended.
WillExecute, ExecuteComplete | Command Execution Management—Notification that the execution of the current command on the connection will start or has ended.
InfoMessage | Informational—Notification that there is additional information about the current operation.
RecordsetEvent | Description
FetchProgress, FetchComplete | Retrieval Status—Notification of the progress of a data retrieval operation, or that the retrieval operation has completed. These events are only available if the Recordset was opened using a client-side cursor.
WillChangeField, FieldChangeComplete | Field Change Management—Notification that the value of the current field will change, or has changed.
WillMove, MoveComplete, EndOfRecordset | Navigation Management—Notification that the current row position in a Recordset will change, has changed, or has reached the end of the Recordset.
WillChangeRecord, RecordChangeComplete | Row Change Management—Notification that something in the current row of the Recordset will change, or has changed.
WillChangeRecordset, RecordsetChangeComplete | Recordset Change Management—Notification that something in the current Recordset will change, or has changed.

See Also

ADO Event Instantiation by Language | ADO Events | Event Parameters | How Event Handlers Work Together | Types of Events


Types of Events

There are two basic types of events. "Will Events," which are called before an operation starts, usually include "Will" in their names—for example, WillChangeRecordset or WillConnect. Events that are called after an event has been completed usually include "Complete" in their names—for example, RecordChangeComplete or ConnectComplete. Exceptions exist—such as InfoMessage—but these occur after the associated operation has completed.

Will Events

Event handlers called before the operation starts offer you the opportunity to examine or modify the operation parameters, and then either cancel the operation or allow it to complete. These event-handler routines usually have names of the form WillEvent.

Complete Events

Event handlers called after an operation completes can notify your application that an operation has concluded. Such an event handler is also notified when a Will event handler cancels a pending operation. These event-handler routines usually have names of the form EventComplete.

Will and Complete events are typically used in pairs.

Other Events

The other event handlers—that is, events whose names are not of the form WillEvent or EventComplete—are called only after an operation completes. These events are Disconnect, EndOfRecordset, and InfoMessage.

See Also

ADO Event Handler Summary | ADO Event Instantiation by Language | Event Parameters | How Event Handlers Work Together


Event Parameters

Every event handler has a status parameter that controls the event handler. For Complete events, this parameter is also used to indicate the success or failure of the operation that generated the event. Most Complete events also have an error parameter to provide information about any error that might have occurred, as well as one or more object parameters that refer to the ADO objects used to perform the operation. For example, the ExecuteComplete event includes object parameters for the Command, Recordset, and Connection objects associated with the event. In the following Microsoft® Visual Basic® example, you can see the pCommand, pRecordset and pConnection objects which represent the Command, Recordset, and Connection objects used by the Execute method.

Private Sub connEvent_ExecuteComplete(ByVal RecordsAffected As Long, ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pCommand As ADODB.Command, ByVal pRecordset As ADODB.Recordset, ByVal pConnection As ADODB.Connection)

Except for the Error object, the same parameters are passed to the Will events. This gives you the opportunity to examine each of the objects to be used in the pending operation and determine whether the operation should be allowed to complete.

Some event handlers have a Reason parameter, which provides additional information about why the event occurred. For example, the WillMove and MoveComplete events can occur due to any one of the navigation methods (MoveNext, MovePrevious, and so on) being called or as the result of a requery.

Status Parameter

When the event-handler routine is called, the Status parameter is set to one of the following values.

Value | Description
adStatusOK | Passed to both Will and Complete events. This value means that the operation that caused the event completed successfully.
adStatusErrorsOccurred | Passed to Complete events only. This value means that the operation that caused the event was unsuccessful, or a Will event canceled the operation. Check the Error parameter for more details.
adStatusCantDeny | Passed to Will events only. This value means that the operation cannot be canceled by the Will event. It must be performed.

If you determine in your Will event that the operation should continue, leave the Status parameter unchanged. As long as the incoming status parameter was not set to adStatusCantDeny, however, you can cancel the pending operation by changing Status to adStatusCancel. When you do so, the Complete event associated with the operation has its Status parameter set to adStatusErrorsOccurred. The Error object passed to the Complete event will contain the value adErrOperationCancelled.

If you no longer want to process an event, you can set Status to adStatusUnwantedEvent and your application will no longer receive notification of that event. Remember, however, that some events can be raised for more than one reason. In that case, you must specify adStatusUnwantedEvent for each possible reason. For example, in order to stop receiving notification of pending RecordChange events, you must set the Status parameter to adStatusUnwantedEvent for adRsnAddNew, adRsnDelete, adRsnUpdate, adRsnUndoUpdate, adRsnUndoAddNew, adRsnUndoDelete, and adRsnFirstChange as they occur.

Value | Description
adStatusUnwantedEvent | Request that this event handler receive no further notifications.
adStatusCancel | Request cancellation of the operation that is about to occur.

Error Parameter

The Error parameter is a reference to an ADO Error object. When the Status parameter is set to adStatusErrorsOccurred, the Error object contains details about why the operation failed. If the Will event associated with a Complete event has canceled the operation by setting the Status parameter to adStatusCancel, the error object is always set to adErrOperationCancelled.

Object Parameter

Each event receives one or more objects representing the objects that are involved in the operation. For example, the ExecuteComplete event receives a Command object, a Recordset object, and a Connection object.

Reason Parameter

The Reason parameter, adReason, provides additional information about why the event occurred. Events with an adReason parameter may be called several times, even for the same operation, each time for a different reason. For example, the WillChangeRecord event handler is called for operations that are about to do or undo the insertion, deletion, or modification of a record. If you want to process an event only when it occurs for a particular reason, you can use the adReason parameter to filter out the occurrences you are not interested in. For example, if you wanted to process record-change events only when they occur because a record was added, you can do something like this:

' BeginEventExampleVB01
Private Sub rsTest_WillChangeRecord(ByVal adReason As ADODB.EventReasonEnum, ByVal cRecords As Long, adStatus As ADODB.EventStatusEnum, ByVal pRecordset As ADODB.Recordset)
   If adReason = adRsnAddNew Then
       ' Process event
       '...
   Else
       ' Cancel event notification for all
       ' other possible adReason values.
       adStatus = adStatusUnwantedEvent
   End If
End Sub
' EndEventExampleVB01

In this case, notification can potentially occur for each of the other reasons. However, it will occur only once for each reason. After the notification has occurred once for each reason, you will receive notification only for the addition of a new record.

In contrast, you need to set adStatus to adStatusUnwantedEvent only one time to request that an event handler without an adReason parameter stop receiving event notifications.

See Also

ADO Event Handler Summary | ADO Event Instantiation by Language | How Event Handlers Work Together | Types of Events


How Event Handlers Work Together

Unless you are programming in Visual Basic, all event handlers for Connection and Recordset events must be implemented, regardless of whether you actually process all of the events. The amount of implementation work you have to do depends on your programming language. For more information, see ADO Event Instantiation by Language.

Paired Event Handlers

Each Will event handler has an associated Complete event handler. For example, when your application changes the value of a field, the WillChangeField event handler is called. If the change is acceptable, your application leaves the adStatus parameter unchanged and the operation is performed. When the operation completes, a FieldChangeComplete event notifies your application that the operation has finished. If it completed successfully, adStatus contains adStatusOK; otherwise, adStatus contains adStatusErrorsOccurred and you must check the Error object to determine the cause of the error.

When WillChangeField is called, you might determine that the change should not be made. In that case, set adStatus to adStatusCancel. The operation is canceled and the FieldChangeComplete event receives an adStatus value of adStatusErrorsOccurred. The Error object contains adErrOperationCancelled so that your FieldChangeComplete handler knows that the operation was canceled.

Sometimes an operation can raise more than one event. For example, the Recordset object has paired events for Field changes and Record changes. When your application changes the value of a Field, the WillChangeField event handler is called. If it determines that the operation can continue, the WillChangeRecord event handler is also raised. If this handler also allows the event to continue, the change is made and the FieldChangeComplete and RecordChangeComplete event handlers are called. The order in which the Will event handlers for a particular operation are called is not defined, so you should avoid writing code that depends on calling handlers in a particular sequence.

In instances when multiple Will events are raised, one of the events might cancel the pending operation. For example, when your application changes the value of a Field, both WillChangeField and WillChangeRecord event handlers would normally be called. However, if the operation is canceled in the first event handler, its associated Complete handler is immediately called with adStatusOperationCancelled. The second handler is never called. If, however, the first event handler allows the event to proceed, the other event handler will be called. If it then cancels the operation, both Complete events will be called as in the earlier examples.

Unpaired Event Handlers

As long as the status passed to the event is not adStatusCantDeny, you can turn off event notifications for any event by returning adStatusUnwantedEvent in the Status parameter. For example, when your Complete event handler is called the first time, you can return adStatusUnwantedEvent. You will subsequently receive only Will events. However, some events can be triggered for more than one reason. In that case, the event will have a Reason parameter. When you return adStatusUnwantedEvent, you will stop receiving notifications for that event only when they occur for that particular reason. In other words, you will potentially receive notification for each possible reason that the event could be triggered.

Single Will event handlers can be useful when you want to examine the parameters that will be used in an operation. You can modify those operation parameters or cancel the operation.

Alternatively, leave Complete event notification enabled. When your first Will event handler is called, return adStatusUnwantedEvent. You will subsequently receive only Complete events.

Single Complete event handlers can be useful for managing asynchronous operations. Each asynchronous operation has an appropriate Complete event.

For example, it can take a long time to populate a large Recordset object. If your application is appropriately written, you can start a Recordset.Open(...,adAsyncExecute) operation and continue with other processing. You will eventually be notified when the Recordset is populated by an ExecuteComplete event.

Single Event Handlers and Multiple Objects

The flexibility of a programming language like Microsoft Visual C++® enables you to have one event handler process events from multiple objects. For example, you could have one Disconnect event handler process events from several Connection objects. If one of the connections ended, the Disconnect event handler would be called. You could tell which connection caused the event because the event-handler object parameter would be set to the corresponding Connection object.

Note This technique cannot be used in Visual Basic because that language can correlate only one object to an event handler.

See Also

ADO Event Handler Summary | ADO Event Instantiation by Language | Event Parameters | Types of Events


ADO Event Instantiation by Language

Each programming language creates instances of ADO events differently. All of the following examples create a ConnectComplete event handler.

  • Visual Basic
  • Visual C++
  • Visual J++
  • VBScript
  • JScript
  • ADO/WFC


Visual Basic

In order to handle ADO events in Microsoft® Visual Basic®, you must declare a module-level variable using the WithEvents keyword. The variable can be declared only as part of a class module and must be declared at the module level. This is not as restrictive as it seems, however, because Visual Basic Form objects are also classes. The simplest way to handle ADO events is to declare a variable using WithEvents. The following example handles the ConnectComplete event for a Connection object:

' BeginEventExampleVB02
Dim WithEvents connEvent As Connection
Attribute connEvent.VB_VarHelpID = -1

Private Sub Form_Load()
Dim strConn As String

   ' Create a new object with event
   ' handling enabled.
   strConn = "Provider=sqloledb;" & _
      "Data Source=a-iresmi2000;Initial Catalog=Northwind;User Id=sa;Password=;"
   Set connEvent = New ADODB.Connection
   connEvent.Open strConn
End Sub

Private Sub connEvent_ConnectComplete(ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pConnection As ADODB.Connection)
Dim strMsg As String

   If adStatus = adStatusErrorsOccurred Then
      Select Case pError.Number
         Case adErrOperationCancelled
            ' The operation was cancelled in the
            ' Will event. Notify the user and exit.
            strMsg = "I'm sorry you can't connect right now." & vbCrLf
            strMsg = strMsg & "Click OK to exit."
            Unload Me
         Case Else
            strMsg = "Error " & Format(pError.Number) & vbCrLf
            strMsg = strMsg & pError.Description
            strMsg = strMsg & "Click OK to exit."
            Unload Me
      End Select
   End If
   frmWait.btnOK.Enabled = True
End Sub
' EndEventExampleVB02

The Connection object is declared at the Form level using the WithEvents keyword to enable event handling. The Form_Load event handler actually creates the object by assigning a new Connection object to connEvent and then opens the connection. Of course, a real application would do more processing in the Form_Load event handler than is shown here.


Visual C++

This is a schematic description of how to instantiate ADO events in Microsoft® Visual C++®. See ADO Events Model Example (Visual C++) for a complete description.

Create classes derived from the ConnectionEventsVt and RecordsetEventsVt interfaces found in the file adoint.h.

// BeginEventExampleVC01
class CConnEvent : public ConnectionEventsVt
{
    public:
    STDMETHODIMP InfoMessage(
            ADOError *pError,
            EventStatusEnum *adStatus,
            _ADOConnection *pConnection);
...
}

class CRstEvent : public RecordsetEventsVt
{
    public:
        STDMETHODIMP WillChangeField(
                LONG cFields,
                VARIANT Fields,
                EventStatusEnum *adStatus,
                _ADORecordset *pRecordset);
...
}
// EndEventExampleVC01

Implement each of the event-handler methods in both classes. It is sufficient that each method merely return an HRESULT of S_OK. However, when you make it known that your event handlers are available, they will be called continuously by default. Instead, you might want to request no further notification after the first time by setting adStatus to adStatusUnwantedEvent.

// BeginEventExampleVC02
STDMETHODIMP CConnEvent::ConnectComplete(
            ADOError *pError,
            EventStatusEnum *adStatus,
            _ADOConnection *pConnection)
        {
        *adStatus = adStatusUnwantedEvent;
        return S_OK;
        }

// EndEventExampleVC02

The event classes inherit from IUnknown, so you must also implement the QueryInterface, AddRef, and Release methods. Also implement class constructors and destructors. Choose the Visual C++ tools with which you are most comfortable to simplify this part of the task.

Make it known that your event handlers are available by issuing QueryInterface on the Recordset and Connection objects for the IConnectionPointContainer and IConnectionPoint interfaces. Then issue IConnectionPoint::Advise for each class.

For example, assume you are using a Boolean function that returns True if it successfully informs a Recordset object that you have event handlers available.

// BeginEventExampleVC03
HRESULT    hr;
DWORD      dwEvtClass;
IConnectionPointContainer    *pCPC = NULL;
IConnectionPoint             *pCP = NULL;
CRstEvent                    *pRStEvent = NULL;
...
_RecordsetPtr                pRs;
pRs.CreateInstance(__uuidof(Recordset));
pRStEvent = new CRstEvent();
if (pRStEvent == NULL) return FALSE;
...
hr = pRs->QueryInterface(IID_IConnectionPointContainer, &pCPC);
if (FAILED(hr)) return FALSE;
hr = pCPC->FindConnectionPoint(IID_ADORecordsetEvents, &pCP);
pCPC->Release();    // Always Release now, even before checking.
if (FAILED(hr)) return FALSE;
hr = pCP->Advise(pRstEvent, &dwEvtClass);   //Turn on event support.
pCP->Release();
if (FAILED(hr)) return FALSE;
...
return TRUE;
...
// EndEventExampleVC03

At this point, events for the RecordsetEvent family are enabled and your methods will be called as Recordset events occur.

Later, when you want to make your event handlers unavailable, get the connection point again and issue the IConnectionPoint::Unadvise method.

<"C:\MDAC SDK 2.6\ADO\Source\ADOPG\Code\VC\ErrorHandling\EventExampleVC04.cpp" BeginEventExampleVC04,EndEventExampleVC04>

You must release interfaces and destroy class objects as appropriate.


Visual J++

This short Microsoft® Visual J++® example shows how you can associate your own function with a particular event.

// BeginEventExampleVJ
import wfc.data.*;
public class MyClass
{
ConnectionEventHandler handler =
    new ConnectionEventHandler(this,"onConnectComplete");

    public void onConnectComplete(Object sender,ConnectionEvent e)
    {
        if (e.adStatus == AdoEnums.EventStatus.ERRORSOCCURRED)
            System.out.println("Connection failed");
        else
            System.out.println("Connection completed");
        return;
    }

    void main( void )
    {
        Connection conn = new Connection();

        conn.addOnConnectComplete(handler);     // Enable event support.
        conn.open("DSN=Pubs");
        conn.close();
        conn.removeOnConnectComplete(handler);  // Disable event support.
    }
}
// EndEventExampleVJ

First, the class method onConnectionComplete is associated with the ConnectionComplete event by creating a new ConnectionEventHandler object and assigning the onConnectComplete function to the object.

The main function then creates a Connection object and enables event handling by calling the addOnConnectComplete method and passing it the address of the handler function.


VBScript

Microsoft Visual Basic Scripting Edition does not support ADO events.


JScript

Microsoft® JScript® does not support ADO events.


ADO/WFC

ADO for Windows Foundation Classes (ADO/WFC) builds on the ADO event model and presents a simplified application programming interface. In general, ADO/WFC intercepts ADO events, consolidates the event parameters into a single event class, and then calls your event handler.

To use ADO events in ADO/WFC

  1. Define your own event handler to process an event. For example, if you wanted to process the ConnectComplete event in the ConnectionEvent family, you might use this code:
    public void onConnectComplete(Object sender,ConnectionEvent e)
{
    System.out.println("onConnectComplete:" + e);
}
  2. Define a handler object to represent your event handler. The handler object should be of data type ConnectEventHandler for an event of type ConnectionEvent, or data type RecordsetEventHandler for an event of type RecordsetEvent. For example, code the following for your ConnectComplete event handler:
        ConnectionEventHandler handler =
        new ConnectionEventHandler(this, "onConnectComplete");

    The first argument of the ConnectionEventHandler constructor is a reference to the class that contains the event handler named in the second argument.

    The Microsoft Visual J++ compiler also supports an equivalent syntax:

        ConnectionEventHandler handler =
        new ConnectionEventHandler(this.onConnectComplete);

    The single argument is a reference to the desired class (this) and method within the class (onConnectComplete).

  3. Add your event handler to a list of handlers designated to process a particular type of event. Use the method with a name such as addOn EventName(handler).
  4. ADO/WFC internally implements all the ADO event handlers. Therefore, an event caused by a Connection or Recordset operation is intercepted by an ADO/WFC event handler.

    The ADO/WFC event handler passes ADO ConnectionEvent parameters in an instance of the ADO/WFC ConnectionEvent class, or ADO RecordsetEvent parameters in an instance of the ADO/WFC RecordsetEvent class. These ADO/WFC classes consolidate the ADO event parameters; that is, each ADO/WFC class contains one data member for each unique parameter in all the ADO ConnectionEvent or RecordsetEvent methods.

  5. ADO/WFC then calls your event handler with the ADO/WFC event object. For example, your onConnectComplete handler has a signature like this:
        public void onConnectComplete(Object sender,ConnectionEvent e)

    The first argument is the type of object that sent the event (Connection or Recordset), and the second argument is the ADO/WFC event object (ConnectionEvent or RecordsetEvent).

    The signature of your event handler is simpler than an ADO event. However, you must still understand the ADO event model to know what parameters apply to an event and how to respond.

  6. Return from your event handler to the ADO/WFC handler for the ADO event. ADO/WFC copies pertinent ADO/WFC event data members back to the ADO event parameters, and then the ADO event handler returns.
  7. When you are finished processing, remove your handler from the list of ADO/WFC event handlers. Use the method with a name such as removeOn EventName(handler).

See Also

ADO Event Handler Summary | ADO/WFC Programming | ADO/WFC Syntax Index | Event Parameters | How Event Handlers Work Together | Types of Events


Chapter 8: Understanding Cursors and Locks

It is important to understand how cursors operate so you can select the best and most efficient cursor type for an application's data-access requirements. A less-than-optimal cursor configuration can make data-access operations painfully slow.

Many capabilities of the ADO Recordset object are determined by the type and location of the cursor, as well as the lock type.

This chapter covers the following topics:

  • What is a Cursor?
  • Types of Cursors
  • The Significance of Cursor Location
  • The Microsoft Cursor Service for OLE DB
  • What is a Lock?
  • Using CacheSize
  • Cursor and Lock Characteristics


What is a Cursor?

Operations in a relational database act on a complete set of rows. The set of rows returned by a SELECT statement consists of all the rows that satisfy the conditions in the WHERE clause of the statement. This complete set of rows returned by the statement is known as the result set. Applications, especially those that are interactive and online, cannot always work effectively with the entire result set as a unit. These applications need a mechanism to work with one row or a small block of rows at a time. Cursors are an extension to result sets that provide that mechanism.

A cursor is implemented by a cursor library. A cursor library is software, often implemented as a part of a database system or a data access API, that is used to manage attributes of data returned from a data source (a result set). These attributes include concurrency management, position in the result set, number of rows returned, and whether or not you can move forward and/or backward through the result set (scrollability).

A cursor keeps track of the position in the result set, and allows you to perform multiple operations row by row against a result set, with or without returning to the original table. In other words, cursors conceptually return a result set based on tables within the databases. The cursor is so named because it indicates the current position in the result set, just as the cursor on a computer screen indicates current position.

It is important to become familiar with the concept of cursors before moving on to learn the specifics of their usage in ADO.

Using cursors, you can:

  • Specify positioning at specific rows in the result set.
  • Retrieve one row or a block of rows based on the current result set position.
  • Modify data in the rows at the current position in the result set.
  • Define different levels of sensitivity to data changes made by other users.

For example, consider an application that displays a list of available products to a potential buyer. The buyer scrolls through the list to see product details and cost, and finally selects a product for purchase. Additional scrolling and selection occurs for the remainder of the list. As far as the buyer is concerned, the products appear one at a time, but the application uses a scrollable cursor to browse up and down through the result set.

You can use cursors in a variety of ways:

  • With no rows at all.
  • With some or all of the rows in a single table.
  • With some or all of the rows from logically joined tables.
  • As read-only or updatable at the cursor or field level.
  • As forward-only or fully scrollable.
  • With the cursor keyset located on the server.
  • Sensitive to underlying table changes caused by other applications (such as membership, sort, inserts, updates, and deletes).
  • Existing on either the server or the client.

Read-only cursors help users browse through the result set, and read/write cursors can implement individual row updates. Complex cursors can be defined with keysets that point back to base table rows. While some cursors are read-only in a forward direction, others can move back and forth and provide a dynamic refresh of the result set based on changes that other applications are making to the database.

Not all applications need to use cursors to access or update data. Some queries simply do not require direct row updating by using a cursor. Cursors should be one of the last techniques you choose to retrieve data—and then you should choose the lowest impact cursor possible. When you create a result set by using a stored procedure, the result set is not updatable using cursor edit or update methods.

Concurrency

In some multi-user applications it is vitally important for the data presented to the end user to be as current as possible. A classic example of such a system is an airline reservation system, where many users might be contending for the same seat on a given flight (and thus, a single record). In a case like this, the application design must handle concurrent operations on a single record.

In other applications, concurrency is not as important. In such cases, the expense involved in keeping the data current at all times cannot be justified.

Position

A cursor also keeps track of the current position in a result set. Think of the cursor position as a pointer to the current record, similar to the way an array index points to the value at that particular location in the array.

Scrollability

The type of cursor employed by your application also affects the ability to move forward and backward through the rows in a result set; this is sometimes called scrollability. The ability to move forward and backward through a result set adds to the complexity of the cursor, and is therefore more expensive to implement. For this reason, you should ask for a cursor with this functionality only when necessary.


Types of Cursors

As a general rule, your application should use the simplest cursor that provides the required data access. Each additional cursor characteristic beyond the basics (forward-only, read-only, static, scrolling, unbuffered) has a price—in client memory, network load, or performance. In many cases, the default cursor options generate a more complex cursor than your application actually needs.

Your choice of cursor type depends on how your application uses the result set and also on several design considerations, including the size of the result set, the percentage of the data likely to be used, sensitivity to data changes, and application performance requirements.

At its most basic, your cursor choice depends on whether you need to change or simply view the data:

  • If you just need to scroll through a set of results, but not change data, use a forward-only or static cursor.
  • If you have a large result set and need to select just a few rows, use a keyset cursor.
  • If you want to synchronize a result set with recent adds, changes, and deletes by all concurrent users, use a dynamic cursor.

Although each cursor type seems to be distinct, keep in mind that these cursor types are not so much different varieties as simply the result of overlapping characteristics and options.

See Also

Forward-Only Cursors | Static Cursors | Keyset Cursors | Dynamic Cursors


Forward-Only Cursors

The typical default cursor type, called a forward-only (or non-scrollable) cursor, can move only forward through the result set. A forward-only cursor does not support scrolling (the ability to move forward and backward in the result set); it only supports fetching rows from the start to the end of the result set. With some forward-only cursors (such as with the SQL Server cursor library), all insert, update, and delete statements made by the current user (or committed by other users) that affect rows in the result set are visible as the rows are fetched. Because the cursor cannot be scrolled backward, however, changes made to rows in the database after the row was fetched are not visible through the cursor.

After the data for the current row is processed, the forward-only cursor releases the resources that were used to hold that data. Forward-only cursors are dynamic by default, meaning that all changes are detected as the current row is processed. This provides faster cursor opening and enables the result set to display updates made to the underlying tables.

While forward-only cursors do not support backward scrolling, your application can return to the beginning of the result set by closing and reopening the cursor. This is an effective way to work with small amounts of data. As an alternative, your application could read the result set once, cache the data locally, and then browse the local data cache.

If your application does not require scrolling through the result set, the forward-only cursor is the best way to retrieve data quickly with the least amount of overhead. Use the adOpenForwardOnly CursorTypeEnum to indicate that you want to use a forward-only cursor in ADO.

See Also

Static Cursors | Keyset Cursors | Dynamic Cursors


Static Cursors

The static cursor always displays the result set as it was when the cursor was first opened. Depending on implementation, static cursors are either read-only or read/write and provide forward and backward scrolling. The static cursor does not usually detect changes made to the membership, order, or values of the result set after the cursor is opened. Static cursors may detect their own updates, deletes, and inserts, although they are not required to do so.

Static cursors never detect other updates, deletes, and inserts. For example, suppose a static cursor fetches a row, and another application then updates that row. If the application refetches the row from the static cursor, the values it sees are unchanged, despite the changes made by the other application. All types of scrolling are supported, but providers may or may not support bookmarks.

If your application does not need to detect data changes and requires scrolling, the static cursor is the best choice. Use the adOpenStatic CursorTypeEnum to indicate that you want to use a static cursor in ADO.

See Also

Forward-Only Cursors | Keyset Cursors | Dynamic Cursors


Keyset Cursors

The keyset cursor provides functionality between a static and a dynamic cursor in its ability to detect changes. Like a static cursor, it does not always detect changes to the membership and order of the result set. Like a dynamic cursor, it does detect changes to the values of rows in the result set.

Keyset-driven cursors are controlled by a set of unique identifiers (keys) known as the keyset. The keys are built from a set of columns that uniquely identify the rows in the result set. The keyset is the set of key values from all the rows returned by the query statement.

With keyset-driven cursors, a key is built and saved for each row in the cursor and stored either on the client workstation or on the server. When you access each row, the stored key is used to fetch the current data values from the data source. In a keyset-driven cursor, result set membership is frozen when the keyset is fully populated. Thereafter, additions or updates that affect membership are not a part of the result set until it is reopened.

Changes to data values (made either by the keyset owner or other processes) are visible as the user scrolls through the result set. Inserts made outside the cursor (by other processes) are visible only if the cursor is closed and reopened. Inserts made from inside the cursor are visible at the end of the result set.

When a keyset-driven cursor attempts to retrieve a row that has been deleted, the row appears as a "hole" in the result set. The key for the row exists in the keyset, but the row no longer exists in the result set. If the key values in a row are updated, the row is considered to have been deleted and then inserted, so such rows also appear as holes in the result set. While a keyset-driven cursor can always detect rows deleted by other processes, it can optionally remove the keys for rows it deletes itself. Keyset-driven cursors that do this cannot detect their own deletes because the evidence has been removed.

An update to a key column operates like a delete of the old key followed by an insert of the new key. The new key value is not visible if the update was not made through the cursor. If the update was made through the cursor, the new key value is visible at the end of the result set.

There is a variation on keyset-driven cursors called keyset-driven standard cursors. In a keyset-driven standard cursor, the membership of rows in the result set and the order of the rows are fixed at cursor open time, but changes to values that are made by the cursor owner and committed changes made by other processes are visible. If a change disqualifies a row for membership or affects the order of a row, the row does not disappear or move unless the cursor is closed and reopened. Inserted data does not appear, but changes to existing data do appear as the rows are fetched.

The keyset-driven cursor is difficult to use correctly because the sensitivity to data changes depends on many differing circumstances, as described above. However, if your application is not concerned with concurrent updates, can programmatically handle bad keys, and must directly access certain keyed rows, the keyset-driven cursor might work for you. Use the adOpenKeyset CursorTypeEnum to indicate that you want to use a keyset cursor in ADO.

See Also

Forward-Only Cursors | Static Cursors | Dynamic Cursors


Dynamic Cursors

Dynamic cursors detect all changes made to the rows in the result set, regardless of whether the changes occur from inside the cursor or by other users outside the cursor. All insert, update, and delete statements made by all users are visible through the cursor. The dynamic cursor can detect any changes made to the rows, order, and values in the result set after the cursor is opened. Updates made outside the cursor are not visible until they are committed (unless the cursor transaction isolation level is set to "uncommitted").

For example, suppose a dynamic cursor fetches two rows and another application, and then updates one of those rows and deletes the other. If the dynamic cursor then fetches those rows, it will not find the deleted row, but it will display the new values for the updated row.

The dynamic cursor is a good choice if your application must detect all concurrent updates made by other users. Use the adOpenDynamic CursorTypeEnum to indicate that you want to use a dynamic cursor in ADO.


The Significance of Cursor Location

Every cursor uses temporary resources to hold its data. These resources can be memory, a disk paging file, temporary disk files, or even temporary storage in the database. The cursor is called a client-side cursor when these resources are located on the client computer. The cursor is called a server-side cursor when these resources are located on the server.

Client-Side Cursors

In ADO, call for a client-side cursor by using the adUseClient CursorLocationEnum. With a non-keyset client-side cursor, the server sends the entire result set across the network to the client computer. The client computer provides and manages the temporary resources needed by the cursor and result set. The client-side application can browse through the entire result set to determine which rows it requires.

Static and keyset-driven client-side cursors may place a significant load on your workstation if they include too many rows. While all of the cursor libraries are capable of building cursors with thousands of rows, applications designed to fetch such large rowsets may perform poorly. There are exceptions, of course. For some applications, a large client-side cursor might be perfectly appropriate and performance might not be an issue.

One obvious benefit of the client-side cursor is quick response. After the result set has been downloaded to the client computer, browsing through the rows is very fast. Your application is generally more scalable with client-side cursors because the cursor's resource requirements are placed on each separate client and not on the server.

Server-Side Cursors

In ADO, call for a server-side cursor by using the adUseServer CursorLocationEnum. With a server-side cursor, the server manages the result set using resources provided by the server computer. The server-side cursor returns only the requested data over the network. This type of cursor can sometimes provide better performance than the client-side cursor, especially in situations where excessive network traffic is a problem.

However, it is important to point out that a server-side cursor is—at least temporarily—consuming precious server resources for every active client. You must plan accordingly to ensure that your server hardware is capable of managing all of the server-side cursors requested by active clients. Also, a server-side cursor can be slow because it provides only single row access—there is no batch cursor available.

Server-side cursors are useful when inserting, updating, or deleting records. With server-side cursors, you can have multiple active statements on the same connection.


The Microsoft Cursor Service for OLE DB

When you select a client-side cursor, or set the ClientLocation property to adUseClient, you are invoking the Microsoft Cursor Service for OLE DB. You might also see references to the "Client Cursor Engine", which is essentially the same thing in the context of ADO. This service supplements the cursor-support functions of data providers. As a result, you can perceive relatively uniform functionality from all data providers.

The Cursor Service for OLE DB makes dynamic properties available and enhances the behavior of certain methods. For example, the Optimize dynamic property enables the creation of temporary indexes to facilitate certain operations, such as the Find method.

The Cursor Service enables support for batch updating in all cases. It also simulates more capable cursor types, such as dynamic cursors, when a data provider can only supply less capable cursors, such as static cursors.

See Also

Microsoft Cursor Service for OLE DB


What is a Lock?

Locking is the process by which a DBMS restricts access to a row in a multi-user environment. When a row or column is exclusively locked, other users are not permitted to access the locked data until the lock is released. This ensures that two users cannot simultaneously update the same column in a row.

Locks can be very expensive from a resource perspective and should be used only when required to preserve data integrity. In a database where hundreds or thousands of users could be trying to access a record every second—such as a database connected to the Internet—unnecessary locking could quickly result in slower performance in your application.

You can control how the data source and the ADO cursor library manage concurrency by choosing the appropriate locking option.

Set the LockType property before opening a Recordset to specify what type of locking the provider should use when opening it. Read the property to return the type of locking in use on an open Recordset object.

Providers might not support all lock types. If a provider cannot support the requested LockType setting, it will substitute another type of locking. To determine the actual locking functionality available in a Recordset object, use the Supports method with adUpdate and adUpdateBatch.

The adLockPessimistic setting is not supported if the CursorLocation property is set to adUseClient. If an unsupported value is set, no error will result; the closest supported LockType will be used instead.

The LockType property is read/write when the Recordset is closed, and read-only when it is open.


Types of Locks

adLockBatchOptimistic

Indicates optimistic batch updates. Required for batch update mode.

Many applications fetch a number of rows at once and then need to make coordinated updates that include the entire set of rows to be inserted, updated, or deleted. With batch cursors, only one round trip to the server is needed, thus improving update performance and decreasing network traffic. Using a batch cursor library, you can create a static cursor and then disconnect from the data source. At this point you can make changes to the rows and subsequently reconnect and post the changes to the data source in a batch.

adLockOptimistic

Indicates that the provider uses optimistic locking—locking records only when you call the Update method. This means that there is a chance that the data may be changed by another user between the time you edit the record and when you call Update, which creates conflicts. Use this lock type in situations where the chances of a collision are low or where collisions can be readily resolved.

adLockPessimistic

Indicates pessimistic locking, record by record. The provider does what is necessary to ensure successful editing of the records, usually by locking records at the data source immediately before editing. Of course, this means that the records are unavailable to other users once you begin to edit, until you release the lock by calling Update. Use this type of lock in a system where you cannot afford to have concurrent changes to data, such as in a reservation system.

adLockReadOnly

Indicates read-only records. You cannot alter the data. A read-only lock is the "fastest" type of lock because it does not require the server to maintain a lock on the records.

adLockUnspecified

Does not specify a type of lock.


Using CacheSize

Use the CacheSize property to control how many records to retrieve at one time into local memory from the provider. For example, if the CacheSize is 10, after first opening the Recordset object, the provider retrieves the first 10 records into local memory. As you move through the Recordset object, the provider returns the data from the local memory buffer. As soon as you move past the last record in the cache, the provider retrieves the next 10 records from the data source into the cache.

Note CacheSize is based on the Maximum Open Rows provider-specific property (in the Properties collection of the Recordset object). You cannot set CacheSize to a value greater than Maximum Open Rows. To modify the number of rows that can be opened by the provider, set Maximum Open Rows.

The value of CacheSize can be adjusted during the life of the Recordset object, but changing this value only affects the number of records in the cache after subsequent retrievals from the data source. Changing the property value alone will not change the current contents of the cache.

If there are fewer records to retrieve than CacheSize specifies, the provider returns the remaining records and no error occurs.

A CacheSize setting of zero is not allowed and returns an error.

Records retrieved from the cache do not reflect concurrent changes that other users made to the source data. To force an update of all the cached data, use the Resync method.

If CacheSize is set to a value greater than 1, the navigation methods (Move, MoveFirst, MoveLast, MoveNext, and MovePrevious) may result in navigation to a deleted record, if deletion occurs after the records were retrieved. After the initial fetch, subsequent deletions will not be reflected in your data cache until you attempt to access a data value from a deleted row. However, setting CacheSize to 1 eliminates this issue because deleted rows cannot be fetched.


Cursor and Lock Characteristics

While the characteristics of a cursor depend upon capabilities of the provider, the following advantages and disadvantages generally apply to the various types of cursors and locks.

Cursor or lock type Advantages Disadvantages
adOpenForwardOnly
  • Low resource requirements
  • Cannot scroll backward
  • No data concurrency
adOpenStatic
  • Scrollable
  • No data concurrency
adOpenKeyset
  • Some data concurrency
  • Scrollable
  • Higher resource requirements
  • Not available in disconnected scenario
adOpenDynamic
  • High data concurrency
  • Scrollable
  • Highest resource requirements
  • Not available in disconnected scenario
adLockReadOnly
  • Low resource requirements
  • Highly scalable
  • Data not updatable through cursor
adLockBatchOptimistic
  • Batch updates
  • Allows disconnected scenarios
  • Other users able to access data
  • Data can be changed by multiple users at once
adLockPessimistic
  • Data cannot be changed by other users while locked
  • Prevents other users from accessing data while locked
adLockOptimistic
  • Other users able to access data
  • Data can be changed by multiple users at once


Chapter 9: Data Shaping

Data shaping provides a way to query a data source and return a Recordset that represents a parent-child relationship between two or more logical entities (a hierarchy). A classic example of a hierarchical relationship is customers and orders. For every customer in a database, there can be zero or more orders. Regular SQL provides a means of retrieving the data using JOIN syntax, but this can be inefficient and unwieldy because redundant parent data is repeated in each record returned for a given parent-child relationship. Data shaping can relate a single parent record in the parent Recordset to multiple child records in the child Recordset, avoiding the redundancy of a JOIN. Most people find the parent-child multiple Recordset programming model more natural and easier to work with than the single Recordset JOIN model.

The data shaping syntax also provides other capabilities. Developers can create new Recordset objects without an underlying data source by using the NEW keyword to describe the fields of the parent and child Recordsets. The new Recordset object can be populated with data and persistently stored. Developers can also perform various calculations or aggregations (for example, SUM, AVG, and MAX) on child fields. Data shaping can also create a parent Recordset from a child Recordset by grouping records in the child and placing one row in the parent for each group in the child.

See the following topics to learn more about data shaping:

  • Data Shaping Summary
  • Required Providers for Data Shaping
  • Shape Commands in General
  • Shape APPEND Clause
  • Shape COMPUTE Clause
  • Fabricating Hierarchical Recordsets
  • Accessing Rows in a Hierarchical Recordset
  • Formal Shape Grammar
  • Visual Basic for Applications Functions


Data Shaping Summary

The following sections describe concepts of data shaping, hierarchical recordsets, reshaping, grandchild aggregates, parameterized shapes, and saving shapes to files.

  • Data Shaping
  • Reshaping
  • Grandchild Aggregates
  • Parameterized Commands
  • Persisting Hierarchical Recordsets


Data Shaping

Data shaping enables you to define the columns of a shaped Recordset, the relationships between the entities represented by the columns, and the manner in which the Recordset is populated with data.

Columns of a shaped Recordset may contain data from a data provider such as Microsoft SQL Server, references to another Recordset, values derived from a calculation on a single row of a Recordset, values derived from an operation over a column of an entire Recordset; or they may be a newly fabricated, empty column.

When you retrieve the value of a column that contains a reference to another Recordset, ADO automatically returns the actual Recordset represented by the reference. A Recordset that contains another Recordset is called a hierarchical recordset. Hierarchical recordsets exhibit a parent-child relationship, in which the parent is the containing recordset and the child is the contained recordset. The reference to a Recordset is actually a reference to a subset of the child, called a chapter. A single parent may reference more than one child Recordset.

The shape command syntax enables you to programmatically create a shaped Recordset. You can then access the components of the Recordset programmatically or through an appropriate visual control. A shape command is issued like any other ADO command text.

You can make hierarchical Recordset objects in two ways with the shape command syntax. The first appends a child Recordset to a parent Recordset. The parent and child typically have at least one column in common: the value of the column in a row of the parent is the same as the value of the column in all rows of the child.

The second way generates a parent Recordset from a child Recordset. The records in the child Recordset are grouped, typically using the BY clause, and one row is added to the parent Recordset for each resulting group in the child. If the BY clause is omitted, the child Recordset will form a single group and the parent Recordset will contain exactly one row. This is useful for computing "grand total" aggregates over the entire child Recordset.

Regardless of which way the parent Recordset is formed, it will contain a chapter column that is used to relate it to a child Recordset. If you wish, the parent Recordset may also have columns that contain aggregates (SUM, MIN, MAX, and so on) over the child rows. Both the parent and the child Recordset may have columns which contain an expression on the row in the Recordset, as well as columns which are new and initially empty.

You can nest hierarchical Recordset objects to any depth (that is, create child Recordset objects of child Recordset objects, and so on).

You can access the Recordset components of the shaped Recordset programmatically or through an appropriate visual control.

Microsoft provides a visual tool that generates shape commands (see The Data Environment Designer in the Visual Basic documentation) and another that displays hierarchical cursors (see Using the Microsoft Hierarchical Flexgrid Control in the Visual Basic documentation).

For examples of shape commands and their resulting hierarchies, see Using the Data Shaping Service for OLE DB: A Closer Look.


Reshaping

A Recordset created by a clause of a shape command may be assigned an alias name (typically with the AS keyword). The alias of a shaped Recordset can be referenced in an entirely different command. That is, you may reuse, or reshape, a previously shaped Recordset in a new shape command. To support this feature, ADO provides a property, Reshape Name.

Reshaping has two main functions. The first is to associate an existing Recordset with a new parent Recordset.

Example

. . .
rs1.Open "SHAPE {select * from Customers} " & _
         "APPEND ({select * from Orders} AS chapOrders " & _
         "RELATE CustomerID to CustomerID)", cn

rs2.Open "SHAPE {select * from Employees} " & _
         "APPEND (chapOrders RELATE EmployeeID to EmployeeID)", cn
. . .

The second function is to enable non-chaptered access to existing child Recordset objects, using the syntax "SHAPE <recordset reshape name>".

Note You cannot append columns to an existing Recordset, reshape a parameterized Recordset or the Recordset objects in any intervening COMPUTE clause, or perform aggregate operations on any Recordset descendant from the Recordset being reshaped. The Recordset being reshaped and the new shape command must both use the same Connection.

See Also

Data Shaping


Grandchild Aggregates

The chapter column created in a clause of a shape command may be given a chapter-alias name (typically with the AS keyword). You may identify any column in any chapter of the shaped Recordset with a fully qualified name identifying the child containing the column. For example, if the parent chapter, chap1, contains a child chapter, chap2, that has an amount column, amt, then the qualified name would be chap1.chap2.amt. The qualified name may then be used as an argument to one of the aggregate functions (SUM, AVG, MAX, MIN, COUNT, STDEV, or ANY).

See Also

Data Shaping


Parameterized Commands with Intervening COMPUTE Commands

A typical parameterized shape APPEND command has a clause that creates a parent Recordset with a query command and another clause that creates a child Recordset with a parameterized query command—that is, a command containing a parameter placeholder (a question mark, "?"). The resulting shaped Recordset has two levels, in which the parent occupies the upper level and the child occupies the lower level.

The clause that creates the child Recordset may now be an arbitrary number of nested shape COMPUTE commands, where the most deeply nested command contains the parameterized query. The resulting shaped Recordset has multiple levels, in which the parent occupies the uppermost level, the child occupies the lowermost level, and an arbitrary number of Recordsets generated by the shape COMPUTE commands occupy the intervening levels.

The typical use for this feature is to invoke the aggregate function and grouping abilities of shape COMPUTE commands to create intervening Recordset objects with analytical information about the child Recordset. Furthermore, because this is a parameterized shape command, each time a chapter column of the parent is accessed, a new child Recordset may be retrieved. Because the intervening levels are derived from the child, they also will be recomputed.

See Also

Data Shaping


Persisting Hierarchical Recordsets

You can save a hierarchical Recordset to a file in either ADTG or XML format by calling the Save method. However, two limitations apply when saving hierarchical Recordsets in XML format: You cannot save in XML if the hierarchical Recordset contains pending updates, and you cannot save a parameterized hierarchical Recordset.

For more information about the Data Shaping provider, see Microsoft Data Shaping Service for OLE DB (ADO) and The Data Shaping Service for OLE DB (OLE DB).

See Also

Data Shaping | Formal Shape Grammar | Shape Commands in General


Required Providers for Data Shaping

Data shaping typically requires two providers. The service provider, Data Shaping Service for OLE DB, supplies the data shaping functionality, and a data provider, such as the OLE DB Provider for SQL Server, supplies rows of data to populate the shaped Recordset.

The name of the service provider (MSDataShape) can be specified as the value of the Connection object Provider property or the connection string keyword "Provider=MSDataShape;".

The name of the data provider can be specified as the value of the Data Provider dynamic property, which is added to the Connection object Properties collection by the Data Shaping Service for OLE DB, or the connection string keyword "Data Provider=provider".

No data provider is required if the Recordset is not populated (for example, as in a fabricated Recordset where columns are created with the NEW keyword). In that case, specify "Data Provider=none;".

Example

Dim cnn As New ADODB.Connection
cnn.Provider = "MSDataShape"
cnn.Open "Data Provider=SQLOLEDB;User Id=sa;Password=;Database=Northwind"

See Also

Data Shaping | Formal Shape Grammar | Shape Commands in General


Shape Commands in General

Data shaping defines the columns of a shaped Recordset, the relationships between the entities represented by the columns, and the manner in which the Recordset is populated with data.

A shaped Recordset may consist of the following types of columns.

Column type | Description
data | Fields from a Recordset returned by a query command to a data provider, table, or previously shaped Recordset.
chapter | A reference to another Recordset, called a chapter. Chapter columns make it possible to define a parent-child relationship where the parent is the Recordset containing the chapter column and the child is the Recordset represented by the chapter.
aggregate | The value of the column is derived by executing an aggregate function on all the rows or a column of all the rows of a child Recordset. (See Aggregate Functions in the following topic, Aggregate Functions, the CALC Function, and the NEW Keyword.)
calculated expression | The value of the column is derived by calculating a Visual Basic for Applications expression on columns in the same row of the Recordset. The expression is the argument to the CALC function. (See Calculated Expression in the following topic, Aggregate Functions, the CALC Function, and the NEW Keyword and in Visual Basic for Applications Functions.)
new | Empty, fabricated fields, which may be populated with data at a later time. The column is defined with the NEW keyword. (See NEW keyword in the following topic, Aggregate Functions, the CALC Function, and the NEW Keyword.)

A shape command may contain a clause specifying a query command to an underlying data provider that will return a Recordset object. The query's syntax depends on the requirements of the underlying data provider. This will usually be Structured Query Language (SQL), although ADO does not require the use of any particular query language.

You could use a SQL JOIN clause to relate two tables; however, a hierarchical Recordset may represent the information more efficiently. Each row of a Recordset created by a JOIN repeats information redundantly from one of the tables. A hierarchical Recordset has only one parent Recordset for each of multiple child Recordset objects.

Shape commands can be issued by Recordset objects or by setting the CommandText property of the Command object and then calling the Execute method.

Shape commands can be nested. That is, the parent-command or child-command may itself be another shape command.

The shape provider always returns a client cursor, even when the user specifies a cursor location of adUseServer.

For information about navigating a hierarchical Recordset, see Accessing Rows in a Hierarchical Recordset.

For precise information about syntactically correct shape commands, see Formal Shape Grammar.


Aggregate Functions, the CALC Function, and the NEW Keyword

Data shaping supports the following functions. The name assigned to the chapter containing the column to be operated on is the chapter-alias.

A chapter-alias may be fully qualified, consisting of each chapter column name leading to the chapter containing the column-name, all separated by periods. For example, if the parent chapter, chap1, contains a child chapter, chap2, that has an amount column, amt, then the qualified name would be chap1.chap2.amt.

Aggregate Functions | Description
SUM(chapter-alias.column-name) | Calculates the sum of all values in the specified column.
AVG(chapter-alias.column-name) | Calculates the average of all values in the specified column.
MAX(chapter-alias.column-name) | Calculates the maximum value in the specified column.
MIN(chapter-alias.column-name) | Calculates the minimum value in the specified column.
COUNT(chapter-alias[.column-name]) | Counts the number of rows in the specified alias. If a column is specified, only rows for which that column is non-Null are included in the count.
STDEV(chapter-alias.column-name) | Calculates the standard deviation in the specified column.
ANY(chapter-alias.column-name) | A value of the specified column. ANY has a predictable value only when the value of the column is the same for all rows in the chapter. Note If the column does not contain the same value for all of the rows in the chapter, the SHAPE command arbitrarily returns one of the values to be the value of the ANY function.
Calculated expression | Description
CALC(expression) | Calculates an arbitrary expression, but only on the row of the Recordset containing the CALC function. Any expression using these Visual Basic for Applications (VBA) Functions is allowed.
NEW keyword | Description
NEW field-type [(width | scale | precision | error [, scale | error])] | Adds an empty column of the specified type to the Recordset.

The field-type passed with the NEW keyword can be any of the following data types.

OLE DB data types | ADO data type equivalent(s)
DBTYPE_BSTR | adBSTR
DBTYPE_BOOL | adBoolean
DBTYPE_DECIMAL | adDecimal
DBTYPE_UI1 | adUnsignedTinyInt
DBTYPE_I1 | adTinyInt
DBTYPE_UI2 | adUnsignedSmallInt
DBTYPE_UI4 | adUnsignedInt
DBTYPE_I8 | adBigInt
DBTYPE_UI8 | adUnsignedBigInt
DBTYPE_GUID | adGuid
DBTYPE_BYTES | adBinary, AdVarBinary, adLongVarBinary
DBTYPE_STR | adChar, adVarChar, adLongVarChar
DBTYPE_WSTR | adWChar, adVarWChar, adLongVarWChar
DBTYPE_NUMERIC | adNumeric
DBTYPE_DBDATE | adDBDate
DBTYPE_DBTIME | adDBTime
DBTYPE_DBTIMESTAMP | adDBTimeStamp
DBTYPE_VARNUMERIC | adVarNumeric
DBTYPE_FILETIME | adFileTime
DBTYPE_ERROR | adError

When the new field is of type decimal (in OLE DB, DBTYPE_DECIMAL, or in ADO, adDecimal), you must specify the precision and scale values.

See Also

Data Shaping | Formal Shape Grammar | Shape Commands in General


Issuing Commands to the Underlying Data Provider

Any command that does not begin with SHAPE is passed through to the data provider. This is equivalent to issuing a shape command in the form "SHAPE {provider command}". These commands do not have to produce a Recordset. For instance, "SHAPE {DROP TABLE MyTable} is a perfectly valid shape command, assuming the data provider supports DROP TABLE.

This capability allows both normal provider commands and shape commands to share the same connection and transaction.

See Also

Data Shaping | Formal Shape Grammar | Shape Commands in General


Shape APPEND Clause

The shape command APPEND clause appends a column or columns to a Recordset. Often these columns are chapter columns, which refer to a child Recordset.

Syntax

SHAPE [parent-command [[AS] parent-alias]] APPEND column-list

Description

The parts of this clause are as follows:

parent-command
Zero or one of the following (you may omit the parent-command entirely):
  • A provider command within curly braces ("{}") that returns a Recordset object. The command is issued to the underlying data provider, and its syntax depends on the requirements of that provider. This will typically be the SQL language, although ADO does not require any particular query language.
  • Another shape command embedded in parentheses.
  • The TABLE keyword, followed by the name of a table in the data provider.
parent-alias
An optional alias that refers to the parent Recordset.
column-list
One or more of the following:
  • An aggregate column.
  • A calculated column.
  • A new column created with the NEW clause.
  • A chapter column. A chapter column definition is enclosed in parentheses ("()"). See syntax below:
SHAPE [parent-command [[AS] parent-alias]]
   APPEND (child-recordset [ [[AS] child-alias]
      RELATE parent-column TO child-column | PARAMETER param-number, ... ])
   [[AS] chapter-alias]
   [, ... ]
child-recordset
  • A provider command within curly braces ("{}") that returns a Recordset object. The command is issued to the underlying data provider, and its syntax depends on the requirements of that provider. This will typically be the SQL language, although ADO does not require any particular query language.
  • Another shape command embedded in parentheses.
  • The name of an existing shaped Recordset.
  • The TABLE keyword, followed by the name of a table in the data provider.
child-alias
An alias that refers to the child Recordset.
parent-column
A column in the Recordset returned by the parent-command.
child-column
A column in the Recordset returned by the child-command.
param-number
See Operation of Parameterized Commands.
chapter-alias
An alias that refers to the chapter column appended to the parent.

Note The "parent-column TO child-column" clause is actually a list, where each relation defined is separated by a comma.

Note The clause after the APPEND keyword is actually a list, where each clause is separated by a comma and defines another column to be appended to the parent.

See Also

Data Shaping | Formal Shape Grammar | Shape Commands in General


Operation of Non-Parameterized Commands

For non-parameterized commands, all of the provider commands are executed and the Recordsets are created during command execution. If the command is executed synchronously, all of the Recordsets will be fully populated. If an asynchronous population mode was selected, the populated state of the Recordsets will depend on the population mode and the size of the Recordsets.

For example, the parent-command could return a Recordset of customers for a company from a Customers table, and the child-command could return a Recordset of orders for all customers from an Orders table.

SHAPE {SELECT * FROM Customers}
   APPEND ({SELECT * FROM Orders} AS chapOrders
   RELATE customerID TO customerID)

For non-parameterized parent-child relationships, each parent and child Recordset object must have a column in common to associate them. The columns are named in the RELATE clause, parent-column first and then child-column. The columns may have different names in their respective Recordset objects but must refer to the same information in order to specify a meaningful relation. For example, the Customers and Orders Recordset objects could both have a customerID field. Because the membership of the child Recordset is determined by the provider command, it is possible for the child Recordset to contain orphaned rows. These orphaned rows are inaccessible without further reshaping.

Data shaping appends a chapter column to the parent Recordset. The values in the chapter column are references to rows in the child Recordset, which satisfy the RELATE clause. That is, the same value is in the parent-column of a given parent row as is in the child-column of all the rows of the chapter child. When multiple TO clauses are used in the same RELATE clause, they are implicitly combined using an AND operator. If the parent columns in the relate clause do not constitute a key to the parent Recordset, a single child row may have multiple parent rows.

When you access the reference in the chapter column, ADO automatically retrieves the Recordset represented by the reference. Note that in a non-parameterized command, although the entire child Recordset has been retrieved, the chapter only presents a subset of rows.

If the appended column has no chapter-alias, a name will be generated for it automatically. A Field object for the column will be appended to the Recordset object's Fields collection, and its data type will be adChapter.

For information about navigating a hierarchical Recordset, see Accessing Rows in a Hierarchical Recordset.

See Also

Data Shaping | Formal Shape Grammar | Shape Commands in General


Operation of Parameterized Commands

If you are working with a large child Recordset, especially compared to the size of the parent Recordset, but need to access only a few child chapters, you might find it more efficient to use a parameterized command.

A non-parameterized command retrieves both the entire parent and child Recordsets, appends a chapter column to the parent, and then assigns a reference to the related child chapter for each parent row.

A parameterized command retrieves the entire parent Recordset, but retrieves only the chapter Recordset when the chapter column is accessed. This difference in retrieval strategy can yield significant performance benefits.

For example, you can specify the following:

SHAPE {SELECT * FROM customer}
   APPEND ({SELECT * FROM orders WHERE cust_id = ?}
   RELATE cust_id TO PARAMETER 0)

The parent and child tables have a column name in common, cust_id. The child-command has a "?" placeholder, to which the RELATE clause refers (that is, "...PARAMETER 0").

Note The PARAMETER clause pertains solely to the shape command syntax. It is not associated with either the ADO Parameter object or the Parameters collection.

When the parameterized shape command is executed, the following occurs:

  1. The parent-command is executed and returns a parent Recordset from the Customers table.
  2. A chapter column is appended to the parent Recordset.
  3. When the chapter column of a parent row is accessed, the child-command is executed using the value of the customer.cust_id as the value of the parameter.
  4. All rows in the data provider rowset created in step 3 are used to populate the child Recordset. In this example, that is all the rows in the Orders table in which the cust_id equals the value of customer.cust_id. By default, the child Recordsets will be cached on the client until all references to the parent Recordset are released. To change this behavior, set the Recordset dynamic property Cache Child Rows to False.
  5. A reference to the retrieved child rows (that is, the chapter of the child Recordset) is placed in the chapter column of the current row of the parent Recordset.
  6. Steps 3–5 are repeated when the chapter column of another row is accessed.

The Cache Child Rows dynamic property is set to True by default. The caching behavior varies depending upon the parameter values of the query. In a query with a single parameter, the child Recordset for a given parameter value will be cached between requests for a child with that value. The following code demonstrates this:

...
SCmd = "SHAPE {select * from customer} " & _
         "APPEND({select * from orders where cust_id = ?} " & _
         "RELATE cust_id TO PARAMETER 0) AS chpCustOrder"
Rst1.Open sCmd, Cnn1
Set RstChild = Rst1("chpCustOrder").Value
Rst1.MoveNext      ' Next cust_id passed to Param 0, & new rs fetched
                   ' into RstChild.
Rst1.MovePrev      ' RstChild now holds cached rs, saving round trip.
...

In a query with two or more parameters, a cached child is used only if all the parameter values match the cached values.

Parameterized Commands and Complex Parent Child Relations

In addition to using parameterized commands to improve performance of an equi-join type hierarchy, parameterized commands can be used to support more complex parent-child relationships. For example, consider a Little League database with two tables: one consisting of the teams (team_id, team_name) and the other of games (date, home_team, visiting_team).

Using a non-parameterized hierarchy, there is no way to relate the teams and games tables in such a way that the child Recordset for each team contains its complete schedule. You can create chapters that contain the home schedule or the road schedule, but not both. This is because the RELATE clause limits you to parent-child relationships of the form (pc1=cc1) AND (pc2=pc2). So, if your command included "RELATE team_id TO home_team, team_id TO visiting_team", you would get only games where a team was playing itself. What you want is "(team_id=home_team) OR (team_id=visiting_team)", but the Shape provider does not support the OR clause.

To obtain the desired result, you can use a parameterized command. For example:

SHAPE {SELECT * FROM teams}
APPEND ({SELECT * FROM games WHERE home_team = ? OR visiting_team = ?}
        RELATE team_id TO PARAMETER 0,
               team_id TO PARAMETER 1)

This example exploits the greater flexibility of the SQL WHERE clause to get the result you need.

See Also

Data Shaping | Formal Shape Grammar | Shape Commands in General


Hybrid Commands

Hybrid commands are partially parameterized commands. For example:

SHAPE {select * from plants}
   APPEND( {select * from customers where country = ?}
           RELATE PlantCountry TO PARAMETER 0,
             PlantRegion TO CustomerRegion )

The caching behavior for a hybrid command is the same as that of regular parameterized commands.

See Also

Data Shaping | Formal Shape Grammar | Shape Commands in General


Intervening Shape COMPUTE Clauses

It is valid to embed one or more COMPUTE clauses between the parent and child in a parameterized shape command, as in the following example:

SHAPE {select au_lname, state from authors} APPEND
   ((SHAPE
      (SHAPE
         {select * from authors where state = ?} rs
      COMPUTE rs, ANY(rs.state) state, ANY(rs.au_lname) au_lname
      BY au_id) rs2
   COMPUTE rs2, ANY(rs2.state) BY au_lname)
RELATE state TO PARAMETER 0)

See Also

Data Shaping | Formal Shape Grammar | Shape Commands in General


Shape COMPUTE Clause

A shape COMPUTE clause generates a parent Recordset, whose columns consist of a reference to the child Recordset; optional columns whose contents are chapter, new, or calculated columns, or the result of executing aggregate functions on the child Recordset or a previously shaped Recordset; and any columns from the child Recordset listed in the optional BY clause.

Syntax

SHAPE child-command [AS] child-alias
   COMPUTE child-alias [[AS] name], [appended-column-list]
   [BY grp-field-list]

Description

The parts of this clause are as follows:

child-command
Consists of one of the following:
  • A query command within curly braces ("{}") that returns a child Recordset object. The command is issued to the underlying data provider, and its syntax depends on the requirements of that provider. This will typically be the SQL language, although ADO does not require any particular query language.
  • The name of an existing shaped Recordset.
  • Another shape command.
  • The TABLE keyword, followed by the name of a table in the data provider.
child-alias
An alias used to refer to the Recordset returned by the child-command. The child-alias is required in the list of columns in the COMPUTE clause and defines the relation between the parent and child Recordset objects.
appended-column-list
A list in which each element defines a column in the generated parent. Each element contains either a chapter column, a new column, a calculated column, or a value resulting from an aggregate function on the child Recordset.
grp-field-list
A list of columns in the parent and child Recordset objects that specifies how rows should be grouped in the child.

For each column in the grp-field-list, there is a corresponding column in the child and parent Recordset objects. For each row in the parent Recordset, the grp-field-list columns have unique values, and the child Recordset referenced by the parent row consists solely of child rows whose grp-field-list columns have the same values as the parent row.

If the BY clause is included, the child Recordset's rows will be grouped based on the columns in the COMPUTE clause. The parent Recordset will contain one row for each group of rows in the child Recordset.

If the BY clause is omitted, the entire child Recordset is treated as a single group and the parent Recordset will contain exactly one row. That row will reference the entire child Recordset. Omitting the BY clause allows you to compute "grand total" aggregates over the entire child Recordset.

For example:

SHAPE {select * from Orders} AS orders
   COMPUTE orders, SUM(orders.OrderAmount) as TotalSales

Regardless of which way the parent Recordset is formed (using COMPUTE or using APPEND), it will contain a chapter column that is used to relate it to a child Recordset. If you wish, the parent Recordset may also contain columns that contain aggregates (SUM, MIN, MAX, and so on) over the child rows. Both the parent and the child Recordset may contain columns that contain an expression on the row in the Recordset, as well as columns that are new and initially empty.

Operation

The child-command is issued to the provider, which returns a child Recordset.

The COMPUTE clause specifies the columns of the parent Recordset, which may be a reference to the child Recordset, one or more aggregates, a calculated expression, or new columns. If there is a BY clause, the columns it defines are also appended to the parent Recordset. The BY clause specifies how the rows of the child Recordset are grouped.

For example, assume you have a table—Demographics—consisting of State, City, and Population fields (the population figures are solely for illustration).

State | City | Population
WA | Seattle | 700,000
OR | Medford | 200,000
OR | Portland | 400,000
CA | Los Angeles | 800,000
CA | San Diego | 600,000
WA | Tacoma | 500,000
OR | Corvallis | 300,000

Now, issue this shape command:

rst.Open  "SHAPE {select * from demographics} AS rs "  & _
          "COMPUTE rs, SUM(rs.population) BY state", _
           objConnection

This command opens a shaped Recordset with two levels. The parent level is a generated Recordset with an aggregate column (SUM(rs.population)), a column referencing the child Recordset (rs), and a column for grouping the child Recordset (state). The child level is the Recordset returned by the query command (select * from demographics).

The child Recordset detail rows will be grouped by state, but otherwise in no particular order. That is, the groups will not be in alphabetical or numerical order. If you want the parent Recordset to be ordered, you can use the Recordset Sort method to order the parent Recordset.

You can now navigate the opened parent Recordset and access the child detail Recordset objects. For more information, see Accessing Rows in a Hierarchical Recordset.

Resultant Parent and Child Detail Recordsets

Parent

SUM (rs.Population) | rs | State
1,300,000 | Reference to child1 | CA
1,200,000 | Reference to child2 | WA
1,100,000 | Reference to child3 | OR

Child1

State | City | Population
CA | Los Angeles | 800,000
CA | San Diego | 600,000

Child2

State | City | Population
WA | Seattle | 700,000
WA | Tacoma | 500,000

Child3

State | City | Population
OR | Medford | 200,000
OR | Portland | 400,000
OR | Corvallis | 300,000

See Also

Accessing Rows in a Hierarchical Recordset | Data Shaping Summary | Field Object | Formal Shape Grammar | Recordset Object | Required Providers for Data Shaping | Shape APPEND Clause | Shape Commands in General | Value Property | Visual Basic for Applications Functions


Fabricating Hierarchical Recordsets

The following example shows how to fabricate a hierarchical Recordset without an underlying data source by using the data shaping grammar to define columns for parent, child, and grandchild Recordsets.

To fabricate a hierarchical Recordset, you must specify the Microsoft Data Shaping Service for OLE DB (MSDataShape), and you may specify a Data Provider value of NONE in the connection string parameter of the Connection object's Open method. For more information, see Required Providers for Data Shaping.

Dim cn As New ADODB.Connection
Dim rsCustomers As New ADODB.Recordset

cn.Open "Provider=MSDataShape;Data Provider=NONE;"

strShape = _
"SHAPE APPEND NEW adInteger AS CustID," & _
            " NEW adChar(25) AS FirstName," & _
            " NEW adChar(25) AS LastName," & _
            " NEW adChar(12) AS SSN," & _
            " NEW adChar(50) AS Address," & _
         " ((SHAPE APPEND NEW adChar(80) AS VIN_NO," & _
                        " NEW adInteger AS CustID," & _
                        " NEW adChar(20) AS BodyColor, " & _
                     " ((SHAPE APPEND NEW adChar(80) AS VIN_NO," & _
                                    " NEW adChar(20) AS Make, " & _
                                    " NEW adChar(20) AS Model," & _
                                    " NEW adChar(4) AS Year) " & _
                        " AS VINS RELATE VIN_NO TO VIN_NO))" & _
            " AS Vehicles RELATE CustID TO CustID) "

rsCustomers.Open strShape, cn, adOpenStatic, adLockOptimistic, -1

Once the Recordset has been fabricated, it can be populated, manipulated, or persisted to a file.

See Also

Accessing Rows in a Hierarchical Recordset | Formal Shape Grammar | Required Providers for Data Shaping | Shape APPEND Clause | Shape Commands in General


Accessing Rows in a Hierarchical Recordset

The following example shows the steps necessary to access rows in a hierarchical Recordset:

  1. Recordset objects from the authors and titleauthor tables are related by author ID.
  2. The outer loop displays each author's first and last name, state, and identification.
  3. The appended Recordset for each row is retrieved from the Fields collection and assigned to rstTitleAuthor.
  4. The inner loop displays four fields from each row in the appended Recordset.

(The StayInSync property is set to FALSE for purposes of illustration—so you can see the chapter change explicitly in each iteration of the outer loop. However, the example will be more efficient if the assignment in step 3 is moved before the first line in step 2, so that the assignment is performed only once. Then set the StayInSync property to TRUE, so that rstTitleAuthor will implicitly and automatically change to the corresponding chapter whenever rst moves to a new row.)

Example

Sub datashape()
   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset
   Dim rstTitleAuthor As New ADODB.Recordset

   cnn.Provider = "MSDataShape"
   cnn.Open    "Data Provider=MSDASQL;" & _
               "Data Source=SRV;User Id=sa;Password=;Database=Pubs"
' STEP 1
   rst.StayInSync = FALSE
   rst.Open    "SHAPE  {select * from authors} "  & _
               "APPEND ({select * from titleauthor} " & _
               "RELATE au_id TO au_id) AS chapTitleAuthor", _
               cnn
' STEP 2
   While Not rst.EOF
      Debug.Print    rst("au_fname"), rst("au_lname"), _
                     rst("state"), rst("au_id")
' STEP 3
      Set rstTitleAuthor = rst("chapTitleAuthor").Value
' STEP 4
      While Not rstTitleAuthor.EOF
         Debug.Print rstTitleAuthor(0), rstTitleAuthor(1), _
                     rstTitleAuthor(2), rstTitleAuthor(3)
         rstTitleAuthor.MoveNext
      Wend
      rst.MoveNext
   Wend
End Sub

See Also

Data Shaping Summary | Field Object | Fields Collection | Formal Shape Grammar | Microsoft Data Shaping Service for OLE DB | Recordset Object | Required Providers for Data Shaping | Shape APPEND Clause | Shape Commands in General | Shape COMPUTE Clause | Visual Basic for Applications Functions


Formal Shape Grammar

This is the formal grammar for creating any shape command:

  • Required grammatical terms are text strings delimited by angle brackets ("<>").
  • Optional terms are delimited by square brackets ("[ ]").
  • Alternatives are indicated by a virgule ("|").
  • Repeating alternatives are indicated by an ellipsis ("...").
  • Alpha indicates a string of alphabetical letters.
  • Digit indicates a string of numbers.
  • Unicode-digit indicates a string of unicode digits.

All other terms are literals.

Term Definition
<shape-command>

SHAPE [<table-exp> [[AS] <alias>]][<shape-action>]

<table-exp>

{<provider-command-text>} |

(<shape-command>) |
TABLE <quoted-name> |
<quoted-name>

<shape-action>

APPEND <aliased-field-list> |

COMPUTE <aliased-field-list> [BY <field-list>]

<aliased-field-list>

<aliased-field> [, <aliased-field...>]

<aliased-field>

<field-exp> [[AS] <alias>]

<field-exp>

(<relation-exp>) |

<calculated-exp> |
<aggregate-exp> |
<new-exp>

<relation_exp>

<table-exp> [[AS] <alias>]

   RELATE <relation-cond-list>

<relation-cond-list>

<relation-cond> [, <relation-cond>...]

<relation-cond>

<field-name> TO <child-ref>

<child-ref>

<field-name> |

PARAMETER <param-ref>

<param-ref>

<number>

<field-list>

<field-name> [, <field-name>]

<aggregate-exp>

SUM(<qualified-field-name>) |

AVG(<qualified-field-name>) |
MIN(<qualified-field-name>) |
MAX(<qualified-field-name>) |
COUNT(<qualified-alias> | <qualified-name>) |
STDEV(<qualified-field-name>) |
ANY(<qualified-field-name>)

<calculated-exp>

CALC(<expression>)

<qualified-field-name>

<alias>.[<alias>...]<field-name>

<alias>

<quoted-name>

<field-name>

<quoted-name> [[AS] <alias>]

<quoted-name>

"<string>" |

'<string>' |
[<string>] |
<name>

<qualified-name>

alias[.alias...]

<name>

alpha [ alpha | digit | _ | # | : | ...]

<number>

digit [digit...]

<new-exp>

NEW <field-type> [(<number> [, <number>])]

<field-type>
 An OLE DB or ADO data type.
<string>

unicode-char [unicode-char...]

<expression>
 A Visual Basic for Applications expression whose operands are other non-CALC columns in the same row.

See Also

Accessing Rows in a Hierarchical Recordset | Data Shaping Summary | Required Providers for Data Shaping | Shape APPEND Clause | Shape Commands in General | Shape COMPUTE Clause | Visual Basic for Applications Functions


Visual Basic for Applications Functions

The following Visual Basic for Applications functions can be used in data shaping CALC expressions:

Abs | Asc | Atn | CBool | CByte | CCur
CDate | CDbl | Chr | ChrB | ChrW | Chr$
ChrB$ | CInt | CLng | Cos | CSng | CStr
Cvar | CVDate | CVErr | Date | Date$ | DateAdd
DateDiff | DatePart | DateSerial | DateValue | Day | DDB
Error | Error$ | Exp | Fix | Format | Format$
FV | Hex | Hex$ | Hour | IIF | InStr
Int | IPmt | IRR | IsDate | IsEmpty | IsError
IsNull | IsNumeric | IsObject | LCase | LCase$ | Left
LeftB | Left$ | LeftB$ | Len | Log | LTrim
LTrim$ | Mid | Mid$ | Minute | MIRR | Month
Now | NPer | NPV | Oct | Oct$ | Pmt
PPmt | PV | QBColor | Rate | RGB | Right
RightB | Right$ | RightB$ | Rnd | RTrim | RTrim$
Second | Sgn | Sin | SLN | Space | Space$
Sqr | Str | Str$ | StrComp | StrConv | String
String$ | SYD | Tan | Time | Time$ | Timer
TimeSerial | TimeValue | Trim | Trim$ | TypeName | UCase
UCase$ | Val | VarType | Weekday | Year |

See Also

Accessing Rows in a Hierarchical Recordset | Data Shaping Summary | Formal Shape Grammar | Required Providers for Data Shaping | Shape APPEND Clause | Shape Commands in General | Shape COMPUTE Clause


Chapter 10: Records and Streams

ADO currently provides the Recordset object as the primary means of accessing information in data sources, such as relational databases. However, some providers support the Record and Stream objects as alternative or complementary objects with which data from providers can be manipulated. For specifics on Record behavior, see your provider's documentation.

Records

Record objects essentially function as one-row Recordsets. However, Records have limited functionality compared to Recordsets and they have different properties and methods.The source for the data in a Record object can be a command which returns one row of data from the provider. Using Record objects rather than Recordset objects to receive the results from a query that returns one row of data eliminates the overhead of instantiating the more complex Recordset object.

Record objects can serve another purpose, particularly with providers for data sources other than traditional relational databases, such as the Microsoft OLE DB Provider for Internet Publishing. Much of the information that must be processed exists, not as tables in databases, but as messages in electronic mail systems and files in modern file systems. The Record and Stream objects facilitate access to information stored in sources other than relational databases.

The Record object can represent and manage data such as directories and files in a file system or folders and messages in an e-mail system. For these purposes, the source for the Record can be the current row of an open Recordset, an absolute URL, or a relative URL in conjunction with an open Connection object.

Typically, a Recordset can be used to represent a container or parent in a hierarchy such as a folder or directory. A Record can be used to return specific information about one node in the parent container, such as a file or document. The primary reason Records are used to represent this type of information is that these sources of data are heterogenous. This means that each Record may have a different set and number of fields. Traditional Recordsets containing rows from a database are homogenous, which means that each row has the same number and type of fields.

For more information about using the Record object for processing this heterogeneous data from providers such as the Internet Publishing Provider, see Using ADO for Internet Publishing.

Streams

The Stream object provides the means to read, write, and manage a stream of bytes. This byte stream may be text or binary and is limited in size only by system resources. Typically, ADO Stream objects are used for the following purposes:

  • To contain the data of a Recordset saved in XML format. These XML streams from saved Recordsets can be used as the source when opening a new Recordset. For more information, see Streams and Persistence.
  • To contain CommandStreams to be executed against the provider as an alternative to CommandText. For example, XML UpdateGrams can be used as the source for a command against the Microsoft OLE DB Provider for SQL Server 2000. For more information, see the Using Streams for Command Input topic in the SQL Server Books Online (Search for “Using Streams for Command Input”).To view the SQL Server Books Online, click this icon
  • To receive results from the provider in a format other than a Recordset, such as XML results from the Microsoft OLE DB Provider for SQL Server 2000. For more information, see Retrieving Resultsets into Streams.
  • To contain the text or bytes that comprise a file or message, typically used with providers such as the Microsoft OLE DB Provider for Internet Publishing. For more information about this use of Stream objects, see Using ADO for Internet Publishing.

A Stream object can be opened on:

  • A simple file specified with a URL.
  • A field of a Record or Recordset containing a Stream object.
  • The default stream of a Record or Recordset object representing a directory or compound file.
  • A resource field containing the URL of a simple file.
  • No particular source at all. In this case, a Stream object is opened in memory. Data can be written to it and then saved in another Stream or file.
  • A BLOB field in a Recordset.


Streams and Persistence

The Recordset object Save method stores, or persists, a Recordset in a file, and the Open method restores the Recordset from that file.

With ADO 2.6, the Save and Open methods can persist a Recordset to a Stream object as well. This feature is especially useful when working with Remote Data Service (RDS) and Active Server Pages (ASP).

For more information about how persistence can be used by itself on ASP pages, see the current ASP documentation.

The following are a few scenarios that show how Stream objects and persistence can be used.

Scenario 1

This scenario simply saves a Recordset to a file and then to a Stream. It then opens the persisted stream into another Recordset.

Dim rs1 As ADODB.Recordset
Dim rs2 As ADODB.Recordset
Dim stm As ADODB.Stream

Set rs1 = New ADODB.Recordset
Set rs2 = New ADODB.Recordset
Set stm = New ADODB.Stream

rs1.Open   "SELECT * FROM Customers", "Provider=sqloledb;" & _
        "Data Source=MyServer;Initial Catalog=Northwind;" & _
        "User Id=sa;Password=;", adopenStatic, adLockReadOnly, adCmdText
rs1.Save "c:\myfolder\mysubfolder\myrs.xml", adPersistXML
rs1.Save stm, adPersistXML
rs2.Open stm

Scenario 2

This scenario persists a Recordset into a Stream in XML format. It then reads the Stream into a string that you can examine, manipulate, or display.

Dim rs As ADODB.Recordset
Dim stm As ADODB.Stream
Dim strRst As String

Set rs = New ADODB.Recordset
Set stm = New ADODB.Stream

' Open, save, and close the recordset.
rs.Open "SELECT * FROM Customers", "Provider=sqloledb;" & _
        "Data Source=MyServer;Initial Catalog=Northwind;" & _
        "User Id=sa;Password=;"
rs.Save stm, adPersistXML
rs.Close
Set rs = nothing

' Put saved Recordset into a string variable.
strRst = stm.ReadText(adReadAll)

' Examine, manipulate, or display the XML data.
...

Scenario 3

This example code shows ASP code persisting a Recordset as XML directly to the Response object:

...
<%
response.ContentType = "text/xml"

' Create and open a Recordset.
Set rs = Server.CreateObject("ADODB.Recordset")
rs.Open "select * from Customers", "Provider=sqloledb;" & _
        "Data Source=MyServer;Initial Catalog=Northwind;" & _
        "User Id=sa;Password=;"

' Save Recordset directly into output stream.
rs.Save Response, adPersistXML

' Close Recordset.
rs.Close
Set rs = nothing
%>
...

Scenario 4

In this scenario, ASP code writes the contents of the Recordset in ADTG format to the client. The Cursor Service can use this data to create a disconnected Recordset.

A new property on the RDS DataControl, URL, points to the .asp page that generates the Recordset. This means a Recordset object can be obtained without RDS using the server-side DataFactory object or the user writing a business object. This simplifies the RDS programming model significantly.

Server-side code, named http://server/directory/recordset.asp:

<%
Dim rs
Set rs = Server.CreateObject("ADODB.Recordset")
rs.Open "select au_fname, au_lname, phone from Authors", & _
        "Provider=sqloledb;Data Source=MyServer;" & _
        "Initial Catalog=Pubs;User Id=sa;Password=;"
response.ContentType = "multipart/mixed"
rs.Save response, adPersistADTG
%>

Client-side code:

<HTML>
<HEAD>
<TITLE>RDS Query Page</TITLE>
</HEAD>
<body>
<CENTER>
<H1>Remote Data Service 2.5</H1>
<TABLE DATASRC="#DC1">
   <TR>
      <TD><SPAN DATAFLD="au_fname"></SPAN></TD>
      <TD><SPAN DATAFLD="au_lname"></SPAN></TD>
      <TD><SPAN DATAFLD="phone"></SPAN></TD>
   </TR>
</TABLE>
<BR>

<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
    ID=DC1 HEIGHT=1 WIDTH = 1>
    <PARAM NAME="URL" VALUE="http://server/directory/recordset.asp">
</OBJECT>

</SCRIPT>
</BODY>
</HTML>

Developers also have the option of using a Recordset object on the client:

...
function GetRs()
    {
    rs = CreateObject("ADODB.Recordset");
    rs.Open "http://server/directory/recordset.asp"
    DC1.SourceRecordset = rs;
    }
...

See Also

Open Method (ADO Recordset) | Record Object | Save Method


CommandStreams

ADO

Command Streams

ADO has always supported command input in string format specified by the CommandText property. As an alternative, with ADO 2.6, you can also use a stream of information for command input by assigning the stream to the CommandStream property. You can assign an ADO Stream object, or any object that supports the COM IStream interface.

The content of the command stream is simply passed from ADO to your provider, so your provider must support command input by stream for this feature to work. For example, SQL Server 2000 supports queries in the form of XML templates or OpenXML extensions to Transact-SQL. For more information about stream command support in SQL Server, see ADO Support for SQL Server XML Features in the SQL Server Books Online.

Because the details of the stream must be interpreted by the provider, you must specify the command dialect by setting the Dialect property. The value of Dialect is a string containing a GUID, which is defined by your provider. For information about valid values for Dialect supported by your provider, see your provider documentation. For information about dialects supported by SQL Server 2000, see Using Streams for Command Input in the SQL Server Books Online.

XML Template Query Example

The following example is written in VBScript to the SQL Server 2000 Northwind database.

First, initialize and open the Stream object that will be used to contain the query stream:

Dim adoStreamQuery
Set adoStreamQuery = Server.CreateObject("ADODB.Stream")
adoStreamQuery.Open

The content of the query stream will be an XML template query. For more information about XML template queries, see the XML SDK.

The template query requires a reference to the XML namespace identified by the sql: prefix of the <sql:query> tag. A SQL SELECT statement is included as the content of the XML template and assigned to a string variable as follows:

sQuery = "<ROOT xmlns:sql='urn:schemas-microsoft-com:xml-sql'>
<sql:query> SELECT * FROM PRODUCTS ORDER BY PRODUCTNAME </sql:query>
</ROOT>"

Next, write the string to the stream:

adoStreamQuery.WriteText sQuery, adWriteChar
adoStreamQuery.Position = 0

Assign adoStreamQuery to the CommandStream property of an ADO Command object:

Dim adoCmd
Set adoCmd  = Server.CreateObject("ADODB.Command")
adoCmd.CommandStream = adoStreamQuery

Specify the command language Dialect, which indicates how the SQL Server OLE DB Provider should interpret the command stream. The dialect specified by a provider-specific GUID:

adoCmd.Dialect = "{5D531CB2-E6Ed-11D2-B252-00C04F681B71}"

Finally, execute the query and return the results to a Recordset object:

Set objRS = adoCmd.Execute


Retrieving Resultsets into Streams

Instead of receiving results in the traditional Recordset object, ADO can instead retrieve query results into a stream. The ADO Stream object (or other objects that support the COM IStream interface, such as the ASP Request and Response objects) can be used to contain these results. One use for this feature is to retrieve results in XML format. With SQL Server 2000, for example, XML results can be returned in multiple ways, such as using the FOR XML clause with a SQL SELECT query or using an XPath query.

To receive query results in stream format rather than in a Recordset, you must specify the adExecuteStream constant from ExecuteOptionEnum as a parameter of the Execute method of a Command object. If your provider supports this feature, the results will be returned in a stream upon execution. You might be required to specify additional provider-specific properties before the code executes. For example, with the Microsoft OLE DB Provider for SQL Server, properties such as Output Stream in the Properties collection of the Command object must be specified. For more information about SQL Server-specific dynamic properties related to this feature, see XML-Related Properties in the SQL Server Books Online.

FOR XML Query Example

The following example is written in VBScript to the SQL Server 2000 Northwind database:

<!-- BeginRecordAndStreamVBS -->
<%@ LANGUAGE = VBScript %>
<%  Option Explicit      %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Developer Studio"/>
<META HTTP-EQUIV="Content-Type" content="text/html"; charset="iso-8859-1">
<TITLE>FOR XML Query Example</TITLE>

<STYLE>
   BODY
   {
      FONT-FAMILY: Tahoma;
      FONT-SIZE: 8pt;
      OVERFLOW: auto
   }

   H3
   {
      FONT-FAMILY: Tahoma;
      FONT-SIZE: 8pt;
      OVERFLOW: auto
   }
</STYLE>

<!-- #include file="adovbs.inc" -->
<%
   Response.Write "<H3>Server-side processing</H3>"

   Response.Write "Page Generated @ " & Now() & "<BR/>"

   Dim adoConn
   Set adoConn = Server.CreateObject("ADODB.Connection")

   Dim sConn
   sConn = "Provider=SQLOLEDB;Data Source=BWILLETT1;Initial Catalog=Northwind;User ID=SA;Password=;"

   Response.write "Connect String = " & sConn & "<BR/>"

   adoConn.ConnectionString = sConn
   adoConn.CursorLocation = adUseClient

   adoConn.Open

   Response.write "ADO Version = " & adoConn.Version & "<BR/>"
   Response.write "adoConn.State = " & adoConn.State & "<BR/>"

   Dim adoCmd
   Set adoCmd = Server.CreateObject("ADODB.Command")
   Set adoCmd.ActiveConnection = adoConn

   Dim sQuery
   sQuery = "<ROOT xmlns:sql='urn:schemas-microsoft-com:xml-sql'><sql:query>SELECT * FROM PRODUCTS WHERE ProductName='Gumbär Gummibärchen' FOR XML AUTO</sql:query></ROOT>"

   Response.write "Query String = " & sQuery & "<BR/>"

   Dim adoStreamQuery
   Set adoStreamQuery = Server.CreateObject("ADODB.Stream")
   adoStreamQuery.Open
   adoStreamQuery.WriteText sQuery, adWriteChar
   adoStreamQuery.Position = 0

   adoCmd.CommandStream = adoStreamQuery
   adoCmd.Dialect = "{5D531CB2-E6Ed-11D2-B252-00C04F681B71}"

   Response.write "Pushing XML to client for processing "  & "<BR/>"

   adoCmd.Properties("Output Stream") = Response
   Response.write "<XML ID='MyDataIsle'>"
   adoCmd.Execute , , 1024
   Response.write "</XML>"

%>

<SCRIPT language="VBScript" For="window" Event="onload">
   Dim xmlDoc
   Set xmlDoc = MyDataIsle.XMLDocument
   xmlDoc.resolveExternals=false
   xmlDoc.async=false

   If xmlDoc.parseError.Reason <> "" then
      Msgbox "parseError.Reason = " & xmlDoc.parseError.Reason
   End If

   Dim root, child
   Set root = xmlDoc.documentElement
   For each child in root.childNodes
      dim OutputXML
      OutputXML = document.all("log").innerHTML
      document.all("log").innerHTML = OutputXML & "<LI>" & child.getAttribute("ProductName") & "</LI>"
   Next
</SCRIPT>

</HEAD>

<BODY>

   <H3>Client-side processing of XML Document MyDataIsle</H3>
   <UL id=log>
   </UL>

</BODY>
</HTML>
<!-- EndRecordAndStreamVBS -->

A new FOR XML clause has been added to the SELECT statement in SQL Server 2000. The FOR XML clause instructs SQL Server to return data in the form of an XML document. For more information about SQL Server FOR XML queries, see Retrieving XML Data Using FOR XML in the SQL Server Books Online.

FOR XML Syntax

FOR XML [RAW|AUTO|EXPLICIT]

FOR XML RAW generates generic row elements with column values as attributes. FOR XML AUTO uses heuristics to generate an hierarchical tree with element names based on table names. FOR XML EXPLICIT generates a universal table with relationships fully described by metadata.

An example SQL SELECT FOR XML statement follows:

SELECT * FROM PRODUCTS ORDER BY PRODUCTNAME FOR XML AUTO

The command may be specified in a string as shown above, assigned to CommandText, or in the form of an XML template query assigned to CommandStream. For more information about XML template queries, see Command Streams in ADO or Using Streams for Command Input in the SQL Server Books Online.

As an XML template query, the FOR XML query appears as follows:

<sql:query> SELECT * FROM PRODUCTS ORDER BY PRODUCTNAME FOR XML AUTO </sql:query>

This example specifies the ASP Response object for the Output Stream property:

adoCmd.Properties("Output Stream") = Response

Next, specify adExecuteStream parameter of Execute. This example wraps the stream in XML tags to create an XML data island. For more information about XML data islands, see the XML SDK:

Response.write "<XML ID=MyDataIsle>"
adoCmd.Execute , , adExecuteStream
Response.write "</XML>"

At this point, XML has been streamed to the client browser and it is ready to be displayed. This is done using client side VBScript to bind the XML document to an instance of the DOM and looping through each child node to build a list of Products in HTML.


Using ADO for Internet Publishing

The OLE DB Provider for Internet Publishing shows a specific example of accessing heterogeneous data with ADO. While the examples in this section will be specific to using the Internet Publishing Provider, the principles demonstrated should be similar when using ADO with other providers to heterogeneous data, such as a provider to an e-mail store.

URLs

Uniform Resource Locators (URLs) can be used as an alternative to connection strings and command text to specify data sources and the location of files and directories. You can use URLs with the existing Connection and Recordset objects as well as with the Record and Stream objects.

For more information about using URLs, see Absolute and Relative URLs.

Record Fields

The distinguishing difference between heterogeneous data and homogeneous data is that for the former, each row of data, or Record, can have a different set of columns, or Fields. For homogeneous data, each row has the same set of columns. For more information about the fields specific to the Internet Publishing Provider, see Records and Provider-Supplied Extra Fields.

Appending New Fields

Several ADO objects have been enhanced to work in conjunction with Record and Stream objects.

  • The Fields collection Append method, which creates and adds a Field object to the collection, can also specify the value of the Field.
  • The Update method finalizes the addition or deletion of fields to the collection.
  • As a shortcut and alternative to the Append method, you may create fields by simply assigning a value to an undefined or previously deleted field.

See Also

Record Object | Stream Object | What's New in ADO


The OLE DB Provider for Internet Publishing

The ADO Record and Stream objects can be used with the Microsoft OLE DB Provider for Internet Publishing (Internet Publishing Provider) to access and manipulate resources, such as Web folders or files served by Microsoft® FrontPage®. With ADO, you can specify the source of a Record, Stream, or Recordset to be a URL. You can then upload, download, move, copy, and delete resources, or directly manipulate resource properties.

For example code using Records and Streams with the Internet Publishing Provider, see the Internet Publishing Scenario.

The Internet Publishing Provider is installed with Microsoft Windows 2000. Earlier versions of the Internet Publishing Provider are also available with Microsoft Office 2000 and Microsoft Internet Explorer 5.0.

There are three ways to connect ADO to the Internet Publishing Provider:

  • Specify "URL=" in the connection string. For example:
    objConn.Open "URL=http://servername"
  • Specify Msdaipp.dso for the Provider keyword of the connection string. For example:
    objConn.Open "provider=MSDAIPP.DSO;data source=http://servername"
  • Specify Msdaipp.dso for the Provider property of the Connection object. For example:
    objConn.Provider = "MSDAIPP.DSO"
objConn.Open "http://servername"

Note If Msdaipp.dso is explicitly specified as the value of the provider, either with the Provider connection string keyword or the Provider property, you cannot use "URL=" in the connection string. If you do, an error will occur. Instead, simply specify the URL as shown above.

For more specific information about the Internet Publishing Provider, see Microsoft OLE DB Provider for Internet Publishing, or the provider documentation provided with the source application with which the OLE DB Provider for Internet Publishing was installed: Windows 2000, Office 2000, or Internet Explorer 5.0.


Internet Publishing Scenario

This code example demonstrates how to use ADO with the Microsoft OLE DB Provider for Internet Publishing. In this scenario, you will create a Visual Basic application that uses Recordset, Record, and Stream objects to display the contents of resources published with the Internet Publishing Provider.

The following steps are necessary to create this scenario:

  • Step 1: Set Up the Visual Basic Project
  • Step 2: Initialize the Main List Box
  • Step 3: Populate the Fields List Box
  • Step 4: Populate the Details Text Box

See Also

Microsoft OLE DB Provider for Internet Publishing | Using ADO with the OLE DB Provider for Internet Publishing


Step 1: Set Up the Visual Basic Project

In this scenario, it is assumed that you have Microsoft Visual Basic 6.0 or later, ADO 2.5 or later, and the Microsoft OLE DB Provider for Internet Publishing installed on your system.

To create an ADO project

  1. In Microsoft Visual Basic, create a new Standard EXE project.
  2. From the Project menu, choose References.
  3. Select Microsoft ActiveX Data Objects 2.5 Library and click OK.

To insert controls on the main form

  1. Add a ListBox control to Form1. Set its Name property to lstMain.
  2. Add another ListBox control to Form1. Set its Name property to lstDetails.
  3. Add a TextBox control to Form1. Set its Name property to txtDetails.

See Also

Step 2: Initialize the Main List Box


Step 2: Initialize the Main List Box

To declare global Record and Recordset objects

  • Insert the following code into the (General) (Declarations) for Form1:
    Option Explicit
Dim grec As Record
Dim grs As Recordset

    This code declares global object references for Record and Recordset objects that will be used later in this scenario.

To connect to a URL and populate lstMain

  • Insert the following code into the Form Load event handler for Form1:
    Private Sub Form_Load()
    Set grec = New Record
    Set grs = New Recordset
    grec.Open "", "URL=http://servername/foldername/", , _
        adOpenIfExists Or adCreateCollection
    Set grs = grec.GetChildren
    While Not grs.EOF
        lstMain.AddItem grs(0)
        grs.MoveNext
    Wend
End Sub

    This code instantiates the global Record and Recordset objects. The Record, grec, is opened with a URL specified as the ActiveConnection. If the URL exists, it is opened; if it does not already exist, it is created. Note that you should replace "http://servername/foldername/" with a valid URL from your environment.

    The Recordset, grs, is opened on the children of the Record, grec. Then lstMain is populated with the file names of the resources published to the URL.

See Also

Step 3: Populate the Fields List Box


Step 3: Populate the Fields List Box

To populate the Fields list box

Insert the following code into the Click event handler of lstMain:

Private Sub lstMain_Click()
    Dim rec As Record
    Dim rs As Recordset
    Set rec = New Record
    Set rs = New Recordset
    grs.MoveFirst
    grs.Move lstMain.ListIndex
    lstDetails.Clear
    rec.Open grs
    Select Case rec.RecordType
        Case adCollectionRecord:
            Set rs = rec.GetChildren
            While Not rs.EOF
                lstDetails.AddItem rs(0)
                rs.MoveNext
            Wend
        Case adSimpleRecord:
            recFields rec, lstDetails, txtDetails

        Case adStructDoc:
    End Select

End Sub

This code declares and instantiates local Record and Recordset objects, rec and rs, respectively.

The row corresponding to the resource selected in lstMain is made the current row of grs. Then the Details list box is cleared and rec is opened with the current row of grs as the source.

If the resource is a collection record (as specified by RecordType), the local Recordset, rs, is opened on the children of rec. Then lstDetails is filled with the values from the rows of rs.

If the resource is a simple record, recFields is called. For more information about recFields, see the next step.

No code is implemented if the resource is a structured document.

See Also

Step 4: Populate the Details Text Box


Step 4: Populate the Details Text Box

To populate the Details text box

Create a new subroutine named recFields and insert the following code:

Sub recFields(r As Record, l As ListBox, t As TextBox)
    Dim f As Field
    Dim s As Stream
    Set s = New Stream
    Dim str As String

    For Each f In r.Fields
        l.AddItem f.Name & ": " & f.Value
    Next
    t.Text = ""
    If r!RESOURCE_CONTENTCLASS = "text/plain" Then
        s.Open r, adModeRead, adOpenStreamFromRecord
        str = s.ReadText(1)
        s.Position = 0
        If Asc(Mid(str, 1, 1)) = 63 Then '//63 = "?"
            s.Charset = "ascii"
            s.Type = adTypeText
        End If
        t.Text = s.ReadText(adReadAll)
    End If
End Sub

This code populates lstDetails with the fields and values of the simple record passed to recFields. If the resource is a text file, a text Stream is opened from the resource record. The code determines if the character set is ASCII and copies the Stream contents into txtDetails.


Absolute and Relative URLs

A URL specifies the location of a target stored on a local or networked computer, such as a file, directory, HTML page, image, program, and so on. In this discussion, an absolute URL is of the form:

scheme://server/path/resource

where:

scheme
Specifies how the resource is to be accessed.
server
Specifies the name of the computer where the resource is located.
path
Specifies the sequence of directories leading to the target. If resource is omitted, the target is the last directory in path.
resource
If included, resource is the target, and is typically the name of a file. It may be a simple file, containing a single binary stream of bytes, or a structured document, containing one or more storages and binary streams of bytes.

An absolute URL contains all the information necessary to locate a resource.

A relative URL locates a resource using an absolute URL as a starting point. In effect, the "complete URL" of the target is specified by concatenating the absolute and relative URLs. A relative URL typically consists only of the path, and optionally, the resource, but no scheme or server.

URL Scheme Registration

If a provider supports URLs, it will register for one or more URL schemes. This means that any URLs using this scheme will automatically invoke the registered provider. For example, the http scheme is registered to the Microsoft OLE DB Provider for Internet Publishing. ADO assumes all URLs prefixed with "http" represent Web folders or files to be used with the Internet Publishing Provider. For information about the schemes registered by your provider, see your provider documentation.

Defining Context with a URL

One function of an open connection, represented by a Connection object, is to restrict subsequent operations to the data source represented by that connection. That is, the connection defines the context for subsequent operations.

With ADO 2.6, an absolute URL may also define a context. For example, when a Record object is opened with an absolute URL, a Connection object is implicitly created to represent the resource specified by the URL.

An absolute URL that defines a context may be specified in the ActiveConnection parameter of the Record object Open method. An absolute URL may also be specified as the value of the new "URL=" keyword in the Connection object Open method ConnectionString parameter, and the Recordset object Open method ActiveConnection parameter.

Context may also be defined with an open Record or Recordset object that represents a directory because these objects already have an implicitly or explicitly declared Connection object that specifies context.

Scoped Operations

The context simultaneously defines a scope—that is, the directory and its subdirectories that may participate in subsequent operations. The Record object has several scoped methods, including CopyRecord, MoveRecord, and DeleteRecord, that operate on a directory and all its subdirectories.

Relative URLs as Command Text

A string specifying a command to be executed on the data source may be specified in the Connection object Execute method CommandText parameter, and the Recordset object Open method Source parameter.

A relative URL may be specified in the CommandText or Source parameter. The relative URL does not actually specify a command (such as an SQL command); it is merely specified in those parameters. In addition, the context of the active connection must be an absolute URL, and the Option parameter must be set to adCmdTableDirect.

For example, a Recordset could be opened on the Readme25.txt file of the Winnt/system32 directory like this:

recordset.Open "system32/Readme25.txt", "URL=http://YourServer/Winnt/",,,adCmdTableDirect

The absolute URL in the connection string specifies the server (YourServer) and the path (Winnt). This URL also defines the context.

The relative URL in the command text uses the absolute URL as a starting point and specifies the remainder of the path (system32) and the file to open (Readme25.txt).

The options field (adCmdTableDirect) indicates that the command type is a relative URL.

As another example, the following code will open a Recordset on the contents of the Winnt directory:

recordset.Open "", "URL=http://YourServer/Winnt/",,,adCmdTableDirect

OLE DB Provider-Supplied URL Schemes

The leading part of a fully-qualified URL is the scheme used to access the resource identified by the remainder of the URL. Examples are HTTP (HyperText Transfer Protocol) and FTP (File Transfer Protocol).

ADO supports OLE DB providers that recognize their own URL schemes. For example, the Microsoft OLE DB Provider for Internet Publishing , which accesses "published" Windows 2000 files, recognizes the existing HTTP scheme.

See Also

Connection Object | Record Object | Recordset Object


Records and Provider-Supplied Fields

When a Record object is opened, its source can be the current row of an open Recordset, an absolute URL, or a relative URL in conjunction with an open Connection object.

If the Record is opened from a Recordset, the Record object Fields collection will contain all the fields from the Recordset, plus any fields added by the underlying provider.

The provider may insert additional fields that serve as supplementary characteristics of the Record. As a result, a Record may have unique fields not in the Recordset as a whole or any Record derived from another row of the Recordset.

For example, all rows of a Recordset derived from an e-mail data source might have columns such as From, To, and Subject. A Record derived from that Recordset will have the same fields. However, the Record may also have other fields unique to the particular message represented by that Record, such as Attachment and Cc (carbon copy).

Although the Record object and current row of the Recordset have the same fields, they are different because Record and Recordset objects have different methods and properties.

A field held in common by the Record and Recordset can be modified on either object. However, the field cannot be deleted on the Record object, although the underlying provider may support setting the field to null.

After the Record is opened, you can programmatically add fields. You can also delete fields you have added, but you cannot delete fields from the original Recordset.

You may also open the Record object directly from a URL. In this case, the fields added to the Record depend on the underlying provider. Currently, most providers add a set of fields that describe the entity represented by the Record. If the entity consists of a stream of bytes, such as a simple file, then a Stream object can usually be opened from the Record.

Special Fields for Document Source Providers

A special class of providers, called document source providers, manages folders and documents. When a Record object represents a document or a Recordset object represents a folder of documents, the document source provider populates those objects with a unique set of fields that describe characteristics of the document instead of the actual document itself. Typically, one field contains a reference to the Stream that represents the document.

These fields constitute a resource record or recordset and are listed for the specific providers that support them in Appendix A: Providers.

Two constants index the Fields collection of a resource Record or Recordset to retrieve a pair of commonly used fields. The Field object Value property returns the desired content.

  • The field accessed with the adDefaultStream constant contains a default stream associated with the Record or Recordset object. The provider assigns a default stream to an object.
  • The field accessed with the adRecordURL constant contains the absolute URL that identifies the document.

A document source provider does not support the Properties collection of Record and Field objects. The content of the Properties collection is null for such objects.

A document source provider may add a provider-specific property such as Datasource Type to identify whether it is a document source provider. For more information about how to determine your type of provider, see your provider documentation.

Resource Recordset Columns

A resource recordset consists of the following columns.

Column name | Type | Description
RESOURCE_PARSENAME | AdVarWChar | Read-only. Indicates the URL of the resource.
RESOURCE_PARENTNAME | AdVarWChar | Read-only. Indicates the absolute URL of the parent record.
RESOURCE_ABSOLUTEPARSENAME | AdVarWChar | Read-only. Indicates the absolute URL of the resource, which is the concatenation of PARENTNAME and PARSENAME.
RESOURCE_ISHIDDEN | AdBoolean | True if the resource is hidden. No rows will be returned unless the command that creates the rowset explicitly selects rows where RESOURCE_ISHIDDEN is True.
RESOURCE_ISREADONLY | AdBoolean | True if the resource is read-only. Attempts to open this resource with DBBINDFLAG_WRITE and will fail with DB_E_READONLY. This property may be edited even when the resource has only been opened for reading.
RESOURCE_CONTENTTYPE | AdVarWChar | Indicates the MIME type of the document, indicating the format such as "text/html".
RESOURCE_CONTENTCLASS | AdVarWChar | Indicates the likely use of the document—for example, a lawyer's brief. This may correspond to the Office template used to create the document.
RESOURCE_CONTENTLANGUAGE | AdVarWChar | Indicates the language in which the content is stored.
RESOURCE_CREATIONTIME | adFileTime | Read-only. Indicates a FILETIME structure containing the time the resource was created. The time is reported in Coordinated Universal Time (UTC) format.
RESOURCE_LASTACCESSTIME | AdFileTime | Read-only. Indicates a FILETIME structure containing the time that the resource was last accessed. The time is in UTC format. The FILETIME members are zero if the provider does not support this time member.
RESOURCE_LASTWRITETIME | AdFileTime | Read-only. Indicates a FILETIME structure containing the time that the resource was last written. The time is in UTC format. The FILETIME members are zero if the provider does not support this time member.
RESOURCE_STREAMSIZE | asUnsignedBigInt | Read-only. Indicates the size of the resource's default stream, in bytes.
RESOURCE_ISCOLLECTION | AdBoolean | Read-only. True if the resource is a collection, such as a directory. False if the resource is a simple file.
RESOURCE_ISSTRUCTUREDDOCUMENT | AdBoolean | True if the resource is a structured document. False if the resource is not a structured document. It could be a collection or a simple file.
DEFAULT_DOCUMENT | AdVarWChar | Read-only. Indicates that this resource contains a URL to the default simple document of a folder or a structured document. Used when the default stream is requested from a resource. This property is blank for a simple file.
CHAPTERED_CHILDREN | AdChapter | Read-only. Optional. Indicates the chapter of the rowset containing the children of the resource. (The OLE DB Provider for Internet Publishing does not use this column.)
RESOURCE_DISPLAYNAME | AdVarWChar | Read-only. Indicates the display name of the resource.
RESOURCE_ISROOT | AdBoolean | Read-only. True if the resource is the root of a collection or structured document.

See Also

Record Object | Appendix A: Providers


Section II: Remote Data Service (RDS)

This section contains the following chapters:

  • Chapter 11: RDS Fundamentals
  • Chapter 12: RDS Tutorial
  • Chapter 13: RDS Usage and Security


Chapter 11: RDS Fundamentals

This section contains a series of topics that you can read in a prescribed order. The following topics are included:

  • Solutions for Remote Data Access
  • Basic RDS Programming Model
  • RDS Programming Model in Detail
  • RDS Programming Model with Objects
  • The RDS Object Model Summary

This section also contains the RDS tutorial and an RDS Scenario, which demonstrate how to access and update a data source, and a series of topics about Using RDS, which discusses more advanced RDS topics.

See Also

Solutions for Remote Data Access | RDS Scenario | RDS Tutorial | Using RDS


Solutions for Remote Data Access

The Issue

ADO enables your application to directly gain access to and modify data sources (sometimes called a two-tier system). For example, if your connection is to the data source that contains your data, that is a direct connection in a two-tier system.

However, you may want to access data sources indirectly through an intermediary such as Microsoft® Internet Information Services (IIS). This arrangement is sometimes called a three-tier system. IIS is a client/server system that provides an efficient way for a local, or client, application to invoke a remote, or server, program across the Internet or an intranet. The server program gains access to the data source and optionally processes the acquired data.

For example, your intranet Web page contains an application written in Microsoft® Visual Basic Scripting Edition (VBScript), which connects to IIS. IIS in turn connects to the actual data source, retrieves the data, processes it in some way, and then returns the processed information to your application.

In this example, your application never directly connected to the data source; IIS did. And IIS accessed the data by means of ADO.

Note The client/server application does not have to be based on the Internet or an intranet (that is, Web-based)—it could consist solely of compiled programs on a local area network. However, the typical case is a Web-based application.

Because some visual control, such as a grid, check box, or list, may use the returned information, the returned information must be easily used by a visual control.

You want a simple and efficient application-programming interface that supports three-tier systems, and returns information as easily as if it had been retrieved on a two-tier system. Remote Data Service (RDS) is this interface.

The Solution

RDS defines a programming model—the sequence of activities necessary to gain access to and update a data source—to gain access to data through an intermediary, such as Internet Information Services (IIS). The programming model summarizes the entire functionality of RDS.

See Also

Basic RDS Programming Model | RDS Scenario | RDS Tutorial | Using RDS


Basic RDS Programming Model

RDS addresses applications that exist in the following environment: A client application specifies a program that will execute on a server and the parameters required to return the desired information. The program invoked on the server gains access to the specified data source, retrieves the information, optionally processes the data, and then returns the resulting information to your client application in a form that it can easily use. RDS provides the means for you to perform the following sequence of actions:

  • Specify the program to be invoked on the server, and obtain a way to refer to it from the client. (This reference is sometimes called a proxy. It represents the remote server program. The client application will "call" the proxy as if it were a local program, but it actually invokes the remote server program.)
  • Invoke the server program. Pass parameters to the server program that identify the data source and the command to issue. (The server program actually uses ADO to gain access to the data source. ADO makes a connection with one of the given parameters, and then issues the command specified in the other parameter.)
  • The server program obtains a Recordset object from the data source. Optionally, the Recordset object is processed on the server.
  • The server program returns the final Recordset object to the client application.
  • On the client, the Recordset object is put into a form that can be easily used by visual controls.
  • Any modifications to the Recordset object are sent back to the server program, which uses them to update the data source.

This programming model contains certain convenience features. If you do not need a complex server program to access the data source, and if you provide the required connection and command parameters, RDS will automatically retrieve the specified data with a simple, default server program.

If you need more complex processing, you can specify your own custom server program. For example, because a custom server program has the full power of ADO at its disposal, it could connect to several different data sources, combine their data in some complex way, and then return a simple, processed result to the client application.

Finally, if your needs are somewhere in between, ADO now supports customizing the behavior of the default server program.

See Also

RDS Programming Model in Detail | RDS Scenario | RDS Tutorial | Recordset Object | Using RDS


RDS Programming Model in Detail

The following are key elements of the RDS programming model:

  • RDS.DataSpace
  • RDSServer.DataFactory
  • RDS.DataControl
  • Event

RDS.DataSpace

Your client application must specify the server and the server program to invoke. In return, your application receives a reference to the server program and can treat the reference as if it were the server program itself.

The RDS object model embodies this functionality with the RDS.DataSpace object.

The server program is specified with a program identifier, or ProgID. The server uses the ProgID and the server machine's registry to locate information about the actual program to initiate.

RDS makes a distinction internally depending on whether the server program is on a remote server across the Internet or an intranet; a server on a local area network; or not on a server at all, but instead on a local dynamic-link library (DLL). This distinction determines how information is exchanged between the client and the server, and makes a tangible difference in the type of reference returned to your client application. However, from your point of view, this distinction has no special meaning. All that matters is that you receive a usable program reference.

RDSServer.DataFactory

RDS provides a default server program that can either perform a SQL query against the data source and return a Recordset object or take a Recordset object and update the data source.

The RDS object model embodies this functionality with the RDSServer.DataFactory object.

In addition, this object has a method for creating an empty Recordset object that you can fill programmatically (CreateRecordset), and another method for converting a Recordset object into a text string to build a Web page (ConvertToString).

With ADO, you can override some of the standard connection and command behavior of the RDSServer.DataFactory with a DataFactory handler and a customization file that contains connection, command, and security parameters.

The server program is sometimes called a business object. You can write your own custom business object that can perform complicated data access, validity checks, and so on. Even when writing a custom business object, you can create an instance of an RDSServer.DataFactory object and use some of its methods to accomplish your own tasks.

RDS.DataControl

RDS provides a means to combine the functionality of the RDS.DataSpace and RDSServer.DataFactory, and also enable visual controls to easily use the Recordset object returned by a query from a data source. RDS attempts, for the most common case, to do as much as possible to automatically gain access to information on a server and display it in a visual control.

The RDS object model embodies this functionality with the RDS.DataControl object.

The RDS.DataControl has two aspects. One aspect pertains to the data source. If you set the command and connection information using the Connect and SQL properties of the RDS.DataControl, it will automatically use the RDS.DataSpace to create a reference to the default RDSServer.DataFactory object. Then the RDSServer.DataFactory will use the Connect property value to connect to the data source, use the SQL property value to obtain a Recordset from the data source, and return the Recordset object to the RDS.DataControl.

The second aspect pertains to the display of returned Recordset information in a visual control. You can associate a visual control with the RDS.DataControl (in a process called binding) and gain access to the information in the associated Recordset object, displaying query results on a Web page in Microsoft® Internet Explorer. Each RDS.DataControl object binds one Recordset object, representing the results of a single query, to one or more visual controls (for example, a text box, combo box, grid control, and so forth). There may be more than one RDS.DataControl object on each page. Each RDS.DataControl object can be connected to a different data source and contain the results of a separate query.

The RDS.DataControl object also has its own methods for navigating, sorting, and filtering the rows of the associated Recordset object. These methods are similar, but not the same as the methods on the ADO Recordset object.

Events

RDS supports two of its own events, which are independent of the ADO event model. The onReadyStateChange event is called whenever the RDS.DataControl ReadyState property changes, thus notifying you when an asynchronous operation has successfully completed, terminated, or experienced an error. The onError event is called whenever an error occurs, even if the error occurs during an asynchronous operation.

Note Microsoft Internet Explorer provides two additional events to RDS—onDataSetChanged (the Recordset is functional but still retrieving rows) and onDataSetComplete (the Recordset has finished retrieving rows).

See Also

RDS Programming Model with Objects | DataControl Object (RDS) | DataFactory Object (RDSServer) | DataSpace Object (RDS) | RDS Scenario | RDS Tutorial | Using RDS


RDS Programming Model with Objects

The goal of RDS is to gain access to and update data sources through an intermediary such as IIS. The programming model specifies the sequence of activities necessary to accomplish this goal. The object model specifies the objects whose methods and properties affect the programming model.

RDS provides the means to perform the following sequence of actions:

  • Specify the program to be invoked on the server, and obtain a way (proxy) to refer to it from the client (RDS.DataSpace).
  • Invoke the server program. Pass parameters to the server program that identifies the data source and the command to issue (proxy or RDS.DataControl).
  • The server program obtains a Recordset object from the data source, typically by using ADO. Optionally, the Recordset object is processed on the server (RDSServer.DataFactory).
  • The server program returns the final Recordset object to the client application (proxy).
  • On the client, the Recordset object is put into a form that can be easily used by visual controls (visual control and RDS.DataControl).
  • Changes to the Recordset object are sent back to the server and used to update the data source (RDS.DataControl or RDSServer.DataFactory).

See Also

RDS Object Model Summary | DataControl Object (RDS) | DataFactory Object (RDSServer) | DataSpace Object (RDS) | RDS Scenario | RDS Tutorial | Recordset Object | Using RDS


RDS Object Model Summary

Object | Description
RDS.DataSpace | This object contains a method to obtain a server proxy. The proxy may be the default or a custom server program (business object). The server program may be invoked on the Internet, an intranet, a local area network, or be a local dynamic-link library.
RDSServer.DataFactory | This object represents the default server program. It executes the default RDS data retrieval and update behavior.
RDS.DataControl | This object can automatically invoke the RDS.DataSpace and RDSServer.DataFactory objects. Use this object to invoke the default RDS data retrieval or update behavior. This object also provides the means for visual controls to access the returned Recordset object.

See Also

RDS Fundamentals | RDS Scenario | RDS Tutorial | Using RDS


RDS Scenario

The Address Book application is a scenario that shows you how to use Remote Data Service (RDS) to build a simple, data-aware Web application—an online corporate address book. This scenario is useful for Microsoft Visual Basic Scripting Edition (VBScript) and COM programmers who want to learn how to use data-aware ActiveX controls with RDS, and for more experienced application developers who want to build data-centric Web applications.

This scenario assumes you know how to use basic HTML layout tags, use DHTML data binding techniques, and program with ActiveX controls.

If you have installed the Platform SDK, the complete source code for the Address Book sample application can be found at <drive>:\Platform SDK\samples\dataaccess\rds\AddrBook\AddrBook.asp. To view the Address Book scenario, in Internet Explorer 4.0 or later, type http://webserver/RDS/AddrBook/AddrBook.asp where webserver is the name given to your Windows NT 4.0 or Windows 2000 Web server computer running Internet Information Services (IIS) and ASP.

Introduction to Address Book

The Address Book sample application provides a simple online address book that you can use to publish a searchable directory over an intranet. The address book is designed so that a user can enter a search string in one or more fields to request information about employees. To show you the basic features of Remote Data Service, the sample application is intentionally kept small, with a minimum number of objects and search fields.

The application interface consists of the following parts:

  • A non-visual RDS.DataControl data-binding object utilized by the client to connect to the database.
  • HTML text boxes that act as input fields for employee attribute search criteria.
  • HTML command buttons to build queries, clear search fields, update the database with employee information, cancel pending changes, and navigate the rows of data displayed in the grid.
  • DHTML data binding to display data returned from queries against a back-end database (through the RDS.DataControl data-binding object) in a table.
  • VBScript routines that connect each of the elements above and allow them to interact. VBScript code is also used to initialize the RDS.DataControl object and dynamically create the column headings in the HTML table from the names of the RDS.DataControl recordset fields.

Follow the links from step to step to set up and run the scenario and to learn more about how the scenario works.

See Also

System Requirements for the Address Book Application | ActiveX Data Objects Start Page | RDS Fundamentals | RDS Tutorial


System Requirements for the Address Book Application

To set up the Address Book sample application, you need to meet the following software and database requirements:

Software Requirements

The server computer software requirements for running this Web application include:

  • Microsoft Windows NT® Server 4.0, with Service Pack 3 or later, or Microsoft Windows® 2000 Server.
  • Microsoft Internet Information Services (IIS) version 3.0 or later, with Active Server Pages.

The client computer software requirements for running this Web application include:

  • Microsoft Internet Explorer 4.0 or later.
  • Microsoft Windows NT 4.0 Workstation or Server, Windows 2000, or Microsoft Windows 98.

Database Requirements

To use this sample, you must have:

  • An operational Microsoft® SQL Server version 6.5 or later database server.
  • Privileges to create the database and populate it with the sample data.
  • Verification of the populated data through Enterprise Manager or the ISQL utilities (called Query Analyzer in SQL Server 7.0).

If you do not have privileges, your database administrator may need to set up the system and give you access permission to the database, or set up the database for you.

See Also

Running the Address Book SQL Script | DataControl Object (RDS) | Running the Address Book Sample Application


Running the Address Book SQL Script

You must use either the ISQL/Query Analyzer command-line utility or the SQL Server Enterprise Manager to run the included SQL script (Sampleemp.sql) that:

  • Creates a new database, AddrBookDB, on the default device.
  • Connects to the database, AddrBookDB.
  • Creates an Employee table.
  • Populates the table with sample data.
  • Runs a simple SELECT statement to verify the population of the database table.
  • Sets up a user account called "rdsdemo" with a password of "rdsdemo."

To run the Sampleemp.sql script

In Microsoft SQL Server 6.5:

  1. Click Start, point to Programs, and then point to Microsoft SQL Server 6.5. Click SQL Enterprise Manager.
  2. From the Tools menu, click SQL Query Tool.
  3. Click Load SQL Script and browse to c:\Platform SDK\Samples\DataAccess\RDS\AddrBook.
  4. Select the file Sampleemp.sql. Click Open.
  5. Click the Execute Query button (the green arrow on the toolbar).
  6. After it executes, close the Query and Enterprise Manager windows.

In Microsoft SQL Server 7.0:

  1. Click Start, point to Programs, and then point to Microsoft SQL Server 7.0. Click Enterprise Manager.
  2. Be sure that the SQL Server that you want to use is selected from your list of registered servers in Enterprise Manager.
  3. From the Tools menu, click SQL Server Query Analyzer.
  4. Click the Load SQL Script button (the open folder on the toolbar) and browse to c:\Platform SDK\Samples\DataAccess\RDS\AddrBook.
  5. Select the file Sampleemp.sql. Click Open.
  6. Click the Execute Query button (the green arrow on the toolbar) or F5.
  7. After it executes, close the Query, Query Analyzer, and Enterprise Manager windows.

See Also

Running the Address Book Sample Application


Running the Address Book Sample Application

To run the Address Book application, follow this procedure.

To run this application

  1. Make sure that Microsoft SQL Server is running. Click Start, point to Programs, point to Microsoft SQL Server 7.0, and then click Service Manager. If there is a green arrow in the white circle, then SQL Server is running. If it is not (there will be a red square in the white circle), click Start/Continue.
  2. In Microsoft Internet Explorer 4.0 or later, type the following address:

    http://webserver/RDS/AddrBook/AddrBook.asp

    where webserver is the name of the Web server where the RDS server components are installed.

  3. You can then try various scenarios in the Address Book sample application, such as searching for a person based on his or her e-mail name, listing all people with the title "Program Manager," or editing existing records. Click Find to fill the data grid with all the available names.

See Also

Address Book Data-Binding Object


Address Book Data-Binding Object

The Address Book application uses the RDS.DataControl object to bind data from the SQL Server database to a visual object (in this case, a DHTML table) in the application's client HTML page. The event-driven VBScript program logic uses the RDS.DataControl to:

  • Query the database, send updates to the database, and refresh the data grid.
  • Allow users to move to the first, next, previous, or last record in the data grid.

The following code defines the RDS.DataControl component:

<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
   ID=DC1 Width=1 Height=1>
   <PARAM NAME="SERVER" VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>">
   <PARAM NAME="CONNECT" VALUE="Provider=sqloledb;
Initial Catalog=AddrBookDb;User ID=adcdemo;Password=adcdemo;">
</OBJECT>

The OBJECT tag defines the RDS.DataControl component in the program. The tag includes two types of parameters:

  • Those associated with the generic OBJECT tag.
  • Those specific to the RDS.DataControl object.

Generic OBJECT Tag Parameters

The following table describes the parameters associated with the OBJECT tag.

Parameter | Description
CLASSID | A unique, 128-bit number that identifies the type of embedded object to the system. This identifier is maintained in the local computer's system registry. (For the class IDs of the RDS.DataControl object, see RDS.DataControl Object.)
ID | Defines a document-wide identifier for the embedded object that is used to identify it in code.

RDS.DataControl Tag Parameters

The following table describes the parameters specific to the RDS.DataControl object. (For a complete list of the RDS.DataControl object parameters, and when to implement them, see RDS.DataControl object.)

Parameter | Description
SERVER | If you are using HTTP, the value is the name of the server computer preceded by http://.
CONNECT | Provides the necessary connection information for the RDS.DataControl to connect to SQL Server.
SQL | Sets or returns the query string used to retrieve the Recordset.

See Also

Address Book Command Buttons


Address Book Command Buttons

The Address Book application includes the following command buttons:

  • A Find button to submit a query to the database.
  • A Clear button to clear the text boxes before starting a new search.
  • An Update Profile button to save changes to an employee record.
  • A Cancel Changes button to discard changes.

Find Button

Clicking the Find button activates the VBScript Find_OnClick Sub procedure, which builds and sends the SQL query. Clicking this button populates the data grid.

Building the SQL Query

The first part of the Find_OnClick Sub procedure builds the SQL query, one phrase at a time, by appending text strings to a global SQL SELECT statement. It begins by setting the variable myQuery to a SQL SELECT statement that requests all rows of data from the data source table. Next, the Sub procedure scans each of the four input boxes on the page.

Because the program uses the word like in building the SQL statements, the queries are substring searches rather than exact matches.

For example, if the Last Name box contained the entry "Berge" and the Title box contained the entry "Program Manager", the SQL statement (value of myQuery) would read:

Select FirstName, LastName, Title, Email, Building, Room, Phone from Employee where lastname like 'Berge%' and title like 'Program Manager%'

If the query was successful, all persons with a last name containing the text "Berge" (such as Berge and Berger) and with a title containing the words "Program Manager" (for example, Program Manager, Advanced Technologies) are displayed in the HTML data grid.

Preparing and Sending the Query

The last part of the Find_OnClick Sub procedure consists of two statements. The first statement assigns the SQL property of the RDS.DataControl object equal to the dynamically built SQL query. The second statement causes the RDS.DataControl object (DC1) to query the database, and then display the new results of the query in the grid.

Sub Find_OnClick
   '...
   DC1.SQL = myQuery
   DC1.Refresh
End Sub

Update Profile Button

Clicking the Update Profile button activates the VBScript Update_OnClick Sub procedure, which executes the RDS.DataControl object's (DC1) SubmitChanges and Refresh methods.

Sub Update_OnClick
   DC1.SubmitChanges
   DC1.Refresh
End Sub

When DC1.SubmitChanges executes, the Remote Data Service packages all the update information and sends it to the server via HTTP. The update is all-or-nothing; if a part of the update is unsuccessful, none of the changes is made, and a status message is returned. DC1.Refresh isn't necessary after SubmitChanges with Remote Data Service, but it ensures fresh data.

Cancel Changes Button

Clicking Cancel Changes activates the VBScript Cancel_OnClick Sub procedure, which executes the RDS.DataControl object's (DC1) CancelUpdate method.

Sub Cancel_OnClick
   DC1.CancelUpdate
End Sub

When DC1.CancelUpdate executes, it discards any edits that a user has made to an employee record on the data grid since the last query or update. It restores the original values.

See Also

Address Book Navigation Buttons | DataControl Object (RDS)


Address Book Navigation Buttons

The Address Book application displays the navigation buttons at the bottom of the Web page. You can use the navigation buttons to navigate through the data in the HTML grid display by selecting either the first or last row of data, or rows adjacent to the current selection.

Navigation Sub Procedures

The Address Book application contains several procedures that allow users to click the First, Next, Previous, and Last buttons to move around the data.

For example, clicking the First button activates the VBScript First_OnClick Sub procedure. The procedure executes a MoveFirst method, which makes the first row of data the current selection. Clicking the Last button activates the Last_OnClick Sub procedure, which invokes the MoveLast method, making the last row of data the current selection. The remaining navigation buttons work in a similar fashion.

' Move to the first record in the bound Recordset.
Sub First_OnClick
   DC1.Recordset.MoveFirst
End Sub

' Move to the next record from the current position
' in the bound Recordset.
Sub Next_OnClick
   If Not DC1.Recordset.EOF Then    ' Cannot move beyond bottom record.
      DC1.Recordset.MoveNext
      Exit Sub
   End If
End Sub

' Move to the previous record from the current position in the bound
' Recordset.
Sub Prev_OnClick
   If Not DC1.Recordset.BOF Then    ' Cannot move beyond top record.
      DC1.Recordset.MovePrevious
      Exit Sub
   End If
End Sub

' Move to the last record in the bound Recordset.
Sub Last_OnClick
   DC1.Recordset.MoveLast
End Sub

See Also

DataControl Object (RDS) | MoveFirst, MoveLast, MoveNext, and MovePrevious Methods (RDS)


Chapter 12: RDS Tutorial

This tutorial illustrates using the RDS programming model to query and update a data source. First, it describes the steps necessary to accomplish this task. Then the tutorial is repeated in Microsoft® Visual Basic Scripting Edition and Microsoft® Visual J++®, featuring ADO for Windows Foundation Classes (ADO/WFC).

This tutorial is coded in different languages for two reasons:

  • The documentation for RDS assumes the reader codes in Visual Basic. This makes the documentation convenient for Visual Basic programmers, but less useful for programmers who use other languages.
  • If you are uncertain about a particular RDS feature and you know a little of another language, you might be able to resolve your question by looking for the same feature expressed in another language.

How the Tutorial is Presented

This tutorial is based on the RDS programming model. It discusses each step of the programming model individually. In addition, it illustrates each step with a fragment of Visual Basic code.

The code example is repeated in other languages with minimal discussion. Each step in a given programming language tutorial is marked with the corresponding step in the programming model and descriptive tutorial. Use the number of the step to refer to the discussion in the descriptive tutorial.

The RDS programming model is stated below. Use it as a roadmap as you proceed through the tutorial.

RDS Programming Model with Objects

  • Specify the program to be invoked on the server, and obtain a way (proxy) to refer to it from the client.
  • Invoke the server program. Pass parameters to the server program that identifies the data source and the command to issue.
  • The server program obtains a Recordset object from the data source, typically by using ADO. Optionally, the Recordset object is processed on the server.
  • The server program returns the final Recordset object to the client application.
  • On the client, the Recordset object is optionally put into a form that can be easily used by visual controls.
  • Changes to the Recordset object are sent back to the server and used to update the data source.

See Also

Step 1: Specify a Server Program (RDS Tutorial) | RDS Tutorial (VBScript) | RDS Tutorial (Visual J++)


Step 1: Specify a Server Program (RDS Tutorial)

In the most general case, use the RDS.DataSpace object CreateObject method to specify the default server program, RDSServer.DataFactory, or your own custom server program (business object). A server program is instantiated on the server, and a reference to the server program, or proxy, is returned.

This tutorial uses the default server program:

Sub RDSTutorial1()
   Dim DS as New RDS.DataSpace
   Dim DF as Object
   Set DF = DS.CreateObject("RDSServer.DataFactory", "http://yourServer")
...

See Also

Step 2: Invoke the Server Program (RDS Tutorial) | RDS Tutorial (VBScript) | RDS Tutorial (Visual J++)


Step 2: Invoke the Server Program (RDS Tutorial)

When you invoke a method on the client proxy, the actual program on the server executes the method. In this step, you'll execute a query on the server.

Part A If you weren't using RDSServer.DataFactory in this tutorial, the most convenient way to perform this step would be to use the RDS.DataControl object. The RDS.DataControl combines the previous step of creating a proxy, with this step, issuing the query.

Set the RDS.DataControl object Server property to identify where the server program should be instantiated; the Connect property to specify the connect string to access the data source; and the SQL property to specify the query command text. Then issue the Refresh method to cause the server program to connect to the data source, retrieve rows specified by the query, and return a Recordset object to the client.

This tutorial does not use the RDS.DataControl, but this is how it would look if it did:

Sub RDSTutorial2A()
   Dim DC as New RDS.DataControl
   DC.Server = "http://yourServer"
   DC.Connect = "DSN=Pubs"
   DC.SQL = "SELECT * FROM Authors"
   DC.Refresh
...

Nor does the tutorial invoke RDS with ADO objects, but this is how it would look if it did:

Dim rs as New ADODB.Recordset
rs.Open "SELECT * FROM Authors","Provider=MS Remote;Data Source=Pubs;" & _
        "Remote Server=http://yourServer;Remote Provider=SQLOLEDB;"

Part B The general method of performing this step is to invoke the RDSServer.DataFactory object Query method. That method takes a connect string, which is used to connect to a data source, and a command text, which is used to specify the rows to be returned from the data source.

This tutorial uses the DataFactory object Query method:

Sub RDSTutorial2B()
   Dim DS as New RDS.DataSpace
   Dim DF
   Dim RS as ADODB.Recordset
   Set DF = DS.CreateObject("RDSServer.DataFactory", "http://yourServer")
   Set RS = DF.Query ("DSN=Pubs", "SELECT * FROM Authors")
...

See Also

Step 3: Server Obtains a Recordset (RDS Tutorial) | RDS Tutorial (VBScript) | RDS Tutorial (Visual J++)


Step 3: Server Obtains a Recordset (RDS Tutorial)

The server program uses the connect string and command text to query the data source for the desired rows. ADO is typically used to retrieve this Recordset, although other Microsoft data access interfaces, such as OLE DB, could be used.

A custom server program might look like this:

Public Function ServerProgram(cn as String, qry as String) as Object
Dim rs as New ADODB.Recordset
   rs.CursorLocation = adUseClient
   rs.Open qry, cn
   rs.ActiveConnection = Nothing
   Set ServerProgram = rs
End Function

See Also

Step 4: Server Returns the Recordset (RDS Tutorial) | RDS Tutorial (VBScript) | RDS Tutorial (Visual J++)


Step 4: Server Returns the Recordset (RDS Tutorial)

RDS converts the retrieved Recordset object to a form that can be sent back to the client (that is, it marshals the Recordset). The exact form of the conversion and how it is sent depends on whether the server is on the Internet or an intranet, a local area network, or is a dynamic-link library. However, this detail is not critical; all that matters is that RDS sends the Recordset back to the client.

On the client side, a Recordset object is returned and assigned to a local variable.

Sub RDSTutorial4()
   Dim DS as New RDS.DataSpace
   Dim RS as ADODB.Recordset
   Dim DF as Object
   Set DF = DS.CreateObject("RDSServer.DataFactory", "http://yourServer")
   Set RS = DF.Query("DSN=Pubs", "SELECT * FROM Authors")
...

See Also

Step 5: DataControl is Made Usable (RDS Tutorial) | RDS Tutorial (VBScript) | RDS Tutorial (Visual J++)


Step 5: DataControl is Made Usable (RDS Tutorial)

The returned Recordset object is available for use. You can examine, navigate, or edit it as you would any other Recordset. What you can do with the Recordset depends on your environment. Visual Basic and Visual C++ have visual controls that can use a Recordset directly or indirectly with the aid of an enabling data control.

For example, if you are displaying a Web page in Microsoft Internet Explorer, you might want to display the Recordset object data in a visual control. Visual controls on a Web page cannot access a Recordset object directly. However, they can access the Recordset object through the RDS.DataControl. The RDS.DataControl becomes usable by a visual control when its SourceRecordset property is set to the Recordset object.

The visual control object must have its DATASRC parameter set to the RDS.DataControl, and its DATAFLD property set to a Recordset object field (column).

In this tutorial, set the SourceRecordset property:

Sub RDSTutorial5()
   Dim DS as New RDS.DataSpace
   Dim RS as ADODB.Recordset
   Dim DC as New RDS.DataControl
   Dim DF as Object
   Set DF = DS.CreateObject("RDSServer.DataFactory", "http://yourServer")
   Set RS = DF.Query ("DSN=Pubs", "SELECT * FROM Authors")
   DC.SourceRecordset = RS         ' Visual controls can now bind to DC.
...

See Also

Step 6: Changes are Sent to the Server (RDS Tutorial) | RDS Tutorial (VBScript) | RDS Tutorial (Visual J++)


Step 6: Changes are Sent to the Server (RDS Tutorial)

If the Recordset object is edited, any changes (that is, rows that are added, changed, or deleted) can be sent back to the server.

Note The default behavior of RDS can be invoked implicitly with ADO objects and the Microsoft OLE DB Remoting Provider. Queries can return Recordsets, and edited Recordsets can update the data source. This tutorial does not invoke RDS with ADO objects, but this is how it would look if it did:

Dim rs as New ADODB.Recordset
rs.Open "SELECT * FROM Authors","Provider=MS Remote;Data Source=Pubs;" & _
        "Remote Server=http://yourServer;Remote Provider=SQLOLEDB;"
...              ' Edit the Recordset.
rs.UpdateBatch   ' The equivalent of SubmitChanges.
...

Part A Assume for this case that you have only used the RDS.DataControl and that a Recordset object is now associated with the RDS.DataControl. The SubmitChanges method updates the data source with any changes to the Recordset object if the Server and Connect properties are still set.

Sub RDSTutorial6A()
Dim DC as New RDS.DataControl
Dim RS as ADODB.Recordset
DC.Server = "http://yourServer"
DC.Connect = "DSN=Pubs"
DC.SQL = "SELECT * FROM Authors"
DC.Refresh
...
Set RS = DC.Recordset
   ' Edit the Recordset.
...
DC.SubmitChanges
...

Part B Alternatively, you could update the server with the RDSServer.DataFactory object, specifying a connection and a Recordset object.

Sub RDSTutorial6B()
Dim DS As New RDS.DataSpace
Dim RS As ADODB.Recordset
Dim DC As New RDS.DataControl
Dim DF As Object
Dim blnStatus As Boolean
Set DF = DS.CreateObject("RDSServer.DataFactory", "http://yourServer")
Set RS = DF.Query ("DSN=Pubs", "SELECT * FROM Authors")
DC.SourceRecordset = RS    ' Visual controls can now bind to DC.
    ' Edit the Recordset.
blnStatus = DF.SubmitChanges "DSN=Pubs", RS
End Sub

This is the end of the tutorial.

See Also

Microsoft OLE DB Remoting Provider | RDS Tutorial | RDS Tutorial (VBScript) | RDS Tutorial (Visual J++)


RDS Tutorial (VBScript)

This is the RDS Tutorial, written in Microsoft Visual Basic Scripting Edition. For a description of the purpose of this tutorial, see the RDS Tutorial.

In this tutorial, RDS.DataControl and RDS.DataSpace are created at design time—that is, they are defined with object tags, like this: <OBJECT>...</OBJECT>. Alternatively, they could be created at run time with the Server.CreateObject method. For example, the RDS.DataControl object could be created like this:

Set DC = Server.CreateObject("RDS.DataControl")
   <!-- RDS.DataControl -->
   <OBJECT
      ID="DC1" CLASSID="CLSID:BD96C556-65A3-11D0-983A-00C04FC29E33">
   </OBJECT>

   <!-- RDS.DataSpace -->
   <OBJECT
      ID="DS1" WIDTH=1 HEIGHT=1
      CLASSID="CLSID:BD96C556-65A3-11D0-983A-00C04FC29E36">
   </OBJECT>

   <SCRIPT LANGUAGE="VBScript">

   Sub RDSTutorial()
   Dim DF1

Step 1—Specify a server program

VBScript can discover the name of the IIS Web server it is running on by accessing the VBScript Request.ServerVariables method available to Active Server Pages:

"http://<%=Request.ServerVariables("SERVER_NAME")%>"

However, for this tutorial, use the imaginary server, "yourServer".

Note Pay attention to the data type of ByRef arguments. VBScript does not let you specify the variable type, so you must always pass a Variant. When using HTTP, RDS will allow you to pass a Variant to a method that expects a non-Variant if you invoke it with the RDS.DataSpace object CreateObject method. When using DCOM or an in-process server, match the parameter types on the client and server sides or you will receive a "Type Mismatch" error.

Set DF1 = DS1.CreateObject("RDSServer.DataFactory", "http://yourServer")

Step 2a—Invoke the server program with RDS.DataControl

This example is merely a comment demonstrating that the default behavior of the RDS.DataControl is to perform the specified query.

<OBJECT CLASSID="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33" ID="DC1">
   <PARAM NAME="SQL" VALUE="SELECT * FROM Authors">
   <PARAM NAME="Connect" VALUE="DSN=Pubs;">
   <PARAM NAME="Server" VALUE="http://yourServer/">
</OBJECT>
...
<SCRIPT LANGUAGE="VBScript">

Sub RDSTutorial2A()
   Dim RS
   DC1.Refresh
   Set RS = DC1.Recordset
...

Step 2b—Invoke the server program with RDSServer.DataFactory

Step 3—Server obtains a Recordset

Step 4—Server returns the Recordset

Set RS = DF1.Query("DSN=Pubs;", "SELECT * FROM Authors")

Step 5—DataControl is made usable by visual controls

' Assign the returned recordset to the DataControl.

DC1.SourceRecordset = RS

Step 6a—Changes are sent to the server with RDS.DataControl

This example is merely a comment demonstrating how the RDS.DataControl performs updates.

<OBJECT CLASSID="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33" ID="DC1">
   <PARAM NAME="SQL" VALUE="SELECT * FROM Authors">
   <PARAM NAME="Connect" VALUE="DSN=Pubs;">
   <PARAM NAME="Server" VALUE="http://yourServer/">
</OBJECT>
...
<SCRIPT LANGUAGE="VBScript">

Sub RDSTutorial6A()
Dim RS
DC1.Refresh
...
Set RS = DC1.Recordset
' Edit the Recordset object...
' The SERVER and CONNECT properties are already set from Step 2A.
Set DC1.SourceRecordset = RS
...
DC1.SubmitChanges

Step 6b—Changes are sent to the server with RDSServer.DataFactory

DF.SubmitChanges "DSN=Pubs", RS

End Sub
</SCRIPT>
</BODY>
</HTML>

This is the end of the tutorial.

See Also

RDS Tutorial | RDS Tutorial (Visual J++)


RDS Tutorial (Visual J++)

ADO/WFC does not completely follow the RDS object model in that it does not implement the RDS.DataControl object. ADO/WFC only implements the client-side class, RDS.DataSpace.

The DataSpace class implements one method, CreateObject, which returns an ObjectProxy object. The DataSpace class also implements the InternetTimeout property.

The ObjectProxy class implements one method, call, which can invoke any server-side business object.

This is the beginning of the tutorial.

import com.ms.wfc.data.*;
public class RDSTutorial
{
   public void tutorial()
   {
// Step 1: Specify a server program.
      ObjectProxy obj =
         DataSpace.createObject(
            "RDSServer.DataFactory",
            "http://YourServer");

// Step 2: Server returns a Recordset.
      Recordset rs = (Recordset) obj.call(
            "Query",
            new Object[] {"DSN=Pubs;", "SELECT * FROM Authors"});

// Step 3: Changes are sent to the server.
      ...                        // Edit Recordset.
      obj.call(
            "SubmitChanges",
            new Object[] {"DSN=Pubs;", rs});
      return;
   }
}

This is the end of the tutorial.

See Also

RDS Tutorial | RDS Tutorial (VBScript)


Chapter 13: RDS Usage and Security

Use the information in this chapter to set up your server and use RDS quickly. This chapter includes specific configuration steps that you might need to take when implementing RDS, describes some of the key relationships between RDS and other technologies, and helps identify solutions to problems that you might encounter when setting up an RDS solution.

This section contains information about:

  • Configuring RDS
  • Using Related Technologies with RDS
  • DataFactory Customization
  • Troubleshooting RDS

See Also

RDS Fundamentals | RDS Scenario | RDS Tutorial


Configuring RDS

To implement RDS efficiently, be sure you are familiar with the various configurations available to you. This section includes important information about security and scalability in your implementation of RDS. See the following topics for information about configuring your computers to use RDS.

  • Granting Guest Privileges to a Web Server Computer
  • Registering a Custom Business Object
  • Marking Business Objects as Safe for Scripting
  • Registering Business Objects on the Client for Use with DCOM
  • Setting DCOM Stream Marshaling Format
  • Enabling a DLL to Run on DCOM
  • Configuring Virtual Servers on IIS
  • Specifying Threads Per Processor on IIS
  • Securing RDS Applications
  • Configuring DataFactory for Safe or Unrestricted Modes

See Also

Using Related Technologies with RDS | DataFactory Customization | Troubleshooting RDS


Granting Guest Privileges to a Web Server Computer

The anonymous Web server account (IUSR_ComputerName) must be added to the Guests local group on the Web server computer to use RDS.

To grant guest privileges to a Web server computer

  1. On your Microsoft Windows® 2000 Server computer, click Start, point to Programs, point to Administrative Tools, and then click Computer Management.
  2. In the console tree, in Local Users and Groups, click Groups.
  3. Select the Guests local group. From the Action menu, choose Properties.
  4. In the Guests Properties dialog box, click Add.
  5. If the anonymous Web server account does not appear in the list in the Select Users or Groups dialog box, type its name (IUSR_ComputerName) into the bottom blank box, and then click Add.
  6. Click OK.


Registering a Custom Business Object

To successfully launch a custom business object (.dll or .exe) through the Web server, the business object's ProgID must be entered into the registry as explained in this procedure. This RDS feature protects the security of your Web server by running only sanctioned executables.

Note For MDAC 2.0 and later, the default business object, RDSServer.DataFactory, is not registered by default during MDAC installation. However, if RDSServer.DataFactory was registered as safe for execution on the computer prior to the installation, the registry entry is maintained for the new installation.

To register a custom business object

  1. Click Start and then click Run.
  2. Type RegEdit and click OK.
  3. In the Registry Editor, navigate to the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\W3SVC\Parameters\ADCLaunch registry key.
  4. Select the ADCLaunch key, and then from the Edit menu, point to New and click Key.
  5. Type the ProgID of your custom business object and click Enter. Leave the Value entry blank.


Marking Business Objects as Safe for Scripting

To help ensure a secure Internet environment, you need to mark any business objects instantiated with the RDS.DataSpace object's CreateObject method as "safe for scripting." You need to ensure they are marked as such in the License area of the system registry before they can be used in DCOM.

To manually mark your business object as safe for scripting, create a text file with a .reg extension that contains the following text. The following two numbers enable the safe-for-scripting feature:

[HKEY_CLASSES_ROOT\CLSID\<MyActiveXGUID>\Implemented
Categories\{7DD95801-9882-11CF-9FA9-00AA006C42C4}]
[HKEY_CLASSES_ROOT\CLSID\<MyActiveXGUID>\Implemented
Categories\{7DD95802-9882-11CF-9FA9-00AA006C42C4}]

where <MyActiveXGUID> is the hexadecimal GUID number of your business object. Save it and merge it into your registry by using the Registry Editor or double-clicking the .reg file in Windows Explorer.

Business objects created in Microsoft® Visual Basic can be automatically marked as "safe for scripting" with the Package and Deployment Wizard. When the wizard asks you to specify safety settings, select Safe for initialization and Safe for scripting.

On the last step, the Application Setup Wizard creates an .htm and a .cab file. You can then copy these two files to the target computer and double-click the .htm file to load the page and correctly register the server.

Because the business object will be installed in the Windows\System32\Occache directory by default, move it to the Windows\System32 directory and change the HKEY_CLASSES_ROOT\CLSID\<MyActiveXGUID>\InprocServer32 registry key to match the correct path.


Registering Business Objects on the Client for Use with DCOM

Custom business objects need to ensure that the client side can map their program name (ProgId) to an identifier (CLSID) that can be used over DCOM. For this reason, the ProgID of the DCOM object must be in the client-side registry and map to the class ID of the server-side business object. For the other supported protocols (HTTP, HTTPS, and in-process), this is not necessary.

For example, if you expose a server-side business object called MyBObj with a specific class ID, for instance, "{00112233-4455-6677-8899-00AABBCCDDEE}", make sure that the following entries are added to the client-side registry:

[HKEY_CLASSES_ROOT]
\MyBObj
    \Clsid
    (Default) "{00112233-4455-6677-8899-00AABBCCDDEE}"


Setting DCOM Stream Marshaling Format

A client computer using components from RDS 1.5 or earlier is not compatible with a server using components from RDS 2.0 or later. When using DCOM as the underlying protocol, the support for RDS 2.0 or later is more efficient in transporting Recordset objects. If your client is running components from RDS 1.5 or earlier, you can set your server to work with the previous RDS support (called RDS 1.0) or the newer RDS support (called RDS 2.0 or later). Set either of the following registry entries:

[HKEY_CLASSES_ROOT]
\CLSID
    \[58ECEE30-E715-11CF-B0E3-00AA003F000F}
        \ADTGOptions]"MarshalFormat"="RDS10"

-or-

[HKEY_CLASSES_ROOT]
\CLSID
    \[58ECEE30-E715-11CF-B0E3-00AA003F000F}
        \ADTGOptions]"MarshalFormat"="RDS20"


Enabling a DLL to Run on DCOM

The following steps outline how to enable a business object .dll to use both DCOM and Microsoft® Internet Information Services (HTTP) via Component Services.

  1. Create a new empty package in the Component Services MMC snap-in.

    You will use the Component Services MMC snap-in to create a package and add the DLL into this package. This makes the .dll accessible through DCOM, but it removes the accessibility through IIS. (If you check in the registry for the .dll, the Inproc key is now empty; setting the Activation attribute, explained later in this topic, adds a value in the Inproc key.)

  2. Install a business object into the package.

    -or-

    Import the RDSServer.DataFactory object into the package.

  3. Set the Activation attribute for the package to In the creator's process (Library application).

    To make the .dll accessible through DCOM and IIS on the same computer, you must set the component's Activation attribute in the Component Services MMC snap-in. After you set the attribute to In the creator's process, you will notice that an Inproc server key in the registry has been added that points to a Component Services surrogate .dll.

For more information about Component Services (or Microsoft® Transaction Service, if you are using Windows NT) and how to perform these steps, see the Component Services documentation or visit the Transaction Server Web site at http://www.microsoft.com/com/tech/mts.asp.


Configuring Virtual Servers on IIS

When creating virtual servers in Internet Information Services 4.0, the following two extra steps are needed in order to configure the virtual server to work with RDS:

  1. When setting up the server, check "Allow Execute Access."
  2. Move msadcs.dll to vroot\msadc, where vroot is the home directory of your virtual server.

See Also

RDS Fundamentals


Specifying Threads Per Processor on IIS

When using RDS with Internet Information Services 4.0 or later, the number of threads created per processor can be controlled by manipulating the registry on the Web server. The number of threads per processor can affect performance in a high traffic situation, or in low traffic situations with large query sizes. The user should experiment for best results.

The method used to determine and change the default value for this setting depends upon the configuration of the IIS 4.0 server.

With MDAC 2.1.2.4202.3 (GA) or later installed on the IIS server, RDS uses the same Component Services (or Microsoft Transaction Services, if you are using Windows NT) thread pool as ASP scripts use. The default value for the number of threads per processor is 10. To change the default, you must add the following key to the server registry:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\InetInfo\Parameters\MaxPoolThreads

where MaxPoolThreads is a REG_DWORD. MaxPoolThreads does not appear in the Registry unless it is specifically added. Valid values range from 5 to a recommended maximum of 20. If the value specified by the registry key is greater than the value of the PoolThreadLimit key (located under the same path), then PoolThreadLimit value is used.

Alternatively, if MDAC 2.1 2.1.1.3711.11 (GA) or earlier is installed on the IIS server, the default value for the number of threads per processor is 6. To change the default, you must add the following key to the registry on the IIS server:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\Parameters\ADCThreads

where ADCThreads is a REG_DWORD. ADCThreads does not appear in the Registry unless it is specifically added. The range of valid values is 1 to 50. If the value specified by the registry key is greater than 50, then the maximum value is used (50).

See Also

RDS Fundamentals


Securing RDS Applications

Microsoft Internet Explorer Security Issues

With new security enhancements added to Microsoft® Internet Explorer, some ADO and RDS objects are restricted to running only in a "safe" mode environment. This requires that you are aware of these issues, including different zones, security levels, restrictive behavior, unsafe operations, and customized security settings.

For more information about these issues, see ADO and RDS Security Issues in Microsoft Internet Explorer under ActiveX Data Objects (ADO) Technical Articles.

Security and Your Web Server

If you use the RDSServer.DataFactory object on your Internet Web server, remember that doing so creates a potential security risk. External users who obtain valid data source name (DSN), user ID, and password information could write pages to send any query to that data source. If you want more restricted access to a data source, one option is to unregister and delete the RDSServer.DataFactory object (msadcf.dll), and instead use custom business objects with hard-coded queries.

For more information on the security implications of using the RDSServer.DataFactory object, see the Microsoft Security Bulletin MS99-025 on the Microsoft Security Web site at http://www.microsoft.com/security/default.asp.

Client Impersonation and Security

If the Password Authentication property for your IIS Web server is set to Windows NT Challenge/Response authentication (for Windows NT 4.0) or to Integrated Windows authentication (for Windows 2000), then business objects are invoked under the client's security context. This is a new feature in RDS 1.5 that allows Client Impersonation over HTTP. When working in this mode, the logon to the Web server (IIS) is not anonymous but uses the user ID and password that the client computer is running under. If the ODBC DSNs are set up to use Trusted Connection, then access to databases, such as SQL Server, also happens under the client's security context. But this only works if the database is on the same computer as the IIS; the client credentials cannot be carried over to yet another computer.

For example, a client, John Doe, with userid="JohnD" and password="secret" is logged on to a client computer. He runs a browser-based application that needs to access the RDSServer.DataFactory object to create a Recordset by executing an SQL query on the "MyServer" computer running IIS. MyServer, a system running Windows NT Server 4.0, is set up to use Windows NT Challenge/Response authentication, its ODBC DSN has "Use Trusted Connection" selected, and the server also contains the SQL Server data source. When a request is received on the Web server, it asks the client for the user ID and password. Thus, the request is logged on MyServer as coming from "JohnD"/"Secret" instead of IUSER_MyServer (which is the default when Anonymous Password Authentication is on). Similarly, when logging on to SQL Server, "JohnD"/"Secret" is used.

Consequently, the IIS Windows NT Challenge/Response authentication mode allows HTML pages to be created without the user being explicitly prompted for the user ID and password information needed to log on to the database. If the IIS Basic Authentication were being used, then this also would be required.

Password Authentication

RDS can communicate with an IIS Web server running in any one of the three Password Authentication modes: Anonymous, Basic, or NT Challenge/Response authentication (called Integrated Windows authentication in Windows 2000). These settings define how a Web server controls access through it, such as requiring that a client computer have explicit access privileges on the NT Web server.


Configuring DataFactory for Safe or Unrestricted Modes

By default, ADO is installed with a "safe" RDSServer.DataFactory configuration. Safe mode for RDS Server components means that the following are true:

  1. Handler is required with the RDSServer.DataFactory (this is mandated by a registry key setting).
  2. The default handler, msdfmap.handler, is registered, present in the safe-handler list, and marked as the default handler.
  3. Msdfmap.ini file is installed in the Windows directory. You must configure this file according to your needs, before using RDS in three-tier mode.

Optionally, you can configure an unrestricted DataFactory installation. DataFactory can be used directly without the custom handler. Users can still use a custom handler by modifying the connection strings, but it is not required. For more information on the implications of using the RDSServer.DataFactory object, see Securing RDS Applications.

The registry file handsafe.reg has been provided to set up the handler registry entries for a safe configuration. To run in safe mode, run handsafe.reg. The registry file handunsf.reg has been provided to set up the handler registry entries for an unrestricted configuration. To run in unrestricted mode, run handunsf.reg.

After running either handsafe.reg or handunsf.reg, you must stop and restart the World Wide Web Publishing Service on the Web server by typing the following commands in a command window: "NET STOP W3SVC" and "NET START W3SVC".

For more information about using the customization handler feature of RDS, see the technical article Using the Customization Handler Feature in RDS 2.1.

See Also

DataFactory Customization | RDS Fundamentals


Using Related Technologies with RDS

This section contains specific information about using RDS with aspects of the following technologies:

  • ODBC Connection Pooling
  • Microsoft Component Services

Many other technologies interact with Remote Data Service or are used in its implementation.

Internet Information Services For more information about Microsoft Internet Information Services (IIS) or Active Server Pages (ASP), see the IIS Web site at http://www.microsoft.com/ntserver/web/exec/feature/Datasheet.asp.

Microsoft Component Services For more information about Component Services (known as Microsoft Transaction Server in Windows NT), see the Windows Web page at http://www.microsoft.com/windows/default.asp. For information about Microsoft Transaction Server, see the MTS Web site at http://www.microsoft.com/com/tech/mts.asp.

Microsoft SQL Server For more information about Microsoft® SQL Server, see the SQL Server Web site at http://www.microsoft.com/sql/.

Microsoft Internet Explorer For more information about Microsoft Internet Explorer, see the Internet Explorer Web page at http://www.microsoft.com/windows/ie/default.htm and the MSDN Online Web Workshop page at http://msdn.microsoft.com/workshop/default.asp.

Microsoft Windows NT Server/Windows 2000 Server For more information about security in Microsoft Windows NT Server or Windows 2000 Server, see the Windows Web page at http://www.microsoft.com/windows/default.asp.

See Also

RDS Fundamentals


Using RDS with ODBC Connection Pooling

If you're using an ODBC data source, you can use the connection pooling option in Internet Information Services (IIS) to achieve high performance handling of client load. Connection pooling is a resource manager for connections, maintaining the open state on frequently used connections.

To enable connection pooling, refer to the Internet Information Services documentation.

Please note that enabling connection pooling may subject the Web server to other restrictions, as noted in the Microsoft Internet Information Services documentation.

To ensure that connection pooling is stable and provides additional performance gains, you must configure Microsoft SQL Server to use the TCP/IP Socket network library.

To do this, you need to:

  • Configure the SQL Server computer to use TCP/IP Sockets.
  • Configure the Web server to use TCP/IP Sockets.

Configuring the SQL Server Computer to Use TCP/IP Sockets

On the SQL Server computer, run the SQL Server Setup program so that interactions with the data source use the TCP/IP Socket network library.

To specify the TCP/IP Socket network library on the SQL Server computer

In Microsoft SQL Server 6.5:

  1. From the Start menu, point to Programs, point to Microsoft SQL Server 6.5, and then click SQL Setup.
  2. Click Continue twice.
  3. In the Microsoft SQL Server —Options dialog box, select Change Network Support, and then click Continue.
  4. Make sure the TCP/IP Sockets check box is selected, and click OK.
  5. Click Continue to finish, and exit setup.

In Microsoft SQL Server 7.0:

  1. From the Start menu, point to Programs, point to Microsoft SQL Server 7.0, and then click Server Network Utility.
  2. On the General tab of the dialog box, click Add.
  3. In the Add Network Library Configuration dialog box, click TCP/IP.
  4. In the Port number and Proxy address boxes, enter the port number and proxy address provided by your network administrator.
  5. Click OK to finish, and exit setup.

Configuring the Web Server to Use TCP/IP Sockets

There are two options for configuring the Web server to use TCP/IP Sockets. What you do depends on whether all SQL Servers are accessed from the Web server, or only a specific SQL Server is accessed from the Web server.

If all SQL Servers are accessed from the Web server, you need to run the SQL Server Client Configuration Utility on the Web server computer. The following steps change the default network library for all SQL Server connections made from this IIS Web server to use the TCP/IP Sockets network library.

To configure the Web server (all SQL Servers)

For Microsoft SQL Server 6.5:

  1. From the Start menu, point to Programs, point to Microsoft SQL Server 6.5 , and then click SQL Client Configuration Utility.
  2. Click the Net Library tab.
  3. In the Default Network box, select TCP/IP Sockets.
  4. Click Done to save changes and exit the utility.

For Microsoft SQL Server 7.0:

  1. From the Start menu, point to Programs, point to Microsoft SQL Server 7.0 , and then click Client Network Utility.
  2. Click the General tab.
  3. In the Default network library box, click TCP/IP.
  4. Click OK to save changes and exit the utility.

If a specific SQL Server is accessed from a Web server, you need to run the SQL Server Client Configuration Utility on the Web server computer. To change the network library for a specific SQL Server connection, configure the SQL Server Client software on the Web server computer as follows.

To configure the Web server (a specific SQL Server)

For Microsoft SQL Server 6.5:

  1. From the Start menu, point to Programs, point to Microsoft SQL Server 6.5, and then click SQL Client Configuration Utility.
  2. Click the Advanced tab.
  3. In the Server box, type the name of the server to connect to using TCP/IP Sockets.
  4. In the DLL Name box, select TCP/IP Sockets.
  5. Click Add/Modify. All data sources pointing to this server will now use TCP/IP Sockets.
  6. Click Done.

For Microsoft SQL Server 7.0:

  1. From the Start menu, point to Programs, point to Microsoft SQL Server 7.0, and then click Client Configuration Utility.
  2. Click the General tab.
  3. Click Add.
  4. Enter the alias of the server in the Server alias box. In the Network libraries box, click TCP/IP. In the Computer name box, enter the computer name of the computer that listens for TCP/IP Sockets clients. In the Port number box, enter the port on which the SQL Server listens.
  5. Click OK, and then OK again to exit the utility.

See Also

RDS Fundamentals


Running Business Objects in Component Services

Business objects can be executable files (.exe) or dynamic-link libraries (.dll). The configuration you use to run the business object depends on whether the object is a .dll or .exe file:

  • Business objects created as .exe files can be called through DCOM. If these business objects are used through Internet Information Services (IIS), they are subject to additional marshaling of data, which will slow client performance.
  • Business objects created as .dll files can be used via IIS (and therefore HTTP). They can also be used over DCOM only via Component Services (or Microsoft Transaction Server, if you are using Windows NT). Business object DLLs will need to be registered on the IIS server computer to give you accessibility via IIS. (For steps on how to configure a DLL to run on DCOM, see the section, "Enabling a DLL to Run on DCOM.")

Note When business objects on the middle tier are implemented as Component Services components (using GetObjectContext, SetComplete, and SetAbort), they can use Component Services (or MTS, if you are using Windows NT) context objects to maintain their state across multiple client calls. This scenario is possible with DCOM, which is typically implemented between trusted clients and servers (an intranet). In this case, the RDS.DataSpace object and CreateObject method on the client side are replaced by the transaction context object and CreateInstance method (provided by the ITransactionContext interface), implemented by Component Services.

See Also

RDS Fundamentals


DataFactory Customization

Remote Data Service (RDS) provides a way to easily perform data access in a three-tier client/server system. A client data control specifies connection and command string parameters to perform a query on a remote data source, or connection string and Recordset object parameters to perform an update.

The parameters are passed to a server program, which performs the data-access operation on the remote data source. RDS provides a default server program called the RDSServer.DataFactory object. The RDSServer.DataFactory object returns any Recordset object produced by a query to the client.

However, the RDSServer.DataFactory is limited to performing queries and updates. It cannot perform any validation or processing on the connection or command strings.

With ADO, you can specify that the DataFactory work in conjunction with another type of server program called a handler. The handler can modify client connection and command strings before they are used to access the data source. In addition, the handler can enforce access rights, which govern the ability of the client to read and write data to the data source.

The parameters the handler uses to modify client parameters and access rights are specified in sections of a customization file.

See the following topics for more information about customizing the DataFactory object:

  • Understanding the Customization File
  • Customization File Connect Section
  • Customization File SQL Section
  • Customization File UserList Section
  • Customization File Logs Section
  • Required Client Settings
  • Writing Your Own Customized Handler


Understanding the Customization File

Each section header in the customization file consists of square brackets ([]) containing a type and parameter. The four section types are indicated by the literal strings connect, sql, userlist, or logs. The parameter is the literal string, the default, a user-specified identifier, or nothing.

Therefore, each section is marked with one of the following section headers:

[ connect   default      ]
[ connect   identifier   ]
[ sql         default      ]
[ sql         identifier   ]
[ userlist   identifier   ]
[ logs                  ]

The section headers have the following parts.

Part | Description
connect | A literal string that modifies a connection string.
sql | A literal string that modifies a command string.
userlist | A literal string that modifies the access rights of a specific user.
logs | A literal string that specifies a log file recording operational errors.
default | A literal string that is used if no identifier is specified or found.
identifier A string that matches a string in the connect or command string.
  • Use this section if the section header contains connect and the identifier string is found in the connection string.
  • Use this section if the section header contains sql and the identifier string is found in the command string.
  • Use this section if the section header contains userlist and the identifier string matches a connect section identifier.

The DataFactory calls the handler, passing client parameters. The handler searches for whole strings in the client parameters that match identifiers in the appropriate section headers. If a match is found, the contents of that section are applied to the client parameter.

A particular section is used under the following circumstances:

  • A connect section is used if the value part of the client connect string keyword, "Data Source=value", matches a connect section identifier.
  • An sql section is used if the client command string contains a string that matches an sql section identifier.
  • A connect or sql section with a default parameter is used if there is no matching identifier.
  • A userlist section is used if the userlist section identifier matches a connect section identifier. If there is a match, the contents of the userlist section are applied to the connection governed by the connect section.
  • If the string in a connection or command string does not match the identifier in any connect or sql section header, and there is no connect or sql section header with a default parameter, then the client string is used without modification.
  • The logs section is used whenever the DataFactory is in operation.

See Also

Customization File Connect Section | Customization File Logs Section | Customization File SQL Section | Customization File UserList Section | DataFactory Customization | Required Client Settings | Writing Your Own Customized Handler


Customization File Connect Section

The default behavior of the handler is to deny all connections. The connect section specifies exceptions to that behavior. For example, if all the connect sections were absent or empty, then by default no connections could be made.

The connect section can contain:

  • A default access entry that specifies the default read and write operations allowed on this connection. If there is no default access entry in the section, the section will be ignored.
  • A new connection string that replaces the client connection string.

Syntax

A default access entry is of the form:

Access=accessRight

A replacement connection string entry is of the form:

Connect=connectionString
Part | Description
Connect | A literal string that indicates this is a connection string entry.
connectionString | A string that replaces the whole client connection string.
Access | A literal string that indicates this is an access entry.
accessRight One of the following access rights:
  • NoAccess — User cannot access the data source.
  • ReadOnly — User can read the data source.
  • ReadWrite — User can read or write to the data source.

If you want to allow any connection (in effect, disabling the default handler behavior), set the access entry in the connect default section to Access=ReadWrite, and delete or comment out any other connect identifier section.

See Also

Customization File Logs Section | Customization File SQL Section | Customization File UserList Section | DataFactory Customization | Required Client Settings | Understanding the Customization File | Writing Your Own Customized Handler


Customization File SQL Section

The sql section can contain a new SQL string that replaces the client command string. If there is no SQL string in the section, the section will be ignored.

The new SQL string may be parameterized. That is, parameters in the sql section SQL string (designated by the '?' character) can be replaced by corresponding arguments in an identifier in the client command string (designated by a comma-delimited list in parentheses). The identifier and argument list behave like a function call.

For example, assume the client command string is "CustomerByID(4)", the SQL section header is [SQL CustomerByID], and the new SQL section string is "SELECT * FROM Customers WHERE CustomerID = ?". The Handler will generate "SELECT * FROM Customers WHERE CustomerID = 4" and use that string to query the data source.

If the new SQL statement is the null string (""), then the section is ignored.

If the new SQL statement string is not valid, then the execution of the statement will fail. The client parameter is effectively ignored. You can do this intentionally to "turn off" all client SQL commands by specifying:

[SQL default]
SQL = " "

Syntax

A replacement SQL string entry is of the form:

SQL=sqlString

Part | Description
SQL | A literal string that indicates this is an SQL section entry.
sqlString | An SQL string that replaces the client string.

See Also

Customization File Connect Section | Customization File Logs Section | Customization File UserList Section | DataFactory Customization | Required Client Settings | Understanding the Customization File | Writing Your Own Customized Handler


Customization File UserList Section

The userlist section pertains to the connect section with the same section identifier parameter.

This section can contain a user access entry, which specifies access rights for the specified user and overrides the default access entry in the matching connect section.

Syntax

A user access entry is of the form:

userName=accessRights

Part | Description
userName | The user name of the person employing this connection. Valid user names are established with the IIS Service Manager dialog.
accessRights One of the following access rights:
  • NoAccess — User cannot access the data source.
  • ReadOnly — User can read the data source.
  • ReadWrite — User can read or write to the data source.

See Also

Customization File Connect Section | Customization File Logs Section | Customization File SQL Section | DataFactory Customization | Required Client Settings | Understanding the Customization File | Writing Your Own Customized Handler


Customization File Logs Section

The logs section contains a log file entry, which specifies the name of a file that records errors during the operation of the DataFactory.

Syntax

A log file entry is of the form:

err=FileName
Part | Description
err | A literal string that indicates this is a log file entry.
FileName | A complete path and file name. The typical file name is c:\msdfmap.log.

The log file will contain the user name, HRESULT, date, and time of each error.

See Also

Customization File Connect Section | Customization File SQL Section | Customization File UserList Section | DataFactory Customization | Required Client Settings | Understanding the Customization File | Writing Your Own Customized Handler


Required Client Settings

Specify the following settings to use a custom DataFactory handler.

  • Specify "Provider=MS Remote" in the Connection object Provider property or the Connection object connection string "Provider=" keyword.
  • Set the CursorLocation property to adUseClient.
  • Specify the name of the handler to use in the RDS.DataControl object's Handler property, or the Recordset object's connection string "Handler=" keyword. (You cannot set the handler in the Connection object connect string.)
RDS provides a default handler on the server named MSDFMAP.Handler. (The default customization file is named MSDFMAP.INI.)

Example

Assume that the following sections in MSDFMAP.INI and the data source name, AdvWorks, have been previously defined:

[connect CustomerDataBase]
Access=ReadWrite
Connect="DSN=AdvWorks"

[sql CustomerById]
SQL="SELECT * FROM Customers WHERE CustomerID = ?"

The following code snippets are written in Visual Basic:

RDS.DataControl Version

Dim dc as New RDS.DataControl
Set dc.Handler = "MSDFMAP.Handler"
Set dc.Server = "http://yourServer"
Set dc.Connect = "Data Source=CustomerDatabase"
Set dc.SQL = "CustomerById(4)"
dc.Refresh

Recordset Version

Dim rs as New ADODB.Recordset
rs.CursorLocation = adUseClient

Specify either the Handler property or keyword; the Provider property or keyword; and the CustomerById and CustomerDatabase identifiers. Then open the Recordset object.

rs.Open "CustomerById(4)", "Handler=MSDFMAP.Handler;" & _
   "Provider=MS Remote;Data Source=CustomerDatabase;" & _
   "Remote Server=http://yourServer"

See Also

Customization File Connect Section | Customization File Logs Section | Customization File SQL Section | Customization File UserList Section | DataFactory Customization | Microsoft OLE DB Remoting Provider | Understanding the Customization File | Writing Your Own Customized Handler


Writing Your Own Customized Handler

You may want to write your own handler if you are an IIS server administrator who wants the default RDS support, but more control over user requests and access rights.

The MSDFMAP.Handler implements the IDataFactoryHandler interface.

IDataFactoryHandler Interface

This interface has two methods, GetRecordset and Reconnect. Both methods require that the CursorLocation property be set to adUseClient.

Both methods take arguments that appear after the first comma in the "Handler=" keyword. For example, "Handler=progid,arg1,arg2;" will pass an argument string of "arg1,arg2", and "Handler=progid" will pass a null argument.

GetRecordset Method

This method queries the data source and creates a new Recordset object using the arguments provided. The Recordset must be opened with adLockBatchOptimistic and must not be opened asynchronously.

Arguments

conn The connection string.

args The arguments for the handler.

query The command text for making a query.

ppRS The pointer where the Recordset should be returned.

Reconnect Method

This method updates the data source. It creates a new Connection object and attaches the given Recordset.

Arguments

conn The connection string.

args The arguments for the handler.

pRS A Recordset object.

msdfhdl.idl

This is the interface definition for IDataFactoryHandler that appears in the msdfhdl.idl file.

[
  uuid(D80DE8B3-0001-11d1-91E6-00C04FBBBFB3),
  version(1.0)
]
library MSDFHDL
{
    importlib("stdole32.tlb");
    importlib("stdole2.tlb");

    // TLib : Microsoft ActiveX Data Objects 2.0 Library
    // {00000200-0000-0010-8000-00AA006D2EA4}
    #ifdef IMPLIB
    importlib("implib\\x86\\release\\ado\\msado15.dll");
    #else
    importlib("msado20.dll");
    #endif

    [
      odl,
      uuid(D80DE8B5-0001-11d1-91E6-00C04FBBBFB3),
      version(1.0)
    ]
    interface IDataFactoryHandler : IUnknown
    {
HRESULT _stdcall GetRecordset(
      [in] BSTR conn,
      [in] BSTR args,
      [in] BSTR query,
      [out, retval] _Recordset **ppRS);

// DataFactory will use the ActiveConnection property
// on the Recordset after calling Reconnect.
   HRESULT _stdcall Reconnect(
      [in] BSTR conn,
      [in] BSTR args,
      [in] _Recordset *pRS);
    };
};

See Also

Customization File Connect Section | Customization File Logs Section | Customization File SQL Section | Customization File UserList Section | DataFactory Customization | Required Client Settings | Understanding the Customization File


Troubleshooting RDS

Refer to this section for solutions to specific errors or issues with RDS. The issues discussed in this section are:

  • Configuring RDS on Windows 2000
  • "Internet Server Error: Access Denied"
  • RDS Returns "Stream Not Read" Error
  • Deadlocks With Read Repeatable Isolation Level
  • Ensuring Sufficient TempDB Space
  • Minimizing Log File Space Usage

See Also

RDS Fundamentals


Configuring RDS on Windows 2000

If you experience difficulties getting RDS to function properly after upgrading to Windows 2000, follow the steps below to troubleshoot the issue.

  1. Make sure that the World Wide Web Publishing Service is running first by navigating to http://server using Internet Explorer. If you are unable to access the web server this way, go to a command prompt and enter the following command, "NET START W3SVC".
  2. From the Start menu, select Run. Type msdfmap.ini and click OK to open the msdfmap.ini file in Notepad. Check the [CONNECT DEFAULT] section, and if the ACCESS parameter is set to NOACCESS, change it to READONLY.
  3. Using the RegEdit utility, navigate to "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\DataFactory\HandlerInfo" and make sure HandlerRequired is set to 0 and DefaultHandler is "" (Null string).

    Note If you make any changes to this section of the registry, you must stop and restart the World Wide Web Publishing Service by entering the following commands at a command prompt: "NET STOP W3SVC" and "NET START W3SVC".

  4. Using the RegEdit utility, navigate in the registry to "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\Parameters\ADCLaunch" and verify that there is a key called RDSServer.Datafactory. If not, create it.
  5. Using Internet Services Manager, go to the Default Web Site and view the properties of the MSADC virtual root. Inspect the Directory Security/IP Address and Domain Name Restrictions. If the "Access is Denied" is checked then select "Granted".

Be sure to try rebooting the server if the changes to do not appear to solve the problem at first.

See Also

RDS Fundamentals


"Internet Server Error: Access Denied"

If you get this error, it usually means that Microsoft Internet Information Services (IIS) returned the following status:

HTTP_STATUS_DENIED 401

Make sure the directories accessed by IIS have proper permissions. RDS can communicate with an IIS Web server running in any one of the three Password Authentication modes: Anonymous, Basic, or NT Challenge/Response (called Integrated Windows authentication in Windows 2000). Also, the Web server must have permissions to the data source computer if it is a Windows NT/Windows 2000 computer.

See Also

RDS Fundamentals


RDS Returns "Stream Not Read" Error

"The Stream object could not be read because it is empty, or the current position is at the end of the Stream. For non-empty Streams, set the current position with the Position property. To determine if a Stream is empty, check the Size property."

If you get the error above, it may be a result of trying to use a parameterized hierarchical query over http. RDS does not permit you to remote parameterized hierarchies.

See Also

RDS Fundamentals


Deadlocks With Read Repeatable Isolation Level

If a custom business object uses an isolation level of read repeatable to access a SQL Server, and the business object is called simultaneously by two clients that send a query and update in the same transaction, a deadlock is possible. Remote Data Service is designed to allow one of the processes to time out to release the deadlock, but the update will fail for that client.

Use the Cursor Service Command Time Out dynamic property to modify the length of the timeout.

See Also

RDS Fundamentals


Ensuring Sufficient TempDB Space

If errors occur while handling Recordset objects that need processing space on Microsoft SQL Server 6.5, you may need to increase the size of the TempDB. (Some queries require temporary processing space; for example, a query with an ORDER BY clause requires a sort of the Recordset, which requires some temporary space.)

Important Read through this procedure before performing the actions because it isn't as easy to shrink a device once it is expanded.

Note By default in Microsoft SQL Server 7.0 and later, TempDB is set to automatically grow as needed. Therefore, this procedure may only be necessary on servers running versions earlier than 7.0.

To increase the TempDB space on SQL Server 6.5

  1. Start Microsoft® SQL Server Enterprise Manager, open the tree for the Server, and then open the Database Devices tree.
  2. Select a (physical) device to expand, such as Master, and double-click the device to open the Edit Database Device dialog box.

    This dialog box shows how much space the current databases are using.

  3. In the Size box, increase the device to the desired amount (for example, 50 megabytes (MB) of hard disk space).
  4. Click Change Now to increase the amount of space to which the (logical) TempDB can expand.
  5. Open the Databases tree on the server, and then double-click TempDB to open the Edit Database dialog box. The Database tab lists the amount of space currently allocated to TempDB (Data Size). By default, this is 2 MB.
  6. Under the Size group, click Expand. The graphs show the available and allocated space on each of the physical devices. The bars in maroon color represent available space.
  7. Select a Log Device, such as Master, to display the available size in the Size (MB) box.
  8. Click Expand Now to allocate that space to the TempDB database.

    The Edit Database dialog box displays the new allocated size for TempDB.

For more information about this topic, search the Microsoft SQL Server Enterprise Manager Help file for "Expand Database dialog box."

See Also

RDS Fundamentals


Minimizing Log File Space Usage

A log file may fill quickly (thus halting the server) if there is a large volume of activity on an SQL Server database. You can set the log file to Truncate on Checkpoint to significantly extend the life of the log file for a database.

To enable Truncate on Checkpoint in Microsoft SQL Server 6.5

  1. Start Microsoft SQL Server Enterprise Manager, open the tree for the Server, and then open the Database Devices tree.
  2. Double-click the name of the database on which this feature will be enabled.
  3. From the Database tab, select Truncate.
  4. From the Options tab, select Truncate Log on Checkpoint, and then click OK.

To enable Truncate on Checkpoint in Microsoft SQL Server 7.0

  1. Start Microsoft SQL Server Enterprise Manager, open the tree for the Server, and then open the Databases tree.
  2. Right-click the name of the database on which this feature will be enabled and choose Properties.
  3. From the Options tab, select Truncate Log on Checkpoint, and then click OK.

For more information about the Truncate on Checkpoint feature, see the Microsoft SQL Server documentation.

See Also

RDS Fundamentals


Section III: ADO (Multidimensional) (ADO MD)

This section contains the following chapter:

  • Chapter 14: ADO MD Fundamentals


Chapter 14: ADO MD Fundamentals

Microsoft® ActiveX® Data Objects (Multidimensional) (ADO MD) provides easy access to multidimensional data from languages such as Microsoft Visual Basic®, Microsoft Visual C++®, and Microsoft Visual J++®. ADO MD extends Microsoft ActiveX® Data Objects (ADO) to include objects specific to multidimensional data, such as the CubeDef and Cellset objects. With ADO MD you can browse multidimensional schema, query a cube, and retrieve the results.

Like ADO, ADO MD uses an underlying OLE DB provider to gain access to data. To work with ADO MD, the provider must be a multidimensional data provider (MDP) as defined by the OLE DB for OLAP specification. MDPs present data in multidimensional views as opposed to tabular data providers (TDPs) that present data in tabular views. Refer to the documentation for your OLAP OLE DB provider for more detailed information on the specific syntax and behaviors supported by your provider.

This document assumes a working knowledge of the Visual Basic programming language and a general knowledge of ADO and OLAP. For more information, see the ADO Programmer's Guide and the OLE DB for OLAP Programmer's Reference. For more overview information about ADO MD, see the following topics:

  • Overview of Multidimensional Schemas and Data
  • Working with Multidimensional Data
  • Using ADO with ADO MD
  • Programming with ADO MD
  • ADO MD Object Model

See Also

ADO MD Object Model | Microsoft ADO Programmer's Guide | Microsoft ADOX Programmer's Reference | Overview of Multidimensional Schemas and Data | Programming with ADO MD | Using ADO with ADO MD | Working with Multidimensional Data


Overview of Multidimensional Schemas and Data

Understanding Multidimensional Schemas

The central metadata object in ADO MD is the cube, which consists of a structured set of related dimensions, hierarchies, levels, and members.

A dimension is an independent category of data from your multidimensional database, derived from your business entities. A dimension typically contains items to be used as query criteria for the measures of the database.

A hierarchy is a path of aggregation of a dimension. A dimension may have multiple levels of granularity, which have parent-child relationships. A hierarchy defines how these levels are related.

A level is a step of aggregation in a hierarchy. For dimensions with multiple layers of information, each layer is a level.

A member is a data item in a dimension. Typically, you create a caption or describe a measure of the database using members.

Cubes are represented by CubeDef objects in ADO MD. Dimensions, hierarchies, levels, and members are also represented by their corresponding ADO MD objects: Dimension, Hierarchy, Level, and Member.

Dimensions

The dimensions of a cube depend on your business entities and types of data to be modeled in the database. Typically, each dimension is an independent entry point or mechanism for selecting data.

For example, a cube containing sales data has the following five dimensions: Salesperson, Geography, Time, Products, and Measures. The Measures dimension contains actual sales data values, while the other dimensions represent ways to categorize and group the sales data values.

The Geography dimension has the following set of members:

{All, North America, Europe, Canada, USA, UK, Germany, Canada-West,
Canada-East, USA-NW, USA-SW, USA-NE, USA-SE, England, Scotland,
Wales,Ireland, Germany-North, Germany-South, Ottawa, Toronto,
Vancouver, Calgary, Seattle, Boise, Los Angeles, Houston,
Shreveport, Miami, Boston, New York, London, Dover, Glasgow,
Edinburgh, Cardiff, Pembroke, Belfast, Londonderry, Berlin,
Hamburg, Munich, Stuttgart}

Hierarchies

Hierarchies define the ways in which the levels of a dimension can be "rolled up" or grouped. A dimension can have more than one hierarchy. A natural hierarchy exists in the Geography dimension:

Levels

In the example Geography dimension pictured in the previous figure, each box represents a level in the hierarchy.

Each level has a set of members, as follows:

  • The World = {All}
  • Continents = {North America, Europe}
  • Countries = {Canada, USA, UK, Germany}
  • Regions = {Canada-East, Canada-West, USA-NE, USA-NW, USA-SE, USA-SW, England, Ireland, Scotland, Wales, Germany-North, Germany-South}
  • Cities = {Ottawa, Toronto, Vancouver, Calgary, Seattle, Boise, Los Angeles, Houston, Shreveport, Miami, Boston, New York, London, Dover, Glasgow, Edinburgh, Cardiff, Pembroke, Belfast, Londonderry, Berlin, Hamburg, Munich, Stuttgart}

Members

Members at the leaf level of a hierarchy have no children, and members at the root level have no parent. 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}

Members can be consolidated along one or more hierarchies per dimension. Consider a Time dimension where there are two ways to roll up to the Year level from the Days level:

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.

See Also

ADO MD Object Model | Microsoft ADO MD Programmer's Reference | Programming with ADO MD | Using ADO with ADO MD | Working with Multidimensional Data


Working with Multidimensional Data

A cellset is the result of a query on multidimensional data. It consists of a collection of axes, usually no more than four axes and typically only two or three. An axis is a collection of members from one or more dimensions, which is used to locate or filter specific values in a cube.

A position is a point along an axis. For an axis consisting of a single dimension, these positions are a subset of the dimension members. If an axis consists of more than one dimension, then each position is a compound entity, which has n parts where n is the number of dimensions oriented along that axis. Each part of the position is a member from one constituent dimension.

For example, if the Geography and Product dimensions from a cube containing sales data are oriented along the x-axis of a cellset, a position along this axis may contain the members "USA" and "Computers." In this example, determining a position along the x-axis requires that members from each dimension are oriented along the axis.

A cell is an object positioned at the intersection of axis coordinates. Each cell has multiple pieces of information associated with it, including the data itself, a formatted string (the displayable form of cell data), and the cell ordinal value. (Each cell is a unique ordinal value in the cellset. The ordinal value of the first cell in the cellset is zero, while the leftmost cell in the second row of a cellset with eight columns would have an ordinal value of eight.)

For example, a cube has the following six dimensions (note that this cube schema differs slightly from the example given in Overview of Multidimensional Schemas and Data):

  • Salesperson
  • Geography (natural hierarchy)—Continents, Countries, States, and so on
  • Quarters—Quarters, Months, Days
  • Years
  • Measures—Sales, PercentChange, BudgetedSales
  • Products

The following cellset represents sales for 1991 for all products:

Note The cell values in the example can be viewed as ordered pairs of axis position ordinals where the first digit represents the x-axis position and the second digit the y-axis position.

The characteristics of this cellset are as follows:

  • Axis dimensions: Quarters, Salesperson, Geography
  • Filter dimensions: Measures, Years, Products
  • Two axes: COLUMN (x, or Axis 0) and ROW (y, or Axis 1)
  • x-axis: two nested dimensions, Salesperson and Geography
  • y-axis: Quarters dimension

The x-axis has two nested dimensions: Salesperson and Geography. From Geography, four members are selected: Seattle, Boston, USA-South, and Japan. Two members are selected from Salesperson: Valentine and Nash. This yields a total of eight positions on this axis (8 = 4*2).

Each coordinate is represented as a position with two members—one from the Salesperson dimension and another from the Geography dimension:

(Valentine, Seattle), (Valentine, Boston), (Valentine, USA_North),
(Valentine, Japan), (Nash, Seattle), (Nash, Boston), (Nash, USA_North),
(Nash, Japan)

The y-axis has only one dimension, containing the following eight positions:

Jan, Feb, Mar, Qtr2, Qtr3, Oct, Nov, Dec

Cellsets, cells, axes, and positions are all represented in ADO MD by corresponding objects: Cellset, Cell, Axis, and Position.

See Also

ADO MD Object Model | Microsoft ADO MD Programmer's Reference | Overview of Multidimensional Schemas and Data | Programming with ADO MD | Using ADO with ADO MD


Using ADO with ADO MD

ADO and ADO MD are related but separate object models. ADO provides objects for connecting to data sources, executing commands, retrieving tabular data and schema metadata in a tabular format, and viewing provider error information. ADO MD provides objects for retrieving multidimensional data and viewing multidimensional schema metadata.

When working with an MDP you may choose to use ADO, ADO MD, or both with your application. By referencing both libraries within your project, you will have full access to the functionality provided by your MDP.

It is often useful for consumers to get a flattened, tabular view of a multidimensional dataset. You can do this by using the ADO Recordset object. Specify the source for your Cellset as the Source parameter for the Open method of a Recordset, rather than as the source for an ADO MD Cellset.

It may also be useful to view the schema metadata in a tabular view rather than as a hierarchy of objects. The ADO OpenSchema method on the Connection object allows the user to open a Recordset containing schema information. The QueryType parameter of the OpenSchema method has several SchemaEnum values that relate specifically to MDPs. These values are:

  • adSchemaCubes
  • adSchemaDimensions
  • adSchemaHierarchies
  • adSchemaLevels
  • adSchemaMeasures
  • adSchemaMembers

To use ADO enum values with ADO MD properties or methods, your project must reference both the ADO and ADO MD libraries. For example, you can use the ADO adState enum values with the ADO MD State property. For more information about establishing references to libraries, see the documentation of your development tool.

For more information about the ADO objects and methods, see the ADO API Reference.

See Also

ADO MD Object Model | Microsoft ADO MD Programmer's Reference | Overview of Multidimensional Schemas and Data | Programming with ADO MD | Working with Multidimensional Data


Programming with ADO MD

To use ADO MD with your development tool, you should establish a reference to the ADO MD type library. The description of the ADO MD library is Microsoft ActiveX Data Objects (Multi-dimensional) Library. The ADO MD library filename is msadomd.dll, and the program ID (ProgID) is "ADOMD". For more information about establishing references to libraries, see the documentation of your development tool.

See Also

Microsoft ADO MD Programmer's Reference | Overview of Multidimensional Schemas and Data | Using ADO with ADO MD | Working with Multidimensional Data


Section IV: ADO Extensions for Data Definition Language and Security (ADOX)

This section contains the following chapter:

  • Chapter 15: ADOX Fundamentals


Chapter 15: ADOX Fundamentals

Microsoft® ActiveX® Data Objects Extensions for Data Definition Language and Security (ADOX) is an extension to the ADO objects and programming model. ADOX includes objects for schema creation and modification, as well as security. Because it is an object-based approach to schema manipulation, you can write code that will work against various data sources regardless of differences in their native syntaxes.

ADOX is a companion library to the core ADO objects. It exposes additional objects for creating, modifying, and deleting schema objects, such as tables and procedures. It also includes security objects to maintain users and groups and to grant and revoke permissions on objects.

To use ADOX with your development tool, you should establish a reference to the ADOX type library. The description of the ADOX library is "Microsoft ADO Ext. for DDL and Security." The ADOX library file name is Msadox.dll, and the program ID (ProgID) is "ADOX". For more information about establishing references to libraries, see the documentation of your development tool.

The Microsoft OLE DB Provider for the Microsoft Jet Database Engine fully supports ADOX. Certain features of ADOX may not be supported, depending on your data provider. For more information about supported features with the Microsoft OLE DB Provider for ODBC, the Microsoft OLE DB Provider for Oracle, or the Microsoft SQL Server OLE DB Provider, see the MDAC readme file.

This document assumes a working knowledge of the Microsoft® Visual Basic® programming language and a general knowledge of ADO. For more information about ADO, see the ADO Programmer's Guide. For more overview information about ADOX, see the following topics:

  • ADOX Object Model
  • ADOX Objects
  • ADOX Collections
  • ADOX Properties
  • ADOX Methods
  • ADOX Examples

See Also

ADOX API Reference | ADOX Code Examples | ADOX Collections | ADOX Enumerated Constants | ADOX Methods | ADOX Object Model | ADOX Objects | ADOX Properties | Microsoft ADO MD Programmer's Reference | ADO Programmer's Guide


Provider Support for ADOX

Certain features of ADOX are unsupported, depending upon your OLE DB data provider. ADOX is fully supported with the OLE DB Provider for Microsoft Jet. The unsupported features with the Microsoft OLE DB Provider for SQL Server, the Microsoft OLE DB Provider for ODBC, or the Microsoft OLE DB Provider for Oracle are listed below. ADOX is not supported by any other Microsoft OLE DB providers.

Microsoft OLE DB Provider for SQL Server

Object or Collection | Usage Restriction
Catalog object | The Create method is not supported.
Tables collection | Properties are read/write prior to object creation, and read-only when referencing an existing object.
Views collection | Views is not supported.
Procedures collection | The Append and Delete methods are not supported.
Procedure object | The Command property is not supported.
Keys collection | The Append and Delete methods are not supported.
Users collection | Users is not supported.
Groups collection | Groups is not supported.

Microsoft OLE DB Provider for ODBC

Object or Collection | Usage Restriction
Catalog object | The Create method is not supported.
Tables collection | The Append and Delete methods are not supported. Properties are read/write prior to object creation, and read-only when referencing an existing object.
Procedures collection | The Append and Delete methods are not supported.
Procedure object | The Command property is not supported.
Indexes collection | The Append and Delete methods are not supported.
Keys collection | The Append and Delete methods are not supported.
Users collection | Users is not supported.
Groups collection | Groups is not supported.

Microsoft OLE DB Provider for Oracle

Object or Collection | Usage Restriction
Catalog object | The Create method is not supported.
Tables collection | The Append and Delete methods are not supported. Properties are read/write prior to object creation, and read-only when referencing an existing object.
Views collection | The Append and Delete methods are not supported.
View object | The Command property is not supported.
Procedures object | The Append and Delete methods are not supported.
Procedure object | The Command property is not supported.
Indexes collection | The Append and Delete methods are not supported.
Keys collection | The Append and Delete methods are not supported.
Users collection | Users is not supported.
Groups collection | Groups is not supported.


Section V: Appendixes

The ADO Programmer's Guide contains the following appendixes:

  • Appendix A: Providers
  • Appendix B: ADO Errors
  • Appendix C: Programming with ADO
  • Appendix D: ADO Samples


Appendix A: Providers

This section addresses three kinds of providers: data providers, service providers, and service components. Providers fall into two categories: those providing data and those providing services. A data provider owns its own data and exposes it in tabular form to your application. A service provider encapsulates a service by producing and consuming data, augmenting features in your ADO applications. A service provider may also be further defined as a service component, which must work in conjunction with other service providers or components.

Data Providers

ADO is powerful and flexible because it can connect to any of several different data providers and still expose the same programming model, regardless of the specific features of any given provider.

However, because each data provider is unique, how your application interacts with ADO will vary slightly by data provider. The differences usually fall into one of three categories:

  • Connection parameters in the ConnectionString property.
  • Command object usage.
  • Provider-specific Recordset behavior.

Details for each of the data providers currently available from Microsoft are listed as follows.

Area | Topic
ODBC databases | Microsoft OLE DB Provider for ODBC
Microsoft Indexing Service | Microsoft OLE DB Provider for Microsoft Indexing Service
Microsoft Active Directory Service | Microsoft OLE DB Provider for Microsoft Active Directory Service
Microsoft Jet databases | OLE DB Provider for Microsoft Jet
Microsoft SQL Server | Microsoft OLE DB Provider for SQL Server
Oracle databases | Microsoft OLE DB Provider for Oracle
Internet Publishing | Microsoft OLE DB Provider for Internet Publishing
Simple data sources | Microsoft OLE DB Simple Provider

Provider-Specific Dynamic Properties

The Properties collections of the Connection, Command, and Recordset objects include dynamic properties specific to the provider. These properties provide information about functionality specific to the provider beyond the built-in properties that ADO supports.

After establishing the connection and creating these objects, use the Refresh method on the object's Properties collection to obtain the provider-specific properties. Refer to the provider documentation and the OLE DB Programmer's Reference for detailed information about these dynamic properties.

Service Providers

To use a service provider, you must supply a keyword. You should also be aware of the provider-specific dynamic properties associated with each service provider. Provider-specific details are listed for each of the service providers currently available from Microsoft:

  • Microsoft Data Shaping Service for OLE DB
  • Microsoft OLE DB Persistence Provider
  • Microsoft OLE DB Remoting Provider

Service Components

The Cursor Service for OLE DB service component supplements the cursor support functions of data providers. It also requires a keyword and has dynamic properties.

For more information about providers, see the documentation for Microsoft OLE DB in the Microsoft® Data Access Components SDK or visit the Microsoft Universal Data Access Web site.

See Also

Command Object | Connection Object | Microsoft OLE DB Provider for Internet Publishing | Microsoft OLE DB Provider for Microsoft Active Directory Service | Microsoft OLE DB Provider for Microsoft Indexing Service | Microsoft OLE DB Provider for ODBC | Microsoft OLE DB Provider for Oracle | Microsoft OLE DB Provider for SQL Server | OLE DB Provider for Microsoft Jet | Properties Collection | Recordset Object | Refresh Method (RDS)


Microsoft OLE DB Provider for ODBC

To an ADO or RDS programmer, an ideal world would be one in which every data source exposes an OLE DB interface, so that ADO could call directly into the data source. Although increasingly more database vendors are implementing OLE DB interfaces, some data sources are not yet exposed this way. However, virtually all DBMS systems in use today can be accessed through ODBC.

ODBC drivers are available for every major DBMS in use today, including Microsoft SQL Server™, Microsoft® Access (Microsoft Jet database engine), and Microsoft FoxPro®, in addition to non-Microsoft database products such as Oracle.

The Microsoft ODBC Provider, however, allows ADO to connect to any ODBC data source. The provider is free-threaded and Unicode enabled.

The provider supports transactions, although different DBMS engines offer different types of transaction support. For example, Microsoft Access supports nested transactions up to five levels deep.

This is the default provider for ADO, and all provider-dependent ADO properties and methods are supported.

Connection String Parameters

To connect to this provider, set the Provider= argument of the ConnectionString property to:

MSDASQL

Reading the Provider property will return this string as well.

Typical Connection String

A typical connection string for this provider is:

"Provider=MSDASQL;DSN=dsnName;UID=userName;PWD=userPassword;"

The string consists of these keywords:

Keyword | Description
Provider | Specifies the OLE DB Provider for ODBC.
DSN | Specifies the data source name.
UID | Specifies the user name.
PWD | Specifies the user password.
URL | Specifies the URL of a file or directory published in a Web folder.

Because this is the default provider for ADO, if you omit the Provider= parameter from the connection string, ADO will attempt to establish a connection to this provider.

The provider does not support any specific connection parameters in addition to those defined by ADO. However, the provider will pass any non-ADO connection parameters to the ODBC driver manager.

Because you can omit the Provider parameter, you can therefore compose an ADO connection string that is identical to an ODBC connection string for the same data source. Use the same parameter names (DRIVER=, DATABASE=, DSN=, and so on), values, and syntax as you would when composing an ODBC connection string. You can connect with or without a predefined data source name (DSN) or FileDSN.

Syntax with a DSN or FileDSN:

"[Provider=MSDASQL;] { DSN=name | FileDSN=filename } ; [DATABASE=database;] UID=user; PWD=password"

Syntax without a DSN (DSN-less connection):

"[Provider=MSDASQL;] DRIVER=driver; SERVER=server; DATABASE=database; UID=user; PWD=password"

If you use a DSN or FileDSN, it must be defined through the ODBC Data Source Administrator in the Windows Control Panel. In Microsoft Windows 2000®, the ODBC Administrator is located under Administrative Tools. In previous versions of Windows, the ODBC Administrator icon is named 32-bit ODBC or simply ODBC.

As an alternative to setting a DSN, you can specify the ODBC driver (DRIVER=), such as "SQL Server;" the server name (SERVER=); and the database name (DATABASE=).

You can also specify a user account name (UID=), and the password for the user account (PWD=) in the ODBC-specific parameters or in the standard ADO-defined user and password parameters.

Although a DSN definition already specifies a database, you can specify a database parameter in addition to a DSN to connect to a different database. It is a good idea to always include the database parameter when you use a DSN. This will ensure that you connect to the proper database in the event that another user changed the default database parameter since you last checked the DSN definition.

Provider-Specific Connection Properties

The OLE DB provider for ODBC adds several properties to the Properties collection of the Connection object. The following table lists these properties with the corresponding OLE DB property name in parentheses.

Property Name | Description
Accessible Procedures (KAGPROP_ACCESSIBLEPROCEDURES) | Indicates whether the user has access to stored procedures.
Accessible Tables (KAGPROP_ACCESSIBLETABLES) | Indicates whether the user has permission to execute SELECT statements against the database tables.
Active Statements (KAGPROP_ACTIVESTATEMENTS) | Indicates the number of handles an ODBC driver can support on a connection.
Driver Name (KAGPROP_DRIVERNAME) | Indicates the file name of the ODBC driver.
Driver ODBC Version (KAGPROP_DRIVERODBCVER) | Indicates the version of ODBC that this driver supports.
File Usage (KAGPROP_FILEUSAGE) | Indicates how the driver treats a file in a data source; as a table or as a catalog.
Like Escape Clause (KAGPROP_LIKEESCAPECLAUSE) | Indicates whether the driver supports the definition and use of an escape character for the percent character (%) and underline character (_) in the LIKE predicate of a WHERE clause.
Max Columns in Group By (KAGPROP_MAXCOLUMNSINGROUPBY) | Indicates the maximum number of columns that can be listed in the GROUP BY clause of a SELECT statement.
Max Columns in Index (KAGPROP_MAXCOLUMNSININDEX) | Indicates the maximum number of columns that can be included in an index.
Max Columns in Order By (KAGPROP_MAXCOLUMNSINORDERBY) | Indicates the maximum number of columns that can be listed in the ORDER BY clause of a SELECT statement.
Max Columns in Select (KAGPROP_MAXCOLUMNSINSELECT) | Indicates the maximum number of columns that can be listed in the SELECT portion of a SELECT statement.
Max Columns in Table (KAGPROP_MAXCOLUMNSINTABLE) | Indicates the maximum number of columns allowed in a table.
Numeric Functions (KAGPROP_NUMERICFUNCTIONS) | Indicates which numeric functions are supported by the ODBC driver. For a listing of function names and the associated values used in this bitmask, see Appendix E: Scalar Functions in the ODBC documentation.
Outer Join Capabilities (KAGPROP_OJCAPABILITY) | Indicates the types of OUTER JOINs supported by the provider.
Outer Joins (KAGPROP_OUTERJOINS) | Indicates whether the provider supports OUTER JOINs.
Special Characters (KAGPROP_SPECIALCHARACTERS) | Indicates which characters have special meaning for the ODBC driver.
Stored Procedures (KAGPROP_PROCEDURES) | Indicates whether stored procedures are available for use with this ODBC driver.
String Functions (KAGPROP_STRINGFUNCTIONS) | Indicates which string functions are supported by the ODBC driver. For a listing of function names and the associated values used in this bitmask, see Appendix E: Scalar Functions in the ODBC documentation.
System Functions (KAGPROP_SYSTEMFUNCTIONS) | Indicates which system functions are supported by the ODBC driver. For a listing of function names and the associated values used in this bitmask, see Appendix E: Scalar Functions in the ODBC documentation.
Time/Date Functions (KAGPROP_TIMEDATEFUNCTIONS) | Indicates which time and date functions are supported by the ODBC driver. For a listing of function names and the associated values used in this bitmask, see Appendix E: Scalar Functions in the ODBC documentation.
SQL Grammar Support (KAGPROP_ODBCSQLCONFORMANCE) | Indicates the SQL grammar that the ODBC driver supports.

Provider-Specific Recordset and Command Properties

The OLE DB provider for ODBC adds several properties to the Properties collection of the Recordset and Command objects. The following table lists these properties with the corresponding OLE DB property name in parentheses.

Property Name | Description
Query Based Updates/Deletes/Inserts (KAGPROP_QUERYBASEDUPDATES) | Indicates whether updates, deletions, and insertions can be performed using SQL queries.
ODBC Concurrency Type (KAGPROP_CONCURRENCY) | Indicates the method used to reduce potential problems caused by two users attempting to access the same data from the data source simultaneously.
BLOB accessibility on Forward-Only cursor (KAGPROP_BLOBSONFOCURSOR) | Indicates whether BLOB Fields can be accessed when using a forward-only cursor.
Include SQL_FLOAT, SQL_DOUBLE, and SQL_REAL in QBU WHERE clauses (KAGPROP_INCLUDENONEXACT) | Indicates whether SQL_FLOAT, SQL_DOUBLE, and SQL_REAL values can be included in a QBU WHERE clause.
Position on the last row after insert (KAGPROP_POSITIONONNEWROW) | Indicates that after a new record has been inserted in a table, the last row in the table will be come the current row.
IRowsetChangeExtInfo (KAGPROP_IROWSETCHANGEEXTINFO) | Indicates whether the IRowsetChange interface provides extended information support.
ODBC Cursor Type (KAGPROP_CURSOR) | Indicates the type of cursor used by the Recordset.
Generate a Rowset that can be marshaled (KAGPROP_MARSHALLABLE) | Indicates that the ODBC driver generates a recordset that can be marshaled

Command Text

How you use the Command object largely depends on the data source, and what type of query or command statement it will accept.

ODBC provides a specific syntax for calling stored procedures. For the CommandText property of a Command object, the CommandText argument to the Execute method on a Connection object, or the Source argument to the Open method on a Recordset object, passes in a string with this syntax:

"{ [ ? = ] call procedure [ ( ? [, ? [ , … ]] ) ] }"

Each ? references an object in the Parameters collection. The first ? references Parameters(0), the next ? references Parameters(1), and so on.

The parameter references are optional and depend on the structure of the stored procedure. If you want to call a stored procedure that defines no parameters, your string would look like this:

"{ call procedure }"

If you have two query parameters, your string would look like this:

"{ call procedure ( ?, ? ) }"

If the stored procedure will return a value, the return value is treated as another parameter. If you have no query parameters but you do have a return value, your string would look like this:

"{ ? = call procedure }"

Finally, if you have a return value and two query parameters, your string would look like this:

"{ ? = call procedure ( ?, ? ) }"

Recordset Behavior

The following tables list the standard ADO methods and properties available on a Recordset object opened with this provider.

For more detailed information about Recordset behavior for your provider configuration, run the Supports method and enumerate the Properties collection of the Recordset to determine whether provider-specific dynamic properties are present.

Availability of standard ADO Recordset properties:

Property | ForwardOnly | Dynamic | Keyset | Static
AbsolutePage | not available | not available | read/write | read/write
AbsolutePosition | not available | not available | read/write | read/write
ActiveConnection | read/write | read/write | read/write | read/write
BOF | read-only | read-only | read-only | read-only
Bookmark | not available | not available | read/write | read/write
CacheSize | read/write | read/write | read/write | read/write
CursorLocation | read/write | read/write | read/write | read/write
CursorType | read/write | read/write | read/write | read/write
EditMode | read-only | read-only | read-only | read-only
Filter | read/write | read/write | read/write | read/write
LockType | read/write | read/write | read/write | read/write
MarshalOptions | read/write | read/write | read/write | read/write
MaxRecords | read/write | read/write | read/write | read/write
PageCount | read/write | not available | read-only | read-only
PageSize | read/write | read/write | read/write | read/write
RecordCount | read/write | not available | read-only | read-only
Source | read/write | read/write | read/write | read/write
State | read-only | read-only | read-only | read-only
Status | read-only | read-only | read-only | read-only

The AbsolutePosition and AbsolutePage properties are write-only when ADO is used with version 1.0 of the Microsoft OLE DB Provider for ODBC.

Availability of standard ADO Recordset methods:

Method | ForwardOnly | Dynamic | Keyset | Static
AddNew | Yes | Yes | Yes | Yes
Cancel | Yes | Yes | Yes | Yes
CancelBatch | Yes | Yes | Yes | Yes
CancelUpdate | Yes | Yes | Yes | Yes
Clone | No | No | Yes | Yes
Close | Yes | Yes | Yes | Yes
Delete | Yes | Yes | Yes | Yes
GetRows | Yes | Yes | Yes | Yes
Move | Yes | Yes | Yes | Yes
MoveFirst | Yes | Yes | Yes | Yes
MoveLast | No | Yes | Yes | Yes
MoveNext | Yes | Yes | Yes | Yes
MovePrevious | No | Yes | Yes | Yes
NextRecordset* | Yes | Yes | Yes | Yes
Open | Yes | Yes | Yes | Yes
Requery | Yes | Yes | Yes | Yes
Resync | No | No | Yes | Yes
Supports | Yes | Yes | Yes | Yes
Update | Yes | Yes | Yes | Yes
UpdateBatch | Yes | Yes | Yes | Yes

*Not supported for Microsoft Access databases.

Dynamic Properties

The Microsoft OLE DB Provider for ODBC inserts several dynamic properties into the Properties collection of the unopened Connection, Recordset, and Command objects.

The tables below are a cross-index of the ADO and OLE DB names for each dynamic property. The OLE DB Programmer's Reference refers to an ADO property name by the term, "Description." You can find more information about these properties in the OLE DB Programmer's Reference. Search for the OLE DB property name in the Index or see Appendix C: OLE DB Properties.

Connection Dynamic Properties

The following properties are added to the Connection object's Properties collection.

ADO Property Name | OLE DB Property Name
Active Sessions | DBPROP_ACTIVESESSIONS
Asynchable Abort | DBPROP_ASYNCTXNABORT
Asynchable Commit | DBPROP_ASYNCTNXCOMMIT
Autocommit Isolation Levels | DBPROP_SESS_AUTOCOMMITISOLEVELS
Catalog Location | DBPROP_CATALOGLOCATION
Catalog Term | DBPROP_CATALOGTERM
Column Definition | DBPROP_COLUMNDEFINITION
Connect Timeout | DBPROP_INIT_TIMEOUT
Current Catalog | DBPROP_CURRENTCATALOG
Data Source | DBPROP_INIT_DATASOURCE
Data Source Name | DBPROP_DATASOURCENAME
Data Source Object Threading Model | DBPROP_DSOTHREADMODEL
DBMS Name | DBPROP_DBMSNAME
DBMS Version | DBPROP_DBMSVER
Extended Properties | DBPROP_INIT_PROVIDERSTRING
GROUP BY Support | DBPROP_GROUPBY
Heterogeneous Table Support | DBPROP_HETEROGENEOUSTABLES
Identifier Case Sensitivity | DBPROP_IDENTIFIERCASE
Initial Catalog | DBPROP_INIT_CATALOG
Isolation Levels | DBPROP_SUPPORTEDTXNISOLEVELS
Isolation Retention | DBPROP_SUPPORTEDTXNISORETAIN
Locale Identifier | DBPROP_INIT_LCID
Location | DBPROP_INIT_LOCATION
Maximum Index Size | DBPROP_MAXINDEXSIZE
Maximum Row Size | DBPROP_MAXROWSIZE
Maximum Row Size Includes BLOB | DBPROP_MAXROWSIZEINCLUDESBLOB
Maximum Tables in SELECT | DBPROP_MAXTABLESINSELECT
Mode | DBPROP_INIT_MODE
Multiple Parameter Sets | DBPROP_MULTIPLEPARAMSETS
Multiple Results | DBPROP_MULTIPLERESULTS
Multiple Storage Objects | DBPROP_MULTIPLESTORAGEOBJECTS
Multi-Table Update | DBPROP_MULTITABLEUPDATE
NULL Collation Order | DBPROP_NULLCOLLATION
NULL Concatenation Behavior | DBPROP_CONCATNULLBEHAVIOR
OLE DB Services | DBPROP_INIT_OLEDBSERVICES
OLE DB Version | DBPROP_PROVIDEROLEDBVER
OLE Object Support | DBPROP_OLEOBJECTS
Open Rowset Support | DBPROP_OPENROWSETSUPPORT
ORDER BY Columns in Select List | DBPROP_ORDERBYCOLUMNSINSELECT
Output Parameter Availability | DBPROP_OUTPUTPARAMETERAVAILABILITY
Password | DBPROP_AUTH_PASSWORD
Pass By Ref Accessors | DBPROP_BYREFACCESSORS
Persist Security Info | DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO
Persistent ID Type | DBPROP_PERSISTENTIDTYPE
Prepare Abort Behavior | DBPROP_PREPAREABORTBEHAVIOR
Prepare Commit Behavior | DBPROP_PREPARECOMMITBEHAVIOR
Procedure Term | DBPROP_PROCEDURETERM
Prompt | DBPROP_INIT_PROMPT
Provider Friendly Name | DBPROP_PROVIDERFRIENDLYNAME
Provider Name | DBPROP_PROVIDERFILENAME
Provider Version | DBPROP_PROVIDERVER
Read-Only Data Source | DBPROP_DATASOURCEREADONLY
Rowset Conversions on Command | DBPROP_ROWSETCONVERSIONSONCOMMAND
Schema Term | DBPROP_SCHEMATERM
Schema Usage | DBPROP_SCHEMAUSAGE
SQL Support | DBPROP_SQLSUPPORT
Structured Storage | DBPROP_STRUCTUREDSTORAGE
Subquery Support | DBPROP_SUBQUERIES
Table Term | DBPROP_TABLETERM
Transaction DDL | DBPROP_SUPPORTEDTXNDDL
User ID | DBPROP_AUTH_USERID
User Name | DBPROP_USERNAME
Window Handle | DBPROP_INIT_HWND

Recordset Dynamic Properties

The following properties are added to the Recordset object's Properties collection.

ADO Property Name | OLE DB Property Name
Access Order | DBPROP_ACCESSORDER
Blocking Storage Objects | DBPROP_BLOCKINGSTORAGEOBJECTS
Bookmark Type | DBPROP_BOOKMARKTYPE
Bookmarkable | DBPROP_IROWSETLOCATE
Change Inserted Rows | DBPROP_CHANGEINSERTEDROWS
Column Privileges | DBPROP_COLUMNRESTRICT
Column Set Notification | DBPROP_NOTIFYCOLUMNSET
Delay Storage Object Updates | DBPROP_DELAYSTORAGEOBJECTS
Fetch Backwards | DBPROP_CANFETCHBACKWARDS
Hold Rows | DBPROP_CANHOLDROWS
IAccessor | DBPROP_IAccessor
IColumnsInfo | DBPROP_IColumnsInfo
IColumnsRowset | DBPROP_IColumnsRowset
IConnectionPointContainer | DBPROP_IConnectionPointContainer
IConvertType | DBPROP_IConvertType
Immobile Rows | DBPROP_IMMOBILEROWS
IRowset | DBPROP_IRowset
IRowsetChange | DBPROP_IRowsetChange
IRowsetIdentity | DBPROP_IRowsetIdentity
IRowsetInfo | DBPROP_IRowsetInfo
IRowsetLocate | DBPROP_IRowsetLocate
IRowsetResynch |
IRowsetUpdate | DBPROP_IRowsetUpdate
ISequentialStream | DBPROP_ISequentialStream
ISupportErrorInfo | DBPROP_ISupportErrorInfo
Literal Bookmarks | DBPROP_LITERALBOOKMARKS
Literal Row Identity | DBPROP_LITERALIDENTITY
Maximum Open Rows | DBPROP_MAXOPENROWS
Maximum Pending Rows | DBPROP_MAXPENDINGROWS
Maximum Rows | DBPROP_MAXROWS
Notification Granularity | DBPROP_NOTIFICATIONGRANULARITY
Notification Phases | DBPROP_NOTIFICATIONPHASES
Objects Transacted | DBPROP_TRANSACTEDOBJECT
Own Changes Visible | DBPROP_OWNUPDATEDELETE
Own Inserts Visible | DBPROP_OWNINSERT
Preserve on Abort | DBPROP_ABORTPRESERVE
Preserve on Commit | DBPROP_COMMITPRESERVE
Quick Restart | DBPROP_QUICKRESTART
Reentrant Events | DBPROP_REENTRANTEVENTS
Remove Deleted Rows | DBPROP_REMOVEDELETED
Report Multiple Changes | DBPROP_REPORTMULTIPLECHANGES
Return Pending Inserts | DBPROP_RETURNPENDINGINSERTS
Row Delete Notification | DBPROP_NOTIFYROWDELETE
Row First Change Notification | DBPROP_NOTIFYROWFIRSTCHANGE
Row Insert Notification | DBPROP_NOTIFYROWINSERT
Row Privileges | DBPROP_ROWRESTRICT
Row Resynchronization Notification | DBPROP_NOTIFYROWRESYNCH
Row Threading Model | DBPROP_ROWTHREADMODEL
Row Undo Change Notification | DBPROP_NOTIFYROWUNDOCHANGE
Row Undo Delete Notification | DBPROP_NOTIFYROWUNDODELETE
Row Undo Insert Notification | DBPROP_NOTIFYROWUNDOINSERT
Row Update Notification | DBPROP_NOTIFYROWUPDATE
Rowset Fetch Position Change Notification | DBPROP_NOTIFYROWSETFETCHPOSISIONCHANGE
Rowset Release Notification | DBPROP_NOTIFYROWSETRELEASE
Scroll Backwards | DBPROP_CANSCROLLBACKWARDS
Skip Deleted Bookmarks | DBPROP_BOOKMARKSKIPPED
Strong Row Identity | DBPROP_STRONGITDENTITY
Unique Rows | DBPROP_UNIQUEROWS
Updatability | DBPROP_UPDATABILITY
Use Bookmarks | DBPROP_BOOKMARKS

Command Dynamic Properties

The following properties are added to the Command object's Properties collection.

ADO Property Name | OLE DB Property Name
Access Order | DBPROP_ACCESSORDER
Blocking Storage Objects | DBPROP_BLOCKINGSTORAGEOBJECTS
Bookmark Type | DBPROP_BOOKMARKTYPE
Bookmarkable | DBPROP_IROWSETLOCATE
Change Inserted Rows | DBPROP_CHANGEINSERTEDROWS
Column Privileges | DBPROP_COLUMNRESTRICT
Column Set Notification | DBPROP_NOTIFYCOLUMNSET
Delay Storage Object Updates | DBPROP_DELAYSTORAGEOBJECTS
Fetch Backwards | DBPROP_CANFETCHBACKWARDS
Hold Rows | DBPROP_CANHOLDROWS
IAccessor | DBPROP_IAccessor
IColumnsInfo | DBPROP_IColumnsInfo
IColumnsRowset | DBPROP_IColumnsRowset
IConnectionPointContainer | DBPROP_IConnectionPointContainer
IConvertType | DBPROP_IConvertType
Immobile Rows | DBPROP_IMMOBILEROWS
IRowset | DBPROP_IRowset
IRowsetChange | DBPROP_IRowsetChange
IRowsetIdentity | DBPROP_IRowsetIdentity
IRowsetInfo | DBPROP_IRowsetInfo
IRowsetLocate | DBPROP_IRowsetLocate
IRowsetResynch |
IRowsetUpdate | DBPROP_IRowsetUpdate
ISequentialStream | DBPROP_ISequentialStream
ISupportErrorInfo | DBPROP_ISupportErrorInfo
Literal Bookmarks | DBPROP_LITERALBOOKMARKS
Literal Row Identity | DBPROP_LITERALIDENTITY
Maximum Open Rows | DBPROP_MAXOPENROWS
Maximum Pending Rows | DBPROP_MAXPENDINGROWS
Maximum Rows | DBPROP_MAXROWS
Notification Granularity | DBPROP_NOTIFICATIONGRANULARITY
Notification Phases | DBPROP_NOTIFICATIONPHASES
Objects Transacted | DBPROP_TRANSACTEDOBJECT
Own Changes Visible | DBPROP_OWNUPDATEDELETE
Own Inserts Visible | DBPROP_OWNINSERT
Preserve on Abort | DBPROP_ABORTPRESERVE
Preserve on Commit | DBPROP_COMMITPRESERVE
Quick Restart | DBPROP_QUICKRESTART
Reentrant Events | DBPROP_REENTRANTEVENTS
Remove Deleted Rows | DBPROP_REMOVEDELETED
Report Multiple Changes | DBPROP_REPORTMULTIPLECHANGES
Return Pending Inserts | DBPROP_RETURNPENDINGINSERTS
Row Delete Notification | DBPROP_NOTIFYROWDELETE
Row First Change Notification | DBPROP_NOTIFYROWFIRSTCHANGE
Row Insert Notification | DBPROP_NOTIFYROWINSERT
Row Privileges | DBPROP_ROWRESTRICT
Row Resynchronization Notification | DBPROP_NOTIFYROWRESYNCH
Row Threading Model | DBPROP_ROWTHREADMODEL
Row Undo Change Notification | DBPROP_NOTIFYROWUNDOCHANGE
Row Undo Delete Notification | DBPROP_NOTIFYROWUNDODELETE
Row Undo Insert Notification | DBPROP_NOTIFYROWUNDOINSERT
Row Update Notification | DBPROP_NOTIFYROWUPDATE
Rowset Fetch Position Change Notification | DBPROP_NOTIFYROWSETFETCHPOSITIONCHANGE
Rowset Release Notification | DBPROP_NOTIFYROWSETRELEASE
Scroll Backwards | DBPROP_CANSCROLLBACKWARDS
Skip Deleted Bookmarks | DBPROP_BOOKMARKSKIP
Strong Row Identity | DBPROP_STRONGIDENTITY
Updatability | DBPROP_UPDATABILITY
Use Bookmarks | DBPROP_BOOKMARKS

See Also For details regarding specific implementation and functional information about the Microsoft OLE DB Provider for ODBC, consult the documentation for OLE DB Provider for ODBC and the Microsoft OLE DB Programmer's Reference available in the MDAC SDK or visit the Universal Data Access Web site.

See Also

Command Object | CommandText Property | Connection Object | ConnectionString Property | Execute Method (ADO Command) | Open Method (ADO Recordset) | Parameters Collection | Properties Collection | Provider Property | Recordset Object | Supports Method


Microsoft OLE DB Provider for Microsoft Indexing Service

The Microsoft OLE DB Provider for Microsoft Indexing Service provides programmatic read-only access to file system and Web data indexed by Microsoft Indexing Service. ADO applications can issue SQL queries to retrieve content and file property information.

The provider is free-threaded and unicode enabled.

Connection String Parameters

To connect to this provider, set the Provider= argument to the ConnectionString property to:

MSIDXS

Reading the Provider property will return this string as well.

Typical Connection String

A typical connection string for this provider is:

"Provider=MSIDXS;Data Source=myCatalog;Locale Identifier=nnnn;"

The string consists of these keywords:

Keyword | Description
Provider | Specifies the OLE DB Provider for Microsoft Indexing Service. Typically this is the only keyword specified in the connection string.
Data Source | Specifies the Indexing Service catalog name. If this keyword is not specified, the default system catalog is used.
Locale Identifier | Specifies a unique 32-bit number (for example, 1033) that specifies preferences related to the user's language. These preferences indicate how dates and times are formatted, items are sorted alphabetically, strings are compared, and so on. If this keyword is not specified, the default system locale identifier is used.

Command Text

The Indexing Service SQL query syntax consists of extensions to the SQL-92 SELECT statement and its FROM and WHERE clauses. The results of the query are returned via OLE DB rowsets, which can be consumed by ADO and manipulated as Recordset objects.

You can search for exact words or phrases, or use wildcards to search for patterns or stems of words. The search logic can be based on Boolean decisions, weighted terms, or proximity to other words. You can also search by "free text," which finds matches based on meaning, rather than exact words.

The specific command dialect is fully documented in the Microsoft Indexing Service Reference.

The provider does not accept stored procedure calls or simple table names (for example, the CommandType property will always be adCmdText).

Recordset Behavior

The following tables list the features available with a Recordset object opened with this provider. Only the Static cursor type (adOpenStatic) is available.

For more detailed information about Recordset behavior for your provider configuration, run the Supports method and enumerate the Properties collection of the Recordset to determine whether provider-specific dynamic properties are present.

Availability of standard ADO Recordset properties:

Property | Availability
AbsolutePage | read/write
AbsolutePosition | read/write
ActiveConnection | read-only
BOF | read-only
Bookmark* | read/write
CacheSize | read/write
CursorLocation | always adUseServer
CursorType | always adOpenStatic
EditMode | always adEditNone
EOF | read-only
Filter | read/write
LockType | read/write
MarshalOptions | not available
MaxRecords | read/write
PageCount | read-only
PageSize | read/write
RecordCount | read-only
Source | read/write
State | read-only
Status | read-only

*Bookmarks must be enabled on the provider in order for this feature to exist on the Recordset.

Availability of standard ADO Recordset methods:

Method | Available?
AddNew | No
Cancel | Yes
CancelBatch | No
CancelUpdate | No
Clone | Yes
Close | Yes
Delete | No
GetRows | Yes
Move | Yes
MoveFirst | Yes
NextRecordset | Yes
Open | Yes
Requery | Yes
Resync | Yes
Supports | Yes
Update | No
UpdateBatch | No

See Also For specific implementation details and functional information about the Microsoft OLE DB Provider for Microsoft Indexing Service, consult the Microsoft OLE DB Programmer's Reference and the Microsoft Indexing Service documentation, or visit the Microsoft Internet Information Services Web page.

See Also

CommandType Property | ConnectionString Property | Properties Collection | Provider Property | Recordset Object | Supports Method


Microsoft OLE DB Provider for Microsoft Active Directory Service

The Microsoft Active Directory Service Interfaces (ADSI) Provider allows ADO to connect to heterogeneous directory services through ADSI. This gives ADO applications read-only access to the Microsoft Windows NT® 4.0 and Microsoft Windows 2000 directory services, in addition to any LDAP-compliant directory service and Novell Directory Services. ADSI itself is based on a provider model, so if there is a new provider giving access to another directory, the ADO application will be able to access it seamlessly. The ADSI provider is free-threaded and unicode enabled.

Connection String Parameters

To connect to this provider, set the Provider argument of the ConnectionString property to:

ADSDSOObject

Reading the Provider property will return this string as well.

Typical Connection String

A typical connection string for this provider is:

"Provider=ADSDSOObject;User ID=userName;Password=userPassword;"

The string consists of these keywords:

Keyword | Description
Provider | Specifies the OLE DB Provider for Microsoft Active Directory Service.
User ID | Specifies the user name. If this keyword is omitted, then the current logon is used.
Password | Specifies the user password. If this keyword is omitted, then the current logon is used.

Command Text

A four-part command text string is recognized by the provider in the following syntax:

"Root; Filter; Attributes[; Scope]"
Value | Description
Root | Indicates the ADsPath object from which to start the search (that is, the root of the search).
Filter | Indicates the search filter in the RFC 960 format.
Attributes | Indicates a comma-delimited list of attributes to be returned.
Scope Optional. A String that specifies the scope of the search. Can be one of the following:
  • Base — Search only the base object (root of the search).
  • OneLevel — Search only one level.
  • Subtree — Search the entire subtree.

For example:

"<LDAP://DC=ArcadiaBay,DC=COM>;(objectClass=*);sn, givenName; subtree"

The provider also supports SQL SELECT for command text. For example:

"SELECT title, telephoneNumber From 'LDAP://DC=Microsoft, DC=COM' WHERE
objectClass='user' AND objectCategory='Person'"

The provider does not accept stored procedure calls or simple table names (for example, the CommandType property will always be adCmdText). See the Active Directory Service Interfaces documentation for a more complete description of the command text elements.

Recordset Behavior

The following tables list the features available on a Recordset object opened with this provider. Only the Static cursor type (adOpenStatic) is available.

For more detailed information about Recordset behavior for your provider configuration, run the Supports method and enumerate the Properties collection of the Recordset to determine whether provider-specific dynamic properties are present.

Availability of standard ADO Recordset properties:

Property | Availability
AbsolutePage | read/write
AbsolutePosition | read/write
ActiveConnection | read-only
BOF | read-only
Bookmark | read/write
CacheSize | read/write
CursorLocation | always adUseServer
CursorType | always adOpenStatic
EditMode | always adEditNone
EOF | read-only
Filter | read/write
LockType | read/write
MarshalOptions | not available
MaxRecords | read/write
PageCount | read-only
PageSize | read/write
RecordCount | read-only
Source | read/write
State | read-only
Status | read-only

Availability of standard ADO Recordset methods:

Method | Available?
AddNew | No
Cancel | No
CancelBatch | No
CancelUpdate | No
Clone | Yes
Close | Yes
Delete | No
GetRows | Yes
Move | Yes
MoveFirst | Yes
MoveLast | Yes
MoveNext | Yes
MovePrevious | Yes
NextRecordset | Yes
Open | Yes
Requery | Yes
Resync | Yes
Supports | Yes
Update | No
UpdateBatch | No

See Also For more general information about ADSI and the specifics of the provider, please refer to the Active Directory Service Interfaces documentation or visit the ADSI Web page.

See Also

CommandType Property | ConnectionString Property | Properties Collection | Provider Property | Recordset Object | Supports Method


Microsoft OLE DB Provider for Microsoft Jet

The OLE DB Provider for Microsoft Jet allows ADO to access Microsoft Jet databases.

Connection String Parameters

To connect to this provider, set the Provider argument of the ConnectionString property to:

Microsoft.Jet.OLEDB.4.0

Reading the Provider property will return this string as well.

Typical Connection String

A typical connection string for this provider is:

"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=databaseName;User ID=userName;Password=userPassword;"

The string consists of these keywords:

Keyword | Description
Provider | Specifies the OLE DB Provider for Microsoft Jet.
Data Source | Specifies the database path and file name (for example, c:\Northwind.mdb).
User ID | Specifies the user name. If this keyword is not specified, the string, "admin", is used by default.
Password | Specifies the user password. If this keyword is not specified, the empty string (""), is used by default.

Provider-Specific Connection Parameters

The OLE DB Provider for Microsoft Jet supports several provider-specific dynamic properties in addition to those defined by ADO. As with all other Connection parameters, they can be set via the Connection object's Properties collection or as part of the connection string.

The following table lists these properties with the corresponding OLE DB property name in parentheses.

Parameter | Description
Jet OLEDB:Compact Reclaimed Space Amount (DBPROP_JETOLEDB_COMPACTFREESPACESIZE) | Indicates an estimate of the amount of space, in bytes, that can be reclaimed by compacting the database. This value is only valid after a database connection has been established.
Jet OLEDB:Connection Control (DBPROP_JETOLEDB_CONNECTIONCONTROL) | Indicates whether users can connect to the database.
Jet OLEDB:Create System Database (DBPROP_JETOLEDB_CREATESYSTEMDATABASE) | Indicates whether a system database should be created when creating a new data source.
Jet OLEDB:Database Locking Mode (DBPROP_JETOLEDB_DATABASELOCKMODE) | Indicates the locking mode for this database. The first user to open the database determines the mode used while the database is open.
Jet OLEDB:Database Password (DBPROP_JETOLEDB_DATABASEPASSWORD) | Indicates the database password.
Jet OLEDB:Don't Copy Locale on Compact (DBPROP_JETOLEDB_COMPACT_DONTCOPYLOCALE) | Indicates whether Jet should copy locale information when compacting a database.
Jet OLEDB:Encrypt Database (DBPROP_JETOLEDB_ENCRYPTDATABASE) | Indicates whether a compacted database should be encrypted. If this property is not set, the compacted database will be encrypted if the original database was also encrypted.
Jet OLEDB:Engine Type (DBPROP_JETOLEDB_ENGINE) | Indicates the storage engine used to access the current data store.
Jet OLEDB:Exclusive Async Delay (DBPROP_JETOLEDB_EXCLUSIVEASYNCDELAY) | Indicates the maximum length of time, in milliseconds, that Jet can delay asynchronous writes to disk when the database is opened exclusively. This property is ignored unless Jet OLEDB:Flush Transaction Timeout is set to 0.
Jet OLEDB:Flush Transaction Timeout (DBPROP_JETOLEDB_FLUSHTRANSACTIONTIMEOUT) | Indicates the amount of time to wait before data stored in a cache for asynchronous writing is actually written to disk. This setting overrides the values for Jet OLEDB:Shared Async Delay and Jet OLEDB:Exclusive Async Delay.
Jet OLEDB:Global Bulk Transactions (DBPROP_JETOLEDB_GLOBALBULKNOTRANSACTIONS) | Indicates whether SQL bulk transactions are transacted.
Jet OLEDB:Global Partial Bulk Ops (DBPROP_JETOLEDB_GLOBALBULKPARTIAL) | Indicates the password used to open the database.
Jet OLEDB:Implicit Commit Sync (DBPROP_JETOLEDB_IMPLICITCOMMITSYNC) | Indicates whether changes made in internal implicit transactions are written in synchronous or asynchronous mode.
Jet OLEDB:Lock Delay (DBPROP_JETOLEDB_LOCKDELAY) | Indicates the number of milliseconds to wait before attempting to acquire a lock after a previous attempt has failed.
Jet OLEDB:Lock Retry (DBPROP_JETOLEDB_LOCKRETRY) | Indicates how many times an attempt to access a locked page is repeated.
Jet OLEDB:Max Buffer Size (DBPROP_JETOLEDB_MAXBUFFERSIZE) | Indicates the maximum amount of memory, in kilobytes, Jet can use before it starts flushing changes to disk.
Jet OLEDB:Max Locks Per File (DBPROP_JETOLEDB_MAXLOCKSPERFILE) | Indicates the maximum number of locks Jet can place on a database. The default value is 9500.
Jet OLEDB:New Database Password (DBPROP_JETOLEDB_NEWDATABASEPASSWORD) | Indicates the new password to be set for this database. The old password is stored in Jet OLEDB:Database Password.
Jet OLEDB:ODBC Command Time Out (DBPROP_JETOLEDB_ODBCCOMMANDTIMEOUT) | Indicates the number of milliseconds before a remote ODBC query from Jet will timeout.
Jet OLEDB:Page Locks to Table Lock (DBPROP_JETOLEDB_PAGELOCKSTOTABLELOCK) | Indicates how many pages need to be locked within a transaction before Jet attempts to promote the lock to a table lock. If this value is 0, then the lock is never promoted.
Jet OLEDB:Page Timeout (DBPROP_JETOLEDB_PAGETIMEOUT) | Indicates the number of milliseconds Jet will wait before checking to see if its cache is out of date with the database file.
Jet OLEDB:Recycle Long-Valued Pages (DBPROP_JETOLEDB_RECYCLELONGVALUEPAGES) | Indicates whether Jet should aggressively try to reclaim BLOB pages when they are freed.
Jet OLEDB:Registry Path (DBPROP_JETOLEDB_REGPATH) | Indicates the Windows registry key that contains values for the Jet database engine.
Jet OLEDB:Reset ISAM Stats (DBPROP_JETOLEDB_RESETISAMSTATS) | Indicates whether the schema Recordset DBSCHEMA_JETOLEDB_ISAMSTATS should reset its performance counters after returning performance information.
Jet OLEDB:Shared Async Delay (DBPROP_JETOLEDB_SHAREDASYNCDELAY) | Indicates the maximum amount of time, in milliseconds, Jet can delay asynchronous writes to disk when the database is opened in multi-user mode.
Jet OLEDB:System Database (DBPROP_JETOLEDB_SYSDBPATH) | Indicates the path and file name for the workgroup information file (system database).
Jet OLEDB:Transaction Commit Mode (DBPROP_JETOLEDB_TXNCOMMITMODE) | Indicates whether Jet writes data to disk synchronously or asynchronously when a transaction is committed.
Jet OLEDB:User Commit Sync (DBPROP_JETOLEDB_USERCOMMITSYNC) | Indicates whether changes made in transactions are written in synchronous or asynchronous mode.

Provider-Specific Recordset and Command Properties

The Jet provider also supports several provider-specific Recordset and Command properties. These properties are accessed and set through the Properties collection of the Recordset or Command object. The table lists the ADO property name and its corresponding OLE DB property name in parentheses.

Property Name | Description
Jet OLEDB:Bulk Transactions (DBPROP_JETOLEDB_BULKNOTRANSACTIONS) | Indicates whether SQL bulk operations are transacted. Large bulk operations might fail when transacted, due to resource delays.
Jet OLEDB:Enable Fat Cursors (DBPROP_JETOLEDB_ENABLEFATCURSOR) | Indicates whether Jet should cache multiple rows when populating a recordset for remote row sources.
Jet OLEDB:Fat Cursor Cache Size (DBPROP_JETOLEDB_FATCURSORMAXROWS) | Indicates the number of rows to cache when using remote data store row caching. This value is ignored unless Jet OLEDB:Enable Fat Cursors is True.
Jet OLEDB:Inconsistent (DBPROP_JETOLEDB_INCONSISTENT) | Indicates whether query results allow inconsistent updates.
Jet OLEDB:Locking Granularity (DBPROP_JETOLEDB_LOCKGRANULARITY) | Indicates whether a table is opened using row-level locking.
Jet OLEDB:ODBC Pass-Through Statement (DBPROP_JETOLEDB_ODBCPASSTHROUGH) | Indicates that Jet should pass the SQL text in a Command object to the back end unaltered.
Jet OLEDB:Partial Bulk Ops (DBPROP_JETOLEDB_BULKPARTIAL) | Indicates Jet's behavior when SQL DML operations fail.
Jet OLEDB:Pass Through Query Bulk-Op (DBPROP_JETOLEDB_PASSTHROUGHBULKOP) | Indicates whether queries that do not return a Recordset are passed unaltered to the data source.
Jet OLEDB:Pass Through Query Connect String (DBPROP_JETOLEDB_ODBCPASSTHROUGHCONNECTSTRING) | Indicates the Jet connect string used to connect to a remote data store. This value is ignored unless Jet OLEDB:ODBC Pass-Through Statement is True.
Jet OLEDB:Stored Query (DBPROP_JETOLEDB_STOREDQUERY) | Indicates whether the command text should be interpreted as a stored query instead of an SQL command.
Jet OLEDB:Validate Rules On Set (DBPROP_JETOLEDB_VALIDATEONSET) | Indicates whether the Jet validation rules are evaluated when column data is set or when the changes are committed to the database.

By default, the OLE DB Provider for Microsoft Jet opens Microsoft Jet databases in read/write mode. To open a database in read-only mode, set the Mode property on the ADO Connection object to adModeRead.

Command Object Usage

Command text in the Command object uses the Microsoft Jet SQL dialect. You can specify row-returning queries, action queries, and table names in the command text; however, stored procedures are not supported and should not be specified.

Recordset Behavior

The Microsoft Jet database engine does not support dynamic cursors. Therefore, the OLE DB Provider for Microsoft Jet does not support the adLockDynamic cursor type. When a dynamic cursor is requested, the provider will return a keyset cursor and reset the CursorType property to indicate the type of Recordset returned. Further, if an updatable Recordset is requested (LockType is adLockOptimistic, adLockBatchOptimistic, or adLockPessimistic) the provider will also return a keyset cursor and reset the CursorType property.

Dynamic Properties

The OLE DB Provider for Microsoft Jet inserts several dynamic properties into the Properties collection of the unopened Connection, Recordset, and Command objects.

The tables below are a cross-index of the ADO and OLE DB names for each dynamic property. The OLE DB Programmer's Reference refers to an ADO property name by the term, "Description." You can find more information about these properties in the OLE DB Programmer's Reference. Search for the OLE DB property name in the Index or see Appendix C: OLE DB Properties.

Connection Dynamic Properties

The following properties are added to the Connection object's Properties collection.

ADO Property Name | OLE DB Property Name
Active Sessions | DBPROP_ACTIVESESSIONS
Asynchable Abort | DBPROP_ASYNCTXNABORT
Asynchable Commit | DBPROP_ASYNCTNXCOMMIT
Autocommit Isolation Levels | DBPROP_SESS_AUTOCOMMITISOLEVELS
Catalog Location | DBPROP_CATALOGLOCATION
Catalog Term | DBPROP_CATALOGTERM
Column Definition | DBPROP_COLUMNDEFINITION
Current Catalog | DBPROP_CURRENTCATALOG
Data Source | DBPROP_INIT_DATASOURCE
Data Source Name | DBPROP_DATASOURCENAME
Data Source Object Threading Model | DBPROP_DSOTHREADMODEL
DBMS Name | DBPROP_DBMSNAME
DBMS Version | DBPROP_DBMSVER
GROUP BY Support | DBPROP_GROUPBY
Heterogeneous Table Support | DBPROP_HETEROGENEOUSTABLES
Identifier Case Sensitivity | DBPROP_IDENTIFIERCASE
Isolation Levels | DBPROP_SUPPORTEDTXNISOLEVELS
Isolation Retention | DBPROP_SUPPORTEDTXNISORETAIN
Locale Identifier | DBPROP_INIT_LCID
Maximum Index Size | DBPROP_MAXINDEXSIZE
Maximum Row Size | DBPROP_MAXROWSIZE
Maximum Row Size Includes BLOB | DBPROP_MAXROWSIZEINCLUDESBLOB
Maximum Tables in SELECT | DBPROP_MAXTABLESINSELECT
Mode | DBPROP_INIT_MODE
Multiple Parameter Sets | DBPROP_MULTIPLEPARAMSETS
Multiple Results | DBPROP_MULTIPLERESULTS
Multiple Storage Objects | DBPROP_MULTIPLESTORAGEOBJECTS
Multi-Table Update | DBPROP_MULTITABLEUPDATE
NULL Collation Order | DBPROP_NULLCOLLATION
NULL Concatenation Behavior | DBPROP_CONCATNULLBEHAVIOR
OLE DB Version | DBPROP_PROVIDEROLEDBVER
OLE Object Support | DBPROP_OLEOBJECTS
Open Rowset Support | DBPROP_OPENROWSETSUPPORT
ORDER BY Columns in Select List | DBPROP_ORDERBYCOLUMNSINSELECT
Output Parameter Availability | DBPROP_OUTPUTPARAMETERAVAILABILITY
Pass By Ref Accessors | DBPROP_BYREFACCESSORS
Password | DBPROP_AUTH_PASSWORD
Persistent ID Type | DBPROP_PERSISTENTIDTYPE
Prepare Abort Behavior | DBPROP_PREPAREABORTBEHAVIOR
Prepare Commit Behavior | DBPROP_PREPARECOMMITBEHAVIOR
Procedure Term | DBPROP_PROCEDURETERM
Prompt | DBPROP_INIT_PROMPT
Provider Friendly Name | DBPROP_PROVIDERFRIENDLYNAME
Provider Name | DBPROP_PROVIDERFILENAME
Provider Version | DBPROP_PROVIDERVER
Read-Only Data Source | DBPROP_DATASOURCEREADONLY
Rowset Conversions on Command | DBPROP_ROWSETCONVERSIONSONCOMMAND
Schema Term | DBPROP_SCHEMATERM
Schema Usage | DBPROP_SCHEMAUSAGE
SQL Support | DBPROP_SQLSUPPORT
Structured Storage | DBPROP_STRUCTUREDSTORAGE
Subquery Support | DBPROP_SUBQUERIES
Table Term | DBPROP_TABLETERM
Transaction DDL | DBPROP_SUPPORTEDTXNDDL
User ID | DBPROP_AUTH_USERID
User Name | DBPROP_USERNAME
Window Handle | DBPROP_INIT_HWND

Recordset Dynamic Properties

The following properties are added to the Recordset object's Properties collection.

ADO Property Name | OLE DB Property Name
Access Order | DBPROP_ACCESSORDER
Append-Only Rowset | DBPROP_APPENDONLY
Blocking Storage Objects | DBPROP_BLOCKINGSTORAGEOBJECTS
Bookmark Type | DBPROP_BOOKMARKTYPE
Bookmarkable | DBPROP_IROWSETLOCATE
Bookmarks Ordered | DBPROP_ORDEREDBOOKMARKS
Cache Deferred Columns | DBPROP_CACHEDEFERRED
Change Inserted Rows | DBPROP_CHANGEINSERTEDROWS
Column Privileges | DBPROP_COLUMNRESTRICT
Column Set Notification | DBPROP_NOTIFYCOLUMNSET
Column Writable | DBPROP_MAYWRITECOLUMN
Defer Column | DBPROP_DEFERRED
Delay Storage Object Updates | DBPROP_DELAYSTORAGEOBJECTS
Fetch Backwards | DBPROP_CANFETCHBACKWARDS
Hold Rows | DBPROP_CANHOLDROWS
IAccessor | DBPROP_IAccessor
IColumnsInfo | DBPROP_IColumnsInfo
IColumnsRowset | DBPROP_IColumnsRowset
IConnectionPointContainer | DBPROP_IConnectionPointContainer
IConvertType | DBPROP_IConvertType
ILockBytes | DBPROP_ILockBytes
Immobile Rows | DBPROP_IMMOBILEROWS
IRowset | DBPROP_IRowset
IRowsetChange | DBPROP_IRowsetChange
IRowsetIdentity | DBPROP_IRowsetIdentity
IRowsetIndex | DBPROP_IRowsetIndex
IRowsetInfo | DBPROP_IRowsetInfo
IRowsetLocate | DBPROP_IRowsestLocate
IRowsetResynch |
IRowsetScroll | DBPROP_IRowsetScroll
IRowsetUpdate | DBPROP_IRowsetUpdate
ISequentialStream | DBPROP_ISequentialStream
IStorage | DBPROP_IStorage
IStream | DBPROP_IStream
ISupportErrorInfo | DBPROP_ISupportErrorInfo
Literal Bookmarks | DBPROP_LITERALBOOKMARKS
Literal Row Identity | DBPROP_LITERALIDENTITY
Maximum Open Rows | DBPROP_MAXOPENROWS
Maximum Pending Rows | DBPROP_MAXPENDINGROWS
Maximum Rows | DBPROP_MAXROWS
Memory Usage | DBPROP_MEMORYUSAGE
Notification Granularity | DBPROP_NOTIFICATIONGRANULARITY
Notification Phases | DBPROP_NOTIFICATIONPHASES
Objects Transacted | DBPROP_TRANSACTEDOBJECT
Others' Changes Visible | DBPROP_OTHERUPDATEDELETE
Others' Inserts Visible | DBPROP_OTHERINSERT
Own Changes Visible | DBPROP_OWNUPDATEDELETE
Own Inserts Visible | DBPROP_OWNINSERT
Preserve on Abort | DBPROP_ABORTPRESERVE
Preserve on Commit | DBPROP_COMMITPRESERVE
Quick Restart | DBPROP_QUICKRESTART
Reentrant Events | DBPROP_REENTRANTEVENTS
Remove Deleted Rows | DBPROP_REMOVEDELETED
Report Multiple Changes | DBPROP_REPORTMULTIPLECHANGES
Return Pending Inserts | DBPROP_RETURNPENDINGINSERTS
Row Delete Notification | DBPROP_NOTIFYROWDELETE
Row First Change Notification | DBPROP_NOTIFYROWFIRSTCHANGE
Row Insert Notification | DBPROP_NOTIFYROWINSERT
Row Privileges | DBPROP_ROWRESTRICT
Row Resynchronization Notification | DBPROP_NOTIFYROWRESYNCH
Row Threading Model | DBPROP_ROWTHREADMODEL
Row Undo Change Notification | DBPROP_NOTIFYROWUNDOCHANGE
Row Undo Delete Notification | DBPROP_NOTIFYROWUNDODELETE
Row Undo Insert Notification | DBPROP_NOTIFYROWUNDOINSERT
Row Update Notification | DBPROP_NOTIFYROWUPDATE
Rowset Fetch Position Change Notification | DBPROP_NOTIFYROWSETFETCHPOSISIONCHANGE
Rowset Release Notification | DBPROP_NOTIFYROWSETRELEASE
Scroll Backwards | DBPROP_CANSCROLLBACKWARDS
Skip Deleted Bookmarks | DBPROP_BOOKMARKSKIPPED
Strong Row Identity | DBPROP_STRONGITDENTITY
Updatability | DBPROP_UPDATABILITY
Use Bookmarks | DBPROP_BOOKMARKS

Command Dynamic Properties

The following properties are added to the Command object's Properties collection.

ADO Property Name | OLE DB Property Name
Access Order | DBPROP_ACCESSORDER
Append-Only Rowset | DBPROP_APPENDONLY
Blocking Storage Objects | DBPROP_BLOCKINGSTORAGEOBJECTS
Bookmark Type | DBPROP_BOOKMARKTYPE
Bookmarkable | DBPROP_IROWSETLOCATE
Change Inserted Rows | DBPROP_CHANGEINSERTEDROWS
Column Privileges | DBPROP_COLUMNRESTRICT
Column Set Notification | DBPROP_NOTIFYCOLUMNSET
Defer Column | DBPROP_DEFERRED
Delay Storage Object Updates | DBPROP_DELAYSTORAGEOBJECTS
Fetch Backwards | DBPROP_CANFETCHBACKWARDS
Hold Rows | DBPROP_CANHOLDROWS
IAccessor | DBPROP_IAccessor
IColumnsInfo | DBPROP_IColumnsInfo
IColumnsRowset | DBPROP_IColumnsRowset
IConnectionPointContainer | DBPROP_IConnectionPointContainer
IConvertType | DBPROP_IConvertType
ILockBytes | DBPROP_ILockBytes
Immobile Rows | DBPROP_IMMOBILEROWS
IRowset | DBPROP_IRowset
IRowsetChange | DBPROP_IRowsetChange
IRowsetIdentity | DBPROP_IRowsetIdentity
IRowsetIndex | DBPROP_IRowsetIndex
IRowsetInfo | DBPROP_IRowsetInfo
IRowsetLocate | DBPROP_IRowsetLocate
IRowsetResynch |
IRowsetScroll | DBPROP_IRowsetScroll
IRowsetUpdate | DBPROP_IRowsetUpdate
ISequentialStream | DBPROP_ISequentialStream
IStorage | DBPROP_IStorage
IStream | DBPROP_IStream
ISupportErrorInfo | DBPROP_ISupportErrorInfo
Literal Bookmarks | DBPROP_LITERALBOOKMARKS
Literal Row Identity | DBPROP_LITERALIDENTITY
Lock Mode | DBPROP_LOCKMODE
Maximum Open Rows | DBPROP_MAXOPENROWS
Maximum Pending Rows | DBPROP_MAXPENDINGROWS
Maximum Rows | DBPROP_MAXROWS
Notification Granularity | DBPROP_NOTIFICATIONGRANULARITY
Notification Phases | DBPROP_NOTIFICATIONPHASES
Objects Transacted | DBPROP_TRANSACTEDOBJECT
Others' Changes Visible | DBPROP_OTHERUPDATEDELETE
Others' Inserts Visible | DBPROP_OTHERINSERT
Own Changes Visible | DBPROP_OWNUPDATEDELETE
Own Inserts Visible | DBPROP_OWNINSERT
Preserve on Abort | DBPROP_ABORTPRESERVE
Preserve on Commit | DBPROP_COMMITPRESERVE
Quick Restart | DBPROP_QUICKRESTART
Reentrant Events | DBPROP_REENTRANTEVENTS
Remove Deleted Rows | DBPROP_REMOVEDELETED
Report Multiple Changes | DBPROP_REPORTMULTIPLECHANGES
Return Pending Inserts | DBPROP_RETURNPENDINGINSERTS
Row Delete Notification | DBPROP_NOTIFYROWDELETE
Row First Change Notification | DBPROP_NOTIFYROWFIRSTCHANGE
Row Insert Notification | DBPROP_NOTIFYROWINSERT
Row Privileges | DBPROP_ROWRESTRICT
Row Resynchronization Notification | DBPROP_NOTIFYROWRESYNCH
Row Threading Model | DBPROP_ROWTHREADMODEL
Row Undo Change Notification | DBPROP_NOTIFYROWUNDOCHANGE
Row Undo Delete Notification | DBPROP_NOTIFYROWUNDODELETE
Row Undo Insert Notification | DBPROP_NOTIFYROWUNDOINSERT
Row Update Notification | DBPROP_NOTIFYROWUPDATE
Rowset Fetch Position Change Notification | DBPROP_NOTIFYROWSETFETCHPOSITIONCHANGE
Rowset Release Notification | DBPROP_NOTIFYROWSETRELEASE
Scroll Backwards | DBPROP_CANSCROLLBACKWARDS
Server Data on Insert | DBPROP_SERVERDATAONINSERT
Skip Deleted Bookmarks | DBPROP_BOOKMARKSKIP
Strong Row Identity | DBPROP_STRONGIDENTITY
Updatability | DBPROP_UPDATABILITY
Use Bookmarks | DBPROP_BOOKMARKS

See Also For specific implementation details and functional information about the OLE DB Provider for Microsoft Jet, consult the OLE DB Provider for Microsoft Jet documentation in the MDAC SDK.

See Also

ConnectionString Property | Provider Property | Recordset Object

OLE DB Provider for Microsoft Jet in the OLE DB documentation.


Microsoft OLE DB Provider for SQL Server

The Microsoft OLE DB Provider for SQL Server, SQLOLEDB, allows ADO to access Microsoft SQL Server.

Connection String Parameters

To connect to this provider, set the Provider argument to the ConnectionString property to:

SQLOLEDB

This value can also be set or read using the Provider property.

Typical Connection String

A typical connection string for this provider is:

"Provider=SQLOLEDB;Data Source=serverName;Initial Catalog=databaseName; User ID=userName;Password=userPassword;"

The string consists of these keywords:

Keyword | Description
Provider | Specifies the OLE DB Provider for SQL Server.
Data Source or Server | Specifies the name of a server.
Initial Catalog or Database | Specifies the name of a database on the server.
User ID or uid | Specifies the user name (for SQL Server Authentication).
Password or pwd | Specifies the user password (for SQL Server Authentication).

Provider-Specific Connection Parameters

The provider supports several provider-specific connection parameters in addition to those defined by ADO. As with the ADO connection properties, these provider-specific properties can be set via the Properties collection of a Connection or can be set as part of the ConnectionString.

Parameter | Description
Trusted_Connection | Indicates the user authentication mode. This can be set to Yes or No. The default value is No. If this property is set to Yes, then SQLOLEDB uses Microsoft Windows NT Authentication Mode to authorize user access to the SQL Server database specified by the Location and Datasource property values. If this property is set to No, then SQLOLEDB uses Mixed Mode to authorize user access to the SQL Server database. The SQL Server login and password are specified in the User Id and Password properties.
Current Language | Indicates a SQL Server language name. Identifies the language used for system message selection and formatting. The language must be installed on the SQL Server, otherwise opening the connection will fail.
Network Address | Indicates the network address of the SQL Server specified by the Location property.
Network Library | Indicates the name of the network 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.
Use Procedure for Prepare | Determines whether SQL Server creates temporary stored procedures when Commands are prepared (by the Prepared property).
Auto Translate | Indicates whether OEM/ANSI characters are converted. This property can be set to True or False. The default value is True. If this property is set to True, then SQLOLEDB performs OEM/ANSI character conversion when multi-byte character strings are retrieved from, or sent to, the SQL Server. If this property is set to False, then SQLOLEDB does not perform OEM/ANSI character conversion on multi-byte character string data.
Packet Size | Indicates 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.
Application Name | Indicates the client application name.
Workstation ID | A string identifying the workstation.

Command Object Usage

SQLOLEDB accepts an amalgam of ODBC, ANSI, and SQL Server-specific Transact-SQL as 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 a SQL Server stored procedure using a 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 the preferred command text over the Transact-SQL form:

ODBC SQL

{call SalesByCategory('Produce', '1995')}

Transact-SQL

EXECUTE SalesByCategory 'Produce', '1995'

SQL Server 2000 Features

With SQL Server 2000, ADO can use XML for Command input and retrieve results in XML stream format rather than in Recordset objects. For more information, see Using Streams for Command Input and Retrieving Resultsets Into Streams.

Accessing sql_variant data using MDAC 2.6

Microsoft SQL Server 2000 introduces a new data type called sql_variant. Similar to OLE DB's DBTYPE_VARIANT, the sql_variant data type can store data of several different types. However, there are a few key differences between DBTYPE_VARIANT and sql_variant. ADO also handles data stored as a sql_variant value differently than how it handles other data types. The following list describes issues to consider when accessing SQL Server data stored in columns of type sql_variant.

  • The MDAC 2.6 OLE DB Provider for SQL Server is the only provider that supports the sql_variant type. The OLE DB Provider for ODBC does not.
  • The sql_variant type does not exactly match the DBTYPE_VARIANT data type. The sql_variant type supports a few new subtypes not supported by DBTYPE_VARIANT, including GUID, ANSI (non-UNICODE) strings, and BIGINT. Using subtypes other than those listed above will work correctly.
  • The sql_variant subtype NUMERIC does not match the DBTYPE_DECIMAL in size.
  • Multiple data type coercions will result in types that do not match. For example, coercing a sql_variant with a subtype of GUID to a DBTYPE_VARIANT will result in a subtype of safearray(bytes). Converting this type back to a sql_variant will result in a new subtype of array(bytes).
  • Recordset fields containing sql_variant data can be remoted (marshaled) or persisted only if the sql_variant contains specific subtypes. Attempting to remote or persist data with the following unsupported subtypes will cause a run-time error (unsupported conversion) from the Microsoft Persistence Provider (MSPersist): VT_VARIANT, VT_RECORD, VT_ILLEGAL, VT_UNKNOWN, VT_BSTR, and VT_DISPATCH.
  • The version 2.6 OLE DB Provider for SQL Server has a dynamic property called Allow Native Variants which, as the name implies, allows developers to access the sql_variant in its native form as opposed to a DBTYPE_VARIANT. If this property is set, and a Recordset is opened with the Client Cursor Engine (adUseClient), the Recordset.Open call will fail. If this property is set and a Recordset is opened with server cursors (adUseServer), the Recordset.Open call will succeed, but accessing columns of type sql_variant will produce an error.
  • Using MDAC 2.5 components (including ADO) against SQL Server 2000 is a supported configuration, except when accessing sql_variant data. Values stored in sql_variant columns will appear to version 2.5 components as strings. Client applications accessing sql_variant data should be upgraded to MDAC 2.6.

Recordset Behavior

SQLOLEDB cannot use SQL Server cursors to support the multiple-result generated by many commands. If a consumer requests a recordset requiring SQL Server cursor support, an error occurs if the command text used generates more than a single recordset as its result.

Scrollable SQLOLEDB recordsets 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 recordset using a command containing an SQL ORDER BY clause can fail.

Dynamic Properties

The Microsoft OLE DB Provider for SQL Server inserts several dynamic properties into the Properties collection of the unopened Connection, Recordset, and Command objects.

The following tables are a cross-index of the ADO and OLE DB names for each dynamic property. The OLE DB Programmer's Reference refers to an ADO property name by the term "Description." You can find more information about these properties in the OLE DB Programmer's Reference. Search for the OLE DB property name in the Index or see Appendix C: OLE DB Properties.

Connection Dynamic Properties

The following properties are added to the Connection object's Properties collection.

ADO Property Name | OLE DB Property Name
Active Sessions | DBPROP_ACTIVESESSIONS
Asynchable Abort | DBPROP_ASYNCTXNABORT
Asynchable Commit | DBPROP_ASYNCTNXCOMMIT
Autocommit Isolation Levels | DBPROP_SESS_AUTOCOMMITISOLEVELS
Catalog Location | DBPROP_CATALOGLOCATION
Catalog Term | DBPROP_CATALOGTERM
Column Definition | DBPROP_COLUMNDEFINITION
Connect Timeout | DBPROP_INIT_TIMEOUT
Current Catalog | DBPROP_CURRENTCATALOG
Data Source | DBPROP_INIT_DATASOURCE
Data Source Name | DBPROP_DATASOURCENAME
Data Source Object Threading Model | DBPROP_DSOTHREADMODEL
DBMS Name | DBPROP_DBMSNAME
DBMS Version | DBPROP_DBMSVER
Extended Properties | DBPROP_INIT_PROVIDERSTRING
GROUP BY Support | DBPROP_GROUPBY
Heterogeneous Table Support | DBPROP_HETEROGENEOUSTABLES
Identifier Case Sensitivity | DBPROP_IDENTIFIERCASE
Initial Catalog | DBPROP_INIT_CATALOG
Isolation Levels | DBPROP_SUPPORTEDTXNISOLEVELS
Isolation Retention | DBPROP_SUPPORTEDTXNISORETAIN
Locale Identifier | DBPROP_INIT_LCID
Maximum Index Size | DBPROP_MAXINDEXSIZE
Maximum Row Size | DBPROP_MAXROWSIZE
Maximum Row Size Includes BLOB | DBPROP_MAXROWSIZEINCLUDESBLOB
Maximum Tables in SELECT | DBPROP_MAXTABLESINSELECT
Multiple Parameter Sets | DBPROP_MULTIPLEPARAMSETS
Multiple Results | DBPROP_MULTIPLERESULTS
Multiple Storage Objects | DBPROP_MULTIPLESTORAGEOBJECTS
Multi-Table Update | DBPROP_MULTITABLEUPDATE
NULL Collation Order | DBPROP_NULLCOLLATION
NULL Concatenation Behavior | DBPROP_CONCATNULLBEHAVIOR
OLE DB Version | DBPROP_PROVIDEROLEDBVER
OLE Object Support | DBPROP_OLEOBJECTS
Open Rowset Support | DBPROP_OPENROWSETSUPPORT
ORDER BY Columns in Select List | DBPROP_ORDERBYCOLUMNSINSELECT
Output Parameter Availability | DBPROP_OUTPUTPARAMETERAVAILABILITY
Pass By Ref Accessors | DBPROP_BYREFACCESSORS
Password | DBPROP_AUTH_PASSWORD
Persist Security Info | DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO
Persistent ID Type | DBPROP_PERSISTENTIDTYPE
Prepare Abort Behavior | DBPROP_PREPAREABORTBEHAVIOR
Prepare Commit Behavior | DBPROP_PREPARECOMMITBEHAVIOR
Procedure Term | DBPROP_PROCEDURETERM
Prompt | DBPROP_INIT_PROMPT
Provider Friendly Name | DBPROP_PROVIDERFRIENDLYNAME
Provider Name | DBPROP_PROVIDERFILENAME
Provider Version | DBPROP_PROVIDERVER
Read-Only Data Source | DBPROP_DATASOURCEREADONLY
Rowset Conversions on Command | DBPROP_ROWSETCONVERSIONSONCOMMAND
Schema Term | DBPROP_SCHEMATERM
Schema Usage | DBPROP_SCHEMAUSAGE
SQL Support | DBPROP_SQLSUPPORT
Structured Storage | DBPROP_STRUCTUREDSTORAGE
Subquery Support | DBPROP_SUBQUERIES
Table Term | DBPROP_TABLETERM
Transaction DDL | DBPROP_SUPPORTEDTXNDDL
User ID | DBPROP_AUTH_USERID
User Name | DBPROP_USERNAME
Window Handle | DBPROP_INIT_HWND

Recordset Dynamic Properties

The following properties are added to the Recordset object's Properties collection.

ADO Property Name | OLE DB Property Name
Access Order | DBPROP_ACCESSORDER
Blocking Storage Objects | DBPROP_BLOCKINGSTORAGEOBJECTS
Bookmark Type | DBPROP_BOOKMARKTYPE
Bookmarkable | DBPROP_IROWSETLOCATE
Change Inserted Rows | DBPROP_CHANGEINSERTEDROWS
Column Privileges | DBPROP_COLUMNRESTRICT
Column Set Notification | DBPROP_NOTIFYCOLUMNSET
Command Time Out | DBPROP_COMMANDTIMEOUT
Defer Column | DBPROP_DEFERRED
Delay Storage Object Updates | DBPROP_DELAYSTORAGEOBJECTS
Fetch Backwards | DBPROP_CANFETCHBACKWARDS
Hold Rows | DBPROP_CANHOLDROWS
IAccessor | DBPROP_IAccessor
IColumnsInfo | DBPROP_IColumnsInfo
IColumnsRowset | DBPROP_IColumnsRowset
IConnectionPointContainer | DBPROP_IConnectionPointContainer
IConvertType | DBPROP_IConvertType
Immobile Rows | DBPROP_IMMOBILEROWS
IRowset | DBPROP_IRowset
IRowsetChange | DBPROP_IRowsetChange
IRowsetIdentity | DBPROP_IRowsetIdentity
IRowsetInfo | DBPROP_IRowsetInfo
IRowsetLocate | DBPROP_IRowsestLocate
IRowsetResynch |
IRowsetScroll | DBPROP_IRowsetScroll
IRowsetUpdate | DBPROP_IRowsetUpdate
ISequentialStream | DBPROP_ISequentialStream
ISupportErrorInfo | DBPROP_ISupportErrorInfo
Literal Bookmarks | DBPROP_LITERALBOOKMARKS
Literal Row Identity | DBPROP_LITERALIDENTITY
Maximum Open Rows | DBPROP_MAXOPENROWS
Maximum Pending Rows | DBPROP_MAXPENDINGROWS
Maximum Rows | DBPROP_MAXROWS
Notification Granularity | DBPROP_NOTIFICATIONGRANULARITY
Notification Phases | DBPROP_NOTIFICATIONPHASES
Objects Transacted | DBPROP_TRANSACTEDOBJECT
Others' Changes Visible | DBPROP_OTHERUPDATEDELETE
Others' Inserts Visible | DBPROP_OTHERINSERT
Own Changes Visible | DBPROP_OWNUPDATEDELETE
Own Inserts Visible | DBPROP_OWNINSERT
Preserve on Abort | DBPROP_ABORTPRESERVE
Preserve on Commit | DBPROP_COMMITPRESERVE
Quick Restart | DBPROP_QUICKRESTART
Reentrant Events | DBPROP_REENTRANTEVENTS
Remove Deleted Rows | DBPROP_REMOVEDELETED
Report Multiple Changes | DBPROP_REPORTMULTIPLECHANGES
Return Pending Inserts | DBPROP_RETURNPENDINGINSERTS
Row Delete Notification | DBPROP_NOTIFYROWDELETE
Row First Change Notification | DBPROP_NOTIFYROWFIRSTCHANGE
Row Insert Notification | DBPROP_NOTIFYROWINSERT
Row Privileges | DBPROP_ROWRESTRICT
Row Resynchronization Notification | DBPROP_NOTIFYROWRESYNCH
Row Threading Model | DBPROP_ROWTHREADMODEL
Row Undo Change Notification | DBPROP_NOTIFYROWUNDOCHANGE
Row Undo Delete Notification | DBPROP_NOTIFYROWUNDODELETE
Row Undo Insert Notification | DBPROP_NOTIFYROWUNDOINSERT
Row Update Notification | DBPROP_NOTIFYROWUPDATE
Rowset Fetch Position Change Notification | DBPROP_NOTIFYROWSETFETCHPOSISIONCHANGE
Rowset Release Notification | DBPROP_NOTIFYROWSETRELEASE
Scroll Backwards | DBPROP_CANSCROLLBACKWARDS
Server Cursor | DBPROP_SERVERCURSOR
Skip Deleted Bookmarks | DBPROP_BOOKMARKSKIPPED
Strong Row Identity | DBPROP_STRONGITDENTITY
Unique Rows | DBPROP_UNIQUEROWS
Updatability | DBPROP_UPDATABILITY
Use Bookmarks | DBPROP_BOOKMARKS

Command Dynamic Properties

The following properties are added to the Command object's Properties collection.

ADO Property Name | OLE DB Property Name
Access Order | DBPROP_ACCESSORDER
Base Path | SSPROP_STREAM_BASEPATH
Blocking Storage Objects | DBPROP_BLOCKINGSTORAGEOBJECTS
Bookmark Type | DBPROP_BOOKMARKTYPE
Bookmarkable | DBPROP_IROWSETLOCATE
Change Inserted Rows | DBPROP_CHANGEINSERTEDROWS
Column Privileges | DBPROP_COLUMNRESTRICT
Column Set Notification | DBPROP_NOTIFYCOLUMNSET
Content Type | SSPROP_STREAM_CONTENTTYPE
Cursor Auto Fetch | SSPROP_CURSORAUTOFETCH
Defer Column | DBPROP_DEFERRED
Defer Prepare | SSPROP_DEFERPREPARE
Delay Storage Object Updates | DBPROP_DELAYSTORAGEOBJECTS
Fetch Backwards | DBPROP_CANFETCHBACKWARDS
Hold Rows | DBPROP_CANHOLDROWS
IAccessor | DBPROP_IAccessor
IColumnsInfo | DBPROP_IColumnsInfo
IColumnsRowset | DBPROP_IColumnsRowset
IConnectionPointContainer | DBPROP_IConnectionPointContainer
IConvertType | DBPROP_IConvertType
Immobile Rows | DBPROP_IMMOBILEROWS
IRowset | DBPROP_IRowset
IRowsetChange | DBPROP_IRowsetChange
IRowsetIdentity | DBPROP_IRowsetIdentity
IRowsetInfo | DBPROP_IRowsetInfo
IRowsetLocate | DBPROP_IRowsetLocate
IRowsetResynch | DBPROP_IRowsetResynch
IRowsetScroll | DBPROP_IRowsetScroll
IRowsetUpdate | DBPROP_IRowsetUpdate
ISequentialStream | DBPROP_ISequentialStream
ISupportErrorInfo | DBPROP_ISupportErrorInfo
Literal Bookmarks | DBPROP_LITERALBOOKMARKS
Literal Row Identity | DBPROP_LITERALIDENTITY
Lock Mode | DBPROP_LOCKMODE
Maximum Open Rows | DBPROP_MAXOPENROWS
Maximum Pending Rows | DBPROP_MAXPENDINGROWS
Maximum Rows | DBPROP_MAXROWS
Notification Granularity | DBPROP_NOTIFICATIONGRANULARITY
Notification Phases | DBPROP_NOTIFICATIONPHASES
Objects Transacted | DBPROP_TRANSACTEDOBJECT
Others' Changes Visible | DBPROP_OTHERUPDATEDELETE
Others' Inserts Visible | DBPROP_OTHERINSERT
Output Encoding Property | DBPROP_OUTPUTENCODING
Output Stream Property | DBPROP_OUTPUTSTREAM
Own Changes Visible | DBPROP_OWNUPDATEDELETE
Own Inserts Visible | DBPROP_OWNINSERT
Preserve on Abort | DBPROP_ABORTPRESERVE
Preserve on Commit | DBPROP_COMMITPRESERVE
Quick Restart | DBPROP_QUICKRESTART
Reentrant Events | DBPROP_REENTRANTEVENTS
Remove Deleted Rows | DBPROP_REMOVEDELETED
Report Multiple Changes | DBPROP_REPORTMULTIPLECHANGES
Return Pending Inserts | DBPROP_RETURNPENDINGINSERTS
Row Delete Notification | DBPROP_NOTIFYROWDELETE
Row First Change Notification | DBPROP_NOTIFYROWFIRSTCHANGE
Row Insert Notification | DBPROP_NOTIFYROWINSERT
Row Privileges | DBPROP_ROWRESTRICT
Row Resynchronization Notification | DBPROP_NOTIFYROWRESYNCH
Row Threading Model | DBPROP_ROWTHREADMODEL
Row Undo Change Notification | DBPROP_NOTIFYROWUNDOCHANGE
Row Undo Delete Notification | DBPROP_NOTIFYROWUNDODELETE
Row Undo Insert Notification | DBPROP_NOTIFYROWUNDOINSERT
Row Update Notification | DBPROP_NOTIFYROWUPDATE
Rowset Fetch Position Change Notification | DBPROP_NOTIFYROWSETFETCHPOSITIONCHANGE
Rowset Release Notification | DBPROP_NOTIFYROWSETRELEASE
Scroll Backwards | DBPROP_CANSCROLLBACKWARDS
Server Cursor | DBPROP_SERVERCURSOR
Server Data on Insert | DBPROP_SERVERDATAONINSERT
Skip Deleted Bookmarks | DBPROP_BOOKMARKSKIP
Strong Row Identity | DBPROP_STRONGIDENTITY
Updatability | DBPROP_UPDATABILITY
Use Bookmarks | DBPROP_BOOKMARKS
XML Root | SSPROP_STREAM_XMLROOT
XSL | SSPROP_STREAM_XSL

For specific implementation details and functional information about the Microsoft SQL Server OLE DB Provider, consult the OLE DB Provider for SQL Server documentation in the OLE DB section of the MDAC SDK.

See Also

ConnectionString Property | Provider Property | Recordset Object


Microsoft OLE DB Provider for Oracle

The Microsoft OLE DB Provider for Oracle allows ADO to access Oracle databases.

Connection String Parameters

To connect to this provider, set the Provider argument of the ConnectionString property to:

MSDAORA

Reading the Provider property will return this string as well.

If a join query with a keyset or dynamic cursor is executed in an Oracle database, an error occurs. Oracle only supports a static read-only cursor.

Typical Connection String

A typical connection string for this provider is:

"Provider=MSDAORA;Data Source=serverName;User ID=userName; Password=userPassword;"

The string consists of these keywords:

Keyword | Description
Provider | Specifies the OLE DB Provider for Oracle.
Data Source | Specifies the name of a server.
User ID | Specifies the user name.
Password | Specifies the user password.

Provider-Specific Connection Parameters

The provider supports several provider-specific connection parameters in addition to those defined by ADO. As with the ADO connection properties, these provider-specific properties can be set via the Properties collection of a Connection or as part of the ConnectionString.

These parameters are fully described in the OLE DB Programmer's Reference. (The ADO Dynamic Property Index provides a cross-reference between these parameter names and the corresponding OLE DB properties.)

Parameter | Description
Window Handle | Indicates the window handle to use to prompt for additional information.
Locale Identifier | Indicates a unique 32-bit number (for example, 1033) that specifies preferences related to the user's language. These preferences indicate how dates and times are formatted, items are sorted alphabetically, strings are compared, and so on.
OLE DB Services | Indicates a bitmask that specifies OLE DB services to enable or disable.
Prompt | Indicates whether to prompt the user while a connection is being established.
Extended Properties | A string containing provider-specific, extended connection information. Use this property only for provider-specific connection information that cannot be described through the property mechanism.

See Also

ConnectionString Property | Provider Property | Recordset Object

OLE DB Provider for Oracle in the OLE DB section of the MDAC SDK documentation


Microsoft OLE DB Provider for Internet Publishing

The Microsoft OLE DB Provider for Internet Publishing allows ADO to access resources served by Microsoft FrontPage or Microsoft Internet Information Server. Resources include web source files such as HTML files, or Windows 2000 web folders.

Connection String Parameters

To connect to this provider, set the Provider argument of the ConnectionString property to:

MSDAIPP.DSO

This value can also be set or read using the Provider property.

Typical Connection String

A typical connection string for this provider is:

"Provider=MSDAIPP.DSO;Data Source=ResourceURL;User ID=userName;Password=userPassword;"

-or-

"URL=ResourceURL;User ID=userName;Password=userPassword;"

The string consists of these keywords:

Keyword | Description
Provider | Specifies the OLE DB Provider for Internet Publishing.
Data Source -or- URL | Specifies the URL of a file or directory published in a Web Folder.
User ID | Specifies the user name.
Password | Specifies the user password.

If you set the ResourceURL value from the "URL=" in the connection string to an invalid value, by default the Internet Publishing Provider raises a dialog box to prompt for a valid value. This is undesirable behavior for a component in the middle tier of an application, because it suspends program execution until the dialog box is cleared and the client appears to freeze because it has not received a response from the component.

Note If MSDAIPP.DSO is explicitly specified as the value of the provider, either with the Provider connection string keyword or the Provider property, you cannot use "URL=" in the connection string. If you do, an error will occur. Instead, simply specify the URL as shown in the topic Using ADO with the OLE DB Provider for Internet Publishing.

See Also

Internet Publishing Scenario | Using ADO with the OLE DB Provider for Internet Publishing


Microsoft Data Shaping Service for OLE DB (ADO Service Provider)

ADO Appendixes

Microsoft Data Shaping Service for OLE DB

The Microsoft Data Shaping Service for OLE DB service provider supports the construction of hierarchical (shaped) Recordset objects from a data provider.

Provider Keyword

To invoke the Data Shaping Service for OLE DB, specify the following keyword and value in the connection string.

"Provider=MSDataShape"

Dynamic Properties

When this service provider is invoked, the following dynamic properties are added to the Connection object's Properties collection.

Dynamic Property Name | Description
Unique Reshape Names | Indicates whether Recordset objects with duplicate values for their Name properties are allowed. If this dynamic property is True and a new Recordset is created with the same name as an existing Recordset, then the new Recordset object's name is modified to make it unique. If this property is False and a new Recordset is created with the same name as the existing Recordset, both Recordset objects will have the same name. Therefore, neither Recordset can be reshaped as long as both recordsets exist. The default value of the property is False.
Data Provider | Indicates the name of the provider that will supply rows to be shaped.

You may also set writable dynamic properties by specifying their names as keywords in the connection string. For example, in Microsoft Visual Basic, set the Data Provider dynamic property to "MSDASQL" by specifying:

Dim cn as New ADODB.Connection
cn.Open "Provider=MSDataShape;Data Provider=MSDASQL"

You may also set or retrieve a dynamic property by specifying its name as the index to the Properties property. For example, get and print the current value of the Data Provider dynamic property, then set a new value, like this:

Debug.Print cn.Properties("Data Provider")
cn.Properties("Data Provider") = "MSDASQL"

For more information about data shaping, see Data Shaping.

See Also

Data Shaping Service for OLE DB (in the OLE DB section of the MDAC SDK documentation)


Microsoft OLE DB Persistence Provider (ADO Service Provider)

ADO Appendixes

Microsoft OLE DB Persistence Provider

The Microsoft OLE DB Persistence Provider enables you to save a Recordset object into a file, and later restore that Recordset object from the file. Schema information, data, and pending changes are preserved.

You can save the Recordset in either the proprietary Advanced Data Table Gram (ADTG) format, or the open Extensible Markup Language (XML) format.

Provider Keyword

To invoke this provider, specify the following keyword and value in the connection string.

"Provider=MSPersist"

Errors

The following errors issued by this provider can be detected in your application.

Constant | Description
E_BADSTREAM | The file opened does not have a valid format (that is, the format is not ADTG or XML).
E_CANTPERSISTROWSET | The Recordset object saved has characteristics that prevent it from being stored.

Remarks

The Microsoft OLE DB Persistence Provider exposes no dynamic properties.

Currently, only parameterized hierarchical Recordset objects cannot be saved.

For more information about persistently storing Recordset objects, see Recordset Persistence.

When a stream is used to open a Recordset, there should be no parameters specified other than the Source parameter of the Open method.

See Also

OLE DB Persistence Provider in the OLE DB section of the MDAC SDK documentation


Microsoft OLE DB Remoting Provider (ADO Service Provider)

ADO Appendixes

Microsoft OLE DB Remoting Provider

The Microsoft OLE DB Remoting Provider enables a local user on a client machine to invoke data providers on a remote machine. Specify the data provider parameters for the remote machine as you would if you were a local user on the remote machine. Then specify the parameters used by the Remoting Provider to access the remote machine. The resulting effect is that you will access the remote machine as if you were a local user.

Provider Keyword

To invoke the OLE DB Remoting Provider, specify the following keyword and value in the connection string. (Note the blank space in the provider name.)

"Provider=MS Remote"

Additional Keywords

When this service provider is invoked, the following additional keywords are relevant.

Keyword | Description
Data Source | Specifies the name of the remote data source. It is passed to the OLE DB Remoting Provider for processing. This keyword is equivalent to the RDS.DataControl object's Connect property.

Dynamic Properties

When this service provider is invoked, the following dynamic properties are added to the Connection object's Properties collection.

Dynamic Property Name | Description
DFMode Indicates the DataFactory Mode. A string that specifies the desired version of the DataFactory object on the server. Set this property before opening a connection to request a particular version of the DataFactory. If the requested version is not available, an attempt will be made to use the preceding version. If there is no preceding version, an error will occur. If DFMode is less than the available version, an error will occur. This property is read-only after a connection is made.

Can be one of the following valid string values:

  • "25"—Version 2.5 (Default)
  • "21"—Version 2.1
  • "20"—Version 2.0
  • "15"—Version 1.5
Command Properties | Indicates values that will be added to the string of command (rowset) properties sent to the server by the MS Remote provider. The default value for this string is vt_empty.
Current DFMode Indicates the actual version number of the DataFactory on the server. Check this property to see if the version requested in the DFMode property was honored.

Can be one of the following valid Long integer values:

  • 25—Version 2.5 (Default)
  • 21—Version 2.1
  • 20—Version 2.0
  • 15—Version 1.5

Adding "DFMode=20;" to your connection string when using the MSRemote provider can improve your server's performance when updating data. With this setting, the RDSServer.DataFactory object on the server uses a less resource-intensive mode. However, the following features are not available in this configuration:

  • Using parameterized queries.
  • Getting parameter or column information before calling the Execute method.
  • Setting Transact Updates to True.
  • Gettting row status.
  • Calling the Resync method.
  • Refreshing (explicitly or automatically) via the Update Resync property.
  • Setting Command or Recordset properties.
  • Using adCmdTableDirect.
Handler | Indicates the name of a server-side customization program (or handler) that extends the functionality of the RDSServer.DataFactory, and any parameters used by the handler, all separated by commas (","). A String value.
Internet Timeout | Indicates the maximum number of milliseconds to wait for a request to travel to and from the server. (The default is 5 minutes.)
Remote Provider | Indicates the name of the data provider to be used on the remote server.
Remote Server | Indicates the server name and communication protocol to be used by this connection. This property is equivalent to the RDS.DataControl object Server property.
Transact Updates | When set to True, this value indicates that when UpdateBatch is performed on the server, it will be done inside a transaction. The default value for this Boolean dynamic property is False.

You may also set writable dynamic properties by specifying their names as keywords in the connection string. For example, set the Internet Timeout dynamic property to five seconds by specifying:

Dim cn as New ADODB.Connection
cn.Open "Provider=MS Remote;Internet Timeout=5000"

You may also set or retrieve a dynamic property by specifying its name as the index to the Properties property. For example, get and print the current value of the Internet Timeout dynamic property, and then set a new value, like this:

Debug.Print cn.Properties("Internet Timeout")
cn.Properties("Internet Timeout") = 5000

Remarks

In ADO 2.0, the OLE DB Remoting Provider could only be specified in the ActiveConnection parameter of the Recordset object Open method. Starting with ADO 2.1, the provider may also be specified in the ConnectionString parameter of the Connection object Open method.

The equivalent of the RDS.DataControl object SQL property is not available. The Recordset object Open method Source argument is used instead.

Specifying "...;Remote Provider=MS Remote;..." would create a four-tier scenario.Scenarios beyond three tiers have not been tested and should not be needed.

Example

This example performs a query on the authors table of the pubs database on a server named, YourServer. The names of the remote data source and remote server are provided in the Connection object Open method, and the SQL query is specified in the Recordset object Open method. A Recordset object is returned, edited, and used to update the data source.

Dim rs as New ADODB.Recordset
Dim cn as New ADODB.Connection
cn.Open  "Provider=MS Remote;Data Source=pubs;" & _
         "Remote Server=http://YourServer"
rs.Open "SELECT * FROM authors", cn
...                'Edit the recordset
rs.UpdateBatch     'Equivalent of RDS SubmitChanges
...

See Also

OLE DB Remoting Provider in the OLE DB section of the MDAC SDK documentation


Microsoft Cursor Service for OLE DB (ADO Service Component)

ADO Appendixes

Microsoft Cursor Service for OLE DB

The Microsoft Cursor Service for OLE DB supplements the cursor support functions of data providers. As a result, the user perceives relatively uniform functionality from all data providers.

The Cursor Service makes dynamic properties available and enhances the behavior of certain methods. For example, the Optimize dynamic property enables the creation of temporary indexes to facilitate certain operations, such as the Find method.

The Cursor Service enables support for batch updating in all cases. It also simulates more capable cursor types, such as dynamic cursors, when a data provider can only supply less capable cursors, such as static cursors.

Keyword

To invoke this service component, set the Recordset or Connection object's CursorLocation property to adUseClient.

connection.CursorLocation=adUseClient
recordset.CursorLocation=adUseClient

Dynamic Properties

When the Cursor Service for OLE DB is invoked, the following dynamic properties are added to the Recordset object's Properties collection. The full list of Connection and Recordset object dynamic properties is listed in the ADO Dynamic Property Index. The associated OLE DB property names, where appropriate, are included in parenthesis after the ADO property name.

Changes to some dynamic properties are not visible to the underlying data source after the Cursor Service has been invoked. For example, setting the Command Time out property on a Recordset will not be visible to the underlying data provider.

…
Recordset1.CursorLocation = adUseClient     'invokes cursor service
Recordset1.Open "authors", _
    "Provider=SQLOLEDB;Data Source=DBServer;User Id=usr;" & _
    "Password=pwd;Initial Catalog=pubs;",,adCmdTable
Recordset1.Properties.Item("Command Time out") = 50
' 'Command Time out' property on DBServer is still default (30).
…

If your application requires the Cursor Service, but you need to set dynamic properties on the underlying provider, set the properties before invoking the Cursor Service. Command object property settings are always passed to the underlying data provider regardless of cursor location. Therefore, you can also use a command object to set the properties at any time.

Property Name | Description
Auto Recalc (DBPROP_ADC_AUTORECALC) | For recordsets created with the Data Shaping Service, this value indicates how often calculated and aggregate columns are calculated. The default (value=1) is to recalculate whenever the Data Shaping Service determines that the values have changed. If the value is 0, the calculated or aggregate columns are only calculated when the hierarchy is initially built.
Batch Size (DBPROP_ADC_BATCHSIZE) | Indicates the number of update statements that can be batched before being sent to the data store. The more statements in a batch, the fewer round trips to the data store.
Cache Child Rows (DBPROP_ADC_CACHECHILDROWS) | For recordsets created with the Data Shaping Service, this value indicates whether child recordsets are stored in a cache for later use.
Cursor Engine Version (DBPROP_ADC_CEVER) | Indicates the version of the Cursor Service being used.
Maintain Change Status (DBPROP_ADC_MAINTAINCHANGESTATUS) | Indicates the text of the command used for resynchronizing a one or more rows in a multiple table join.
Optimize | Indicates whether an index should be created. When set to True, authorizes the temporary creation of indexes to improve the execution of certain operations.
Reshape Name | Indicates the name of the Recordset. May be referenced within the current, or subsequent, data shaping commands.
Resync Command | Indicates a custom command string that is used by the Resync method when the Unique Table property is in effect.
Unique Catalog | Indicates the name of the database containing the table referenced in the Unique Table property.
Unique Schema | Indicates the name of the owner of the table referenced in the Unique Table property.
Unique Table | Indicates the name of the one table in a Recordset created from multiple tables that may be modified by insertions, updates, or deletions.
Update Criteria (DBPROP_ADC_UPDATECRITERIA) | Indicates which fields in the WHERE clause are used to handle collisions occurring during an update.
Update Resync (DBPROP_ADC_UPDATERESYNC) | Indicates whether the Resync method is implicitly invoked after the UpdateBatch method (and its behavior), when the Unique Table property is in effect.

You may also set or retrieve a dynamic property by specifying its name as the index to the Properties collection. For example, get and print the current value of the Optimize dynamic property, then set a new value, like this:

Debug.Print rs.Properties("Optimize")
rs.Properties("Optimize") = True

Built-in Property Behavior

The Cursor Service for OLE DB also affects the behavior of certain built-in properties.

Property Name | Description
CursorType | Supplements the types of cursors that are available for a Recordset.
LockType | Supplements the types of locks available for a Recordset. Enables batch updates.
Sort | Specifies one or more field names that the Recordset is sorted on, and whether each field is sorted in ascending or descending order.

Method Behavior

The Cursor Service for OLE DB enables or affects the behavior of the Field object's Append method; and the Recordset object's Open, Resync, UpdateBatch, and Save methods.

See Also

Cursor Service for OLE DB in the OLE DB section of the MDAC SDK documentation


Microsoft OLE DB Simple Provider

The Microsoft OLE DB Simple Provider (OSP) allows ADO to access any data for which a provider has been written using the OLE DB Simple Provider Toolkit. Simple providers are intended to access data sources that require only fundamental OLE DB support, such as in-memory arrays or XML documents.

Connection String Parameters

To connect to the OLE DB Simple Provider DLL, set the Provider argument to the ConnectionString property to:

MSDAOSP

This value can also be set or read using the Provider property.

You can connect to simple providers that have been registered as full OLE DB providers by using the registered provider name, determined by the provider writer.

Typical Connection String

A typical connection string for this provider is:

"Provider=MSDAOSP;Data Source=serverName"

The string consists of these keywords:

Keyword | Description
Provider | Specifies the OLE DB Provider for SQL Server.
Data Source | Specifies the name of a server.

XML Document Example

The OLE DB Simple Provider (OSP) in MDAC 2.6 has been enhanced to support opening hierarchical ADO Recordsets over arbitrary XML files. These XML files may contain the ADO XML persistence schema, but it is not required. This has been implemented by connecting the OSP to the MSXML2.DLL, therefore MSXML2.DLL or newer is required.

The portfolio.xml file used in the following example contains the following tree:

Portfolio
   Stock
      Shares
      Symbol
      Price
      Info
         Company Name
         WebSite

The XML DSO uses built-in heuristics to convert the nodes in an XML tree to chapters in a hierarchical Recordset.

Using these built-in heuristics, the XML tree is converted into a two-level hierarchical Recordset of the following form:

Parent Recordset
Shares, Symbol, Price, $Text
   Child Recordset
      Company Name, WebSite, $Text

Note that the Portfolio and Info tags are not represented in the hierarchical Recordset. For an explanation of how the XML DSO converts XML trees to hierarchical Recordsets, see the following rules. The $Text column is discussed below.

Rules for Assigning XML Elements and Attributes to Columns and Rows

The XML DSO follows a procedure for assigning elements and attributes to columns and rows in data-bound applications. XML is modeled as a tree with one tag containing the entire hierarchy. For example, an XML description of a book could contain chapter tags, figure tags, and section tags. At the highest level would be the book tag, containing the subelements chapter, figure, and section. When the XML DSO maps XML elements to rows and columns, the subelements, not the top level element, are converted.

The XML DSO uses this procedure for converting the subelements:

  • Each subelement and attribute corresponds to a column in some Recordset in the hierarchy.
  • The name of the column is the same as the name of the subelement or attribute, unless the parent element has an attribute and a subelement with the same name, in which case a "!" is prepended to the subelement's column name.
  • Each column is either a simple column containing scalar values (usually strings) or a Recordset column containing child Recordsets.
  • Columns corresponding to attributes are always simple.
  • Columns corresponding to subelements are Recordset columns if either the subelement has its own subelements and/or attributes, or the subelement's parent has more than one instance of the subelement as a child. Otherwise the column is simple.
  • When there are multiple instances of a subelement (under different parents), its column is a Recordset column if any of the instances imply a Recordset column; its column is simple only if all instances imply a simple column.
  • All Recordsets have an additional column named $Text.

For more information about the XML DSO, refer to the XML SDK.

The code needed to construct a Recordset is as follows:

   Dim adoConn as ADODB.Connection
   Dim adoRS as ADODB.Recordset

   Set adoRS = New ADODB.Connection
   Set adoRS = New ADODB.Recordset

   adoConn.Open "Provider=MSDAOSP; Data Source=MSXML2.DSOControl.2.6;"
   adoRS.Open "http://WebServer/VRoot/portfolio.xml”, adoConn

Note The path to the data file can be specified using four different naming conventions.

   'HTTP://
   adoRS.Open "http://WebServer/VRoot/portfolio.xml", adoConn
   'FILE://
   adoRS.Open "file:/// C:\\Directory\\portfolio.xml", adoConn
   'UNC Path
   adoRS.Open "\\ComputerName\ShareName\portfolio.xml", adoConn
   'Full DOS Path
   adoRS.Open "C:\Directory\portfolio.xml", adoConn

Once the Recordset has been opened, the usual ADO Recordset navigation commands can be used.

Recordsets generated by the OSP have a few limitations:

  • Client cursors (adUseClient) are not supported.
  • Hierarchical Recordsets created over arbitrary XML cannot be persisted using Recordset.Save.
  • Recordsets created with the OSP are read-only.
  • The XMLDSO adds an additional column of Data ($Text) to each Recordset in the Hierarchy.

For more information about the OLE DB Simple Provider, see the OLE DB Simple Provider Toolkit in the OLE DB section of the MDAC SDK documentation.

Code Example

The following Visual Basic code demonstrates opening an arbitrary XML file, constructing a hierarchical Recordset, and recursively writing each record of each Recordset to the debug window.

Here is a simple XML file containing stock quotes. The following code uses this file to construct a two-level hierarchical Recordset.

<portfolio>
   <stock>
      <shares>100</shares>
      <symbol>MSFT</symbol>
      <price>$70.00</price>
      <info>
         <companyname>Microsoft Corporation</companyname>
         <website>http://www.microsoft.com</website>
      </info>
   </stock>
   <stock>
      <shares>100</shares>
      <symbol>AAPL</symbol>
      <price>$107.00</price>
      <info>
         <companyname>Apple Computer, Inc.</companyname>
         <website>http://www.apple.com</website>
      </info>
   </stock>
   <stock>
      <shares>100</shares>
      <symbol>DELL</symbol>
      <price>$50.00</price>
      <info>
         <companyname>Dell Corporation</companyname>
         <website>http://www.dell.com</website>
      </info>
    </stock>
    <stock>
       <shares>100</shares>
       <symbol>INTC</symbol>
       <price>$115.00</price>
       <info>
          <companyname>Intel Corporation</companyname>
          <website>http://www.intel.com</website>
       </info>
   <stock>
</portfolio>

Following are two Visual Basic sub procedures. The first creates the Recordset and passes it to the WalkHier sub procedure, which recursively walks down the hierarchy, writing each Field in each record in each Recordset to the debug window.

Private Sub BrowseHierRecordset()
' Add ADO 2.6 to Project/References
' No need to add MSXML2, ADO just passes the ProgID through to the OSP.

    Dim adoConn As ADODB.Connection
    Dim adoRS As ADODB.Recordset
    Dim adoChildRS As ADODB.Recordset

    Set adoConn = New ADODB.Connection
    Set adoRS = New ADODB.Recordset
    Set adoChildRS = ADODB.Recordset

    adoConn.Open "Provider=MSDAOSP; Data Source=MSXML2.DSOControl.2.6;"
    adoRS.Open "http://bwillett3/Kowalski/portfolio.xml", adoConn

    Dim iLevel As Integer
    iLevel = 0
    WalkHier iLevel, adoRS

End Sub

Sub WalkHier(ByVal iLevel As Integer, ByVal adoRS As ADODB.Recordset)
    iLevel = iLevel + 1
    PriorLevel = iLevel
    While Not adoRS.EOF
        For ndx = 0 To adoRS.Fields.Count - 1
            If adoRS.Fields(ndx).Name <> "$Text" Then
                If adoRS.Fields(ndx).Type = adChapter Then
                    Set adoChildRS = adoRS.Fields(ndx).Value
                    WalkHier iLevel, adoChildRS
                Else
                    Debug.Print iLevel & ": adoRS.Fields(" & ndx & _
                       ") = " & adoRS.Fields(ndx).Name & " = " & _
                       adoRS.Fields(ndx).Value
                End If
            End If
        Next ndx
        adoRS.MoveNext
    Wend
    iLevel = PriorLevel
End Sub


Appendix B: ADO Errors

See the following topics for more information about particular error messages:

  • ADO Error Codes
  • DataControl Error Codes
  • Internet Explorer Error Codes
  • Internet Information Services Error Codes

See Also

ADO API Reference | ADO Collections | ADO Dynamic Properties | ADO Enumerated Constants | ADO Events | ADO Methods | ADO Object Model | ADO Objects | ADO Properties


ADO Error Codes

In addition to the provider errors returned in the Error objects of the Errors collection, ADO itself can return errors to the exception-handling mechanism of your run-time environment. Use the error trapping mechanism your programming language, such as the On Error statement in Microsoft® Visual Basic or the try-catch block in Microsoft Visual C++® or Microsoft Visual J++® to capture ADO errors.

For the list of ADO error codes, see ErrorValueEnum.

See Also

Error Object | Errors Collection


DataControl Error Codes

The following table lists the RDS.DataControl object error codes. The positive decimal translation of the low two bytes, the negative decimal translation of the full error code, and the hexadecimal values are shown.

RDS.DataControl error codes | Number | Description
IDS_AsyncPending | 4107 -2146824175 0x800A1011 | Operation cannot be performed while async operation is pending.
IDS_BadInlineTablegram | 4105 -2146824183 0x800A1009 | Bad inline tablegram.
IDS_CantConnect | 4099 -2146824189 0x800A1003 | Cannot connect to server.
IDS_CantCreateObject | 4100 -2146824188 0x800A1004 | Business object cannot be created.
IDS_CantFindDataspace | 4102 -2146824186 0x800A1006 | Dataspace property is not valid.
IDS_CantInvokeMethod | 4101 -2146824187 0x800A1005 | Method cannot be invoked on business object.
IDS_CrossDomainWarning | 4112 -2146824170 0x800A1016 | This page accesses data on another domain. Do you want to allow this? To avoid this message in Internet Explorer, you can add a secure Web site to your Trusted Sites zone on the Security tab of the Internet Options dialog box.
IDS_InvalidADCClientVersion | 4106 -2146824176 0x800A1010 | Invalid RDS Client Version — Client is newer than server.
IDS_INVALIDARG | 5376 -2147019520 0x80071500 | One or more arguments are invalid.
IDS_InvalidBindings | 4097 -2146824191 0x800A1001 | Error in bindings property.
IDS_InvalidParam | 4110 -2146824172 0x800A1014 | One or more arguments are invalid.
IDS_NOINTERFACE | 5377 -2147019519 0x80071501 | No such interface is supported.
IDS_NotReentrant | 4111 -2146824171 0x800A1015 | Request cannot be executed while the event handler is still processing.
IDS_ObjectNotSafe | 4103 -2146824185 0x800A1007 | Safety settings on this computer prohibit creation of business object.
IDS_RecordsetNotOpen | 4109 -2146824173 0x800A1013 | Recordset is not open.
IDS_ResetInvalidField | 4108 -2146824174 0x800A1012 | Column specified in SortColumn or FilterColumn does not exist.
IDS_RowsetNotUpdateable | 4104 -2146824184 0x800A1008 | Rowset not updateable.
IDS_UnexpectedError | 4351 -2146823937 0x800A10FF | Unexpected error.
IDS_UpdatesFailed | 4098 -2146824190 0x800A1002 | Unable to update database.
IDS_URLMONNotFound | 4119 -2146824169 0x800A1017 | DataControl URL property requires the system file Urlmon.dll, which cannot be found.

See Also

DataControl Object (RDS)


Internet Explorer Error Codes

The following table lists Microsoft® Internet Explorer error codes related to Remote Data Service usage. The positive decimal translation of the low two bytes, the negative decimal translation of the full error code, and the hexadecimal values are shown.

Internet Explorer (Wininet) errors | Number | Description
IDS_WinInet_CantConnect | 8195 -2146820093 0x800A2003 | Internet Client Error: Cannot Connect to Server.
IDS_WinInet_ ConnectionReset | 12031 -2146816257 0x800A2EFF | Internet Client Error: Connection Reset.
IDS_WinInet_Error | 8193 -2146820095 0x800A2001 | Internet Client Error.
IDS_WinInet_ InvalidServerResponse | 8430 -2146819858 0x800A20EE | Internet Client Error: Invalid Server Response.
IDS_WinInet_ SSLPostLimitation | 8196 -2146820092 0x800A2004 | Internet Client Error: SSL Error (possibly 32K data upload limitation).
IDS_WinInet_Timeout | 8194 -2146820094 0x800A2002 | Internet Client Error: Request Timeout.


Internet Information Services Error Codes

The following table lists Microsoft® Internet Information Services (IIS) error codes related to Remote Data Service usage. The positive decimal translation of the low two bytes, the negative decimal translation of the full error code, and the hexadecimal values are shown.

Internet Information Services errors | Number | Description
IDS_IIS_AccessDenied | 8208 -2146820080 0x800A2010 | Internet Server Error: Access Denied.
IDS_IIS_ObjectNotFound | 8209 -2146820079 0x800A2011 | Internet Server Error: Object/module not found.
IDS_IIS_RequestForbidden | 8210 -2146820078 0x800A2012 | Internet Server Error: Request Forbidden.
IDS_IIS_UnexpectedError | 8447 -2146819841 0x800A20FF | Internet Server Error.


Appendix C: Programming with ADO

ADO is a COM automation interface component that can be used with many programming languages, including Microsoft® Visual Basic, VBScript, JScript®, Visual C++®, and Visual J++®. A version of ADO is installed with each of these tools and other applications, such as Microsoft® Office® and Microsoft SQL Server. The most recent version of ADO is available separately with the Microsoft Data Access Components. See the Microsoft Data Access Web site for more information.

The library for ADO is msado15.dll and the program ID (ProgID) prefix is "ADODB." For example, to explicitly refer to an ADO Recordset, use ADODB.Recordset.

For more information about programming with ADO in various development environments, see the following topics:

  • Using ADO with Microsoft Visual Basic
  • Using ADO with Scripting Languages
  • Using ADO with Microsoft Visual C++
  • Using ADO with Microsoft Visual J++

See Also

ADO API Reference | ADO Samples | Configuring RDS | ActiveX Data Objects Start Page | Appendix A: Providers | What's New in ADO


Using ADO with Microsoft Visual Basic

Setting up an ADO project and writing ADO code is similar whether you use Visual Basic or Visual Basic for Applications. This topic addresses using ADO with both Visual Basic and Visual Basic for Applications and notes any differences.

Referencing the ADO Library

The ADO library must be referenced by your project.

To reference ADO from Microsoft Visual Basic

  1. In Visual Basic, from the Project menu, select References....
  2. Select Microsoft ActiveX Data Objects x.x Library from the list. Verify that at least the following libraries are also selected:
    • Visual Basic for Applications
    • Visual Basic runtime objects and procedures
    • Visual Basic objects and procedures
    • OLE Automation
  3. Click OK.

You can use ADO just as easily with Visual Basic for Applications, using Microsoft Access, for example.

To reference ADO from Microsoft Access

  1. In Microsoft Access, select or create a module from the Modules tab in the Database window.
  2. From the Tools menu, select References....
  3. Select Microsoft ActiveX Data Objects x.x Library from the list. Verify that at least the following libraries are also selected:
    • Visual Basic for Applications
    • Microsoft Access 8.0 Object Library (or later)
    • Microsoft DAO 3.5 Object Library (or later)
  4. Click OK.

Creating ADO Objects in Visual Basic

To create an automation variable and an instance of an object for that variable, you can use two methods: Dim or CreateObject.

Dim

You can use the New keyword with Dim to declare and instantiate ADO objects in one step:

Dim conn As New ADODB.Connection

Alternately, the Dim statement declaration and object instantiation can also be two steps:

Dim conn As ADODB.Connection
Set conn = New ADODB.Connection

Note It is not required to explicitly use the ADODB progid with the Dim statement, assuming you have properly referenced the ADO library in your project. However, using it ensures that you won't have naming conflicts with other libraries.

For example, if you include references to both ADO and DAO in the same project, you should include a qualifier to specify which object model to use when instantiating Recordset objects, as in the following code:

Dim adoRS As ADODB.Recordset
Dim daoRS As DAO.Recordset

CreateObject

With the CreateObject method, the declaration and object instantiation must be two discrete steps:

Dim conn1
Set conn1 = CreateObject("ADODB.Connection") As Object

Objects instantiated with CreateObject are late-bound, which means that they are not strongly typed and command-line completion is disabled. However, it does allow you to skip referencing the ADO library from your project, and enables you to instantiate specific versions of objects. For example:

Set conn1 = CreateObject("ADODB.Connection.2.0") As Object

You could also accomplish this by specifically creating a reference to the ADO version 2.0 type library and creating the object.

Instantiating objects with the CreateObject method is typically slower than using the Dim statement.

Handling Events

In order to handle ADO events in Microsoft® Visual Basic®, you must declare a module-level variable using the WithEvents keyword. The variable can be declared only as part of a class module and must be declared at the module level. For a more complete discussion of handling ADO events, see Chapter 7: Handling ADO Events.

Visual Basic Examples

Many Visual Basic examples are included with the ADO documentation. For more information, see ADO Code Examples in Microsoft Visual Basic.

See Also

ActiveX Data Objects Start Page | Using ADO with Microsoft Visual C++ | Using ADO with Microsoft Visual J++ | Using ADO with Scripting Languages


Using ADO with Scripting Languages

Within a scripting environment, ADO allows you to expose data by way of server-side scripting. In this scenario, ADO, the underlying OLE DB provider that it utilizes, and any other components needed to reference a given data store are installed on a server running Internet Information Services (IIS). Using Active Server Pages (ASP), ADO is a component referenced in a script that can generate HTML, for example. This HTML content can be passed via HTTP to a client Web browser. Through the use of scripting, the Web page can send actions back to the server-side script, allowing you to update, traverse, or view specific data.

ODBC Data Sources

One notable difference between scripting and non-scripting ADO code is the ODBC Data Source, if used. For non-scripting applications, you can create a User DSN in the ODBC Data Source Administrator. For scripts that are running under IIS, you must create a System DSN; otherwise your scripts won't recognize the data source you created. This applies to any ADO scripting application using the Microsoft OLE DB Provider for ODBC through Microsoft IIS.

Referencing the ADO Library

N/A with scripting languages.

Handling events

N/A with scripting languages.

The following topics contain more specific information about using ADO with scripting languages:

  • ADO in VBScript
  • ADO in JScript

See Also

ActiveX Data Objects Start Page | Using ADO with Microsoft Visual Basic | Using ADO with Microsoft Visual C++ | Using ADO with Microsoft Visual J++


VBScript ADO Programming

Creating an ADO Project

Microsoft Visual Basic, Scripting Edition does not support type libraries, so you do not need to reference ADO in your project. Consequently, no associated features such as command line completion are supported. Also, by default, ADO enumerated constants are not defined in VBScript.

However, ADO provides you with two include files containing the following definitions to be used with VBScript:

  • For server-side scripting use Adovbs.inc, which is installed in the c:\Program Files\Common Files\System\ado\ folder by default.
  • For client-side scripting use Adcvbs.inc, which is installed in the c:\Program Files\Common Files\System\msdac\ folder by default.

You can either copy and paste constant definitions from these files into your ASP pages, or, if you are doing server-side scripting, copy Adovbs.inc file to a folder on your Web site and referencing it from your ASP page like this:

<!--#include File="adovbs.inc"-->

Creating ADO Objects in VBScript

You cannot use the Dim statement to assign objects to a specific type in VBScript. Also, VBScript does not support the New syntax used with the Dim statement in Visual Basic for Applications. You must instead use the CreateObject function call:

Dim Rs1
Set Rs1 = Server.CreateObject( "ADODB.Recordset" )

VBScript Examples

The following code is a generic example of VBScript server-side programming in an Active Server Page (ASP) file:

<%  @LANGUAGE="VBSCRIPT" %>
<%  Option Explicit %>
<!--#include File="adovbs.inc"-->
<HTML>
    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

    <!-- Your ASP Code goes here -->
<%
Dim Source
Dim Connect
Dim Rs1

Source = "SELECT * FROM Authors"
Connect = "Provider=sqloledb;Data Source=srv;" & _
    "Initial Catalog=Pubs;User Id=sa;Password=;"

Set Rs1 = Server.CreateObject( "ADODB.Recordset" )
Rs1.Open Source, Connect, adOpenForwardOnly
Response.Write("Success!")
%>
    </BODY>
</HTML>

More specific VBScript examples are included with the ADO documentation. For more information, see ADO Code Examples in Microsoft Visual Basic Scripting Edition.

Differences Between VBScript and Visual Basic

Using ADO with VBScript is similar to using ADO with Visual Basic in many ways, including how syntax is used. However, some significant differences exist:

  • VBScript supports only the Variant data type, which can hold different types of data. You can store the data you need in a Variant data type, and the data will function appropriately due to casting performed by VBScript. It recognizes the type required by ADO, and converts the value in the Variant accordingly.
  • You cannot use on error goto <label> within VBScript.
  • VBScript supports some of the built-in Visual Basic functions such as Msgbox, Date, and IsNumeric. However, because VBScript is a subset of Visual Basic, not all built-in functions are supported. For example, VBScript does not support the Format function and the file I/O functions.


JScript ADO Programming

Creating an ADO Project

Microsoft JScript does not support type libraries, so you do not need to reference ADO in your project. Consequently, no associated features such as command line completion are supported. Also, by default, ADO enumerated constants are not defined in JScript.

However, ADO provides you with two include files containing the following definitions to be used with JScript:

  • For server-side scripting use Adojavas.inc, which is installed in the c:\Program Files\Common Files\System\ado\ folder by default.
  • For client-side scripting use Adcjavas.inc, which is installed in the c:\Program Files\Common Files\System\msdac\ folder by default.

You can either copy and paste constant definitions from these files into your ASP pages, or, if you are doing server-side scripting, copy Adojavas.inc file to a folder on your Web site and references it from your ASP page like this:

<!--#include File="adojavas.inc"-->

Creating ADO Objects in JScript

You must instead use the CreateObject function call:

var Rs1;
Rs1 = Server.CreateObject("ADODB.Recordset");

JScript Example

The following code is a generic example of JScript server-side programming in an Active Server Page (ASP) file that opens a Recordset object:

<%  @LANGUAGE="JScript" %>
<!--#include File="adojavas.inc"-->
<HTML>
<BODY BGCOLOR="White" topmargin="10" leftmargin="10">
<%
var Source = "SELECT * FROM Authors";
var Connect =  "Provider=sqloledb;Data Source=srv;" +
    "Initial Catalog=Pubs;User Id=sa;Password=;"
var Rs1 = Server.CreateObject( "ADODB.Recordset.2.5" );
Rs1.Open(Source,Connect,adOpenForwardOnly);
Response.Write("Success!");
%>
</BODY>
</HTML>


Using ADO with Microsoft Visual C++

For information about using ADO with Visual C++, see the following sections:

  • Visual C++ ADO Programming
  • Visual C++ Extensions for ADO
  • ADO for Visual C++ Syntax Index for COM
  • ADO for Visual C++ Syntax Index with #import

See Also

ActiveX Data Objects Start Page | ADO for Visual C++ Syntax Index for COM | ADO for Visual C++ Syntax Index with #import | Using ADO with Microsoft Visual Basic | Using ADO with Microsoft Visual J++ | Using ADO with Scripting Languages | Visual C++ Extensions for ADO


Visual C++ ADO Programming

The ADO API Reference describes the functionality of the ADO application programming interface (API) using a syntax similar to Microsoft Visual Basic. Though the intended audience is all users, ADO programmers employ diverse languages such as Visual Basic, Visual C++ (with and without the #import directive), and Visual J++ (with the ADO/WFC class package).

To accommodate this diversity, the ADO for Visual C++ Syntax Indexes provide Visual C++ language-specific syntax with links to common descriptions of functionality, parameters, exceptional behaviors, and so on, in the API Reference.

ADO is implemented with COM (Component Object Model) interfaces. However, it is easier for programmers to work with COM in certain programming languages than others. For example, nearly all the details of using COM are handled implicitly for Visual Basic programmers, whereas Visual C++ programmers must attend to those details themselves.

The following sections summarize details for C and C++ programmers using ADO and the #import directive. It focuses on data types specific to COM (Variant, BSTR, and SafeArray), and error handling (_com_error).

Using the #import Compiler Directive

The #import Visual C++ compiler directive simplifies working with the ADO methods and properties. The directive takes the name of a file containing a type library, such as the ADO .dll (Msado15.dll), and generates header files containing typedef declarations, smart pointers for interfaces, and enumerated constants. Each interface is encapsulated, or wrapped, in a class.

For each operation within a class (that is, a method or property call), there is a declaration to call the operation directly (that is, the "raw" form of the operation), and a declaration to call the raw operation and throw a COM error if the operation fails to execute successfully. If the operation is a property, there is usually a compiler directive that creates an alternative syntax for the operation that has syntax like Visual Basic.

Operations that retrieve the value of a property have names of the form, Get Property. Operations that set the value of a property have names of the form, Put Property. Operations that set the value of a property with a pointer to an ADO object have names of the form, PutRef Property.

You can get or set a property with calls of these forms:

variable = objectPtr->GetProperty(); // get property value
objectPtr->PutProperty(value);       // set property value
objectPtr->PutRefProperty(&value);   // set property with object pointer

Using Property Directives

The __declspec(property...) compiler directive is a Microsoft-specific C language extension that declares a function used as a property to have an alternative syntax. As a result, you can set or get values of a property in a way similar to Visual Basic. For example, you can set and get a property this way:

objectPtr->property = value;        // set property value
variable = objectPtr->property;     // get property value

Notice you do not have to code:

objectPtr->PutProperty(value);      // set property value
variable = objectPtr->GetProperty;  // get property value

The compiler will generate the appropriate Get-, Put-, or PutRef Property call based on what alternative syntax is declared and whether the property is being read or written.

The __declspec(property...) compiler directive can only declare get, put, or get and put alternative syntax for a function. Read-only operations only have a get declaration; write-only operations only have a put declaration; operations that are both read and write have both get and put declarations.

Only two declarations are possible with this directive; however, each property may have three property functions: Get Property, Put Property, and PutRef Property. In that case, only two forms of the property have the alternative syntax.

For example, the Command object ActiveConnection property is declared with an alternative syntax for Get ActiveConnection and PutRef ActiveConnection. The PutRef- syntax is a good choice because in practice, you will typically want to put an open Connection object (that is, a Connection object pointer) in this property. On the other hand, the Recordset object has Get-, Put-, and PutRef ActiveConnection operations, but no alternative syntax.

Collections, the GetItem Method, and the Item Property

ADO defines several collections, including Fields, Parameters, Properties, and Errors. In Visual C++, the GetItem(index) method returns a member of the collection. Index is a Variant, the value of which is either a numerical index of the member in the collection, or a string containing the name of the member.

The __declspec(property...) compiler directive declares the Item property as an alternative syntax to each collection's fundamental GetItem() method. The alternative syntax uses square brackets and looks similar to an array reference. In general, the two forms look like the following:

collectionPtr->GetItem(index);
collectionPtr->Item[index];

For example, assign a value to a field of a Recordset object, named rs, derived from the authors table of the pubs database. Use the Item() property to access the third Field of the Recordset object Fields collection (collections are indexed from zero; assume the third field is named au_fname). Then call the Value() method on the Field object to assign a string value.

This can be expressed in Visual Basic in the following four ways (the last two forms are unique to Visual Basic; other languages do not have equivalents):

rs.Fields.Item(2).Value = "value"
rs.Fields.Item("au_fname").Value = "value"
rs(2) = "value"
rs!au_fname = "value"

The equivalent in Visual C++ to the first two forms above is:

rs->Fields->GetItem(2)->PutValue("value");
rs->Fields->GetItem("au_fname")->PutValue("value");

-or- (the alternative syntax for the Value property is also shown)

rs->Fields->Item[2]->Value = "value";
rs->Fields->Item["au_fname"]->Value = "value";

COM-Specific Data Types

In general, any Visual Basic data type you find in the ADO API Reference has a Visual C++ equivalent. These include standard data types such as unsigned char for a Visual Basic Byte, short for Integer, and long for Long. Look in the Syntax Indexes to see exactly what is required for the operands of a given method or property.

The exceptions to this rule are the data types specific to COM: Variant, BSTR, and SafeArray.

Variant

A Variant is a structured data type that contains a value member and a data type member. A Variant may contain a wide range of other data types including another Variant, BSTR, Boolean, IDispatch or IUnknown pointer, currency, date, and so on. COM also provides methods that make it easy to convert one data type to another.

The _variant_t class encapsulates and manages the Variant data type.

When the ADO API Reference says a method or property operand takes a value, it usually means the value is passed in a _variant_t.

This rule is explicitly true when the Parameters section in the topics of the ADO API Reference says an operand is a Variant. One exception is when the documentation explicitly says the operand takes a standard data type, such as Long or Byte, or an enumeration. Another exception is when the operand takes a String.

BSTR

A BSTR (Basic STRing) is a structured data type that contains a character string and the string's length. COM provides methods to allocate, manipulate, and free a BSTR.

The _bstr_t class encapsulates and manages the BSTR data type.

When the ADO API Reference says a method or property takes a String value, it means the value is in the form of a _bstr_t.

Casting _variant_t and _bstr_t Classes

Often it is not necessary to explicitly code a _variant_t or _bstr_t in an argument to an operation. If the _variant_t or _bstr_t class has a constructor that matches the data type of the argument, then the compiler will generate the appropriate _variant_t or _bstr_t.

However, if the argument is ambiguous, that is, the argument's data type matches more than one constructor, you must cast the argument with the appropriate data type to invoke the correct constructor.

For example, the declaration for the Recordset::Open method is:

    HRESULT Open (
        const _variant_t & Source,
        const _variant_t & ActiveConnection,
        enum CursorTypeEnum CursorType,
        enum LockTypeEnum LockType,
        long Options );

The ActiveConnection argument takes a reference to a _variant_t, which you may code as a connection string or a pointer to an open Connection object.

The correct _variant_t will be constructed implicitly if you pass a string such as "DSN=pubs;uid=sa;pwd=;", or a pointer such as "(IDispatch *) pConn".

Or you may explicitly code a _variant_t containing a pointer such as "_variant_t((IDispatch *) pConn, true)". The cast, (IDispatch *), resolves the ambiguity with another constructor that takes a pointer to an IUnknown interface.

It is a crucial, though seldom mentioned fact, that ADO is an IDispatch interface. Whenever a pointer to an ADO object must be passed as a Variant, that pointer must be cast as a pointer to an IDispatch interface.

The last case explicitly codes the second boolean argument of the constructor with its optional, default value of true. This argument causes the Variant constructor to call its AddRef() method, which compensates for ADO automatically calling the _variant_t::Release() method when the ADO method or property call completes.

SafeArray

A SafeArray is a structured data type that contains an array of other data types. A SafeArray is called safe because it contains information about the bounds of each array dimension, and limits access to array elements within those bounds.

When the ADO API Reference says a method or property takes or returns an array, it means the method or property takes or returns a SafeArray, not a native C/C++ array.

For example, the second parameter of the Connection object OpenSchema method requires an array of Variant values. Those Variant values must be passed as elements of a SafeArray, and that SafeArray must be set as the value of another Variant. It is that other Variant that is passed as the second argument of OpenSchema.

As further examples, the first argument of the Find method is a Variant whose value is a one-dimensional SafeArray; each of the optional first and second arguments of AddNew is a one-dimensional SafeArray; and the return value of the GetRows method is a Variant whose value is a two-dimensional SafeArray.

Missing and Default Parameters

Visual Basic allows missing parameters in methods. For example, the Recordset object Open method has five parameters, but you can skip intermediate parameters and leave off trailing parameters. A default BSTR or Variant will be substituted depending on the data type of the missing operand.

In C/C++, all operands must be specified. If you want to specify a missing parameter whose data type is a string, specify a _bstr_t containing a null string. If you want to specify a missing parameter whose data type is a Variant, specify a _variant_t with a value of DISP_E_PARAMNOTFOUND and a type of VT_ERROR. Alternatively, specify the equivalent _variant_t constant, vtMissing, which is supplied by the #import directive.

Three methods are exceptions to the typical use of vtMissing. These are the Execute methods of the Connection and Command objects, and the NextRecordset method of the Recordset object. The following are their signatures:

_RecordsetPtr Execute( _bstr_t CommandText, VARIANT * RecordsAffected,
        long Options );  // Connection
_RecordsetPtr Execute( VARIANT * RecordsAffected, VARIANT * Parameters,
        long Options );  // Command
_RecordsetPtr NextRecordset( VARIANT * RecordsAffected );  // Recordset

The parameters, RecordsAffected and Parameters, are pointers to a Variant. Parameters is an input parameter which specifies the address of a Variant containing a single parameter, or array of parameters, that will modify the command being executed. RecordsAffected is an output parameter that specifies the address of a Variant, where the number of rows affected by the method is returned.

In the Command object Execute method, indicate that no parameters are specified by setting Parameters to either &vtMissing (which is recommended) or to the null pointer (that is, NULL or zero (0)). If Parameters is set to the null pointer, the method internally substitutes the equivalent of vtMissing, and then completes the operation.

In all the methods, indicate that the number of records affected should not be returned by setting RecordsAffected to the null pointer. In this case, the null pointer is not so much a missing parameter as an indication that the method should discard the number of records affected.

Thus, for these three methods, it is valid to code something such as:

pConnection->Execute("commandText", NULL, adCmdText);
pCommand->Execute(NULL, NULL, adCmdText);
pRecordset->NextRecordset(NULL);

Error Handling

In COM, most operations return an HRESULT return code that indicates whether a function completed successfully. The #import directive generates wrapper code around each "raw" method or property and checks the returned HRESULT. If the HRESULT indicates failure, the wrapper code throws a COM error by calling _com_issue_errorex() with the HRESULT return code as an argument. COM error objects can be caught in a try-catch block. (For efficiency's sake, catch a reference to a _com_error object.)

Remember, these are ADO errors: they result from the ADO operation failing. Errors returned by the underlying provider appear as Error objects in the Connection object Errors collection.

The #import directive creates only error handling routines for methods and properties declared in the ADO .dll. However, you can take advantage of this same error handling mechanism by writing your own error checking macro or inline function. See the topic, Visual C++ Extensions, or the code in the following sections for examples.

Visual C++ Equivalents of Visual Basic Conventions

The following is a summary of several conventions in the ADO documentation, coded in Visual Basic, as well as their equivalents in Visual C++.

Declaring an ADO Object

In Visual Basic, an ADO object variable (in this case for a Recordset object) is declared as follows:

Dim rst As ADODB.Recordset

The clause, "ADODB.Recordset", is the ProgID of the Recordset object as defined in the Registry. A new instance of a Record object is declared as follows:

Dim rst As New ADODB.Recordset

-or-

Dim rst As ADODB.Recordset
Set rst = New ADODB.Recordset

In Visual C++, the #import directive generates smart pointer-type declarations for all the ADO objects. For example, a variable that points to a _Recordset object is of type _RecordsetPtr, and is declared as follows:

_RecordsetPtr  rs;

A variable that points to a new instance of a _Recordset object is declared as follows:

_RecordsetPtr  rs("ADODB.Recordset");

-or-

_RecordsetPtr  rs;
rs.CreateInstance("ADODB.Recordset");

-or-

_RecordsetPtr  rs;
rs.CreateInstance(__uuidof(_Recordset));

After the CreateInstance method is called, the variable can be used as follows:

rs->Open(...);

Notice that in one case, the "." operator is used as if the variable were an instance of a class (rs.CreateInstance), and in another case, the "->" operator is used as if the variable were a pointer to an interface (rs->Open).

One variable can be used in two ways because the "->" operator is overloaded to allow an instance of a class to behave like a pointer to an interface. A private class member of the instance variable contains a pointer to the _Recordset interface; the "->" operator returns that pointer; and the returned pointer accesses the members of the _Recordset object.

Coding a Missing Parameter — String

When you need to code a missing String operand in Visual Basic, you merely omit the operand. You must specify the operand in Visual C++. Code a _bstr_t that has an empty string as a value.

_bstr_t strMissing(L"");

Coding a Missing Parameter — Variant

When you need to code a missing Variant operand in Visual Basic, you merely omit the operand. You must specify all operands in Visual C++. Code a missing Variant parameter with a _variant_t set to the special value, DISP_E_PARAMNOTFOUND, and type, VT_ERROR. Alternatively, specify vtMissing, which is an equivalent pre-defined constant supplied by the #import directive.

_variant_t  vtMissingYours(DISP_E_PARAMNOTFOUND, VT_ERROR);

-or use -

...vtMissing...;

Declaring a Variant

In Visual Basic, a Variant is declared with the Dim statement as follows:

Dim VariableName As Variant

In Visual C++, declare a variable as type _variant_t. A few schematic _variant_t declarations are shown below.

Note These declarations merely give a rough idea of what you would code in your own program. For more information, see the examples below, and the Visual C++ documentation.

_variant_t  VariableName(value);
_variant_t  VariableName((data type cast) value);
_variant_t  VariableName(value, VT_DATATYPE);
_variant_t  VariableName(interface * value, bool fAddRef = true);

Using Arrays of Variants

In Visual Basic, arrays of Variants can be coded with the Dim statement, or you may use the Array function, as demonstrated in the following example code:

Public Sub ArrayOfVariants
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim fld As ADODB.Field

cn.Open "DSN=pubs", "sa", ""
rs = cn.OpenSchema(adSchemaColumns, _
                     Array(Empty, Empty, "authors", Empty))
For Each fld in rs.Fields
   Debug.Print "Name = "; fld.Name
Next fld
rs.Close
cn.Close
End Sub

The following Visual C++ example demonstrates using a SafeArray used with a _variant_t.

Notes The following notes correspond to commented sections in the code example.

1. Once again, the TESTHR() inline function is defined to take advantage of the existing error-handling mechanism.

2. You only need a one-dimensional array, so you can use SafeArrayCreateVector, instead of the general purpose SAFEARRAYBOUND declaration and SafeArrayCreate function. The following is what that code would look like using SafeArrayCreate:

   SAFEARRAYBOUND   sabound[1];
   sabound[0].lLbound = 0;
   sabound[0].cElements = 4;
   pSa = SafeArrayCreate(VT_VARIANT, 1, sabound);

3. The schema identified by the enumerated constant, adSchemaColumns, is associated with four constraint columns: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, and COLUMN_NAME. Therefore, an array of Variant values with four elements is created. Then a constraint value that corresponds to the third column, TABLE_NAME, is specified.

The Recordset that is returned consists of several columns, a subset of which is the constraint columns. The values of the constraint columns for each returned row must be the same as the corresponding constraint values.

4. Those familiar with SafeArrays may be surprised that SafeArrayDestroy() is not called before the exit. In fact, calling SafeArrayDestroy() in this case will cause a run-time exception. The reason is that the destructor for vtCriteria will call VariantClear() when the _variant_t goes out of scope, which will free the SafeArray. Calling SafeArrayDestroy, without manually clearing the _variant_t, would cause the destructor to try to clear an invalid SafeArray pointer.

If SafeArrayDestroy were called, the code would look like this:

   TESTHR(SafeArrayDestroy(pSa));
   vtCriteria.vt = VT_EMPTY;
   vtCriteria.parray = NULL;

However, it is much simpler to let the _variant_t manage the SafeArray.

#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
   no_namespace rename("EOF", "EndOfFile")
#include <stdio.h>

// Note 1
inline void TESTHR( HRESULT _hr )
   { if FAILED(_hr) _com_issue_error(_hr); }

void main(void)
{
   CoInitialize(NULL);
   try
   {
   _RecordsetPtr   pRs("ADODB.Recordset");
   _ConnectionPtr  pCn("ADODB.Connection");
   _variant_t      vtTableName("authors"),
                   vtCriteria;
   long            ix[1];
   SAFEARRAY       *pSa = NULL;

   pCn->Open("DSN=pubs;User ID=sa;pwd=;Provider=MSDASQL;", "", "",
               adConnectUnspecified);
// Note 2, Note 3
   pSa = SafeArrayCreateVector(VT_VARIANT, 1, 4);
   if (!pSa) _com_issue_error(E_OUTOFMEMORY);

// Specify TABLE_NAME in the third array element (index of 2).

   ix[0] = 2;
   TESTHR(SafeArrayPutElement(pSa, ix, &vtTableName));

//   There is no Variant constructor for a SafeArray, so manually set the
//   type (SafeArray of Variant) and value (pointer to a SafeArray).

   vtCriteria.vt = VT_ARRAY | VT_VARIANT;
   vtCriteria.parray = pSa;

   pRs = pCn->OpenSchema(adSchemaColumns, vtCriteria, vtMissing);

   long limit = pRs->GetFields()->Count;
   for (long x = 0; x < limit; x++)
      printf("%d: %s\n", x+1,
         ((char*) pRs->GetFields()->Item[x]->Name));
// Note 4
   pRs->Close();
   pCn->Close();
   }
   catch (_com_error &e)
   {
   printf("Error:\n");
   printf("Code = %08lx\n", e.Error());
   printf("Code meaning = %s\n", (char*) e.ErrorMessage());
   printf("Source = %s\n", (char*) e.Source());
   printf("Description = %s\n", (char*) e.Description());
   }
   CoUninitialize();
}

Using Property Get/Put/PutRef

In Visual Basic, the name of a property is not qualified by whether it is retrieved, assigned, or assigned a reference.

Public Sub GetPutPutRef
Dim rs As New ADODB.Recordset
Dim cn As New ADODB.Connection
Dim sz as Integer
cn.Open "Provider=sqloledb;Data Source=yourserver;" & _
         "Initial Catalog=pubs;User Id=sa;Password=;"
rs.PageSize = 10
sz = rs.PageSize
rs.ActiveConnection = cn
rs.Open "authors",,adOpenStatic
' ...
rs.Close
cn.Close
End Sub

This Visual C++ example demonstrates the Get/Put/PutRef Property.

Notes The following notes correspond to commented sections in the code example.

1. This example uses two forms of a missing string argument: an explicit constant, strMissing, and a string that the compiler will use to create a temporary _bstr_t that will exist for the scope of the Open method.

2. It isn't necessary to cast the operand of rs->PutRefActiveConnection(cn) to (IDispatch *) because the type of the operand is already (IDispatch *).

#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
   no_namespace rename("EOF", "EndOfFile")
#include <stdio.h>

void main(void)
{
   CoInitialize(NULL);
   try
   {
      _ConnectionPtr  cn("ADODB.Connection");
      _RecordsetPtr   rs("ADODB.Recordset");
      _bstr_t         strMissing(L"");
      long            oldPgSz = 0,
                      newPgSz = 5;

// Note 1
      cn->Open("Provider=sqloledb;Data Source=MyServer;"
         "Initial Catalog=pubs;User Id=sa;Password=;",
         strMissing, "",
         adConnectUnspecified);

      oldPgSz = rs->GetPageSize();
   // -or-
      oldPgSz = rs->PageSize;

      rs->PutPageSize(newPgSz);
   // -or-
      rs->PageSize = newPgSz;

// Note 2
      rs->PutRefActiveConnection( cn );
      rs->Open("authors", vtMissing, adOpenStatic, adLockReadOnly,
               adCmdTable);
      printf("Original pagesize = %d, new pagesize = %d\n", oldPgSz,
               rs->GetPageSize());
      rs->Close();
      cn->Close();
   }
   catch (_com_error &e)
   {
      printf("Description = %s\n", (char*) e.Description());
   }
   ::CoUninitialize();
}

Using GetItem(x) and Item[x]

This Visual Basic example demonstrates the standard and alternative syntax for Item().

Public Sub GetItemItem
Dim rs As New ADODB.Recordset
Dim name as String
rs = rs.Open "authors", "DSN=pubs;", adOpenDynamic, _
         adLockBatchOptimistic, adTable
name = rs(0)
' -or-
name = rs.Fields.Item(0)
rs(0) = "Test"
rs.UpdateBatch
' Restore name
rs(0) = name
rs.UpdateBatch
rs.Close
End Sub

This Visual C++ example demonstrates Item.

Note The following note corresponds to commented sections in the code example.

1. When the collection is accessed with Item, the index, 2, must be cast to long so an appropriate constructor will be invoked.

#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
   no_namespace rename("EOF", "EndOfFile")
#include <stdio.h>

void main(void)
{
   CoInitialize(NULL);
   try {
      _RecordsetPtr   rs("ADODB.Recordset");
      _variant_t      vtFirstName;

      rs->Open("authors",
               "Provider=sqloledb;Data Source=MyServer;"
               "Initial Catalog=pubs;User Id=sa;Password=;",
               adOpenStatic, adLockOptimistic, adCmdTable);
      rs->MoveFirst();

// Note 1. Get a field.
      vtFirstName = rs->Fields->GetItem((long)2)->GetValue();
   // -or-
      vtFirstName = rs->Fields->Item[(long)2]->Value;

      printf( "First name = '%s'\n", (char*) ((_bstr_t) vtFirstName));

      rs->Fields->GetItem((long)2)->Value = L"TEST";
      rs->Update(vtMissing, vtMissing);

   // Restore name
      rs->Fields->GetItem((long)2)->PutValue(vtFirstName);
      // -or-
      rs->Fields->GetItem((long)2)->Value = vtFirstName;
      rs->Update(vtMissing, vtMissing);
      rs->Close();
   }
   catch (_com_error &e)
   {
      printf("Description = '%s'\n", (char*) e.Description());
   }
   ::CoUninitialize();
}

Casting ADO object pointers with (IDispatch *)

The following Visual C++ example demonstrates using (IDispatch *) to cast ADO object pointers.

Notes The following notes correspond to commented sections in the code example.

1. Specify an open Connection object in an explicitly coded Variant. Cast it with (IDispatch *) so the correct constructor will be invoked. Also, explicitly set the second _variant_t parameter to the default value of true, so the object reference count will be correct when the Recordset::Open operation ends.

2. The expression, (_bstr_t), is not a cast, but a _variant_t operator that extracts a _bstr_t string from the Variant returned by Value.

The expression, (char*), is not a cast, but a _bstr_t operator that extracts a pointer to the encapsulated string in a _bstr_t object.

This section of code demonstrates some of the useful behaviors of _variant_t and _bstr_t operators.

#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>

void main(void)
{
   CoInitialize(NULL);
   try
   {
      _ConnectionPtr pConn("ADODB.Connection");
      _RecordsetPtr  pRst("ADODB.Recordset");

      pConn->Open("Provider=sqloledb;Data Source=MyServer;"
         "Initial Catalog=pubs;User Id=sa;Password=;",
         "", "", adConnectUnspecified);
// Note 1.
      pRst->Open(
         "authors",
         _variant_t((IDispatch *) pConn, true),
         adOpenStatic,
         adLockReadOnly,
         adCmdTable);
      pRst->MoveLast();
// Note 2.
      printf("Last name is '%s %s'\n",
            (char*) ((_bstr_t) pRst->GetFields()->GetItem("au_fname")->GetValue()),
            (char*) ((_bstr_t) pRst->Fields->Item["au_lname"]->Value));

      pRst->Close();
      pConn->Close();
   }
   catch (_com_error &e)
   {
      printf("Description = '%s'\n", (char*) e.Description());
   }
::CoUninitialize();
}


Visual C++ Extensions for ADO

The preferred method of programming ADO with Visual C++ is using the #import directive, as discussed in Microsoft Visual C++ ADO Programming. However, earlier versions of ADO shipped with an alternate method of programming using Visual C++: the Visual C++ Extensions. This section documents this feature for those who must maintain Visual C++ Extensions code, but new ADO code should be written using #import.

One of the most tedious jobs Visual C++ programmers face when retrieving data with ADO is converting data returned as a VARIANT data type into a C++ data type, and then storing the converted data in a class or structure. In addition to being cumbersome, retrieving C++ data through a VARIANT data type diminishes performance.

ADO provides an interface that supports retrieving data into native C/C++ data types without going through a VARIANT, and also provides preprocessor macros that simplify using the interface. The result is a flexible tool that is easier to use and has great performance.

A common C/C++ client scenario is to bind a record in a Recordset to a C/C++ struct or class containing native C/C++ types. When going through VARIANTs, this involves writing conversion code from VARIANT to C/C++ native types. The Visual C++ Extensions for ADO are targeted at making this scenario much easier for the Visual C++ programmer.

See the following topics to learn more about the Visual C++ Extensions for ADO.

  • Using Visual C++ Extensions for ADO
  • Visual C++ Extensions Header
  • ADO with Visual C++ Extensions Example

See Also

ADO for Visual C++ Syntax Index for COM | ADO with Visual C++ Extensions Example | Using Visual C++ Extensions for ADO | Visual C++ Extensions Header


Using Visual C++ Extensions

The IADORecordBinding Interface

The Microsoft Visual C++ Extensions for ADO associate, or bind, fields of a Recordset object to C/C++ variables. Whenever the current row of the bound Recordset changes, all the bound fields in the Recordset are copied to the C/C++ variables. If necessary, the copied data is converted to the declared data type of the C/C++ variable.

The BindToRecordset method of the IADORecordBinding interface binds fields to C/C++ variables. The AddNew method adds a new row to the bound Recordset. The Update method populates fields in new rows of the Recordset, or updates fields in existing rows, with the value of the C/C++ variables.

The IADORecordBinding interface is implemented by the Recordset object. You do not code the implementation yourself.

Binding Entries

The Visual C++ Extensions for ADO map fields of a Recordset object to C/C++ variables. The definition of a mapping between a field and a variable is called a binding entry. Macros provide binding entries for numeric, fixed-length, and variable-length data. The binding entries and C/C++ variables are declared in a class derived from the Visual C++ Extensions class, CADORecordBinding. The CADORecordBinding class is defined internally by the binding entry macros.

ADO internally maps the parameters in these macros to an OLE DB DBBINDING structure and creates an OLE DB Accessor object to manage the movement and conversion of data between fields and variables. OLE DB defines data as consisting of three parts: A buffer where the data is stored; a status that indicates whether a field was successfully stored in the buffer, or how the variable should be restored to the field; and the length of the data. (See the OLE DB Programmer's Reference, Chapter 6: Getting and Setting Data for more information.)

Header File

Include the following file in your application in order to use the Visual C++ Extensions for ADO:

#include <icrsint.h>

Binding Recordset Fields

To Bind Recordset Fields to C/C++ Variables

  1. Create a class derived from the CADORecordBinding class.
  2. Specify binding entries and corresponding C/C++ variables in the derived class. Bracket the binding entries between BEGIN_ADO_BINDING and END_ADO_BINDING macros. Do not terminate the macros with commas or semicolons. Appropriate delimiters are specified automatically by each macro.

    Specify one binding entry for each field to be mapped to a C/C++ variable. Use an appropriate member from the ADO_FIXED_LENGTH_ENTRY, ADO_NUMERIC_ENTRY, or ADO_VARIABLE_LENGTH_ENTRY family of macros.

  3. In your application, create an instance of the class derived from CADORecordBinding. Get the IADORecordBinding interface from the Recordset. Then call the BindToRecordset method to bind the Recordset fields to the C/C++ variables.

See the Visual C++ Extensions Example for more information.

Interface Methods

The IADORecordBinding interface has three methods: BindToRecordset, AddNew, and Update. The sole argument to each method is a pointer to an instance of the class derived from CADORecordBinding. Therefore, the AddNew and Update methods cannot specify any of the parameters of their ADO method namesakes.

Syntax

The BindToRecordset method associates the Recordset fields with C/C++ variables.

BindToRecordset(CADORecordBinding *binding)

The AddNew method invokes its namesake, the ADO AddNew method, to add a new row to the Recordset.

AddNew(CADORecordBinding *binding)

The Update method invokes its namesake, the ADO Update method, to update the Recordset.

Update(CADORecordBinding *binding)

Binding Entry Macros

Binding entry macros define the association of a Recordset field and a variable. A beginning and ending macro delimits the set of binding entries.

Families of macros are provided for fixed-length data, such as adDate or adBoolean; numeric data, such as adTinyInt, adInteger, or adDouble; and variable-length data, such as adChar, adVarChar or adVarBinary. All numeric types, except for adVarNumeric, are also fixed-length types. Each family has differing sets of parameters so that you can exclude binding information that is of no interest.

See the OLE DB Programmer's Reference, Appendix A: Data Types for additional information.

Begin Binding Entries

BEGIN_ADO_BINDING(Class)

Fixed-Length Data

ADO_FIXED_LENGTH_ENTRY(Ordinal, DataType, Buffer, Status, Modify)
ADO_FIXED_LENGTH_ENTRY2(Ordinal, DataType, Buffer, Modify)

Numeric Data

ADO_NUMERIC_ENTRY(Ordinal, DataType, Buffer, Precision, Scale, Status,
Modify)
ADO_NUMERIC_ENTRY2(Ordinal, DataType, Buffer, Precision, Scale, Modify)

Variable-Length Data

ADO_VARIABLE_LENGTH_ENTRY(Ordinal, DataType, Buffer, Size, Status,
Length, Modify)
ADO_VARIABLE_LENGTH_ENTRY2(Ordinal, DataType, Buffer, Size, Status,
Modify)
ADO_VARIABLE_LENGTH_ENTRY3(Ordinal, DataType, Buffer, Size, Length,
Modify)
ADO_VARIABLE_LENGTH_ENTRY4(Ordinal, DataType, Buffer, Size, Modify)

End Binding Entries

END_ADO_BINDING()

Parameter | Description
Class | Class in which the binding entries and C/C++ variables are defined.
Ordinal | Ordinal number, counting from one, of the Recordset field corresponding to your C/C++ variable.
DataType | Equivalent ADO data type of the C/C++ variable (see DataTypeEnum for a list of valid data types). The value of the Recordset field will be converted to this data type if necessary.
Buffer | Name of the C/C++ variable where the Recordset field will be stored.
Size | Maximum size in bytes of Buffer. If Buffer will contain a variable-length string, allow room for a terminating zero.
Status | Name of a variable that will indicate whether the contents of Buffer are valid, and whether the conversion of the field to DataType was successful. The two most important values for this variable are adFldOK, which means the conversion was successful; and adFldNull, which means the value of the field was null, not merely empty. Possible values for Status are listed in the next table, "Status Values."
Modify | Boolean flag; if TRUE, indicates ADO is allowed to update the corresponding Recordset field with the value contained in Buffer. Set the Boolean modify parameter to TRUE to enable ADO to update the bound field, and FALSE if you want to examine the field but not change it.
Precision | Number of digits that can be represented in a numeric variable.
Scale | Number of decimal places in a numeric variable.
Length | Name of a four-byte variable that will contain the actual length of the data in Buffer.

Status Values

The value of the Status variable indicates whether a field was successfully copied to a variable.

When setting data, Status may be set to adFldNull to indicate the Recordset field should be set to null.

Constant | Value | Description
adFldOK | 0 | A non-null field value was returned.
adFldBadAccessor | 1 | Binding was invalid.
adFldCantConvertValue | 2 | Value couldn't be converted for reasons other than sign mismatch or data overflow.
adFldNull | 3 | When getting a field, indicates a null value was returned. When setting a field, indicates the field should be set to NULL when the field cannot encode NULL itself (for example, a character array or an integer).
adFldTruncated | 4 | Variable-length data or numeric digits were truncated.
adFldSignMismatch | 5 | Value is signed and variable data type is unsigned.
adFldDataOverFlow | 6 | Value is larger than could be stored in the variable data type.
adFldCantCreate | 7 | Unknown column type and field already open.
adFldUnavailable | 8 | Field value could not be determined—for example, on a new, unassigned field with no default value.
adFldPermissionDenied | 9 | When updating, no permission to write data.
adFldIntegrityViolation | 10 | When updating, field value would violate column integrity.
adFldSchemaViolation | 11 | When updating, field value would violate column schema.
adFldBadStatus | 12 | When updating, invalid status parameter.
adFldDefault | 13 | When updating, a default value was used.

See Also

ADO with Visual C++ Extensions Example | Visual C++ Extensions Header


Visual C++ Extensions Header

The following header, icrsint.h, details the interface that allow clients to retrieve fields from a Recordset into variables defined in a class derived from CADORecordBinding. You must specify an ADO binding macro for each field you intend to access.

#ifndef _ICRSINT_H_
#define _ICRSINT_H_

#include <olectl.h>
#include <stddef.h>

// forwards
class CADORecordBinding;

#define classoffset(base, derived) ((DWORD)(static_cast<base*>((derived*)8))-8)

enum ADOFieldStatusEnum
{
   adFldOK = 0,
   adFldBadAccessor = 1,
   adFldCantConvertValue = 2,
   adFldNull = 3,
   adFldTruncated = 4,
   adFldSignMismatch = 5,
   adFldDataOverFlow = 6,
   adFldCantCreate = 7,
   adFldUnavailable = 8,
   adFldPermissionDenied = 9,
   adFldIntegrityViolation = 10,
   adFldSchemaViolation = 11,
   adFldBadStatus = 12,
   adFldDefault = 13
};

typedef struct stADO_BINDING_ENTRY
{
   ULONG      ulOrdinal;
   WORD       wDataType;
   BYTE       bPrecision;
   BYTE       bScale;
   ULONG      ulSize;
   ULONG      ulBufferOffset;
   ULONG      ulStatusOffset;
   ULONG      ulLengthOffset;
   ULONG      ulADORecordBindingOffSet;
   BOOL       fModify;
} ADO_BINDING_ENTRY;

#define BEGIN_ADO_BINDING(cls) public: \
   typedef cls ADORowClass; \
   const ADO_BINDING_ENTRY* STDMETHODCALLTYPE GetADOBindingEntries() { \
   static const ADO_BINDING_ENTRY rgADOBindingEntries[] = {

//
// Fixed length non-numeric data
//
#define ADO_FIXED_LENGTH_ENTRY(Ordinal, DataType, Buffer, Status, Modify)\
   {Ordinal, \
   DataType, \
   0, \
   0, \
   0, \
   offsetof(ADORowClass, Buffer), \
   offsetof(ADORowClass, Status), \
   0, \
   classoffset(CADORecordBinding, ADORowClass), \
   Modify},

#define ADO_FIXED_LENGTH_ENTRY2(Ordinal, DataType, Buffer, Modify)\
   {Ordinal, \
   DataType, \
   0, \
   0, \
   0, \
   offsetof(ADORowClass, Buffer), \
   0, \
   0, \
   classoffset(CADORecordBinding, ADORowClass), \
   Modify},

//
// Numeric data
//
#define ADO_NUMERIC_ENTRY(Ordinal, DataType, Buffer, Precision, Scale, Status, Modify)\
   {Ordinal, \
   DataType, \
   Precision, \
   Scale, \
   0, \
   offsetof(ADORowClass, Buffer), \
   offsetof(ADORowClass, Status), \
   0, \
   classoffset(CADORecordBinding, ADORowClass), \
   Modify},

#define ADO_NUMERIC_ENTRY2(Ordinal, DataType, Buffer, Precision, Scale, Modify)\
   {Ordinal, \
   DataType, \
   Precision, \
   Scale, \
   0, \
   offsetof(ADORowClass, Buffer), \
   0, \
   0, \
   classoffset(CADORecordBinding, ADORowClass), \
   Modify},

//
// Variable length data
//
#define ADO_VARIABLE_LENGTH_ENTRY(Ordinal, DataType, Buffer, Size, Status, Length, Modify)\
   {Ordinal, \
   DataType, \
   0, \
   0, \
   Size, \
   offsetof(ADORowClass, Buffer), \
   offsetof(ADORowClass, Status), \
   offsetof(ADORowClass, Length), \
   classoffset(CADORecordBinding, ADORowClass), \
   Modify},

#define ADO_VARIABLE_LENGTH_ENTRY2(Ordinal, DataType, Buffer, Size, Status, Modify)\
   {Ordinal, \
   DataType, \
   0, \
   0, \
   Size, \
   offsetof(ADORowClass, Buffer), \
   offsetof(ADORowClass, Status), \
   0, \
   classoffset(CADORecordBinding, ADORowClass), \
   Modify},

#define ADO_VARIABLE_LENGTH_ENTRY3(Ordinal, DataType, Buffer, Size, Length, Modify)\
   {Ordinal, \
   DataType, \
   0, \
   0, \
   Size, \
   offsetof(ADORowClass, Buffer), \
   0, \
   offsetof(ADORowClass, Length), \
   classoffset(CADORecordBinding, ADORowClass), \
   Modify},

#define ADO_VARIABLE_LENGTH_ENTRY4(Ordinal, DataType, Buffer, Size, Modify)\
   {Ordinal, \
   DataType, \
   0, \
   0, \
   Size, \
   offsetof(ADORowClass, Buffer), \
   0, \
   0, \
   classoffset(CADORecordBinding, ADORowClass), \
   Modify},

#define END_ADO_BINDING()   {0, adEmpty, 0, 0, 0, 0, 0, 0, 0, FALSE}};\
   return rgADOBindingEntries;}

//
// Interface that the client 'record' class needs to support. The ADO Binding entries
// provide the implementation for this interface.
//
class CADORecordBinding
{
public:
   STDMETHOD_(const ADO_BINDING_ENTRY*, GetADOBindingEntries) (VOID) PURE;
};

//
// Interface that allows a client to fetch a record of data into class data members.
//
struct __declspec(uuid("00000544-0000-0010-8000-00aa006d2ea4")) IADORecordBinding;
DECLARE_INTERFACE_(IADORecordBinding, IUnknown)
{
public:
   STDMETHOD(BindToRecordset) (CADORecordBinding *pAdoRecordBinding) PURE;
   STDMETHOD(AddNew) (CADORecordBinding *pAdoRecordBinding) PURE;
   STDMETHOD(Update) (CADORecordBinding *pAdoRecordBinding) PURE;
};

#endif // !_ICRSINT_H_

See Also

ADO with Visual C++ Extensions Example | Using Visual C++ Extensions for ADO


Visual C++ Extensions Example

This program shows how values are retrieved from fields and converted to C/C++ variables.

This example also takes advantage of "smart pointers," which automatically handle the COM-specific details of calling QueryInterface and reference counting for the IADORecordBinding interface.

Without smart pointers, you would code:

IADORecordBinding   *picRs = NULL;
...
TESTHR(pRs->QueryInterface(
          __uuidof(IADORecordBinding), (LPVOID*)&picRs));
...
if (picRs) picRs->Release();

With smart pointers, you derive the IADORecordBindingPtr type from the IADORecordBinding interface with this statement:

_COM_SMARTPTR_TYPEDEF(IADORecordBinding, __uuidof(IADORecordBinding));

And instantiate the pointer like this:

IADORecordBindingPtr picRs(pRs);

Because the Visual C++ Extensions are implemented by the Recordset object, the constructor for the smart pointer, picRs, takes the _RecordsetPtr pointer, pRs. The constructor calls QueryInterface using pRs to find the IADORecordBinding interface.

// Visual C++ Extensions Example
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
   no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>
#include <icrsint.h>
_COM_SMARTPTR_TYPEDEF(IADORecordBinding, __uuidof(IADORecordBinding));

inline void TESTHR(HRESULT _hr) { if FAILED(_hr) _com_issue_error(_hr); }

class CCustomRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CCustomRs)
   ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_ch_fname,
                        sizeof(m_ch_fname), m_ul_fnameStatus, false)
   ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_ch_lname,
                        sizeof(m_ch_lname), m_ul_lnameStatus, false)
END_ADO_BINDING()
public:
   CHAR    m_ch_fname[22];
   CHAR    m_ch_lname[32];
   ULONG   m_ul_fnameStatus;
   ULONG   m_ul_lnameStatus;
};

void main(void)
{
   ::CoInitialize(NULL);
   try
      {
      _RecordsetPtr pRs("ADODB.Recordset");
      CCustomRs rs;
      IADORecordBindingPtr picRs(pRs);

      pRs->Open("SELECT * FROM Employee ORDER BY lname",
         "dsn=pubs;uid=sa;pwd=;",
         adOpenStatic, adLockOptimistic, adCmdText);

      TESTHR(picRs->BindToRecordset(&rs));

      while (!pRs->EndOfFile)
         {
      // Process data in the CCustomRs C++ instance variables.
         printf("Name = %s %s\n",
            (rs.m_ul_fnameStatus == adFldOK ? rs.m_ch_fname: "<Error>"),
            (rs.m_ul_lnameStatus == adFldOK ? rs.m_ch_lname: "<Error>"));

      // Move to the next row of the Recordset.
      // Fields in the new row will automatically be
      // placed in the CCustomRs C++ instance variables.

         pRs->MoveNext();
         }
      }
   catch (_com_error &e )
      {
      printf("Error:\n");
      printf("Code = %08lx\n", e.Error());
      printf("Meaning = %s\n", e.ErrorMessage());
      printf("Source = %s\n", (LPCSTR) e.Source());
      printf("Description = %s\n", (LPCSTR) e.Description());
      }
   ::CoUninitialize();
}

See Also

Using Visual C++ Extensions for ADO | Visual C++ Extensions Header


Using ADO with Microsoft Visual J++

You can implement ADO using Visual J++ in the following ways:

  • With Visual J++ 6.0 (or later), use ADO for Windows Foundation Classes.
  • With Visual J++ 1.x, use the Java Type Library Wizard.
  • Use the Microsoft SDK for Java utilities.

For further information about using ADO with Visual J++, see the following topics:

  • ADO Java Class Wrappers
  • ADO/WFC Syntax Index

See Also

ADO for Windows Foundation Classes | ADO Java Class Wrappers | ADO/WFC Syntax Index | Using ADO with Microsoft Visual Basic | Using ADO with Microsoft Visual C++ | Using ADO with Scripting Languages | Using ADO with the Java Type Library Wizard | Using ADO with the Microsoft SDK for Java


ADO/WFC Programming

For Microsoft Visual J++ 6.0, ADO has been extended to work with Windows Foundation Classes (WFC) in the following ways. First, a set of Java classes has been implemented that extends the ADO interfaces and creates notifications interesting to the Java programmer; the Java classes also expose functions that return Java types to the user. To improve performance, the Java class directly accesses the native data types in the OLE DB rowset object, and returns them to the Java programmer as Java types without first converting them to and from a variant. ADO has also been extended to work with event notifications in the WFC framework.

ADO for Windows Foundation Classes (ADO/WFC) supports all the standard ADO methods, properties, objects, and events. However, operations that require a variant as a parameter and show excellent performance in a language such as Microsoft Visual Basic, display lesser performance in a language such as Visual J++. For that reason, ADO/WFC also provides accessor functions on the Field object that take native Java data types instead of the variant data type.

For more detailed information within the ADO documentation about ADO/WFC packages, see the ADO/WFC Syntax Index.

For related information within the Visual J++ documentation, see Converting Visual Basic ADO Examples to Visual J++.

Referencing the Library

To import the ADO/WFC data classes into your project, include the following line in your code:

import com.ms.wfc.data.*;

See Also

ADO Event Instantiation by Language | ADO/WFC Syntax Index | Using ADO with the Java Type Library Wizard | Using ADO with the Microsoft SDK for Java


Using the Java Type Library Wizard

The Java Type Library Wizard is a feature of Visual J++ 1.x, integrated into the Tools menu of the development environment. Its purpose is to search a type library and create a Java interface that allows access to COM objects. For Visual J++ 6.0, the Java Type Library Wizard has been replaced with ADO for Windows Foundation Classes.

The Java Type Library Wizard produces similar results as the command-line tools included with the Microsoft SDK for Java. However, you cannot step into the class wrappers that the Wizard generates, unlike the class wrappers generated by the Microsoft SDK for Java.

The Java Type Library Wizard generates the classes in the following location: \<windows directory>\Java\trustlib\msado15. The Summary.txt file, located in the directory where the classes were generated, shows the class definitions it generated.

The Java Type Library Wizard converts enumerated types, found in any given type library, to type INT (integer). It also defines an interface that corresponds to each enumerated type in the type library. You can reference the values of an ADO enumerated type with the following syntax:

msado15.<Enum Name>.<constant Name>

An example of this is shown in the following code fragment for setting the CommandType property of a Command object:

Cmd1.putCommandType( msado15.CommandTypeEnum.adCmdStoredProc );

Alternately, you could inherit from the enumerated type wrapper generated by the Java Type Library Wizard. If so, you could remove "msado15." from the syntax. However, your class would need to inherit from each Java object and enumerated type interface that it references to completely eliminate the need to reference msado15.* in front of all ADO objects and enumerated values.

For more sample code, see ADO Java Class Wrappers.

To run the Java Type Libary Wizard from Visual J++ version 1.x

  1. From the Tools menu, select Java Type Library Wizard.
  2. Select "Microsoft ActiveX Data Objects Library" and click OK. This now (re)generates files in the \trustlib directory for ADO (by default at c:\winnt\java\trustlib\msado15). If you used the Microsoft SDK for Java to already generate classes for ADO, they will be replaced with those from the Java Type Library Wizard.
  3. To use these files, open your project in Visual J++. From the Project menu, choose Add To Project. Select Files, and add all of the .JAVA files generated in the \trustlib directory (by default at c:\winnt\java\trustlib\msado15) to your project.

See Also

ADO for Windows Foundation Classes | ADO Java Class Wrappers | Using ADO with the Microsoft SDK for Java


Using the Microsoft SDK for Java

The Microsoft SDK for Java is the developer kit for the Microsoft Internet Explorer environment. Tools, information, and samples are provided to help you develop Java programs and applets based on JDK 1.1 and the Microsoft Win32® virtual machine (Microsoft VM). The Microsoft SDK for Java is not tied to Microsoft Visual J++ and can be downloaded from http://www.microsoft.com/Java.

The Jactivex.exe utility generates classes from a type library but can only be invoked on the command line. This feature is not integrated with the Visual J++ development environment. Unlike the classes generated by the Java Type Library Wizard, you can step into the class wrappers created by the SDK. This is useful for debugging how your code uses the ADO wrapper classes.

This mechanism reads the ADO type library and generates classes that you can instantiate within your application. It generates those classes in the following location: \<windows directory>\Java\trustlib\msado15.

Creating an ADO application in Java using the Microsoft SDK for Java is fundamentally identical, from the perspective of source code, to using the Java Type Library Wizard. For sample code, see ADO Java Class Wrappers. The only real difference is in how you generate the wrapper classes in the first place, as demonstrated in the steps below.

To create an ADO project with the Microsoft SDK for Java

  1. Run the following from a command prompt. You need to set the path to include the Microsoft SDK for Java Bin directory, or run the command from that location. Typically, the Microsoft SDK for Java is installed in the same location as Visual Studio. This is a single command statement.
    \<path to DevStudio>\<path to Java SDK>\bin\JactiveX.exe /javatlb "C:\program files\common files\system\ado\msado15.dll"
  2. Run the following command to compile the generated classes. The /g:t switch turns on generation of debug symbols so that you can trace into the .Java symbols. Remove it for release builds.
    jvc /g:t c:\<windows>\Java\trustlib\msado15\*.Java
  3. To use these files, open your project in Visual J++. From the Project menu, choose Add To Project. Select Files, and add all of the .JAVA files generated in the trustlib\msado15 directory to your project.

See Also

ADO for Windows Foundation Classes | ADO Java Class Wrappers | Using ADO with the Java Type Library Wizard


ADO Java Class Wrappers

This code declares an instance of the ADO Recordset class wrapper and initializes it, all on the same line of code. Further, it declares variables for each of the arguments in the Open method, especially for LockType and CursorType (because Java doesn't support enumerated types). It opens and closes the Recordset object. Setting Rs1 to NULL merely schedules that variable to be released when Java performs its systematic and intermittent release of unused objects.

public static void main( String args[])
{
   msado15._Recordset   Rs1 = new msado15.Recordset();
   Variant Source     = new Variant( "SELECT * FROM Authors" );
   Variant Connect    = new Variant( "DSN=AdoDemo;UID=admin;PWD=;" );
   int     LockType   = msado15.CursorTypeEnum.adOpenForwardOnly;
   int     CursorType = msado15.LockTypeEnum.adLockReadOnly;
   int     Options    = -1;

   Rs1.Open( Source, Connect, LockType,  CursorType, Options );
   Rs1.Close();
   Rs1 = null;

   System.out.println( "Success!\n" );
}

See Also

Using ADO with the Java Type Library Wizard | Using ADO with the Microsoft SDK for Java


Appendix D: ADO Samples

The MDAC SDK contains several sample applications that demonstrate usage of ADO and RDS code. For more information about these samples, see Sample Applications.

The ADO documentation also contains code examples in multiple languages, which are included as topics within this online help. For more information about these examples, see ADO Code Examples.

Many other sample code resources are available. Visual Studio documentation and the MSDN Library contain a variety of sample applications and documentation that can help you learn about ADO and RDS.

Visual Studio Samples

  • Visual Studio Solutions Center Web site
  • Island Hopper News Sample
  • Visual Basic Samples
  • Visual C++ Samples
  • Visual InterDev Samples
  • Visual J++ Samples

Visual Studio Documentation

  • Visual Studio: Data Access and Security Strategies
  • Visual Basic: Data Access Guide
  • Visual InterDev: Integrating Databases
  • Visual J++: Accessing Data

See Also

ADO API Reference | Configuring RDS | ActiveX Data Objects Start Page | Programming with ADO | Appendix A: Providers | What's New in ADO


Sample Applications

Sample applications are included with the MDAC SDK for ADO and RDS. These samples are intended to help customers learn how to use ADO and RDS, but are not intended to be deployed on production servers.

Note Companies, names, and data used in samples are fictitious unless otherwise noted.

ADO includes several sample applications. From the Microsoft Platform SDK root folder, they are installed to C:\Program Files\Microsoft Platform SDK\Samples\DataAccess. For more information about installing and locating items in the Platform SDK, see Locating MDAC Components.

To get started with the ADO and RDS sample applications, open default.htm from the ADO and RDS samples folders, respectively. These files describe each available sample application and explain how to configure and run the samples how to browse the sample source code.

See Also

ADO Code Examples | ADO Code Examples in Microsoft Visual Basic | ADO Code Examples in Microsoft Visual Basic Scripting Edition | ADO Code Examples in Microsoft Visual C++ | ADO Code Examples in Microsoft Visual J++


ADO Glossary

ADO Glossary

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

A

absolute URL

ActiveX control

ADISAPI

Advanced Data Internet Server Application Programming Interface

aggregate function

alias

apartment threading

asynchronous operation

B

binding entry

bitmask

bookmark

business object

business rule

C

calculated expression

chapter

chapter-alias

character set

child

child-alias

class identifier

CLSID

client tier

COM

COM component

comparison operator

component

Component Object Model

compound file

constant

cursor

D

data binding

data definition language

data manipulation language

data provider

data shaping

data source name

data source tier

DCOM

DDL

default stream

disconnected recordset

distributed application

Distributed Component Object Model

DLL

DML

document source provider

DSN

dynamic-link library

dynamic property

E

enumeration

event

event handler

F

G

H

handler

hierarchical recordset

hierarchy

I

Internet Server Application Programming Interface

ISAPI

J

K

key

L

M

marshaling

middle tier

MIME

Multi-purpose Internet Mail Extension

N

node

O

object variable

ODBC

OLE DB

Open Database Connectivity

optimistic locking

ordinal value

P

parameterized command

parent

parent-alias

parent-child relationship

persist

pessimistic locking

pooling

ProgID

programmatic identifier

proxy

Q

R

relative URL

remote data source

resource record

root

rowset

S

schema

scope

service provider

shaped Recordset

sibling

stored procedure

stub

sub-node

synchronous operation

T

tree

U

Uniform Resource Locator

URL

V

W

Web server

X

Y

Z


ADO Programmer's Reference

ADO Programmer's Reference

This section of the ADO documentation contains reference topics for ADO, RDS, ADO MD, and ADOX.

  • ADO API Reference
  • RDS API Reference
  • ADO MD API Reference
  • ADOX API Reference


ADO API Reference

ADO API Reference

This section of the ADO documentation contains topics for each ADO object, collection, property, dynamic property, method, event, and enumeration. In addition, it contains a list of ADO syntax indexes to be used with Microsoft Visual C++ and Windows Foundation Classes (WFC).

For more information, search for a specific topic in the index or refer to the following topics:

  • ADO Object Model
  • ADO Objects
  • ADO Collections
  • ADO Properties
  • ADO Dynamic Properties
  • ADO Methods
  • ADO Events
  • ADO Enumerated Constants
  • ADO Syntax Indexes

See Also

ADO Samples | Configuring RDS | ActiveX Data Objects Start Page | Programming with ADO | Appendix A: Providers | What's New in ADO


ADO Object Model

The following figures show the ADO objects and their collections. Click an object or collection for more information.

See Also

ADO Collections | ADO Dynamic Properties | ADO Enumerated Constants | ADO Errors | ADO Events | ADO Methods | ADO Objects | ADO Properties


ADO Objects

The relationships between these objects are represented in the ADO Object Model.

Each object can be contained in its corresponding collection. For example, an Error object can be contained in an Errors collection. For more information, see ADO Collections or a specific collection topic.

Command | Defines a specific command that you intend to execute against a data source.
Connection | Represents an open connection to a data source.
Error | Contains details about data access errors that pertain to a single operation involving the provider.
Field | Represents a column of data with a common data type.
Parameter | Represents a parameter or argument associated with a Command object based on a parameterized query or stored procedure.
Property | Represents a dynamic characteristic of an ADO object that is defined by the provider.
Record | Represents a row of a Recordset, or a directory or file in a file system.
Recordset | Represents the entire set of records from a base table or the results of an executed command. At any time, the Recordset object refers to only a single record within the set as the current record.
Stream | Represents a binary stream of data.

See Also

ADO API Reference | ADO Collections | ADO Dynamic Properties | ADO Enumerated Constants | ADO Errors | ADO Events | ADO Methods | ADO Object Model | ADO Properties


Command Object (ADO)

ADO API Reference

Command Object

Defines a specific command that you intend to execute against a data source.

Remarks

Use a Command object to query a database and return records in a Recordset object, to execute a bulk operation, or to manipulate the structure of a database. Depending on the functionality of the provider, some Command collections, methods, or properties may generate an error when referenced.

With the collections, methods, and properties of a Command object, you can do the following:

  • Define the executable text of the command (for example, an SQL statement) with the CommandText property. Alternatively, for command or query structures other than simple strings (for example, XML template queries) define the command with the CommandStream property.
  • Optionally, indicate the command dialect used in the CommandText or CommandStream with the Dialect property.
  • Define parameterized queries or stored-procedure arguments with Parameter objects and the Parameters collection.
  • Indicate whether parameter names should be passed to the provider with the NamedParameters property.
  • Execute a command and return a Recordset object if appropriate with the Execute method.
  • Specify the type of command with the CommandType property prior to execution to optimize performance.
  • Control whether the provider saves a prepared (or compiled) version of the command prior to execution with the Prepared property.
  • Set the number of seconds that a provider will wait for a command to execute with the CommandTimeout property.
  • Associate an open connection with a Command object by setting its ActiveConnection property.
  • Set the Name property to identify the Command object as a method on the associated Connection object.
  • Pass a Command object to the Source property of a Recordset in order to obtain data.
  • Access provider-specific attributes with the Properties collection.

Note To execute a query without using a Command object, pass a query string to the Execute method of a Connection object or to the Open method of a Recordset object. However, a Command object is required when you want to persist the command text and re-execute it, or use query parameters.

To create a Command object independently of a previously defined Connection object, set its ActiveConnection property to a valid connection string. ADO still creates a Connection object, but it doesn't assign that object to an object variable. However, if you are associating multiple Command objects with the same connection, you should explicitly create and open a Connection object; this assigns the Connection object to an object variable. If you do not set the Command object's ActiveConnection property to this object variable, ADO creates a new Connection object for each Command object, even if you use the same connection string.

To execute a Command, simply call it by its Name property on the associated Connection object. The Command must have its ActiveConnection property set to the Connection object. If the Command has parameters, pass their values as arguments to the method.

If two or more Command objects are executed on the same connection and either Command object is a stored procedure with output parameters, an error occurs. To execute each Command object, use separate connections or disconnect all other Command objects from the connection.

The Parameters collection is the default member of the Command object. As a result, the following two code statements are equivalent.

objCmd.Parameters.Item(0)
objCmd(0)

See Also

Properties, Methods, and Events | Connection Object | Parameters Collection | Properties Collection | Appendix A: Providers


Properties, Methods, and Events

ADO API Reference

Command Object Properties, Methods, and Events

Properties/Collections

ActiveConnection Property

CommandStream Property

CommandText Property

CommandTimeout Property

CommandType Property

Dialect Property

Name Property

NamedParameters Property

Parameters Collection

Prepared Property

Properties Collection

State Property

Methods

Cancel Method

CreateParameter Method

Execute Method (ADO Command)

Events

None.

See Also

Command Object


Connection Object (ADO)

ADO API Reference

Connection Object

Represents an open connection to a data source.

Remarks

A Connection object represents a unique session with a data source. In the case of a client/server database system, it may be equivalent to an actual network connection to the server. Depending on the functionality supported by the provider, some collections, methods, or properties of a Connection object may not be available.

With the collections, methods, and properties of a Connection object, you can do the following:

  • Configure the connection before opening it with the ConnectionString, ConnectionTimeout, and Mode properties. ConnectionString is the default property of the Connection object.
  • Set the CursorLocation property to client to invoke the Cursor Service for OLE DB, which supports batch updates.
  • Set the default database for the connection with the DefaultDatabase property.
  • Set the level of isolation for the transactions opened on the connection with the IsolationLevel property.
  • Specify an OLE DB provider with the Provider property.
  • Establish, and later break, the physical connection to the data source with the Open and Close methods.
  • Execute a command on the connection with the Execute method and configure the execution with the CommandTimeout property.

    Note To execute a query without using a Command object, pass a query string to the Execute method of a Connection object. However, a Command object is required when you want to persist the command text and re-execute it, or use query parameters.

  • Manage transactions on the open connection, including nested transactions if the provider supports them, with the BeginTrans, CommitTrans, and RollbackTrans methods and the Attributes property.
  • Examine errors returned from the data source with the Errors collection.
  • Read the version from the ADO implementation used with the Version property.
  • Obtain schema information about your database with the OpenSchema method.

You can create Connection objects independently of any other previously defined object.

You can execute commands or stored procedures as if they were native methods on the Connection object.

To execute a command, give the command a name using the Command object Name property. Set the Command object's ActiveConnection property to the connection. Then issue a statement where the command name is used as if it were a method on the Connection object, followed by any parameters, then followed by a Recordset object if any rows are returned. Set the Recordset properties to customize the resulting Recordset. For example:

Dim cnn As New ADODB.Connection
Dim cmd As New ADODB.Command
Dim rst As New ADODB.Recordset
...
cnn.Open "..."
cmd.Name = "yourCommandName"
cmd.ActiveConnection = cnn
...
'Your command name, any parameters, and an optional Recordset.
cnn.yourCommandName "parameter", rst

To execute a stored procedure, issue a statement where the stored procedure name is used as if it were a method on the Connection object, followed by any parameters. ADO will make a "best guess" of parameter types. For example:

Dim cnn As New ADODB.Connection
...
'Your stored procedure name and any parameters.
cnn.sp_yourStoredProcedureName "parameter"

See Also

Properties, Methods, and Events | Command Object | Errors Collection | Properties Collection | Recordset Object | Appendix A: Providers


Properties, Methods, and Events

ADO API Reference

Connection Object Properties, Methods, and Events

Properties/Collections

Attributes Property

CommandTimeout Property

ConnectionString Property

ConnectionTimeout Property

CursorLocation Property

DefaultDatabase Property

Errors Collection

IsolationLevel Property

Mode Property

Properties Collection

Provider Property

State Property

Version Property

Methods

BeginTrans, CommitTrans, and RollbackTrans Methods

Cancel Method

Close Method

Execute Method (ADO Connection)

Open Method (ADO Connection)

OpenSchema Method

Events

BeginTransComplete, CommitTransComplete, and RollbackTransComplete Events

ConnectComplete and Disconnect Events

ExecuteComplete Event

InfoMessage Event

WillConnect Event

WillExecute Event

See Also

Connection Object


Error Object (ADO)

ADO API Reference

Error Object

Contains details about data access errors that pertain to a single operation involving the provider.

Remarks

Any operation involving ADO objects can generate one or more provider errors. As each error occurs, one or more Error objects are placed in the Errors collection of the Connection object. When another ADO operation generates an error, the Errors collection is cleared, and the new set of Error objects is placed in the Errors collection.

Note Each Error object represents a specific provider error, not an ADO error. ADO errors are exposed to the run-time exception-handling mechanism. For example, in Microsoft Visual Basic, the occurrence of an ADO-specific error will trigger an On Error event and appear in the Error object. For a complete list of ADO errors, see the ErrorValueEnum topic.

You can read an Error object's properties to obtain specific details about each error, including the following:

  • The Description property, which contains the text of the error. This is the default property.
  • The Number property, which contains the Long integer value of the error constant.
  • The Source property, which identifies the object that raised the error. This is particularly useful when you have several Error objects in the Errors collection following a request to a data source.
  • The SQLState and NativeError properties, which provide information from SQL data sources.

When a provider error occurs, it is placed in the Errors collection of the Connection object. ADO supports the return of multiple errors by a single ADO operation to allow for error information specific to the provider. To obtain this rich error information in an error handler, use the appropriate error-trapping features of the language or environment you are working with, then use nested loops to enumerate the properties of each Error object in the Errors collection.

Microsoft Visual Basic and VBScript Users If there is no valid Connection object, you will need to retrieve error information from the Error object.

Just as providers do, ADO clears the OLE Error Info object before making a call that could potentially generate a new provider error. However, the Errors collection on the Connection object is cleared and populated only when the provider generates a new error, or when the Clear method is called.

Some properties and methods return warnings that appear as Error objects in the Errors collection but do not halt a program's execution. Before you call the Resync, UpdateBatch, or CancelBatch methods on a Recordset object; the Open method on a Connection object; or set the Filter property on a Recordset object, call the Clear method on the Errors collection. That way, you can read the Count property of the Errors collection to test for returned warnings.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Properties, Methods, and Events | Connection Object | Errors Collection | Appendix A: Providers


Properties, Methods, and Events

ADO API Reference

Error Object Properties, Methods, and Events

Properties

Description Property

HelpContext, HelpFile Properties

NativeError Property

Number Property

Source Property (ADO Error)

SQLState Property

Methods

None.

Events

None.

See Also

Error Object


Field Object (ADO)

ADO API Reference

Field Object

Represents a column of data with a common data type.

Remarks

Each Field object corresponds to a column in the Recordset. You use the Value property of Field objects to set or return data for the current record. Depending on the functionality the provider exposes, some collections, methods, or properties of a Field object may not be available.

With the collections, methods, and properties of a Field object, you can do the following:

  • Return the name of a field with the Name property.
  • View or change the data in the field with the Value property. Value is the default property of the Field object.
  • Return the basic characteristics of a field with the Type, Precision, and NumericScale properties.
  • Return the declared size of a field with the DefinedSize property.
  • Return the actual size of the data in a given field with the ActualSize property.
  • Determine what types of functionality are supported for a given field with the Attributes property and Properties collection.
  • Manipulate the values of fields containing long binary or long character data with the AppendChunk and GetChunk methods.
  • If the provider supports batch updates, resolve discrepancies in field values during batch updating with the OriginalValue and UnderlyingValue properties.

All of the metadata properties (Name, Type, DefinedSize, Precision, and NumericScale) are available before opening the Field object's Recordset. Setting them at that time is useful for dynamically constructing forms.

See Also

Properties, Methods, and Events | Fields Collection | Properties Collection | Recordset Object


Properties, Methods, and Events

ADO API Reference

Field Object Properties, Methods, and Events

Properties/Collections

ActualSize Property

Attributes Property

DefinedSize Property

Name Property

NumericScale Property

OriginalValue Property

Precision Property

Properties Collection

Status Property (ADO Field)

Type Property

UnderlyingValue Property

Value Property

Methods

AppendChunk Method

GetChunk Method

Events

None.

See Also

Field Object


Parameter Object (ADO)

ADO API Reference

Parameter Object

Represents a parameter or argument associated with a Command object based on a parameterized query or stored procedure.

Remarks

Many providers support parameterized commands. These are commands in which the desired action is defined once, but variables (or parameters) are used to alter some details of the command. For example, an SQL SELECT statement could use a parameter to define the matching criteria of a WHERE clause, and another to define the column name for a SORT BY clause.

Parameter objects represent parameters associated with parameterized queries, or the in/out arguments and the return values of stored procedures. Depending on the functionality of the provider, some collections, methods, or properties of a Parameter object may not be available.

With the collections, methods, and properties of a Parameter object, you can do the following:

  • Set or return the name of a parameter with the Name property.
  • Set or return the value of a parameter with the Value property. Value is the default property of the Parameter object.
  • Set or return parameter characteristics with the Attributes, Direction, Precision, NumericScale, Size, and Type properties.
  • Pass long binary or character data to a parameter with the AppendChunk method.
  • Access provider-specific attributes with the Properties collection.

If you know the names and properties of the parameters associated with the stored procedure or parameterized query you wish to call, you can use the CreateParameter method to create Parameter objects with the appropriate property settings and use the Append method to add them to the Parameters collection. This lets you set and return parameter values without having to call the Refresh method on the Parameters collection to retrieve the parameter information from the provider, a potentially resource-intensive operation.

See Also

Properties, Methods, and Events | Command Object | CreateParameter Method | Parameters Collection | Properties Collection


Properties, Methods, and Events

ADO API Reference

Parameter Object Properties, Methods, and Events

Properties/Collections

Attributes Property

Direction Property

Name Property

NumericScale Property

Precision Property

Properties Collection

Size Property

Type Property

Value Property

Methods

AppendChunk Method

Events

None.

See Also

Parameter Object


Property Object (ADO)

ADO API Reference

Property Object

Represents a dynamic characteristic of an ADO object that is defined by the provider.

Remarks

ADO objects have two types of properties: built-in and dynamic.

Built-in properties are those properties implemented in ADO and immediately available to any new object, using the MyObject.Property syntax. They do not appear as Property objects in an object's Properties collection, so although you can change their values, you cannot modify their characteristics.

Dynamic properties are defined by the underlying data provider, and appear in the Properties collection for the appropriate ADO object. For example, a property specific to the provider may indicate if a Recordset object supports transactions or updating. These additional properties will appear as Property objects in that Recordset object's Properties collection. Dynamic properties can be referenced only through the collection, using the MyObject.Properties(0) or MyObject.Properties("Name") syntax.

You cannot delete either kind of property.

A dynamic Property object has four built-in properties of its own:

  • The Name property is a string that identifies the property.
  • The Type property is an integer that specifies the property data type.
  • The Value property is a variant that contains the property setting. Value is the default property for a Property object.
  • The Attributes property is a long value that indicates characteristics of the property specific to the provider.

See Also

Properties, Methods, and Events | Command Object | Connection Object | Field Object | Properties Collection | Recordset Object


Properties, Methods, and Events

ADO API Reference

Property Object Properties, Methods, and Events

Properties

Attributes Property

Name Property

Type Property

Value Property

Methods

None.

Events

None.

See Also

Property Object


Record Object (ADO)

ADO API Reference

Record Object

Represents a row from a Recordset or the data provider, or an object returned by a semi-structured data provider, such as a file or directory.

Remarks

A Record object represents one row of data, and has some conceptual similarities with a one-row Recordset. Depending upon the capabilities of your provider, Record objects may be returned directly from your provider instead of a one-row Recordset, for example when an SQL query that selects only one row is executed. Or, a Record object can be obtained directly from a Recordset object. Or, a Record can be returned directly from a provider to semi-structured data, such as the Microsoft Exchange OLE DB provider.

You can view the fields associated with the Record object by way of the Fields collection on the Record object. ADO allows object-valued columns including Recordset, SafeArray, and scalar values in the Fields collection of Record objects.

If the Record object represents a row in a Recordset, then it is possible to return to that original Recordset with the Source property.

The Record object can also be used by semi-structured data providers such as the Microsoft OLE DB Provider for Internet Publishing, to model tree-structured namespaces. Each node in the tree is a Record object with associated columns. The columns can represent the attributes of that node and other relevant information. The Record object can represent both a leaf node and a non-leaf node in the tree structure. Non-leaf nodes have other nodes as their contents while leaf nodes do not have such contents. Leaf nodes typically contain binary streams of data while non-leaf nodes may also have a default binary stream associated with them. Properties on the Record object identify the type of node.

The Record object also represents an alternative way for navigating hierarchically organized data. A Record object may be created to represent the root of a specific sub-tree in a large tree structure and new Record objects may be opened to represent child nodes.

A resource (for example, a file or directory) can be uniquely identified by an absolute URL. A Connection object is implicitly created and set to the Record object when the Record is opened with an absolute URL. A Connection object may explicitly be set to the Record object via the ActiveConnection property. The files and directories accessible via the Connection object define the context in which Record operations may occur.

Data modification and navigation methods on the Record object also accept a relative URL, which locates a resource using an absolute URL or the Connection object context as a starting point.

Note URLs using the http scheme will automatically invoke the Microsoft OLE DB Provider for Internet Publishing. For more information, see Absolute and Relative URLs.

A Connection object is associated with each Record object. Therefore, Record object operations can be part of a transaction by invoking Connection object transaction methods.

The Record object does not support ADO events, and therefore will not respond to notifications.

With the methods and properties of a Record object, you can do the following:

  • Set or return the associated Connection object with the ActiveConnection property.
  • Indicate access permissions with the Mode property.
  • Return the URL of the directory, if any, that contains the resource represented by the Record with the ParentURL property.
  • Indicate the absolute URL, relative URL, or Recordset from which the Record is derived with the Source property.
  • Indicate the current status of the Record with the State property.
  • Indicate the type of Record—simple, collection, or structured document—with the RecordType property.
  • Halt execution of an asynchronous operation with the Cancel method.
  • Disassociate the Record from a data source with the Close method.
  • Copy the file or directory represented by a Record to another location with the CopyRecord method.
  • Delete the file, or directory and subdirectories, represented by a Record with the DeleteRecord method.
  • Open a Recordset containing rows that represent the subdirectories and files of the entity represented by the Record with the GetChildren method.
  • Move (rename) the file, or directory and subdirectories, represented by a Record to another location with the MoveRecord method.
  • Associate the Record with an existing data source, or create a new file or directory with the Open method.

See Also

Example | Example

Properties, Methods, and Events | Fields Collection | Properties Collection | Records and Streams | Recordset Object


Properties, Methods, and Events

ADO API Reference

Record Object Properties, Methods, and Events

Properties/Collections

ActiveConnection Property

Fields Collection

Mode Property

ParentURL Property

Properties Collection

RecordType Property

Source Property (ADO Record)

State Property

Methods

Cancel Method

Close Method

CopyRecord Method

DeleteRecord Method

GetChildren Method

MoveRecord Method

Open Method (ADO Record)

Events

None.

See Also

Record Object


Recordset Object (ADO)

ADO API Reference

Recordset Object

Represents the entire set of records from a base table or the results of an executed command. At any time, the Recordset object refers to only a single record within the set as the current record.

Remarks

You use Recordset objects to manipulate data from a provider. When you use ADO, you manipulate data almost entirely using Recordset objects. All Recordset objects consist of records (rows) and fields (columns). Depending on the functionality supported by the provider, some Recordset methods or properties may not be available.

ADODB.Recordset is the ProgID that should be used to create a Recordset object. Existing applications that reference the outdated ADOR.Recordset ProgID will continue to work without recompiling, but new development should reference ADODB.Recordset.

There are four different cursor types defined in ADO:

  • Dynamic cursor — allows you to view additions, changes, and deletions by other users; allows all types of movement through the Recordset that doesn't rely on bookmarks; and allows bookmarks if the provider supports them.
  • Keyset cursor — behaves like a dynamic cursor, except that it prevents you from seeing records that other users add, and prevents access to records that other users delete. Data changes by other users will still be visible. It always supports bookmarks and therefore allows all types of movement through the Recordset.
  • Static cursor — provides a static copy of a set of records for you to use to find data or generate reports; always allows bookmarks and therefore allows all types of movement through the Recordset. Additions, changes, or deletions by other users will not be visible. This is the only type of cursor allowed when you open a client-side Recordset object.
  • Forward-only cursor — allows you to only scroll forward through the Recordset. Additions, changes, or deletions by other users will not be visible. This improves performance in situations where you need to make only a single pass through a Recordset.

Set the CursorType property prior to opening the Recordset to choose the cursor type, or pass a CursorType argument with the Open method. Some providers don't support all cursor types. Check the documentation for the provider. If you don't specify a cursor type, ADO opens a forward-only cursor by default.

If the CursorLocation property is set to adUseClient to open a Recordset, the UnderlyingValue property on Field objects is not available in the returned Recordset object. When used with some providers (such as the Microsoft ODBC Provider for OLE DB in conjunction with Microsoft SQL Server), you can create Recordset objects independently of a previously defined Connection object by passing a connection string with the Open method. ADO still creates a Connection object, but it doesn't assign that object to an object variable. However, if you are opening multiple Recordset objects over the same connection, you should explicitly create and open a Connection object; this assigns the Connection object to an object variable. If you do not use this object variable when opening your Recordset objects, ADO creates a new Connection object for each new Recordset, even if you pass the same connection string.

You can create as many Recordset objects as needed.

When you open a Recordset, the current record is positioned to the first record (if any) and the BOF and EOF properties are set to False. If there are no records, the BOF and EOF property settings are True.

You can use the MoveFirst, MoveLast, MoveNext, and MovePrevious methods; the Move method; and the AbsolutePosition, AbsolutePage, and Filter properties to reposition the current record, assuming the provider supports the relevant functionality. Forward-only Recordset objects support only the MoveNext method. When you use the Move methods to visit each record (or enumerate the Recordset), you can use the BOF and EOF properties to determine if you've moved beyond the beginning or end of the Recordset.

Recordset objects can support two types of updating: immediate and batched. In immediate updating, all changes to data are written immediately to the underlying data source once you call the Update method. You can also pass arrays of values as parameters with the AddNew and Update methods and simultaneously update several fields in a record.

If a provider supports batch updating, you can have the provider cache changes to more than one record and then transmit them in a single call to the database with the UpdateBatch method. This applies to changes made with the AddNew, Update, and Delete methods. After you call the UpdateBatch method, you can use the Status property to check for any data conflicts in order to resolve them.

Note To execute a query without using a Command object, pass a query string to the Open method of a Recordset object. However, a Command object is required when you want to persist the command text and re-execute it, or use query parameters.

The Mode property governs access permissions.

The Fields collection is the default member of the Recordset object. As a result, the following two code statements are equivalent.

Debug.Print objRs.Fields.Item(0)  ' Both statements print
Debug.Print objRs(0)              '  the Value of Item(0).

See Also

Properties, Methods, and Events | Connection Object | Fields Collection | Properties Collection | Appendix A: Providers


Properties, Methods, and Events

ADO API Reference

Recordset Object Properties, Methods, and Events

Properties/Collections

AbsolutePage Property

AbsolutePosition Property

ActiveCommand Property

ActiveConnection Property

BOF, EOF Properties

Bookmark Property

CacheSize Property

CursorLocation Property

CursorType Property

DataMember Property

DataSource Property

EditMode Property

Fields Collection

Filter Property

Index Property

LockType Property

MarshalOptions Property

MaxRecords Property

PageCount Property

PageSize Property

Properties Collection

RecordCount Property

Sort Property

Source Property (ADO Recordset)

State Property

Status Property (ADO Recordset)

StayInSync Property

Methods

AddNew Method

Cancel Method

CancelBatch Method

CancelUpdate Method

Clone Method

Close Method

CompareBookmarks Method

Delete Method (ADO Recordset)

Find Method

GetRows Method

GetString Method

Move Method

MoveFirst, MoveLast, MoveNext, and MovePrevious Methods

NextRecordset Method

Open Method (ADO Recordset)

Requery Method

Resync Method

Save Method

Seek Method

Supports Method

Update Method

UpdateBatch Method

Events

EndOfRecordset Event

FetchComplete Event

FetchProgress Event

WillChangeField and FieldChangeComplete Events

WillChangeRecord and RecordChangeComplete Events

WillChangeRecordset and RecordsetChangeComplete Events

WillMove and MoveComplete Events

See Also

Recordset Object


Stream Object (ADO)

ADO API Reference

Stream Object

Represents a stream of binary data or text.

Remarks

In tree-structured hierarchies such as a file system or an e-mail system, a Record may have a default binary stream of bits associated with it that contains the contents of the file or the e-mail. A Stream object can be used to manipulate fields or records containing these streams of data. A Stream object can be obtained in these ways:

  • From a URL pointing to an object (typically a file) containing binary or text data. This object can be a simple document, a Record object representing a structured document, or a folder.
  • By opening the default Stream object associated with a Record object. You can obtain the default stream associated with a Record object when the Record is opened, to eliminate a round-trip just to open the stream.
  • By instantiating a Stream object. These Stream objects can be used to store data for the purposes of your application. Unlike a Stream associated with a URL, or the default Stream of a Record, an instantiated Stream has no association with an underlying source by default.

With the methods and properties of a Stream object, you can do the following:

  • Open a Stream object from a Record or URL with the Open method.
  • Close a Stream with the Close method.
  • Input bytes or text to a Stream with the Write and WriteText methods.
  • Read bytes from the Stream with the Read and ReadText methods.
  • Write any Stream data still in the ADO buffer to the underlying object with the Flush method.
  • Copy the contents of a Stream to another Stream with the CopyTo method.
  • Control how lines are read from the source file with the SkipLine method and the LineSeparator property.
  • Determine the end of stream position with the EOS property and SetEOS method.
  • Save and restore data in files with the SaveToFile and LoadFromFile methods.
  • Specify the character set used for storing the Stream with the Charset property.
  • Halt an asynchronous Stream operation with the Cancel method.
  • Determine the number of bytes in a Stream with the Size property.
  • Control the current position within a Stream with the Position property.
  • Determine the type of data in a Stream with the Type property.
  • Determine the current state of the Stream (closed, open, or executing) with the State property.
  • Specify the access mode for the Stream with the Mode property.

Note URLs using the http scheme will automatically invoke the Microsoft OLE DB Provider for Internet Publishing. For more information, see Absolute and Relative URLs.

See Also

Example


Properties, Methods, and Events

ADO API Reference

Stream Object Properties, Methods, and Events

Properties

Charset Property

EOS Property

LineSeparator Property

Mode Property

Position Property

Size Property (ADO Stream)

State Property

Type Property (ADO Stream)

Methods

Cancel Method

Close Method

CopyTo Method

Flush Method

LoadFromFile Method

Open Method (ADO Stream)

Read Method

ReadText Method

SaveToFile Method

SetEOS Method

SkipLine Method

Write Method

WriteText Method

Events

None.

See Also

Stream Object


ADO Collections

The relationships between these collections and the ADO objects are represented in the ADO Object Model.

Each collection can contain its corresponding object. For example, an Error object can be contained in an Errors collection. For more information about objects, see ADO Objects or a specific object topic.

Errors | Contains all the Error objects created in response to a single provider-related failure.
Fields | Contains all the Field objects of a Recordset object.
Parameters | Contains all the Parameter objects of a Command object.
Properties | Contains all the Property objects for a specific instance of an object.

See Also

ADO API Reference | ADO Dynamic Properties | ADO Enumerated Constants | ADO Errors | ADO Events | ADO Methods | ADO Object Model | ADO Objects | ADO Properties


Errors Collection (ADO)

ADO API Reference

Errors Collection

Contains all the Error objects created in response to a single provider-related failure provider.

Remarks

Any operation involving ADO objects can generate one or more provider errors. As each error occurs, one or more Error objects can be placed in the Errors collection of the Connection object. When another ADO operation generates an error, the Errors collection is cleared, and the new set of Error objects can be placed in the Errors collection.

Each Error object represents a specific provider error, not an ADO error. ADO errors are exposed to the run-time exception-handling mechanism. For example, in Microsoft Visual Basic, the occurrence of an ADO-specific error will trigger an onError event and appear in the Err object.

ADO operations that don't generate an error have no effect on the Errors collection. Use the Clear method to manually clear the Errors collection.

The set of Error objects in the Errors collection describes all errors that occurred in response to a single statement. Enumerating the specific errors in the Errors collection enables your error-handling routines to more precisely determine the cause and origin of an error, and take appropriate steps to recover.

Some properties and methods return warnings that appear as Error objects in the Errors collection but do not halt a program's execution. Before you call the Resync, UpdateBatch, or CancelBatch methods on a Recordset object, the Open method on a Connection object, or set the Filter property on a Recordset object, call the Clear method on the Errors collection. That way you can read the Count property of the Errors collection to test for returned warnings.

Note See the Error object topic for a more detailed explanation of the way a single ADO operation can generate multiple errors.

See Also

Properties, Methods, and Events | Error Object | Appendix A: Providers


Errors Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Clear Method

Refresh Method

Events

None.

See Also

Errors Collection


Fields Collection (ADO)

ADO API Reference

Fields Collection

Contains all the Field objects of a Recordset or Record object.

Remarks

A Recordset object has a Fields collection made up of Field objects. Each Field object corresponds to a column in the Recordset. You can populate the Fields collection before opening the Recordset by calling the Refresh method on the collection.

Note See the Field object topic for a more detailed explanation of how to use Field objects.

The Fields collection has an Append method, which provisionally creates and adds a Field object to the collection, and an Update method, which finalizes any additions or deletions.

A Record object has two special fields that can be indexed with FieldEnum constants. One constant accesses a field containing the default stream for the Record, and the other accesses a field containing the absolute URL string for the Record.

Certain providers (for example, the Microsoft OLE DB Provider for Internet Publishing) may populate the Fields collection with a subset of available fields for the Record or Recordset. Other fields will not be added to the collection until they are first referenced by name or indexed by your code.

If you attempt to reference a nonexistent field by name, a new Field object will be appended to the Fields collection with a Status of adFieldPendingInsert. When you call Update, ADO will create a new field in your data source if allowed by your provider.

See Also

Properties, Methods, and Events | Field Object


Fields Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Append Method

CancelUpdate Method

Delete Method (ADO Fields Collection)

Refresh Method

Resync Method

Update Method

Events

None.

See Also

Fields Collection


Parameters Collection (ADO)

ADO API Reference

Parameters Collection

Contains all the Parameter objects of a Command object.

Remarks

A Command object has a Parameters collection made up of Parameter objects.

Using the Refresh method on a Command object's Parameters collection retrieves provider parameter information for the stored procedure or parameterized query specified in the Command object. Some providers do not support stored procedure calls or parameterized queries; calling the Refresh method on the Parameters collection when using such a provider will return an error.

If you have not defined your own Parameter objects and you access the Parameters collection before calling the Refresh method, ADO will automatically call the method and populate the collection for you.

You can minimize calls to the provider to improve performance if you know the properties of the parameters associated with the stored procedure or parameterized query you wish to call. Use the CreateParameter method to create Parameter objects with the appropriate property settings and use the Append method to add them to the Parameters collection. This lets you set and return parameter values without having to call the provider for the parameter information. If you are writing to a provider that does not supply parameter information, you must manually populate the Parameters collection using this method to be able to use parameters at all. Use the Delete method to remove Parameter objects from the Parameters collection if necessary.

The objects in the Parameters collection of a Recordset go out of scope (therefore becoming unavailable) when the Recordset is closed.

See Also

Properties, Methods, and Events | Append Method | CreateParameter Method | Parameter Object


Parameters Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Append Method

Delete Method (ADO Parameters Collection)

Refresh Method

Events

None.

See Also

Parameters Collection


Properties Collection (ADO)

ADO API Reference

Properties Collection

Contains all the Property objects for a specific instance of an object.

Remarks

Some ADO objects have a Properties collection made up of Property objects. Each Property object corresponds to a characteristic of the ADO object specific to the provider.

Note See the Property object topic for a more detailed explanation of how to use Property objects.

The Dynamic Properties of the Recordset object go out of scope (become unavailable) when the Recordset is closed.

See Also

Properties, Methods, and Events | Property Object | Appendix A: Providers


Properties Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Refresh Method

Events

None.

See Also

Properties Collection


ADO Properties

AbsolutePage | Indicates on which page the current record resides.
AbsolutePosition | Indicates the ordinal position of a Recordset object's current record.
ActiveCommand | Indicates the Command object that created the associated Recordset object.
ActiveConnection | Indicates to which Connection object the specified Command, Recordset, or Record object currently belongs.
ActualSize | Indicates the actual length of a field's value.
Attributes | Indicates one or more characteristics of an object.
BOF and EOF | BOF — indicates that the current record position is before the first record in a Recordset object. EOF — indicates that the current record position is after the last record in a Recordset object.
Bookmark | Indicates a bookmark that uniquely identifies the current record in a Recordset object or sets the current record in a Recordset object to the record identified by a valid bookmark.
CacheSize | Indicates the number of records from a Recordset object that are cached locally in memory.
CharSet | Indicates the character set into which the contents of a text Stream should be translated.
CommandStream | Indicates the stream used as the input to a Command object.
CommandText | Indicates the text of a command to be issued against a provider.
CommandTimeout | Indicates how long to wait while executing a command before terminating the attempt and generating an error.
CommandType | Indicates the type of a Command object.
ConnectionString Property | Indicates the information used to establish a connection to a data source.
ConnectionTimeout | Indicates how long to wait while establishing a connection before terminating the attempt and generating an error.
Count | Indicates the number of objects in a collection.
CursorLocation | Indicates the location of the cursor service.
CursorType | Indicates the type of cursor used in a Recordset object.
DataMember | Indicates the name of the data member that will be retrieved from the object referenced by the DataSource property.
DataSource | Indicates an object that contains data to be represented as a Recordset object.
DefaultDatabase | Indicates the default database for a Connection object.
DefinedSize | Indicates the data capacity of a Field object.
Description | Describes an Error object.
Dialect | Indicates the syntax and general rules that the provider will use to parse the CommandText or CommandStream properties.
Direction | Indicates whether the Parameter represents an input parameter, an output parameter, or both, or if the parameter is the return value from a stored procedure.
EditMode | Indicates the editing status of the current record.
EOS | Indicates whether the current position is at the end of the stream.
Filter | Indicates a filter for data in a Recordset.
HelpContext and HelpFile | Indicates the help file and topic associated with an Error object. HelpContextID — returns a context ID, as a Long value, for a topic in a Help file. HelpFile — returns a String value that evaluates to a fully resolved path to a Help file.
Index | Indicates the name of the index currently in effect for a Recordset object.
IsolationLevel | Indicates the level of isolation for a Connection object.
Item | Indicates a specific member of a collection, by name or ordinal number.
LineSeparator | Indicates the binary character to be used as the line separator in text Stream objects.
LockType | Indicates the type of locks placed on records during editing.
MarshalOptions | Indicates which records are to be marshaled back to the server.
MaxRecords | Indicates the maximum number of records to return to a Recordset from a query.
Mode | Indicates the available permissions for modifying data in a Connection, Record, or Stream object.
Name | Indicates the name of an object.
NativeError | Indicates the provider-specific error code for a given Error object.
Number | Indicates the number that uniquely identifies an Error object.
NumericScale | Indicates the scale of numeric values in a Parameter or Field object.
OriginalValue | Indicates the value of a Field that existed in the record before any changes were made.
PageCount | Indicates how many pages of data the Recordset object contains.
PageSize | Indicates how many records constitute one page in the Recordset.
ParentURL | Indicates an absolute URL string that points to the parent Record of the current Record object.
Position | Indicates the current position within a Stream object.
Precision | Indicates the degree of precision for numeric values in a Parameter object or for numeric Field objects.
Prepared | Indicates whether to save a compiled version of a command before execution.
Provider | Indicates the name of the provider for a Connection object.
RecordCount | Indicates the number of records in a Recordset object.
RecordType | Indicates the type of Record object.
Size | Indicates the maximum size, in bytes or characters, of a Parameter object.
Size (ADO Stream) | Indicates the total size of the stream in number of bytes.
Sort | Indicates one or more field names on which the Recordset is sorted, and whether each field is sorted in ascending or descending order.
Source (ADO Error) | Indicates the name of the object or application that originally generated an error.
Source (ADO Record) | Indicates the entity represented by the Record object.
Source (ADO Recordset) | Indicates the source for the data in a Recordset object
SQLState | Indicates the SQL state for a given Error object.
State | Indicates for all applicable objects whether the state of the object is open or closed. Indicates for all applicable objects executing an asynchronous method, whether the current state of the object is connecting, executing, or retrieving
Status (ADO Field) | Indicates the status of a Field object.
Status (ADO Recordset) | Indicates the status of the current record with respect to batch updates or other bulk operations.
StayInSync | Indicates, in a hierarchical Recordset object, whether the reference to the underlying child records (that is, the chapter) changes when the parent row position changes.
Type | Indicates the operational type or data type of a Parameter, Field, or Property object.
Type (ADO Stream) | Indicates the type of data contained in the Stream (binary or text).
UnderlyingValue | Indicates a Field object's current value in the database.
Value | Indicates the value assigned to a Field, Parameter, or Property object.
Version | Indicates the ADO version number.

See Also

ADO API Reference | ADO Collections | ADO Dynamic Properties | ADO Enumerated Constants | ADO Errors | ADO Events | ADO Methods | ADO Object Model | ADO Objects


AbsolutePage Property (ADO)

ADO API Reference

AbsolutePage Property

Indicates on which page the current record resides.

Settings and Return Values

Sets or returns a Long value from 1 to the number of pages in the Recordset object (PageCount), or returns one of the PositionEnum values.

Remarks

Use the AbsolutePage property to identify the page number on which the current record is located. Use the PageSize property to logically divide the Recordset object into a series of pages, each of which has the number of records equal to PageSize (except for the last page, which may have fewer records). The provider must support the appropriate functionality for this property to be available.

Like the AbsolutePosition property, AbsolutePage is 1-based and equals 1 when the current record is the first record in the Recordset. Set this property to move to the first record of a particular page. Obtain the total number of pages from the PageCount property.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

AbsolutePosition Property | PageCount Property | PageSize Property

Applies To: Recordset Object


AbsolutePosition Property (ADO)

ADO API Reference

AbsolutePosition Property

Indicates the ordinal position of a Recordset object's current record.

Settings and Return Values

Sets or returns a Long value from 1 to the number of records in the Recordset object (RecordCount), or returns one of the PositionEnum values.

Remarks

Use the AbsolutePosition property to move to a record based on its ordinal position in the Recordset object, or to determine the ordinal position of the current record. The provider must support the appropriate functionality for this property to be available.

Like the AbsolutePage property, AbsolutePosition is 1-based and equals 1 when the current record is the first record in the Recordset. You can obtain the total number of records in the Recordset object from the RecordCount property.

When you set the AbsolutePosition property, even if it is to a record in the current cache, ADO reloads the cache with a new group of records starting with the record you specified. The CacheSize property determines the size of this group.

Note You should not use the AbsolutePosition property as a surrogate record number. The position of a given record changes when you delete a preceding record. There is also no assurance that a given record will have the same AbsolutePosition if the Recordset object is requeried or reopened. Bookmarks are still the recommended way of retaining and returning to a given position and are the only way of positioning across all types of Recordset objects.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

AbsolutePage Property | RecordCount Property

Applies To: Recordset Object


ActiveCommand Property (ADO)

ADO API Reference

ActiveCommand Property

Indicates the Command object that created the associated Recordset object.

Return Value

Returns a Variant that contains a Command object. Default is a null object reference.

Remarks

The ActiveCommand property is read-only.

If a Command object was not used to create the current Recordset, then a Null object reference is returned.

Use this property to find the associated Command object when you are given only the resulting Recordset object.

See Also

Visual Basic Example |JScript Example | VC++Example | VJ++Example

Command Object

Applies To: Recordset Object


ActiveConnection Property (ADO)

ADO API Reference

ActiveConnection Property

Indicates to which Connection object the specified Command, Recordset, or Record object currently belongs.

Settings and Return Values

Sets or returns a String value that contains a definition for a connection if the connection is closed, or a Variant containing the current Connection object if the connection is open. Default is a null object reference. See the ConnectionString property.

Remarks

Use the ActiveConnection property to determine the Connection object over which the specified Command object will execute or the specified Recordset will be opened.

Command

For Command objects, the ActiveConnection property is read/write.

If you attempt to call the Execute method on a Command object before setting this property to an open Connection object or valid connection string, an error occurs.

Microsoft Visual Basic Setting the ActiveConnection property to Nothing disassociates the Command object from the current Connection and causes the provider to release any associated resources on the data source. You can then associate the Command object with the same or another Connection object. Some providers allow you to change the property setting from one Connection to another, without having to first set the property to Nothing.

If the Parameters collection of the Command object contains parameters supplied by the provider, the collection is cleared if you set the ActiveConnection property to Nothing or to another Connection object. If you manually create Parameter objects and use them to fill the Parameters collection of the Command object, setting the ActiveConnection property to Nothing or to another Connection object leaves the Parameters collection intact.

Closing the Connection object with which a Command object is associated sets the ActiveConnection property to Nothing. Setting this property to a closed Connection object generates an error.

Recordset

For open Recordset objects or for Recordset objects whose Source property is set to a valid Command object, the ActiveConnection property is read-only. Otherwise, it is read/write.

You can set this property to a valid Connection object or to a valid connection string. In this case, the provider creates a new Connection object using this definition and opens the connection. Additionally, the provider may set this property to the new Connection object to give you a way to access the Connection object for extended error information or to execute other commands.

If you use the ActiveConnection argument of the Open method to open a Recordset object, the ActiveConnection property will inherit the value of the argument.

If you set the Source property of the Recordset object to a valid Command object variable, the ActiveConnection property of the Recordset inherits the setting of the Command object's ActiveConnection property.

Remote Data Service Usage When used on a client-side Recordset object, this property can be set only to a connection string or (in Microsoft Visual Basic or Visual Basic, Scripting Edition) to Nothing.

Record

This property is read/write when the Record object is closed, and may contain a connection string or reference to an open Connection object. This property is read-only when the Record object is open, and contains a reference to an open Connection object.

A Connection object is created implicitly when the Record object is opened from a URL. Open the Record with an existing, open Connection object by assigning the Connection object to this property, or using the Connection object as a parameter in the Open method call. If the Record is opened from an existing Record or Recordset, then it is automatically associated with that Record or Recordset object's Connection object.

Note URLs using the http scheme will automatically invoke the Microsoft OLE DB Provider for Internet Publishing. For more information, see Absolute and Relative URLs.

See Also

Visual Basic Example | Visual C++ Example | Visual J++Example

Connection Object | ConnectionString Property

Applies To: Command Object | Record Object | Recordset Object


ActualSize Property (ADO)

ADO API Reference

ActualSize Property

Indicates the actual length of a field's value.

Settings and Return Values

Returns a Long value. Some providers may allow this property to be set to reserve space for BLOB data, in which case the default value is 0.

Remarks

Use the ActualSize property to return the actual length of a Field object's value. For all fields, the ActualSize property is read-only. If ADO cannot determine the length of the Field object's value, the ActualSize property returns adUnknown.

The ActualSize and DefinedSize properties are different, as shown in the following example. A Field object with a declared type of adVarChar and a maximum length of 50 characters returns a DefinedSize property value of 50, but the ActualSize property value it returns is the length of the data stored in the field for the current record. Fields with a DefinedSize greater than 255 bytes are treated as variable length columns.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

DefinedSize Property

Applies To: Field Object


Attributes Property (ADO)

ADO API Reference

Attributes Property

Indicates one or more characteristics of an object.

Settings and Return Values

Sets or returns a Long value.

For a Connection object, the Attributes property is read/write, and its value can be the sum of one or more XactAttributeEnum values. The default is zero (0).

For a Parameter object, the Attributes property is read/write, and its value can be the sum of any one or more ParameterAttributesEnum values. The default is adParamSigned.

For a Field object, the Attributes property can be the sum of one or more FieldAttributeEnum values. It is normally read-only, However, for new Field objects that have been appended to the Fields collection of a Record, Attributes is read/write only after the Value property for the Field has been specified and the new Field has been successfully added by the data provider by calling the Update method of the Fields collection.

For a Property object, the Attributes property is read-only, and its value can be the sum of any one or more PropertyAttributesEnum values.

Remarks

Use the Attributes property to set or return characteristics of Connection objects, Parameter objects, Field objects, or Property objects.

When you set multiple attributes, you can sum the appropriate constants. If you set the property value to a sum including incompatible constants, an error occurs.

Remote Data Service Usage This property is not available on a client-side Connection object.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

AppendChunk Method | BeginTrans, CommitTrans, and RollbackTrans Methods | GetChunk Method

Applies To: Connection Object | Field Object | Parameter Object | Property Object


BOF, EOF Properties (ADO)

ADO API Reference

BOF, EOF Properties

  • BOF — Indicates that the current record position is before the first record in a Recordset object.
  • EOF — Indicates that the current record position is after the last record in a Recordset object.

Return Value

The BOF and EOF properties return Boolean values.

Remarks

Use the BOF and EOF properties to determine whether a Recordset object contains records or whether you've gone beyond the limits of a Recordset object when you move from record to record.

The BOF property returns True (-1) if the current record position is before the first record and False (0) if the current record position is on or after the first record.

The EOF property returns True if the current record position is after the last record and False if the current record position is on or before the last record.

If either the BOF or EOF property is True, there is no current record.

If you open a Recordset object containing no records, the BOF and EOF properties are set to True (see the RecordCount property for more information about this state of a Recordset). When you open a Recordset object that contains at least one record, the first record is the current record and the BOF and EOF properties are False.

If you delete the last remaining record in the Recordset object, the BOF and EOF properties may remain False until you attempt to reposition the current record.

This table shows which Move methods are allowed with different combinations of the BOF and EOF properties.

 | MoveFirst, MoveLast | MovePrevious, Move < 0 | Move 0 | MoveNext, Move > 0
BOF=True, EOF=False | Allowed | Error | Error | Allowed
BOF=False, EOF=True | Allowed | Allowed | Error | Error
Both True | Error | Error | Error | Error
Both False | Allowed | Allowed | Allowed | Allowed

Allowing a Move method doesn't guarantee that the method will successfully locate a record; it only means that calling the specified Move method won't generate an error.

The following table shows what happens to the BOF and EOF property settings when you call various Move methods but are unable to successfully locate a record.

 | BOF | EOF
MoveFirst, MoveLast | Set to True | Set to True
Move 0 | No change | No change
MovePrevious, Move < 0 | Set to True | No change
MoveNext, Move > 0 | No change | Set to True

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Recordset Object


Bookmark Property (ADO)

ADO API Reference

Bookmark Property

Indicates a bookmark that uniquely identifies the current record in a Recordset object or sets the current record in a Recordset object to the record identified by a valid bookmark.

Settings and Return Values

Sets or returns a Variant expression that evaluates to a valid bookmark.

Remarks

Use the Bookmark property to save the position of the current record and return to that record at any time. Bookmarks are available only in Recordset objects that support bookmark functionality.

When you open a Recordset object, each of its records has a unique bookmark. To save the bookmark for the current record, assign the value of the Bookmark property to a variable. To quickly return to that record at any time after moving to a different record, set the Recordset object's Bookmark property to the value of that variable.

The user may not be able to view the value of the bookmark. Also, users should not expect bookmarks to be directly comparable—two bookmarks that refer to the same record may have different values.

If you use the Clone method to create a copy of a Recordset object, the Bookmark property settings for the original and the duplicate Recordset objects are identical and you can use them interchangeably. However, you cannot use bookmarks from different Recordset objects interchangeably, even if they were created from the same source or command.

Remote Data Service Usage When used on a client-side Recordset object, the Bookmark property is always available.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Supports Method

Applies To: Recordset Object


CacheSize Property (ADO)

ADO API Reference

CacheSize Property

Indicates the number of records from a Recordset object that are cached locally in memory.

Settings and Return Values

Sets or returns a Long value that must be greater than 0. Default is 1.

Remarks

Use the CacheSize property to control how many records to retrieve at one time into local memory from the provider. For example, if the CacheSize is 10, after first opening the Recordset object, the provider retrieves the first 10 records into local memory. As you move through the Recordset object, the provider returns the data from the local memory buffer. As soon as you move past the last record in the cache, the provider retrieves the next 10 records from the data source into the cache.

Note CacheSize is based on the Maximum Open Rows provider-specific property (in the Properties collection of the Recordset object). You cannot set CacheSize to a value greater than Maximum Open Rows. To modify the number of rows which can be opened by the provider, set Maximum Open Rows.

The value of CacheSize can be adjusted during the life of the Recordset object, but changing this value only affects the number of records in the cache after subsequent retrievals from the data source. Changing the property value alone will not change the current contents of the cache.

If there are fewer records to retrieve than CacheSize specifies, the provider returns the remaining records and no error occurs.

A CacheSize setting of zero is not allowed and returns an error.

Records retrieved from the cache don't reflect concurrent changes that other users made to the source data. To force an update of all the cached data, use the Resync method.

If CacheSize is set to a value greater than one, the navigation methods (Move, MoveFirst, MoveLast, MoveNext, and MovePrevious) may result in navigation to a deleted record, if deletion occurs after the records were retrieved. After the initial fetch, subsequent deletions will not be reflected in your data cache until you attempt to access a data value from a deleted row. However, setting CacheSize to one eliminates this issue since deleted rows cannot be fetched.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Recordset Object


Charset Property (ADO)

ADO API Reference

Charset Property

Indicates the character set into which the contents of a text Stream should be translated.

Settings and Return Values

Sets or returns a String value that specifies the character set into which the contents of the Stream will be translated. The default value is "Unicode". Allowed values are typical strings passed over the interface as Internet character set strings (for example, "iso-8859-1", "Windows-1252", etc.). For a list of the character set strings that is known by a system, see the subkeys of HKEY_CLASSES_ROOT\MIME\Database\Charset in the Windows Registry.

Remarks

In a text Stream object, text data is stored as Unicode. The Charset property translates the data read from the Stream into the specified character set. Similarly, data written to the Stream in the specified character set is translated into Unicode for storage in the Stream object.

For an open Stream, the current Position must be at the beginning of the Stream (0) to be able to set Charset.

Charset is used only with text Stream objects (Type is adTypeText). This property is ignored if Type is adTypeBinary.

See Also

Example

Applies To: Stream Object


CommandStream Property (ADO)

ADO API Reference

CommandStream Property

Indicates the stream used as the input for a Command object.

Settings and Return Values

Sets or returns the stream used as the input for a Command object. The format for this stream is provider-specific, see your provider's documentation for details. This property is similar to the CommandText property, used to specify a string for the input of a Command.

Remarks

CommandStream and CommandText are mutually exclusive. When the user sets the CommandStream property, the CommandText property will be set to the empty string (""). If the user sets the CommandString property, the CommandStream property will be set to Nothing.

The behaviors of the Command.Parameters.Refresh and Command.Prepare methods are defined by the provider. The values of parameters in a stream may not be refreshed.

The input stream is not available to other ADO objects that return the source of a Command. For example, if the Source property of a Recordset is set to a Command object that has a stream as its input, Recordset.Source continues to return the CommandText property, which contains an empty string (""), instead of the stream contents of the CommandStream property.

When using a command stream (as specified by CommandStream), the only valid CommandTypeEnum values for the CommandType property are adCmdText and adCmdUnknown. Any other value causes an error.

See Also

CommandText Property | Dialect Property | CommandTypeEnum

Applies To: Command Object


CommandText Property (ADO)

ADO API Reference

CommandText Property

Indicates the text of a command to be issued against a provider.

Settings and Return Values

Sets or returns a String value that contains a provider command, such as an SQL statement, a table name, a relative URL, or a stored procedure call. Default is "" (zero-length string).

Remarks

Use the CommandText property to set or return the text of a command represented by a Command object. Usually this will be an SQL statement, but can also be any other type of command statement recognized by the provider, such as a stored procedure call. An SQL statement must be of the particular dialect or version supported by the provider's query processor.

If the Prepared property of the Command object is set to True and the Command object is bound to an open connection when you set the CommandText property, ADO prepares the query (that is, a compiled form of the query that is stored by the provider) when you call the Execute or Open methods.

Depending on the CommandType property setting, ADO may alter the CommandText property. You can read the CommandText property at any time to see the actual command text that ADO will use during execution.

Use the CommandText property to set or return a relative URL that specifies a resource, such as a file or directory. The resource is relative to a location specified explicitly by an absolute URL, or implicitly by an open Connection object.

Note URLs using the http scheme will automatically invoke the Microsoft OLE DB Provider for Internet Publishing. For more information, see Absolute and Relative URLs.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Requery Method

Applies To: Command Object


CommandTimeout Property (ADO)

ADO API Reference

CommandTimeout Property

Indicates how long to wait while executing a command before terminating the attempt and generating an error.

Settings and Return Values

Sets or returns a Long value that indicates, in seconds, how long to wait for a command to execute. Default is 30.

Remarks

Use the CommandTimeout property on a Connection object or Command object to allow the cancellation of an Execute method call, due to delays from network traffic or heavy server use. If the interval set in the CommandTimeout property elapses before the command completes execution, an error occurs and ADO cancels the command. If you set the property to zero, ADO will wait indefinitely until the execution is complete. Make sure the provider and data source to which you are writing code support the CommandTimeout functionality.

The CommandTimeout setting on a Connection object has no effect on the CommandTimeout setting on a Command object on the same Connection; that is, the Command object's CommandTimeout property does not inherit the value of the Connection object's CommandTimeout value.

On a Connection object, the CommandTimeout property remains read/write after the Connection is opened.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

ConnectionTimeout Property

Applies To: Command Object | Connection Object


CommandType Property (ADO)

ADO API Reference

CommandType Property

Indicates the type of a Command object.

Settings and Return Values

Sets or returns one or more CommandTypeEnum values.

Note Do not use the CommandTypeEnum values of adCmdFile or adCmdTableDirect with CommandType. These values can only be used as options with the Open and Requery methods of a Recordset.

Remarks

Use the CommandType property to optimize evaluation of the CommandText property.

If the CommandType property value equals adCmdUnknown (the default value), you may experience diminished performance because ADO must make calls to the provider to determine if the CommandText property is an SQL statement, a stored procedure, or a table name. If you know what type of command you're using, setting the CommandType property instructs ADO to go directly to the relevant code. If the CommandType property does not match the type of command in the CommandText property, an error occurs when you call the Execute method.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Command Object


ConnectionString Property (ADO)

ADO API Reference

ConnectionString Property

Indicates the information used to establish a connection to a data source.

Settings and Return Values

Sets or returns a String value.

Remarks

Use the ConnectionString property to specify a data source by passing a detailed connection string containing a series of argument = value statements separated by semicolons.

ADO supports five arguments for the ConnectionString property; any other arguments pass directly to the provider without any processing by ADO. The arguments ADO supports are as follows.

Argument | Description
Provider= | Specifies the name of a provider to use for the connection.
File Name= | Specifies the name of a provider-specific file (for example, a persisted data source object) containing preset connection information.
Remote Provider= | Specifies the name of a provider to use when opening a client-side connection. (Remote Data Service only.)
Remote Server= | Specifies the path name of the server to use when opening a client-side connection. (Remote Data Service only.)
URL= | Specifies the connection string as an absolute URL identifying a resource, such as a file or directory.

After you set the ConnectionString property and open the Connection object, the provider may alter the contents of the property, for example, by mapping the ADO-defined argument names to their provider equivalents.

The ConnectionString property automatically inherits the value used for the ConnectionString argument of the Open method, so you can override the current ConnectionString property during the Open method call.

Because the File Name argument causes ADO to load the associated provider, you cannot pass both the Provider and File Name arguments.

The ConnectionString property is read/write when the connection is closed and read-only when it is open.

Duplicates of an argument in the ConnectionString property are ignored. The last instance of any argument is used.

Remote Data Service Usage When used on a client-side Connection object, the ConnectionString property can include only the Remote Provider and Remote Server parameters.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Appendix A: Providers

Applies To: Connection Object


ConnectionTimeout Property (ADO)

ADO API Reference

ConnectionTimeout Property

Indicates how long to wait while establishing a connection before terminating the attempt and generating an error.

Settings and Return Values

Sets or returns a Long value that indicates, in seconds, how long to wait for the connection to open. Default is 15.

Remarks

Use the ConnectionTimeout property on a Connection object if delays from network traffic or heavy server use make it necessary to abandon a connection attempt. If the time from the ConnectionTimeout property setting elapses prior to the opening of the connection, an error occurs and ADO cancels the attempt. If you set the property to zero, ADO will wait indefinitely until the connection is opened. Make sure the provider to which you are writing code supports the ConnectionTimeout functionality.

The ConnectionTimeout property is read/write when the connection is closed and read-only when it is open.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

CommandTimeout Property

Applies To: Connection Object


Count Property (ADO)

ADO API Reference

Count Property

Indicates the number of objects in a collection.

Return Value

Returns a Long value.

Remarks

Use the Count property to determine how many objects are in a given collection.

Because numbering for members of a collection begins with zero, you should always code loops starting with the zero member and ending with the value of the Count property minus 1. If you are using Microsoft Visual Basic and want to loop through the members of a collection without checking the Count property, use the For Each...Next command.

If the Count property is zero, there are no objects in the collection.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Refresh Method

Applies To: Axes Collection | Columns Collection | CubeDefs Collection | Dimensions Collection | Errors Collection | Fields Collection | Groups Collection | Hierarchies Collection | Indexes Collection | Keys Collection | Levels Collection | Members Collection | Parameters Collection | Positions Collection | Procedures Collection | Properties Collection | Tables Collection | Users Collection | Views Collection


CursorLocation Property (ADO)

ADO API Reference

CursorLocation Property

Indicates the location of the cursor service.

Settings And Return Values

Sets or returns a Long value that can be set to one of the CursorLocationEnum values.

Remarks

This property allows you to choose between various cursor libraries accessible to the provider. Usually, you can choose between using a client-side cursor library or one that is located on the server.

This property setting affects connections established only after the property has been set. Changing the CursorLocation property has no effect on existing connections.

Cursors returned by the Execute method inherit this setting. Recordset objects will automatically inherit this setting from their associated connections.

This property is read/write on a Connection or a closed Recordset, and read-only on an open Recordset.

Remote Data Service Usage When used on a client-side Recordset or Connection object, the CursorLocation property can only be set to adUseClient.

See Also

Appendix A: Providers

Applies To: Connection Object | Recordset Object


CursorType Property (ADO)

ADO API Reference

CursorType Property

Indicates the type of cursor used in a Recordset object.

Settings and Return Values

Sets or returns a CursorTypeEnum value. The default value is adOpenForwardOnly.

Remarks

Use the CursorType property to specify the type of cursor that should be used when opening the Recordset object.

Only a setting of adOpenStatic is supported if the CursorLocation property is set to adUseClient. If an unsupported value is set, then no error will result; the closest supported CursorType will be used instead.

If a provider does not support the requested cursor type, it may return another cursor type. The CursorType property will change to match the actual cursor type in use when the Recordset object is open. To verify specific functionality of the returned cursor, use the Supports method. After you close the Recordset, the CursorType property reverts to its original setting.

The following chart shows the provider functionality (identified by Supports method constants) required for each cursor type.

For a Recordset of this CursorType | The Supports method must return True for all of these constants
adOpenForwardOnly | none
adOpenKeyset | adBookmark, adHoldRecords, adMovePrevious, adResync
adOpenDynamic | adMovePrevious
adOpenStatic | adBookmark, adHoldRecords, adMovePrevious, adResync

Note Although Supports(adUpdateBatch) may be true for dynamic and forward-only cursors, for batch updates you should use either a keyset or static cursor. Set the LockType property to adLockBatchOptimistic and the CursorLocation property to adUseClient to enable the Cursor Service for OLE DB, which is required for batch updates.

The CursorType property is read/write when the Recordset is closed and read-only when it is open.

Remote Data Service Usage When used on a client-side Recordset object, the CursorType property can be set only to adOpenStatic.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Supports Method

Applies To: Recordset Object


DataMember Property (ADO)

ADO API Reference

DataMember Property

Indicates the name of the data member that will be retrieved from the object referenced by the DataSource property.

Settings and Return Values

Sets or returns a String value. The name is not case sensitive.

Remarks

This property is used to create data-bound controls with the Data Environment. The Data Environment maintains collections of data (data sources) containing named objects (data members) that will be represented as a Recordset object.

The DataMember and DataSource properties must be used in conjunction.

The DataMember property determines which object specified by the DataSource property will be represented as a Recordset object. The Recordset object must be closed before this property is set. An error is generated if the DataMember property isn't set before the DataSource property, or if the DataMember name isn't recognized by the object specified in the DataSource property.

Usage

Dim rs as New ADODB.Recordset
rs.DataMember = "Command"     'Name of the rowset to bind to
Set rs.DataSource = myDE      'Name of the object containing an IRowset

See Also

DataSource Property

Applies To: Recordset Object


DataSource Property (ADO)

ADO API Reference

DataSource Property

Indicates an object that contains data to be represented as a Recordset object.

Remarks

This property is used to create data-bound controls with the Data Environment. The Data Environment maintains collections of data (data sources) containing named objects (data members) that will be represented as a Recordset object.

The DataMember and DataSource properties must be used in conjunction.

The object referenced must implement the IDataSource interface and must contain an IRowset interface.

Usage

Dim rs as New ADODB.Recordset
rs.DataMember = "Command"     'Name of the rowset to bind to
Set rs.DataSource = myDE      'Name of the object containing an IRowset

See Also

DataMember Property

Applies To: Recordset Object


DefaultDatabase Property (ADO)

ADO API Reference

DefaultDatabase Property

Indicates the default database for a Connection object.

Settings and Return Values

Sets or returns a String value that evaluates to the name of a database available from the provider.

Remarks

Use the DefaultDatabase property to set or return the name of the default database on a specific Connection object.

If there is a default database, SQL strings may use an unqualified syntax to access objects in that database. To access objects in a database other than the one specified in the DefaultDatabase property, you must qualify object names with the desired database name. Upon connection, the provider will write default database information to the DefaultDatabase property. Some providers allow only one database per connection, in which case you cannot change the DefaultDatabase property.

Some data sources and providers may not support this feature, and may return an error or an empty string.

Remote Data Service Usage This property is not available on a client-side Connection object.

See Also

Visual Basic Example

Applies To: Connection Object


DefinedSize Property (ADO)

ADO API Reference

DefinedSize Property

Indicates the data capacity of a Field object.

Return Value

Returns a Long value that reflects the defined size of a field as a number of bytes.

Remarks

Use the DefinedSize property to determine the data capacity of a Field object.

The DefinedSize and ActualSize properties are different. For example, consider a Field object with a declared type of adVarChar and a DefinedSize property value of 50, containing a single character. The ActualSize property value it returns is the length in bytes of the single character.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

ActualSize Property

Applies To: Field Object


Description Property (ADO)

ADO API Reference

Description Property

Describes an Error object.

Return Value

Returns a String value that contains a description of the error.

Remarks

Use the Description property to obtain a short description of the error. Display this property to alert the user to an error that you cannot or do not want to handle. The string will come from either ADO or a provider.

Providers are responsible for passing specific error text to ADO. ADO adds an Error object to the Errors collection for each provider error or warning it receives. Enumerate the Errors collection to trace the errors that the provider passes.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

HelpContext, HelpFile Properties | Number Property | Source Property (ADO Error)

Applies To: Error Object


Dialect Property (ADO)

ADO API Reference

Dialect Property

Indicates the dialect of the CommandText or CommandStream properties. The dialect defines the syntax and general rules that the provider uses to parse the string or stream.

Settings and Return Values

The Dialect property contains a valid GUID that represents the dialect of the command text or stream. The default value for this property is {C8B521FB-5CF3-11CE-ADE5-00AA0044773D}, which indicates that the provider should choose how to interpret the command text or stream.

Remarks

ADO does not query the provider when the user reads the value of this property; it returns a string representation of the value currently stored in the Command object.

When the user sets the Dialect property, ADO validates the GUID and raises an error if the value supplied is not a valid GUID. See the documentation for your provider to determine the GUID values supported by the Dialect property.

See Also

CommandStream Property | CommandText Property | Execute Method (ADO Command)

Applies To: Command


Direction Property (ADO)

ADO API Reference

Direction Property

Indicates whether the Parameter represents an input parameter, an output parameter, an input and an output parameter, or if the parameter is the return value from a stored procedure.

Settings and Return Values

Sets or returns a ParameterDirectionEnum value.

Remarks

Use the Direction property to specify how a parameter is passed to or from a procedure. The Direction property is read/write; this allows you to work with providers that don't return this information or to set this information when you don't want ADO to make an extra call to the provider to retrieve parameter information.

Not all providers can determine the direction of parameters in their stored procedures. In these cases, you must set the Direction property before you execute the query.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Parameter Object


EditMode Property (ADO)

ADO API Reference

EditMode Property

Indicates the editing status of the current record.

Return Value

Returns an EditModeEnum value.

Remarks

ADO maintains an editing buffer associated with the current record. This property indicates whether changes have been made to this buffer, or whether a new record has been created. Use the EditMode property to determine the editing status of the current record. You can test for pending changes if an editing process has been interrupted and determine whether you need to use the Update or CancelUpdate method.

See the AddNew method for a more detailed description of the EditMode property under different editing conditions.

When a call to Delete does not successfully delete the record or records in the data source (due to referential integrity violations, for example), the Recordset will remain in edit mode (EditMode = adEditInProgress). This means that CancelUpdate must be called before moving off the current record (with Move, NextRecordset, or Close, for example).

Note EditMode can return a valid value only if there is a current record. EditMode will return an error if BOF or EOF is true, or if the current record has been deleted.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

AddNew Method | CancelUpdate Method | Update Method

Applies To: Recordset Object


EOS Property (ADO)

ADO API Reference

EOS Property

Indicates whether the current position is at the end of the stream.

Return Values

Returns a Boolean value that indicates whether the current position is at the end of the stream. EOS returns True if there are no more bytes in the stream; it returns False if there are more bytes following the current position.

To set the end of stream position, use the SetEOS method. To determine the current position, use the Position property.

See Also

Applies To: Stream Object


Filter Property (ADO)

ADO API Reference

Filter Property

Indicates a filter for data in a Recordset.

Settings and Return Values

Sets or returns a Variant value, which can contain one of the following:

  • Criteria string — a string made up of one or more individual clauses concatenated with AND or OR operators.
  • Array of bookmarks — an array of unique bookmark values that point to records in the Recordset object.
  • A FilterGroupEnum value.

Remarks

Use the Filter property to selectively screen out records in a Recordset object. The filtered Recordset becomes the current cursor. Other properties that return values based on the current cursor are affected, such as AbsolutePosition, AbsolutePage, RecordCount, and PageCount. This is because setting the Filter property to a specific value will move the current record to the first record that satisfies the new value.

The criteria string is made up of clauses in the form FieldName-Operator-Value (for example, "LastName = 'Smith'"). You can create compound clauses by concatenating individual clauses with AND (for example, "LastName = 'Smith' AND FirstName = 'John'") or OR (for example, "LastName = 'Smith' OR LastName = 'Jones'"). Use the following guidelines for criteria strings:

  • FieldName must be a valid field name from the Recordset. If the field name contains spaces, you must enclose the name in square brackets.
  • Operator must be one of the following: <, >, <=, >=, <>, =, or LIKE.
  • Value is the value with which you will compare the field values (for example, 'Smith', #8/24/95#, 12.345, or $50.00). Use single quotes with strings and pound signs (#) with dates. For numbers, you can use decimal points, dollar signs, and scientific notation. If Operator is LIKE, Value can use wildcards. Only the asterisk (*) and percent sign (%) wild cards are allowed, and they must be the last character in the string. Value cannot be null.

Note To include single quotation marks (') in the filter Value, use two single quotation marks to represent one. For example, to filter on O'Malley, the criteria string should be "col1 = 'O''Malley'". To include single quotation marks at both the beginning and the end of the filter value, enclose the string with pound signs (#). For example, to filter on '1', the criteria string should be "col1 = #'1'#".

  • There is no precedence between AND and OR. Clauses can be grouped within parentheses. However, you cannot group clauses joined by an OR and then join the group to another clause with an AND, like this:
    (LastName = 'Smith' OR LastName = 'Jones') AND FirstName = 'John'
  • Instead, you would construct this filter as
    (LastName = 'Smith' AND FirstName = 'John') OR (LastName = 'Jones' AND FirstName = 'John')
  • In a LIKE clause, you can use a wildcard at the beginning and end of the pattern (for example, LastName Like '*mit*'), or only at the end of the pattern (for example, LastName Like 'Smit*').

The filter constants make it easier to resolve individual record conflicts during batch update mode by allowing you to view, for example, only those records that were affected during the last UpdateBatch method call.

Setting the Filter property itself may fail because of a conflict with the underlying data (for example, a record has already been deleted by another user). In such a case, the provider returns warnings to the Errors collection but does not halt program execution. A run-time error occurs only if there are conflicts on all the requested records. Use the Status property to locate records with conflicts.

Setting the Filter property to a zero-length string ("") has the same effect as using the adFilterNone constant.

Whenever the Filter property is set, the current record position moves to the first record in the filtered subset of records in the Recordset. Similarly, when the Filter property is cleared, the current record position moves to the first record in the Recordset.

See the Bookmark property for an explanation of bookmark values from which you can build an array to use with the Filter property.

Only Filters in the form of Criteria Strings (e.g. OrderDate > '12/31/1999') affect the contents of a persisted Recordset. Filters created with an Array of Bookmarks or using a value from the FilterGroupEnum will not affect the contents of the persisted Recordset. These rules apply to Recordsets created with either client-side or server-side cursors.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Clear Method | Optimize Property—Dynamic (ADO)

Applies To: Recordset Object


HelpContext, HelpFile Properties (ADO)

ADO API Reference

HelpContext, HelpFile Properties

Indicates the help file and topic associated with an Error object.

Return Values

  • HelpContextID — returns a context ID, as a Long value, for a topic in a Help file.
  • HelpFile — returns a String value that evaluates to a fully resolved path to a Help file.

Remarks

If a Help file is specified in the HelpFile property, the HelpContext property is used to automatically display the Help topic it identifies. If there is no relevant help topic available, the HelpContext property returns zero and the HelpFile property returns a zero-length string ("").

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Description Property | Number Property | Source Property (ADO Error)

Applies To: Error Object


Index Property (ADO)

ADO API Reference

Index Property

Indicates the name of the index currently in effect for a Recordset object.

Settings and Return Values

Sets or returns a String value, which is the name of the index.

Remarks

The index named by the Index property must have previously been declared on the base table underlying the Recordset object. That is, the index must have been declared programmatically either as an ADOX Index object, or when the base table was created.

A run-time error will occur if the index cannot be set. The Index property cannot be set:

  • Within a WillChangeRecordset or RecordsetChangeComplete event handler.
  • If the Recordset is still executing an operation (which can be determined by the State property).
  • If a filter has been set on the Recordset with the Filter property.

The Index property can always be set successfully if the Recordset is closed, but the Recordset will not open successfully, or the index will not be usable, if the underlying provider does not support indexes.

If the index can be set, the current row position may change. This will cause an update to the AbsolutePosition property, and the generation of WillChangeRecordset, RecordsetChangeComplete, WillMove, and MoveComplete events.

If the index can be set and the LockType property is adLockPessimistic or adLockOptimistic, then an implicit UpdateBatch operation is performed. This releases the current and affected groups. Any existing filter is released, and the current row position is changed to the first row of the reordered Recordset.

The Index property is used in conjunction with the Seek method. If the underlying provider does not support the Index property, and thus the Seek method, consider using the Find method instead. Determine whether the Recordset object supports indexes with the Supports (adIndex) method.

The built-in Index property is not related to the dynamic Optimize property, although they both deal with indexes.

See Also

Visual Basic Example

Index Object | Seek Method

Applies To: Recordset Object


IsolationLevel Property (ADO)

ADO API Reference

IsolationLevel Property

Indicates the level of isolation for a Connection object.

Settings and Return Values

Sets or returns an IsolationLevelEnum value. The default is adXactChaos.

Remarks

Use the IsolationLevel property to set the isolation level of a Connection object. The setting does not take effect until the next time you call the BeginTrans method. If the level of isolation you request is unavailable, the provider may return the next greater level of isolation.

The IsolationLevel property is read/write.

Remote Data Service Usage When used on a client-side Connection object, the IsolationLevel property can be set only to adXactUnspecified.

Because users are working with disconnected Recordset objects on a client-side cache, there may be multiuser issues. For instance, when two different users try to update the same record, Remote Data Service simply allows the user who updates the record first to "win." The second user's update request will fail with an error.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Connection Object


Item Property (ADO)

ADO API Reference

Item Property

Indicates a specific member of a collection, by name or ordinal number.

Syntax

Set object = collection.Item ( Index )

Return Value

Returns an object reference.

Parameters

Index
A Variant expression that evaluates either to the name or to the ordinal number of an object in a collection.

Remarks

Use the Item property to return a specific object in a collection. If Item cannot find an object in the collection corresponding to the Index argument, an error occurs. Also, some collections don't support named objects; for these collections, you must use ordinal number references.

The Item property is the default property for all collections; therefore, the following syntax forms are interchangeable:

collection.Item (Index)
collection (Index)

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Axes Collection | Columns Collection | CubeDefs Collection | Dimensions Collection | Errors Collection | Fields Collection | Groups Collection | Hierarchies Collection | Indexes Collection | Keys Collection | Levels Collection | Members Collection | Parameters Collection | Positions Collection | Procedures Collection | Properties Collection | Tables Collection | Users Collection | Views Collection


LineSeparator Property (ADO)

ADO API Reference

LineSeparator Property

Indicates the binary character to be used as the line separator in text Stream objects.

Settings and Return Values

Sets or returns a LineSeparatorsEnum value that indicates the line separator character used in the Stream. The default value is adCRLF.

Remarks

LineSeparator is used to interpret lines when reading the content of a text Stream. Lines can be skipped with the SkipLine method.

LineSeparator is used only with text Stream objects (Type is adTypeText). This property is ignored if Type is adTypeBinary.

See Also

Applies To: Stream Object


LockType Property (ADO)

ADO API Reference

LockType Property

Indicates the type of locks placed on records during editing.

Settings and Return Values

Sets or returns a LockTypeEnum value. The default value is adLockReadOnly.

Remarks

Set the LockType property before opening a Recordset to specify what type of locking the provider should use when opening it. Read the property to return the type of locking in use on an open Recordset object.

Providers may not support all lock types. If a provider cannot support the requested LockType setting, it will substitute another type of locking. To determine the actual locking functionality available in a Recordset object, use the Supports method with adUpdate and adUpdateBatch.

The adLockPessimistic setting is not supported if the CursorLocation property is set to adUseClient. If an unsupported value is set, then no error will result; the closest supported LockType will be used instead.

The LockType property is read/write when the Recordset is closed and read-only when it is open.

Remote Data Service Usage When used on a client-side Recordset object, the LockType property can only be set to adLockBatchOptimistic.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

CancelBatch Method | UpdateBatch Method

Applies To: Recordset Object


MarshalOptions Property (ADO)

ADO API Reference

MarshalOptions Property

Indicates which records are to be marshaled back to the server.

Settings And Return Values

Sets or returns a MarshalOptionsEnum value. The default value is adMarshalAll.

Remarks

When using a client-side Recordset, records that have been modified on the client are written back to the middle tier or Web server through a technique called marshaling, the process of packaging and sending interface method parameters across thread or process boundaries. Setting the MarshalOptions property can improve performance when modified remote data is marshaled for updating back to the middle tier or Web server.

Remote Data Service Usage This property is used only on a client-side Recordset.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Recordset Object


MaxRecords Property (ADO)

ADO API Reference

MaxRecords Property

Indicates the maximum number of records to return to a Recordset from a query.

Settings and Return Values

Sets or returns a Long value that indicates the maximum number of records to return. Default is zero (no limit).

Remarks

Use the MaxRecords property to limit the number of records that the provider returns from the data source. The default setting of this property is zero, which means the provider returns all requested records.

The MaxRecords property is read/write when the Recordset is closed and read-only when it is open.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Recordset Object


Mode Property (ADO)

ADO API Reference

Mode Property

Indicates the available permissions for modifying data in a Connection, Record, or Stream object.

Settings and Return Values

Sets or returns a ConnectModeEnum value. The default value for a Connection is adModeUnknown. The default value for a Record object is adModeRead. The default value for a Stream associated with an underlying source (opened with a URL as the source, or as the default Stream of a Record) is adModeRead. The default value for a Stream not associated with an underlying source (instantiated in memory) is adModeUnknown.

Remarks

Use the Mode property to set or return the access permissions in use by the provider on the current connection. You can set the Mode property only when the Connection object is closed.

For a Stream object, if the access mode is not specified, it is inherited from the source used to open the Stream object. For example, if a Stream is opened from a Record object, by default it is opened in the same mode as the Record.

This property is read/write while the object is closed and read-only while the object is open.

Remote Data Service Usage When used on a client-side Connection object, the Mode property can only be set to adModeUnknown.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Connection Object | Record Object | Stream Object


Name Property (ADO)

ADO API Reference

Name Property

Indicates the name of an object.

Settings and Return Values

Sets or returns a String value that indicates the name of an object.

Remarks

Use the Name property to assign a name to or retrieve the name of a Command, Property, Field, or Parameter object.

The value is read/write on a Command object and read-only on a Property object.

For a Field object, Name is normally read-only. However, for new Field objects that have been appended to the Fields collection of a Record, Name is read/write only after the Value property for the Field has been specified and the data provider has successfully added the new Field by calling the Update method of the Fields collection.

For Parameter objects not yet appended to the Parameters collection, the Name property is read/write. For appended Parameter objects and all other objects, the Name property is read-only. Names do not have to be unique within a collection.

You can retrieve the Name property of an object by an ordinal reference, after which you can refer to the object directly by name. For example, if rstMain.Properties(20).Name yields Updatability, you can subsequently refer to this property as rstMain.Properties("Updatability").

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Command Object | Field Object | Parameter Object | Property Object


NamedParameters Property (ADO)

ADO API Reference

NamedParameters Property

Indicates whether parameter names should be passed to the provider.

Remarks

When this property is true, ADO passes the value of the Name property of each parameter in the Command’s Parameter collection. The provider uses a parameter name to match parameters in the CommandText or CommandStream properties. If this property is false (the default), parameter names are ignored and the provider uses the order of parameters to match values to parameters in the CommandText or CommandStream properties.

See Also

CommandText Property | CommandStream Property | Parameters Collection

Applies To: Command Object


NativeError Property (ADO)

ADO API Reference

NativeError Property

Indicates the provider-specific error code for a given Error object.

Return Value

Returns a Long value that indicates the error code.

Remarks

Use the NativeError property to retrieve the database-specific error information for a particular Error object. For example, when using the Microsoft ODBC Provider for OLE DB with a Microsoft SQL Server database, native error codes that originate from SQL Server pass through ODBC and the ODBC Provider to the ADO NativeError property.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Error Object


Number Property (ADO)

ADO API Reference

Number Property

Indicates the number that uniquely identifies an Error object.

Return Value

Returns a Long value that may correspond to one of the ErrorValueEnum constants.

Remarks

Use the Number property to determine which error occurred. The value of the property is a unique number that corresponds to the error condition.

The Errors collection returns an HRESULT in either hexadecimal format (for example, 0x80004005) or long value (for example, 2147467259). These HRESULTs can be raised by underlying components, such as OLE DB or even OLE itself. For more information about these numbers, see Chapter 16 of the OLE DB Programmer's Reference.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Description Property | HelpContext, HelpFile Properties | Source Property (ADO Error)

Applies To: Error Object


NumericScale Property (ADO)

ADO API Reference

NumericScale Property

Indicates the scale of numeric values in a Parameter or Field object.

Settings and Return Values

Sets or returns a Byte value that indicates the number of decimal places to which numeric values will be resolved.

Remarks

Use the NumericScale property to determine how many digits to the right of the decimal point will be used to represent values for a numeric Parameter or Field object.

For Parameter objects, the NumericScale property is read/write.

For a Field object, NumericScale is normally read-only. However, for new Field objects that have been appended to the Fields collection of a Record, NumericScale is read/write only after the Value property for the Field has been specified and the data provider has successfully added the new Field by calling the Update method of the Fields collection.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Precision Property

Applies To: Field Object | Parameter Object


OriginalValue Property (ADO)

ADO API Reference

OriginalValue Property

Indicates the value of a Field that existed in the record before any changes were made.

Return Value

Returns a Variant value that represents the value of a field prior to any change.

Remarks

Use the OriginalValue property to return the original field value for a field from the current record.

In immediate update mode (in which the provider writes changes to the underlying data source after you call the Update method), the OriginalValue property returns the field value that existed prior to any changes (that is, since the last Update method call). This is the same value that the CancelUpdate method uses to replace the Value property.

In batch update mode (in which the provider caches multiple changes and writes them to the underlying data source only when you call the UpdateBatch method), the OriginalValue property returns the field value that existed prior to any changes (that is, since the last UpdateBatch method call). This is the same value that the CancelBatch method uses to replace the Value property. When you use this property with the UnderlyingValue property, you can resolve conflicts that arise from batch updates.

Record

For Record objects, the OriginalValue property will be empty for fields added before Update is called.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

UnderlyingValue Property

Applies To: Field Object


PageCount Property (ADO)

ADO API Reference

PageCount Property

Indicates how many pages of data the Recordset object contains.

Return Value

Returns a Long value that indicates the number of pages in the Recordset.

Remarks

Use the PageCount property to determine how many pages of data are in the Recordset object. Pages are groups of records whose size equals the PageSize property setting. Even if the last page is incomplete because there are fewer records than the PageSize value, it counts as an additional page in the PageCount value. If the Recordset object does not support this property, the value will be -1 to indicate that the PageCount is indeterminable.

See the PageSize and AbsolutePage properties for more on page functionality.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

AbsolutePage Property | PageSize Property | RecordCount Property

Applies To: Recordset Object


PageSize Property (ADO)

ADO API Reference

PageSize Property

Indicates how many records constitute one page in the Recordset.

Settings and Return Values

Sets or returns a Long value that indicates how many records are on a page. The default is 10.

Remarks

Use the PageSize property to determine how many records make up a logical page of data. Establishing a page size allows you to use the AbsolutePage property to move to the first record of a particular page. This is useful in Web-server scenarios when you want to allow the user to page through data, viewing a certain number of records at a time.

This property can be set at any time, and its value will be used for calculating the location of the first record of a particular page.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

AbsolutePage Property | PageCount Property

Applies To: Recordset Object


ParentURL Property (ADO)

ADO API Reference

ParentURL Property

Indicates an absolute URL string that points to the parent Record of the current Record object.

Return Value

Returns a String value that indicates the URL of the parent Record.

Remarks

The ParentURL property depends upon the source used to open the Record object. For example, the Record may be opened with a source containing a relative path name of a directory referenced by the ActiveConnection property.

Suppose "second" is a folder contained under "first". Open the Record object with the following:

record.ActiveConnection = "http://first"
record.Open "second"

Now, the value of the ParentURL property is "http://first", the same as ActiveConnection.

The source may also be an absolute URL such as, "http://first/second". The ParentURL property is then "http://first", the level above "second".

This property may be a null value if:

  • There is no parent for the current object; for example, if the Record object represents the root of a directory.
  • The Record object represents an entity that cannot be specified with a URL.

This property is read-only.

Note This property is only supported by document source providers, such as the Microsoft OLE DB Provider for Internet Publishing. For more information, see Records and Provider-Supplied Fields.

Note URLs using the http scheme will automatically invoke the Microsoft OLE DB Provider for Internet Publishing. For more information, see Absolute and Relative URLs.

Note If the current record contains a data record from an ADO Recordset, accessing the ParentURL property causes a run-time error, indicating that no URL is possible.

See Also

Applies To: Record Object


Position Property (ADO)

ADO API Reference

Position Property

Indicates the current position within a Stream object.

Settings and Return Values

Sets or returns a Long value that specifies the offset, in number of bytes, of the current position from the beginning of the stream. The default is 0, which represents the first byte in the stream.

Remarks

The current position can be moved to a point after the end of the stream. If you specify the current position beyond the end of the stream, the Size of the Stream object will be increased accordingly. Any new bytes added in this way will be null.

Notes Position always measures bytes. For text streams using multibyte character sets, multiply the position by the character size to determine the character number. For example, for a two-byte character set, the first character is at position 0, the second character at position 2, the third character at position 4, and so on.

Negative values cannot be used to change the current position in a Stream. Only positive numbers can be used for Position.

For read-only Stream objects, ADO will not return an error if Position is set to a value greater than the Size of the Stream. This does not change the size of the Stream, or alter the Stream contents in any way. However, doing this should be avoided because it results in a meaningless Position value.

See Also

Example

Charset Property

Applies To: Stream Object


Precision Property (ADO)

ADO API Reference

Precision Property

Indicates the degree of precision for numeric values in a Parameter object or for numeric Field objects.

Settings and Return Values

Sets or returns a Byte value that indicates the maximum number of digits used to represent values.

Remarks

Use the Precision property to determine the maximum number of digits used to represent values for a numeric Parameter or Field object.

The value is read/write on a Parameter object.

For a Field object, Precision is normally read-only. However, for new Field objects that have been appended to the Fields collection of a Record, Precision is read/write only after the Value property for the Field has been specified and the data provider has successfully added the new Field by calling the Update method of the Fields collection.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

NumericScale Property

Applies To: Field Object | Parameter Object


Prepared Property (ADO)

ADO API Reference

Prepared Property

Indicates whether to save a compiled version of a command before execution.

Settings and Return Values

Sets or returns a Boolean value that, if set to True, indicates that the command should be prepared.

Remarks

Use the Prepared property to have the provider save a prepared (or compiled) version of the query specified in the CommandText property before a Command object's first execution. This may slow a command's first execution, but once the provider compiles a command, the provider will use the compiled version of the command for any subsequent executions, which will result in improved performance.

If the property is False, the provider will execute the Command object directly without creating a compiled version.

If the provider does not support command preparation, it may return an error as soon as this property is set to True. If it does not return an error, it simply ignores the request to prepare the command and sets the Prepared property to False.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Command Object


Provider Property (ADO)

ADO API Reference

Provider Property

Indicates the name of the provider for a Connection object.

Settings and Return Values

Sets or returns a String value that indicates the provider name.

Remarks

Use the Provider property to set or return the name of the provider for a connection. This property can also be set by the contents of the ConnectionString property or the ConnectionString argument of the Open method; however, specifying a provider in more than one place while calling the Open method can have unpredictable results. If no provider is specified, the property will default to MSDASQL (Microsoft OLE DB Provider for ODBC).

The Provider property is read/write when the connection is closed and read-only when it is open. The setting does not take effect until you either open the Connection object or access the Properties collection of the Connection object. If the setting is not valid, an error occurs.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Microsoft OLE DB Provider for ODBC | Appendix A: Providers

Applies To: Connection Object


RecordCount Property (ADO)

ADO API Reference

RecordCount Property

Indicates the number of records in a Recordset object.

Return Value

Returns a Long value that indicates the number of records in the Recordset.

Remarks

Use the RecordCount property to find out how many records are in a Recordset object. The property returns -1 when ADO cannot determine the number of records or if the provider or cursor type does not support RecordCount. Reading the RecordCount property on a closed Recordset causes an error.

If the Recordset object supports approximate positioning or bookmarks—that is, Supports (adApproxPosition) or Supports (adBookmark), respectively, return True—this value will be the exact number of records in the Recordset, regardless of whether it has been fully populated. If the Recordset object does not support approximate positioning, this property may be a significant drain on resources because all records will have to be retrieved and counted to return an accurate RecordCount value.

The cursor type of the Recordset object affects whether the number of records can be determined. The RecordCount property will return -1 for a forward-only cursor; the actual count for a static or keyset cursor; and either -1 or the actual count for a dynamic cursor, depending on the data source.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

AbsolutePosition Property | PageCount Property

Applies To: Recordset Object


RecordType Property (ADO)

ADO API Reference

RecordType Property

Indicates the type of Record object.

Return Value

Returns a RecordTypeEnum value.

Remarks

The RecordType property is read-only.

See Also

Example

Type Property | Type Property (ADO Stream)

Applies To: Record Object


Size Property (ADO)

ADO API Reference

Size Property

Indicates the maximum size, in bytes or characters, of a Parameter object.

Settings and Return Values

Sets or returns a Long value that indicates the maximum size in bytes or characters of a value in a Parameter object.

Remarks

Use the Size property to determine the maximum size for values written to or read from the Value property of a Parameter object.

If you specify a variable-length data type for a Parameter object (for example, any String type, such as adVarChar), you must set the object's Size property before appending it to the Parameters collection; otherwise, an error occurs.

If you have already appended the Parameter object to the Parameters collection of a Command object and you change its type to a variable-length data type, you must set the Parameter object's Size property before executing the Command object; otherwise, an error occurs.

If you use the Refresh method to obtain parameter information from the provider and it returns one or more variable-length data type Parameter objects, ADO may allocate memory for the parameters based on their maximum potential size, which could cause an error during execution. To prevent an error, you should explicitly set the Size property for these parameters before executing the command.

The Size property is read/write.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Size Property (ADO Stream)

Applies To: Parameter Object


Size Property (ADO Stream)

Indicates the size of the stream in number of bytes.

Return Values

Returns a Long value that specifies the size of the stream in number of bytes. The default value is the size of the stream, or -1 if the size of the stream is not known.

Remarks

Size can be used only with open Stream objects.

Note Any number of bits can be stored in a Stream object, limited only by system resources. If the Stream contains more bits than can be represented by a Long value, Size is truncated and therefore does not accurately represent the length of the Stream.

See Also

Size Property

Applies To: Stream Object


Sort Property (ADO)

ADO API Reference

Sort Property

Indicates one or more field names on which the Recordset is sorted, and whether each field is sorted in ascending or descending order.

Settings and Return Values

Sets or returns a String value that indicates the field names in the Recordset on which to sort. Each name is separated by a comma, and is optionally followed by a blank and the keyword, ASC, which sorts the field in ascending order, or DESC, which sorts the field in descending order. By default, if no keyword is specified, the field is sorted in ascending order.

Remarks

This property requires the CursorLocation property to be set to adUseClient. A temporary index will be created for each field specified in the Sort property if an index does not already exist.

The sort operation is efficient because data is not physically rearranged, but is simply accessed in the order specified by the index.

Setting the Sort property to an empty string will reset the rows to their original order and delete temporary indexes. Existing indexes will not be deleted.

Suppose a Recordset contains three fields named firstName, middleInitial, and lastName. Set the Sort property to the string, "lastName DESC, firstName ASC", which will order the Recordset by last name in descending order, then by first name in ascending order. The middle initial is ignored.

No field can be named "ASC" or "DESC" because those names conflict with the keywords ASC and DESC. Give a field with a conflicting name an alias by using the AS keyword in the query that returns the Recordset.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Optimize Property—Dynamic (ADO) | SortColumn Property (RDS) | SortDirection Property (RDS)

Applies To: Recordset Object


Source Property (ADO Error)

Indicates the name of the object or application that originally generated an error.

Return Value

Returns a String value that indicates the name of an object or application.

Remarks

Use the Source property on an Error object to determine the name of the object or application that originally generated an error. This could be the object's class name or programmatic ID. For errors in ADO, the property value will be ADODB.ObjectName, where ObjectName is the name of the object that triggered the error. For ADOX and ADO MD, the value will be ADOX.ObjectName and ADOMD.ObjectName, respectively.

Based on the error documentation from the Source, Number, and Description properties of Error objects, you can write code that will handle the error appropriately.

The Source property is read-only for Error objects.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Description Property | HelpContext, HelpFile Properties | Number Property | Source Property (ADO Record) | Source Property (ADO Recordset)

Applies To: Error Object


Source Property (ADO Record)

Indicates the data source or object represented by the Record.

Settings and Return Values

Sets or returns a Variant value that indicates the entity represented by the Record.

Remarks

The Source property returns the Source argument of the Record object Open method. It can contain an absolute or relative URL string. An absolute URL can be used without setting the ActiveConnection property to directly open the Record object. An implicit Connection object is created in this case.

The Source property can also contain a reference to an already open Recordset, which opens a Record object representing the current row in the Recordset.

The Source property could also contain a reference to a Command object which returns a single row of data from the provider.

If the ActiveConnection property is also set, then the Source property must point to some object that exists within the scope of that connection. For example, in tree-structured namespaces, if the Source property contains an absolute URL, it must point to a node that exists inside the scope of the node identified by the URL in the connection string. If the Source property contains a relative URL, then it is validated within the context set by the ActiveConnection property.

The Source property is read/write while the Record object is closed, and is read-only while the Record object is open.

Note URLs using the http scheme will automatically invoke the Microsoft OLE DB Provider for Internet Publishing. For more information, see Absolute and Relative URLs.

See Also

Source Property (ADO Error) | Source Property (ADO Recordset)

Applies To: Record Object


Source Property (ADO Recordset)

Indicates the data source for a Recordset object.

Settings and Return Values

Sets a String value or Command object reference; returns only a String value that indicates the source of the Recordset.

Remarks

Use the Source property to specify a data source for a Recordset object using one of the following: a Command object variable, an SQL statement, a stored procedure, or a table name.

If you set the Source property to a Command object, the ActiveConnection property of the Recordset object will inherit the value of the ActiveConnection property for the specified Command object. However, reading the Source property does not return a Command object; instead, it returns the CommandText property of the Command object to which you set the Source property.

If the Source property is an SQL statement, a stored procedure, or a table name, you can optimize performance by passing the appropriate Options argument with the Open method call.

The Source property is read/write for closed Recordset objects and read-only for open Recordset objects.

See Also

Visual Basic Example

Source Property (ADO Error) | Source Property (ADO Record)

Applies To: Recordset Object


SQLState Property (ADO)

ADO API Reference

SQLState Property

Indicates the SQL state for a given Error object.

Return Value

Returns a five-character String value that follows the ANSI SQL standard and indicates the error code.

Remarks

Use the SQLState property to read the five-character error code that the provider returns when an error occurs during the processing of an SQL statement. For example, when using the Microsoft OLE DB Provider for ODBC with a Microsoft SQL Server database, SQL state error codes originate from ODBC, based either on errors specific to ODBC or on errors that originate from Microsoft SQL Server, and are then mapped to ODBC errors. These error codes are documented in the ANSI SQL standard, but may be implemented differently by different data sources.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Error Object


State Property (ADO)

ADO API Reference

State Property

Indicates for all applicable objects whether the state of the object is open or closed.

Indicates for all applicable objects executing an asynchronous method, whether the current state of the object is connecting, executing, or retrieving.

Return Value

Returns a Long value that can be an ObjectStateEnum value. The default value is adStateClosed.

Remarks

You can use the State property to determine the current state of a given object at any time.

The object's State property can have a combination of values. For example, if a statement is executing, this property will have a combined value of adStateOpen and adStateExecuting.

The State property is read-only.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Command Object | Connection Object | Record Object | Recordset Object | Stream Object


Status Property (ADO Field)

Indicates the status of a Field object.

Return Value

Returns a FieldStatusEnum value. The default value is adFieldOK.

Remarks

Changes to the value of a Field object in the Fields collection of either a Record or Recordset are cached until the object's Update method is called. At that point, if the change to the Field's value caused an error, OLE DB raises the error DB_E_ERRORSOCCURRED (2147749409). The Status property of any of the Field objects in the Fields collection that caused the error will contain a value from the FieldStatusEnum describing the cause of the problem.

Additions and deletions to the Fields collections of the Record object are cached until the Update method is called. The Status property enables you to determine which fields have been successfully added or deleted.

To enhance performance, schema changes are cached until Update is called, and then the changes are made in a batch optimistic update. If the Update method is not called, the server is not updated. If any updates fail then an OLE DB provider error (DB_E_ERRORSOCCURRED) is returned and the Status property indicates the combined values of the operation and error status code. For example, adFieldPendingInsert OR adFieldPermissionDenied. The Status property for each Field can be used to determine why the Field was not added, modified, or deleted.

Many types of problems encountered when adding, modifying, or deleting a Field are reported through the Status property. For example, if the user deletes a Field, it is marked for deletion from the Fields collection. If the subsequent Update returns an error because the user tried to delete a Field for which they do not have permission, the Field will have a Status of adFieldPermissionDenied OR adFieldPendingDelete. Calling the CancelUpdate method restores original values and sets the Status to adFieldOK.

Similarly, the Update method may return an error because a new Field was added and given an inappropriate value. In that case the new Field will be in the Fields collection and have a status of adFieldPendingInsert and perhaps adFieldCantCreate (depending upon your provider). You can supply an appropriate value for the new Field and call Update again.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Field Object


Status Property (ADO Recordset)

Indicates the status of the current record with respect to batch updates or other bulk operations.

Return Value

Returns a sum of one or more RecordStatusEnum values.

Remarks

Use the Status property to see what changes are pending for records modified during batch updating. You can also use the Status property to view the status of records that fail during bulk operations, such as when you call the Resync, UpdateBatch, or CancelBatch methods on a Recordset object, or set the Filter property on a Recordset object to an array of bookmarks. With this property, you can determine how a given record failed and resolve it accordingly.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Recordset Object


StayInSync Property (ADO)

ADO API Reference

StayInSync Property

Indicates, in a hierarchical Recordset object, whether the reference to the underlying child records (that is, the chapter) changes when the parent row position changes.

Settings and Return Values

Sets or returns a Boolean value. The default value is True. If True, the chapter will be updated if the parent Recordset object changes row position; if False, the chapter will continue to refer to data in the previous chapter even though the parent Recordset object has changed row position.

Remarks

This property applies to hierarchical recordsets, such as those supported by the Microsoft Data Shaping Service for OLE DB, and must be set on the parent Recordset before the child Recordset is retrieved. This property simplifies navigating hierarchical recordsets.

See Also

Visual Basic Example

Microsoft Data Shaping Service for OLE DB

Applies To: Recordset Object


Type Property (ADO)

ADO API Reference

Type Property

Indicates the operational type or data type of a Parameter, Field, or Property object.

Settings and Return Values

Sets or returns a DataTypeEnum value.

Remarks

For Parameter objects, the Type property is read/write. For new Field objects that have been appended to the Fields collection of a Record, Type is read/write only after the Value property for the Field has been specified and the data provider has successfully added the new Field by calling the Update method of the Fields collection.

For all other objects, the Type property is read-only.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

RecordType Property | Type Property (ADO Stream)

Applies To: Field Object | Parameter Object | Property Object


Type Property (ADO Stream)

Indicates the type of data contained in the Stream (binary or text).

Settings and Return Values

Sets or returns a StreamTypeEnum value that specifies the type of data contained in the Stream object. The default value is adTypeText. However, if binary data is initially written to a new, empty Stream, the Type will be changed to adTypeBinary.

Remarks

The Type property is read/write only when the current position is at the beginning of the Stream (Position is 0), and read-only at any other position.

The Type property determines which methods should be used for reading and writing the Stream. For text Streams, use ReadText and WriteText. For binary Streams, use Read and Write.

See Also

Example

RecordType Property | Type Property

Applies To: Stream Object


UnderlyingValue Property (ADO)

ADO API Reference

UnderlyingValue Property

Indicates a Field object's current value in the database.

Return Value

Returns a Variant value that indicates the value of the Field.

Remarks

Use the UnderlyingValue property to return the current field value from the database. The field value in the UnderlyingValue property is the value that is visible to your transaction and may be the result of a recent update by another transaction. This may differ from the OriginalValue property, which reflects the value that was originally returned to the Recordset.

This is similar to using the Resync method, but the UnderlyingValue property returns only the value for a specific field from the current record. This is the same value that the Resync method uses to replace the Value property.

When you use this property with the OriginalValue property, you can resolve conflicts that arise from batch updates.

Record

For Record objects, this property will be empty for fields added before Update is called.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

OriginalValue Property | Resync Method

Applies To: Field Object


Value Property (ADO)

ADO API Reference

Value Property

Indicates the value assigned to a Field, Parameter, or Property object.

Settings and Return Values

Sets or returns a Variant value that indicates the value of the object. Default value depends on the Type property.

Remarks

Use the Value property to set or return data from Field objects, to set or return parameter values with Parameter objects, or to set or return property settings with Property objects. Whether the Value property is read/write or read-only depends upon numerous factors—see the respective object topics for more information.

ADO allows setting and returning long binary data with the Value property.

Notes For Parameter objects, ADO reads the Value property only once from the provider. If a command contains a Parameter whose Value property is empty, and you create a Recordset from the command, ensure that you first close the Recordset before retrieving the Value property. Otherwise, for some providers, the Value property may be empty, and won't contain the correct value.

For new Field objects that have been appended to the Fields collection of a Record object, the Value property must be set before any other Field properties can be specified. First, a specific value for the Value property must have been assigned and Update on the Fields collection called. Then, other properties such as Type or Attributes can be accessed.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Field Object | Parameter Object | Property Object


Version Property (ADO)

ADO API Reference

Version Property

Indicates the ADO version number.

Return Value

Returns a String value that indicates the version.

Remarks

Use the Version property to return the version number of the ADO implementation.

The version of the provider will be available as a dynamic property in the Properties collection.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Connection Object


ADO Dynamic Properties

Dynamic properties can be added to the Properties collections of the Connection, Command, or Recordset objects. The source for these properties is either a data provider, such as the OLE DB Provider for SQL Server, or a service provider, such as the Microsoft Cursor Service for OLE DB. Refer to the appropriate data provider or service provider documentation for more information about a specific dynamic property.

The ADO Dynamic Property Index provides a cross-reference between the ADO and OLE DB names for each standard OLE DB provider dynamic property.

The following dynamic properties are of special interest, and are also documented in the sources mentioned above. Special functionality with ADO is documented in the ADO help topics listed below.

Optimize | Specifies whether an index should be created on this field.
Prompt | Specifies whether the OLE DB provider should prompt the user for initialization information.
Reshape Name | Specifies a name for the Recordset object.
Resync Command | Specifies a user-supplied command string that the Resync method issues to refresh the data in the table named in the Unique Table dynamic property.
Unique Table, Unique Schema, Unique Catalog | Unique Table — specifies the name of the base table upon which updates, insertions, and deletions are allowed. Unique Schema — specifies the schema, or name of the owner of the table. Unique Catalog — specifies the catalog, or name of the database containing the table.
Update Resync | Specifies whether the UpdateBatch method is followed by an implicit Resync method operation, and if so, the scope of that operation.

See Also

ADO API Reference | ADO Collections | ADO Enumerated Constants | ADO Errors | ADO Events | ADO Methods | ADO Object Model | ADO Objects | ADO Properties


ADO Dynamic Property Index

Data providers, service providers, and service components can add dynamic properties to the Properties collections of the unopened Connection and Recordset objects. A given provider may also insert additional properties when these objects are opened. Some of these properties are listed in the ADO Dynamic Properties section. More are listed under the specific providers in the Appendix A: Providers section.

The table below is a cross-index of the ADO and OLE DB names for each standard OLE DB provider dynamic property. Your providers may add more properties than listed here. For the specific information about provider-specific dynamic properties, see your provider documentation.

The OLE DB Programmer's Reference refers to an ADO property name by the term, "Description." You can find more information about these standard properties in the OLE DB Programmer's Reference. Search for the OLE DB property name in the Index or see the following topics:

  • Appendix C: OLE DB Properties
  • Supported Properties of the Cursor Service
  • Supported Properties of the Persistence Provider
  • Supported OLE DB Properties of the Remoting Provider

Remarks

Note numbers used in the cross-index:

(1) This property is a Boolean flag indicating whether the named interface should be used. The equivalent OLE DB property name is listed if it exists.

(2) The "Bookmarkable" ADO property is generated internally for backwards compatibility, and is mapped to the OLE DB property, DBPROP_IROWSETLOCATE. This is the same property that corresponds to the ADO property, IRowsetLocate.

(3) The ADO property name, "Hidden Columns", is named differently than the OLE DB property name Description, "Hidden Columns Count."

(4) Does not apply.

Connection Dynamic Properties

ADO Property Name | OLE DB Property Name
Active Sessions | DBPROP_ACTIVESESSIONS
Asynchable Abort | DBPROP_ASYNCTXNABORT
Asynchable Commit | DBPROP_ASYNCTNXCOMMIT
Autocommit Isolation Levels | DBPROP_SESS_AUTOCOMMITISOLEVELS
Catalog Location | DBPROP_CATALOGLOCATION
Catalog Term | DBPROP_CATALOGTERM
Column Definition | DBPROP_COLUMNDEFINITION
Connect Timeout | DBPROP_INIT_TIMEOUT
Current Catalog | DBPROP_CURRENTCATALOG
Data Source | DBPROP_INIT_DATASOURCE
Data Source Name | DBPROP_DATASOURCENAME
Data Source Object Threading Model | DBPROP_DSOTHREADMODEL
DBMS Name | DBPROP_DBMSNAME
DBMS Version | DBPROP_DBMSVER
Extended Properties | DBPROP_INIT_PROVIDERSTRING
GROUP BY Support | DBPROP_GROUPBY
Heterogeneous Table Support | DBPROP_HETEROGENEOUSTABLES
Identifier Case Sensitivity | DBPROP_IDENTIFIERCASE
Initial Catalog | DBPROP_INIT_CATALOG
Isolation Levels | DBPROP_SUPPORTEDTXNISOLEVELS
Isolation Retention | DBPROP_SUPPORTEDTXNISORETAIN
Locale Identifier | DBPROP_INIT_LCID
Location | DBPROP_INIT_LOCATION
Maximum Index Size | DBPROP_MAXINDEXSIZE
Maximum Row Size | DBPROP_MAXROWSIZE
Maximum Row Size Includes BLOB | DBPROP_MAXROWSIZEINCLUDESBLOB
Maximum Tables in SELECT | DBPROP_MAXTABLESINSELECT
Mode | DBPROP_INIT_MODE
Multiple Parameter Sets | DBPROP_MULTIPLEPARAMSETS
Multiple Results | DBPROP_MULTIPLERESULTS
Multiple Storage Objects | DBPROP_MULTIPLESTORAGEOBJECTS
Multi-Table Update | DBPROP_MULTITABLEUPDATE
NULL Collation Order | DBPROP_NULLCOLLATION
NULL Concatenation Behavior | DBPROP_CONCATNULLBEHAVIOR
OLE DB Services | DBPROP_INIT_OLEDBSERVICES
OLE DB Version | DBPROP_PROVIDEROLEDBVER
OLE Object Support | DBPROP_OLEOBJECTS
Open Rowset Support | DBPROP_OPENROWSETSUPPORT
ORDER BY Columns in Select List | DBPROP_ORDERBYCOLUMNSINSELECT
Output Parameter Availability | DBPROP_OUTPUTPARAMETERAVAILABILITY
Pass By Ref Accessors | DBPROP_BYREFACCESSORS
Password | DBPROP_AUTH_PASSWORD
Persist Security Info | DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO
Persistent ID Type | DBPROP_PERSISTENTIDTYPE
Prepare Abort Behavior | DBPROP_PREPAREABORTBEHAVIOR
Prepare Commit Behavior | DBPROP_PREPARECOMMITBEHAVIOR
Procedure Term | DBPROP_PROCEDURETERM
Prompt | DBPROP_INIT_PROMPT
Provider Friendly Name | DBPROP_PROVIDERFRIENDLYNAME
Provider Name | DBPROP_PROVIDERFILENAME
Provider Version | DBPROP_PROVIDERVER
Read-Only Data Source | DBPROP_DATASOURCEREADONLY
Rowset Conversions on Command | DBPROP_ROWSETCONVERSIONSONCOMMAND
Schema Term | DBPROP_SCHEMATERM
Schema Usage | DBPROP_SCHEMAUSAGE
SQL Support | DBPROP_SQLSUPPORT
Structured Storage | DBPROP_STRUCTUREDSTORAGE
Subquery Support | DBPROP_SUBQUERIES
Table Term | DBPROP_TABLETERM
Transaction DDL | DBPROP_SUPPORTEDTXNDDL
User ID | DBPROP_AUTH_USERID
User Name | DBPROP_USERNAME
Window Handle | DBPROP_INIT_HWND

Recordset Dynamic Properties

Note that the Dynamic Properties of the Recordset object go out of scope (become unavailable) when the Recordset is closed.

ADO Property Name | OLE DB Property Name
IAccessor | DBPROP_IACCESSOR (1)
IChapteredRowset | (1)
IColumnsInfo | DBPROP_ICOLUMNSINFO (1)
IColumnsRowset | DBPROP_ICOLUMNSROWSET (1)
IConnectionPointContainer | DBPROP_ICONNECTIONPOINTCONTAINER (1)
IConvertType | (1)
ILockBytes | DBPROP_ILOCKBYTES (1)
IRowset | DBPROP_IROWSET (1)
IDBAsynchStatus | DBPROP_IDBASYNCHSTATUS (1)
IParentRowset | (1)
IRowsetChange | DBPROP_IROWSETCHANGE (1)
IRowsetExactScroll | (1)
IRowsetFind | DBPROP_IROWSETFIND (1)
IRowsetIdentity | DBPROP_IROWSETIDENTITY (1)
IRowsetInfo | DBPROP_IROWSETINFO (1)
IRowsetLocate | DBPROP_IROWSETLOCATE (1)
IRowsetRefresh | DBPROP_IROWSETREFRESH (1)
IRowsetResynch | (1)
IRowsetScroll | DBPROP_IROWSETSCROLL (1)
IRowsetUpdate | DBPROP_IROWSETUPDATE (1)
IRowsetView | DBPROP_IROWSETVIEW (1)
IRowsetIndex | DBPROP_IROWSETINDEX (1)
ISequentialStream | DBPROP_ISEQUENTIALSTREAM (1)
IStorage | DBPROP_ISTORAGE (1)
IStream | DBPROP_ISTREAM (1)
ISupportErrorInfo | DBPROP_ISUPPORTERRORINFO (1)
Access Order | DBPROP_ACCESSORDER
Append-Only Rowset | DBPROP_APPENDONLY
Asynchronous Rowset Processing | DBPROP_ROWSET_ASYNCH
Auto Recalc | DBPROP_ADC_AUTORECALC
Background Fetch Size | DBPROP_ASYNCHFETCHSIZE
Background Thread Priority | DBPROP_ASYNCHTHREADPRIORITY
Batch Size | DBPROP_ADC_BATCHSIZE
Blocking Storage Objects | DBPROP_BLOCKINGSTORAGEOBJECTS
Bookmark Type | DBPROP_BOOKMARKTYPE
Bookmarkable | DBPROP_IROWSETLOCATE (2)
Bookmarks Ordered | DBPROP_ORDEREDBOOKMARKS
Cache Child Rows | DBPROP_ADC_CACHECHILDROWS
Cache Deferred Columns | DBPROP_CACHEDEFERRED
Change Inserted Rows | DBPROP_CHANGEINSERTEDROWS
Column Privileges | DBPROP_COLUMNRESTRICT
Column Set Notification | DBPROP_NOTIFYCOLUMNSET
Column Writable | DBPROP_MAYWRITECOLUMN
Command Time Out | DBPROP_COMMANDTIMEOUT
Cursor Engine Version | DBPROP_ADC_CEVER
Defer Column | DBPROP_DEFERRED
Delay Storage Object Updates | DBPROP_DELAYSTORAGEOBJECTS
Fetch Backwards | DBPROP_CANFETCHBACKWARDS
Filter Operations | DBPROP_FILTERCOMPAREOPS
Find Operations | DBPROP_FINDCOMPAREOPS
Hidden Columns (Count) | DBPROP_HIDDENCOLUMNS (3)
Hold Rows | DBPROP_CANHOLDROWS
Immobile Rows | DBPROP_IMMOBILEROWS
Initial Fetch Size | DBPROP_ASYNCHPREFETCHSIZE
Literal Bookmarks | DBPROP_LITERALBOOKMARKS
Literal Row Identity | DBPROP_LITERALIDENTITY
Maintain Change Status | DBPROP_ADC_MAINTAINCHANGESTATUS
Maximum Open Rows | DBPROP_MAXOPENROWS
Maximum Pending Rows | DBPROP_MAXPENDINGROWS
Maximum Rows | DBPROP_MAXROWS
Memory Usage | DBPROP_MEMORYUSAGE
Notification Granularity | DBPROP_NOTIFICATIONGRANULARITY
Notification Phases | DBPROP_NOTIFICATIONPHASES
Objects Transacted | DBPROP_TRANSACTEDOBJECT
Others' Changes Visible | DBPROP_OTHERUPDATEDELETE
Others' Inserts Visible | DBPROP_OTHERINSERT
Own Changes Visible | DBPROP_OWNUPDATEDELETE
Own Inserts Visible | DBPROP_OWNINSERT
Preserve on Abort | DBPROP_ABORTPRESERVE
Preserve on Commit | DBPROP_COMMITPRESERVE
Private1 | (4)
Quick Restart | DBPROP_QUICKRESTART
Reentrant Events | DBPROP_REENTRANTEVENTS
Remove Deleted Rows | DBPROP_REMOVEDELETED
Report Multiple Changes | DBPROP_REPORTMULTIPLECHANGES
Reshape Name | DBPROP_ADC_RESHAPENAME
Resync Command | DBPROP_ADC_CUSTOMRESYNCH
Return Pending Inserts | DBPROP_RETURNPENDINGINSERTS
Row Delete Notification | DBPROP_NOTIFYROWDELETE
Row First Change Notification | DBPROP_NOTIFYROWFIRSTCHANGE
Row Insert Notification | DBPROP_NOTIFYROWINSERT
Row Privileges | DBPROP_ROWRESTRICT
Row Resynchronization Notification | DBPROP_NOTIFYROWRESYNCH
Row Threading Model | DBPROP_ROWTHREADMODEL
Row Undo Change Notification | DBPROP_NOTIFYROWUNDOCHANGE
Row Undo Delete Notification | DBPROP_NOTIFYROWUNDODELETE
Row Undo Insert Notification | DBPROP_NOTIFYROWUNDOINSERT
Row Update Notification | DBPROP_NOTIFYROWUPDATE
Rowset Fetch Position Change Notification | DBPROP_NOTIFYROWSETFETCHPOSITIONCHANGE
Rowset Release Notification | DBPROP_NOTIFYROWSETRELEASE
Scroll Backwards | DBPROP_CANSCROLLBACKWARDS
Server Cursor | DBPROP_SERVERCURSOR
Skip Deleted Bookmarks | DBPROP_BOOKMARKSKIPPED
Strong Row Identity | DBPROP_STRONGIDENTITY
Unique Catalog | DBPROP_ADC_UNIQUECATALOG
Unique Rows | DBPROP_UNIQUEROWS
Unique Schema | DBPROP_ADC_UNIQUESCHEMA
Unique Table | DBPROP_ADC_UNIQUETABLE
Updatability | DBPROP_UPDATABILITY
Update Criteria | DBPROP_ADC_UPDATECRITERIA
Update Resync | DBPROP_ADC_UPDATERESYNC
Use Bookmarks | DBPROP_BOOKMARKS


Optimize Property--Dynamic (ADO)

ADO API Reference

Optimize Property—Dynamic (ADO)

Specifies whether an index should be created on a field.

Settings and Return Values

Sets or returns a Boolean value that indicates whether an index should be created.

Remarks

An index can improve the performance of operations that find or sort values in a Recordset. The index is internal to ADO—you cannot explicitly access or use it in your application.

To create an index on a field, set the Optimize property to True. To delete the index, set this property to False.

Optimize is a dynamic property appended to the Field object Properties collection when the CursorLocation property is set to adUseClient.

Usage

Dim rs As New Recordset
Dim fld As Field
rs.CursorLocation = adUseClient      'Enable index creation
rs.Fields.Append "Field1", adChar, 35, adFldIsNullable
rs.Open
Set fld = rs.Fields(0)
fld.Properties("Optimize") = True    'Create an index
fld.Properties("Optimize") = False   'Delete an index

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Filter Property | Find Method | Sort Property

Applies To: Field Object


Prompt Property--Dynamic (ADO)

ADO API Reference

Prompt Property—Dynamic (ADO)

Specifies whether the OLE DB provider should prompt the user for initialization information.

Settings and Return Values

Sets and returns a ConnectPromptEnum value.

Remarks

Prompt is a dynamic property, which may be appended to the Connection object's Properties collection by the OLE DB provider. To prompt for initialization information, OLE DB providers will typically display a dialog box to the user.

Dynamic properties of a Connection object are lost when the Connection is closed. The Prompt property must be reset before re-opening the Connection to use a value other than the default.

Note Do not specify that the provider should prompt the user in scenarios in which the user will not be able to respond to the dialog box. For example, the user will not be able to respond if the application is running on a server system instead of on the user's client, or if the application is running on a system with no user logged on. In these cases, the application will wait indefinitely for a response and seem to lock up.

Usage

Set cn = New Connection
cn.Provider = "SQLOLEDB"
cn.Properties("Prompt") = adPromptNever    ' do not prompt the user

See Also

Applies To: Connection Object


Reshape Name Property--Dynamic (ADO)

ADO API Reference

Reshape Name Property—Dynamic (ADO)

Specifies a name for the Recordset object.

Return Values

Returns a String value that is the name of the Recordset.

Remarks

Names persist for the duration of the connection or until the Recordset is closed.

The Reshape Name property is primarily intended for use with the re-shaping feature of the Microsoft Data Shaping Service for OLE DB service provider. Names must be unique in order to participate in re-shaping.

This property is read-only, but can be set indirectly when a Recordset is created. For example, if a clause of a SHAPE command creates a Recordset and gives it an alias name with the "AS" keyword, then the alias is assigned to the Reshape Name property. If no alias is declared then the reshape name property is assigned a unique name generated by the data shaping service. If the alias name is the same as the name of an existing Recordset, neither Recordset be reshaped until one of them is released. The default behavior can be changed by setting the "Unique Reshape Names" (See "Microsoft Data shaping service for OLE DB") property on the ADO connection to TRUE. This give the data shaping service permission to change user assigend names if necessary to insure uniqueness.

Use the Reshape Name property when you want to refer to a Recordset in a SHAPE command, or when you don't know its name because it was generated by Data Shaping Service. In that case, you could generate a SHAPE command by concatenating the command around the string returned by the Reshape Name property.

Reshape Name is a dynamic property appended to the Recordset object's Properties collection when the CursorLocation property is set to adUseClient.

See Also

Microsoft Data Shaping Service for OLE DB (Unique Reshape Names)

Applies To: Recordset Object


Resync Command Property--Dynamic (ADO)

ADO API Reference

Resync Command Property—Dynamic (ADO)

Specifies a user-supplied command string that the Resync method issues to refresh the data in the table named in the Unique Table dynamic property.

Settings and Return Values

Sets or returns a String value which is a command string.

Remarks

The Recordset object is the result of a JOIN operation executed on multiple base tables. The rows affected depend on the AffectRecords parameter of the Resync method. The standard Resync method is executed if the Unique Table and Resync Command properties are not set.

The command string of the Resync Command property is a parameterized command or stored procedure that uniquely identifies the row being refreshed, and returns a single row containing the same number and order of columns as the row to be refreshed. The command string contains a parameter for each primary key column in the Unique Table; otherwise, a run-time error is returned. The parameters are automatically filled in with primary key values from the row to be refreshed.

Here are two examples based on SQL:

1) The Recordset is defined by a command:

SELECT * FROM Customers JOIN Orders ON
   Customers.CustomerID = Orders.CustomerID
   WHERE city = ‘Seattle’
   ORDER BY CustomerID

The Resync Command property is set to:

"SELECT * FROM
   (SELECT * FROM Customers JOIN Orders
   ON Customers.CustomerID = Orders.CustomerID
   city = ‘Seattle’ ORDER BY CustomerID)
WHERE Orders.OrderID = ?"

The Unique Table is Orders and its primary key, OrderID, is parameterized. The sub-select provides a simple way to programmatically ensure that the same number and order of columns are returned as by the original command.

2) The Recordset is defined by a stored procedure:

CREATE PROC Custorders @CustomerID char(5) AS
SELECT * FROM Customers JOIN Orders ON
Customers.CustomerID = Orders.CustomerID
WHERE Customers.CustomerID = @CustomerID

The Resync method should execute the following stored procedure:

CREATE PROC CustordersResync @ordid int AS
SELECT * FROM Customers JOIN Orders ON
Customers.CustomerID = Orders.CustomerID
WHERE Orders.ordid  = @ordid

The Resync Command property is set to:

"{call CustordersResync (?)}"

Once again, the Unique Table is Orders and its primary key, OrderID, is parameterized.

Resync Command is a dynamic property appended to the Recordset object Properties collection when the CursorLocation property is set to adUseClient.

See Also

Applies To: Recordset Object


Unique Table, Unique Schema, Unique Catalog Properties--Dynamic (ADO)

ADO API Reference

Unique Table, Unique Schema, Unique Catalog Properties—Dynamic (ADO)

Enables you to closely control modifications to a particular base table in a Recordset that was formed by a JOIN operation on multiple base tables.

  • Unique Table specifies the name of the base table upon which updates, insertions, and deletions are allowed.
  • Unique Schema specifies the schema, or name of the owner of the table.
  • Unique Catalog specifies the catalog, or name of the database containing the table.

Settings and Return Values

Sets or returns a String value that is the name of a table, schema, or catalog.

Remarks

The desired base table is uniquely identified by its catalog, schema, and table names. When the Unique Table property is set, the values of the Unique Schema or Unique Catalog properties are used to find the base table. It is intended, but not required, that either or both the Unique Schema and Unique Catalog properties be set before the Unique Table property is set.

The primary key of the Unique Table is treated as the primary key of the entire Recordset. This is the key that is used for any method requiring a primary key.

While Unique Table is set, the Delete method affects only the named table. The AddNew, Resync, Update, and UpdateBatch methods affect any appropriate underlying base tables of the Recordset.

Unique Table must be specified before doing any custom resynchronizations. If Unique Table has not been specified, the Resync Command property will have no effect.

A run-time error results if a unique base table cannot be found.

These dynamic properties are all appended to the Recordset object Properties collection when the CursorLocation property is set to adUseClient.

See Also

Applies To: Recordset Object


Update Resync Property--Dynamic (ADO)

ADO API Reference

Update Resync Property—Dynamic (ADO)

Specifies whether the UpdateBatch method is followed by an implicit Resync method operation, and if so, the scope of that operation.

Settings and Return Values

Sets or returns one or more of the ADCPROP_UPDATERESYNC_ENUM values.

Remarks

The values of ADCPROP_UPDATERESYNC_ENUM may be combined, except for adResyncAll which already represents the combination of the rest of the values.

The constant adResyncConflicts stores the resync values as underlying values, but does not override pending changes.

Update Resync is a dynamic property appended to the Recordset object Properties collection when the CursorLocation property is set to adUseClient.

See Also

Applies To: Recordset Object


ADO Methods

AddNew | Creates a new record for an updatable Recordset object.
Append | Appends an object to a collection. If the collection is Fields, a new Field object may be created before it is appended to the collection.
AppendChunk | Appends data to a large text or binary data Field, or to a Parameter object.
BeginTrans, CommitTrans, and RollbackTrans | Manages transaction processing within a Connection object as follows: BeginTrans — Begins a new transaction. CommitTrans — Saves any changes and ends the current transaction. It may also start a new transaction. RollbackTrans — Cancels any changes and ends the current transaction. It may also start a new transaction.
Cancel | Cancels execution of a pending, asynchronous method call.
CancelBatch | Cancels a pending batch update.
CancelUpdate | Cancels any changes made to the current or new row of a Recordset object, or the Fields collection of a Record object, before calling the Update method.
Clear | Removes all the Error objects from the Errors collection.
Clone | Creates a duplicate Recordset object from an existing Recordset object. Optionally, specifies that the clone be read-only.
Close | Closes an open object and any dependent objects.
CompareBookmarks | Compares two bookmarks and returns an indication of their relative values.
CopyRecord | Copies a file or directory, and its contents, to another location.
CopyTo | Copies the specified number of characters or bytes (depending on Type) in the Stream to another Stream object.
CreateParameter | Creates a new Parameter object with the specified properties.
Delete (ADO Parameters Collection) | Deletes an object from the Parameters collection.
Delete (ADO Fields Collection) | Deletes an object from the Fields collection.
Delete (ADO Recordset) | Deletes the current record or a group of records.
DeleteRecord | Deletes a file or directory, and all its subdirectories.
Execute (ADO Command) | Executes the query, SQL statement, or stored procedure specified in the CommandText property.
Execute (ADO Connection) | Executes the specified query, SQL statement, stored procedure, or provider-specific text.
Find | Searches a Recordset for the row that satisfies the specified criteria.
Flush | Forces the contents of the Stream remaining in the ADO buffer to the underlying object with which the Stream is associated.
GetChildren | Returns a Recordset whose rows represent the files and subdirectories in the directory represented by this Record.
GetChunk | Returns all, or a portion of, the contents of a large text or binary data Field object.
GetRows | Retrieves multiple records of a Recordset object into an array.
GetString | Returns the Recordset as a string.
LoadFromFile | Loads the contents of an existing file into a Stream.
Move | Moves the position of the current record in a Recordset object.
MoveFirst, MoveLast, MoveNext, and MovePrevious | Moves to the first, last, next, or previous record in a specified Recordset object and makes that record the current record.
MoveRecord | Moves a file, or a directory and its contents, to another location.
NextRecordset | Clears the current Recordset object and returns the next Recordset by advancing through a series of commands.
Open (ADO Connection) | Opens a connection to a data source.
Open (ADO Record) | Opens an existing Record object, or creates a new file or directory.
Open (ADO Recordset) | Opens a cursor.
Open (ADO Stream) | Opens a Stream object to manipulate streams of binary or text data.
OpenSchema | Obtains database schema information from the provider.
Read | Reads a specified number of bytes from a Stream object.
ReadText | Reads a specified number of characters from a text Stream object.
Refresh | Updates the objects in a collection to reflect objects available from, and specific to, the provider.
Requery | Updates the data in a Recordset object by re-executing the query on which the object is based.
Resync | Refreshes the data in the current Recordset object, or Fields collection of a Record object, from the underlying database.
Save | Saves the Recordset in a file or Stream object.
SaveToFile | Saves the binary contents of a Stream to a file.
Seek | Searches the index of a Recordset to quickly locate the row that matches the specified values, and changes the current row position to that row.
SetEOS | Sets the position that is the end of the stream.
SkipLine | Skips one entire line when reading a text stream.
Supports | Determines whether a specified Recordset object supports a particular type of functionality.
Update | Saves any changes you make to the current row of a Recordset object, or the Fields collection of a Record object.
UpdateBatch | Writes all pending batch updates to disk.
Write | Writes binary data to a Stream object.
WriteText | Writes a specified text string to a Stream object.

See Also

ADO API Reference | ADO Collections | ADO Dynamic Properties | ADO Enumerated Constants | ADO Errors | ADO Events | ADO Object Model | ADO Objects | ADO Properties


AddNew Method (ADO)

ADO API Reference

AddNew Method

Creates a new record for an updatable Recordset object.

Syntax

recordset.AddNew FieldList, Values

Parameters

recordset
A Recordset object.
FieldList
Optional. A single name, or an array of names or ordinal positions of the fields in the new record.
Values
Optional. A single value, or an array of values for the fields in the new record. If Fieldlist is an array, Values must also be an array with the same number of members; otherwise, an error occurs. The order of field names must match the order of field values in each array.

Remarks

Use the AddNew method to create and initialize a new record. Use the Supports method with adAddNew (a CursorOptionEnum value) to verify whether you can add records to the current Recordset object.

After you call the AddNew method, the new record becomes the current record and remains current after you call the Update method. If the Recordset object does not support bookmarks, you may not be able to access the new record once you move to another record. Depending on your cursor type, you may need to call the Requery method to make the new record accessible.

If you call AddNew while editing the current record or while adding a new record, ADO calls the Update method to save any changes and then creates the new record.

The behavior of the AddNew method depends on the updating mode of the Recordset object and whether you pass the Fieldlist and Values arguments.

In immediate update mode (in which the provider writes changes to the underlying data source once you call the Update method), calling the AddNew method without arguments sets the EditMode property to adEditAdd (an EditModeEnum value). The provider caches any field value changes locally. Calling the Update method posts the new record to the database and resets the EditMode property to adEditNone (an EditModeEnum value). If you pass the Fieldlist and Values arguments, ADO immediately posts the new record to the database (no Update call is necessary); the EditMode property value does not change (adEditNone).

In batch update mode (in which the provider caches multiple changes and writes them to the underlying data source only when you call the UpdateBatch method), calling the AddNew method without arguments sets the EditMode property to adEditAdd. The provider caches any field value changes locally. Calling the Update method adds the new record to the current Recordset and resets the EditMode property to adEditNone, but the provider does not post the changes to the underlying database until you call the UpdateBatch method. If you pass the Fieldlist and Values arguments, ADO sends the new record to the provider for storage in a cache; you need to call the UpdateBatch method to post the new record to the underlying database.

See Also

Visual Basic Example | VBScript Example | Visual C++ Example | Visual J++ Example

SafeArray | CancelUpdate Method | EditMode Property | Requery Method | Supports Method | Update Method | UpdateBatch Method

Applies To: Recordset Object


Append Method (ADO)

ADO API Reference

Append Method

Appends an object to a collection. If the collection is Fields, a new Field object may be created before it is appended to the collection.

Syntax

collection.Append object
fields.Append Name, Type, DefinedSize, Attrib, FieldValue

Parameters

collection
A collection object.
fields
A Fields collection.
object
An object variable that represents the object to be appended.
Name
A String value that contains the name of the new Field object, and must not be the same name as any other object in fields.
Type
A DataTypeEnum value, whose default value is adEmpty, that specifies the data type of the new field. The following data types are not supported by ADO, and should not be used when appending new fields to a Recordset: adIDispatch, adIUnknown, adVariant.
DefinedSize
Optional. A Long value that represents the defined size, in characters or bytes, of the new field. The default value for this parameter is derived from Type. Fields with a DefinedSize greater than 255 bytes, and treated as variable length columns. (The default DefinedSize is unspecified.)
Attrib
Optional. A FieldAttributeEnum value, whose default value is adFldDefault, that specifies attributes for the new field. If this value is not specified, the field will contain attributes derived from Type.
FieldValue
Optional. A Variant that represents the value for the new field. If not specified, then the field is appended with a null value.

Remarks

Parameters Collection

You must set the Type property of a Parameter object before appending it to the Parameters collection. If you select a variable-length data type, you must also set the Size property to a value greater than zero.

Describing parameters yourself minimizes calls to the provider and consequently improves performance when using stored procedures or parameterized queries. However, you must know the properties of the parameters associated with the stored procedure or parameterized query that you want to call.

Use the CreateParameter method to create Parameter objects with the appropriate property settings and use the Append method to add them to the Parameters collection. This lets you set and return parameter values without having to call the provider for the parameter information. If you are writing to a provider that does not supply parameter information, you must use this method to manually populate the Parameters collection in order to use parameters at all.

Fields Collection

The FieldValue parameter is only valid when adding a Field object to a Record object, not to a Recordset object. With a Record object, you may append fields and provide values at the same time. With a Recordset object, you must create fields while the Recordset is closed, then open the Recordset and assign values to the fields.

Notes For new Field objects that have been appended to the Fields collection of a Record object, the Value property must be set before any other Field properties can be specified. First, a specific value for the Value property must have been assigned and Update on the Fields collection called. Then, other properties such as Type or Attributes can be accessed.

Field objects of the following data types (DataTypeEnum) cannot be appended to the Fields collection and will cause an error to occur: adArray, adChapter, adEmpty, adPropVariant, and adUserDefined. Also, the following data types are not supported by ADO: adIDispatch, adIUnknown, and adIVariant. For these types, no error will occur when appended, but usage can produce unpredictable results including memory leaks.

Recordset

If you do not set the CursorLocation property before calling the Append method, CursorLocation will be set to adUseClient (a CursorLocationEnum value) automatically when the Recordset object's Open method is called.

A run-time error will occur if the Append method is called on the Fields collection of an open Recordset, or on a Recordset where the ActiveConnection property has been set. You can only append fields to a Recordset that is not open and has not yet been connected to a data source. This is typically the case when a Recordset object is fabricated with the CreateRecordset method or assigned to an object variable.

Record

A run-time error will not occur if the Append method is called on the Fields collection of an open Record. The new field will be added to the Record object's Fields collection. If the Record was derived from a Recordset, then the new field will not appear in the Recordset object's Fields collection.

A non-existent field can be created and appended to the Fields collection by assigning a value to the field object as if it already existed in the collection. The assignment will trigger the automatic creation and appending of the Field object, then the assignment will be completed.

After appending a Field to a Record object's Fields collection, call the Update method of the Fields collection to save the change.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

CreateParameter Method | Delete Method (ADO Fields Collection) | Delete Method (ADO Parameters Collection) | Delete Method (ADO Recordset) | Update Method

Applies To: Fields Collection | Parameters Collection | Record Object


AppendChunk Method (ADO)

ADO API Reference

AppendChunk Method

Appends data to a large text or binary data Field, or to a Parameter object.

Syntax

object.AppendChunk Data

Parameters

object
A Field or Parameter object.
Data
A Variant that contains the data to append to the object.

Remarks

Use the AppendChunk method on a Field or Parameter object to fill it with long binary or character data. In situations where system memory is limited, you can use the AppendChunk method to manipulate long values in portions rather than in their entirety.

Field

If the adFldLong bit in the Attributes property of a Field object is set to true, you can use the AppendChunk method for that field.

The first AppendChunk call on a Field object writes data to the field, overwriting any existing data. Subsequent AppendChunk calls add to existing data. If you are appending data to one field and then you set or read the value of another field in the current record, ADO assumes that you are finished appending data to the first field. If you call the AppendChunk method on the first field again, ADO interprets the call as a new AppendChunk operation and overwrites the existing data. Accessing fields in other Recordset objects that are not clones of the first Recordset object will not disrupt AppendChunk operations.

If there is no current record when you call AppendChunk on a Field object, an error occurs.

Note The AppendChunk method does not operate on Field objects of a Record object. It does not perform any operation and will produce a run-time error.

Parameter

If the adFldLong bit in the Attributes property of a Parameter object is set to true, you can use the AppendChunk method for that parameter.

The first AppendChunk call on a Parameter object writes data to the parameter, overwriting any existing data. Subsequent AppendChunk calls on a Parameter object add to existing parameter data. An AppendChunk call that passes a null value discards all of the parameter data.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Attributes Property | GetChunk Method

Applies To: Field Object | Parameter Object


BeginTrans, CommitTrans, and RollbackTrans Methods (ADO)

ADO API Reference

BeginTrans, CommitTrans, and RollbackTrans Methods

These transaction methods manage transaction processing within a Connection object as follows:

  • BeginTrans — Begins a new transaction.
  • CommitTrans — Saves any changes and ends the current transaction. It may also start a new transaction.
  • RollbackTrans — Cancels any changes made during the current transaction and ends the transaction. It may also start a new transaction.

Syntax

level = object.BeginTrans()
object.BeginTrans
object.CommitTrans
object.RollbackTrans

Return Value

BeginTrans can be called as a function that returns a Long variable indicating the nesting level of the transaction.

Parameters

object
A Connection object.

Connection

Use these methods with a Connection object when you want to save or cancel a series of changes made to the source data as a single unit. For example, to transfer money between accounts, you subtract an amount from one and add the same amount to the other. If either update fails, the accounts no longer balance. Making these changes within an open transaction ensures that either all or none of the changes go through.

Note Not all providers support transactions. Verify that the provider-defined property "Transaction DDL" appears in the Connection object's Properties collection, indicating that the provider supports transactions. If the provider does not support transactions, calling one of these methods will return an error.

After you call the BeginTrans method, the provider will no longer instantaneously commit changes you make until you call CommitTrans or RollbackTrans to end the transaction.

For providers that support nested transactions, calling the BeginTrans method within an open transaction starts a new, nested transaction. The return value indicates the level of nesting: a return value of "1" indicates you have opened a top-level transaction (that is, the transaction is not nested within another transaction), "2" indicates that you have opened a second-level transaction (a transaction nested within a top-level transaction), and so forth. Calling CommitTrans or RollbackTrans affects only the most recently opened transaction; you must close or roll back the current transaction before you can resolve any higher-level transactions.

Calling the CommitTrans method saves changes made within an open transaction on the connection and ends the transaction. Calling the RollbackTrans method reverses any changes made within an open transaction and ends the transaction. Calling either method when there is no open transaction generates an error.

Depending on the Connection object's Attributes property, calling either the CommitTrans or RollbackTrans methods may automatically start a new transaction. If the Attributes property is set to adXactCommitRetaining, the provider automatically starts a new transaction after a CommitTrans call. If the Attributes property is set to adXactAbortRetaining, the provider automatically starts a new transaction after a RollbackTrans call.

Remote Data Service

The BeginTrans, CommitTrans, and RollbackTrans methods are not available on a client-side Connection object.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Attributes Property

Applies To: Connection Object


Cancel Method (ADO)

ADO API Reference

Cancel Method

Cancels execution of a pending, asynchronous method call.

Syntax

object.Cancel

Remarks

Use the Cancel method to terminate execution of an asynchronous method call (that is, a method invoked with the adAsyncConnect, adAsyncExecute, or adAsyncFetch option).

The following table shows what task is terminated when you use the Cancel method on a particular type of object.

If object is a | The last asynchronous call to this method is terminated
Command | Execute
Connection | Execute or Open
Record | CopyRecord, DeleteRecord, MoveRecord, or Open
Recordset | Open
Stream | Open

See Also

Visual Basic Example | VBScript Example | Visual C++ Example | Visual J++ Example

Cancel Method (RDS) | CancelBatch Method | CancelUpdate Method | CancelUpdate Method (RDS) | Execute Method (ADO Command) | Execute Method (ADO Connection) | Open Method (ADO Connection) | Open Method (ADO Recordset)

Applies To: Command Object | Connection Object | Record Object | Recordset Object | Stream Object


CancelBatch Method (ADO)

ADO API Reference

CancelBatch Method

Cancels a pending batch update.

Syntax

recordset.CancelBatch AffectRecords

Parameters

AffectRecords
Optional. An AffectEnum value that indicates how many records the CancelBatch method will affect.

Remarks

Use the CancelBatch method to cancel any pending updates in a Recordset in batch update mode. If the Recordset is in immediate update mode, calling CancelBatch without adAffectCurrent generates an error.

If you are editing the current record or are adding a new record when you call CancelBatch, ADO first calls the CancelUpdate method to cancel any cached changes. After that, all pending changes in the Recordset are canceled.

It's possible that the current record will be indeterminable after a CancelBatch call, especially if you were in the process of adding a new record. For this reason, it is prudent to set the current record position to a known location in the Recordset after the CancelBatch call. For example, call the MoveFirst method.

If the attempt to cancel the pending updates fails because of a conflict with the underlying data (for example, a record has been deleted by another user), the provider returns warnings to the Errors collection but does not halt program execution. A run-time error occurs only if there are conflicts on all the requested records. Use the Filter property (adFilterAffectedRecords) and the Status property to locate records with conflicts.

See Also

Visual Basic Example | Visual C++ Example

Cancel Method | Cancel Method (RDS) | CancelUpdate Method | CancelUpdate Method (RDS) | Clear Method | LockType Property | UpdateBatch Method

Applies To: Recordset Object


CancelUpdate Method (ADO)

ADO API Reference

CancelUpdate Method

Cancels any changes made to the current or new row of a Recordset object, or the Fields collection of a Record object, before calling the Update method.

Syntax

recordset.CancelUpdate
record.Fields.CancelUpdate

Remarks

Recordset

Use the CancelUpdate method to cancel any changes made to the current row or to discard a newly added row. You cannot cancel changes to the current row or a new row after you call the Update method, unless the changes are either part of a transaction that you can roll back with the RollbackTrans method, or part of a batch update. In the case of a batch update, you can cancel the Update with the CancelUpdate or CancelBatch method.

If you are adding a new row when you call the CancelUpdate method, the current row becomes the row that was current before the AddNew call.

If you are in edit mode and want to move off the current record (for example, with Move, NextRecordset, or Close), you can use CancelUpdate to cancel any pending changes. You may need to do this if the update cannot successfully be posted to the data source (for example, an attempted delete that fails due to referential integrity violations will leave the Recordset in edit mode after a call to Delete).

Record

The CancelUpdate method cancels any pending insertions or deletions of Field objects, and cancels pending updates of existing fields and restores them to their original values. The Status property of all fields in the Fields collection is set to adFieldOK.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

AddNew Method | Cancel Method | Cancel Method (RDS) | CancelBatch Method | CancelUpdate Method (RDS) | EditMode Property | Update Method

Applies To: Fields Collection | Recordset Object


Clear Method (ADO)

ADO API Reference

Clear Method

Removes all the Error objects from the Errors collection.

Syntax

Errors.Clear

Remarks

Use the Clear method on the Errors collection to remove all existing Error objects from the collection. When an error occurs, ADO automatically clears the Errors collection and fills it with Error objects based on the new error.

Some properties and methods return warnings that appear as Error objects in the Errors collection but do not halt a program's execution. Before you call the Resync, UpdateBatch, or CancelBatch methods on a Recordset object; the Open method on a Connection object; or set the Filter property on a Recordset object, call the Clear method on the Errors collection. That way, you can read the Count property of the Errors collection to test for returned warnings.

See Also

Visual Basic Example | VBScript Example | Visual C++ Example | Visual J++ Example

CancelBatch Method | Delete Method (ADO Fields Collection) | Delete Method (ADO Parameters Collection) | Delete Method (ADO Recordset) | Filter Property | Resync Method | UpdateBatch Method

Applies To: Errors Collection


Clone Method (ADO)

ADO API Reference

Clone Method

Creates a duplicate Recordset object from an existing Recordset object. Optionally, specifies that the clone be read-only.

Syntax

Set rstDuplicate = rstOriginal.Clone (LockType)

Return Value

Returns a Recordset object reference.

Parameters

rstDuplicate
An object variable that identifies the duplicate Recordset object to be created.
rstOriginal
An object variable that identifies the Recordset object to be duplicated.
LockType
Optional. A LockTypeEnum value that specifies either the lock type of the original Recordset, or a read-only Recordset. Valid values are adLockUnspecified or adLockReadOnly.

Remarks

Use the Clone method to create multiple, duplicate Recordset objects, particularly if you want to maintain more than one current record in a given set of records. Using the Clone method is more efficient than creating and opening a new Recordset object with the same definition as the original.

The Filter property of the original Recordset, if any, will not be applied to the clone. Set the Filter property of the new Recordset in order to filter the results. The simplest way to copy any existing Filter value is to assign it directly, like this: rsNew.Filter = rsOriginal.Filter

The current record of a newly created clone is set to the first record.

Changes you make to one Recordset object are visible in all of its clones regardless of cursor type. However, after you execute Requery on the original Recordset, the clones will no longer be synchronized to the original.

Closing the original Recordset does not close its copies, nor does closing a copy close the original or any of the other copies.

You can only clone a Recordset object that supports bookmarks. Bookmark values are interchangeable; that is, a bookmark reference from one Recordset object refers to the same record in any of its clones.

Recordset events that are triggered will also fire in all Recordset clones. However, because the current record can differ between cloned Recordsets, the events may not be valid for the clone. For example, if you change a value of a field, a WillChangeField event will occur in the changed Recordset and in all clones. The Fields parameter of WillChangeField for the changed Recordset will accurately return the changed fields, but the Fields parameter of a cloned Recordset may not. The Fields parameter of the WillChangeField event of a cloned Recordset (where the change was not made) will simply refer to the fields of the current record of the clone, which may be a different record than the current record of the original Recordset where the change occurred.

See Also

Visual Basic Example | VBScript Example | Visual C++ Example | Visual J++ Example

Applies To: Recordset Object


Close Method (ADO)

ADO API Reference

Close Method

Closes an open object and any dependent objects.

Syntax

object.Close

Remarks

Use the Close method to close a Connection, a Record, a Recordset, or a Stream object to free any associated system resources. Closing an object does not remove it from memory; you can change its property settings and open it again later. To completely eliminate an object from memory, set the object variable to Nothing (in Visual Basic) after closing the object.

Connection

Using the Close method to close a Connection object also closes any active Recordset objects associated with the connection. A Command object associated with the Connection object you are closing will persist, but it will no longer be associated with a Connection object; that is, its ActiveConnection property will be set to Nothing. Also, the Command object's Parameters collection will be cleared of any provider-defined parameters.

You can later call the Open method to re-establish the connection to the same, or another, data source. While the Connection object is closed, calling any methods that require an open connection to the data source generates an error.

Closing a Connection object while there are open Recordset objects on the connection rolls back any pending changes in all of the Recordset objects. Explicitly closing a Connection object (calling the Close method) while a transaction is in progress generates an error. If a Connection object falls out of scope while a transaction is in progress, ADO automatically rolls back the transaction.

Recordset, Record, Stream

Using the Close method to close a Recordset, Record, or Stream object releases the associated data and any exclusive access you may have had to the data through this particular object. You can later call the Open method to reopen the object with the same, or modified, attributes.

While a Recordset object is closed, calling any methods that require a live cursor generates an error.

If an edit is in progress while in immediate update mode, calling the Close method generates an error; instead, call the Update or CancelUpdate method first. If you close the Recordset object while in batch update mode, all changes since the last UpdateBatch call are lost.

If you use the Clone method to create copies of an open Recordset object, closing the original or a clone does not affect any of the other copies.

See Also

Visual Basic Example | VBScript Example | Visual C++ Example | Visual J++ Example

Open Method (ADO Connection) | Open Method (ADO Recordset) | Save Method

Applies To: Connection Object | Record Object | Recordset Object | Stream Object


CompareBookmarks Method (ADO)

ADO API Reference

CompareBookmarks Method

Compares two bookmarks and returns an indication of their relative values.

Syntax

result = recordset.CompareBookmarks(Bookmark1, Bookmark2)

Return Value

Returns a CompareEnum value that indicates the relative row position of two records represented by their bookmarks.

Parameters

Bookmark1
The bookmark of the first row.
Bookmark2
The bookmark of the second row.

Remarks

The bookmarks must apply to the same Recordset object, or a Recordset object and its clone. You cannot reliably compare bookmarks from different Recordset objects, even if they were created from the same source or command. Nor can you compare bookmarks for a Recordset object whose underlying provider does not support comparisons.

A bookmark uniquely identifies a row in a Recordset object. Use the current row's Bookmark property to obtain its bookmark.

Because the data type of a bookmark is provider specific, ADO exposes it as a Variant. For example, SQL Server bookmarks are of type DBTYPE_R8 (Double). ADO would expose this type as a Variant with a subtype of Double.

When comparing bookmarks, ADO does not attempt any type of coercion. The values are simply passed to the provider where the compare occurs. If bookmarks passed to the CompareBookmarks method are stored in variables of differing types, it can generate the type mismatch error, "Arguments are of the wrong type, are out of the acceptable range, or are in conflict with each other."

A bookmark that is not valid or incorrectly formed will cause an error.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Bookmark Property

Applies To: Recordset Object


CopyRecord Method (ADO)

ADO API Reference

CopyRecord Method

Copies a entity represented by a Record to another location.

Syntax

Record.CopyRecord (Source, Destination, UserName, Password, Options, Async)

Parameters

Source
Optional. A String value that contains a URL specifying the entity to be copied (for example, a file or directory). If Source is omitted or specifies an empty string, the file or directory represented by the current Record will be copied.
Destination
Optional. A String value that contains a URL specifying the location where Source will be copied.
UserName
Optional. A String value that contains the user ID that, if needed, authorizes access to Destination.
Password
Optional. A String value that contains the password that, if needed, verifies UserName.
Options
Optional. A CopyRecordOptionsEnum value that has a default value of adCopyUnspecified. Specifies the behavior of this method.
Async
Optional. A Boolean value that, when True, specifies that this operation should be asynchronous.

Return Value

A String value that typically returns the value of Destination. However, the exact value returned is provider-dependent.

Remarks

The values of Source and Destination must not be identical; otherwise, a run-time error occurs. At least one of the server, path, or resource names must differ.

All children (for example, subdirectories) of Source are copied recursively, unless adCopyNonRecursive is specified. In a recursive operation, Destination must not be a subdirectory of Source; otherwise, the operation will not complete.

This method fails if Destination identifies an existing entity (for example, a file or directory), unless adCopyOverWrite is specified.

Important Use the adCopyOverWrite option judiciously. For example, specifying this option when copying a file to a directory will delete the directory and replace it with the file.

Note URLs using the http scheme will automatically invoke the Microsoft OLE DB Provider for Internet Publishing. For more information, see Absolute and Relative URLs.

See Also

Applies To: Record Object


CopyTo Method (ADO)

ADO API Reference

CopyTo Method

Copies the specified number of characters or bytes (depending on Type) in the Stream to another Stream object.

Syntax

Stream.CopyTo DestStream, NumChars

Parameters

DestStream
An object variable value that contains a reference to an open Stream object. The current Stream is copied to the destination Stream specified by DestStream. The destination Stream must already be open. If not, a run-time error occurs.

Note The DestStream parameter may not be a proxy of Stream object because this requires access to a private interface on the Stream object that cannot be remoted to the client.

NumChars
Optional. An Integer value that specifies the number of bytes or characters to be copied from the current position in the source Stream to the destination Stream. The default value is –1, which specifies that all characters or bytes are copied from the current position to EOS.

Remarks

This method copies the specified number of characters or bytes, starting from the current position specified by the Position property. If the specified number is more than the available number of bytes until EOS, then only characters or bytes from the current position to EOS are copied. If the value of NumChars is –1, or omitted, all characters or bytes starting from the current position are copied.

If there are existing characters or bytes in the destination stream, all contents beyond the point where the copy ends remain, and are not truncated. Position becomes the byte immediately following the last byte copied. If you want to truncate these bytes, call SetEOS.

CopyTo should be used to copy data to a destination Stream of the same type as the source Stream (their Type property settings are both adTypeText or both adTypeBinary). For text Stream objects, you can change the Charset property setting of the destination Stream to translate from one character set to another. Also, text Stream objects can be successfully copied into binary Stream objects, but binary Stream objects cannot be copied into text Stream objects.

See Also

Applies To: Stream Object


CreateParameter Method (ADO)

ADO API Reference

CreateParameter Method

Creates a new Parameter object with the specified properties.

Syntax

Set parameter = command.CreateParameter (Name, Type, Direction, Size, Value)

Return Value

Returns a Parameter object.

Parameters

Name
Optional. A String value that contains the name of the Parameter object.
Type
Optional. A DataTypeEnum value that specifies the data type of the Parameter object.
Direction
Optional. A ParameterDirectionEnum value that specifies the type of Parameter object.
Size
Optional. A Long value that specifies the maximum length for the parameter value in characters or bytes.
Value
Optional. A Variant that specifies the value for the Parameter object.

Remarks

Use the CreateParameter method to create a new Parameter object with a specified name, type, direction, size, and value. Any values you pass in the arguments are written to the corresponding Parameter properties.

This method does not automatically append the Parameter object to the Parameters collection of a Command object. This lets you set additional properties whose values ADO will validate when you append the Parameter object to the collection.

If you specify a variable-length data type in the Type argument, you must either pass a Size argument or set the Size property of the Parameter object before appending it to the Parameters collection; otherwise, an error occurs.

If you specify a numeric data type (adNumeric or adDecimal) in the Type argument, then you must also set the NumericScale and Precision properties.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Append Method | Parameter Object | Parameters Collection

Applies To: Command Object


Delete Method (ADO Parameters Collection)

Deletes an object from the Parameters collection.

Syntax

Parameters.Delete Index

Parameters

Index
A String value that contains the name of the object you want to delete, or the object’s ordinal position (index) in the collection.

Remarks

Using the Delete method on a collection lets you remove one of the objects in the collection. This method is available only on the Parameters collection of a Command object. You must use the Parameter object's Name property or its collection index when calling the Delete method—an object variable is not a valid argument.

See Also

Delete Method (ADO Fields Collection) | Delete Method (ADO Recordset) | DeleteRecord Method

Applies To: Parameters Collection


Delete Method (ADO Fields Collection)

Deletes an object from the Fields collection.

Syntax

Fields.Delete Field

Parameters

Field
A Variant that designates the Field object to delete. This parameter can be the name of the Field object or the ordinal position of the Field object itself.

Remarks

Calling the Fields.Delete method on an open Recordset causes a run-time error.

See Also

Delete Method (ADO Parameters Collection) | Delete Method (ADO Recordset) | DeleteRecord Method

Applies To: Fields Collection


Delete Method (ADO Recordset)

Deletes the current record or a group of records.

Syntax

recordset.Delete AffectRecords

Parameters

AffectRecords
An AffectEnum value that determines how many records the Delete method will affect. The default value is adAffectCurrent.

Note adAffectAll and adAffectAllChapters are not valid arguments to Delete.

Remarks

Using the Delete method marks the current record or a group of records in a Recordset object for deletion. If the Recordset object doesn't allow record deletion, an error occurs. If you are in immediate update mode, deletions occur in the database immediately. If a record cannot be successfully deleted (due to database integrity violations, for example), the record will remain in edit mode after the call to Update. This means that you must cancel the update with CancelUpdate before moving off the current record (for example, with Close, Move, or NextRecordset).

If you are in batch update mode, the records are marked for deletion from the cache and the actual deletion happens when you call the UpdateBatch method. (Use the Filter property to view the deleted records.)

Retrieving field values from the deleted record generates an error. After deleting the current record, the deleted record remains current until you move to a different record. Once you move away from the deleted record, it is no longer accessible.

If you nest deletions in a transaction, you can recover deleted records with the RollbackTrans method. If you are in batch update mode, you can cancel a pending deletion or group of pending deletions with the CancelBatch method.

If the attempt to delete records fails because of a conflict with the underlying data (for example, a record has already been deleted by another user), the provider returns warnings to the Errors collection but does not halt program execution. A run-time error occurs only if there are conflicts on all the requested records.

If the Unique Table dynamic property is set, and the Recordset is the result of executing a JOIN operation on multiple tables, then the Delete method will only delete rows from the table named in the Unique Table property.

See Also

Visual Basic Example | VBScript Example | Visual C++ Example | Visual J++ Example

Delete Method (ADO Fields Collection) | Delete Method (ADO Parameters Collection) | DeleteRecord Method

Applies To: Recordset Object


DeleteRecord Method (ADO)

ADO API Reference

DeleteRecord Method

Deletes a the entity represented by a Record.

Syntax

Record.DeleteRecord Source, Async

Parameters

Source
Optional. A String value that contains a URL identifying the entity (for example, the file or directory) to be deleted. If Source is omitted or specifies an empty string, the entity represented by the current Record is deleted. If the Record is a collection record (RecordType of adCollectionRecord, such as a directory) all children (for example, subdirectories) will also be deleted.
Async
Optional. A Boolean value that, when True, specifies that the delete operation is asychronous.

Remarks

Operations on the object represented by this Record may fail after this method completes. After calling DeleteRecord, the Record should be closed because the behavior of the Record may become unpredictable depending upon when the provider updates the Record with the data source.

If this Record was obtained from a Recordset, then the results of this operation will not be reflected immediately in the Recordset. Refresh the Recordset by closing and re-opening it, or by executing the Recordset Requery, or Update and Resync methods.

Note URLs using the http scheme will automatically invoke the Microsoft OLE DB Provider for Internet Publishing. For more information, see Absolute and Relative URLs.

See Also

Delete Method (ADO Fields Collection) | Delete Method (ADO Parameters Collection) | Delete Method (ADO Recordset)

Applies To: Record Object


Execute Method (ADO Command)

Executes the query, SQL statement, or stored procedure specified in the CommandText or CommandStream property.

Syntax

For a Recordset-returning Command:

Set recordset = command.Execute( RecordsAffected, Parameters, Options )

For a non–recordset-returning Command:

command.Execute RecordsAffected, Parameters, Options

Return Value

Returns a Recordset object reference, a stream, or Nothing.

Parameters

RecordsAffected
Optional. A Long variable to which the provider returns the number of records that the operation affected. The RecordsAffected parameter applies only for action queries or stored procedures. RecordsAffected does not return the number of records returned by a result-returning query or stored procedure. To obtain this information, use the RecordCount property.
Parameters
Optional. A Variant array of parameter values used in conjunction with the input string or stream specified in CommandText or CommandStream. (Output parameters will not return correct values when passed in this argument.)
Options
Optional. A Long value that indicates how the provider should evaluate the CommandText or the CommandStream property of the Command object. Can a bitmask of one or more CommandTypeEnum or ExecuteOptionEnum values.

Note Use the ExecuteOptionEnum value adExecuteNoRecords to improve performance by minimizing internal processing.

If adExecuteStream was specified, the options adAsyncFetch and adAsynchFetchNonBlocking are ignored.

Do not use the CommandTypeEnum values of adCmdFile or adCmdTableDirect with Execute. These values can only be used as options with the Open and Requery methods of a Recordset.

Remarks

Using the Execute method on a Command object executes the query specified in the CommandText property or CommandStream property of the object.

Results are returned in a Recordset (by default) or as a stream of binary information. To obtain a binary stream, specify adExecuteStream in Options, then supply a stream by setting Command.Properties("Output Stream"). An ADO Stream object can be specified to receive the results, or another stream object such as the IIS Response object can be specified. If no stream was specified before calling Execute with adExecuteStream, an error occurs. The position of the stream on return from Execute is provider specific.

If the command is not intended to return results (for example, an SQL UPDATE query) the provider returns Nothing. Some application languages allow you to ignore this return value if no Recordset is desired.

Execute raises an error if the user specifies a value for CommandStream when the CommandType is adCmdStoredProc, adCmdTable, or adCmdTableDirect.

If the query has parameters, the current values for the Command object's parameters are used unless you override these with parameter values passed with the Execute call. You can override a subset of the parameters by omitting new values for some of the parameters when calling the Execute method. The order in which you specify the parameters is the same order in which the method passes them. For example, if there were four (or more) parameters and you wanted to pass new values for only the first and fourth parameters, you would pass Array(var1,,,var4) as the Parameters argument.

Note Output parameters will not return correct values when passed in the Parameters argument.

An ExecuteComplete event will be issued when this operation concludes.

Note When issuing commands containing URLs, those using the http scheme will automatically invoke the Microsoft OLE DB Provider for Internet Publishing. For more information, see Absolute and Relative URLs.

See Also

Visual Basic Example | VBScript Example | Visual C++ Example | Visual J++ Example

CommandStream Property | CommandText Property | CommandTypeEnum | Execute Method (ADO Connection) | ExecuteComplete Event

Applies To: Command Object


Execute Method (ADO Connection)

Executes the specified query, SQL statement, stored procedure, or provider-specific text.

Syntax

For a non–row-returning command string:

connection.Execute CommandText, RecordsAffected, Options

For a row-returning command string:

Set recordset = connection.Execute (CommandText, RecordsAffected, Options)

Return Value

Returns a Recordset object reference.

Parameters

CommandText
A String value that contains the SQL statement, table name, stored procedure, a URL, or provider-specific text to execute.
RecordsAffected
Optional. A Long variable to which the provider returns the number of records that the operation affected.
Options
Optional. A Long value that indicates how the provider should evaluate the CommandText argument. Can be a bitmask of one or more CommandTypeEnum or ExecuteOptionEnum values.

Note Use the ExecuteOptionEnum value adExecuteNoRecords to improve performance by minimizing internal processing.

Do not use adExecuteStream with the Execute method of a Connection object.

Do not use the CommandTypeEnum values of adCmdFile or adCmdTableDirect with Execute. These values can only be used as options with the Open and Requery methods of a Recordset.

Remarks

Using the Execute method on a Connection object executes whatever query you pass to the method in the CommandText argument on the specified connection. If the CommandText argument specifies a row-returning query, any results that the execution generates are stored in a new Recordset object. If the command is not a row-returning query, the provider returns a closed Recordset object.

The returned Recordset object is always a read-only, forward-only cursor. If you need a Recordset object with more functionality, first create a Recordset object with the desired property settings, then use the Recordset object's Open method to execute the query and return the desired cursor type.

The contents of the CommandText argument are specific to the provider and can be standard SQL syntax or any special command format that the provider supports.

An ExecuteComplete event will be issued when this operation concludes.

Note URLs using the http scheme will automatically invoke the Microsoft OLE DB Provider for Internet Publishing. For more information, see Absolute and Relative URLs.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Execute Method (ADO Command) | ExecuteComplete Event

Applies To: Connection Object


Find Method (ADO)

ADO API Reference

Find Method

Searches a Recordset for the row that satisfies the specified criteria. Optionally, the direction of the search, starting row, and offset from the starting row may be specified. If the criteria is met, the current row position is set on the found record; otherwise, the position is set to the end (or start) of the Recordset.

Syntax

Find (Criteria, SkipRows, SearchDirection, Start)

Parameters

Criteria
A String value that contains a statement specifying the column name, comparison operator, and value to use in the search.
SkipRows
Optional. A Long value, whose default value is zero, that specifies the row offset from the current row or Start bookmark to begin the search. By default, the search will start on the current row.
SearchDirection
Optional. A SearchDirectionEnum value that specifies whether the search should begin on the current row or the next available row in the direction of the search. An unsuccessful search stops at the end of the Recordset if the value is adSearchForward. An unsuccessful search stops at the start of the Recordset if the value is adSearchBackward.
Start
Optional. A Variant bookmark that functions as the starting position for the search.

Remarks

Only a single-column name may be specified in criteria. This method does not support multi-column searches.

The comparison operator in Criteria may be ">" (greater than), "<" (less than), "=" (equal), ">=" (greater than or equal), "<=" (less than or equal), "<>" (not equal), or "like" (pattern matching).

The value in Criteria may be a string, floating-point number, or date. String values are delimited with single quotes or "#" (number sign) marks (for example, "state = 'WA'" or "state = #WA#"). Date values are delimited with "#" (number sign) marks (for example, "start_date > #7/22/97#").

If the comparison operator is "like", the string value may contain an asterisk (*) to find one or more occurrences of any character or substring. For example, "state like 'M*'" matches Maine and Massachusetts. You can also use leading and trailing asterisks to find a substring contained within the values. For example, "state like '*as*'" matches Alaska, Arkansas, and Massachusetts.

Asterisks can be used only at the end of a criteria string, or together at both the beginning and end of a criteria string, as shown above. You cannot use the asterisk as a leading wildcard ('*str'), or embedded wildcard ('s*r'). This will cause an error.

Note An error will occur if a current row position is not set before calling Find. Any method that sets row position, such as MoveFirst, should be called before calling Find.

See Also

Visual Basic Example

Index Property | Optimize Property—Dynamic (ADO) | Seek Method

Applies To: Recordset Object


Flush Method (ADO)

ADO API Reference

Flush Method

Forces the contents of the Stream remaining in the ADO buffer to the underlying object with which the Stream is associated.

Syntax

Stream.Flush

Remarks

This method may be used to send the contents of the stream buffer to the underlying object (for example, the node or file represented by the URL that is the source of the Stream object). This method should be called when you want to ensure that all changes made to the contents of a Stream have been written. However, with ADO it is not usually necessary to call Flush, as ADO continuously flushes its buffer as much as possible in the background. Changes to the content of a Stream are made automatically, not cached until Flush is called.

Closing a Stream with the Close method flushes the contents of a Stream automatically; there is no need to explicitly call Flush immediately before Close.

See Also

Applies To: Stream Object


GetChildren Method (ADO)

ADO API Reference

GetChildren Method

Returns a Recordset whose rows represent the children of a collection Record.

Syntax

Set recordset = record.GetChildren

Return Value

A Recordset object for which each row represents a child of the current Record object. For example, the children of a Record that represents a directory would be the files and subdirectories contained within the parent directory.

Remarks

The provider determines what columns exist in the returned Recordset. For example, a document source provider always returns a resource Recordset.

See Also

Example | Example

Applies To: Record Object | Recordset Object


GetChunk Method (ADO)

ADO API Reference

GetChunk Method

Returns all, or a portion, of the contents of a large text or binary data Field object.

Syntax

variable = field.GetChunk( Size )

Return Value

Returns a Variant.

Parameters

Size
A Long expression that is equal to the number of bytes or characters that you want to retrieve.

Remarks

Use the GetChunk method on a Field object to retrieve part or all of its long binary or character data. In situations where system memory is limited, you can use the GetChunk method to manipulate long values in portions, rather than in their entirety.

The data that a GetChunk call returns is assigned to variable. If Size is greater than the remaining data, the GetChunk method returns only the remaining data without padding variable with empty spaces. If the field is empty, the GetChunk method returns a null value.

Each subsequent GetChunk call retrieves data starting from where the previous GetChunk call left off. However, if you are retrieving data from one field and then you set or read the value of another field in the current record, ADO assumes you have finished retrieving data from the first field. If you call the GetChunk method on the first field again, ADO interprets the call as a new GetChunk operation and starts reading from the beginning of the data. Accessing fields in other Recordset objects that are not clones of the first Recordset object will not disrupt GetChunk operations.

If the adFldLong bit in the Attributes property of a Field object is set to True, you can use the GetChunk method for that field.

If there is no current record when you use the GetChunk method on a Field object, error 3021 (no current record) occurs.

Note The GetChunk method does not operate on Field objects of a Record object. It does not perform any operation and will produce a run-time error.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

AppendChunk Method | Attributes Property

Applies To: Field Object


GetRows Method (ADO)

ADO API Reference

GetRows Method

Retrieves multiple records of a Recordset object into an array.

Syntax

array = recordset.GetRows( Rows, Start, Fields )

Return Value

Returns a Variant whose value is a two-dimensional array.

Parameters

Rows
Optional. A GetRowsOptionEnum value that indicates the number of records to retrieve. The default is adGetRowsRest.
Start
Optional. A String value or Variant that evaluates to the bookmark for the record from which the GetRows operation should begin. You can also use a BookmarkEnum value.
Fields
Optional. A Variant that represents a single field name or ordinal position, or an array of field names or ordinal position numbers. ADO returns only the data in these fields.

Remarks

Use the GetRows method to copy records from a Recordset into a two-dimensional array. The first subscript identifies the field and the second identifies the record number. The array variable is automatically dimensioned to the correct size when the GetRows method returns the data.

If you do not specify a value for the Rows argument, the GetRows method automatically retrieves all the records in the Recordset object. If you request more records than are available, GetRows returns only the number of available records.

If the Recordset object supports bookmarks, you can specify at which record the GetRows method should begin retrieving data by passing the value of that record's Bookmark property in the Start argument.

If you want to restrict the fields that the GetRows call returns, you can pass either a single field name/number or an array of field names/numbers in the Fields argument.

After you call GetRows, the next unread record becomes the current record, or the EOF property is set to True if there are no more records.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Recordset Object


GetString Method (ADO)

ADO API Reference

GetString Method

Returns the Recordset as a string.

Syntax

Variant = recordset.GetString(StringFormat, NumRows, ColumnDelimiter, RowDelimiter, NullExpr)

Return Value

Returns the Recordset as a string-valued Variant (BSTR).

Parameters

StringFormat
A StringFormatEnum value that specifies how the Recordset should be converted to a string. The RowDelimiter, ColumnDelimiter, and NullExpr parameters are used only with a StringFormat of adClipString.
NumRows
Optional. The number of rows to be converted in the Recordset. If NumRows is not specified, or if it is greater than the total number of rows in the Recordset, then all the rows in the Recordset are converted.
ColumnDelimiter
Optional. A delimiter used between columns, if specified, otherwise the TAB character.
RowDelimiter
Optional. A delimiter used between rows, if specified, otherwise the CARRIAGE RETURN character.
NullExpr
Optional. An expression used in place of a null value, if specified, otherwise the empty string.

Remarks

Row data, but no schema data, is saved to the string. Therefore, a Recordset cannot be reopened using this string.

This method is equivalent to the RDO GetClipString method.

See Also

Visual Basic Example

Applies To: Recordset Object


LoadFromFile Method (ADO)

ADO API Reference

LoadFromFile Method

Loads the contents of an existing file into a Stream.

Syntax

Stream.LoadFromFile FileName

Parameter

FileName
A String value that contains the name of a file to be loaded into the Stream. FileName can contain any valid path and name in UNC format. If the specified file does not exist, a run-time error occurs.

Remarks

This method may be used to load the contents of a local file into a Stream object. This may be used to upload the contents of a local file to a server.

The Stream object must be already open before calling LoadFromFile. This method does not change the binding of the Stream object; it will still be bound to the object specified by the URL or Record with which the Stream was originally opened.

LoadFromFile overwrites the current contents of the Stream object with data read from the file. Any existing bytes in the Stream are overwritten by the contents of the file. Any previously existing and remaining bytes following the EOS created by LoadFromFile, are truncated.

After a call to LoadFromFile, the current position is set to the beginning of the Stream (Position is 0).

See Also

Applies To: Stream Object


Move Method (ADO)

ADO API Reference

Move Method

Moves the position of the current record in a Recordset object.

Syntax

recordset.Move NumRecords, Start

Parameters

NumRecords
A signed Long expression that specifies the number of records that the current record position moves.
Start
Optional. A String value or Variant that evaluates to a bookmark. You can also use a BookmarkEnum value.

Remarks

The Move method is supported on all Recordset objects.

If the NumRecords argument is greater than zero, the current record position moves forward (toward the end of the Recordset). If NumRecords is less than zero, the current record position moves backward (toward the beginning of the Recordset).

If the Move call would move the current record position to a point before the first record, ADO sets the current record to the position before the first record in the recordset (BOF is True). An attempt to move backward when the BOF property is already True generates an error.

If the Move call would move the current record position to a point after the last record, ADO sets the current record to the position after the last record in the recordset (EOF is True). An attempt to move forward when the EOF property is already True generates an error.

Calling the Move method from an empty Recordset object generates an error.

If you pass the Start argument, the move is relative to the record with this bookmark, assuming the Recordset object supports bookmarks. If not specified, the move is relative to the current record.

If you are using the CacheSize property to locally cache records from the provider, passing a NumRecords argument that moves the current record position outside the current group of cached records forces ADO to retrieve a new group of records, starting from the destination record. The CacheSize property determines the size of the newly retrieved group, and the destination record is the first record retrieved.

If the Recordset object is forward only, a user can still pass a NumRecords argument less than zero, provided the destination is within the current set of cached records. If the Move call would move the current record position to a record before the first cached record, an error will occur. Thus, you can use a record cache that supports full scrolling over a provider that supports only forward scrolling. Because cached records are loaded into memory, you should avoid caching more records than is necessary. Even if a forward-only Recordset object supports backward moves in this way, calling the MovePrevious method on any forward-only Recordset object will still generate an error.

Note Support for moving backwards in a forward-only Recordset is not predictable, depending upon your provider. If the current record has been postioned after the last record in the Recordset, Move backwards may not result in the correct current position.

See Also

Visual Basic Example | VBScript Example | Visual C++ Example | Visual J++ Example

MoveFirst, MoveLast, MoveNext, and MovePrevious Methods | MoveFirst, MoveLast, MoveNext, and MovePrevious Methods (RDS) | MoveRecord Method

Applies To: Recordset Object


MoveFirst, MoveLast, MoveNext, and MovePrevious Methods (ADO)

ADO API Reference

MoveFirst, MoveLast, MoveNext, and MovePrevious Methods

Moves to the first, last, next, or previous record in a specified Recordset object and makes that record the current record.

Syntax

recordset.{MoveFirst | MoveLast | MoveNext | MovePrevious}

Remarks

Use the MoveFirst method to move the current record position to the first record in the Recordset.

Use the MoveLast method to move the current record position to the last record in the Recordset. The Recordset object must support bookmarks or backward cursor movement; otherwise, the method call will generate an error.

Use the MoveNext method to move the current record position one record forward (toward the bottom of the Recordset). If the last record is the current record and you call the MoveNext method, ADO sets the current record to the position after the last record in the Recordset (EOF is True). An attempt to move forward when the EOF property is already True generates an error.

In cases where the Recordset has been filtered or sorted and the current record's data is changed, the position may also change. In such cases the MoveNext method works normally, but you should be aware that the position is moved one record forward from the new position, not the old position. For example, changing the data in the current record, such that the record is moved to the end of the sorted Recordset, would mean that calling MoveNext results in ADO setting the current record to the position after the last record in the Recordset (EOF = True).

Use the MovePrevious method to move the current record position one record backward (toward the top of the Recordset). The Recordset object must support bookmarks or backward cursor movement; otherwise, the method call will generate an error. If the first record is the current record and you call the MovePrevious method, ADO sets the current record to the position before the first record in the Recordset (BOF is True). An attempt to move backward when the BOF property is already True generates an error. If the Recordset object does not support either bookmarks or backward cursor movement, the MovePrevious method will generate an error.

If the Recordset is forward only and you want to support both forward and backward scrolling, you can use the CacheSize property to create a record cache that will support backward cursor movement through the Move method. Because cached records are loaded into memory, you should avoid caching more records than is necessary. You can call the MoveFirst method in a forward-only Recordset object; doing so may cause the provider to re-execute the command that generated the Recordset object.

See Also

Visual Basic Example | VBScript Example | Visual C++ Example | Visual J++ Example

Move Method | MoveFirst, MoveLast, MoveNext, and MovePrevious Methods (RDS) | MoveRecord Method

Applies To: Recordset Object


MoveRecord Method (ADO)

ADO API Reference

MoveRecord Method

Moves the entity represent by a Record to another location.

Syntax

Record.MoveRecord (Source, Destination, UserName, Password, Options, Async)

Parameters

Source
Optional. A String value that contains a URL identifying the Record to be moved. If Source is omitted or specifies an empty string, the object represented by this Record is moved. For example, if the Record represents a file, the contents of the file are moved to the location specified by Destination.
Destination
Optional. A String value that contains a URL specifying the location where Source will be moved.
UserName
Optional. A String value that contains the user ID that, if needed, authorizes access to Destination.
Password
Optional. A String that contains the password that, if needed, verifies UserName.
Options
Optional. A MoveRecordOptionsEnum value whose default value is adMoveUnspecified. Specifies the behavior of this method.
Async
Optional. A Boolean value that, when True, specifies this operation should be asynchronous.

Return Value

A String value. Typically, the value of Destination is returned. However, the exact value returned is provider-dependent.

Remarks

The values of Source and Destination must not be identical; otherwise, a run-time error occurs. At least the server, path, and resource names must differ.

For files moved using the Internet Publishing Provider, this method updates all hypertext links in files being moved unless otherwise specified by Options. This method fails if Destination identifies an existing object (for example, a file or directory), unless adMoveOverWrite is specified.

Note Use the adMoveOverWrite option judiciously. For example, specifying this option when moving a file to a directory will delete the directory and replace it with the file.

Certain attributes of the Record object, such as the ParentURL property, will not be updated after this operation completes. Refresh the Record object's properties by closing the Record, then re-opening it with the URL of the location where the file or directory was moved.

If this Record was obtained from a Recordset, the new location of the moved file or directory will not be reflected immediately in the Recordset. Refresh the Recordset by closing and re-opening it.

Note URLs using the http scheme will automatically invoke the Microsoft OLE DB Provider for Internet Publishing. For more information, see Absolute and Relative URLs.

See Also

Move Method | MoveFirst, MoveLast, MoveNext, and MovePrevious Methods | MoveFirst, MoveLast, MoveNext, and MovePrevious Methods (RDS)

Applies To: Record Object


NextRecordset Method (ADO)

ADO API Reference

NextRecordset Method

Clears the current Recordset object and returns the next Recordset by advancing through a series of commands.

Syntax

Set recordset2 = recordset1.NextRecordset( RecordsAffected )

Return Value

Returns a Recordset object. In the syntax model, recordset1 and recordset2 can be the same Recordset object, or you can use separate objects. When using separate Recordset objects, resetting the ActiveConnection property on the original Recordset (recordset1) after NextRecordset has been called will generate an error.

Parameters

RecordsAffected
Optional. A Long variable to which the provider returns the number of records that the current operation affected.

Note This parameter only returns the number of records affected by an operation; it does not return a count of records from a select statement used to generate the Recordset.

Remarks

Use the NextRecordset method to return the results of the next command in a compound command statement or of a stored procedure that returns multiple results. If you open a Recordset object based on a compound command statement (for example, "SELECT * FROM table1;SELECT * FROM table2") using the Execute method on a Command or the Open method on a Recordset, ADO executes only the first command and returns the results to recordset. To access the results of subsequent commands in the statement, call the NextRecordset method.

As long as there are additional results and the Recordset containing the compound statements is not disconnected or marshaled across process boundaries, the NextRecordset method will continue to return Recordset objects. If a row-returning command executes successfully but returns no records, the returned Recordset object will be open but empty. Test for this case by verifying that the BOF and EOF properties are both True. If a non–row-returning command executes successfully, the returned Recordset object will be closed, which you can verify by testing the State property on the Recordset. When there are no more results, recordset will be set to Nothing.

The NextRecordset method is not available on a disconnected Recordset object, where ActiveConnection has been set to Nothing (in Microsoft Visual Basic) or NULL (in other languages).

If an edit is in progress while in immediate update mode, calling the NextRecordset method generates an error; call the Update or CancelUpdate method first.

To pass parameters for more than one command in the compound statement by filling the Parameters collection, or by passing an array with the original Open or Execute call, the parameters must be in the same order in the collection or array as their respective commands in the command series. You must finish reading all the results before reading output parameter values.

Your OLE DB provider determines when each command command in a compound statement is executed. The Microsoft OLE DB Provider for SQL Server, for example, executes all commands in a batch upon receiving the compound statement. The resulting Recordsets are simply returned when you call NextRecordset.

However, other providers may execute the next command in a statement only after NextRecordset is called. For these providers, if you explicitly close the Recordset object before stepping through the entire command statement, ADO never executes the remaining commands.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Applies To: Recordset Object


Open Method (ADO Connection)

Opens a connection to a data source.

Syntax

connection.Open ConnectionString, UserID, Password, Options

Parameters

ConnectionString
Optional. A String value that contains connection information. See the ConnectionString property for details on valid settings.
UserID
Optional. A String value that contains a user name to use when establishing the connection.
Password
Optional. A String value that contains a password to use when establishing the connection.
Options
Optional. A ConnectOptionEnum value that determines whether this method should return after (synchronously) or before (asynchronously) the connection is established.

Remarks

Using the Open method on a Connection object establishes the physical connection to a data source. After this method successfully completes, the connection is live and you can issue commands against it and process the results.

Use the optional ConnectionString argument to specify either a connection string containing a series of argument = value statements separated by semicolons, or a file or directory resource identified with a URL. The ConnectionString property automatically inherits the value used for the ConnectionString argument. Therefore, you can either set the ConnectionString property of the Connection object before opening it, or use the ConnectionString argument to set or override the current connection parameters during the Open method call.

If you pass user and password information both in the ConnectionString argument and in the optional UserID and Password arguments, the UserID and Password arguments will override the values specified in ConnectionString.

When you have concluded your operations over an open Connection, use the Close method to free any associated system resources. Closing an object does not remove it from memory; you can change its property settings and use the Open method to open it again later. To completely eliminate an object from memory, set the object variable to Nothing.

Remote Data Service Usage When used on a client-side Connection object, the Open method doesn't actually establish a connection to the server until a Recordset is opened on the Connection object.

Note URLs using the http scheme will automatically invoke the Microsoft OLE DB Provider for Internet Publishing. For more information, see Absolute and Relative URLs.

See Also

Visual Basic Example | VBScript Example | Visual C++ Example | Visual J++ Example

Open Method (ADO Record) | Open Method (ADO Recordset) | Open Method (ADO Stream) | OpenSchema Method

Applies To: Connection Object


Open Method (ADO Record)

Opens an existing Record object, or creates a new item represented by the Record (such as a file or directory).

Syntax

Open Source, ActiveConnection, Mode, CreateOptions, Options, UserName, Password

Parameters

Source
Optional. A Variant that may represent the URL of the entity to be represented by this Record object, a Command, an open Recordset or another Record object, a string containing a SQL SELECT statement or a table name.
ActiveConnection
Optional. A Variant that represents the connect string or open Connection object.
Mode
Optional. A ConnectModeEnum value, whose default value is adModeUnknown, that specifies the access mode for the resultant Record object.
CreateOptions
Optional. A RecordCreateOptionsEnum value, whose default value is adFailIfNotExists, that specifies whether an existing file or directory should be opened, or a new file or directory should be created. If set to the default value, the access mode is obtained from the Mode property. This parameter is ignored when the Source parameter doesn’t contain a URL.
Options
Optional. A RecordOpenOptionsEnum value, whose default value is adOpenRecordUnspecified, that specifies options for opening the Record. These values may be combined.
UserName
Optional. A String value that contains the user ID that, if needed, authorizes access to Source.
Password
Optional. A String value that contains the password that, if needed, verifies UserName.

Remarks

Source may be:

  • A URL. If the protocol for the URL is http, then the Internet Provider will be invoked by default. If the URL points to a node that contains an executable script (such as an .ASP page), then a Record containing the source rather than the executed contents is opened by default. Use the Options argument to modify this behavior.
  • A Record object. A Record object opened from another Record will clone the original Record object.
  • A Command object. The opened Record object represents the single row returned by executing the Command. If the results contain more than a single row, the contents of the first row are placed in the record and an error may be added to the Errors collection.
  • A SQL SELECT statement. The opened Record object represents the single row returned by executing the contents of the string. If the results contain more than a single row, the contents of the first row are placed in the record and an error may be added to the Errors collection.
  • A table name.

If the Record object represents an entity that cannot be accessed with a URL (for example, a row of a Recordset derived from a database), then the values of both the ParentURL property and the field accessed with the adRecordURL constant are null.

Note URLs using the http scheme will automatically invoke the Microsoft OLE DB Provider for Internet Publishing. For more information, see Absolute and Relative URLs.

See Also

Example | Example

Open Method (ADO Connection) | Open Method (ADO Recordset) | Open Method (ADO Stream) | OpenSchema Method

Applies To: Record Object


Open Method (ADO Recordset)

Opens a cursor.

Syntax

recordset.Open Source, ActiveConnection, CursorType, LockType, Options

Parameters

Source
Optional. A Variant that evaluates to a valid Command object, an SQL statement, a table name, a stored procedure call, a URL, or the name of a file or Stream object containing a persistently stored Recordset.
ActiveConnection
Optional. Either a Variant that evaluates to a valid Connection object variable name, or a String that contains ConnectionString parameters.
CursorType
Optional. A CursorTypeEnum value that determines the type of cursor that the provider should use when opening the Recordset. The default value is adOpenForwardOnly.
LockType
Optional. A LockTypeEnum value that determines what type of locking (concurrency) the provider should use when opening the Recordset. The default value is adLockReadOnly.
Options
Optional. A Long value that indicates how the provider should evaluate the Source argument if it represents something other than a Command object, or that the Recordset should be restored from a file where it was previously saved. Can be one or more CommandTypeEnum or ExecuteOptionEnum values, which can be combined with a bitwise AND operator.

Note If you open a Recordset from a Stream containing a persisted Recordset, using an ExecuteOptionEnum value of adAsyncFetchNonBlocking will not have an effect; the fetch will be synchronous and blocking.

The ExecuteOpenEnum values of adExecuteNoRecords or adExecuteStream should not be used with Open.

Remarks

The default cursor for an ADO Recordset is a forward-only, read-only cursor located on the server.

Using the Open method on a Recordset object opens a cursor that represents records from a base table, the results of a query, or a previously saved Recordset.

Use the optional Source argument to specify a data source using one of the following: a Command object variable, an SQL statement, a stored procedure, a table name, a URL, or a complete file path name. If Source is a file path name, it can be a full path ("c:\dir\file.rst"), a relative path ("..\file.rst"), or a URL ("http://files/file.rst").

The ActiveConnection argument corresponds to the ActiveConnection property and specifies in which connection to open the Recordset object. If you pass a connection definition for this argument, ADO opens a new connection using the specified parameters. After opening the Recordset with a client-side cursor (CursorLocation = adUseClient), you can change the value of this property to send updates to another provider. Or you can set this property to Nothing (in Microsoft Visual Basic) or NULL to disconnect the Recordset from any provider. Changing ActiveConnection for a server-side cursor generates an error, however.

For the other arguments that correspond directly to properties of a Recordset object (Source, CursorType, and LockType), the relationship of the arguments to the properties is as follows:

  • The property is read/write before the Recordset object is opened.
  • The property settings are used unless you pass the corresponding arguments when executing the Open method. If you pass an argument, it overrides the corresponding property setting, and the property setting is updated with the argument value.
  • After you open the Recordset object, these properties become read-only.

Note The ActiveConnection property is read only for Recordset objects whose Source property is set to a valid Command object, even if the Recordset object isn't open.

If you pass a Command object in the Source argument and also pass an ActiveConnection argument, an error occurs. The ActiveConnection property of the Command object must already be set to a valid Connection object or connection string.

If you pass something other than a Command object in the Source argument, you can use the Options argument to optimize evaluation of the Source argument. If the Options argument is not defined, you may experience diminished performance because ADO must make calls to the provider to determine if the argument is an SQL statement, a stored procedure, a URL, or a table name. If you know what Source type you're using, setting the Options argument instructs ADO to jump directly to the relevant code. If the Options argument does not match the Source type, an error occurs.

If you pass a Stream object in the Source argument, you should not pass information into the other arguments. Doing so will generate an error. The ActiveConnection information is not retained when a Recordset is opened from a Stream.

The default for the Options argument is adCmdFile if no connection is associated with the Recordset. This will typically be the case for persistently stored Recordset objects.

If the data source returns no records, the provider sets both the BOF and EOF properties to True, and the current record position is undefined. You can still add new data to this empty Recordset object if the cursor type allows it.

When you have concluded your operations over an open Recordset object, use the Close method to free any associated system resources. Closing an object does not remove it from memory; you can change its property settings and use the Open method to open it again later. To completely eliminate an object from memory, set the object variable to Nothing.

Before the ActiveConnection property is set, call Open with no operands to create an instance of a Recordset created by appending fields to the Recordset Fields collection.

If you have set the CursorLocation property to adUseClient, you can retrieve rows asynchronously in one of two ways. The recommended method is to set Options to adAsyncFetch. Alternatively, you can use the "Asynchronous Rowset Processing" dynamic property in the Properties collection, but related retrieved events can be lost if you do not set the Options parameter to adAsyncFetch.

Note Background fetching in the MS Remote provider is supported only through the Open method's Options parameter.

Note URLs using the http scheme will automatically invoke the Microsoft OLE DB Provider for Internet Publishing. For more information, see Absolute and Relative URLs.

See Also

Visual Basic Example | VBScript Example | Visual C++ Example | Visual J++ Example | Visual Basic Example

Open Method (ADO Connection) | Open Method (ADO Record) | Open Method (ADO Stream) | OpenSchema Method | Save Method

Applies To: Recordset Object


Open Method (ADO Stream)

Opens a Stream object to manipulate streams of binary or text data.

Syntax

Stream.Open Source, Mode, OpenOptions, UserName, Password

Parameters

Source
Optional. A Variant value that specifies the source of data for the Stream. Source may contain an absolute URL string that points to an existing node in a well-known tree structure, like an e-mail or file system. A URL should be specified using the URL keyword ("URL=scheme://server/folder"). Alternately, Source may contain a reference to an already open Record object, which opens the default stream associated with the Record. If Source is not specified, a Stream is instantiated and opened, associated with no underlying source by default. For more information about URL schemes and their associated providers, see Absolute and Relative URLs.
Mode
Optional. A ConnectModeEnum value that specifies the access mode for the resultant Stream (for example, read/write or read-only). Default value is adModeUnknown. See the Mode property for more information about access modes. If Mode is not specified, it is inherited by the source object. For example, if the source Record is opened in read-only mode, the Stream will also be opened in read-only mode by default.
OpenOptions
Optional. A StreamOpenOptionsEnum value. Default value is adOpenStreamUnspecified.
UserName
Optional. A String value that contains the user identification that, if needed, accesses the Stream object.
Password
Optional. A String value that contains the password that, if needed, accesses the Stream object.

Remarks

When a Record object is passed in as the source parameter, the UserID and Password parameters are not used because access to the Record object is already available. Similarly, the Mode of the Record object is transferred to the Stream object.When Source is not specified, the Stream opened contains no data and has a Size of zero (0). To avoid losing any data that is written to this Stream when the Stream is closed, save the Stream with the CopyTo or SaveToFile methods, or save it to another memory location.

An OpenOptions value of adOpenStreamFromRecord identifies the contents of the Source parameter to be an already open Record object. The default behavior is to treat Source as a URL that points directly to a node in a tree structure, such as a file. The default stream associated with that node is opened.

While the Stream is not open, it is possible to read all the read-only properties of the Stream. If a Stream is opened asynchronously, all subsequent operations (other than checking the State and other read-only properties) are blocked until the Open operation is completed.

In addition to the options discussed above, by not specifying Source, you can simply instantiate a Stream object in memory without associating it with an underlying source. You can dynamically add data to the stream simply by writing binary or text data to the Stream with Write or WriteText, or by loading data from a file with LoadFromFile.

See Also

Example

Open Method (ADO Connection) | Open Method (ADO Record) | Open Method (ADO Recordset) | OpenSchema Method | SaveToFile Method

Applies To: Stream Object


OpenSchema Method (ADO)

ADO API Reference

OpenSchema Method

Obtains database schema information from the provider.

Syntax

Set recordset = connection.OpenSchema (QueryType, Criteria, SchemaID)

Return Values

Returns a Recordset object that contains schema information. The Recordset will be opened as a read-only, static cursor. The QueryType determines what columns appear in the Recordset.

Parameters

QueryType
Any SchemaEnum value that represents the type of schema query to run.
Criteria
Optional. An array of query constraints for each QueryType option, as listed in SchemaEnum.
SchemaID
The GUID for a provider-schema query not defined by the OLE DB specification. This parameter is required if QueryType is set to adSchemaProviderSpecific; otherwise, it is not used.

Remarks

The OpenSchema method returns self-descriptive information about the data source, such as what tables are in the data source, the columns in the tables, and the data types supported.

The QueryType argument is a GUID that indicates the columns (schemas) returned. The OLE DB specification has a full list of schemas.

The Criteria argument limits the results of a schema query. Criteria specifies an array of values that must occur in a corresponding subset of columns, called constraint columns, in the resulting Recordset.

The constant adSchemaProviderSpecific is used for the QueryType argument if the provider defines its own nonstandard schema queries outside those listed above. When this constant is used, the SchemaID argument is required to pass the GUID of the schema query to execute. If QueryType is set to adSchemaProviderSpecific but SchemaID is not provided, an error will result.

Providers are not required to support all of the OLE DB standard schema queries. Specifically, only adSchemaTables, adSchemaColumns, and adSchemaProviderTypes are required by the OLE DB specification. However, the provider is not required to support the Criteria constraints listed above for those schema queries.

Remote Data Service Usage The OpenSchema method is not available on a client-side Connection object.

Note In Visual Basic, columns that have a four-byte unsigned integer (DBTYPE UI4) in the Recordset returned from the OpenSchema method on the Connection object cannot be compared to other variables. For more information about OLE DB data types, see Chapter 13 and Appendix A of the Microsoft OLE DB Programmer's Reference.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Open Method (ADO Connection) | Open Method (ADO Record) | Open Method (ADO Recordset) | Open Method (ADO Stream) | Appendix A: Providers

Applies To: Connection Object


Read Method (ADO)

ADO API Reference

Read Method

Reads a specified number of bytes from a binary Stream object.

Syntax

Variant = Stream.Read ( NumBytes )

Parameters

NumBytes
Optional. A Long value that specifies the number of bytes to read from the file or the StreamReadEnum value adReadAll, which is the default.

Return Value

The Read method reads a specified number of bytes or the entire stream from a Stream object and returns the resulting data as a Variant.

Remarks

If NumBytes is more than the number of bytes left in the Stream, only the bytes remaining are returned. The data read is not padded to match the length specified by NumBytes. If there are no bytes left to read, a variant with a null value is returned. Read cannot be used to read backwards.

Note NumBytes always measures bytes. For text Stream objects (Type is adTypeText), use ReadText.

See Also

Example

ReadText Method

Applies To: Stream Object


ReadText Method (ADO)

ADO API Reference

ReadText Method

Reads specified number of characters from a text Stream object.

Syntax

String = Stream.ReadText ( NumChars )

Parameters

NumChars
Optional. A Long value that specifies the number of characters to read from the file, or a StreamReadEnum value. The default value is adReadAll.

Return Value

The ReadText method reads a specified number of characters, an entire line, or the entire stream from a Stream object and returns the resulting string.

Remarks

If NumChar is more than the number of characters left in the stream, only the characters remaining are returned. The string read is not padded to match the length specified by NumChar. If there are no characters left to read, a variant whose value is null is returned. ReadText cannot be used to read backwards.

Note The ReadText method is used with text streams (Type is adTypeText). For binary streams (Type is adTypeBinary), use Read.

See Also

Example

Read Method

Applies To: Stream Object


Refresh Method (ADO)

ADO API Reference

Refresh Method

Updates the objects in a collection to reflect objects available from, and specific to, the provider.

Syntax

collection.Refresh

Remarks

The Refresh method accomplishes different tasks depending on the collection from which you call it.

Parameters

Using the Refresh method on a Command object's Parameters collection retrieves provider-side parameter information for the stored procedure or parameterized query specified in the Command object. The collection will be empty for providers that do not support stored procedure calls or parameterized queries.

You should set the ActiveConnection property of the Command object to a valid Connection object, the CommandText property to a valid command, and the CommandType property to adCmdStoredProc before calling the Refresh method.

If you access the Parameters collection before calling the Refresh method, ADO will automatically call the method and populate the collection for you.

Note If you use the Refresh method to obtain parameter information from the provider and it returns one or more variable-length data type Parameter objects, ADO may allocate memory for the parameters based on their maximum potential size, which will cause an error during execution. You should explicitly set the Size property for these parameters before calling the Execute method to prevent errors.

Fields

Using the Refresh method on the Fields collection has no visible effect. To retrieve changes from the underlying database structure, you must use either the Requery method or, if the Recordset object does not support bookmarks, the MoveFirst method.

Properties

Using the Refresh method on a Properties collection of some objects populates the collection with the dynamic properties that the provider exposes. These properties provide information about functionality specific to the provider, beyond the built-in properties ADO supports.

See Also

Visual Basic Example | Visual C++ Example | Visual VJ++ Example

Count Property | Refresh Method (RDS)

Applies To: Axes Collection | Columns Collection | CubeDefs Collection | Dimensions Collection | Errors Collection | Fields Collection | Groups Collection | Hierarchies Collection | Indexes Collection | Keys Collection | Levels Collection | Members Collection | Parameters Collection | Positions Collection | Procedures Collection | Properties Collection | Tables Collection | Users Collection | Views Collection


Requery Method (ADO)

ADO API Reference

Requery Method

Updates the data in a Recordset object by re-executing the query on which the object is based.

Syntax

recordset.Requery Options

Parameter

Options
Optional. A bitmask that contains ExecuteOptionEnum and CommandTypeEnum values affecting this operation.

Note If Options is set to adAsyncExecute, this operation will execute asynchronously and a RecordsetChangeComplete event will be issued when it concludes.

The ExecuteOpenEnum values of adExecuteNoRecords or adExecuteStream should not be used with Requery.

Remarks

Use the Requery method to refresh the entire contents of a Recordset object from the data source by reissuing the original command and retrieving the data a second time. Calling this method is equivalent to calling the Close and Open methods in succession. If you are editing the current record or adding a new record, an error occurs.

While the Recordset object is open, the properties that define the nature of the cursor (CursorType, LockType, MaxRecords, and so forth) are read-only. Thus, the Requery method can only refresh the current cursor. To change any of the cursor properties and view the results, you must use the Close method so that the properties become read/write again. You can then change the property settings and call the Open method to reopen the cursor.

See Also

Visual Basic Example | VBScript Example | Visual C++ Example | Visual J++ Example

CommandText Property

Applies To: Recordset Object


Resync Method (ADO)

ADO API Reference

Resync Method

Refreshes the data in the current Recordset object, or Fields collection of a Record object, from the underlying database.

Syntax

Recordset.Resync AffectRecords, ResyncValues
Record.Fields.Resync ResyncValues

Parameters

AffectRecords
Optional. An AffectEnum value that determines how many records the Resync method will affect. The default value is adAffectAll. This value is not available with the Resync method of the Fields collection of a Record object.
ResyncValues
Optional. A ResyncEnum value that specifies whether underlying values are overwritten. The default value is adResyncAllValues.

Remarks

Recordset

Use the Resync method to resynchronize records in the current Recordset with the underlying database. This is useful if you are using either a static or forward-only cursor, but you want to see any changes in the underlying database.

If you set the CursorLocation property to adUseClient, Resync is only available for non-read-only Recordset objects.

Unlike the Requery method, the Resync method does not re-execute the Recordset object's underlying command. New records in the underlying database will not be visible.

If the attempt to resynchronize fails because of a conflict with the underlying data (for example, a record has been deleted by another user), the provider returns warnings to the Errors collection and a run-time error occurs. Use the Filter property (adFilterConflictingRecords) and the Status property to locate records with conflicts.

If the Unique Table and Resync Command dynamic properties are set, and the Recordset is the result of executing a JOIN operation on multiple tables, then the Resync method will execute the command given in the Resync Command property only on the table named in the Unique Table property.

Fields

Use the Resync method to resynchronize the values of the Fields collection of a Record object with the underlying data source. The Count property is not affected by this method.

If ResyncValues is set to adResyncAllValues (the default value), then the UnderlyingValue, Value, and OriginalValue properties of Field objects in the collection are synchronized. If ResyncValues is set to adResyncUnderlyingValues, only the UnderlyingValue property is synchronized.

The value of the Status property for each Field object at the time of the call also affects the behavior of Resync. For Field objects with Status values of adFieldPendingUnknown or adFieldPendingInsert, Resync has no effect. For Status values of adFieldPendingChange or adFieldPendingDelete, Resync synchronizes data values for fields that still exist at the data source.

Resync will not modify Status values of Field objects unless an error occurs when Resync is called. For example, if the field no longer exists, the provider will return an appropriate Status value for the Field object, such as adFieldDoesNotExist. Returned Status values may be logically combined within the value of the Status property.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Clear Method | UnderlyingValue Property

Applies To: Fields Collection | Recordset Object


Save Method (ADO)

ADO API Reference

Save Method

Saves the Recordset in a file or Stream object.

Syntax

recordset.Save Destination, PersistFormat

Parameters

Destination
Optional. A Variant that represents the complete path name of the file where the Recordset is to be saved, or a reference to a Stream object.
PersistFormat
Optional. A PersistFormatEnum value that specifies the format in which the Recordset is to be saved (XML or ADTG). The default value is adPersistADTG.

Remarks

The Save method can only be invoked on an open Recordset. Use the Open method to later restore the Recordset from Destination.

If the Filter property is in effect for the Recordset, then only the rows accessible under the filter are saved. If the Recordset is hierarchical, then the current child Recordset and its children are saved, including the parent Recordset. If the Save method of a child Recordset is called, the child and all its children are saved, but the parent is not.

The first time you save the Recordset, it is optional to specify Destination. If you omit Destination, a new file will be created with a name set to the value of the Source property of the Recordset.

Omit Destination when you subsequently call Save after the first save, or a run-time error will occur. If you subsequently call Save with a new Destination, the Recordset is saved to the new destination. However, the new destination and the original destination will both be open.

Save does not close the Recordset or Destination, so you can continue to work with the Recordset and save your most recent changes. Destination remains open until the Recordset is closed, during which time other applications can read but not write to Destination.

For reasons of security, the Save method permits only the use of low and custom security settings from a script executed by Microsoft Internet Explorer. For a more detailed explanation of security issues, see "ADO and RDS Security Issues in Microsoft Internet Explorer" under ActiveX Data Objects (ADO) Technical Articles in Microsoft Data Access Technical Articles.

If the Save method is called while an asynchronous Recordset fetch, execute, or update operation is in progress, then Save waits until the asynchronous operation is complete.

Records are saved beginning with the first row of the Recordset. When the Save method is finished, the current row position is moved to the first row of the Recordset.

For best results, set the CursorLocation property to adUseClient with Save. If your provider does not support all of the functionality necessary to save Recordset objects, the Cursor Service will provide that functionality.

When a Recordset is persisted with the CursorLocation property set to adUseServer, the update capability for the Recordset is limited. Typically, only single-table updates, insertions, and deletions are allowed (dependant upon provider functionality). The Resync method is also unavailable in this configuration.

Note Saving a Recordset with Fields of type adVariant, adIDispatch, or adIUnknown is not supported by ADO and can cause unpredictable results.

Only Filters in the form of Criteria Strings (e.g. OrderDate > '12/31/1999') affect the contents of a persisted Recordset. Filters created with an Array of Bookmarks or using a value from the FilterGroupEnum will not affect the contents of the persisted Recordset. These rules apply to Recordsets created with either client-side or server-side cursors.

Because the Destination parameter can accept any object that supports the OLE DB IStream interface, you can save a Recordset directly to the ASP Response object. For more details, please see the XML Recordset Persistence Scenario.

You can also save a Recordset in XML format to an instance of an MSXML DOM object, as is shown in the following Visual Basic code:

Dim xDOM As New MSXML.DOMDocument
Dim rsXML As New ADODB.Recordset
Dim sSQL As String, sConn As String

sSQL = "SELECT customerid, companyname, contactname FROM customers"
sConn="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\Program Files" & _
        "\Common Files\System\msadc\samples\NWind.mdb"
rsXML.Open sSQL, sConn
rsXML.Save xDOM, adPersistXML   'Save Recordset directly into a DOM tree.
...

Note Two limitations apply when saving hierarchical Recordsets (data shapes) in XML format. You cannot save into XML if the hierarchical Recordset contains pending updates, and you cannot save a parameterized hierarchical Recordset.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

Open Method (ADO Recordset) | Open Method (ADO Stream) | SaveToFile Method

Applies To: Recordset Object | Stream Object


SaveToFile Method (ADO)

ADO API Reference

SaveToFile Method

Saves the binary contents of a Stream to a file.

Syntax

Stream.SaveToFile FileName, SaveOptions

Parameters

FileName
A String value that contains the fully-qualified name of the file to which the contents of the Stream will be saved. You can save to any valid local location, or any location you have access to via a UNC value.
SaveOptions
A SaveOptionsEnum value that specifies whether a new file should be created by SaveToFile, if it does not already exist. Default value is adSaveCreateNotExists. With these options you can specify that an error occurs if the specified file does not exist. You can also specify that SaveToFile overwrites the current contents of an existing file.

Note If you overwrite an existing file (when adSaveCreateOverwrite is set), SaveToFile truncates any bytes from the original existing file that follow the new EOS.

Remarks

SaveToFile may be used to copy the contents of a Stream object to a local file. There is no change in the contents or properties of the Stream object. The Stream object must be open before calling SaveToFile.

This method does not change the association of the Stream object to its underlying source. The Stream object will still be associated with the original URL or Record that was its source when opened.

After a SaveToFile operation, the current position (Position) in the stream is set to the beginning of the stream (0).

See Also

Open Method (ADO Stream) | Save Method

Applies To: Stream Object


Seek Method (ADO)

ADO API Reference

Seek Method

Searches the index of a Recordset to quickly locate the row that matches the specified values, and changes the current row position to that row.

Syntax

recordset.Seek KeyValues, SeekOption

Parameters

KeyValues
An array of Variant values. An index consists of one or more columns and the array contains a value to compare against each corresponding column.
SeekOption
A SeekEnum value that specifies the type of comparison to be made between the columns of the index and the corresponding KeyValues.

Remarks

Use the Seek method in conjunction with the Index property if the underlying provider supports indexes on the Recordset object. Use the Supports (adSeek) method to determine whether the underlying provider supports Seek, and the Supports(adIndex) method to determine whether the provider supports indexes. (For example, the OLE DB Provider for Microsoft Jet supports Seek and Index.)

If Seek does not find the desired row, no error occurs, and the row is positioned at the end of the Recordset. Set the Index property to the desired index before executing this method.

This method is supported only with server-side cursors. Seek is not supported when the Recordset object's CursorLocation property value is adUseClient.

This method can only be used when the Recordset object has been opened with a CommandTypeEnum value of adCmdTableDirect.

See Also

Visual Basic Example | Visual C++ Example

Find Method | Index Property

Applies To: Recordset Object


SetEOS Method (ADO)

ADO API Reference

SetEOS Method

Sets the position that is the end of the stream.

Syntax

Stream.SetEOS

Remarks

SetEOS updates the value of the EOS property, by making the current Position the end of the stream. Any bytes or characters following the current position are truncated.

Since Write, WriteText, and CopyTo do not truncate any extra values in existing Stream objects, you can truncate these bytes or characters by setting the new end-of-stream position with SetEOS.

Caution If you set EOS to a position before the actual end of the stream, you will lose all data after the new EOS position.

See Also

Applies To: Stream Object


SkipLine Method (ADO)

ADO API Reference

SkipLine Method

Skips one entire line when reading a text stream.

Syntax

Stream.SkipLine

Remarks

All characters up to, and including the next line separator, are skipped. By default, the LineSeparator is adCRLF. If you attempt to skip past EOS, the current position will simply remain at EOS.

The SkipLine method is used with text streams (Type is adTypeText).

See Also

Applies To: Stream Object


Supports Method (ADO)

ADO API Reference

Supports Method

Determines whether a specified Recordset object supports a particular type of functionality.

Syntax

boolean = recordset.Supports( CursorOptions )

Return Value

Returns a Boolean value that indicates whether all of the features identified by the CursorOptions argument are supported by the provider.

Parameters

CursorOptions
A Long expression that consists of one or more CursorOptionEnum values.

Remarks

Use the Supports method to determine what types of functionality a Recordset object supports. If the Recordset object supports the features whose corresponding constants are in CursorOptions, the Supports method returns True. Otherwise, it returns False.

Note Although the Supports method may return True for a given functionality, it does not guarantee that the provider can make the feature available under all circumstances. The Supports method simply returns whether the provider can support the specified functionality, assuming certain conditions are met. For example, the Supports method may indicate that a Recordset object supports updates even though the cursor is based on a multiple table join, some columns of which are not updatable.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

CursorType Property

Applies To: Recordset Object


Update Method (ADO)

ADO API Reference

Update Method

Saves any changes you make to the current row of a Recordset object, or the Fields collection of a Record object.

Syntax

recordset.Update Fields, Values
record.Fields.Update

Parameters

Fields
Optional. A Variant that represents a single name, or a Variant array that represents names or ordinal positions of the field or fields you wish to modify.
Values
Optional. A Variant that represents a single value, or a Variant array that represents values for the field or fields in the new record.

Remarks

Recordset

Use the Update method to save any changes you make to the current record of a Recordset object since calling the AddNew method or since changing any field values in an existing record. The Recordset object must support updates.

To set field values, do one of the following:

  • Assign values to a Field object's Value property and call the Update method.
  • Pass a field name and a value as arguments with the Update call.
  • Pass an array of field names and an array of values with the Update call.

When you use arrays of fields and values, there must be an equal number of elements in both arrays. Also, the order of field names must match the order of field values. If the number and order of fields and values do not match, an error occurs.

If the Recordset object supports batch updating, you can cache multiple changes to one or more records locally until you call the UpdateBatch method. If you are editing the current record or adding a new record when you call the UpdateBatch method, ADO will automatically call the Update method to save any pending changes to the current record before transmitting the batched changes to the provider.

If you move from the record you are adding or editing before calling the Update method, ADO will automatically call Update to save the changes. You must call the CancelUpdate method if you want to cancel any changes made to the current record or discard a newly added record.

The current record remains current after you call the Update method.

If the Unique Table dynamic property is set, and the Recordset is the result of executing a JOIN operation on multiple tables, then the Update method cannot update any primary key of the multiple tables. Furthermore, the Update method can update fields only in the table specified in the Unique Table property.

Record

The Update method finalizes additions, deletions, and updates to fields in the Fields collection of a Record object.

For example, fields deleted with the Delete method are marked for deletion immediately but remain in the collection. The Update method must be called to actually delete these fields from the provider's collection.

See Also

Visual Basic Example | Visual C++ Example

AddNew Method | CancelUpdate Method | EditMode Property | UpdateBatch Method

Applies To: Fields Collection | Recordset Object


UpdateBatch Method (ADO)

ADO API Reference

UpdateBatch Method

Writes all pending batch updates to disk.

Syntax

recordset.UpdateBatch AffectRecords

Parameters

AffectRecords
Optional. An AffectEnum value that indicates how many records the UpdateBatch method will affect.

Remarks

Use the UpdateBatch method when modifying a Recordset object in batch update mode to transmit all changes made in a Recordset object to the underlying database.

If the Recordset object supports batch updating, you can cache multiple changes to one or more records locally until you call the UpdateBatch method. If you are editing the current record or adding a new record when you call the UpdateBatch method, ADO will automatically call the Update method to save any pending changes to the current record before transmitting the batched changes to the provider. You should use batch updating with either a keyset or static cursor only.

Note Specifying adAffectGroup as the value for this parameter will result in an error when there are no visible records in the current Recordset (such as a filter for which no records match).

If the attempt to transmit changes fails for any or all records because of a conflict with the underlying data (for example, a record has already been deleted by another user), the provider returns warnings to the Errors collection and a run-time error occurs. Use the Filter property (adFilterAffectedRecords) and the Status property to locate records with conflicts.

To cancel all pending batch updates, use the CancelBatch method.

If the Unique Table and Update Resync dynamic properties are set, and the Recordset is the result of executing a JOIN operation on multiple tables, then the execution of the UpdateBatch method is implicitly followed by the Resync method depending on the settings of the Update Resync property.

The order in which the individual updates of a batch are performed on the data source is not necessarily the same as the order in which they were performed on the local Recordset. Update order is dependent upon the provider. Take this into account when coding updates that are related to one another, such as foreign key constraints on an insert or update.

See Also

Visual Basic Example | Visual C++ Example | Visual J++ Example

CancelBatch Method | Clear Method | LockType Property | Update Method

Applies To: Recordset Object


Write Method (ADO)

ADO API Reference

Write Method

Writes binary data to a Stream object.

Syntax

Stream.Write Buffer

Parameters

Buffer
A Variant that contains an array of bytes to be written.

Remarks

Specified bytes are written to the Stream object without any intervening spaces between each byte.

The current Position is set to the byte following the written data. The Write method does not truncate the rest of the data in a stream. If you want to truncate these bytes, call SetEOS.

If you write past the current EOS position, the Size of the Stream will be increased to contain any new bytes, and EOS will move to the new last byte in the Stream.

Note The Write method is used with binary streams (Type is adTypeBinary). For text streams (Type is adTypeText), use WriteText.

See Also

WriteText Method

Applies To: Stream Object


WriteText Method (ADO)

ADO API Reference

WriteText Method

Writes a specified text string to a Stream object.

Syntax

Stream.WriteText Data, Options

Parameters

Data
A String value that contains the text in characters to be written.
Options
Optional. A StreamWriteEnum value that specifies whether a line separator character must be written at the end of the specified string.

Remarks

Specified strings are written to the Stream object without any intervening spaces or characters between each string.

The current Position is set to the character following the written data. The WriteText method does not truncate the rest of the data in a stream. If you want to truncate these characters, call SetEOS.

If you write past the current EOS position, the Size of the Stream will be increased to contain any new characters, and EOS will move to the new last byte in the Stream.

Note The WriteText method is used with text streams (Type is adTypeText). For binary streams (Type is adTypeBinary), use Write.

See Also

Write Method

Applies To: Stream Object


ADO Events

BeginTransComplete | Called after the BeginTrans operation.
CommitTransComplete | Called after the CommitTrans operation.
ConnectComplete | Called after a connection starts.
Disconnect | Called after a connection ends.
EndOfRecordset | Called when there is an attempt to move to a row past the end of the Recordset.
ExecuteComplete | Called after a command has finished executing.
FetchComplete | Called after all the records in a lengthy asynchronous operation have been retrieved into the Recordset.
FetchProgress | Called periodically during a lengthy asynchronous operation to report how many rows have currently been retrieved into the Recordset.
FieldChangeComplete | Called after the value of one or more Field objects has changed.
InfoMessage | Called whenever a warning occurs during a ConnectionEvent operation.
MoveComplete | Called after the current position in the Recordset changes.
RecordChangeComplete | Called after one or more records change.
RecordsetChangeComplete | Called after the Recordset has changed.
RollbackTransComplete | Called after the RollbackTrans operation.
WillChangeField | Called before a pending operation changes the value of one or more Field objects in the Recordset.
WillChangeRecord | Called before one or more records (rows) in the Recordset change.
WillChangeRecordset | Called before a pending operation changes the Recordset.
WillConnect | Called before a connection starts.
WillExecute | Called just before a pending command executes on this connection and affords the user an opportunity to examine and modify the pending execution parameters.
WillMove | The WillMove event is called before a pending operation changes the current position in the Recordset.

See Also

ADO API Reference | ADO Collections | ADO Dynamic Properties | ADO Enumerated Constants | ADO Errors | ADO Event Model, Synchronous and Asynchronous Operations | ADO Methods | ADO Object Model | ADO Objects | ADO Properties


BeginTransComplete, CommitTransComplete, and RollbackTransComplete Events (ADO)

ADO API Reference

BeginTransComplete, CommitTransComplete, and RollbackTransComplete Events

These events will be called after the associated operation on the Connection object finishes executing.

  • BeginTransComplete is called after the BeginTrans operation.
  • CommitTransComplete is called after the CommitTrans operation.
  • RollbackTransComplete is called after the RollbackTrans operation.

Syntax

BeginTransComplete TransactionLevel, pError, adStatus, pConnection
CommitTransComplete pError, adStatus, pConnection
RollbackTransComplete pError, adStatus, pConnection

Parameters

TransactionLevel
A Long value that contains the new transaction level of the BeginTrans that caused this event.
pError
An Error object. It describes the error that occurred if the value of EventStatusEnum is adStatusErrorsOccurred; otherwise it is not set.
adStatus
An EventStatusEnum status value. When any of these events is called, this parameter is set to adStatusOK if the operation that caused the event was successful, or to adStatusErrorsOccurred if the operation failed.

These events can prevent subsequent notifications by setting this parameter to adStatusUnwantedEvent before the event returns.

pConnection
The Connection object for which this event occurred.

Remarks

In Visual C++, multiple Connections can share the same event handling method. The method uses the returned Connection object to determine which object caused the event.

If the Attributes property is set to adXactCommitRetaining or adXactAbortRetaining, a new transaction starts after committing or rolling back a transaction. Use the BeginTransComplete event to ignore all but the first transaction start event.

See Also

Visual C++ Example | Visual J++ Example | Visual Basic Example

ADO Event Handler Summary | BeginTrans, CommitTrans, and RollbackTrans Methods

Applies To: Connection Object


ConnectComplete and Disconnect Events (ADO)

ADO API Reference

ConnectComplete and Disconnect Events

The ConnectComplete event is called after a connection starts. The Disconnect event is called after a connection ends.

Syntax

ConnectComplete pError, adStatus, pConnection
Disconnect adStatus, pConnection

Parameters

pError
An Error object. It describes the error that occurred if the value of adStatus is adStatusErrorsOccurred; otherwise it is not set.
adStatus
An EventStatusEnum value that always returns adStatusOK.

When ConnectComplete is called, this parameter is set to adStatusCancel if a WillConnect event has requested cancellation of the pending connection.

Before either event returns, set this parameter to adStatusUnwantedEvent to prevent subsequent notifications. However, closing and reopening the Connection causes these events to occur again.

pConnection
The Connection object for which this event applies.

See Also

Visual C++ Example

ADO Event Handler Summary

Applies To: Connection Object


EndOfRecordset Event (ADO)

ADO API Reference

EndOfRecordset Event

The EndOfRecordset event is called when there is an attempt to move to a row past the end of the Recordset.

Syntax

EndOfRecordset fMoreData, adStatus, pRecordset

Parameters

fMoreData
A VARIANT_BOOL value that, if set to VARIANT_TRUE, indicates more rows have been added to the Recordset.
adStatus
An EventStatusEnum status value.

When EndOfRecordset is called, this parameter is set to adStatusOK if the operation that caused the event was successful. It is set to adStatusCantDeny if this event cannot request cancellation of the operation that caused this event.

Before EndOfRecordset returns, set this parameter to adStatusUnwantedEvent to prevent subsequent notifications.

pRecordset
A Recordset object. The Recordset for which this event occurred.

Remarks

An EndOfRecordset event may occur if the MoveNext operation fails.

This event handler is called when an attempt is made to move past the end of the Recordset object, perhaps as a result of calling MoveNext. However, while in this event, you could retrieve more records from a database and append them to the end of the Recordset. In that case, set fMoreData to VARIANT_TRUE, and return from EndOfRecordset. Then call MoveNext again to access the newly retrieved records.

See Also

Visual C++ Example

ADO Event Handler Summary

Applies To: Recordset Object


ExecuteComplete Event (ADO)

ADO API Reference

ExecuteComplete Event

The ExecuteComplete event is called after a command has finished executing.

Syntax

ExecuteComplete RecordsAffected, pError, adStatus, pCommand, pRecordset, pConnection

Parameters

RecordsAffected
A Long value indicating the number of records affected by the command.
pError
An Error object. It describes the error that occurred if the value of adStatus is adStatusErrorsOccurred; otherwise it is not set.
adStatus
An EventStatusEnum status value. When this event is called, this parameter is set to adStatusOK if the operation that caused the event was successful, or to adStatusErrorsOccurred if the operation failed.

Before this event returns, set this parameter to adStatusUnwantedEvent to prevent subsequent notifications.

pCommand
The Command object that was executed. Contains a Command object even when calling Connection.Execute or Recordset.Open without explicitly creating a Command, in which cases the Command object is created internally by ADO.
pRecordset
A Recordset object that is the result of the executed command. This Recordset may be empty.
pConnection
A Connection object. The connection over which the operation was executed.

Remarks

An ExecuteComplete event may occur due to the Connection. Execute, Command. Execute, Recordset. Open, Recordset. Requery, or Recordset. NextRecordset methods.

See Also

Visual C++ Example

ADO Event Handler Summary

Applies To: Connection Object


FetchComplete Event (ADO)

ADO API Reference

FetchComplete Event

The FetchComplete event is called after all the records in a lengthy asynchronous operation have been retrieved into the Recordset.

Syntax

FetchComplete pError, adStatus, pRecordset

Parameters

pError
An Error object. It describes the error that occurred if the value of adStatus is adStatusErrorsOccurred; otherwise it is not set.
adStatus
An EventStatusEnum status value. When this event is called, this parameter is set to adStatusOK if the operation that caused the event was successful, or to adStatusErrorsOccurred if the operation failed.

Before this event returns, set this parameter to adStatusUnwantedEvent to prevent subsequent notifications.

pRecordset
A Recordset object. The object for which the records were retrieved.

Remarks

To use FetchComplete with Microsoft Visual Basic, Visual Basic 6.0 or later is required.

See Also

Visual C++ Example

ADO Event Handler Summary

Applies To: Recordset Object


FetchProgress Event (ADO)

ADO API Reference

FetchProgress Event

The FetchProgress event is called periodically during a lengthy asynchronous operation to report how many more rows have currently been retrieved into the Recordset.

Syntax

FetchProgress Progress, MaxProgress, adStatus, pRecordset

Parameters

Progress
A Long value indicating the number of records that have currently been retrieved by the fetch operation.
MaxProgress
A Long value indicating the maximum number of records expected to be retrieved.
adStatus
An EventStatusEnum status value.
pRecordset
A Recordset object that is the object for which the records are being retrieved.

Remarks

When using FetchProgress with a child Recordset, be aware that the Progress and MaxProgress parameter values are derived from the underlying Cursor Service rowset. The values returned represent the total number of records in the underlying rowset, not just the number of records in the current chapter.

Note To use FetchProgress with Microsoft Visual Basic, Visual Basic 6.0 or later is required.

See Also

Visual C++ Example

ADO Event Handler Summary

Applies To: Recordset Object


InfoMessage Event (ADO)

ADO API Reference

InfoMessage Event

The InfoMessage event is called whenever a warning occurs during a ConnectionEvent operation.

Syntax

InfoMessage pError, adStatus, pConnection

Parameters

pError
An Error object. This parameter contains any errors that are returned. If multiple errors are returned, enumerate the Errors collection to find them.
adStatus
An EventStatusEnum status value. If a warning occurs, adStatus is set to adStatusOK and the pError contains the warning.

Before this event returns, set this parameter to adStatusUnwantedEvent to prevent subsequent notifications.

pConnection
A Connection object. The connection for which the warning occurred. For example, warnings can occur when opening a Connection object or executing a Command on a Connection.

See Also

Visual C++ Example

ADO Event Handler Summary

Applies To: Connection Object


WillChangeField and FieldChangeComplete Events (ADO)

ADO API Reference

WillChangeField and FieldChangeComplete Events

The WillChangeField event is called before a pending operation changes the value of one or more Field objects in the Recordset. The FieldChangeComplete event is called after the value of one or more Field objects has changed.

Syntax

WillChangeField cFields, Fields, adStatus, pRecordset
FieldChangeComplete cFields, Fields, pError, adStatus, pRecordset

Parameters

cFields
A Long that indicates the number of Field objects in Fields.
Fields
An array of Variants that contains Field objects with pending changes.
pError
An Error object. It describes the error that occurred if the value of adStatus is adStatusErrorsOccurred; otherwise it is not set.
adStatus
An EventStatusEnum status value.

When WillChangeField is called, this parameter is set to adStatusOK if the operation that caused the event was successful. It is set to adStatusCantDeny if this event cannot request cancellation of the pending operation.

When FieldChangeComplete is called, this parameter is set to adStatusOK if the operation that caused the event was successful, or to adStatusErrorsOccurred if the operation failed.

Before WillChangeField returns, set this parameter to adStatusCancel to request cancellation of the pending operation.

Before FieldChangeComplete returns, set this parameter to adStatusUnwantedEvent to prevent subsequent notifications.

pRecordset
A Recordset object. The Recordset for which this event occurred.

Remarks

A WillChangeField or FieldChangeComplete event may occur when setting the Value property and calling the Update method with field and value array parameters.

See Also

Visual C++ Example

ADO Event Handler Summary

Applies To: Recordset Object


WillChangeRecord and RecordChangeComplete Events (ADO)

ADO API Reference

WillChangeRecord and RecordChangeComplete Events

The WillChangeRecord event is called before one or more records (rows) in the Recordset change. The RecordChangeComplete event is called after one or more records change.

Syntax

WillChangeRecord adReason, cRecords, adStatus, pRecordset
RecordChangeComplete adReason, cRecords, pError, adStatus, pRecordset

Parameters

adReason
An EventReasonEnum value that specifies the reason for this event. Its value can be adRsnAddNew, adRsnDelete, adRsnUpdate, adRsnUndoUpdate, adRsnUndoAddNew, adRsnUndoDelete, or adRsnFirstChange.
cRecords
A Long value that indicates the number of records changing (affected).
pError
An Error object. It describes the error that occurred if the value of adStatus is adStatusErrorsOccurred; otherwise it is not set.
adStatus
An EventStatusEnum status value.

When WillChangeRecord is called, this parameter is set to adStatusOK if the operation that caused the event was successful. It is set to adStatusCantDeny if this event cannot request cancellation of the pending operation.

When RecordChangeComplete is called, this parameter is set to adStatusOK if the operation that caused the event was successful, or to adStatusErrorsOccurred if the operation failed.

Before WillChangeRecord returns, set this parameter to adStatusCancel to request cancellation of the operation that caused this event or set this parameter to adStatusUnwantedEvent to prevent subsequent notications.

Before RecordChangeComplete returns, set this parameter to adStatusUnwantedEvent to prevent subsequent notifications.

pRecordset
A Recordset object. The Recordset for which this event occurred.

Remarks

A WillChangeRecord or RecordChangeComplete event may occur for the first changed field in a row due to the following Recordset operations: Update, Delete, CancelUpdate, AddNew, UpdateBatch, and CancelBatch. The value of the Recordset CursorType determines which operations cause the events to occur.

During the WillChangeRecord event, the Recordset Filter property is set to adFilterAffectedRecords. You cannot change this property while processing the event.

You must set the adStatus parameter to adStatusUnwantedEvent for each possible adReason value in order to completely stop event noticiation for any event that includes an adReason parameter.

See Also

Visual C++ Example

ADO Event Handler Summary

Applies To: Recordset Object


WillChangeRecordset and RecordsetChangeComplete Events (ADO)

ADO API Reference

WillChangeRecordset and RecordsetChangeComplete Events

The WillChangeRecordset event is called before a pending operation changes the Recordset. The RecordsetChangeComplete event is called after the Recordset has changed.

Syntax

WillChangeRecordset   adReason, adStatus, pRecordset
RecordsetChangeComplete   adReason, pError, adStatus, pRecordset

Parameters

adReason
An EventReasonEnum value that specifies the reason for this event. Its value can be adRsnRequery, adRsnResynch, adRsnClose, adRsnOpen.
adStatus
An EventStatusEnum status value.

When WillChangeRecordset is called, this parameter is set to adStatusOK if the operation that caused the event was successful. It is set to adStatusCantDeny if this event cannot request cancellation of the pending operation.

When RecordsetChangeComplete is called, this parameter is set to adStatusOK if the operation that caused the event was successful, adStatusErrorsOccurred if the operation failed, or adStatusCancel if the operation associated with the previously accepted WillChangeRecordset event has been canceled.

Before WillChangeRecordset returns, set this parameter to adStatusCancel to request cancellation of the pending operation or set this parameter to adStatusUnwantedEvent to prevent subsequent notifications.

Before WillChangeRecordset or RecordsetChangeComplete returns, set this parameter to adStatusUnwantedEvent to prevent subsequent notifications.

pError
An Error object. It describes the error that occurred if the value of adStatus is adStatusErrorsOccurred; otherwise it is not set.
pRecordset
A Recordset object. The Recordset for which this event occurred.

Remarks

A WillChangeRecordset or RecordsetChangeComplete event may occur due to the Recordset Requery or Open methods.

If the provider does not support bookmarks, a RecordsetChange event notification occurs each time new rows are retrieved from the provider. The frequency of this event depends on the RecordsetCacheSize property.

You must set the adStatus parameter to adStatusUnwantedEvent for each possible adReason value in order to completely stop event notification for any event that includes an adReason parameter.

See Also

Visual C++ Example

ADO Event Handler Summary

Applies To: Recordset Object


WillConnect Event (ADO)

ADO API Reference

WillConnect Event

The WillConnect event is called before a connection starts.

Syntax

WillConnect ConnectionString, UserID, Password, Options, adStatus, pConnection

Parameters

ConnectionString
A String that contains connection information for the pending connection.
UserID
A String that contains a user name for the pending connection.
Password
A String that contains a password for the pending connection.
Options
A Long value that indicates how the provider should evaluate the ConnectionString. Your only option is adAsyncOpen.
adStatus
An EventStatusEnum status value.

When this event is called, this parameter is set to adStatusOK by default. It is set to adStatusCantDeny if the event cannot request cancellation of the pending operation.

Before this event returns, set this parameter to adStatusUnwantedEvent to prevent subsequent notifications. Set this parameter to adStatusCancel to request the connection operation that caused cancellation of this notification.

pConnection
The Connection object for which this event notification applies. Changes to the parameters of the Connection by the WillConnect event handler will have no effect on the Connection.

Remarks

When WillConnect is called, the ConnectionString, UserID, Password, and Options parameters are set to the values established by the operation that caused this event (the pending connection), and can be changed before the event returns. WillConnect may return a request that the pending connection be canceled.

When this event is canceled, ConnectComplete will be called with its adStatus parameter set to adStatusErrorsOccurred.

See Also

Visual C++ Example

ADO Event Handler Summary

Applies To: Connection Object


WillExecute Event (ADO)

ADO API Reference

WillExecute Event

The WillExecute event is called just before a pending command executes on a connection.

Syntax

WillExecute Source, CursorType, LockType, Options, adStatus, pCommand, pRecordset, pConnection

Parameters

Source
A String that contains an SQL command or a stored procedure name.
CursorType
A CursorTypeEnum that contains the type of cursor for the Recordset that will be opened. With this parameter, you can change the cursor to any type during a Recordset Open operation. CursorType will be ignored for any other operation.
LockType
A LockTypeEnum that contains the lock type for the Recordset that will be opened. With this parameter, you can change the lock to any type during a Recordset Open operation. LockType will be ignored for any other operation.
Options
A Long value that indicates options that can be used to execute the command or open the Recordset.
adStatus
An EventStatusEnum status value that may be adStatusCantDeny or adStatusOK when this event is called. If it is adStatusCantDeny, this event may not request cancellation of the pending operation.

Before this event returns, set this parameter to adStatusUnwantedEvent to prevent subsequent notifications, or adStatusCancel to request cancellation of the operation that caused this event.

pCommand
The Command object for which this event notification applies.
pRecordset
The Recordset object for which this event notification applies.
pConnection
The Connection object for which this event notification applies.

Remarks

A WillExecute event may occur due to a Connection. Execute, Command. Execute, or Recordset. Open method The pConnection parameter should always contain a valid reference to a Connection object. If the event is due to Connection.Execute, the pRecordset and pCommand parameters are set to Nothing. If the event is due to Recordset.Open, the pRecordset parameter will reference the Recordset object and the pCommand parameter is set to Nothing. If the event is due to Command.Execute, the pCommand parameter will reference the Command object and the pRecordset parameter is set to Nothing.

WillExecute allows you to examine and modify the pending execution parameters. This event may return a request that the pending command be canceled.

Note If the original source for a Command is a stream specified by the CommandStream property, assigning a new string to the WillExecute Source parameter changes the source of the Command. The CommandStream property will be cleared and the CommandText property will be updated with the new source. The original stream specified by CommandStream will be released and cannot be accessed.

If the dialect of the new source string differs from the original setting of the Dialect property (which corresponded to the CommandStream), the correct dialect must be specified by setting the Dialect property of the command object referenced by pCommand.

See Also

Visual C++ Example

ADO Event Handler Summary

Applies To: Connection Object


WillMove and MoveComplete Events (ADO)

ADO API Reference

WillMove and MoveComplete Events

The WillMove event is called before a pending operation changes the current position in the Recordset. The MoveComplete event is called after the current position in the Recordset changes.

Syntax

WillMove adReason, adStatus, pRecordset
MoveComplete adReason, pError, adStatus, pRecordset

Parameters

adReason
An EventReasonEnum value that specifies the reason for this event. Its value can be adRsnMoveFirst, adRsnMoveLast, adRsnMoveNext, adRsnMovePrevious, adRsnMove, or adRsnRequery.
pError
An Error object. It describes the error that occurred if the value of adStatus is adStatusErrorsOccurred; otherwise it is not set.
adStatus
An EventStatusEnum status value.

When WillMove is called, this parameter is set to adStatusOK if the operation that caused the event was successful. It is set to adStatusCantDeny if this event cannot request cancellation of the pending operation.

When MoveComplete is called, this parameter is set to adStatusOK if the operation that caused the event was successful, or to adStatusErrorsOccurred if the operation failed.

Before WillMove returns, set this parameter to adStatusCancel to request cancellation of the pending operation or set this parameter to adStatusUnwantedEvent to prevent subsequent notications.

Before MoveComplete returns, set this parameter to adStatusUnwantedEvent to prevent subsequent notifications.

pRecordset
A Recordset object. The Recordset for which this event occurred.

Remarks

A WillMove or MoveComplete event may occur due to the following Recordset operations: Open, Move, MoveFirst, MoveLast, MoveNext, MovePrevious, AddNew, and Requery. These events may occur because of the following properties: Filter, Index, Bookmark, AbsolutePage, and AbsolutePosition. These events also occur if a child Recordset has Recordset events connected and the parent Recordset is moved.

You must set the adStatus parameter to adStatusUnwantedEvent for each possible adReason value in order to completely stop event notification for any event that includes an adReason parameter.

See Also

Visual C++ Example

ADO Event Handler Summary

Applies To: Recordset Object


ADO Enumerated Constants

To assist in debugging, the ADO enumerations list a value for each constant. However, this value is purely advisory, and may change from one release of ADO to another. Your code should only depend on the name, not the actual value, of each enumerated constant.

ADCPROP_ASYNCTHREADPRIORITY_ENUM | For an RDS Recordset object, specifies the execution priority of the asynchronous thread that retrieves data.
ADCPROP_AUTORECALC_ENUM | Specifies when the MSDataShape provider re-calculates aggregate and calculated columns in a hierarchical Recordset.
ADCPROP_UPDATECRITERIA_ENUM | Specifies which fields can be used to detect conflicts during an optimistic update of a row of the data source with a Recordset object.
ADCPROP_UPDATERESYNC_ENUM | Specifies whether the UpdateBatch method is followed by an implicit Resync method operation and if so, the scope of that operation.
AffectEnum | Specifies which records are affected by an operation.
BookmarkEnum | Specifies a bookmark indicating where the operation should begin.
CommandTypeEnum | Specifies how a command argument should be interpreted.
CompareEnum | Specifies the relative position of two records represented by their bookmarks.
ConnectModeEnum | Specifies the available permissions for modifying data in a Connection, opening a Record, or specifying values for the Mode property of the Record and Stream objects.
ConnectOptionEnum | Specifies whether the Open method of a Connection object should return after (synchronously) or before (asynchronously) the connection is established.
ConnectPromptEnum | Specifies whether a dialog box should be displayed to prompt for missing parameters when opening a connection to an ODBC data source.
CopyRecordOptionsEnum | Specifies the behavior of the CopyRecord method.
CursorLocationEnum | Specifies the location of the cursor engine.
CursorOptionEnum | Specifies what functionality the Supports method should test for.
CursorTypeEnum | Specifies the type of cursor used in a Recordset object.
DataTypeEnum | Specifies the data type of a Field, Parameter, or Property.
EditModeEnum | Specifies the editing status of a record.
ErrorValueEnum | Specifies the type of ADO run-time error.
EventReasonEnum | Specifies the reason that caused an event to occur.
EventStatusEnum | Specifies the current status of the execution of an event.
ExecuteOptionEnum | Specifies how a provider should execute a command.
FieldEnum | Specifies the special fields referenced in a Record object's Fields collection.
FieldAttributeEnum | Specifies one or more attributes of a Field object.
FieldStatusEnum | Specifies the status of a Field object.
FilterGroupEnum | Specifies the group of records to be filtered from a Recordset.
GetRowsOptionEnum | Specifies how many records to retrieve from a Recordset.
IsolationLevelEnum | Specifies the level of transaction isolation for a Connection object.
LineSeparatorsEnum | Specifies the character used as a line separator in text Stream objects.
LockTypeEnum | Specifies the type of lock placed on records during editing.
MarshalOptionsEnum | Specifies which records should be returned to the server.
MoveRecordOptionsEnum | Specifies the behavior of the Record object MoveRecord method.
ObjectStateEnum | Specifies whether an object is open or closed, connecting to a data source, executing a command, or fetching data.
ParameterAttributesEnum | Specifies the attributes of a Parameter object.
ParameterDirectionEnum | Specifies whether the Parameter represents an input parameter, an output parameter, or both, or if the parameter is the return value from a stored procedure.
PersistFormatEnum | Specifies the format in which to save a Recordset.
PositionEnum | Specifies the current position of the record pointer within a Recordset.
PropertyAttributesEnum | Specifies the attributes of a Property object.
RecordCreateOptionsEnum | Specifies for the Record object Open method whether an existing Record should be opened, or a new Record should be created.
RecordOpenOptionsEnum | Specifies options for opening a Record. These values may be combined by using an OR operator.
RecordStatusEnum | Specifies the status of a record with regard to batch updates and other bulk operations.
RecordTypeEnum | Specifies the type of Record object.
ResyncEnum | Specifies whether underlying values are overwritten by a call to Resync.
SaveOptionsEnum | Specifies whether a file should be created or overwritten when saving from a Stream object. The values can be combined with an AND operator.
SchemaEnum | Specifies the type of schema Recordset that the OpenSchema method retrieves.Specifies the direction of a record search within a Recordset.
SearchDirectionEnum | Specifies the direction of a record search within a Recordset.Specifies the type of Seek to execute.
SeekEnum | Specifies the type of Seek to execute.Specifies options for opening a Stream object. The values can be combined with an AND operator.
StreamOpenOptionsEnum | Specifies options for opening a Stream object. The values can be combined with an AND operator.Specifies whether the whole stream or the next line should be read from a Stream object.
StreamReadEnum | Specifies whether the whole stream or the next line should be read from a Stream object.Specifies the type of data stored in a Stream object.
StreamTypeEnum | Specifies the type of data stored in a Stream object.Specifies whether a line separator is appended to the string written to a Stream object.
StreamWriteEnum | Specifies whether a line separator is appended to the string written to a Stream object.Specifies the format when retrieving a Recordset as a string.
StringFormatEnum | Specifies the format when retrieving a Recordset as a string.Specifies the transaction attributes of a Connection object.
XactAttributeEnum | Specifies the transaction attributes of a Connection object.

See Also

ADO API Reference | ADO Collections | ADO Dynamic Properties | ADO Errors | ADO Events | ADO Methods | ADO Object Model | ADO Objects | ADO Properties


ADCPROP_ASYNCTHREADPRIORITY_ENUM

For an RDS Recordset object, specifies the execution priority of the asynchronous thread that retrieves data.

Use these constants with the Recordset "Background Thread Priority" dynamic property, which is referenced in the ADO-to-OLE DB Dynamic Property index and documented in the Microsoft Cursor Service for OLE DB documentation.

Constant | Value | Description
adPriorityAboveNormal | 4 | Sets priority between normal and highest.
adPriorityBelowNormal | 2 | Sets priority between lowest and normal.
adPriorityHighest | 5 | Sets priority to the highest possible.
AdPriorityLowest | 1 | Sets priority to the lowest possible.
adPriorityNormal | 3 | Sets priority to normal.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.AdcPropAsyncThreadPriority.ABOVENORMAL
AdoEnums.AdcPropAsyncThreadPriority.BELOWNORMAL
AdoEnums.AdcPropAsyncThreadPriority.HIGHEST
AdoEnums.AdcPropAsyncThreadPriority.LOWEST
AdoEnums.AdcPropAsyncThreadPriority.NORMAL


ADCPROP_AUTORECALC_ENUM

Specifies when the MSDataShape provider re-calculates aggregate and calculated columns in a hierarchical Recordset.

These constants are only used with the MSDataShape provider and the Recordset "Auto Recalc" dynamic property, which is referenced in the ADO Dynamic Property Index and documented in the Microsoft Cursor Service for OLE DB or Microsoft Data Shaping Service for OLE DB documentation.

Constant | Value | Description
adRecalcAlways | 1 | Default. Recalculates whenever the MSDataShape provider determines values that the calculated columns depend upon have changed.
adRecalcUpFront | 0 | Calculates only when initially building the hierarchical Recordset.

ADO/WFC Equivalent

These constants do not have ADO/WFC equivalents.


ADCPROP_UPDATECRITERIA_ENUM

Specifies which fields can be used to detect conflicts during an optimistic update of a row of the data source with a Recordset object.

Use these constants with the Recordset "Update Criteria" dynamic property, which is referenced in the ADO Dynamic Property Index and documented in the Microsoft Cursor Service for OLE DB documentation.

Constant | Value | Description
adCriteriaAllCols | 1 | Detects conflicts if any column of the data source row has been changed.
adCriteriaKey | 0 | Detects conflicts if the key column of the data source row has been changed, which means that the row has been deleted.
adCriteriaTimeStamp | 3 | Detects conflicts if the timestamp of the data source row has been changed, which means the row has been accessed after the Recordset was obtained.
adCriteriaUpdCols | 2 | Detects conflicts if any of the columns of the data source row that correspond to updated fields of the Recordset have been changed.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.AdcPropUpdateCriteria.ALLCOLS
AdoEnums.AdcPropUpdateCriteria.KEY
AdoEnums.AdcPropUpdateCriteria.TIMESTAMP
AdoEnums.AdcPropUpdateCriteria.UPDCOLS


ADCPROP_UPDATERESYNC_ENUM

Specifies whether the UpdateBatch method is followed by an implicit Resync method operation and if so, the scope of that operation.

Constant | Value | Description
adResyncAll | 15 | Invokes Resync with the combined value of all the other ADCPROP_UPDATERESYNC_ENUM members.
adResyncAutoIncrement | 1 | Default. Attempts to retrieve the new identity value for columns that are automatically incremented or generated by the data source, such as Microsoft Jet AutoNumber fields or Microsoft SQL Server Identity columns.
adResyncConflicts | 2 | Invokes Resync for all rows in which the update or delete operation failed because of a concurrency conflict.
adResyncInserts | 8 | Invokes Resync for all successfully inserted rows. However, AutoIncrement column values are not resynchronized. Instead, the contents of newly inserted rows are resynchronized based on the existing primary key value. If the primary key is an AutoIncrement value, Resync won't retrieve the contents of the intended row. For automatically incrementing AutoIncrement primary key values, call UpdateBatch with the combined value adResyncAutoIncrement + adResyncInserts.
adResyncNone | 0 | Does not invoke Resync.
adResyncUpdates | 4 | Invokes Resync for all successfully updated rows.

See Also

Applies To: Update Resync Property—Dynamic (ADO)


AffectEnum

Specifies which records are affected by an operation.

Constant | Value | Description
adAffectAll 3 If there is not a Filter applied to the Recordset, affects all records.

If the Filter property is set to a string criteria (such as "Author='Smith'"), then the operation affects visible records in the current chapter.

If the Filter property is set to a member of the FilterGroupEnum or an array of Bookmarks, then the operation will affect all rows of the Recordset.

Note adAffectAll is hidden in the Visual Basic Object Browser.
adAffectAllChapters | 4 | Affects all records in all sibling chapters of the Recordset, including those not visible via any Filter that is currently applied.
adAffectCurrent | 1 | Affects only the current record.
adAffectGroup | 2 | Affects only records that satisfy the current Filter property setting. You must set the Filter property to a FilterGroupEnum value or an array of Bookmarks to use this option.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.Affect.ALL
AdoEnums.Affect.ALLCHAPTERS
AdoEnums.Affect.CURRENT
AdoEnums.Affect.GROUP

See Also

Applies To: CancelBatch Method | Delete Method (ADO Recordset) | Resync Method | UpdateBatch Method


BookmarkEnum

Specifies a bookmark indicating where the operation should begin.

Constant | Value | Description
adBookmarkCurrent | 0 | Starts at the current record.
adBookmarkFirst | 1 | Starts at the first record.
adBookmarkLast | 2 | Starts at the last record.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.Bookmark.CURRENT
AdoEnums.Bookmark.FIRST
AdoEnums.Bookmark.LAST

See Also

Applies To: GetRows Method | Move Method


CommandTypeEnum

Specifies how a command argument should be interpreted.

Constant | Value | Description
adCmdUnspecified | -1 | Does not specify the command type argument.
adCmdText | 1 | Evaluates CommandText as a textual definition of a command or stored procedure call.
adCmdTable | 2 | Evaluates CommandText as a table name whose columns are all returned by an internally generated SQL query.
adCmdStoredProc | 4 | Evaluates CommandText as a stored procedure name.
adCmdUnknown | 8 | Default. Indicates that the type of command in the CommandText property is not known.
adCmdFile | 256 | Evaluates CommandText as the file name of a persistently stored Recordset. Used with Recordset. Open or Requery only.
adCmdTableDirect | 512 | Evaluates CommandText as a table name whose columns are all returned. Used with Recordset.Open or Requery only. To use the Seek method, the Recordset must be opened with adCmdTableDirect.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.CommandType.UNSPECIFIED
AdoEnums.CommandType.TEXT
AdoEnums.CommandType.TABLE
AdoEnums.CommandType.STOREDPROC
AdoEnums.CommandType.UNKNOWN
AdoEnums.CommandType.FILE
AdoEnums.CommandType.TABLEDIRECT

See Also

Applies To: CommandType Property | Execute Method (ADO Command) | Execute Method (ADO Connection) | Open Method (ADO Recordset) | Requery Method


CompareEnum

Specifies the relative position of two records represented by their bookmarks.

Constant | Value | Description
adCompareEqual | 1 | Indicates that the bookmarks are equal.
adCompareGreaterThan | 2 | Indicates that the first bookmark is after the second.
adCompareLessThan | 0 | Indicates that the first bookmark is before the second.
adCompareNotComparable | 4 | Indicates that the bookmarks cannot be compared.
adCompareNotEqual | 3 | Indicates that the bookmarks are not equal and not ordered.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.Compare.EQUAL
AdoEnums.Compare.GREATERTHAN
AdoEnums.Compare.LESSTHAN
AdoEnums.Compare.NOTCOMPARABLE
AdoEnums.Compare.NOTEQUAL

See Also

Applies To: CompareBookmarks Method


ConnectModeEnum

Specifies the available permissions for modifying data in a Connection, opening a Record, or specifying values for the Mode property of the Record and Stream objects.

Constant | Value | Description
adModeRead | 1 | Indicates read-only permissions.
adModeReadWrite | 3 | Indicates read/write permissions.
adModeRecursive | 0x400000 | Used in conjunction with the other *ShareDeny* values (adModeShareDenyNone, adModeShareDenyWrite, or adModeShareDenyRead) to propagate sharing restrictions to all sub-records of the current Record. It has no affect if the Record does not have any children. A run-time error is generated if it is used with adModeShareDenyNone only. However, it can be used with adModeShareDenyNone when combined with other values. For example, you can use "adModeRead Or adModeShareDenyNone Or adModeRecursive".
adModeShareDenyNone | 16 | Allows others to open a connection with any permissions. Neither read nor write access can be denied to others.
adModeShareDenyRead | 4 | Prevents others from opening a connection with read permissions.
adModeShareDenyWrite | 8 | Prevents others from opening a connection with write permissions.
adModeShareExclusive | 12 | Prevents others from opening a connection.
adModeUnknown | 0 | Default. Indicates that the permissions have not yet been set or cannot be determined.
adModeWrite | 2 | Indicates write-only permissions.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.ConnectMode.READ
AdoEnums.ConnectMode.READWRITE
(There is no equivalent of AdoEnums.ConnectMode.RECURSIVE)
AdoEnums.ConnectMode.SHAREDENYNONE
AdoEnums.ConnectMode.SHAREDENYREAD
AdoEnums.ConnectMode.SHAREDENYWRITE
AdoEnums.ConnectMode.SHAREEXCLUSIVE
AdoEnums.ConnectMode.UNKNOWN
AdoEnums.ConnectMode.WRITE

See Also

Applies To: Mode Property | Open Method (ADO Record) | Open Method (ADO Stream) | Stream Object


ConnectOptionEnum

Specifies whether the Open method of a Connection object should return after (synchronously) or before (asynchronously) the connection is established.

Constant | Value | Description
adAsyncConnect | 16 | Opens the connection asynchronously. The ConnectComplete event may be used to determine when the connection is available.
adConnectUnspecified | -1 | Default. Opens the connection synchronously.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.ConnectOption.ASYNCCONNECT
AdoEnums.ConnectOption.CONNECTUNSPECIFIED

See Also

Applies To: Open Method (ADO Connection)


ConnectPromptEnum

Specifies whether a dialog box should be displayed to prompt for missing parameters when opening a connection to a data source.

Constant | Value | Description
adPromptAlways | 1 | Prompts always.
adPromptComplete | 2 | Prompts if more information is required.
adPromptCompleteRequired | 3 | Prompts if more information is required but optional parameters are not allowed.
adPromptNever | 4 | Never prompts.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.ConnectPrompt.ALWAYS
AdoEnums.ConnectPrompt.COMPLETE
AdoEnums.ConnectPrompt.COMPLETEREQUIRED
AdoEnums.ConnectPrompt.NEVER

See Also

Applies To: Prompt Property—Dynamic (ADO)


CopyRecordOptionsEnum

Specifies the behavior of the CopyRecord method.

Constant | Value | Description
adCopyAllowEmulation | 4 | Indicates that the Source provider attempts to simulate the copy using download and upload operations if this method fails due to Destination being on a different server or is serviced by a different provider than Source. Note that differing provider capabilities may hamper performance or lose data.
adCopyNonRecursive | 2 | Copies the current directory, but none of its subdirectories, to the destination. The copy operation is not recursive.
adCopyOverWrite | 1 | Overwrites the file or directory if the Destination points to an existing file or directory.
adCopyUnspecified | -1 | Default. Performs the default copy operation: The operation fails if the destination file or directory already exists, and the operation copies recursively.

ADO/WFC Equivalent

These constants do not have ADO/WFC equivalents.

See Also

Applies To: CopyRecord Method


CursorLocationEnum

Specifies the location of the cursor service.

Constant | Value | Description
adUseClient | 3 | Uses client-side cursors supplied by a local cursor library. Local cursor services often will allow many features that driver-supplied cursors may not, so using this setting may provide an advantage with respect to features that will be enabled. For backward compatibility, the synonym adUseClientBatch is also supported.
adUseNone | 1 | Does not use cursor services. (This constant is obsolete and appears solely for the sake of backward compatibility.)
adUseServer | 2 | Default. Uses data-provider or driver-supplied cursors. These cursors are sometimes very flexible and allow for additional sensitivity to changes others make to the data source. However, some features of the Microsoft Cursor Service for OLE DB (such as disassociated Recordset objects) cannot be simulated with server-side cursors and these features will be unavailable with this setting.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.CursorLocation.CLIENT
AdoEnums.CursorLocation.NONE
AdoEnums.CursorLocation.SERVER

See Also

Applies To: CursorLocation Property


CursorOptionEnum

Specifies what functionality the Supports method should test for.

Constant | Value | Description
adAddNew | 0x1000400 | Supports the AddNew method to add new records.
adApproxPosition | 0x4000 | Supports the AbsolutePosition and AbsolutePage properties.
adBookmark | 0x2000 | Supports the Bookmark property to gain access to specific records.
adDelete | 0x1000800 | Supports the Delete method to delete records.
adFind | 0x80000 | Supports the Find method to locate a row in a Recordset.
adHoldRecords | 0x100 | Retrieves more records or changes the next position without committing all pending changes.
adIndex | 0x100000 | Supports the Index property to name an index.
adMovePrevious | 0x200 | Supports the MoveFirst and MovePrevious methods, and Move or GetRows methods to move the current record position backward without requiring bookmarks.
adNotify | 0x40000 | Indicates that the underlying data provider supports notifications (which determines whether Recordset events are supported).
adResync | 0x20000 | Supports the Resync method to update the cursor with the data that is visible in the underlying database.
adSeek | 0x200000 | Supports the Seek method to locate a row in a Recordset.
adUpdate | 0x1008000 | Supports the Update method to modify existing data.
adUpdateBatch | 0x10000 | Supports batch updating (UpdateBatch and CancelBatch methods) to transmit groups of changes to the provider.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.CursorOption.ADDNEW
AdoEnums.CursorOption.APPROXPOSITION
AdoEnums.CursorOption.BOOKMARK
AdoEnums.CursorOption.DELETE
AdoEnums.CursorOption.FIND
AdoEnums.CursorOption.HOLDRECORDS
AdoEnums.CursorOption.INDEX
AdoEnums.CursorOption.MOVEPREVIOUS
AdoEnums.CursorOption.NOTIFY
AdoEnums.CursorOption.RESYNC
AdoEnums.CursorOption.SEEK
AdoEnums.CursorOption.UPDATE
AdoEnums.CursorOption.UPDATEBATCH

See Also

Applies To: Supports Method


CursorTypeEnum

Specifies the type of cursor used in a Recordset object.

Constant | Value | Description
adOpenDynamic | 2 | Uses a dynamic cursor. Additions, changes, and deletions by other users are visible, and all types of movement through the Recordset are allowed, except for bookmarks, if the provider doesn't support them.
adOpenForwardOnly | 0 | Default. Uses a forward-only cursor. Identical to a static cursor, except that you can only scroll forward through records. This improves performance when you need to make only one pass through a Recordset.
adOpenKeyset | 1 | Uses a keyset cursor. Like a dynamic cursor, except that you can't see records that other users add, although records that other users delete are inaccessible from your Recordset. Data changes by other users are still visible.
adOpenStatic | 3 | Uses a static cursor. A static copy of a set of records that you can use to find data or generate reports. Additions, changes, or deletions by other users are not visible.
adOpenUnspecified | -1 | Does not specify the type of cursor.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.CursorType.DYNAMIC
AdoEnums.CursorType.FORWARDONLY
AdoEnums.CursorType.KEYSET
AdoEnums.CursorType.STATIC
AdoEnums.CursorType.UNSPECIFIED

See Also

Applies To: CursorType Property


DataTypeEnum

Specifies the data type of a Field, Parameter, or Property. The corresponding OLE DB type indicator is shown in parentheses in the description column of the following table. For more information about OLE DB data types, see Chapter 13 and Appendix A of the OLE DB Programmer's Reference.

Constant | Value | Description
AdArray (Does not apply to ADOX.) | 0x2000 | A flag value, always combined with another data type constant, that indicates an array of that other data type.
adBigInt | 20 | Indicates an eight-byte signed integer (DBTYPE_I8).
adBinary | 128 | Indicates a binary value (DBTYPE_BYTES).
adBoolean | 11 | Indicates a boolean value (DBTYPE_BOOL).
adBSTR | 8 | Indicates a null-terminated character string (Unicode) (DBTYPE_BSTR).
adChapter | 136 | Indicates a four-byte chapter value that identifies rows in a child rowset (DBTYPE_HCHAPTER).
adChar | 129 | Indicates a string value (DBTYPE_STR).
adCurrency | 6 | Indicates a currency value (DBTYPE_CY). 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.
adDate | 7 | Indicates a date value (DBTYPE_DATE). A date is stored as 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.
adDBDate | 133 | Indicates a date value (yyyymmdd) (DBTYPE_DBDATE).
adDBTime | 134 | Indicates a time value (hhmmss) (DBTYPE_DBTIME).
adDBTimeStamp | 135 | Indicates a date/time stamp (yyyymmddhhmmss plus a fraction in billionths) (DBTYPE_DBTIMESTAMP).
adDecimal | 14 | Indicates an exact numeric value with a fixed precision and scale (DBTYPE_DECIMAL).
adDouble | 5 | Indicates a double-precision floating-point value (DBTYPE_R8).
adEmpty | 0 | Specifies no value (DBTYPE_EMPTY).
adError | 10 | Indicates a 32-bit error code (DBTYPE_ERROR).
adFileTime | 64 | Indicates a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (DBTYPE_FILETIME).
adGUID | 72 | Indicates a globally unique identifier (GUID) (DBTYPE_GUID).
adIDispatch | 9 | Indicates a pointer to an IDispatch interface on a COM object (DBTYPE_IDISPATCH). Note This data type is currently not supported by ADO. Usage may cause unpredictable results.
adInteger | 3 | Indicates a four-byte signed integer (DBTYPE_I4).
adIUnknown | 13 | Indicates a pointer to an IUnknown interface on a COM object (DBTYPE_IUNKNOWN). Note This data type is currently not supported by ADO. Usage may cause unpredictable results.
adLongVarBinary | 205 | Indicates a long binary value (Parameter object only).
adLongVarChar | 201 | Indicates a long string value (Parameter object only).
adLongVarWChar | 203 | Indicates a long null-terminated Unicode string value (Parameter object only).
adNumeric | 131 | Indicates an exact numeric value with a fixed precision and scale (DBTYPE_NUMERIC).
adPropVariant | 138 | Indicates an Automation PROPVARIANT (DBTYPE_PROP_VARIANT).
adSingle | 4 | Indicates a single-precision floating-point value (DBTYPE_R4).
adSmallInt | 2 | Indicates a two-byte signed integer (DBTYPE_I2).
adTinyInt | 16 | Indicates a one-byte signed integer (DBTYPE_I1).
adUnsignedBigInt | 21 | Indicates an eight-byte unsigned integer (DBTYPE_UI8).
adUnsignedInt | 19 | Indicates a four-byte unsigned integer (DBTYPE_UI4).
adUnsignedSmallInt | 18 | Indicates a two-byte unsigned integer (DBTYPE_UI2).
adUnsignedTinyInt | 17 | Indicates a one-byte unsigned integer (DBTYPE_UI1).
adUserDefined | 132 | Indicates a user-defined variable (DBTYPE_UDT).
adVarBinary | 204 | Indicates a binary value (Parameter object only).
adVarChar | 200 | Indicates a string value (Parameter object only).
adVariant | 12 | Indicates an Automation Variant (DBTYPE_VARIANT). Note This data type is currently not supported by ADO. Usage may cause unpredictable results.
adVarNumeric | 139 | Indicates a numeric value (Parameter object only).
adVarWChar | 202 | Indicates a null-terminated Unicode character string (Parameter object only).
adWChar | 130 | Indicates a null-terminated Unicode character string (DBTYPE_WSTR).

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.DataType.ARRAY
AdoEnums.DataType.BIGINT
AdoEnums.DataType.BINARY
AdoEnums.DataType.BOOLEAN
AdoEnums.DataType.BSTR
AdoEnums.DataType.CHAPTER
AdoEnums.DataType.CHAR
AdoEnums.DataType.CURRENCY
AdoEnums.DataType.DATE
AdoEnums.DataType.DBDATE
AdoEnums.DataType.DBTIME
AdoEnums.DataType.DBTIMESTAMP
AdoEnums.DataType.DECIMAL
AdoEnums.DataType.DOUBLE
AdoEnums.DataType.EMPTY
AdoEnums.DataType.ERROR
AdoEnums.DataType.FILETIME
AdoEnums.DataType.GUID
AdoEnums.DataType.IDISPATCH
AdoEnums.DataType.INTEGER
AdoEnums.DataType.IUNKNOWN
AdoEnums.DataType.LONGVARBINARY
AdoEnums.DataType.LONGVARCHAR
AdoEnums.DataType.LONGVARWCHAR
AdoEnums.DataType.NUMERIC
AdoEnums.DataType.PROPVARIANT
AdoEnums.DataType.SINGLE
AdoEnums.DataType.SMALLINT
AdoEnums.DataType.TINYINT
AdoEnums.DataType.UNSIGNEDBIGINT
AdoEnums.DataType.UNSIGNEDINT
AdoEnums.DataType.UNSIGNEDSMALLINT
AdoEnums.DataType.UNSIGNEDTINYINT
AdoEnums.DataType.USERDEFINED
AdoEnums.DataType.VARBINARY
AdoEnums.DataType.VARCHAR
AdoEnums.DataType.VARIANT
AdoEnums.DataType.VARNUMERIC
AdoEnums.DataType.VARWCHAR
AdoEnums.DataType.WCHAR

See Also

Applies To: Append Method | CreateParameter Method | CreateRecordset Method (RDS) | Type Property


EditModeEnum

Specifies the editing status of a record.

Constant | Value | Description
adEditNone | 0 | Indicates that no editing operation is in progress.
adEditInProgress | 1 | Indicates that data in the current record has been modified but not saved.
adEditAdd | 2 | Indicates that the AddNew method has been called, and the current record in the copy buffer is a new record that has not been saved in the database.
adEditDelete | 4 | Indicates that the current record has been deleted.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.EditMode.NONE
AdoEnums.EditMode.INPROGRESS
AdoEnums.EditMode.ADD
AdoEnums.EditMode.DELETE

See Also

Applies To: EditMode Property


ErrorValueEnum

Specifies the type of ADO run-time error.

Three forms of the error number are listed:

  • Positive decimal—the low two bytes of the full number in decimal format. This number is displayed in the default Visual Basic error message dialog box. For example, Run-time error '3707'.
  • Negative decimal—The decimal translation of the full error number.
  • Hexadecimal—The hexadecimal representation of the full error number. The Windows facility code is in the fourth digit. The facility code for ADO error numbers is A. For example: 0x800A0E7B.

Note OLE DB errors may be passed to your ADO application. Typically, these can be identified by a Windows facility code of 4. For example, 0x8004.... For more information about these numbers, see Chapter 16 of the OLE DB Programmer's Reference.

Constant | Value | Description
adErrBoundToCommand | 3707 -2146824581 0x800A0E7B | Cannot change the ActiveConnection property of a Recordset object which has a Command object as its source.
adErrCannotComplete | 3732 -2146824556 0x800A0E94 | Server cannot complete the operation.
adErrCantChangeConnection | 3748 -2146824540 0x800A0EA4 | Connection was denied. New connection you requested has different characteristics than the one already in use.
adErrCantChangeProvider | 3220 -2146825068 0X800A0C94 | Supplied provider is different from the one already in use.
adErrCantConvertvalue | 3724 -2146824564 0x800A0E8C | Data value cannot be converted for reasons other than sign mismatch or data overflow. For example, conversion would have truncated data.
adErrCantCreate | 3725 -2146824563 0x800A0E8D | Data value cannot be set or retrieved because the field data type was unknown, or the provider had insufficient resources to perform the operation.
adErrCatalogNotSet | 3747 -2146824541 0x800A0EA3 | Operation requires a valid ParentCatalog.
adErrColumnNotOnThisRow | 3726 -2146824562 0x800A0E8E | Record does not contain this field.
adErrDataConversion | 3421 -2146824867 0x800A0D5D | Application uses a value of the wrong type for the current operation.
adErrDataOverflow | 3721 -2146824567 0x800A0E89 | Data value is too large to be represented by the field data type.
adErrDelResOutOfScope | 3738 -2146824550 0x800A0E9A | URL of the object to be deleted is outside the scope of the current record.
adErrDenyNotSupported | 3750 -2146824538 0x800A0EA6 | Provider does not support sharing restrictions.
adErrDenyTypeNotSupported | 3751 -2146824537 0x800A0EA7 | Provider does not support the requested kind of sharing restriction.
adErrFeatureNotAvailable | 3251 -2146825037 0x800A0CB3 | Object or provider is not capable of performing requested operation.
adErrFieldsUpdateFailed | 3749 -2146824539 0x800A0EA5 | Fields update failed. For further information, examine the Status property of individual field objects.
adErrIllegalOperation | 3219 -2146825069 0x800A0C93 | Operation is not allowed in this context.
adErrIntegrityViolation | 3719 -2146824569 0x800A0E87 | Data value conflicts with the integrity constraints of the field.
adErrInTransaction | 3246 -2146825042 0x800A0CAE | Connection object cannot be explicitly closed while in a transaction.
adErrInvalidArgument | 3001 -2146825287 0x800A0BB9 | Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another.
adErrInvalidConnection | 3709 -2146824579 0x800A0E7D | The connection cannot be used to perform this operation. It is either closed or invalid in this context.
adErrInvalidParamInfo | 3708 -2146824580 0x800A0E7C | Parameter object is improperly defined. Inconsistent or incomplete information was provided.
adErrInvalidTransaction | 3714 -2146824574 0x800A0E82 | Coordinating transaction is invalid or has not started.
adErrInvalidURL | 3729 -2146824559 0x800A0E91 | URL contains invalid characters. Make sure the URL is typed correctly.
adErrItemNotFound | 3265 -2146825023 0x800A0CC1 | Item cannot be found in the collection corresponding to the requested name or ordinal.
adErrNoCurrentRecord | 3021 -2146825267 0x800A0BCD | Either BOF or EOF is True, or the current record has been deleted. Requested operation requires a current record.
adErrNotExecuting | 3715 -2146824573 0x800A0E83 | Operation cannot be performed while not executing.
adErrNotReentrant | 3710 -2146824578 0x800A0E7E | Operation cannot be performed while processing event.
adErrObjectClosed | 3704 -2146824584 0x800A0E78 | Operation is not allowed when the object is closed.
adErrObjectInCollection | 3367 -2146824921 0x800A0D27 | Object is already in collection. Cannot append.
adErrObjectNotSet | 3420 -2146824868 0x800A0D5C | Object is no longer valid.
adErrObjectOpen | 3705 -2146824583 0x800A0E79 | Operation is not allowed when the object is open.
adErrOpeningFile | 3002 -2146825286 0x800A0BBA | File could not be opened.
adErrOperationCancelled | 3712 -2146824576 0x800A0E80 | Operation has been cancelled by the user.
adErrOutOfSpace | 3734 -2146824554 0x800A0E96 | Operation cannot be performed. Provider cannot obtain enough storage space.
adErrPermissionDenied | 3720 -2146824568 0x800A0E88 | Insufficent permission prevents writing to the field.
adErrProviderFailed | 3000 -2146825288 0x800A0BB8 | Provider failed to perform the requested operation.
adErrProviderNotFound | 3706 -2146824582 0x800A0E7A | Provider cannot be found. It may not be properly installed.
adErrReadFile | 3003 -2146825285 0x800A0BBB | File could not be read.
adErrResourceExists | 3731 -2146824557 0x800A0E93 | Copy operation cannot be performed. Object named by destination URL already exists. Specify adCopyOverwrite to replace the object.
adErrResourceLocked | 3730 -2146824558 0x800A0E92 | Object represented by the specified URL is locked by one or more other processes. Wait until the process has finished and attempt the operation again.
adErrResourceOutOfScope | 3735 -2146824553 0x800A0E97 | Source or destination URL is outside the scope of the current record.
adErrSchemaViolation | 3722 -2146824566 0x800A0E8A | Data value conflicts with the data type or constraints of the field.
adErrSignMismatch | 3723 -2146824565 0x800A0E8B | Conversion failed because the data value was signed and the field data type used by the provider was unsigned.
adErrStillConnecting | 3713 -2146824575 0x800A0E81 | Operation cannot be performed while connecting aynchronously.
adErrStillExecuting | 3711 -2146824577 0x800A0E7F | Operation cannot be performed while executing asynchronously.
adErrTreePermissionDenied | 3728 -2146824560 0x800A0E90 | Permissions are insufficient to access tree or subtree.
adErrUnavailable | 3736 -2146824552 0x800A0E98 | Operation failed to complete and the status is unavailable. The field may be unavailable or the operation was not attempted.
adErrUnsafeOperation | 3716 -2146824572 0x800A0E84 | Safety settings on this computer prohibit accessing a data source on another domain.
adErrURLDoesNotExist | 3727 -2146824561 0x800A0E8F | Either the source URL or the parent of the destination URL does not exist.
adErrURLNamedRowDoesNotExist | 3737 -2146824551 0x800A0E99 | Record named by this URL does not exist.
adErrVolumeNotFound | 3733 -2146824555 0x800A0E95 | Provider cannot locate the storage device indicated by the URL. Make sure the URL is typed correctly.
adErrWriteFile | 3004 -2146825284 0x800A0BBC | Write to file failed.
adWrnSecurityDialog | 3717 -2146824571 0x800A0E85 | For internal use only. Don't use.
adWrnSecurityDialogHeader | 3718 -2146824570 0x800A0E86 | For internal use only. Don't use.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Only the following subsets of ADO/WFC equivalents are defined.

Constant
AdoEnums.ErrorValue.BOUNDTOCOMMAND
AdoEnums.ErrorValue.DATACONVERSION
AdoEnums.ErrorValue.FEATURENOTAVAILABLE
AdoEnums.ErrorValue.ILLEGALOPERATION
AdoEnums.ErrorValue.INTRANSACTION
AdoEnums.ErrorValue.INVALIDARGUMENT
AdoEnums.ErrorValue.INVALIDCONNECTION
AdoEnums.ErrorValue.INVALIDPARAMINFO
AdoEnums.ErrorValue.ITEMNOTFOUND
AdoEnums.ErrorValue.NOCURRENTRECORD
AdoEnums.ErrorValue.NOTEXECUTING
AdoEnums.ErrorValue.NOTREENTRANT
AdoEnums.ErrorValue.OBJECTCLOSED
AdoEnums.ErrorValue.OBJECTINCOLLECTION
AdoEnums.ErrorValue.OBJECTNOTSET
AdoEnums.ErrorValue.OBJECTOPEN
AdoEnums.ErrorValue.OPERATIONCANCELLED
AdoEnums.ErrorValue.PROVIDERNOTFOUND
AdoEnums.ErrorValue.STILLCONNECTING
AdoEnums.ErrorValue.STILLEXECUTING
AdoEnums.ErrorValue.UNSAFEOPERATION

See Also

ADO Error Codes

Applies To: Number Property


EventReasonEnum

Specifies the reason that caused an event to occur.

Constant | Value | Description
adRsnAddNew | 1 | An operation added a new record.
adRsnClose | 9 | An operation closed the Recordset.
adRsnDelete | 2 | An operation deleted a record.
adRsnFirstChange | 11 | An operation made the first change to a record.
adRsnMove | 10 | An operation moved the record pointer within the Recordset.
adRsnMoveFirst | 12 | An operation moved the record pointer to the first record in the Recordset.
adRsnMoveLast | 15 | An operation moved the record pointer to the last record in the Recordset.
adRsnMoveNext | 13 | An operation moved the record pointer to the next record in the Recordset.
adRsnMovePrevious | 14 | An operation moved the record pointer to the previous record in the Recordset.
adRsnRequery | 7 | An operation requeried the Recordset.
adRsnResynch | 8 | An operation resynchronized the Recordset with the database.
adRsnUndoAddNew | 5 | An operation reversed the addition of a new record.
adRsnUndoDelete | 6 | An operation reversed the deletion of a record.
adRsnUndoUpdate | 4 | An operation reversed the update of a record.
adRsnUpdate | 3 | An operation updated an existing record.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.EventReason.ADDNEW
AdoEnums.EventReason.CLOSE
AdoEnums.EventReason.DELETE
AdoEnums.EventReason.FIRSTCHANGE
AdoEnums.EventReason.MOVE
AdoEnums.EventReason.MOVEFIRST
AdoEnums.EventReason.MOVELAST
AdoEnums.EventReason.MOVENEXT
AdoEnums.EventReason.MOVEPREVIOUS
AdoEnums.EventReason.REQUERY
AdoEnums.EventReason.RESYNCH
AdoEnums.EventReason.UNDOADDNEW
AdoEnums.EventReason.UNDODELETE
AdoEnums.EventReason.UNDOUPDATE
AdoEnums.EventReason.UPDATE

See Also

Applies To: WillChangeRecord and RecordChangeComplete Events | WillChangeRecordset and RecordsetChangeComplete Events | WillMove and MoveComplete Events


EventStatusEnum

Specifies the current status of the execution of an event.

Constant | Value | Description
adStatusCancel | 4 | Requests cancellation of the operation that caused the event to occur.
adStatusCantDeny | 3 | Indicates that the operation cannot request cancellation of the pending operation.
adStatusErrorsOccurred | 2 | Indicates that the operation that caused the event failed due to an error or errors.
adStatusOK | 1 | Indicates that the operation that caused the event was successful.
adStatusUnwantedEvent | 5 | Prevents subsequent notifications before the event method has finished executing.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.EventStatus.CANCEL
AdoEnums.EventStatus.CANTDENY
AdoEnums.EventStatus.ERRORSOCCURRED
AdoEnums.EventStatus.OK
AdoEnums.EventStatus.UNWANTEDEVENT

See Also

Applies To: BeginTransComplete, CommitTransComplete, and RollbackTransComplete Events | ConnectComplete and Disconnect Events | EndOfRecordset Event | ExecuteComplete Event | FetchComplete Event | InfoMessage Event | WillChangeField and FieldChangeComplete Events | WillChangeRecord and RecordChangeComplete Events | WillChangeRecordset and RecordsetChangeComplete Events | WillConnect Event | WillExecute Event | WillMove and MoveComplete Events


ExecuteOptionEnum

Specifies how a provider should execute a command.

Constant | Value | Description
adAsyncExecute | 0x10 | Indicates that the command should execute asynchronously.
adAsyncFetch | 0x20 | Indicates that the remaining rows after the initial quantity specified in the CacheSize property should be retrieved asynchronously.
adAsyncFetchNonBlocking | 0x40 | Indicates that the main thread never blocks while retrieving. If the requested row has not been retrieved, the current row automatically moves to the end of the file. If you open a Recordset from a Stream containing a persistently stored Recordset, adAsyncFetchNonBlocking will not have an effect; the operation will be synchronous and blocking. adAsynchFetchNonBlocking has no effect when the adCmdTableDirect option is used to open the Recordset.
adExecuteNoRecords | 0x80 | Indicates that the command text is a command or stored procedure that does not return rows (for example, a command that only inserts data). If any rows are retrieved, they are discarded and not returned. adExecuteNoRecords can only be passed as an optional parameter to the Command or Connection Execute method.
adExecuteStream | 0x400 | Indicates that the results of a command execution should be returned as a stream. adExecuteStream can only be passed as an optional parameter to the Command Execute method.
adExecuteRecord |  | Indicates that the CommandText is a command or stored procedure that returns a single row which should be returned as a Record object.
adOptionUnspecified | -1 | Indicates that the command is unspecified.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.ExecuteOption.ASYNCEXECUTE
AdoEnums.ExecuteOption.ASYNCFETCH
AdoEnums.ExecuteOption.ASYNCFETCHNONBLOCKING
AdoEnums.ExecuteOption.NORECORDS
AdoEnums.ExecuteOption.UNSPECIFIED

See Also

Applies To: Execute Method (ADO Command) | Execute Method (ADO Connection) | Open Method (ADO Recordset) | Requery Method


FieldEnum

Specifies the special fields referenced in a Record object's Fields collection.

Remarks

These constants provide a "shortcut" to accessing special fields associated with a Record. Retrieve the Field object from the Fields collection, and then obtain its contents with the Field object's Value property.

Constant | Value | Description
adDefaultStream | -1 | References the field containing the default Stream object associated with a Record.
adRecordURL | -2 | References the field containing the absolute URL string for the current Record.


FieldAttributeEnum

Specifies one or more attributes of a Field object.

Constant | Value | Description
adFldCacheDeferred | 0x1000 | Indicates that the provider caches field values and that subsequent reads are done from the cache.
adFldFixed | 0x10 | Indicates that the field contains fixed-length data.
adFldIsChapter | 0x2000 | Indicates that the field contains a chapter value, which specifies a specific child recordset related to this parent field. Typically chapter fields are used with data shaping or filters.
adFldIsCollection | 0x40000 | Indicates that the field specifies that the resource represented by the record is a collection of other resources, such as a folder, rather than a simple resource, such as a text file.
adFldIsDefaultStream | 0x20000 | Indicates that the field contains the default stream for the resource represented by the record. For example, the default stream can be the HTML content of a root folder on a Web site, which is automatically served when the root URL is specified.
adFldIsNullable | 0x20 | Indicates that the field accepts null values.
adFldIsRowURL | 0x10000 | Indicates that the field contains the URL that names the resource from the data store represented by the record.
adFldLong | 0x80 | Indicates that the field is a long binary field. Also indicates that you can use the AppendChunk and GetChunk methods.
adFldMayBeNull | 0x40 | Indicates that you can read null values from the field.
adFldMayDefer | 0x2 | Indicates that the field is deferred—that is, the field values are not retrieved from the data source with the whole record, but only when you explicitly access them.
adFldNegativeScale | 0x4000 | Indicates that the field represents a numeric value from a column that supports negative scale values. The scale is specified by the NumericScale property.
adFldRowID | 0x100 | Indicates that the field contains a persistent row identifier that cannot be written to and has no meaningful value except to identify the row (such as a record number, unique identifier, and so forth).
adFldRowVersion | 0x200 | Indicates that the field contains some kind of time or date stamp used to track updates.
adFldUnknownUpdatable | 0x8 | Indicates that the provider cannot determine if you can write to the field.
adFldUnspecified | -1 0xFFFFFFFF | Indicates that the provider does not specify the field attributes.
adFldUpdatable | 0x4 | Indicates that you can write to the field.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.FieldAttribute.CACHEDEFERRED
AdoEnums.FieldAttribute.FIXED
AdoEnums.FieldAttribute.ISNULLABLE
AdoEnums.FieldAttribute.LONG
AdoEnums.FieldAttribute.MAYBENULL
AdoEnums.FieldAttribute.MAYDEFER
AdoEnums.FieldAttribute.NEGATIVESCALE
AdoEnums.FieldAttribute.ROWID
AdoEnums.FieldAttribute.ROWVERSION
AdoEnums.FieldAttribute.UNKNOWNUPDATABLE
AdoEnums.FieldAttribute.UNSPECIFIED
AdoEnums.FieldAttribute.UPDATABLE

See Also

Applies To: Append Method | Attributes Property


FieldStatusEnum

Specifies the status of a Field object.

The adFieldPending* values indicate the operation that caused the status to be set, and may be combined with other status values.

Constant | Value | Description
adFieldAlreadyExists | 26 | Indicates that the specified field already exists.
adFieldBadStatus | 12 | Indicates that an invalid status value was sent from ADO to the OLE DB provider. Possible causes include an OLE DB 1.0 or 1.1 provider, or an improper combination of Value and Status.
adFieldCannotComplete | 20 | Indicates that the server of the URL specified by Source could not complete the operation.
adFieldCannotDeleteSource | 23 | Indicates that during a move operation, a tree or subtree was moved to a new location, but the source could not be deleted.
adFieldCantConvertValue | 2 | Indicates that the field cannot be retrieved or stored without loss of data.
adFieldCantCreate | 7 | Indicates that the field could not be added because the provider exceeded a limitation (such as the number of fields allowed).
adFieldDataOverflow | 6 | Indicates that the data returned from the provider overflowed the data type of the field.
adFieldDefault | 13 | Indicates that the default value for the field was used when setting data.
adFieldDoesNotExist | 16 | Indicates that the field specified does not exist.
adFieldIgnore | 15 | Indicates that this field was skipped when setting data values in the source. No value was set by the provider.
adFieldIntegrityViolation | 10 | Indicates that the field cannot be modified because it is a calculated or derived entity.
adFieldInvalidURL | 17 | Indicates that the data source URL contains invalid characters.
adFieldIsNull | 3 | Indicates that the provider returned a null value.
adFieldOK | 0 | Default. Indicates that the field was successfully added or deleted.
adFieldOutOfSpace | 22 | Indicates that the provider is unable to obtain enough storage space to complete a move or copy operation.
adFieldPendingChange | 0x40000 | Indicates either that the field has been deleted and then re-added, perhaps with a different data type, or that the value of the field which previously had a status of adFieldOK has changed. The final form of the field will modify the Fields collection after the Update method is called.
adFieldPendingDelete | 0x20000 | Indicates that the Delete operation caused the status to be set. The field has been marked for deletion from the Fields collection after the Update method is called.
adFieldPendingInsert | 0x10000 | Indicates that the Append operation caused the status to be set. The Field has been marked to be added to the Fields collection after the Update method is called.
adFieldPendingUnknown | 0x80000 | Indicates that the provider cannot determine what operation caused field status to be set.
adFieldPendingUnknownDelete | 0x100000 | Indicates that the provider cannot determine what operation caused field status to be set, and that the field will be deleted from the Fields collection after the Update method is called.
adFieldPermissionDenied | 9 | Indicates that the field cannot be modified because it is defined as read-only.
adFieldReadOnly | 24 | Indicates that the field in the data source is defined as read-only.
adFieldResourceExists | 19 | Indicates that the provider was unable to perform the operation because an object already exists at the destination URL and it is not able to overwrite the object.
adFieldResourceLocked | 18 | Indicates that the provider was unable to perform the operation because the data source is locked by one or more other application or process.
adFieldResourceOutOfScope | 25 | Indicates that a source or destination URL is outside the scope of the current record.
adFieldSchemaViolation | 11 | Indicates that the value violated the data source schema constraint for the field.
adFieldSignMismatch | 5 | Indicates that data value returned by the provider was signed but the data type of the ADO field value was unsigned.
adFieldTruncated | 4 | Indicates that variable-length data was truncated when reading from the data source.
adFieldUnavailable | 8 | Indicates that the provider could not determine the value when reading from the data source. For example, the row was just created, the default value for the column was not available, and a new value had not yet been specified.
adFieldVolumeNotFound | 21 | Indicates that the provider is unable to locate the storage volume indicated by the URL.

ADO/WFC Equivalent

These constants do not have ADO/WFC equivalents.

See Also

Applies To: Status Property (ADO Field)


FilterGroupEnum

Specifies the group of records to be filtered from a Recordset.

Constant | Value | Description
adFilterAffectedRecords | 2 | Filters for viewing only records affected by the last Delete, Resync, UpdateBatch, or CancelBatch call.
adFilterConflictingRecords | 5 | Filters for viewing the records that failed the last batch update.
adFilterFetchedRecords | 3 | Filters for viewing the records in the current cache—that is, the results of the last call to retrieve records from the database.
adFilterNone | 0 | Removes the current filter and restores all records for viewing.
adFilterPendingRecords | 1 | Filters for viewing only records that have changed but have not yet been sent to the server. Applicable only for batch update mode.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.FilterGroup.AFFECTEDRECORDS
AdoEnums.FilterGroup.CONFLICTINGRECORDS
AdoEnums.FilterGroup.FETCHEDRECORDS
AdoEnums.FilterGroup.NONE
AdoEnums.FilterGroup.PENDINGRECORDS

See Also

Applies To: Filter Property


GetRowsOptionEnum

Specifies how many records to retrieve from a Recordset.

Constant | Value | Description
adGetRowsRest | -1 | Retrieves the rest of the records in the Recordset, from either the current position or a bookmark specified by the Start parameter of the GetRows method.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.GetRowsOption.REST

See Also

Applies To: GetRows Method


IsolationLevelEnum

Specifies the level of transaction isolation for a Connection object.

Constant | Value | Description
adXactUnspecified | -1 | Indicates that the provider is using a different isolation level than specified, but that the level cannot be determined.
adXactChaos | 16 | Indicates that pending changes from more highly isolated transactions cannot be overwritten.
adXactBrowse | 256 | Indicates that from one transaction you can view uncommitted changes in other transactions.
adXactReadUncommitted | 256 | Same as adXactBrowse.
adXactCursorStability | 4096 | Indicates that from one transaction you can view changes in other transactions only after they have been committed.
adXactReadCommitted | 4096 | Same as adXactCursorStability.
adXactRepeatableRead | 65536 | Indicates that from one transaction you cannot see changes made in other transactions, but that requerying can retrieve new Recordset objects.
adXactIsolated | 1048576 | Indicates that transactions are conducted in isolation of other transactions.
adXactSerializable | 1048576 | Same as adXactIsolated.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.IsolationLevel.UNSPECIFIED
AdoEnums.IsolationLevel.CHAOS
AdoEnums.IsolationLevel.BROWSE
AdoEnums.IsolationLevel.READUNCOMMITTED
AdoEnums.IsolationLevel.CURSORSTABILITY
AdoEnums.IsolationLevel.READCOMMITTED
AdoEnums.IsolationLevel.REPEATABLEREAD
AdoEnums.IsolationLevel.ISOLATED
AdoEnums.IsolationLevel.SERIALIZABLE

See Also

Applies To: IsolationLevel Property


LineSeparatorsEnum

Specifies the character used as a line separator in text Stream objects.

Constant | Value | Description
adCR | 13 | Indicates carriage return.
adCRLF | -1 | Default. Indicates carriage return line feed.
adLF | 10 | Indicates line feed.

ADO/WFC Equivalent

These constants do not have ADO/WFC equivalents.

See Also

Applies To: LineSeparator Property


LockTypeEnum

Specifies the type of lock placed on records during editing.

Constant | Value | Description
adLockBatchOptimistic | 4 | Indicates optimistic batch updates. Required for batch update mode.
adLockOptimistic | 3 | Indicates optimistic locking, record by record. The provider uses optimistic locking, locking records only when you call the Update method.
adLockPessimistic | 2 | Indicates pessimistic locking, record by record. The provider does what is necessary to ensure successful editing of the records, usually by locking records at the data source immediately after editing.
adLockReadOnly | 1 | Indicates read-only records. You cannot alter the data.
adLockUnspecified | -1 | Does not specify a type of lock. For clones, the clone is created with the same lock type as the original.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.LockType.BATCHOPTIMISTIC
AdoEnums.LockType.OPTIMISTIC
AdoEnums.LockType.PESSIMISTIC
AdoEnums.LockType.READONLY
AdoEnums.LockType.UNSPECIFIED

See Also

Applies To: Clone Method | LockType Property | Open Method (ADO Recordset) | WillExecute Event


MarshalOptionsEnum

Specifies which records should be returned to the server.

Constant | Value | Description
adMarshalAll | 0 | Default. Returns all rows to the server.
adMarshalModifiedOnly | 1 | Returns only modified rows to the server.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.MarshalOptions.ALL
AdoEnums.MarshalOptions.MODIFIEDONLY

See Also

Applies To: MarshalOptions Property


MoveRecordOptionsEnum

Specifies the behavior of the Record object MoveRecord method.

Constant | Value | Description
adMoveUnspecified | -1 | Default. Performs the default move operation: The operation fails if the destination file or directory already exists, and the operation updates hypertext links.
adMoveOverWrite | 1 | Overwrites the destination file or directory, even if it already exists.
adMoveDontUpdateLinks | 2 | Modifies the default behavior of MoveRecord method by not updating the hypertext links of the source Record. The default behavior depends on the capabilities of the provider. Move operation updates links if the provider is capable. If the provider cannot fix links or if this value is not specified, then the move succeeds even when links have not been fixed.
adMoveAllowEmulation | 4 | Requests that the provider attempt to simulate the move (using download, upload, and delete operations). If the attempt to move the Record fails because the destination URL is on a different server or serviced by a different provider than the source, this may cause increased latency or data loss, due to different provider capabilities when moving resources between providers.

ADO/WFC Equivalent

These constants do not have ADO/WFC equivalents.

See Also

Applies To: MoveRecord Method


ObjectStateEnum

Specifies whether an object is open or closed, connecting to a data source, executing a command, or retrieving data.

Constant | Value | Description
adStateClosed | 0 | Indicates that the object is closed.
adStateOpen | 1 | Indicates that the object is open.
adStateConnecting | 2 | Indicates that the object is connecting.
adStateExecuting | 4 | Indicates that the object is executing a command.
adStateFetching | 8 | Indicates that the rows of the object are being retrieved.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.ObjectState.CLOSED
AdoEnums.ObjectState.OPEN
AdoEnums.ObjectState.CONNECTING
AdoEnums.ObjectState.EXECUTING
AdoEnums.ObjectState.FETCHING

See Also

Applies To: State Property | State Property


ParameterAttributesEnum

Specifies the attributes of a Parameter object.

Constant | Value | Description
adParamSigned | 16 | Indicates that the parameter accepts signed values.
adParamNullable | 64 | Indicates that the parameter accepts null values.
adParamLong | 128 | Indicates that the parameter accepts long binary data.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.ParameterAttributes.SIGNED
AdoEnums.ParameterAttributes.NULLABLE
AdoEnums.ParameterAttributes.LONG

See Also

Applies To: Attributes Property


ParameterDirectionEnum

Specifies whether the Parameter represents an input parameter, an output parameter, both an input and an output parameter, or the return value from a stored procedure.

Constant | Value | Description
adParamInput | 1 | Default. Indicates that the parameter represents an input parameter.
adParamInputOutput | 3 | Indicates that the parameter represents both an input and output parameter.
adParamOutput | 2 | Indicates that the parameter represents an output parameter.
adParamReturnValue | 4 | Indicates that the parameter represents a return value.
adParamUnknown | 0 | Indicates that the parameter direction is unknown.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.ParameterDirection.INPUT
AdoEnums.ParameterDirection.INPUTOUTPUT
AdoEnums.ParameterDirection.OUTPUT
AdoEnums.ParameterDirection.RETURNVALUE
AdoEnums.ParameterDirection.UNKNOWN

See Also

Applies To: CreateParameter Method | Direction Property


PersistFormatEnum

Specifies the format in which to save a Recordset.

Constant | Value | Description
adPersistADTG | 0 | Indicates Microsoft Advanced Data TableGram (ADTG) format.
adPersistADO | 1 | Indicates that ADO's own Extensible Markup Language (XML) format will be used. This value is the same as adPersistXML and is included for backwards compatibility.
adPersistXML | 1 | Indicates Extensible Markup Language (XML) format.
adPersistProviderSpecific | 2 | Indicates that the provider will persist the Recordset using its own format.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.PersistFormat.ADTG
AdoEnums.PersistFormat.XML

See Also

Applies To: Save Method


PositionEnum

Specifies the current position of the record pointer within a Recordset.

Constant | Value | Description
adPosBOF | -2 | Indicates that the current record pointer is at BOF (that is, the BOF property is True).
adPosEOF | -3 | Indicates that the current record pointer is at EOF (that is, the EOF property is True).
adPosUnknown | -1 | Indicates that the Recordset is empty, the current position is unknown, or the provider does not support the AbsolutePage or AbsolutePosition property.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.Position.BOF
AdoEnums.Position.EOF
AdoEnums.Position.UNKNOWN

See Also

Applies To: AbsolutePage Property | AbsolutePosition Property


PropertyAttributesEnum

Specifies the attributes of a Property object.

Constant | Value | Description
adPropNotSupported | 0 | Indicates that the property is not supported by the provider.
adPropRequired | 1 | Indicates that the user must specify a value for this property before the data source is initialized.
adPropOptional | 2 | Indicates that the user does not need to specify a value for this property before the data source is initialized.
adPropRead | 512 | Indicates that the user can read the property.
adPropWrite | 1024 | Indicates that the user can set the property.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.PropertyAttributes.NOTSUPPORTED
AdoEnums.PropertyAttributes.REQUIRED
AdoEnums.PropertyAttributes.OPTIONAL
AdoEnums.PropertyAttributes.READ
AdoEnums.PropertyAttributes.WRITE

See Also

Applies To: Attributes Property


RecordCreateOptionsEnum

Specifies whether an existing Record should be opened or a new Record created for the Record object Open method. The values can be combined with an AND operator.

Constant | Value | Description
adCreateCollection | 0x2000 | Creates a new Record at the node specified by Source parameter, instead of opening an existing Record. If the source points to an existing node, then a run-time error occurs, unless adCreateCollection is combined with adOpenIfExists or adCreateOverwrite.
adCreateNonCollection | 0 | Creates a new Record of type adSimpleRecord.
adCreateOverwrite | 0x4000000 | Modifies the creation flags adCreateCollection, adCreateNonCollection, and adCreateStructDoc. When OR is used with this value and one of the creation flag values, if the source URL points to an existing node or Record, then the existing Record is overwritten and a new one is created in its place. This value cannot be used together with adOpenIfExists.
adCreateStructDoc | 0x80000000 | Creates a new Record of type adStructDoc, instead of opening an existing Record.
adFailIfNotExists | -1 | Default. Results in a run-time error if Source points to a non-existent node.
adOpenIfExists | 0x2000000 | Modifies the creation flags adCreateCollection, adCreateNonCollection, and adCreateStructDoc. When OR is used with this value and one of the creation flag values, if the source URL points to an existing node or Record object, then the provider must open the existing Record instead of creating a new one. This value cannot be used together with adCreateOverwrite.

ADO/WFC Equivalent

These constants do not have ADO/WFC equivalents.

See Also

Applies To: Open Method (ADO Record)


RecordOpenOptionsEnum

Specifies options for opening a Record. These values may be combined by using OR.

Constant | Value | Description
adDelayFetchFields | 0x8000 | Indicates to the provider that the fields associated with the Record need not be retrieved initially, but can be retrieved at the first attempt to access the field. The default behavior, indicated by the absence of this flag, is to retrieve all the Record object fields.
adDelayFetchStream | 0x4000 | Indicates to the provider that the default stream associated with the Record need not be retrieved initially. The default behavior, indicated by the absence of this flag, is to retrieve the default stream associated with the Record object.
adOpenAsync | 0x1000 | Indicates that the Record object is opened in asynchronous mode.
adOpenExecuteCommand | 0x10000 | Indicates that the Source string contains command text that should be executed. This value is equivalent to the adCmdText option on Recordset.Open.
adOpenRecordUnspecified | -1 | Default. Indicates no options are specified.
adOpenOutput | 0x800000 | Indicates that if the source points to a node that contains an executable script (such as an .ASP page), then the opened Record will contain the results of the executed script. This value is only valid with non-collection records.

ADO/WFC Equivalent

These constants do not have ADO/WFC equivalents.

See Also

Applies To: Open Method (ADO Record)


RecordStatusEnum

Specifies the status of a record with regard to batch updates and other bulk operations.

Constant | Value | Description
adRecCanceled | 0x100 | Indicates that the record was not saved because the operation was canceled.
adRecCantRelease | 0x400 | Indicates that the new record was not saved because the existing record was locked.
adRecConcurrencyViolation | 0x800 | Indicates that the record was not saved because optimistic concurrency was in use.
adRecDBDeleted | 0x40000 | Indicates that the record has already been deleted from the data source.
adRecDeleted | 0x4 | Indicates that the record was deleted.
adRecIntegrityViolation | 0x1000 | Indicates that the record was not saved because the user violated integrity constraints.
adRecInvalid | 0x10 | Indicates that the record was not saved because its bookmark is invalid.
adRecMaxChangesExceeded | 0x2000 | Indicates that the record was not saved because there were too many pending changes.
adRecModified | 0x2 | Indicates that the record was modified.
adRecMultipleChanges | 0x40 | Indicates that the record was not saved because it would have affected multiple records.
adRecNew | 0x1 | Indicates that the record is new.
adRecObjectOpen | 0x4000 | Indicates that the record was not saved because of a conflict with an open storage object.
adRecOK | 0 | Indicates that the record was successfully updated.
adRecOutOfMemory | 0x8000 | Indicates that the record was not saved because the computer has run out of memory.
adRecPendingChanges | 0x80 | Indicates that the record was not saved because it refers to a pending insert.
adRecPermissionDenied | 0x10000 | Indicates that the record was not saved because the user has insufficient permissions.
adRecSchemaViolation | 0x20000 | Indicates that the record was not saved because it violates the structure of the underlying database.
adRecUnmodified | 0x8 | Indicates that the record was not modified.

ADO/WFC Equivalent

AdoEnums.RecordStatus.

Package: com.ms.wfc.data

Constant
AdoEnums.RecordStatus.CANCELED
AdoEnums.RecordStatus.CANTRELEASE
AdoEnums.RecordStatus.CONCURRENCYVIOLATION
AdoEnums.RecordStatus.DBDELETED
AdoEnums.RecordStatus.DELETED
AdoEnums.RecordStatus.INTEGRITYVIOLATION
AdoEnums.RecordStatus.INVALID
AdoEnums.RecordStatus.MAXCHANGESEXCEEDED
AdoEnums.RecordStatus.MODIFIED
AdoEnums.RecordStatus.MULTIPLECHANGES
AdoEnums.RecordStatus.NEW
AdoEnums.RecordStatus.OBJECTOPEN
AdoEnums.RecordStatus.OK
AdoEnums.RecordStatus.OUTOFMEMORY
AdoEnums.RecordStatus.PENDINGCHANGES
AdoEnums.RecordStatus.PERMISSIONDENIED
AdoEnums.RecordStatus.SCHEMAVIOLATION
AdoEnums.RecordStatus.UNMODIFIED

See Also

Applies To: Status Property (ADO Recordset)


RecordTypeEnum

Specifies the type of Record object.

Constant | Value | Description
adSimpleRecord | 0 | Indicates a simple record (does not contain child nodes).
adCollectionRecord | 1 | Indicates a collection record (contains child nodes).
adRecordUnknown | -1 | Indicates that the type of this Record is unknown.
adStructDoc | 2 | Indicates a special kind of collection record that represents COM structured documents.

ADO/WFC Equivalent

These constants do not have ADO/WFC equivalents.

See Also

Applies To: RecordType Property


ResyncEnum

Specifies whether underlying values are overwritten by a call to Resync.

Constant | Value | Description
adResyncAllValues | 2 | Default. Overwrites data, and pending updates are canceled.
adResyncUnderlyingValues | 1 | Does not overwrite data, and pending updates are not canceled.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.Resync.ALLVALUES
AdoEnums.Resync.UNDERLYINGVALUES

See Also

Applies To: Resync Method


SaveOptionsEnum

Specifies whether a file should be created or overwritten when saving from a Stream object. The values can be combined with an AND operator.

Constant | Value | Description
adSaveCreateNotExist | 1 | Default. Creates a new file if the file specified by the FileName parameter does not already exist.
adSaveCreateOverWrite | 2 | Overwrites the file with the data from the currently open Stream object, if the file specified by the Filename parameter already exists.

ADO/WFC Equivalent

These constants do not have ADO/WFC equivalents.

See Also

Applies To: SaveToFile Method


SchemaEnum

Specifies the type of schema Recordset that the OpenSchema method retrieves.

Remarks

Additional information about the function and columns returned for each ADO constant can be found in topics of Appendix B of the OLE DB Programmer’s Reference. The name of each topic is listed in parentheses in the Description section of the table below.

Additional information about the function and columns returned for each ADO MD constant can be found in topics of Chapter 23 of the OLE DB for OLAP documentation. The name of each topic is listed in parentheses and marked with an asterisk (*) in the Description column of the table below.

Translate the data types of columns in the OLE DB documentation to ADO data types by referring to the Description column of the ADO DataTypeEnum topic. For example, an OLE DB data type of DBTYPE_WSTR is equivalent to an ADO data type of adWChar.

ADO generates schema-like results for the constants, adSchemaDBInfoKeywords and adSchemaDBInfoLiterals. ADO creates a Recordset, then fills each row with the values returned respectively by the IDBInfo::GetKeywords and IDBInfo::GetLiteralInfo methods. Additional information about these methods can be found in the IDBInfo section of the OLE DB Programmer's Reference.

Constant | Value | Description | Constraint Columns
adSchemaAsserts | 0 | Returns the assertions defined in the catalog that are owned by a given user. (ASSERTIONS Rowset) | CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME
adSchemaCatalogs | 1 | Returns the physical attributes associated with catalogs accessible from the DBMS. (CATALOGS Rowset) | CATALOG_NAME
adSchemaCharacterSets | 2 | Returns the character sets defined in the catalog that are accessible to a given user. (CHARACTER_SETS Rowset) | CHARACTER_SET_CATALOG CHARACTER_SET_SCHEMA CHARACTER_SET_NAME
adSchemaCheckConstraints | 5 | Returns the check constraints defined in the catalog that are owned by a given user. (CHECK_CONSTRAINTS Rowset) | CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME
adSchemaCollations | 3 | Returns the character collations defined in the catalog that are accessible to a given user. (COLLATIONS Rowset) | COLLATION_CATALOG COLLATION_SCHEMA COLLATION_NAME
adSchemaColumnPrivileges | 13 | Returns the privileges on columns of tables defined in the catalog that are available to, or granted by, a given user. (COLUMN_PRIVILEGES Rowset) | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME GRANTOR GRANTEE
adSchemaColumns | 4 | Returns the columns of tables (including views) defined in the catalog that are accessible to a given user. (COLUMNS Rowset) | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME
adSchemaColumnsDomainUsage | 11 | Returns the columns defined in the catalog that are dependent on a domain defined in the catalog and owned by a given user. (COLUMN_DOMAIN_USAGE Rowset) | DOMAIN_CATALOG DOMAIN_SCHEMA DOMAIN_NAME COLUMN_NAME
adSchemaConstraintColumnUsage | 6 | Returns the columns used by referential constraints, unique constraints, check constraints, and assertions, defined in the catalog and owned by a given user. (CONSTRAINT_COLUMN_USAGE Rowset) | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME
adSchemaConstraintTableUsage | 7 | Returns the tables that are used by referential constraints, unique constraints, check constraints, and assertions defined in the catalog and owned by a given user. (CONSTRAINT_TABLE_USAGE Rowset) | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME
adSchemaCubes | 32 | Returns information about the available cubes in a schema (or the catalog, if the provider does not support schemas). (CUBES Rowset*) | CATALOG_NAME SCHEMA_NAME CUBE_NAME
adSchemaDBInfoKeywords | 30 | Returns a list of provider-specific keywords. (IDBInfo::GetKeywords *) | <None>
adSchemaDBInfoLiterals | 31 | Returns a list of provider-specific literals used in text commands. (IDBInfo::GetLiteralInfo *) | <None>
adSchemaDimensions | 33 | Returns information about the dimensions in a given cube. It has one row for each dimension. (DIMENSIONS Rowset *) | CATALOG_NAME SCHEMA_NAME CUBE_NAME DIMENSION_NAME DIMENSION_UNIQUE_NAME
adSchemaForeignKeys | 27 | Returns the foreign key columns defined in the catalog by a given user. (FOREIGN_KEYS Rowset) | PK_TABLE_CATALOG PK_TABLE_SCHEMA PK_TABLE_NAME FK_TABLE_CATALOG FK_TABLE_SCHEMA FK_TABLE_NAME
adSchemaHierarchies | 34 | Returns information about the hierarchies available in a dimension. (HIERARCHIES Rowset *) | CATALOG_NAME SCHEMA_NAME CUBE_NAME DIMENSION_UNIQUE_NAME HIERARCHY_NAME HIERARCHY_UNIQUE_NAME
adSchemaIndexes | 12 | Returns the indexes defined in the catalog that are owned by a given user. (INDEXES Rowset) | TABLE_CATALOG TABLE_SCHEMA INDEX_NAME TYPE TABLE_NAME
adSchemaKeyColumnUsage | 8 | Returns the columns defined in the catalog that are constrained as keys by a given user. (KEY_COLUMN_USAGE Rowset) | CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME
adSchemaLevels | 35 | Returns information about the levels available in a dimension. (LEVELS Rowset*) | CATALOG_NAME SCHEMA_NAME CUBE_NAME DIMENSION_UNIQUE_NAME HIERARCHY_UNIQUE_NAME LEVEL_NAME LEVEL_UNIQUE_NAME
adSchemaMeasures | 36 | Returns information about the available measures. (MEASURES Rowset *) | CATALOG_NAME SCHEMA_NAME CUBE_NAME MEASURE_NAME MEASURE_UNIQUE_NAME
adSchemaMembers | 38 | Returns information about the available members. (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 MEMBER_TYPE Tree operator (For more information, see the OLE DB for OLAP documentation.)
adSchemaPrimaryKeys | 28 | Returns the primary key columns defined in the catalog by a given user. (PRIMARY_KEYS Rowset) | PK_TABLE_CATALOG PK_TABLE_SCHEMA PK_TABLE_NAME
adSchemaProcedureColumns | 29 | Returns information about the columns of rowsets returned by procedures. (PROCEDURE_COLUMNS Rowset) | PROCEDURE_CATALOG PROCEDURE_SCHEMA PROCEDURE_NAME COLUMN_NAME
adSchemaProcedureParameters | 26 | Returns information about the parameters and return codes of procedures. (PROCEDURE_PARAMETERS Rowset) | PROCEDURE_CATALOG PROCEDURE_SCHEMA PROCEDURE_NAME PARAMETER_NAME
adSchemaProcedures | 16 | Returns the procedures defined in the catalog that are owned by a given user. (PROCEDURES Rowset) | PROCEDURE_CATALOG PROCEDURE_SCHEMA PROCEDURE_NAME PROCEDURE_TYPE
adSchemaProperties | 37 | Returns information about the available properties for each level of the dimension. (PROPERTIES Rowset *) | CATALOG_NAME SCHEMA_NAME CUBE_NAME DIMENSION_UNIQUE_NAME HIERARCHY_UNIQUE_NAME LEVEL_UNIQUE_NAME MEMBER_UNIQUE_NAME PROPERTY_TYPE PROPERTY_NAME
adSchemaProviderSpecific | -1 | Used if the provider defines its own nonstandard schema queries. | <Provider specific>
adSchemaProviderTypes | 22 | Returns the (base) data types supported by the data provider. (PROVIDER_TYPES Rowset) | DATA_TYPE BEST_MATCH
AdSchemaReferentialConstraints | 9 | Returns the referential constraints defined in the catalog that are owned by a given user. (REFERENTIAL_CONSTRAINTS Rowset) | CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME
adSchemaSchemata | 17 | Returns the schemas (database objects) that are owned by a given user. (SCHEMATA Rowset) | CATALOG_NAME SCHEMA_NAME SCHEMA_OWNER
adSchemaSQLLanguages | 18 | Returns the conformance levels, options, and dialects supported by the SQL-implementation processing data defined in the catalog. (SQL_LANGUAGES Rowset) | <None>
adSchemaStatistics | 19 | Returns the statistics defined in the catalog that are owned by a given user. (STATISTICS Rowset) | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME
adSchemaTableConstraints | 10 | Returns the table constraints defined in the catalog that are owned by a given user. (TABLE_CONSTRAINTS Rowset) | CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME CONSTRAINT_TYPE
adSchemaTablePrivileges | 14 | Returns the privileges on tables defined in the catalog that are available to, or granted by, a given user. (TABLE_PRIVILEGES Rowset) | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME GRANTOR GRANTEE
adSchemaTables | 20 | Returns the tables (including views) defined in the catalog that are accessible to a given user. (TABLES Rowset) | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE
adSchemaTranslations | 21 | Returns the character translations defined in the catalog that are accessible to a given user. (TRANSLATIONS Rowset) | TRANSLATION_CATALOG TRANSLATION_SCHEMA TRANSLATION_NAME
adSchemaTrustees | 39 | Reserved for future use. |
adSchemaUsagePrivileges | 15 | Returns the USAGE privileges on objects defined in the catalog that are available to, or granted by, a given user. (USAGE_PRIVILEGES Rowset) | OBJECT_CATALOG OBJECT_SCHEMA OBJECT_NAME OBJECT_TYPE GRANTOR GRANTEE
adSchemaViewColumnUsage | 24 | Returns the columns on which viewed tables, defined in the catalog and owned by a given user, are dependent. (VIEW_COLUMN_USAGE Rowset) | VIEW_CATALOG VIEW_SCHEMA VIEW_NAME
adSchemaViews | 23 | Returns the views defined in the catalog that are accessible to a given user. (VIEWS Rowset) | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME
adSchemaViewTableUsage | 25 | Returns the tables on which viewed tables, defined in the catalog and owned by a given user, are dependent. (VIEW_TABLE_USAGE Rowset) | VIEW_CATALOG VIEW_SCHEMA VIEW_NAME

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.Schema.ASSERTS
AdoEnums.Schema.CATALOGS
AdoEnums.Schema.CHARACTERSETS
AdoEnums.Schema.CHECKCONSTRAINTS
AdoEnums.Schema.COLLATIONS
AdoEnums.Schema.COLUMNPRIVILEGES
AdoEnums.Schema.COLUMNS
AdoEnums.Schema.COLUMNSDOMAINUSAGE
AdoEnums.Schema.CONSTRAINTCOLUMNUSAGE
AdoEnums.Schema.CONSTRAINTTABLEUSAGE
AdoEnums.Schema.CUBES
AdoEnums.Schema.DBINFOKEYWORDS
AdoEnums.Schema.DBINFOLITERALS
AdoEnums.Schema.DIMENSIONS
AdoEnums.Schema.FOREIGNKEYS
AdoEnums.Schema.HIERARCHIES
AdoEnums.Schema.INDEXES
AdoEnums.Schema.KEYCOLUMNUSAGE
AdoEnums.Schema.LEVELS
AdoEnums.Schema.MEASURES
AdoEnums.Schema.MEMBERS
AdoEnums.Schema.PRIMARYKEYS
AdoEnums.Schema.PROCEDURECOLUMNS
AdoEnums.Schema.PROCEDUREPARAMETERS
AdoEnums.Schema.PROCEDURES
AdoEnums.Schema.PROPERTIES
AdoEnums.Schema.PROVIDERSPECIFIC
AdoEnums.Schema.PROVIDERTYPES
AdoEnums.Schema.REFERENTIALCONTRAINTS
AdoEnums.Schema.SCHEMATA
AdoEnums.Schema.SQLLANGUAGES
AdoEnums.Schema.STATISTICS
AdoEnums.Schema.TABLECONSTRAINTS
AdoEnums.Schema.TABLEPRIVILEGES
AdoEnums.Schema.TABLES
AdoEnums.Schema.TRANSLATIONS
AdoEnums.Schema.TRUSTEES
AdoEnums.Schema.USAGEPRIVILEGES
AdoEnums.Schema.VIEWCOLUMNUSAGE
AdoEnums.Schema.VIEWS
AdoEnums.Schema.VIEWTABLEUSAGE

See Also

Applies To: OpenSchema Method


SearchDirectionEnum

Specifies the direction of a record search within a Recordset.

Constant | Value | Description
adSearchBackward | -1 | Searches backward, stopping at the beginning of the Recordset. If a match is not found, the record pointer is positioned at BOF.
adSearchForward | 1 | Searches forward, stopping at the end of the Recordset. If a match is not found, the record pointer is positioned at EOF.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.SearchDirection.BACKWARD
AdoEnums.SearchDirection.FORWARD

See Also

Applies To: Find Method


SeekEnum

Specifies the type of Seek to execute.

Constant | Value | Description
adSeekFirstEQ | 1 | Seeks the first key equal to KeyValues.
adSeekLastEQ | 2 | Seeks the last key equal to KeyValues.
adSeekAfterEQ | 4 | Seeks either a key equal to KeyValues or just after where that match would have occurred.
adSeekAfter | 8 | Seeks a key just after where a match with KeyValues would have occurred.
adSeekBeforeEQ | 16 | Seeks either a key equal to KeyValues or just before where that match would have occurred.
adSeekBefore | 32 | Seeks a key just before where a match with KeyValues would have occurred.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.Seek.FIRSTEQ
AdoEnums.Seek.LASTEQ
AdoEnums.Seek.AFTEREQ
AdoEnums.Seek.AFTER
AdoEnums.Seek.BEFOREEQ
AdoEnums.Seek.BEFORE

See Also

Applies To: Seek Method


StreamOpenOptionsEnum

Specifies options for opening a Stream object. The values can be combined with an OR operation.

Constant | Value | Description
adOpenStreamAsync | 1 | Opens the Stream object in asynchronous mode.
adOpenStreamFromRecord | 4 | Identifies the contents of the Source parameter to be an already open Record object. The default behavior is to treat Source as a URL that points directly to a node in a tree structure. The default stream associated with that node is opened.
adOpenStreamUnspecified | -1 | Default. Specifies opening the Stream object with default options.

ADO/WFC Equivalent

These constants do not have ADO/WFC equivalents.

See Also

Applies To: Open Method (ADO Stream)


StreamReadEnum

Specifies whether the whole stream or the next line should be read from a Stream object.

Constant | Value | Description
adReadAll | -1 | Default. Reads all bytes from the stream, from the current position onwards to the EOS marker. This is the only valid StreamReadEnum value with binary streams (Type is adTypeBinary).
adReadLine | -2 | Reads the next line from the stream (designated by the LineSeparator property).

ADO/WFC Equivalent

These constants do not have ADO/WFC equivalents.

See Also

Applies To: Read Method | ReadText Method


StreamTypeEnum

Specifies the type of data stored in a Stream object.

Constant | Value | Description
adTypeBinary | 1 | Indicates binary data.
adTypeText | 2 | Default. Indicates text data, which is in the character set specified by Charset.

ADO/WFC Equivalent

These constants do not have ADO/WFC equivalents.

See Also

Applies To: Type Property (ADO Stream)


StreamWriteEnum

Specifies whether a line separator is appended to the string written to a Stream object.

Constant | Value | Description
adWriteChar | 0 | Default. Writes the specified text string (specified by the Data parameter) to the Stream object.
adWriteLine | 1 | Writes a text string and a line separator character to a Stream object. If the LineSeparator property is not defined, then this returns a run-time error.

ADO/WFC Equivalent

These constants do not have ADO/WFC equivalents.

See Also

Applies To: WriteText Method


StringFormatEnum

Specifies the format when retrieving a Recordset as a string.

Constant | Value | Description
adClipString | 2 | Delimits rows by RowDelimiter, columns by ColumnDelimiter, and null values by NullExpr. These three parameters of the GetString method are valid only with a StringFormat of adClipString.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.StringFormat.CLIPSTRING

See Also

Applies To: GetString Method


XactAttributeEnum

Specifies the transaction attributes of a Connection object.

Constant | Value | Description
adXactAbortRetaining | 262144 | Performs retaining aborts—that is, calling RollbackTrans automatically starts a new transaction. Not all providers support this.
adXactCommitRetaining | 131072 | Performs retaining commits—that is, calling CommitTrans automatically starts a new transaction. Not all providers support this.

ADO/WFC Equivalent

Package: com.ms.wfc.data

Constant
AdoEnums.XactAttribute.ABORTRETAINING
AdoEnums.XactAttribute.COMMITRETAINING

See Also

Applies To: Attributes Property


ADO Syntax Indexes

The syntax for calling ADO methods and properties differs depending upon your development environment. The rest of the ADO Language Reference uses the Microsoft Visual Basic programming language to illustrate ADO method and property syntax. However, refer to the sections below for more specific syntax examples based on your programming language and methodology.

  • The ADO for Visual C++ Syntax Index for COM covers ADO properties and methods without using the #import compiler directive with Microsoft Visual C++.
  • The ADO for Visual C++ Syntax Index with #import covers ADO properties and methods when using the #import compiler directive with Microsoft Visual C++.
  • The ADO/WFC Syntax Index covers using the ADO Windows Foundation Classes with Visual J++.

See Also

Using ADO with Microsoft Visual Basic | Using ADO with Microsoft Visual C++ | Using ADO with Microsoft Visual J++ | Using ADO with Scripting Languages


ADO for Visual C++ Syntax Index for COM

This index is a cross-reference to the ADO Language Reference based on Microsoft Visual C++.

If you use the #import directive in your application, a header file will be generated that will enable you to use syntax similar to Microsoft Visual Basic. Property names of the form get_PropertyName and put_PropertyName can be treated as if they were declared simply as PropertyName. A property can then be treated like a data member instead of a function.

All of the methods, properties, and events are functions that return an HRESULT, which you can test to determine if the function executed successfully.

Method and property syntax in Visual C++ is listed for the following elements:

  • Collections
  • Command object
  • Connection object
  • Error object
  • Field object
  • Parameter object
  • Record object
  • Recordset object
  • Stream object

See Also

ADO for Visual C++ Syntax Index with #import | ActiveX Data Objects Start Page


Collections (ADO for Visual C++ Syntax)

Parameters

Methods

Append(IDispatch *Object)

Delete(VARIANT Index)

Refresh(void)

Properties

get_Count(long *c)

get_Item(VARIANT Index, _ADOParameter **ppvObject)

Fields

Methods

Append(BSTR bstrName, DataTypeEnum Type, long DefinedSize,
         FieldAttributeEnum Attrib)

Delete(VARIANT Index)

Refresh(void)

Properties

get_Count(long *c)

get_Item(VARIANT Index, ADOField **ppvObject)

Errors

Methods

Clear(void)

Refresh(void)

Properties

get_Count(long *c)

get_Item(VARIANT Index, ADOError **ppvObject)

Properties

Methods

Refresh(void)

Properties

get_Count(long *c)

get_Item(VARIANT Index, ADOProperty **ppvObject)

See Also

Errors Collection | Fields Collection | Parameters Collection | Properties Collection


Command (ADO for Visual C++ Syntax)

Methods

Cancel(void)

CreateParameter(BSTR Name, DataTypeEnum Type,
                  ParameterDirectionEnum Direction, long Size, VARIANT Value,
                  _ADOParameter **ppiprm)

Execute(VARIANT *RecordsAffected, VARIANT *Parameters, long Options,
         _ADORecordset **ppirs)

Properties

get_ActiveConnection(_ADOConnection **ppvObject)
put_ActiveConnection(VARIANT vConn)
putref_ActiveConnection(_ADOConnection *pCon)

get_CommandText(BSTR *pbstr)
put_CommandText(BSTR bstr)

get_CommandTimeout(LONG *pl)
put_CommandTimeout(LONG Timeout)

get_CommandType(CommandTypeEnum *plCmdType)
put_CommandType(CommandTypeEnum lCmdType)

get_Name(BSTR *pbstrName)
put_Name(BSTR bstrName)

get_Prepared(VARIANT_BOOL *pfPrepared)
put_Prepared(VARIANT_BOOL fPrepared)

get_State(LONG *plObjState)

get_Parameters(ADOParameters **ppvObject)

See Also

Command Object


Connection (ADO for Visual C++ Syntax)

Methods

BeginTrans(long *TransactionLevel)

CommitTrans(void)

RollbackTrans(void)

Cancel(void)

Close(void)

Execute(BSTR CommandText, VARIANT *RecordsAffected, long Options,
         _ADORecordset **ppiRset)

Open(BSTR ConnectionString, BSTR UserID, BSTR Password, long Options)

OpenSchema(SchemaEnum Schema, VARIANT Restrictions, VARIANT SchemaID,
          _ADORecordset **pprset)

Properties

get_Attributes(long *plAttr)
put_Attributes(long lAttr)

get_CommandTimeout(LONG *plTimeout)
put_CommandTimeout(LONG lTimeout)

get_ConnectionString(BSTR *pbstr)
put_ConnectionString(BSTR bstr)

get_ConnectionTimeout(LONG *plTimeout)
put_ConnectionTimeout(LONG lTimeout)

get_CursorLocation(CursorLocationEnum *plCursorLoc)
put_CursorLocation(CursorLocationEnum lCursorLoc)

get_DefaultDatabase(BSTR *pbstr)
put_DefaultDatabase(BSTR bstr)

get_IsolationLevel(IsolationLevelEnum *Level)
put_IsolationLevel(IsolationLevelEnum Level)

get_Mode(ConnectModeEnum *plMode)
put_Mode(ConnectModeEnum lMode)

get_Provider(BSTR *pbstr)
put_Provider(BSTR Provider)

get_State(LONG *plObjState)

get_Version(BSTR *pbstr)

get_Errors(ADOErrors **ppvObject)

Events

BeginTransComplete(LONG TransactionLevel, ADOError *pError,
                  EventStatusEnum *adStatus, _ADOConnection *pConnection)

CommitTransComplete(ADOError *pError, EventStatusEnum *adStatus,
                  _ADOConnection *pConnection)

ConnectComplete(ADOError *pError, EventStatusEnum *adStatus,
               _ADOConnection *pConnection)

Disconnect(EventStatusEnum *adStatus, _ADOConnection *pConnection)

ExecuteComplete(LONG RecordsAffected, ADOError *pError,
               EventStatusEnum *adStatus, _ADOCommand *pCommand,
               _ADORecordset *pRecordset, _ADOConnection *pConnection)

InfoMessage(ADOError *pError, EventStatusEnum *adStatus,
            _ADOConnection *pConnection)

RollbackTransComplete(ADOError *pError, EventStatusEnum *adStatus,
                     _ADOConnection *pConnection)

WillConnect(BSTR *ConnectionString, BSTR *UserID, BSTR *Password,
               long *Options, EventStatusEnum *adStatus,
               _ADOConnection *pConnection)

WillExecute(BSTR *Source, CursorTypeEnum *CursorType,
               LockTypeEnum *LockType, long *Options,
               EventStatusEnum *adStatus, _ADOCommand *pCommand,
               _ADORecordset *pRecordset, _ADOConnection *pConnection)

See Also

Connection Object


Error (ADO for Visual C++ Syntax)

Properties

get_Description(BSTR *pbstr)

get_NativeError(long *pl)

get_Number(long *pl)

get_Source(BSTR *pbstr)

get_SQLState(BSTR *pbstr)

See Also

Error Object


Field (ADO for Visual C++ Syntax)

Methods

AppendChunk(VARIANT Data)

GetChunk(long Length, VARIANT *pvar)

Properties

get_ActualSize(long *pl)

get_Attributes(long *pl)
put_Attributes(long lAttributes)

get_DataFormat(IUnknown **ppiDF)
put_DataFormat(IUnknown *piDF)

get_DefinedSize(long *pl)
put_DefinedSize(long lSize)

get_Name(BSTR *pbstr)

get_NumericScale(BYTE *pbNumericScale)
put_NumericScale(BYTE bScale)

get_OriginalValue(VARIANT *pvar)

get_Precision(BYTE *pbPrecision)
put_Precision(BYTE bPrecision)

get_Type(DataTypeEnum *pDataType)
put_Type(DataTypeEnum DataType)

get_UnderlyingValue(VARIANT *pvar)

get_Value(VARIANT *pvar)
put_Value(VARIANT Val)

See Also

Field Object


Parameter (ADO for Visual C++ Syntax)

Methods

AppendChunk(VARIANT Val)

Properties

get_Attributes(LONG *plParmAttribs)
put_Attributes(LONG lParmAttribs)

get_Direction(ParameterDirectionEnum *plParmDirection)
put_Direction(ParameterDirectionEnum lParmDirection)

get_Name(BSTR *pbstr)
put_Name(BSTR bstr)

get_NumericScale(BYTE *pbScale)
put_NumericScale(BYTE bScale)

get_Precision(BYTE *pbPrecision)
put_Precision(BYTE bPrecision)

get_Size(long *pl)
put_Size(long l)

get_Type(DataTypeEnum *psDataType)
put_Type(DataTypeEnum sDataType)

get_Value(VARIANT *pvar)
put_Value(VARIANT val)

See Also

Parameter Object


Record (ADO for Visual C++ Syntax)

Methods

Cancel(void)

Close(void)

CopyRecord(BSTR Source, BSTR Destination, BSTR UserName, BSTR Password,
            CopyRecordOptionsEnum Options, VARIANT_BOOL Async, BSTR
            *pbstrNewURL)

DeleteRecord(BSTR Source, VARIANT_BOOL Async)

GetChildren(_ADORecordset **ppRSet)

MoveRecord(BSTR Source, BSTR Destination, BSTR UserName, BSTR Password,
            MoveRecordOptionsEnum Options, VARIANT_BOOL Async, BSTR
            *pbstrNewURL)

Open(VARIANT Source, VARIANT ActiveConnection, ConnectModeEnum Mode,
         RecordCreateOptionsEnum CreateOptions, RecordOpenOptionsEnum
         Options, BSTR UserName, BSTR Password)

Properties

get_ActiveConnection(VARIANT *pvar)
put_ActiveConnection(BSTR bstrConn)
putref_ActiveConnection(_ADOConnection *Con)

get_Fields(ADOFields **ppFlds)

get_Mode(ConnectModeEnum *pMode)
put_Mode(ConnectModeEnum Mode)

get_ParentURL(BSTR *pbstrParentURL)

get_RecordType(RecordTypeEnum *pType)

get_Source(VARIANT *pvar)
put_Source(BSTR Source)
putref_Source(IDispatch *Source)

get_State(ObjectStateEnum *pState)

See Also

Record Object


Recordset (ADO for Visual C++ Syntax)

Methods

AddNew(VARIANT FieldList, VARIANT Values)

Cancel(void)

CancelBatch(AffectEnum AffectRecords)

CancelUpdate(void)

Clone(LockTypeEnum LockType, _ADORecordset **ppvObject)

Close(void)

CompareBookmarks(VARIANT Bookmark1, VARIANT Bookmark2,
                     CompareEnum *pCompare)

Delete(AffectEnum AffectRecords)

Find(BSTR Criteria, LONG SkipRecords, SearchDirectionEnum SearchDirection,
      VARIANT Start)

GetRows(long Rows, VARIANT Start, VARIANT Fields, VARIANT *pvar)

GetString(StringFormatEnum StringFormat, long NumRows, BSTR ColumnDelimeter,
            BSTR RowDelimeter, BSTR NullExpr, BSTR *pRetString)

Move(long NumRecords, VARIANT Start)

MoveFirst(void)

MoveLast(void)

MoveNext(void)

MovePrevious(void)

NextRecordset(VARIANT *RecordsAffected, _ADORecordset **ppiRs)

Open(VARIANT Source, VARIANT ActiveConnection, CursorTypeEnum CursorType,
         LockTypeEnum LockType, LONG Options)

Requery(LONG Options)

Resync(AffectEnum AffectRecords, ResyncEnum ResyncValues)

Save(BSTR FileName, PersistFormatEnum PersistFormat)

Supports(CursorOptionEnum CursorOptions, VARIANT_BOOL *pb)

Update(VARIANT Fields, VARIANT Values)

UpdateBatch(AffectEnum AffectRecords)

Properties

get_AbsolutePage(PositionEnum *pl)
put_AbsolutePage(PositionEnum Page)

get_AbsolutePosition(PositionEnum *pl)
put_AbsolutePosition(PositionEnum Position)

get_ActiveCommand(IDispatch **ppCmd)

get_ActiveConnection(VARIANT *pvar)
put_ActiveConnection(VARIANT vConn)
putref_ActiveConnection(IDispatch *pconn)

get_BOF(VARIANT_BOOL *pb)

get_Bookmark(VARIANT *pvBookmark)
put_Bookmark(VARIANT vBookmark)

get_CacheSize(long *pl)
put_CacheSize(long CacheSize)

get_CursorLocation(CursorLocationEnum *plCursorLoc)
put_CursorLocation(CursorLocationEnum lCursorLoc)

get_CursorType(CursorTypeEnum *plCursorType)
put_CursorType(CursorTypeEnum lCursorType)

get_DataMember(BSTR *pbstrDataMember)
put_DataMember(BSTR bstrDataMember)

get_DataSource(IUnknown **ppunkDataSource)
putref_DataSource(IUnknown *punkDataSource)

get_EditMode(EditModeEnum *pl)

get_EOF(VARIANT_BOOL *pb)

get_Filter(VARIANT *Criteria)
put_Filter(VARIANT Criteria)

get_LockType(LockTypeEnum *plLockType)
put_LockType(LockTypeEnum lLockType)

get_MarshalOptions(MarshalOptionsEnum *peMarshal)
put_MarshalOptions(MarshalOptionsEnum eMarshal)

get_MaxRecords(long *plMaxRecords)
put_MaxRecords(long lMaxRecords)

get_PageCount(long *pl)

get_PageSize(long *pl)
put_PageSize(long PageSize)

get_RecordCount(long *pl)

get_Sort(BSTR *Criteria)
put_Sort(BSTR Criteria)

get_Source(VARIANT *pvSource)
put_Source(BSTR bstrConn)
putref_Source(IDispatch *pcmd)

get_State(LONG *plObjState)

get_Status(long *pl)

get_StayInSync(VARIANT_BOOL *pbStayInSync)
put_StayInSync(VARIANT_BOOL bStayInSync)

get_Fields(ADOFields **ppvObject)

Events

EndOfRecordset(VARIANT_BOOL *fMoreData, EventStatusEnum *adStatus,
                  _ADORecordset *pRecordset)

FetchComplete(ADOError *pError, EventStatusEnum *adStatus,
                  _ADORecordset *pRecordset)

FetchProgress(long Progress, long MaxProgress, EventStatusEnum *adStatus,
                  _ADORecordset *pRecordset)

FieldChangeComplete(LONG cFields, VARIANT Fields, ADOError *pError,
                  EventStatusEnum *adStatus, _ADORecordset *pRecordset)

MoveComplete(EventReasonEnum adReason, ADOError *pError,
                  EventStatusEnum *adStatus, _ADORecordset *pRecordset)

RecordChangeComplete(EventReasonEnum adReason, LONG cRecords,
                  ADOError *pError, EventStatusEnum *adStatus,
                  _ADORecordset *pRecordset)
RecordsetChangeComplete(EventReasonEnum adReason, ADOError *pError,
                  EventStatusEnum *adStatus, _ADORecordset *pRecordset)

WillChangeField(LONG cFields, VARIANT Fields, EventStatusEnum *adStatus,
                  _ADORecordset *pRecordset)

WillChangeRecord(EventReasonEnum adReason, LONG cRecords,
                  EventStatusEnum *adStatus, _ADORecordset *pRecordset)

WillChangeRecordset(EventReasonEnum adReason, EventStatusEnum *adStatus,
                  _ADORecordset *pRecordset)

WillMove(EventReasonEnum adReason, EventStatusEnum *adStatus,
                  _ADORecordset *pRecordset)

See Also

Recordset Object


Stream (ADO for Visual C++ Syntax)

Methods

Cancel(void)

Close(void)

CopyTo(_ADOStream *DestStream, LONG CharNumber = -1)

Flush(void)

LoadFromFile(BSTR FileName)

Open(VARIANT Source, ConnectModeEnum Mode, StreamOpenOptionsEnum Options,
         BSTR UserName, BSTR Password)

Read(long NumBytes, VARIANT *pVal)

ReadText(long NumChars, BSTR *pbstr)

SaveToFile(BSTR FileName, SaveOptionsEnum Options = adSaveCreateNotExist)

SetEOS(void)

SkipLine(void)

Write(VARIANT Buffer)

WriteText(BSTR Data, StreamWriteEnum Options = adWriteChar)

Properties

get_Charset(BSTR *pbstrCharset)
put_Charset(BSTR Charset)

get_EOS(VARIANT_BOOL *pEOS)

get_LineSeparator(LineSeparatorEnum *pLS)
put_LineSeparator(LineSeparatorEnum LineSeparator)

get_Mode(ConnectModeEnum *pMode)
put_Mode(ConnectModeEnum Mode)

get_Position(LONG *pPos)
put_Position(LONG Position)

get_Size(LONG *pSize)

get_State(ObjectStateEnum *pState)

get_Type(StreamTypeEnum *pType)
put_Type(StreamTypeEnum Type)

See Also

Stream Object


ADO for Visual C++ Syntax Index with #import

This index is a cross-reference to the ADO Language Reference based on Microsoft Visual C++ and the #import directive.

This particular index was derived by compiling a program with the #import directive against the ADO .dll, then reformatting the *.tlh file that was generated. Only information about methods, properties, and events was preserved. The alternative syntax declared for each property is listed by the corresponding "__declspec(property...)" directive.

You are strongly encouraged to read Visual C++ ADO Programming for more information.

Method and property syntax in Visual C++ with the #import directive is listed for the following elements:

  • ADO Collections
  • Command Object
  • Connection Object
  • Error Object
  • Field Object
  • Parameter Object
  • Property object
  • Record Object
  • Recordset Object
  • Stream Object
  • Connection Events
  • Recordset Events

See Also

ADO for Visual C++ Syntax Index for COM


Collections (Visual C++ Syntax Index with #import)

It is useful to know that collections inherit certain common methods and properties.

All collections inherit the Count property and Refresh method, and all collections add the Item property. The Errors collection adds the Clear method. The Parameters collection inherits the Append and Delete methods, while the Fields collection adds the Append, Delete, and Update methods.

Properties Collection

Methods

HRESULT Refresh( );

Properties

long GetCount( );
__declspec(property(get=GetCount)) long Count;

PropertyPtr GetItem( const _variant_t & Index );
__declspec(property(get=GetItem)) PropertyPtr Item[];

Errors Collection

Methods

HRESULT Clear( );

HRESULT Refresh( );

Properties

long GetCount( );
__declspec(property(get=GetCount)) long Count;

PropertyPtr GetItem( const _variant_t & Index );
__declspec(property(get=GetItem)) PropertyPtr Item[];

Parameters Collection

Methods

HRESULT Append( IDispatch * Object );

HRESULT Delete( const _variant_t & Index );

HRESULT Refresh( );

Properties

long GetCount( );
__declspec(property(get=GetCount)) long Count;

PropertyPtr GetItem( const _variant_t & Index );
__declspec(property(get=GetItem)) PropertyPtr Item[];

Fields Collection

Methods

HRESULT Append( _bstr_t Name, enum DataTypeEnum Type, long DefinedSize,
    enum FieldAttributeEnum Attrib, const _variant_t & FieldValue =
    vtMissing );

HRESULT Delete( const _variant_t & Index );

HRESULT Refresh( );

HRESULT Update( );

Properties

long GetCount( );
__declspec(property(get=GetCount)) long Count;

PropertyPtr GetItem( const _variant_t & Index );
__declspec(property(get=GetItem)) PropertyPtr Item[];

See Also

Errors Collection | Fields Collection | Parameters Collection | Properties Collection


Command (Visual C++ Syntax Index with #import)

Methods

HRESULT Cancel( );

_RecordsetPtr Execute( VARIANT * RecordsAffected, VARIANT
    * Parameters, long Options );

_ParameterPtr CreateParameter( _bstr_t Name, enum
    DataTypeEnum Type, enum ParameterDirectionEnum Direction, long Size,
    const _variant_t & Value = vtMissing );

Properties

_ConnectionPtr GetActiveConnection( );
void PutRefActiveConnection( struct _Connection * ppvObject );
void PutActiveConnection( const _variant_t & ppvObject );
__declspec(property(get=GetActiveConnection,put=PutRefActiveConnection))
    _ConnectionPtr ActiveConnection;

_bstr_t GetCommandText( );
void PutCommandText( _bstr_t pbstr );
__declspec(property(get=GetCommandText,put=PutCommandText)) _bstr_t
    CommandText;

long GetCommandTimeout( );
void PutCommandTimeout( long pl );
__declspec(property(get=GetCommandTimeout,put=PutCommandTimeout)) long
    CommandTimeout;

void PutCommandType( enum CommandTypeEnum plCmdType );
enum CommandTypeEnum GetCommandType( );
__declspec(property(get=GetCommandType,put=PutCommandType)) enum
    CommandTypeEnum CommandType;

VARIANT_BOOL GetPrepared( );
void PutPrepared( VARIANT_BOOL pfPrepared );
__declspec(property(get=GetPrepared,put=PutPrepared)) VARIANT_BOOL
    Prepared;

ParametersPtr GetParameters( );
__declspec(property(get=GetParameters)) ParametersPtr
    Parameters;

_bstr_t GetName( );
void PutName( _bstr_t pbstrName );
__declspec(property(get=GetName,put=PutName)) _bstr_t Name;

long GetState( );
__declspec(property(get=GetState)) long State;

See Also

Command Object


Connection (Visual C++ Syntax Index with #import)

Methods

HRESULT Cancel( );

HRESULT Close( );

_RecordsetPtr Execute( _bstr_t CommandText, VARIANT *
    RecordsAffected,     long Options );

long BeginTrans( );
HRESULT CommitTrans( );
HRESULT RollbackTrans( );

HRESULT Open( _bstr_t ConnectionString, _bstr_t UserID,
    _bstr_t Password,     long Options );

_RecordsetPtr OpenSchema( enum SchemaEnum Schema, const
    _variant_t &     Restrictions = vtMissing, const _variant_t &
    SchemaID = vtMissing );

Properties

_bstr_t GetConnectionString( );
void PutConnectionString( _bstr_t pbstr );
__declspec(property(get=GetConnectionString,put=PutConnectionString))
    _bstr_t ConnectionString;

long GetCommandTimeout( );
void PutCommandTimeout( long plTimeout );
__declspec(property(get=GetCommandTimeout,put=PutCommandTimeout)) long
    CommandTimeout;

long GetConnectionTimeout( );
void PutConnectionTimeout( long plTimeout );
__declspec(property(get=GetConnectionTimeout,put=PutConnectionTimeout))
    long ConnectionTimeout;

_bstr_t GetVersion( );
__declspec(property(get=GetVersion)) _bstr_t Version;

ErrorsPtr GetErrors( );
__declspec(property(get=GetErrors)) ErrorsPtr Errors;

_bstr_t GetDefaultDatabase( );
void PutDefaultDatabase( _bstr_t pbstr );
__declspec(property(get=GetDefaultDatabase,put=PutDefaultDatabase))
    _bstr_t DefaultDatabase;

enum IsolationLevelEnum GetIsolationLevel( );
void PutIsolationLevel( enum IsolationLevelEnum Level );
__declspec(property(get=GetIsolationLevel,put=PutIsolationLevel)) enum
    IsolationLevelEnum IsolationLevel;

long GetAttributes( );
void PutAttributes( long plAttr );
__declspec(property(get=GetAttributes,put=PutAttributes)) long
    Attributes;

enum CursorLocationEnum GetCursorLocation( );
void PutCursorLocation( enum CursorLocationEnum plCursorLoc );
__declspec(property(get=GetCursorLocation,put=PutCursorLocation)) enum
    CursorLocationEnum CursorLocation;

enum ConnectModeEnum GetMode( );
void PutMode( enum ConnectModeEnum plMode );
__declspec(property(get=GetMode,put=PutMode)) enum ConnectModeEnum
    Mode;

_bstr_t GetProvider( );
void PutProvider( _bstr_t pbstr );
__declspec(property(get=GetProvider,put=PutProvider)) _bstr_t
    Provider;

long GetState( );
__declspec(property(get=GetState)) long State;

See Also

Connection Object


Error (Visual C++ Syntax Index with #import)

Properties

_bstr_t GetDescription( );
__declspec(property(get=GetDescription)) _bstr_t Description;

long GetHelpContext( );
__declspec(property(get=GetHelpContext)) long HelpContext;

_bstr_t GetHelpFile( );
__declspec(property(get=GetHelpFile)) _bstr_t HelpFile;

long GetNativeError( );
__declspec(property(get=GetNativeError)) long NativeError;

long GetNumber( );
__declspec(property(get=GetNumber)) long Number;

_bstr_t GetSource( );
__declspec(property(get=GetSource)) _bstr_t Source;

_bstr_t GetSQLState( );
__declspec(property(get=GetSQLState)) _bstr_t SQLState;

See Also

Error Object


Field (Visual C++ Syntax Index with #import)

Methods

HRESULT AppendChunk( const _variant_t & Data );

_variant_t GetChunk( long Length );

Properties

long GetActualSize( );
__declspec(property(get=GetActualSize)) long ActualSize;

long GetAttributes( );
void PutAttributes( long pl );
__declspec(property(get=GetAttributes,put=PutAttributes)) long     Attributes;

IUnknownPtr GetDataFormat( );
void PutRefDataFormat( IUnknown * ppiDF );
__declspec(property(get=GetDataFormat,put=PutRefDataFormat)) IunknownPtr
    DataFormat;

long GetDefinedSize( );
void PutDefinedSize( long pl );
__declspec(property(get=GetDefinedSize,put=PutDefinedSize)) long
    DefinedSize;

_bstr_t GetName( );
__declspec(property(get=GetName)) _bstr_t Name;

unsigned char GetNumericScale( );
void PutNumericScale( unsigned char pbNumericScale );
__declspec(property(get=GetNumericScale,put=PutNumericScale)) unsigned
    char NumericScale;

_variant_t GetOriginalValue( );
__declspec(property(get=GetOriginalValue)) _variant_t OriginalValue;

unsigned char GetPrecision( );
void PutPrecision( unsigned char pbPrecision );
__declspec(property(get=GetPrecision,put=PutPrecision)) unsigned char
    Precision;

enum DataTypeEnum GetType( );
void PutType( enum DataTypeEnum pDataType );
__declspec(property(get=GetType,put=PutType)) enum DataTypeEnum Type;

_variant_t GetUnderlyingValue( );
__declspec(property(get=GetUnderlyingValue)) _variant_t UnderlyingValue;

_variant_t GetValue( );
void PutValue( const _variant_t & pvar );
__declspec(property(get=GetValue,put=PutValue)) _variant_t Value;

See Also

Field Object


Parameter (Visual C++ Syntax Index with #import)

Methods

HRESULT AppendChunk( const _variant_t & Val );

Properties

long GetAttributes( );
void PutAttributes( long plParmAttribs );
__declspec(property(get=GetAttributes,put=PutAttributes)) long
    Attributes;

enum ParameterDirectionEnum GetDirection( );
void PutDirection( enum ParameterDirectionEnum plParmDirection );
__declspec(property(get=GetDirection,put=PutDirection)) enum
    ParameterDirectionEnum Direction;

_bstr_t GetName( );
void PutName( _bstr_t pbstr );
__declspec(property(get=GetName,put=PutName)) _bstr_t Name;

unsigned char GetNumericScale( );
void PutNumericScale( unsigned char pbScale );
__declspec(property(get=GetNumericScale,put=PutNumericScale)) unsigned
    char NumericScale;

unsigned char GetPrecision( );
void PutPrecision( unsigned char pbPrecision );
__declspec(property(get=GetPrecision,put=PutPrecision)) unsigned char
    Precision;

long GetSize( );
void PutSize( long pl );
__declspec(property(get=GetSize,put=PutSize)) long Size;

enum DataTypeEnum GetType( );
void PutType( enum DataTypeEnum psDataType );
__declspec(property(get=GetType,put=PutType)) enum DataTypeEnum Type;

_variant_t GetValue( );
void PutValue( const _variant_t & pvar );
__declspec(property(get=GetValue,put=PutValue)) _variant_t Value;

See Also

Parameter Object


Property (Visual C++ Syntax Index with #import)

Properties

long GetAttributes( );
void PutAttributes( long plAttributes );
__declspec(property(get=GetAttributes,put=PutAttributes)) long
    Attributes;

_bstr_t GetName( );
__declspec(property(get=GetName)) _bstr_t Name;

enum DataTypeEnum GetType( );
__declspec(property(get=GetType)) enum DataTypeEnum Type;

_variant_t GetValue( );
void PutValue( const _variant_t & pval );
__declspec(property(get=GetValue,put=PutValue)) _variant_t Value;

See Also

Property Object


Record (Visual C++ Syntax Index with #import)

Methods

HRESULT Cancel( );

HRESULT Close( );

_bstr_t CopyRecord( _bstr_t Source, _bstr_t Destination,
    _bstr_t     UserName, _bstr_t Password, enum CopyRecordOptionsEnum
    Options,     VARIANT_BOOL Async );

HRESULT DeleteRecord( _bstr_t Source, VARIANT_BOOL Async );

_RecordsetPtr GetChildren( );

_bstr_t MoveRecord( _bstr_t Source, _bstr_t Destination,
    _bstr_t     UserName, _bstr_t Password, enum MoveRecordOptionsEnum
    Options,     VARIANT_BOOL Async );

HRESULT Open( const _variant_t & Source, const _variant_t
&     ActiveConnection, enum ConnectModeEnum Mode, enum
    RecordCreateOptionsEnum CreateOptions, enum RecordOpenOptionsEnum
    Options, _bstr_t UserName, _bstr_t Password );

Properties

_variant_t GetActiveConnection( );
void PutActiveConnection( _bstr_t pvar );
void PutRefActiveConnection( struct _Connection * pvar );

FieldsPtr GetFields( );
__declspec(property(get=GetFields)) FieldsPtr Fields;

enum ConnectModeEnum GetMode( );
void PutMode( enum ConnectModeEnum pMode );
__declspec(property(get=GetMode,put=PutMode)) enum ConnectModeEnum Mode;

_bstr_t GetParentURL( );
__declspec(property(get=GetParentURL)) _bstr_t ParentURL;

enum RecordTypeEnum GetRecordType( );
__declspec(property(get=GetRecordType)) enum RecordTypeEnum
    RecordType;

_variant_t GetSource( );
void PutSource( _bstr_t pvar );
void PutRefSource( IDispatch * pvar );

enum ObjectStateEnum GetState( );
__declspec(property(get=GetState)) enum ObjectStateEnum State;

See Also

Record Object


Recordset (Visual C++ Syntax Index with #import)

Methods

HRESULT AddNew( const _variant_t & FieldList = vtMissing,
    const    _variant_t & Values = vtMissing );

HRESULT Cancel( );

HRESULT CancelBatch( enum AffectEnum AffectRecords );

HRESULT CancelUpdate( );

_RecordsetPtr Clone( enum LockTypeEnum LockType );

HRESULT Close( );

enum CompareEnum CompareBookmarks( const _variant_t
    & Bookmark1, const     _variant_t & Bookmark2 );

HRESULT Delete( enum AffectEnum AffectRecords );

HRESULT Find( _bstr_t Criteria, long SkipRecords, enum
    SearchDirectionEnum SearchDirection, const _variant_t & Start =
    vtMissing );

_variant_t GetRows( long Rows, const _variant_t & Start =
    vtMissing, const _variant_t & Fields = vtMissing );

_bstr_t GetString( enum
    StringFormatEnum StringFormat, long NumRows,     _bstr_t
    ColumnDelimeter, _bstr_t RowDelimeter, _bstr_t NullExpr );

HRESULT Move( long NumRecords, const _variant_t & Start =
    vtMissing );

HRESULT MoveFirst( );
HRESULT MoveLast( );
HRESULT MoveNext( );
HRESULT MovePrevious( );

_RecordsetPtr NextRecordset( VARIANT * RecordsAffected );

HRESULT Open( const _variant_t & Source, const _variant_t &
    ActiveConnection, enum CursorTypeEnum CursorType, enum LockTypeEnum
    LockType, long Options );

HRESULT Requery( long Options );

HRESULT Update( const _variant_t & Fields = vtMissing, const
    _variant_t &     Values = vtMissing );

HRESULT UpdateBatch( enum AffectEnum AffectRecords );

HRESULT Resync( enum AffectEnum AffectRecords, enum
    ResyncEnum     ResyncValues );

HRESULT Save( const _variant_t & Destination, enum
    PersistFormatEnum     PersistFormat );

HRESULT Seek( const _variant_t & KeyValues, enum SeekEnum
    SeekOption );

VARIANT_BOOL Supports( enum CursorOptionEnum CursorOptions );

Properties

enum PositionEnum GetAbsolutePage( );
void PutAbsolutePage( enum PositionEnum pl );
__declspec(property(get=GetAbsolutePage,put=PutAbsolutePage)) enum
    PositionEnum AbsolutePage;

enum PositionEnum GetAbsolutePosition( );
void PutAbsolutePosition( enum PositionEnum pl );
__declspec(property(get=GetAbsolutePosition,put=PutAbsolutePosition))
    enum PositionEnum AbsolutePosition;

IDispatchPtr GetActiveCommand( );
__declspec(property(get=GetActiveCommand)) IDispatchPtr ActiveCommand;

void PutRefActiveConnection( IDispatch * pvar );
void PutActiveConnection( const _variant_t & pvar );
_variant_t GetActiveConnection( );

enum CursorLocationEnum GetCursorLocation( );
void PutCursorLocation( enum CursorLocationEnum plCursorLoc );
__declspec(property(get=GetCursorLocation,put=PutCursorLocation)) enum
    CursorLocationEnum CursorLocation;

VARIANT_BOOL GetBOF( );
__declspec(property(get=GetBOF)) VARIANT_BOOL BOF;

VARIANT_BOOL GetEndOfFile( ); // Actually, GetEOF. Renamed in #import.
__declspec(property(get=GetEndOfFile)) VARIANT_BOOL EndOfFile;

_variant_t GetBookmark( );
void PutBookmark( const _variant_t & pvBookmark );
__declspec(property(get=GetBookmark,put=PutBookmark)) _variant_t
    Bookmark;

long GetCacheSize( );
void PutCacheSize( long pl );
__declspec(property(get=GetCacheSize,put=PutCacheSize)) long
    CacheSize;

enum CursorTypeEnum GetCursorType( );
void PutCursorType( enum CursorTypeEnum plCursorType );
__declspec(property(get=GetCursorType,put=PutCursorType)) enum
    CursorTypeEnum CursorType;

_bstr_t GetDataMember( );
void PutDataMember( _bstr_t pbstrDataMember );
__declspec(property(get=GetDataMember,put=PutDataMember)) _bstr_t
    DataMember;

IUnknownPtr GetDataSource( );
void PutRefDataSource( IUnknown * ppunkDataSource );
__declspec(property(get=GetDataSource,put=PutRefDataSource)) IUnknownPtr
    DataSource;

enum EditModeEnum GetEditMode( );
__declspec(property(get=GetEditMode)) enum EditModeEnum EditMode;

FieldsPtr GetFields( );
__declspec(property(get=GetFields)) FieldsPtr Fields;

_variant_t GetFilter( );
void PutFilter( const _variant_t & Criteria );
__declspec(property(get=GetFilter,put=PutFilter)) _variant_t Filter;

_bstr_t GetIndex( );
void PutIndex( _bstr_t pbstrIndex );
__declspec(property(get=GetIndex,put=PutIndex)) _bstr_t Index;

enum LockTypeEnum GetLockType( );
void PutLockType( enum LockTypeEnum plLockType );
__declspec(property(get=GetLockType,put=PutLockType)) enum LockTypeEnum
    LockType;

enum MarshalOptionsEnum GetMarshalOptions( );
void PutMarshalOptions( enum MarshalOptionsEnum peMarshal );
__declspec(property(get=GetMarshalOptions,put=PutMarshalOptions)) enum
    MarshalOptionsEnum MarshalOptions;

long GetMaxRecords( );
void PutMaxRecords( long plMaxRecords );
__declspec(property(get=GetMaxRecords,put=PutMaxRecords)) long
    MaxRecords;

long GetPageCount( );
__declspec(property(get=GetPageCount)) long PageCount;

long GetPageSize( );
void PutPageSize( long pl );
__declspec(property(get=GetPageSize,put=PutPageSize)) long PageSize;

long GetRecordCount( );
__declspec(property(get=GetRecordCount)) long RecordCount;

_bstr_t GetSort( );
void PutSort( _bstr_t Criteria );
__declspec(property(get=GetSort,put=PutSort)) _bstr_t Sort;

void PutRefSource( IDispatch * pvSource );
void PutSource( _bstr_t pvSource );
_variant_t GetSource( );

long GetState( );
__declspec(property(get=GetState)) long State;

long GetStatus( );
__declspec(property(get=GetStatus)) long Status;

VARIANT_BOOL GetStayInSync( );
void PutStayInSync( VARIANT_BOOL pbStayInSync );
__declspec(property(get=GetStayInSync,put=PutStayInSync)) VARIANT_BOOL
    StayInSync;

See Also

Recordset Object


Stream (Visual C++ Syntax Index with #import)

Methods

HRESULT Cancel( );

HRESULT Close( );

HRESULT CopyTo( struct _Stream * DestStream, int CharNumber );

HRESULT Flush( );

HRESULT LoadFromFile( _bstr_t FileName );

HRESULT Open( const _variant_t & Source, enum
    ConnectModeEnum Mode, enum     StreamOpenOptionsEnum Options, _bstr_t
    UserName, _bstr_t Password );

_variant_t Read( long NumBytes );

_bstr_t ReadText( long NumChars );

HRESULT SaveToFile( _bstr_t FileName, enum SaveOptionsEnum
    Options );

HRESULT SetEOS( );

HRESULT SkipLine( );

HRESULT Write( const _variant_t & Buffer );

HRESULT WriteText( _bstr_t Data, enum StreamWriteEnum
    Options );

Properties

_bstr_t GetCharset( );
void PutCharset( _bstr_t pbstrCharset );
__declspec(property(get=GetCharset,put=PutCharset)) _bstr_t Charset;

VARIANT_BOOL GetEOS( );
__declspec(property(get=GetEOS)) VARIANT_BOOL EOS;

enum LineSeparatorEnum GetLineSeparator( );
void PutLineSeparator( enum LineSeparatorEnum pLS );
__declspec(property(get=GetLineSeparator,put=PutLineSeparator)) enum
    LineSeparatorEnum LineSeparator;

enum ConnectModeEnum GetMode( );
void PutMode( enum ConnectModeEnum pMode );
__declspec(property(get=GetMode,put=PutMode)) enum ConnectModeEnum Mode;

long GetPosition( );
void PutPosition( long pPos );
__declspec(property(get=GetPosition,put=PutPosition)) long Position;

long GetSize( );
__declspec(property(get=GetSize)) long Size;

enum ObjectStateEnum GetState( );
__declspec(property(get=GetState)) enum ObjectStateEnum State;

enum StreamTypeEnum GetType( );
void PutType( enum StreamTypeEnum ptype );
__declspec(property(get=GetType,put=PutType)) enum StreamTypeEnum Type;

See Also

Stream Object


ConnectionEvents (Visual C++ Syntax Index with #import)

Events

HRESULT InfoMessage( struct Error * pError, enum
    EventStatusEnum *     adStatus, struct _Connection * pConnection );

HRESULT BeginTransComplete( long TransactionLevel,
    struct Error * pError, enum EventStatusEnum * adStatus, struct
    _Connection * pConnection );

HRESULT CommitTransComplete( struct Error *
    pError, enum EventStatusEnum     * adStatus, struct _Connection *
    pConnection );

HRESULT RollbackTransComplete( struct Error *
    pError, enum     EventStatusEnum * adStatus, struct _Connection *
    pConnection );

HRESULT WillExecute( BSTR * Source, enum
    CursorTypeEnum * CursorType,
    enum LockTypeEnum * LockType, long * Options, enum EventStatusEnum *
    adStatus, struct _Command * pCommand, struct _Recordset * pRecordset,
    struct _Connection * pConnection );

HRESULT ExecuteComplete( long RecordsAffected, struct
    Error * pError,     enum EventStatusEnum * adStatus, struct _Command
    * pCommand, struct     _Recordset * pRecordset, struct _Connection *
    pConnection );

HRESULT WillConnect( BSTR * ConnectionString, BSTR *
    UserID, BSTR *     Password, long * Options, enum EventStatusEnum *
    adStatus, struct     _Connection * pConnection );

HRESULT ConnectComplete( struct Error *
    pError, enum EventStatusEnum *     adStatus, struct _Connection *
    pConnection );

HRESULT Disconnect( enum EventStatusEnum *
    adStatus, struct _Connection *     pConnection );


RecordsetEvents (Visual C++ Syntax Index with #import)

Events

HRESULT WillChangeField( long cFields, const
    _variant_t & Fields, enum     EventStatusEnum * adStatus, struct
    _Recordset * pRecordset );

HRESULT FieldChangeComplete( long cFields, const
    _variant_t & Fields,     struct Error * pError, enum EventStatusEnum
    * adStatus, struct     _Recordset * pRecordset );

HRESULT WillChangeRecord( enum EventReasonEnum
    adReason, long cRecords,     enum EventStatusEnum * adStatus, struct
    _Recordset * pRecordset );

HRESULT RecordChangeComplete( enum EventReasonEnum
    adReason, long     cRecords, struct Error * pError, enum
    EventStatusEnum * adStatus,     struct _Recordset * pRecordset );

HRESULT WillChangeRecordset( enum EventReasonEnum
    adReason, enum     EventStatusEnum * adStatus, struct _Recordset *
    pRecordset );

HRESULT RecordsetChangeComplete( enum
    EventReasonEnum adReason, struct     Error * pError, enum
    EventStatusEnum * adStatus, struct _Recordset *     pRecordset );

HRESULT WillMove( enum EventReasonEnum adReason, enum
    EventStatusEnum *     adStatus, struct _Recordset * pRecordset );

HRESULT MoveComplete( enum EventReasonEnum adReason, struct
    Error *     pError, enum EventStatusEnum * adStatus, struct
    _Recordset * pRecordset );

HRESULT EndOfRecordset( VARIANT_BOOL * fMoreData, enum
    EventStatusEnum *     adStatus, struct _Recordset * pRecordset );

HRESULT FetchProgress( long Progress, long MaxProgress,
    enum     EventStatusEnum * adStatus, struct _Recordset * pRecordset );

HRESULT FetchComplete( struct Error * pError, enum
    EventStatusEnum *     adStatus, struct _Recordset * pRecordset );


ADO/WFC Syntax Index

The ADO Language Reference uses the Microsoft Visual Basic programming language to illustrate ADO method and property syntax. This index is a cross-reference to the ADO Language Reference topics, based on Microsoft Visual J++ and ADO for Windows Foundation Classes (ADO/WFC). When differences in syntax arise, use the function signatures in this index, as opposed to the syntax listings in the language reference topic.

Method and property syntax are listed for the following elements:

ActiveX Data Objects

  • ADO Collections
  • Command object
  • Connection object
  • Error object
  • Field object
  • Parameter object
  • Recordset object

Remote Data Service

  • DataSpace
  • ObjectProxy

See Also

ADO Event Model, Synchronous and Asynchronous Operations | ADO for Windows Foundation Classes | ActiveX Data Objects Start Page


Collections (ADO/WFC Syntax)

package com.ms.wfc.data

Parameters

Methods

public void append(com.ms.wfc.data.Parameter param)
public void delete(int n)
public void delete(String s)
public void refresh()
public Parameter getItem(int n)
public Parameter getItem(String s)

Properties

public int getCount()

Fields

Methods

public void append(String name, int type)
public void append(String name, int type, int definedSize)
public void append(String name, int type, int definedSize, int attrib)
public void delete(int n)
public void delete(String s)
public void refresh()
public com.ms.wfc.data.Field getItem(int n)
public com.ms.wfc.data.Field getItem(String s)

Properties

public int getCount()

Errors

Methods

public void clear()
public void refresh()
public com.ms.wfc.data.Error getItem(int n)
public com.ms.wfc.data.Error getItem(String s)

Properties

public int getCount()

See Also

Errors Collection | Fields Collection | Parameters Collection


Command (ADO/WFC Syntax)

package com.ms.wfc.data

Constructor

public Command()
public Command(String commandtext)

Methods

public void cancel()
public com.ms.wfc.data.Parameter createParameter(String
    Name, int Type, int Direction, int Size, Object Value)
public Recordset execute()
public Recordset execute(Object[] parameters)
public Recordset execute(Object[] parameters, int options)
public int executeUpdate(Object[] parameters)
public int executeUpdate(Object[] parameters, int options)
public int executeUpdate()

The executeUpdate method is a special case method that calls the underlying ADO execute method with certain parameters. The executeUpdate method does not support the return of a Recordset object, so the execute method's options parameter is modified with AdoEnums.ExecuteOptions.NORECORDS. After the execute method completes, its updated RecordsAffected parameter is passed back to the executeUpdate method, which is finally returned as an int.

Properties

public com.ms.wfc.data.Connection getActiveConnection()
public void setActiveConnection(com.ms.wfc.data.Connection con)
public void setActiveConnection(String conString)
public String getCommandText()
public void setCommandText(String command)
public int getCommandTimeout()
public void setCommandTimeout(int timeout)
public int getCommandType()
public void setCommandType(int type)
public String getName()
public void setName(String name)
public boolean getPrepared()
public void setPrepared(boolean prepared)
public int getState()
public com.ms.wfc.data.Parameter getParameter(int n)
public com.ms.wfc.data.Parameter getParameter(String n)
public com.ms.wfc.data.Parameters getParameters()
public AdoProperties getProperties()

See Also

Command Object


Connection (ADO/WFC Syntax)

package com.ms.wfc.data

Constructor

public Connection()
public Connection(String connectionstring)

Methods

public int beginTrans()
public void commitTrans()
public void rollbackTrans()
public void cancel()
public void close()
public com.ms.wfc.data.Recordset execute(String commandText)
public com.ms.wfc.data.Recordset execute(String commandText, int options)
public int executeUpdate(String commandText)
public int executeUpdate(String commandText, int options)

The executeUpdate method is a special case method that calls the underlying ADO execute method with certain parameters. The executeUpdate method does not support the return of a Recordset object, so the execute method's options parameter is modified with AdoEnums.ExecuteOptions.NORECORDS. After the execute method completes, its updated RecordsAffected parameter is passed back to the executeUpdate method, which is finally returned as an int.

public void open()
public void open(String connectionString)
public void open(String connectionString, String userID)
public void open(String connectionString, String userID, String password)
public void open(String connectionString, String userID, String password, int options)
public Recordset openSchema(int schema, Object[]
    restrictions, String schemaID)
public Recordset openSchema(int schema)
public Recordset openSchema(int schema, Object[] restrictions)

Properties

public int getAttributes()
public void setAttributes(int attr)
public int getCommandTimeout()
public void setCommandTimeout(int timeout)
public String getConnectionString()
public void setConnectionString(String con)
public int getConnectionTimeout()
public void setConnectionTimeout(int timeout)
public int getCursorLocation()
public void setCursorLocation(int cursorLoc)
public String getDefaultDatabase()
public void setDefaultDatabase(String db)
public int getIsolationLevel()
public void setIsolationLevel(int level)
public int getMode()
public void setMode(int mode)
public String getProvider()
public void setProvider(String provider)
public int getState()
public String getVersion()
public AdoProperties getProperties()
public com.ms.wfc.data.Errors getErrors()

Events

For more information about ADO/WFC events, see ADO Event Instantiation by Language.

public void addOnBeginTransComplete(ConnectionEventHandler handler)
public void removeOnBeginTransComplete(ConnectionEventHandler handler)
public void addOnCommitTransComplete(ConnectionEventHandler handler)
public void removeOnCommitTransComplete(ConnectionEventHandler handler)
public void addOnConnectComplete(ConnectionEventHandler handler)
public void removeOnConnectComplete(ConnectionEventHandler handler)
public void addOnDisconnect(ConnectionEventHandler handler)
public void removeOnDisconnect(ConnectionEventHandler handler)
public void addOnExecuteComplete(ConnectionEventHandler handler)
public void removeOnExecuteComplete(ConnectionEventHandler handler)
public void addOnInfoMessage(ConnectionEventHandler handler)
public void removeOnInfoMessage(ConnectionEventHandler handler)
public void addOnRollbackTransComplete(ConnectionEventHandler handler)
public void removeOnRollbackTransComplete(ConnectionEventHandler handler)
public void addOnWillConnect(ConnectionEventHandler handler)
public void removeOnWillConnect(ConnectionEventHandler handler)
public void addOnWillExecute(ConnectionEventHandler handler)
public void removeOnWillExecute(ConnectionEventHandler handler)

See Also

Connection Object


DataSpace (ADO/WFC Syntax)

The createObject method of the DataSpace class specifies both a business object to process client application requests (progid) and the communications protocol and server (connection). createObject returns an ObjectProxy object that represents the server.

package com.ms.wfc.data

Constructor

public DataSpace()

Methods

public static ObjectProxy DataSpace.createObject(String
    progid, String connection)

Properties

public static int getInternetTimeout()
public static void setInternetTimeout(int plInetTimeout)

See Also

DataSpace Object (RDS)


Error (ADO/WFC Syntax)

package com.ms.wfc.data

Properties

public String getDescription()
public int getNativeError()
public int getNumber()
public String getSource()
public String getSQLState()

See Also

Error Object


Field (ADO/WFC Syntax)

package com.ms.wfc.data

Methods

public void appendChunk(byte[] bytes)
public void appendChunk(char[] chars)
public void appendChunk(String chars)
public byte[] getByteChunk(int len)
public char[] getCharChunk(int len)
public String getStringChunk(int len)

Properties

public int getActualSize()
public int getAttributes()
public void setAttributes(int pl)
public com.ms.com.IUnknown getDataFormat()
public void setDataFormat(com.ms.com.IUnknown format)

(For more information, see the Microsoft Visual J++ WFC Reference documentation for the com.ms.wfc.data.IDataFormat interface.)

public int getDefinedSize()
public void setDefinedSize(int pl)
public String getName()
public int getNumericScale()
public void setNumericScale(byte pbNumericScale)
public Variant getOriginalValue()
public int getPrecision()
public void setPrecision(byte pbPrecision)
public int getType()
public void setType(int pDataType)
public Variant getUnderlyingValue()
public Variant getValue()
public void setValue(Variant value)
public AdoProperties getProperties()

Field Accessor Methods

The Value property of a Field object gets or sets the content of that object. The content is represented as a VARIANT, a type of object that can be assigned a value and any of several data types.

ADO/WFC implements the Value property with the getValue method, which returns a VARIANT object; and the setValue method, which takes a VARIANT as an argument. VARIANTs are highly efficient in certain languages, such as Microsoft Visual Basic. However, you can attain better performance in Microsoft Visual J++ by using native Java data types.

In addition to the Value property, ADO/WFC provides accessor methods that use Java data types to get and set the content of Field objects. Most of these methods have names of the form get DataType or set DataType.

There are two noteworthy exceptions: One of the getObject methods returns an object coerced into a specified class. There is no getNull property; instead, there is an isNull property that returns a Boolean value indicating whether the field is null.

public native boolean getBoolean();
public void setBoolean(boolean v)
public native byte getByte();
public void setByte(byte v)
public native byte[] getBytes();
public void setBytes(byte[] v)
public native double getDouble();
public void setDouble(double v)
public native float getFloat();
public void setFloat(float v)
public native int getInt();
public void setInt(int v)
public native long getLong();
public void setLong(long v)
public native short getShort();
public void setShort(short v)
public native String getString();
public void setString(String v)
public native boolean isNull();
public void setNull()
public Object getObject()
public Object getObject(Class c)
public void setObject(Object value)

See Also

Field Object


ObjectProxy (ADO/WFC Syntax)

An ObjectProxy object represents a server, and is returned by the createObject method of the DataSpace object. The ObjectProxy class has one method, call, which can invoke a method on the server and return an object resulting from that invocation.

package com.ms.wfc.data

Methods

Call Method (ADO/WFC Syntax)

Invokes a method on the server represented by the ObjectProxy. Optionally, method arguments may be passed as an array of objects.

Syntax

public Object ObjectProxy.call( String method )
public Object ObjectProxy.call( String method, Object[] args)

Returns

Object
An object resulting from invoking the method.

Parameters

ObjectProxy
An ObjectProxy object that represents the server.
method
A String, containing the name of the method to invoke on the server.
args
Optional. An array of objects that are arguments to the method on the server. Java data types are automatically converted to data types suitable for use on the server.


Parameter (ADO/WFC Syntax)

package com.ms.wfc.data

Constructor

public Parameter()
public Parameter(String name)
public Parameter(String name, int type)
public Parameter(String name, int type, int dir)
public Parameter(String name, int type, int dir, int size)
public Parameter(String name, int type, int dir, int size, Object value)

Methods

public void appendChunk(byte[] bytes)
public void appendChunk(char[] chars)
public void appendChunk(String chars)

Properties

public int getAttributes()
public void setAttributes(int attr)
public int getDirection()
public void setDirection(int dir)
public String getName()
public void setName(String name)
public int getNumericScale()
public void setNumericScale(int scale)
public int getPrecision()
public void setPrecision(int prec)
public int getSize()
public void setSize(int size)
public int getType()
public void setType(int type)
public com.ms.com.Variant getValue()
public void setValue(Object v)
public AdoProperties getProperties()

Parameter Accessor Methods

The Value property of a Parameter object gets or sets the content of that object. The content is represented as a VARIANT, a type of object that can be assigned a value and any of several data types.

ADO/WFC implements the Value property with the getValue method, which returns a VARIANT object; and the setValue method, which takes a VARIANT as an argument. VARIANTs are highly efficient in certain languages, such as Microsoft Visual Basic. However, you can attain better performance in Microsoft Visual J++ by using native Java data types.

In addition to the Value property, ADO/WFC provides accessor methods that use Java data types to get and set the content of Parameter objects. Most of these methods have names of the form get DataType or set DataType.

There is one noteworthy exception: There is no getNull property; instead, there is an isNull property that returns a Boolean value indicating whether the field is null.

public boolean getBoolean()
public void setBoolean(boolean v)
public byte getByte()
public void setByte(byte v)
public double getDouble()
public void setDouble(double v)
public float getFloat()
public void setFloat(float v)
public int getInt()
public void setInt(int v)
public long getLong()
public void setLong(long v)
public short getShort()
public void setShort(short v)
public String getString()
public void setString(String v)
public boolean isNull()
public void setNull()

See Also

Parameter Object


Recordset (ADO/WFC Syntax)

package com.ms.wfc.data

Constructors

public Recordset()
public Recordset(Object r)

Methods

public void addNew(Object[] fieldList, Object[] valueList)
public void addNew(Object[] valueList)
public void addNew()
public void cancel()
public void cancelBatch(int affectRecords)
public void cancelBatch()
public void cancelUpdate()
public Object clone()
public Object clone(int lockType)
public void close()
public int compareBookmarks(Object bookmark1, Object bookmark2)
public void delete(int affectRecords)
public void delete()
public void find(String criteria)
public void find(String criteria, int SkipRecords)
public void find(String criteria, int SkipRecords, int searchDirection)
public void find(String criteria, int SkipRecords, int searchDirection, Object bmkStart)
public Object[][] getRows(int Rows, Object bmkStart, Object[] fieldList)
public void move(int numRecords)
public void move(int numRecords, Object bmkStart)
public void moveFirst()
public void moveLast()
public void moveNext()
public void movePrevious()
public Recordset nextRecordset()
public Recordset nextRecordset(int[] recordsAffected)
public void open()
public void open(Object source)
public void open(Object source, Object activeConnection)
public void open(Object source, Object activeConnection, int cursorType)
public void open(Object source, Object activeConnection, int cursorType,
                  int lockType)
public void open(Object source, Object activeConnection, int cursorType,
                  int lockType, int options)
public void requery()
public void requery(int options)
public void resync()
public void resync(int affectRecords, int resyncValues)
public void save(String fileName)
public void save(String fileName, int persistFormat)
public boolean supports(int cursorOptions)
public void update()
public void update(Object[] valueList)
public void update(Object[] fieldList, Object[] valueList)
public void updateBatch()
public void updateBatch(int affectRecords)

Properties

public int getAbsolutePage()
public void setAbsolutePage(int page)
public int getAbsolutePosition()
public void setAbsolutePosition(int pos)
public Command getActiveCommand()
public Connection getActiveConnection()
public void setActiveConnection(String conn)
public void setActiveConnection(com.ms.wfc.data.Connection c)
public boolean getBOF()
public boolean getEOF()
public Object getBookmark()
public void setBookmark(Object bmk)
public int getCacheSize()
public void setCacheSize(int size)
public int getCursorLocation()
public void setCursorLocation(int cursorLoc)
public int getCursorType()
public void setCursorType(int cursorType)
public String getDataMember()
public void setDataMember(String pbstrDataMember)
public Iunknown getDataSource()
public void setDataSource(IUnknown dataSource)
public int getEditMode()
public Object getFilter()
public void setFilter(Object filter)
public int getLockType()
public void setLockType(int lockType)
public int getMarshalOptions()
public void setMarshalOptions(int options)
public int getMaxRecords()
public void setMaxRecords(int maxRecords)
public int getPageCount()
public int getPageSize()
public void setPageSize(int pageSize)
public int getRecordCount()
public String getSort()
public void setSort(String criteria)
public String getSource()
public void setSource(String query)
public void setSource(com.ms.wfc.data.Command command)
public int getState()
public int getStatus()
public boolean getStayInSync()
public void setStayInSync(boolean pbStayInSync)
public com.ms.wfc.data.Field getField(int n)
public com.ms.wfc.data.Field getField(String n)
public com.ms.wfc.data.Fields getFields()
public AdoProperties getProperties()

Events

For more information about ADO/WFC events, see ADO Event Instantiation by Language.

public void addOnEndOfRecordset(RecordsetEventHandler handler)
public void removeOnEndOfRecordset(RecordsetEventHandler handler)
public void addOnFetchComplete(RecordsetEventHandler handler)
public void removeOnFetchComplete(RecordsetEventHandler handler)
public void addOnFetchProgress(RecordsetEventHandler handler)
public void removeOnFetchProgress(RecordsetEventHandler handler)
public void addOnFieldChangeComplete(RecordsetEventHandler handler)
public void removeOnFieldChangeComplete(RecordsetEventHandler handler)
public void addOnMoveComplete(RecordsetEventHandler handler)
public void removeOnMoveComplete(RecordsetEventHandler handler)
public void addOnRecordChangeComplete(RecordsetEventHandler handler)
public void removeOnRecordChangeComplete(RecordsetEventHandler handler)
public void addOnRecordsetChangeComplete(RecordsetEventHandler handler)
public void removeOnRecordsetChangeComplete(RecordsetEventHandler handler)
public void addOnWillChangeField(RecordsetEventHandler handler)
public void removeOnWillChangeField(RecordsetEventHandler handler)
public void addOnWillChangeRecord(RecordsetEventHandler handler)
public void removeOnWillChangeRecord(RecordsetEventHandler handler)
public void addOnWillChangeRecordset(RecordsetEventHandler handler)
public void removeOnWillChangeRecordset(RecordsetEventHandler handler)
public void addOnWillMove(RecordsetEventHandler handler)
public void removeOnWillMove(RecordsetEventHandler handler)

See Also

Recordset Object


ADO Code Examples

Use the following code examples to learn how to use the ADO objects, methods, properties, and events.

Note Paste the entire code example into your code editor. The example may not run correctly if partial examples are used or if paragraph formatting is lost.

  • ADO Code Examples in Microsoft Visual Basic
  • ADO Code Examples in Microsoft Visual Basic Scripting Edition
  • ADO Code Examples in Microsoft Visual C++
  • ADO Code Examples in Microsoft Visual J++
  • ADO Code Examples in Microsoft JScript


ADO Code Examples in Microsoft Visual Basic

Use the following code examples to learn how to use the ADO methods, properties, and events when writing in Visual Basic.

Note Paste the entire code example, from Sub to End Sub, into your code editor. The example may not run correctly if partial examples are used or if paragraph formatting is lost.

Methods

  • AddNew Method Example
  • Append and CreateParameter Methods Example
  • AppendChunk and GetChunk Methods Example
  • BeginTrans, CommitTrans, and RollbackTrans Methods Example
  • Cancel Method Example
  • Clone Method Example
  • CompareBookmarks Method Example
  • ConvertToString Method Example
  • CopyRecord, CopyTo, and SaveToFile Methods Example
  • CreateRecordset Method Example
  • Delete Method Example
  • DeleteRecord and MoveRecord Methods Example
  • Execute, Requery, and Clear Methods Example
  • Find Method Example
  • GetRows Method Example
  • GetString Method Example
  • SkipLine Method, EOS, and LineSeparator Properties Example
  • Move Method Example
  • MoveFirst, MoveLast, MoveNext, and MovePrevious Methods Example
  • NextRecordset Method Example
  • Open and Close Methods Example
  • OpenSchema Method Example
  • Read, ReadText, Write, and WriteText Methods Example
  • Refresh Method Example
  • Resync Method Example
  • Save and Open Methods Example
  • Seek Method and Index Property Example
  • Supports Method Example
  • Update and CancelUpdate Methods Example
  • UpdateBatch and CancelBatch Methods Example

Properties

  • AbsolutePage, PageCount, and PageSize Properties Example
  • AbsolutePosition and CursorLocation Properties Example
  • ActiveCommand Property Example
  • ActiveConnection, CommandText, CommandTimeout, CommandType, Size, and Direction Properties Example
  • ActualSize and DefinedSize Properties Example
  • Attributes and Name Properties Example
  • BOF, EOF, and Bookmark Properties Example
  • CacheSize Property Example
  • ConnectionString, ConnectionTimeout, and State Properties Example
  • Count Property Example
  • CursorType, LockType, and EditMode Properties Example
  • Description, HelpContext, HelpFile, NativeError, Number, Source, and SQLState Properties Example
  • EOS and LineSeparator Properties, SkipLine Method Example
  • Filter and RecordCount Properties Example
  • IsolationLevel and Mode Properties Example
  • Item Property Example
  • MarshalOptions Property Example
  • MaxRecords Property Example
  • NumericScale and Precision Properties Example
  • Optimize Property Example
  • OriginalValue and UnderlyingValue Properties Example
  • Prepared Property Example
  • Provider and DefaultDatabase Properties Example
  • Sort Property Example
  • Source Property Example
  • State Property Example
  • Status Property Example
  • StayInSync Property Example
  • Type Property Example (Field)
  • Type Property Example (Property)
  • Value Property Example
  • Version Property Example

See Also

ADO Code Examples in Microsoft Visual Basic Scripting Edition | ADO Code Examples in Microsoft Visual C++ | ADO Code Examples in Microsoft Visual J++ | Sample Applications


AbsolutePage, PageCount, and PageSize Properties Example (VB)

'BeginAbsolutePageVB
Public Sub AbsolutePageX()

Dim rstEmployees As ADODB.Recordset
Dim strCnn As String
Dim strMessage As String
Dim intPage As Integer
Dim intPageCount As Integer
Dim intRecord As Integer

   ' Open a recordset using a client cursor
   ' for the employee table.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=;"
   Set rstEmployees = New ADODB.Recordset
   ' Use client cursor to enable AbsolutePosition property.
   rstEmployees.CursorLocation = adUseClient
   rstEmployees.Open "employee", strCnn, , , adCmdTable

   ' Display names and hire dates, five records
   ' at a time.
   rstEmployees.PageSize = 5
   intPageCount = rstEmployees.PageCount
   For intPage = 1 To intPageCount
      rstEmployees.AbsolutePage = intPage
      strMessage = ""
      For intRecord = 1 To rstEmployees.PageSize
         strMessage = strMessage & _
            rstEmployees!fname & " " & _
            rstEmployees!lname & " " & _
            rstEmployees!hire_date & vbCr
         rstEmployees.MoveNext
         If rstEmployees.EOF Then Exit For
      Next intRecord
      MsgBox strMessage
   Next intPage
   rstEmployees.Close

End Sub
'EndAbsolutePageVB

See Also

AbsolutePage Property | PageCount Property | PageSize Property | Recordset Object


AbsolutePosition and CursorLocation Properties Example (VB)

This example demonstrates how the AbsolutePosition property can track the progress of a loop that enumerates all the records of a Recordset. It uses the CursorLocation property to enable the AbsolutePosition property by setting the cursor to a client cursor.

'BeginAbsolutePositionVB
Public Sub AbsolutePositionX()

Dim rstEmployees As ADODB.Recordset
Dim strCnn As String
Dim strMessage As String

   ' Open a recordset for the Employee table
   ' using a client cursor.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstEmployees = New ADODB.Recordset
   ' Use client cursor to enable AbsolutePosition property.
   rstEmployees.CursorLocation = adUseClient
   rstEmployees.Open "employee", strCnn, , , adCmdTable

   ' Enumerate Recordset.
   Do While Not rstEmployees.EOF
      ' Display current record information.
      strMessage = "Employee: " & rstEmployees!lname & vbCr & _
         "(record " & rstEmployees.AbsolutePosition & _
         " of " & rstEmployees.RecordCount & ")"
      If MsgBox(strMessage, vbOKCancel) = vbCancel _
         Then Exit Do
      rstEmployees.MoveNext
   Loop

   rstEmployees.Close

End Sub
'EndAbsolutePositionVB

See Also

AbsolutePosition Property | CursorLocation Property | Recordset Object


ActiveCommand Property Example (VB)

This example demonstrates the ActiveCommand property.

A subroutine is given a Recordset object whose ActiveCommand property is used to display the command text and parameter that created the Recordset.

'BeginActiveCommandVB
Public Sub ActiveCommandX()
Dim cnn As New ADODB.Connection
Dim cmd As New ADODB.Command
Dim rst As New ADODB.Recordset
Dim strPrompt As String, strName As String

strPrompt = "Enter an author's name (e.g., Ringer): "
strName = Trim(InputBox(strPrompt, "ActiveCommandX Example"))
cmd.CommandText = "SELECT * FROM Authors WHERE au_lname = ?"
cmd.Parameters.Append _
   cmd.CreateParameter("LastName", adChar, adParamInput, 20, strName)

cnn.Open "DSN=Pubs;Provider=MSDASQL; uid=sa; pwd=;"
cmd.ActiveConnection = cnn
Set rst = cmd.Execute(, , adCmdText)
ActiveCommandXprint rst

rst.Close
cnn.Close
End Sub
'EndActiveCommandVB

The ActiveCommandXprint routine is given only a Recordset object, yet it must print the command text and parameter that created the Recordset. This can be done because the Recordset object's ActiveCommand property yields the associated Command object.

The Command object's CommandText property yields the parameterized command that created the Recordset. The Command object's Parameters collection yields the value that was substituted for the command's parameter placeholder ("?").

Finally, an error message or the author's name and ID are printed.

'BeginActiveCommandPrintVB
Public Sub ActiveCommandXprint(rstp As ADODB.Recordset)
Dim strName As String

strName = rstp.ActiveCommand.Parameters.Item("LastName").Value

Debug.Print "Command text = '"; rstp.ActiveCommand.CommandText; "'"
Debug.Print "Parameter = '"; strName; "'"

If rstp.BOF = True Then
   Debug.Print "Name = '"; strName; "', not found."
Else
   Debug.Print "Name = '"; rstp!au_fname; " "; rstp!au_lname; _
         "', author ID = '"; rstp!au_id; "'"
End If

End Sub
'EndActiveCommandPrintVB

See Also

ActiveCommand Property | Command Object | Recordset Object


ActiveConnection, CommandText, CommandTimeout, CommandType, Size, and Direction Properties Example (VB)

This example uses the ActiveConnection, CommandText, CommandTimeout, CommandType, Size, and Direction properties to execute a stored procedure.

'BeginActiveConnectionVB
Public Sub ActiveConnectionX()

   Dim cnn1 As ADODB.Connection
   Dim cmdByRoyalty As ADODB.Command
   Dim prmByRoyalty As ADODB.Parameter
   Dim rstByRoyalty As ADODB.Recordset
   Dim rstAuthors As ADODB.Recordset
   Dim intRoyalty As Integer
   Dim strAuthorID As String
   Dim strCnn As String

   ' Define a command object for a stored procedure.
   Set cnn1 = New ADODB.Connection
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   cnn1.Open strCnn
   Set cmdByRoyalty = New ADODB.Command
   Set cmdByRoyalty.ActiveConnection = cnn1
   cmdByRoyalty.CommandText = "byroyalty"
   cmdByRoyalty.CommandType = adCmdStoredProc
   cmdByRoyalty.CommandTimeout = 15

   ' Define the stored procedure's input parameter.
   intRoyalty = Trim(InputBox( _
      "Enter royalty:"))
   Set prmByRoyalty = New ADODB.Parameter
   prmByRoyalty.Type = adInteger
   prmByRoyalty.Size = 3
   prmByRoyalty.Direction = adParamInput
   prmByRoyalty.Value = intRoyalty
   cmdByRoyalty.Parameters.Append prmByRoyalty

   ' Create a recordset by executing the command.
   Set rstByRoyalty = cmdByRoyalty.Execute()

   ' Open the Authors table to get author names for display.
   Set rstAuthors = New ADODB.Recordset
   rstAuthors.Open "Authors", strCnn, , , adCmdTable

   ' Print current data in the recordset, adding
   ' author names from Authors table.
   Debug.Print "Authors with " & intRoyalty & _
      " percent royalty"
   Do While Not rstByRoyalty.EOF
      strAuthorID = rstByRoyalty!au_id
      Debug.Print , rstByRoyalty!au_id & ", ";
      rstAuthors.Filter = "au_id = '" & strAuthorID & "'"
      Debug.Print rstAuthors!au_fname & " " & _
         rstAuthors!au_lname
      rstByRoyalty.MoveNext
   Loop

   rstByRoyalty.Close
   rstAuthors.Close
   cnn1.Close

End Sub
'EndActiveConnectionVB

See Also

ActiveCommand Property | Command Object | CommandText Property | CommandTimeout Property | CommandType Property | Connection Object | Direction Property | Parameter Object | Record Object | Recordset Object | Size Property


ActualSize and DefinedSize Properties Example (VB)

This example uses the ActualSize and DefinedSize properties to display the defined size and actual size of a field.

'BeginActualSizeVB
Public Sub ActualSizeX()

   Dim rstStores As ADODB.Recordset
   Dim strCnn As String

   ' Open a recordset for the Stores table.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Northwind;User Id=sa;Password=; "
   Set rstStores = New ADODB.Recordset
   rstStores.Open "Suppliers", strCnn, , , adCmdTable

   ' Loop through the recordset displaying the contents
   ' of the stor_name field, the field's defined size,
   ' and its actual size.
   rstStores.MoveFirst

   Dim strMessage As String

   Do Until rstStores.EOF
      strMessage "Company name: " & rstStores!CompanyName & _
      vbCrLf & "Defined size: " & _
      rstStores!CompanyName.DefinedSize & _
      vbCrLf & "Actual size: " & _
      rstStores!CompanyName.ActualSize & vbCrLf
      MsgBox strMessage, vbOKCancel, "ADO ActualSize Property (Visual Basic)"
      rstStores.MoveNext
   Loop

   rstStores.Close

End Sub
'EndActualSizeVB

See Also

ActualSize Property | DefinedSize Property | Field Object


AddNew Method Example (VB)

This example uses the AddNew method to create a new record with the specified name.

'BeginAddNewVB
Public Sub AddNewX()

   Dim cnn1 As ADODB.Connection
   Dim rstEmployees As ADODB.Recordset
   Dim strCnn As String
   Dim strID As String
   Dim strFirstName As String
   Dim strLastName As String
   Dim booRecordAdded As Boolean

   ' Open a connection.
   Set cnn1 = New ADODB.Connection
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Northwind;User Id=sa;Password=;"
   cnn1.Open strCnn

   ' Open Employee table.
   Set rstEmployees = New ADODB.Recordset
   rstEmployees.CursorType = adOpenKeyset
   rstEmployees.LockType = adLockOptimistic
   rstEmployees.Open "Employees", cnn1, , , adCmdTable

   ' Get data from the user. The employee ID must be formatted as
   ' first, middle, and last initial, five numbers, then M or F to
   ' signify the gender. For example, the employee id for Bill Sornsin
   ' would be "B-S55555M".
   strID = Trim(InputBox("Enter employee ID:"))
   strFirstName = Trim(InputBox("Enter first name:"))
   strLastName = Trim(InputBox("Enter last name:"))

   ' Proceed only if the user actually entered something
   ' for both the first and last names.
   If (strID <> "") And (strFirstName <> "") _
      And (strLastName <> "") Then

      rstEmployees.AddNew
      rstEmployees!firstname = strFirstName
      rstEmployees!LastName = strLastName
      rstEmployees.Update
      booRecordAdded = True

      ' Show the newly added data.
      MsgBox "New record: " & rstEmployees!emp_id & " " & _
         rstEmployees!fname & " " & rstEmployees!lname

   Else
      MsgBox "Please enter an employee ID, " & _
         "first name, and last name."
   End If

   ' Delete the new record because this is a demonstration.
   cnn1.Execute "DELETE FROM employee WHERE emp_id = '" & strID & "'"

   rstEmployees.Close
   cnn1.Close

End Sub
'EndAddNewVB

See Also

AddNew Method | Recordset Object


Append and CreateParameter Methods Example (VB)

This example uses the Append and CreateParameter methods to execute a stored procedure with an input parameter.

'BeginAppendVB
Public Sub AppendX()

   Dim cnn1 As ADODB.Connection
   Dim cmdByRoyalty As ADODB.Command
   Dim prmByRoyalty As ADODB.Parameter
   Dim rstByRoyalty As ADODB.Recordset
   Dim rstAuthors As ADODB.Recordset
   Dim intRoyalty As Integer
   Dim strAuthorID As String
   Dim strCnn As String

   ' Open connection.
   Set cnn1 = New ADODB.Connection
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   cnn1.Open strCnn
   cnn1.CursorLocation = adUseClient

   ' Open command object with one parameter.
   Set cmdByRoyalty = New ADODB.Command
   cmdByRoyalty.CommandText = "byroyalty"
   cmdByRoyalty.CommandType = adCmdStoredProc

   ' Get parameter value and append parameter.
   intRoyalty = Trim(InputBox("Enter royalty:"))
   Set prmByRoyalty = cmdByRoyalty.CreateParameter("percentage", _
      adInteger, adParamInput)
   cmdByRoyalty.Parameters.Append prmByRoyalty
   prmByRoyalty.Value = intRoyalty

   ' Create recordset by executing the command.
   Set cmdByRoyalty.ActiveConnection = cnn1
   Set rstByRoyalty = cmdByRoyalty.Execute

   ' Open the Authors table to get author names for display.
   Set rstAuthors = New ADODB.Recordset
   rstAuthors.Open "Authors", cnn1, , , adCmdTable

   ' Print current data in the recordset, adding
   ' author names from Authors table.
   Debug.Print "Authors with " & intRoyalty & " percent royalty"
   Do While Not rstByRoyalty.EOF
      strAuthorID = rstByRoyalty!au_id
      Debug.Print "   " & rstByRoyalty!au_id & ", ";
      rstAuthors.Filter = "au_id = '" & strAuthorID & "'"
      Debug.Print rstAuthors!au_fname & " " & rstAuthors!au_lname
      rstByRoyalty.MoveNext
   Loop

   rstByRoyalty.Close
   rstAuthors.Close
   cnn1.Close

End Sub
'EndAppendVB

See Also

Append Method | CreateParameter Method | Field Object | Fields Collection | Parameter Object


AppendChunk and GetChunk Methods Example (VB)

This example uses the AppendChunk and GetChunk methods to fill an image field with data from another record.

'BeginAppendChunkVB
Public Sub AppendChunkX()

   Dim cnn1 As ADODB.Connection
   Dim rstPubInfo As ADODB.Recordset
   Dim strCnn As String
   Dim strPubID As String
   Dim strPRInfo As String
   Dim lngOffset As Long
   Dim lngLogoSize As Long
   Dim varLogo As Variant
   Dim varChunk As Variant

   Const conChunkSize = 100

   ' Open a connection.
   Set cnn1 = New ADODB.Connection
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   cnn1.Open strCnn

   ' Open the pub_info table.
   Set rstPubInfo = New ADODB.Recordset
   rstPubInfo.CursorType = adOpenKeyset
   rstPubInfo.LockType = adLockOptimistic
   rstPubInfo.Open "pub_info", cnn1, , , adCmdTable

   ' Prompt for a logo to copy.
   Dim strMsg As String
   strMsg = "Available logos are : " & vbCr & vbCr
   Do While Not rstPubInfo.EOF
      strMsg = strMsg & rstPubInfo!pub_id & vbCr & _
         Left(rstPubInfo!pr_info, InStr(rstPubInfo!pr_info, ",") - 1) & _
         vbCr & vbCr
      rstPubInfo.MoveNext
   Loop
   strMsg = strMsg & "Enter the ID of a logo to copy:"
   strPubID = InputBox(strMsg)

   ' Copy the logo to a variable in chunks.
   rstPubInfo.Filter = "pub_id = '" & strPubID & "'"
   lngLogoSize = rstPubInfo!logo.ActualSize
   Do While lngOffset < lngLogoSize
      varChunk = rstPubInfo!logo.GetChunk(conChunkSize)
      varLogo = varLogo & varChunk
      lngOffset = lngOffset + conChunkSize
   Loop

   ' Get data from the user.
   strPubID = Trim(InputBox("Enter a new pub ID" & _
                            " [must be > 9899 & < 9999]:"))
   strPRInfo = Trim(InputBox("Enter descriptive text:"))

   ' Add the new publisher to the publishers table to avoid
   ' getting an error due to foreign key constraint.
   cnn1.Execute "INSERT publishers(pub_id, pub_name) VALUES('" & _
                  strPubID & "','Your Test Publisher')"

   ' Add a new record, copying the logo in chunks.
   rstPubInfo.AddNew
   rstPubInfo!pub_id = strPubID
   rstPubInfo!pr_info = strPRInfo

   lngOffset = 0 ' Reset offset.
   Do While lngOffset < lngLogoSize
      varChunk = LeftB(RightB(varLogo, lngLogoSize - lngOffset), _
         conChunkSize)
      rstPubInfo!logo.AppendChunk varChunk
      lngOffset = lngOffset + conChunkSize
   Loop
   rstPubInfo.Update

    ' Show the newly added data.
   MsgBox "New record: " & rstPubInfo!pub_id & vbCr & _
      "Description: " & rstPubInfo!pr_info & vbCr & _
      "Logo size: " & rstPubInfo!logo.ActualSize

   ' Delete new records because this is a demonstration.
   rstPubInfo.Requery
   cnn1.Execute "DELETE FROM pub_info " & _
      "WHERE pub_id = '" & strPubID & "'"

   cnn1.Execute "DELETE FROM publishers " & _
      "WHERE pub_id = '" & strPubID & "'"

   rstPubInfo.Close
   cnn1.Close

End Sub
'EndAppendChunkVB

See Also

AppendChunk Method | Field Object | GetChunk Method | Parameter Object


Attributes and Name Properties Example (VB)

This example displays the value of the Attributes property for Connection, Field, and Property objects. It uses the Name property to display the name of each Field and Property object.

'BeginAttributesVB
Public Sub AttributesX()

   Dim cnn1 As ADODB.Connection
   Dim rstEmployees As ADODB.Recordset
   Dim fldLoop As ADODB.Field
   Dim proLoop As ADODB.Property
   Dim strCnn As String

   ' Open connection and recordset.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set cnn1 = New ADODB.Connection
   cnn1.Open strCnn
   Set rstEmployees = New ADODB.Recordset
   rstEmployees.Open "employee", cnn1, , , adCmdTable

   ' Display the attributes of the connection.
   Debug.Print "Connection attributes = " & _
      cnn1.Attributes

   ' Display the attributes of the Employee table's
   ' fields.
   Debug.Print "Field attributes:"
   For Each fldLoop In rstEmployees.fields
      Debug.Print "   " & fldLoop.Name & " = " & _
         fldLoop.Attributes
   Next fldLoop

   ' Display Fields of the Employee table which are NULLABLE.
   Debug.Print "NULLABLE Fields:"
   For Each fldLoop In rstEmployees.fields
      If CBool(fldLoop.Attributes And adFldIsNullable) Then
         Debug.Print "   " & fldLoop.Name
      End If
   Next fldLoop

   ' Display the attributes of the Employee table's
   ' properties.
   Debug.Print "Property attributes:"
   For Each proLoop In rstEmployees.Properties
      Debug.Print "   " & proLoop.Name & " = " & _
         proLoop.Attributes
   Next proLoop

   rstEmployees.Close
   cnn1.Close

End Sub
'EndAttributesVB

See Also

Attributes Property | Connection Object | Field Object | Name Property | Property Object


BeginTrans, CommitTrans, and RollbackTrans Methods Example (VB)

This example changes the book type of all psychology books in the Titles table of the database. After the BeginTrans method starts a transaction that isolates all the changes made to the Titles table, the CommitTrans method saves the changes. You can use the RollbackTrans method to undo changes that you saved using the Update method.

'BeginBeginTransVB
Public Sub BeginTransX()

   Dim cnn1 As ADODB.Connection
   Dim rstTitles As ADODB.Recordset
   Dim strCnn As String
   Dim strTitle As String
   Dim strMessage As String

   ' Open connection.
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set cnn1 = New ADODB.Connection
   cnn1.Open strCnn

   ' Open Titles table.
   Set rstTitles = New ADODB.Recordset
   rstTitles.CursorType = adOpenDynamic
   rstTitles.LockType = adLockPessimistic
   rstTitles.Open "titles", cnn1, , , adCmdTable

   rstTitles.MoveFirst
   cnn1.BeginTrans

   ' Loop through recordset and ask user if she wants
   ' to change the type for a specified title.
   Do Until rstTitles.EOF
      If Trim(rstTitles!Type) = "psychology" Then
         strTitle = rstTitles!title
         strMessage = "Title: " & strTitle & vbCr & _
         "Change type to self help?"

         ' Change the title for the specified
         ' employee.
         If MsgBox(strMessage, vbYesNo) = vbYes Then
            rstTitles!Type = "self_help"
            rstTitles.Update
         End If
      End If

         rstTitles.MoveNext
   Loop

   ' Ask if the user wants to commit to all the
   ' changes made above.
   If MsgBox("Save all changes?", vbYesNo) = vbYes Then
      cnn1.CommitTrans
   Else
      cnn1.RollbackTrans
   End If

   ' Print current data in recordset.
   rstTitles.Requery
   rstTitles.MoveFirst
   Do While Not rstTitles.EOF
      Debug.Print rstTitles!title & " - " & rstTitles!Type
      rstTitles.MoveNext
   Loop

   ' Restore original data because this
   ' is a demonstration.
   rstTitles.MoveFirst
   Do Until rstTitles.EOF
      If Trim(rstTitles!Type) = "self_help" Then
         rstTitles!Type = "psychology"
         rstTitles.Update
      End If
      rstTitles.MoveNext
   Loop

   rstTitles.Close
   cnn1.Close

End Sub
'EndBeginTransVB

See Also

BeginTrans, CommitTrans, and RollbackTrans Methods | Connection Object


BOF, EOF, and Bookmark Properties Example (VB)

This example uses the BOF and EOF properties to display a message if a user tries to move past the first or last record of a Recordset. It uses the Bookmark property to let the user flag a record in a Recordset and return to it later.

'BeginBOFVB
Public Sub BOFX()

   Dim rstPublishers As ADODB.Recordset
   Dim strCnn As String
   Dim strMessage As String
   Dim intCommand As Integer
   Dim varBookmark As Variant

   ' Open recordset with data from Publishers table.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstPublishers = New ADODB.Recordset
   rstPublishers.CursorType = adOpenStatic
   ' Use client cursor to enable AbsolutePosition property.
   rstPublishers.CursorLocation = adUseClient
   rstPublishers.Open "SELECT pub_id, pub_name FROM publishers " & _
      "ORDER BY pub_name", strCnn, , , adCmdText

   rstPublishers.MoveFirst

   Do While True
      ' Display information about current record
      ' and get user input.
      strMessage = "Publisher: " & rstPublishers!pub_name & _
         vbCr & "(record " & rstPublishers.AbsolutePosition & _
         " of " & rstPublishers.RecordCount & ")" & vbCr & vbCr & _
         "Enter command:" & vbCr & _
         "[1 - next / 2 - previous /" & vbCr & _
         "3 - set bookmark / 4 - go to bookmark]"
      intCommand = Val(InputBox(strMessage))

      Select Case intCommand
         ' Move forward or backward, trapping for BOF
         ' or EOF.
         Case 1
            rstPublishers.MoveNext
            If rstPublishers.EOF Then
               MsgBox "Moving past the last record." & _
                  vbCr & "Try again."
               rstPublishers.MoveLast
            End If
         Case 2
            rstPublishers.MovePrevious
            If rstPublishers.BOF Then
               MsgBox "Moving past the first record." & _
                  vbCr & "Try again."
               rstPublishers.MoveFirst
            End If

         ' Store the bookmark of the current record.
         Case 3
            varBookmark = rstPublishers.Bookmark

         ' Go to the record indicated by the stored
         ' bookmark.
         Case 4
            If IsEmpty(varBookmark) Then
               MsgBox "No Bookmark set!"
            Else
               rstPublishers.Bookmark = varBookmark
            End If

         Case Else
            Exit Do
      End Select

   Loop

   rstPublishers.Close

End Sub
'EndBOFVB

This example uses the Bookmark and Filter properties to create a limited view of the Recordset. Only records referenced by the array of bookmarks are accessible.

'BeginBOF2VB
Public Sub BOFX2()

Dim rs As New ADODB.Recordset
Dim bmk(10)

rs.CursorLocation = adUseClient
rs.ActiveConnection = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=;"

rs.Open "select * from Authors", , adOpenStatic, adLockBatchOptimistic
Debug.Print "Number of records before filtering: ", rs.RecordCount

Dim ii As Integer
ii = 0
While rs.EOF <> True And ii < 11
    bmk(ii) = rs.Bookmark
    ii = ii + 1
    rs.Move 2
Wend
rs.Filter = bmk
Debug.Print "Number of records after filtering: ", rs.RecordCount

rs.MoveFirst
While rs.EOF <> True
    Debug.Print rs.AbsolutePosition, rs("au_lname")
    rs.MoveNext
Wend

End Sub
'EndBOF2VB

See Also

BOF, EOF Properties | Bookmark Property | Recordset Object


CacheSize Property Example (VB)

This example uses the CacheSize property to show the difference in performance for an operation performed with and without a 30-record cache.

'BeginCacheSizeVB
Public Sub CacheSizeX()

   Dim rstRoySched As ADODB.Recordset
   Dim strCnn As String
   Dim sngStart As Single
   Dim sngEnd As Single
   Dim sngNoCache As Single
   Dim sngCache As Single
   Dim intLoop As Integer
   Dim strTemp As String

   ' Open the RoySched table.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstRoySched = New ADODB.Recordset
   rstRoySched.Open "roysched", strCnn, , , adCmdTable

   ' Enumerate the Recordset object twice and record
   ' the elapsed time.
   sngStart = Timer

   For intLoop = 1 To 2
      rstRoySched.MoveFirst

      Do While Not rstRoySched.EOF
         ' Execute a simple operation for the
         ' performance test.
         strTemp = rstRoySched!title_id
         rstRoySched.MoveNext
      Loop
   Next intLoop

   sngEnd = Timer
   sngNoCache = sngEnd - sngStart

   ' Cache records in groups of 30 records.
   rstRoySched.MoveFirst
   rstRoySched.CacheSize = 30
   sngStart = Timer

   ' Enumerate the Recordset object twice and record
   ' the elapsed time.
   For intLoop = 1 To 2

      rstRoySched.MoveFirst
      Do While Not rstRoySched.EOF
         ' Execute a simple operation for the
         ' performance test.
         strTemp = rstRoySched!title_id
         rstRoySched.MoveNext
      Loop
   Next intLoop

   sngEnd = Timer
   sngCache = sngEnd - sngStart

   ' Display performance results.
   MsgBox "Caching Performance Results:" & vbCr & _
      "   No cache: " & Format(sngNoCache, _
      "##0.000") & " seconds" & vbCr & _
      "   30-record cache: " & Format(sngCache, _
      "##0.000") & " seconds"
   rstRoySched.Close

End Sub
'EndCacheSizeVB

See Also

CacheSize Property | Recordset Object


Cancel Method Example (VB)

This example uses the Cancel method to cancel a command executing on a Connection object if the connection is busy.

'BeginCancelVB
Public Sub CancelX()

   Dim cnn1 As ADODB.Connection
   Dim strCnn As String
   Dim strCmdChange As String
   Dim strCmdRestore As String
   Dim booChanged As Boolean

   ' Open a connection.
   Set cnn1 = New ADODB.Connection
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   cnn1.Open strCnn

   ' Define command strings.
   strCmdChange = "UPDATE titles SET type = 'self_help' " & _
      "WHERE type = 'psychology'"
   strCmdRestore = "UPDATE titles SET type = 'psychology' " & _
      "WHERE type = 'self_help'"

   ' Begin a transaction, then execute a command asynchronously.
   cnn1.BeginTrans
   cnn1.Execute strCmdChange, , adAsyncExecute

   ' do something else for a little while – this could be changed
    Dim i As Integer
      For i = 1 To 10
         i = i + i
         Debug.Print i
      Next i

   ' If the command has NOT completed, cancel the execute
   ' and roll back the transaction. Otherwise, commit the
   ' transaction.
   If CBool(cnn1.State And adStateExecuting) Then
      cnn1.Cancel
      cnn1.RollbackTrans
      booChanged = False
      MsgBox "Update canceled."
   Else
      cnn1.CommitTrans
      booChanged = True
      MsgBox "Update complete."
   End If

   ' If the change was made, restore the data
   ' because this is a demonstration.
   If booChanged Then
      cnn1.Execute strCmdRestore
      MsgBox "Data restored."
   End If

   cnn1.Close

End Sub
'EndCancelVB

See Also

Cancel Method | Connection Object


Clone Method Example (VB)

This example uses the Clone method to create copies of a Recordset and then lets the user position the record pointer of each copy independently.

'BeginCancelVB
Public Sub CancelX()

   Dim cnn1 As ADODB.Connection
   Dim strCnn As String
   Dim strCmdChange As String
   Dim strCmdRestore As String
   Dim booChanged As Boolean

   ' Open a connection.
   Set cnn1 = New ADODB.Connection
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   cnn1.Open strCnn

   ' Define command strings.
   strCmdChange = "UPDATE titles SET type = 'self_help' " & _
      "WHERE type = 'psychology'"
   strCmdRestore = "UPDATE titles SET type = 'psychology' " & _
      "WHERE type = 'self_help'"

   ' Begin a transaction, then execute a command asynchronously.
   cnn1.BeginTrans
   cnn1.Execute strCmdChange, , adAsyncExecute

   ' do something else for a little while – this could be changed
    Dim i As Integer
      For i = 1 To 10
         i = i + i
         Debug.Print i
      Next i

   ' If the command has NOT completed, cancel the execute
   ' and roll back the transaction. Otherwise, commit the
   ' transaction.
   If CBool(cnn1.State And adStateExecuting) Then
      cnn1.Cancel
      cnn1.RollbackTrans
      booChanged = False
      MsgBox "Update canceled."
   Else
      cnn1.CommitTrans
      booChanged = True
      MsgBox "Update complete."
   End If

   ' If the change was made, restore the data
   ' because this is a demonstration.
   If booChanged Then
      cnn1.Execute strCmdRestore
      MsgBox "Data restored."
   End If

   cnn1.Close

End Sub
'EndCancelVB

See Also

Clone Method | Recordset Object


CompareBookmarks Method Example (VB)

This example demonstrates the CompareBookmarks method. The relative value of bookmarks is seldom needed unless a particular bookmark is somehow special.

Designate a random row of a Recordset derived from the Authors table as the target of a search. Then display the position of each row relative to that target.

'BeginCompareBookmarksVB
Public Sub CompareBookmarksX()
Dim rst As ADODB.Recordset
Dim count As Integer
Dim target As Variant
Dim result As Long
Dim strAns As String
Dim strTitle As String
strTitle = "CompareBookmarks Example"

Set rst = New ADODB.Recordset
rst.Open "SELECT * FROM Authors", _
         "DSN=Pubs;Provider=MSDASQL; uid=sa;pwd=;", _
         adOpenStatic, adLockReadOnly, adCmdText

count = rst.RecordCount
Debug.Print "Rows in the Recordset = "; count
If count = 0 Then Exit Sub   'Exit if an empty recordset

Randomize
count = (Int(count * Rnd))   'Get position between 0 and count-1
Debug.Print "Randomly chosen row position = "; count
rst.Move count, adBookmarkFirst 'Move row to random position
target = rst.Bookmark        'Remember the mystery row.

count = 0
rst.MoveFirst
Do While Not rst.EOF         'Loop through recordset
   result = rst.CompareBookmarks(rst.Bookmark, target)
   If result = adCompareNotEqual Then
      Debug.Print "Row "; count; ": Bookmarks are not equal."
   ElseIf result = adCompareNotComparable Then
      Debug.Print "Row "; count; ": Bookmarks are not comparable."
   Else
      Select Case result
         Case adCompareLessThan
         strAns = "less than"
         Case adCompareEqual
            strAns = "equal to"
         Case adCompareGreaterThan
            strAns = "greater than"
         Case Else
            strAns = "in error comparing to"
      End Select
      Debug.Print "Row position " & count & " is " & strAns & _
                  " the target."
   End If
   count = count + 1
   rst.MoveNext
Loop

rst.Close
End Sub
'EndCompareBookmarksVB

See Also

CompareBookmarks Method | CompareEnum | Recordset Object


ConnectionString, ConnectionTimeout, and State Properties Example (VB)

This example demonstrates different ways of using the ConnectionString property to open a Connection object. It also uses the ConnectionTimeout property to set a connection timeout period, and the State property to check the state of the connections. The GetState function is required for this procedure to run.

'BeginConnectionStringVB
Public Sub ConnectionStringX()

   Dim cnn1 As ADODB.Connection
   Dim cnn2 As ADODB.Connection
   Dim cnn3 As ADODB.Connection
   Dim cnn4 As ADODB.Connection

   ' Open a connection without using a Data Source Name (DSN).
   Set cnn1 = New ADODB.Connection
   cnn1.ConnectionString = "driver={SQL Server};" & _
      "server=srv;uid=sa;pwd=pwd;database=Pubs"
   cnn1.ConnectionTimeout = 30
   cnn1.Open

   ' Open a connection using a DSN and ODBC tags.
   Set cnn2 = New ADODB.Connection
   cnn2.ConnectionString = "DSN=Pubs;UID=sa;PWD=pwd;"
   cnn2.Open

   ' Open a connection using a DSN and OLE DB tags.
   Set cnn3 = New ADODB.Connection
   cnn3.ConnectionString = "Data Source=Pubs;User ID=sa;Password=pwd;"
   cnn3.Open

   ' Open a connection using a DSN and individual
   ' arguments instead of a connection string.
   Set cnn4 = New ADODB.Connection
   cnn4.Open "Pubs", "sa", "pwd"

   ' Display the state of the connections.
   MsgBox "cnn1 state: " & GetState(cnn1.State) & vbCr & _
      "cnn2 state: " & GetState(cnn2.State) & vbCr & _
      "cnn3 state: " & GetState(cnn3.State) & vbCr & _
      "cnn4 state: " & GetState(cnn4.State)

   cnn4.Close
   cnn3.Close
   cnn2.Close
   cnn1.Close

End Sub

Public Function GetState(intState As Integer) As String

   Select Case intState
      Case adStateClosed
         GetState = "adStateClosed"
      Case adStateOpen
         GetState = "adStateOpen"
   End Select

End Function
'EndConnectionStringVB

See Also

Connection Object | ConnectionString Property | ConnectionTimeout Property | State Property


ConvertToString Method Example (VB)

'BeginConvertToStringVB
Public Sub ConvertToStringX()
    Dim oRDF As RDSServer.DataFactory
    Dim oRDC As RDS.DataControl
    Dim oRs As ADODB.Recordset
    Dim vString As Variant
    Dim sConnString As String
    Dim sSQL As String

    ' Create a DataFactory object
    Set oRDF = New RDSServer.DataFactory

    ' Get all of the Author records
    sConnString = "Provider=sqloledb;" & _
                        "Data Source=MyServer;" & _
                        "Initial Catalog=pubs;" & _
                        "User ID=sa;" & _
                        "Password=;"
    sSQL = "Select * From authors"
    Set oRs = oRDF.Query(sConnString, sSQL)
    Debug.Print oRs.RecordCount

    ' Convert the recordset into a MIME formatted string
    vString = oRDF.ConvertToString(oRs)

    ' Convert string value back into an ADO Recordset
    Set oRDC = New RDS.DataControl
    oRDC.SQL = vString
    oRDC.ExecuteOptions = adcExecSync
    oRDC.FetchOptions = adcFetchUpFront
    oRDC.Refresh
    Debug.Print oRDC.Recordset.RecordCount
End Sub
'EndConvertToStringVB

See Also

ConvertToString Method (RDS) | Recordset Object


CopyRecord, CopyTo, and SaveToFile Methods Example (VB)

This example demonstrates how to create copies of a file using Stream or Record objects. One copy is made to a Web folder for Internet publishing. Other properties and methods shown include Stream Type, Open, LoadFromFile, and Record Open.

'BeginCopyRecordVB
Option Explicit

Public Sub CopyRecordX()
    ' Declare variables
    Dim strPicturePath, strStreamPath, strStream2Path, _
        strRecordPath, strStreamURL, strRecordURL As String
    Dim objStream, objStream2 As Stream
    Dim objRecord As Record
    Dim objField As Field

    ' Instantiate objects
    Set objStream = New Stream
    Set objStream2 = New Stream
    Set objRecord = New Record

    ' Initialize path and URL strings
    strPicturePath = _
    "C:\Program Files\Microsoft Office\Clipart\Popular\Checkmrk.wmf"
    strStreamPath = "\\websrv\folder\mywmf.wmf"
    strStreamURL = "URL=http://websrv/folder/mywmf.wmf"
    strStream2Path = "D:\samples\check2.wmf"
    strRecordPath = "\\websrv\folder\mywmf.wmf"
    strRecordURL = "http://websrv/folder/mywmf.wmf"

    ' Load the file into the stream
    objStream.Open
    objStream.Type = adTypeBinary
    objStream.LoadFromFile (strPicturePath)

    ' Save the stream to a new path and filename
    objStream.SaveToFile strStreamPath, adSaveCreateOverWrite

    ' Copy the contents of the first stream to a second stream
    objStream2.Open
    objStream2.Type = adTypeBinary
    objStream.CopyTo objStream2

    ' Save the second stream to a different path
    objStream2.SaveToFile strStream2Path, adSaveCreateOverWrite

    ' Because strStreamPath is a Web Folder, open a Record on the URL
    objRecord.Open "", strStreamURL

    ' Display the Fields of the record
    For Each objField In objRecord.fields
        Debug.Print objField.Name & ": " & objField.Value
    Next

    ' Copy the record to a new URL
    objRecord.CopyRecord "", strRecordURL, , , adCopyOverWrite

    ' Load each copy of the graphic into Image controls for viewing
    Image1.Picture = LoadPicture(strPicturePath)
    Image2.Picture = LoadPicture(strStreamPath)
    Image3.Picture = LoadPicture(strStream2Path)
    Image4.Picture = LoadPicture(strRecordPath)

    ' Clean up
    objStream.Close
    objStream2.Close
    objRecord.Close
End Sub
'EndCopyRecordVB

See Also

CopyRecord Method | CopyTo Method | LoadFromFile Method | Open Method (ADO Record) | Open Method (ADO Stream) | Record Object | SaveToFile Method | Stream Object | Type Property (ADO Stream)


Count Property Example (VB)

This example demonstrates the Count property with two collections in the Employee database. The property obtains the number of objects in each collection, and sets the upper limit for loops that enumerate these collections. Another way to enumerate these collections without using the Count property would be to use For Each...Next statements.

'BeginCountVB
Public Sub CountX()

   Dim rstEmployees As ADODB.Recordset
   Dim strCnn As String
   Dim intLoop As Integer

   ' Open recordset with data from Employee table.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstEmployees = New ADODB.Recordset
   rstEmployees.Open "employee", strCnn, , , adCmdTable

   ' Print information about Fields collection.
   Debug.Print rstEmployees.fields.count & _
      " Fields in Employee"
   For intLoop = 0 To rstEmployees.fields.count - 1
      Debug.Print "   " & rstEmployees.fields(intLoop).Name
   Next intLoop

   ' Print information about Properties collection.
   Debug.Print rstEmployees.Properties.count & _
      " Properties in Employee"
   For intLoop = 0 To rstEmployees.Properties.count - 1
      Debug.Print "   " & rstEmployees.Properties(intLoop).Name
   Next intLoop

   rstEmployees.Close

End Sub
'EndCountVB

See Also

Count Property


CreateRecordset Method Example (VB)

You can create a Recordset object and specify the column information. You can then insert data into the Recordset object; the underlying rowset buffers the inserts.

The following code example shows how to define a Recordset by using the RDSServer.DataFactory object. You can also do this with the RDS.DataControl object.

'BeginRsDefineShapeVB
Sub RsDefineShape()

   Dim ADF As RDSServer.DataFactory
   Dim vntRecordShape(3)
   Dim vntField1Shape(3)
   Dim vntField2Shape(3)
   Dim vntField3Shape(3)
   Dim vntField4Shape(3)

   Set ADF = New RDSServer.DataFactory

   ' For each field, specify the name,
   ' type, size, and nullability.

   vntField1Shape(0) = "Name"   ' Column name.
   vntField1Shape(1) = CInt(129)   ' Column type.
   vntField1Shape(2) = CInt(40)   ' Column size.
   vntField1Shape(3) = False      ' Nullable?

   vntField2Shape(0) = "Age"
   vntField2Shape(1) = CInt(3)
   vntField2Shape(2) = CInt(-1)
   vntField2Shape(3) = True

   vntField3Shape(0) = "DateOfBirth"
   vntField3Shape(1) = CInt(7)
   vntField3Shape(2) = CInt(-1)
   vntField3Shape(3) = True

   vntField4Shape(0) = "Balance"
   vntField4Shape(1) = CInt(6)
   vntField4Shape(2) = CInt(-1)
   vntField4Shape(3) = True

   ' Put all fields into an array of arrays.
   vntRecordShape(0) = vntField1Shape
   vntRecordShape(1) = vntField2Shape
   vntRecordShape(2) = vntField3Shape
   vntRecordShape(3) = vntField4Shape

   ' Use the RDSServer.DataFactory to create an empty
   ' recordset. It takes an array of variants where
   ' every element is itself another array of
   ' variants, one for every column required in the
   ' recordset.
   ' The elements of the inner array are the column's
   ' name, type, size, and nullability.
   '
   ' NOTE: You could just use the RDS.DataControl object
   ' instead of the RDSServer.DataFactory object. In
   ' that case, the following code would be Set NewRS
   ' = ADC1.CreateRecordset(vntRecordShape)
   Dim NewRs As ADODB.Recordset
   Set NewRs = ADF.CreateRecordSet(vntRecordShape)

   Dim fields(3)
   fields(0) = vntField1Shape(0)
   fields(1) = vntField2Shape(0)
   fields(2) = vntField3Shape(0)
   fields(3) = vntField4Shape(0)

   ' Populate the new recordset with data values.
   Dim fieldVals(3)

   ' Use AddNew to add the records.
   fieldVals(0) = "Joe"
   fieldVals(1) = 5
   fieldVals(2) = CDate(#1/5/1996#)
   fieldVals(3) = 123.456
   NewRs.AddNew fields, fieldVals

   fieldVals(0) = "Mary"
   fieldVals(1) = 6
   fieldVals(2) = CDate(#6/5/1996#)
   fieldVals(3) = 31
   NewRs.AddNew fields, fieldVals

   fieldVals(0) = "Alex"
   fieldVals(1) = 13
   fieldVals(2) = CDate(#1/6/1996#)
   fieldVals(3) = 34.0001
   NewRs.AddNew fields, fieldVals

   fieldVals(0) = "Susan"
   fieldVals(1) = 13
   fieldVals(2) = CDate(#8/6/1996#)
   fieldVals(3) = 0#
   NewRs.AddNew fields, fieldVals

   NewRs.MoveFirst

   ' Set the newly created and populated Recordset to
   ' the SourceRecordset property of the
   ' RDS.DataControl to bind to visual controls
   Dim ADC1 As RDS.DataControl
   Set ADC1 = New RDS.DataControl
   Set ADC1.SourceRecordset = NewRs

End Sub
'EndRsDefineShapeVB


CursorType, LockType, and EditMode Properties Example (VB)

This example demonstrates setting the CursorType and LockType properties before opening a Recordset. It also shows the value of the EditMode property under various conditions. The EditModeOutput function is required for this procedure to run.

'BeginEditModeVB
Public Sub EditModeX()

   Dim cnn1 As ADODB.Connection
   Dim rstEmployees As ADODB.Recordset
   Dim strCnn As String

   ' Open recordset with data from Employee table.
   Set cnn1 = New ADODB.Connection
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   cnn1.Open strCnn

   Set rstEmployees = New ADODB.Recordset
   Set rstEmployees.ActiveConnection = cnn1
   rstEmployees.CursorLocation = adUseClient
   rstEmployees.CursorType = adOpenStatic
   rstEmployees.LockType = adLockBatchOptimistic
   rstEmployees.Open "employee", , , , adCmdTable

   ' Show the EditMode property under different editing
   ' states.
   rstEmployees.AddNew
   rstEmployees!emp_id = "T-T55555M"
   rstEmployees!fname = "temp_fname"
   rstEmployees!lname = "temp_lname"
   EditModeOutput "After AddNew:", rstEmployees.EditMode
   rstEmployees.UpdateBatch
   EditModeOutput "After UpdateBatch:", rstEmployees.EditMode
   rstEmployees!fname = "test"
   EditModeOutput "After Edit:", rstEmployees.EditMode
   rstEmployees.Close

   ' Delete new record because this is a demonstration.
   cnn1.Execute "DELETE FROM employee WHERE emp_id = 'T-T55555M'"

End Sub

Public Function EditModeOutput(strTemp As String, _
   intEditMode As Integer)

   ' Print report based on the value of the EditMode
   ' property.
   Debug.Print strTemp
   Debug.Print "  EditMode = ";

   Select Case intEditMode
      Case adEditNone
         Debug.Print "adEditNone"
      Case adEditInProgress
         Debug.Print "adEditInProgress"
      Case adEditAdd
         Debug.Print "adEditAdd"
   End Select

End Function
'EndEditModeVB

See Also

CursorType Property | CursorTypeEnum | EditMode Property | EditModeEnum | LockType Property | LockTypeEnum | Recordset Object


Delete Method Example (VB)

This example uses the Delete method to remove a specified record from a Recordset.

'BeginDeleteVB
Public Sub DeleteX()

   Dim rstRoySched As ADODB.Recordset
   Dim strCnn As String
   Dim strMsg As String
   Dim strTitleID As String
   Dim intLoRange As Integer
   Dim intHiRange As Integer
   Dim intRoyalty As Integer

   ' Open RoySched table.
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstRoySched = New ADODB.Recordset
   rstRoySched.CursorLocation = adUseClient
   rstRoySched.CursorType = adOpenStatic
   rstRoySched.LockType = adLockBatchOptimistic
   rstRoySched.Open "SELECT * FROM roysched " & _
      "WHERE royalty = 20", strCnn, , , adCmdText

   ' Prompt for a record to delete.
   strMsg = "Before delete there are " & _
      rstRoySched.RecordCount & _
      " titles with 20 percent royalty:" & vbCr & vbCr
   Do While Not rstRoySched.EOF
      strMsg = strMsg & rstRoySched!title_id & vbCr
      rstRoySched.MoveNext
   Loop
   strMsg = strMsg & vbCr & vbCr & _
      "Enter the ID of a record to delete:"
   strTitleID = UCase(InputBox(strMsg))

   ' Move to the record and save data so it can be restored.
   rstRoySched.Filter = "title_id = '" & strTitleID & "'"
   intLoRange = rstRoySched!lorange
   intHiRange = rstRoySched!hirange
   intRoyalty = rstRoySched!royalty

   ' Delete the record.
   rstRoySched.delete
   rstRoySched.UpdateBatch

   ' Show the results.
   rstRoySched.Filter = adFilterNone
   rstRoySched.Requery
   strMsg = ""
   strMsg = "After delete there are " & _
      rstRoySched.RecordCount & _
      " titles with 20 percent royalty:" & vbCr & vbCr
   Do While Not rstRoySched.EOF
      strMsg = strMsg & rstRoySched!title_id & vbCr
      rstRoySched.MoveNext
   Loop
   MsgBox strMsg

   ' Restore the data because this is a demonstration.
   rstRoySched.AddNew
   rstRoySched!title_id = strTitleID
   rstRoySched!lorange = intLoRange
   rstRoySched!hirange = intHiRange
   rstRoySched!royalty = intRoyalty
   rstRoySched.UpdateBatch

   rstRoySched.Close

End Sub
'EndDeleteVB

See Also

Delete Method (ADO Recordset) | Recordset Object


DeleteRecord and MoveRecord Methods Example (VB)

This example demonstrates how to copy, move, edit, and delete the contents of a text file published to a Web folder. Other properties and methods used include GetChildren, ParentURL, Source, and Flush.

'BeginDeleteRecordVB
Public Sub DeleteRecordX()
    'Declare and instantiate object variables
    Dim objFileR As Record
    Dim objDestFolderR As Record
    Dim objDestFileR As Record
    Dim objDestFolderRS As Recordset
    Dim objStream As Stream

    Set objFileR = New Record
    Set objDestFolderR = New Record
    Set objDestFileR = New Record
    Set objDestFolderRS = New Recordset
    Set objStream = New Stream

    'Open a Record on a text file
    objFileR.Open "", "URL=http://websrv/folder/file.txt", _
        adModeReadWrite, adOpenIfExists Or adCreateNonCollection

    'Edit the contents of the text file
    MsgBox "Edit"

    objStream.Open objFileR, , adOpenStreamFromRecord
    Debug.Print "Source: " + objFileR.Source
    Debug.Print "Original text: " + objStream.ReadText
    objStream.Position = 0
    objStream.WriteText "New Text"
    objStream.Position = 0
    Debug.Print "New text: " + objStream.ReadText
    objStream.Flush
    objStream.Close
    objFileR.Close

    'Reopen Record to see new contents of text file
    objFileR.Open "file.txt", "URL=http://websrv/folder/", _
        adModeReadWrite, adOpenIfExists Or adCreateNonCollection
    objStream.Open objFileR, adModeReadWrite, adOpenStreamFromRecord
    Debug.Print "Source: " + objFileR.Source
    Debug.Print "Edited text: " + objStream.ReadText

    'Copy the file to a subfolder
    MsgBox "Copy"

    objFileR.CopyRecord , _
        "URL=http://websrv/folder/subfolder/file.txt", , , _
        adCopyOverWrite

    'Delete the original file
    MsgBox "Delete"

    objFileR.DeleteRecord

    'Move the file from the subfolder back to original location
    MsgBox "Move Back"

    objDestFolderR.Open "", "URL=http://websrv/folder/subfolder/", , _
        adOpenIfExists Or adCreateCollection

    Set objDestFolderRS = objDestFolderR.GetChildren

    objDestFolderRS.MoveFirst

    'Position current record at on the correct file
    Do While Not _
        (objDestFolderRS.EOF Or (objDestFolderRS(0) = "file.txt"))
        objDestFolderRS.MoveNext
    Loop

    'Open a Record on the currect row of the Recordset
    objDestFileR.Open objDestFolderRS

    'Do the move
    objDestFileR.MoveRecord , objDestFolderR.ParentURL + "/file.txt" _
        , , , adMoveOverWrite 'objDestFolderR.ParentURL

    MsgBox "All done"
End Sub
'EndDeleteRecordVB

See Also

DeleteRecord Method | Flush Method | GetChildren Method | MoveRecord Method | ParentURL Property | Source Property (ADO Record)


Description, HelpContext, HelpFile, NativeError, Number, Source, and SQLState Properties Example (VB)

This example triggers an error, traps it, and displays the Description, HelpContext, HelpFile, NativeError, Number, Source, and SQLState properties of the resulting Error object.

'BeginDescriptionVB
Public Sub DescriptionX()

   Dim cnn1 As ADODB.Connection
   Dim errLoop As ADODB.Error
   Dim strError As String

   On Error GoTo ErrorHandler

   ' Intentionally trigger an error.
   Set cnn1 = New ADODB.Connection
   cnn1.Open "nothing"

   Exit Sub

ErrorHandler:

   ' Enumerate Errors collection and display
   ' properties of each Error object.
   For Each errLoop In cnn1.Errors
      strError = "Error #" & errLoop.Number & vbCr & _
         "   " & errLoop.Description & vbCr & _
         "   (Source: " & errLoop.Source & ")" & vbCr & _
         "   (SQL State: " & errLoop.SQLState & ")" & vbCr & _
         "   (NativeError: " & errLoop.NativeError & ")" & vbCr
      If errLoop.HelpFile = "" Then
         strError = strError & _
            "   No Help file available" & _
            vbCr & vbCr
      Else
         strError = strError & _
            "   (HelpFile: " & errLoop.HelpFile & ")" & vbCr & _
            "   (HelpContext: " & errLoop.HelpContext & ")" & _
            vbCr & vbCr
      End If

   Debug.Print strError
   Next

   Resume Next

End Sub
'EndDescriptionVB

See Also

Description Property | Error Object | HelpContext Property | HelpFile Property | NativeError Property | Number Property | Source Property (ADO Error) | SQLState Property


EOS and LineSeparator Properties and SkipLine Method Example (VB)

This example demonstrates how to manipulate text streams one line at a time. The effect of changing the line separator from the default carriage return/linefeed (adCRLF) to simply linefeed (adLF) or carriage return (adCR) is shown.

'BeginSkipLineVB
Public Sub SkipLineX()
    'Declare variables
    Dim i As Integer
    Dim objStream As Stream
    Dim strLine, strChar As String

    'Instantiate and open stream
    Set objStream = New Stream
    objStream.Open

    'Set line separator to line feed
    objStream.LineSeparator = adLF

    'Load text content of list box into stream
    'One line at a time
    For i = 0 To (List1.ListCount - 1)
        objStream.WriteText List1.List(i), adWriteLine
    Next

    'Display the entire stream
    Debug.Print "Whole Stream:"
    objStream.Position = 0
    Debug.Print objStream.ReadText

    'Display the first line
    Debug.Print "First Line:"
    objStream.Position = 0
    strLine = objStream.ReadText(adReadLine)
    Debug.Print strLine
    Debug.Print "Line length: " + Str(Len(strLine))

    'Skip a line, then display another line
    Debug.Print "Third Line:"
    objStream.SkipLine
    strLine = objStream.ReadText(adReadLine)
    Debug.Print strLine
    Debug.Print "Line length: " + Str(Len(strLine))

    'Switch line separator to carriage return
    'All items from list will be considered one line
    'Assuming no CRs have been loaded into stream
    Debug.Print "Whole Stream/First Line:"
    objStream.Position = 0
    objStream.LineSeparator = adCR
    strLine = objStream.ReadText(adReadLine)
    Debug.Print strLine
    Debug.Print "Line length: " + Str(Len(strLine))
    Debug.Print "Stream size: " + Str(objStream.Size)

    'Use EOS to Determine End of Stream
    Debug.Print "Character by character:"
    objStream.Position = 0
    Do Until objStream.EOS
        strChar = objStream.ReadText(1)
        Debug.Print strChar
    Loop
End Sub
'EndSkipLineVB

See Also

EOS Property | LineSeparator Property | SkipLine Method


Execute, Requery, and Clear Methods Example (VB)

This example demonstrates the Execute method when run from both a Command object and a Connection object. It also uses the Requery method to retrieve current data in a Recordset, and the Clear method to clear the contents of the Errors collection. (The Errors collection is accessed via the Connection object of the ActiveConnection property of the Recordset.) The ExecuteCommand and PrintOutput procedures are required for this procedure to run.

'BeginExecuteVB
Public Sub ExecuteX()

   Dim strSQLChange As String
   Dim strSQLRestore As String
   Dim strCnn As String
   Dim cnn1 As ADODB.Connection
   Dim cmdChange As ADODB.Command
   Dim rstTitles As ADODB.Recordset
   Dim errLoop As ADODB.Error

   ' Define two SQL statements to execute as command text.
   strSQLChange = "UPDATE Titles SET Type = " & _
      "'self_help' WHERE Type = 'psychology'"
   strSQLRestore = "UPDATE Titles SET Type = " & _
      "'psychology' WHERE Type = 'self_help'"

   ' Open connection.
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set cnn1 = New ADODB.Connection
   cnn1.Open strCnn

   ' Create command object.
   Set cmdChange = New ADODB.Command
   Set cmdChange.ActiveConnection = cnn1
   cmdChange.CommandText = strSQLChange

   ' Open titles table.
   Set rstTitles = New ADODB.Recordset
   rstTitles.Open "titles", cnn1, , , adCmdTable

   ' Print report of original data.
   Debug.Print _
      "Data in Titles table before executing the query"
   PrintOutput rstTitles

   ' Clear extraneous errors from the Errors collection.
   cnn1.Errors.Clear

   ' Call the ExecuteCommand subroutine to execute cmdChange command.
   ExecuteCommand cmdChange, rstTitles

   ' Print report of new data.
   Debug.Print _
      "Data in Titles table after executing the query"
   PrintOutput rstTitles

   ' Use the Connection object's execute method to
   ' execute SQL statement to restore data. Trap for
   ' errors, checking the Errors collection if necessary.
   On Error GoTo Err_Execute
   cnn1.Execute strSQLRestore, , adExecuteNoRecords
   On Error GoTo 0

   ' Retrieve the current data by requerying the recordset.
   rstTitles.Requery

   ' Print report of restored data.
   Debug.Print "Data after executing the query " & _
      "to restore the original information"
   PrintOutput rstTitles

   rstTitles.Close
   cnn1.Close

   Exit Sub

Err_Execute:

   ' Notify user of any errors that result from
   ' executing the query.
   If rstTitles.ActiveConnection.Errors.count >= 0 Then
      For Each errLoop In rstTitles.ActiveConnection.Errors
         MsgBox "Error number: " & errLoop.Number & vbCr & _
            errLoop.Description
      Next errLoop
   End If
   Resume Next

End Sub

Public Sub ExecuteCommand(cmdTemp As ADODB.Command, _
   rstTemp As ADODB.Recordset)

   Dim errLoop As Error

   ' Run the specified Command object. Trap for
   ' errors, checking the Errors collection if necessary.
   On Error GoTo Err_Execute
   cmdTemp.Execute
   On Error GoTo 0

   ' Retrieve the current data by requerying the recordset.
   rstTemp.Requery

   Exit Sub

Err_Execute:

   ' Notify user of any errors that result from
   ' executing the query.
   If rstTemp.ActiveConnection.Errors.count > 0 Then
      For Each errLoop In rstTemp.ActiveConnection.Errors
         MsgBox "Error number: " & errLoop.Number & vbCr & _
            errLoop.Description
      Next errLoop
   End If

   Resume Next

End Sub

Public Sub PrintOutput(rstTemp As ADODB.Recordset)

   ' Enumerate Recordset.
   Do While Not rstTemp.EOF
      Debug.Print "  " & rstTemp!title & _
         ", " & rstTemp!Type
      rstTemp.MoveNext
   Loop

End Sub
'EndExecuteVB

See Also

Clear Method | Command Object | Connection Object | Error Object | Execute Method (ADO Command) | Execute Method (ADO Connection) | Recordset Object | Requery Method


Filter and RecordCount Properties Example (VB)

This example uses the Filter property to open a new Recordset based on a specified condition applied to an existing Recordset. It uses the RecordCount property to show the number of records in the two Recordsets. The FilterField function is required for this procedure to run.

'BeginFilterVB
Public Sub FilterX()

   Dim rstPublishers As ADODB.Recordset
   Dim rstPublishersCountry As ADODB.Recordset
   Dim strCnn As String
   Dim intPublisherCount As Integer
   Dim strCountry As String
   Dim strMessage As String

   ' Open recordset with data from Publishers table.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstPublishers = New ADODB.Recordset
   rstPublishers.CursorType = adOpenStatic
   rstPublishers.Open "publishers", strCnn, , , adCmdTable

   ' Populate the Recordset.
   intPublisherCount = rstPublishers.RecordCount

   ' Get user input.
   strCountry = Trim(InputBox( _
      "Enter a country to filter on (e.g. USA):"))

   If strCountry <> "" Then
      ' Open a filtered Recordset object.
      Set rstPublishersCountry = _
         FilterField(rstPublishers, "Country", strCountry)

      If rstPublishersCountry.RecordCount = 0 Then
         MsgBox "No publishers from that country."
      Else
         ' Print number of records for the original
         ' Recordset object and the filtered Recordset
         ' object.
         strMessage = "Orders in original recordset: " & _
            vbCr & intPublisherCount & vbCr & _
            "Orders in filtered recordset (Country = '" & _
            strCountry & "'): " & vbCr & _
            rstPublishersCountry.RecordCount
         MsgBox strMessage
      End If
      rstPublishersCountry.Close

   End If

End Sub

Public Function FilterField(rstTemp As ADODB.Recordset, _
   strField As String, strFilter As String) As ADODB.Recordset

   ' Set a filter on the specified Recordset object and then
   ' open a new Recordset object.
   rstTemp.Filter = strField & " = '" & strFilter & "'"
   Set FilterField = rstTemp

End Function
'EndFilterVB

Note When you know the data you want to select, it's usually more efficient to open a Recordset with an SQL statement. This example shows how you can create just one Recordset and obtain records from a particular country.

'BeginFilter2VB
Public Sub FilterX2()

   Dim rstPublishers As ADODB.Recordset
   Dim strCnn As String

   ' Open recordset with data from Publishers table.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstPublishers = New ADODB.Recordset
   rstPublishers.CursorType = adOpenStatic
   rstPublishers.Open "SELECT * FROM publishers " & _
      "WHERE Country = 'USA'", strCnn, , , adCmdText

   ' Print current data in recordset.
   rstPublishers.MoveFirst
   Do While Not rstPublishers.EOF
      Debug.Print rstPublishers!pub_name & ", " & _
         rstPublishers!country
      rstPublishers.MoveNext
   Loop

   rstPublishers.Close

End Sub
'EndFilter2VB

See Also

Filter Property | RecordCount Property | Recordset Object


Find Method Example (VB)

This example uses the Recordset object's Find method to locate and count the number of business titles in the Pubs database. The example assumes the underlying provider does not support similar functionality.

'BeginFindVB
Public Sub FindX()
Dim cnn As New ADODB.Connection
Dim rst As New ADODB.Recordset
Dim mark As Variant
Dim count As Integer

count = 0
cnn.Open "DSN=Pubs; Provider=MSDASQL; uid=sa; pwd=;"
rst.Open "SELECT title_id FROM titles", cnn, _
         adOpenStatic, adLockReadOnly, adCmdText

' The default parameters are sufficient to search forward
' through a Recordset.

rst.Find "title_id LIKE 'BU%'"

' Skip the current record to avoid finding the same row repeatedly.
' The bookmark is redundant because Find searches from the current
' position.

Do While rst.EOF <> True    'Continue if last find succeeded.
   Debug.Print "Title ID: "; rst!title_id
   count = count + 1        'Count the last title found.
   mark = rst.Bookmark      'Note current position.
   rst.Find "title_id LIKE 'BU%'", 1, adSearchForward, mark
Loop

rst.Close
cnn.Close
Debug.Print "The number of business titles is " & count

End Sub
'EndFindVB

See Also

Find Method | Recordset Object


GetRows Method Example (VB)

This example uses the GetRows method to retrieve a specified number of rows from a Recordset and to fill an array with the resulting data. The GetRows method will return fewer than the desired number of rows in two cases: either if EOF has been reached, or if GetRows tried to retrieve a record that was deleted by another user. The function returns False only if the second case occurs. The GetRowsOK function is required for this procedure to run.

'BeginGetRowsVB
Public Sub GetRowsX()

   Dim rstEmployees As ADODB.Recordset
   Dim strCnn As String
   Dim strMessage As String
   Dim intRows As Integer
   Dim avarRecords As Variant
   Dim intRecord As Integer

   ' Open recordset with names and hire dates from employee table.
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstEmployees = New ADODB.Recordset
   rstEmployees.Open "SELECT fName, lName, hire_date " & _
      "FROM Employee ORDER BY lName", strCnn, , , adCmdText

   Do While True
      ' Get user input for number of rows.
      strMessage = "Enter number of rows to retrieve."
      intRows = Val(InputBox(strMessage))

      If intRows <= 0 Then Exit Do

      ' If GetRowsOK is successful, print the results,
      ' noting if the end of the file was reached.
      If GetRowsOK(rstEmployees, intRows, _
            avarRecords) Then
         If intRows > UBound(avarRecords, 2) + 1 Then
            Debug.Print "(Not enough records in " & _
               "Recordset to retrieve " & intRows & _
               " rows.)"
         End If
         Debug.Print UBound(avarRecords, 2) + 1 & _
            " records found."

         ' Print the retrieved data.
         For intRecord = 0 To UBound(avarRecords, 2)
            Debug.Print "  " & _
               avarRecords(0, intRecord) & " " & _
               avarRecords(1, intRecord) & ", " & _
               avarRecords(2, intRecord)
         Next intRecord
      Else
         ' Assuming the GetRows error was due to data
         ' changes by another user, use Requery to
         ' refresh the Recordset and start over.
         If MsgBox("GetRows failed--retry?", _
               vbYesNo) = vbYes Then
            rstEmployees.Requery
         Else
            Debug.Print "GetRows failed!"
            Exit Do
         End If
      End If

      ' Because using GetRows leaves the current
      ' record pointer at the last record accessed,
      ' move the pointer back to the beginning of the
      ' Recordset before looping back for another search.
      rstEmployees.MoveFirst
   Loop

   rstEmployees.Close

End Sub

Public Function GetRowsOK(rstTemp As ADODB.Recordset, _
   intNumber As Integer, avarData As Variant) As Boolean

   ' Store results of GetRows method in array.
   avarData = rstTemp.GetRows(intNumber)
   ' Return False only if fewer than the desired
   ' number of rows were returned, but not because the
   ' end of the Recordset was reached.
   If intNumber > UBound(avarData, 2) + 1 And _
         Not rstTemp.EOF Then
      GetRowsOK = False
   Else
      GetRowsOK = True
   End If

End Function
'EndGetRowsVB

See Also

GetRows Method | Recordset Object


GetString Method Example (VB)

This example demonstrates the GetString method.

Assume you are debugging a data access problem and want a quick, simple way of printing the current contents of a small Recordset.

'BeginGetStringVB
Public Sub GetStringX()

Dim cnn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim varOutput As Variant
Dim strQuery As String
Dim strPrompt As String
Dim strState As String

Set cnn = New ADODB.Connection
Set rst = New ADODB.Recordset
strPrompt = "Enter a state (CA, IN, KS, MD, MI, OR, TN, UT): "
strState = Trim(InputBox(strPrompt, "GetString Example"))
strQuery = "SELECT au_fname, au_lname, address, city FROM Authors " & _
            "WHERE state = '" & strState & "'"

cnn.Open "DSN=Pubs;Provider=MSDASQL; uid=sa;pwd=;"
rst.Open strQuery, cnn, adOpenStatic, adLockReadOnly, adCmdText

If rst.RecordCount > 0 Then
'Use all defaults: get all rows, TAB column delimiter, CARRIAGE RETURN
'row delimiter, empty-string null delimiter
   varOutput = rst.GetString(adClipString)
   Debug.Print "State = '" & strState & "'"
   Debug.Print "Name             Address             City" & vbCr
   Debug.Print varOutput
Else
   Debug.Print "No rows found for state = '" & strState & "'" & vbCr
End If

rst.Close
cnn.Close
End Sub
'EndGetStringVB

See Also

GetString Method | Recordset Object


IsolationLevel and Mode Properties Example (VB)

This example uses the Mode property to open an exclusive connection, and the IsolationLevel property to open a transaction that is conducted in isolation of other transactions.

'BeginIsolationLevelVB
Public Sub IsolationLevelX()

   Dim cnn1 As ADODB.Connection
   Dim rstTitles As ADODB.Recordset
   Dim strCnn As String

   ' Assign connection string to variable.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "

   ' Open connection and Titles table.
   Set cnn1 = New ADODB.Connection
   cnn1.Mode = adModeShareExclusive
   cnn1.IsolationLevel = adXactIsolated
   cnn1.Open strCnn

   Set rstTitles = New ADODB.Recordset
   rstTitles.CursorType = adOpenDynamic
   rstTitles.LockType = adLockPessimistic
   rstTitles.Open "Titles", cnn1, , , adCmdTable

   cnn1.BeginTrans

   ' Display connection mode.
   If cnn1.Mode = adModeShareExclusive Then
      MsgBox "Connection mode is exclusive."
   Else
      MsgBox "Connection mode is not exclusive."
   End If

   ' Display isolation level.
   If cnn1.IsolationLevel = adXactIsolated Then
      MsgBox "Transaction is isolated."
   Else
      MsgBox "Transaction is not isolated."
   End If

   ' Change the type of psychology titles.
   Do Until rstTitles.EOF
      If Trim(rstTitles!Type) = "psychology" Then
         rstTitles!Type = "self_help"
         rstTitles.Update
      End If
      rstTitles.MoveNext
   Loop

   ' Print current data in recordset.
   rstTitles.Requery
   Do While Not rstTitles.EOF
      Debug.Print rstTitles!title & " - " & rstTitles!Type
      rstTitles.MoveNext
   Loop

   ' Restore original data.
   cnn1.RollbackTrans
   rstTitles.Close

   cnn1.Close

End Sub
'EndIsolationLevelVB

See Also

Connection Object | IsolationLevel Property | Mode Property


Item Property Example (VB)

This example demonstrates how the Item property accesses members of a collection. The example opens the Authors table of the Pubs database with a parameterized command.

The parameter in the command issued against the database is accessed from the Command object's Parameters collection by index and name. The fields of the returned Recordset are then accessed from that object's Fields collection by index and name.

'BeginItemVB
Public Sub ItemX()
Dim cnn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim cmd As ADODB.Command
Dim prm As ADODB.Parameter
Dim fld As ADODB.Field
Dim ix As Integer
Dim limit As Long
Dim Column(0 To 8) As Variant

Set cnn = New ADODB.Connection
Set rst = New ADODB.Recordset
Set cmd = New ADODB.Command

'Set the array with the Authors table field (column) names
Column(0) = "au_id"
Column(1) = "au_lname"
Column(2) = "au_fname"
Column(3) = "phone"
Column(4) = "address"
Column(5) = "city"
Column(6) = "state"
Column(7) = "zip"
Column(8) = "contract"

cmd.CommandText = "SELECT * FROM Authors WHERE state = ?"
Set prm = cmd.CreateParameter("ItemXparm", adChar, adParamInput, 2, "CA")
cmd.Parameters.Append prm

cnn.Open "DSN=Pubs;Provider=MSDASQL; uid=sa;pwd=;"
cmd.ActiveConnection = cnn
'Connection and CommandType are omitted because a Command object is
'provided.
rst.Open cmd, , adOpenStatic, adLockReadOnly

Debug.Print "The Parameters collection accessed by index..."
Set prm = cmd.Parameters.Item(0)
Debug.Print "Parameter name = '"; prm.Name; "', value = '"; prm.Value; _
            "'"
Debug.Print

Debug.Print "The Parameters collection accessed by name..."
Set prm = cmd.Parameters.Item("ItemXparm")
Debug.Print "Parameter name = '"; prm.Name; "', value = '"; prm.Value; _
            "'"
Debug.Print

Debug.Print "The Fields collection accessed by index..."
rst.MoveFirst
limit = rst.fields.count - 1
For ix = 0 To limit
   Set fld = rst.fields.Item(ix)
   Debug.Print "Field "; ix; ": Name = '"; fld.Name; _
               "', Value = '"; fld.Value; "'"
Next ix
Debug.Print

Debug.Print "The Fields collection accessed by name..."
rst.MoveFirst
For ix = 0 To 8
   Set fld = rst.fields.Item(Column(ix))
   Debug.Print "Field name = '"; fld.Name; "', Value = '"; fld.Value; "'"
Next ix

rst.Close
cnn.Close
End Sub
'EndItemVB

See Also

Command Object | Fields Collection | Item Property | Parameters Collection | Recordset Object


MarshalOptions Property Example (VB)

This example uses the MarshalOptions property to specify what rows are sent back to the server—All Rows or only Modified Rows.

'BeginMarshalOptionsVB
Public Sub MarshalOptionsX()

   Dim rstEmployees As ADODB.Recordset
   Dim strCnn As String
   Dim strOldFirst As String
   Dim strOldLast As String
   Dim strMessage As String
   Dim strMarshalAll As String
   Dim strMarshalModified As String

   ' Open recordset with names from Employees table.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstEmployees = New ADODB.Recordset
   rstEmployees.CursorType = adOpenKeyset
   rstEmployees.LockType = adLockOptimistic
   rstEmployees.CursorLocation = adUseClient
   rstEmployees.Open "SELECT fname, lname " & _
      "FROM Employee ORDER BY lname", strCnn, , , adCmdText

   ' Store original data.
   strOldFirst = rstEmployees!fname
   strOldLast = rstEmployees!lname

   ' Change data in edit buffer.
   rstEmployees!fname = "Linda"
   rstEmployees!lname = "Kobara"

   ' Show contents of buffer and get user input.
   strMessage = "Edit in progress:" & vbCr & _
      " Original data = " & strOldFirst & " " & _
      strOldLast & vbCr & " Data in buffer = " & _
      rstEmployees!fname & " " & rstEmployees!lname & vbCr & vbCr & _
      "Use Update to replace the original data with " & _
      "the buffered data in the Recordset?"
   strMarshalAll = "Would you like to send all the rows " & _
               "in the recordset back to the server?"
   strMarshalModified = "Would you like to send only " & _
               "modified rows back to the server?"

   If MsgBox(strMessage, vbYesNo) = vbYes Then
      If MsgBox(strMarshalAll, vbYesNo) = vbYes Then
         rstEmployees.MarshalOptions = adMarshalAll
         rstEmployees.Update
      ElseIf MsgBox(strMarshalModified, vbYesNo) = vbYes Then
         rstEmployees.MarshalOptions = adMarshalModifiedOnly
         rstEmployees.Update
      End If
   End If

   ' Show the resulting data.
   MsgBox "Data in recordset = " & rstEmployees!fname & " " & _
      rstEmployees!lname

   ' Restore original data because this is a demonstration.
   If Not (strOldFirst = rstEmployees!fname And _
         strOldLast = rstEmployees!lname) Then
      rstEmployees!fname = strOldFirst
      rstEmployees!lname = strOldLast
      rstEmployees.Update
   End If

   rstEmployees.Close

End Sub
'EndMarshalOptionsVB

See Also

MarshalOptions Property | MarshalOptionsEnum


MaxRecords Property Example (VB)

This example uses the MaxRecords property to open a Recordset containing the 10 most expensive titles in the Titles table.

'BeginMaxRecordsVB
Public Sub MaxRecordsX()

   Dim rstTemp As ADODB.Recordset
   Dim strCnn As String

   ' Open recordset containing the 10 most expensive
   ' titles in the Titles table.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstTemp = New ADODB.Recordset
   rstTemp.MaxRecords = 10
   rstTemp.Open "SELECT Title, Price FROM Titles " & _
      "ORDER BY Price DESC", strCnn, , , adCmdText

   ' Display the contents of the recordset.
   Debug.Print "Top Ten Titles by Price:"

   Do While Not rstTemp.EOF
      Debug.Print "  " & rstTemp!title & " - " & rstTemp!Price
      rstTemp.MoveNext
   Loop
   rstTemp.Close

End Sub
'EndMaxRecordsVB

See Also

MaxRecords Property | Recordset Object


Move Method Example (VB)

This example uses the Move method to position the record pointer based on user input.

'BeginMoveVB
Public Sub MoveX()

   Dim rstAuthors As ADODB.Recordset
   Dim strCnn As String
   Dim varBookmark As Variant
   Dim strCommand As String
   Dim lngMove As Long

   ' Open recordset from Authors table.
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstAuthors = New ADODB.Recordset
   rstAuthors.CursorType = adOpenStatic
   ' Use client cursor to allow use of
   ' AbsolutePosition property.
   rstAuthors.CursorLocation = adUseClient
   rstAuthors.Open "SELECT au_id, au_fname, au_lname, city, state " & _
      "FROM Authors ORDER BY au_lname", strCnn, , , adCmdText

   rstAuthors.MoveFirst

   Do While True
      ' Display information about current record and
      ' ask how many records to move.

      strCommand = InputBox( _
         "Record " & rstAuthors.AbsolutePosition & _
         " of " & rstAuthors.RecordCount & vbCr & _
         "Author: " & rstAuthors!au_fname & _
         " " & rstAuthors!au_lname & vbCr & _
         "Location: " & rstAuthors!city & _
         ", " & rstAuthors!State & vbCr & vbCr & _
         "Enter number of records to Move " & _
         "(positive or negative).")

      If strCommand = "" Then Exit Do

      ' Store bookmark in case the Move goes too far
      ' forward or backward.
      varBookmark = rstAuthors.Bookmark

      ' Move method requires parameter of data type Long.
      lngMove = CLng(strCommand)
      rstAuthors.Move lngMove

      ' Trap for BOF or EOF.
      If rstAuthors.BOF Then
         MsgBox "Too far backward! " & _
            "Returning to current record."
         rstAuthors.Bookmark = varBookmark
      End If
      If rstAuthors.EOF Then
         MsgBox "Too far forward! " & _
            "Returning to current record."
         rstAuthors.Bookmark = varBookmark
      End If
   Loop
   rstAuthors.Close

End Sub
'EndMoveVB

See Also

Move Method | Recordset Object


MoveFirst, MoveLast, MoveNext, and MovePrevious Methods Example (VB)

This example uses the MoveFirst, MoveLast, MoveNext, and MovePrevious methods to move the record pointer of a Recordset based on the supplied command. The MoveAny procedure is required for this procedure to run.

'BeginMoveFirstVB
Public Sub MoveFirstX()

   Dim rstAuthors As ADODB.Recordset
   Dim strCnn As String
   Dim strMessage As String
   Dim intCommand As Integer

   ' Open recordset from Authors table.
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstAuthors = New ADODB.Recordset
   rstAuthors.CursorType = adOpenStatic
   ' Use client cursor to enable AbsolutePosition property.
   rstAuthors.CursorLocation = adUseClient
   rstAuthors.Open "Authors", strCnn, , , adCmdTable

   ' Show current record information and get user's method choice.
   Do While True

      strMessage = "Name: " & rstAuthors!au_fname & " " & _
         rstAuthors!au_lname & vbCr & "Record " & _
         rstAuthors.AbsolutePosition & " of " & _
         rstAuthors.RecordCount & vbCr & vbCr & _
         "[1 - MoveFirst, 2 - MoveLast, " & vbCr & _
         "3 - MoveNext, 4 - MovePrevious]"
      intCommand = Val(Left(InputBox(strMessage), 1))
      If intCommand < 1 Or intCommand > 4 Then Exit Do

      ' Call method based on user's input.
      MoveAny intCommand, rstAuthors
   Loop
   rstAuthors.Close

End Sub

Public Sub MoveAny(intChoice As Integer, _
   rstTemp As Recordset)

   ' Use specified method, trapping for BOF and EOF.
   Select Case intChoice
      Case 1
         rstTemp.MoveFirst
      Case 2
         rstTemp.MoveLast
      Case 3
         rstTemp.MoveNext
         If rstTemp.EOF Then
            MsgBox "Already at end of recordset!"
            rstTemp.MoveLast
         End If
      Case 4
         rstTemp.MovePrevious
         If rstTemp.BOF Then
            MsgBox "Already at beginning of recordset!"
            rstTemp.MoveFirst
         End If
   End Select

End Sub
'EndMoveFirstVB

See Also

MoveFirst, MoveLast, MoveNext, and MovePrevious Methods | Recordset Object


NextRecordset Method Example (VB)

This example uses the NextRecordset method to view the data in a recordset that uses a compound command statement made up of three separate SELECT statements.

'BeginNextRecordsetVB
Public Sub NextRecordsetX()

   Dim rstCompound As ADODB.Recordset
   Dim strCnn As String
   Dim intCount As Integer

   ' Open compound recordset.
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "

   Set rstCompound = New ADODB.Recordset
   rstCompound.Open "SELECT * FROM Authors; " & _
      "SELECT * FROM stores; " & _
      "SELECT * FROM jobs", strCnn, , , adCmdText

   ' Display results from each SELECT statement.
   intCount = 1
   Do While Not (rstCompound Is Nothing)
      Debug.Print "Contents of recordset #" & intCount
      Do While Not rstCompound.EOF
         Debug.Print , rstCompound.fields(0), _
            rstCompound.fields(1)
         rstCompound.MoveNext
      Loop

      Set rstCompound = rstCompound.NextRecordset
      intCount = intCount + 1
   Loop

End Sub
'EndNextRecordsetVB

See Also

NextRecordset Method | Recordset Object


NumericScale and Precision Properties Example (VB)

This example uses the NumericScale and Precision properties to display the numeric scale and precision of fields in the Discounts table of the Pubs database.

'BeginNumericScaleVB
Public Sub NumericScaleX()

   Dim rstDiscounts As ADODB.Recordset
   Dim fldTemp As ADODB.Field
   Dim strCnn As String

   ' Open recordset.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstDiscounts = New ADODB.Recordset
   rstDiscounts.Open "Discounts", strCnn, , , adCmdTable

   ' Display numeric scale and precision of
   ' numeric and small integer fields.
   For Each fldTemp In rstDiscounts.fields
      If fldTemp.Type = adNumeric _
         Or fldTemp.Type = adSmallInt Then
         MsgBox "Field: " & fldTemp.Name & vbCr & _
            "Numeric scale: " & _
               fldTemp.NumericScale & vbCr & _
            "Precision: " & fldTemp.Precision
      End If
   Next fldTemp

   rstDiscounts.Close

End Sub
'EndNumericScaleVB

See Also

Field Object | NumericScale Property | Parameter Object | Precision Property


Open and Close Methods Example (VB)

This example uses the Open and Close methods on both Recordset and Connection objects that have been opened.

'BeginOpenVB
Public Sub OpenX()

   Dim cnn1 As ADODB.Connection
   Dim rstEmployees As ADODB.Recordset
   Dim strCnn As String
   Dim varDate As Variant

   ' Open connection.
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set cnn1 = New ADODB.Connection
   cnn1.Open strCnn

   ' Open employee table.
   Set rstEmployees = New ADODB.Recordset
   rstEmployees.CursorType = adOpenKeyset
   rstEmployees.LockType = adLockOptimistic
   rstEmployees.Open "employee", cnn1, , , adCmdTable

   ' Assign the first employee record's hire date
   ' to a variable, then change the hire date.
   varDate = rstEmployees!hire_date
   Debug.Print "Original data"
   Debug.Print "  Name - Hire Date"
   Debug.Print "  " & rstEmployees!fname & " " & _
      rstEmployees!lname & " - " & rstEmployees!hire_date
   rstEmployees!hire_date = #1/1/1900#
   rstEmployees.Update
   Debug.Print "Changed data"
   Debug.Print "  Name - Hire Date"
   Debug.Print "  " & rstEmployees!fname & " " & _
      rstEmployees!lname & " - " & rstEmployees!hire_date

   ' Requery Recordset and reset the hire date.
   rstEmployees.Requery
   rstEmployees!hire_date = varDate
   rstEmployees.Update
   Debug.Print "Data after reset"
   Debug.Print "  Name - Hire Date"
   Debug.Print "  " & rstEmployees!fname & " " & _
      rstEmployees!lname & " - " & rstEmployees!hire_date

   rstEmployees.Close
   cnn1.Close

End Sub
'EndOpenVB

See Also

Close Method | Connection Object | Open Method (ADO Connection) | Open Method (ADO Recordset) | Recordset Object


OpenSchema Method Example (VB)

This example uses the OpenSchema method to display the name and type of each table in the Pubs database.

'BeginOpenSchemaVB
Public Sub OpenSchemaX()

   Dim cnn1 As ADODB.Connection
   Dim rstSchema As ADODB.Recordset
   Dim strCnn As String

   Set cnn1 = New ADODB.Connection
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   cnn1.Open strCnn

   Set rstSchema = cnn1.OpenSchema(adSchemaTables)

   Do Until rstSchema.EOF
      Debug.Print "Table name: " & _
         rstSchema!TABLE_NAME & vbCr & _
         "Table type: " & rstSchema!TABLE_TYPE & vbCr
      rstSchema.MoveNext
   Loop
   rstSchema.Close

   cnn1.Close

End Sub
'EndOpenSchemaVB

This example specifies a TABLE_TYPE query constraint in the OpenSchema method Criteria argument. As a result, only schema information for the Views specified in the Pubs database are returned. The example then displays the name(s) and type(s) of each table(s).

'BeginOpenSchema2VB
Public Sub OpenSchemaX2()

   Dim cnn2 As ADODB.Connection
   Dim rstSchema As ADODB.Recordset
   Dim strCnn As String

   Set cnn2 = New ADODB.Connection
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   cnn2.Open strCnn

   Set rstSchema = cnn2.OpenSchema(adSchemaTables, Array(Empty, Empty, Empty, "VIEW"))

      Do Until rstSchema.EOF
         Debug.Print "Table name: " & _
            rstSchema!TABLE_NAME & vbCr & _
            "Table type: " & rstSchema!TABLE_TYPE & vbCr
         rstSchema.MoveNext
      Loop
   rstSchema.Close

   cnn2.Close

End Sub
'EndOpenSchema2VB

See Also

OpenSchema Method | Recordset Object


Optimize Property Example (VB)

This example demonstrates the Field object’s dynamic Optimize property. The zip field of the Authors table in the Pubs database is not indexed. Setting the Optimize property to True on the zip field authorizes ADO to build an index that improves the performance of the Find method.

'BeginOptimizeVB
Public Sub OptimizeX()
Dim rst As ADODB.Recordset

Set rst = New ADODB.Recordset
rst.CursorLocation = adUseClient        'Enable index creation
rst.Open "SELECT * FROM Authors", _
         "DSN=Pubs;Provider=MSDASQL;", _
         adOpenStatic, adLockReadOnly, adCmdText
rst!zip.Properties("Optimize") = True   'Create the index
rst.Find "zip = '94595'"                'Find Akiko Yokomoto
Debug.Print rst!au_fname & " " & rst!au_lname & " " & _
         rst!address & " " & rst!city & " " & rst!State
rst!zip.Properties("Optimize") = False  'Delete the index

rst.Close
End Sub
'EndOptimizeVB

See Also

Field Object | Optimize Property—Dynamic (ADO)


OriginalValue and UnderlyingValue Properties Example (VB)

This example demonstrates the OriginalValue and UnderlyingValue properties by displaying a message if a record's underlying data has changed during a Recordset batch update.

'BeginOriginalValueVB
Public Sub OriginalValueX()

   Dim cnn1 As ADODB.Connection
   Dim rstTitles As ADODB.Recordset
   Dim fldType As ADODB.Field
   Dim strCnn As String

   ' Open connection.
   Set cnn1 = New ADODB.Connection
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   cnn1.Open strCnn

   ' Open recordset for batch update.
   Set rstTitles = New ADODB.Recordset
   Set rstTitles.ActiveConnection = cnn1
   rstTitles.CursorType = adOpenKeyset
   rstTitles.LockType = adLockBatchOptimistic
   rstTitles.Open "Titles"

   ' Set field object variable for Type field.
   Set fldType = rstTitles!Type

   ' Change the type of psychology titles.
   Do Until rstTitles.EOF
      If Trim(fldType) = "psychology" Then
         fldType = "self_help"
      End If
      rstTitles.MoveNext
   Loop

   ' Similate a change by another user by updating
   ' data using a command string.
   cnn1.Execute "UPDATE Titles SET type = 'sociology' " & _
      "WHERE type = 'psychology'"

   'Check for changes.
   rstTitles.MoveFirst
   Do Until rstTitles.EOF
      If fldType.OriginalValue <> _
         fldType.UnderlyingValue Then

         MsgBox "Data has changed!" & vbCr & vbCr & _
            "  Title ID: " & rstTitles!title_id & vbCr & _
            "  Current value: " & fldType & vbCr & _
            "  Original value: " & _
            fldType.OriginalValue & vbCr & _
            "  Underlying value: " & _
            fldType.UnderlyingValue & vbCr
      End If
      rstTitles.MoveNext
   Loop

   ' Cancel the update because this is a demonstration.
   rstTitles.CancelBatch
   rstTitles.Close

   ' Restore original values.
   cnn1.Execute "UPDATE Titles SET type = 'psychology' " & _
      "WHERE type = 'sociology'"

   cnn1.Close

End Sub
'EndOriginalValueVB

See Also

OriginalValue Property | Recordset Object | UnderlyingValue Property


Prepared Property Example (VB)

This example demonstrates the Prepared property by opening two Command objects—one prepared and one not prepared.

'BeginPreparedVB
Public Sub PreparedX()

   Dim cnn1 As ADODB.Connection
   Dim cmd1 As ADODB.Command
   Dim cmd2 As ADODB.Command
   Dim strCnn As String
   Dim strCmd As String
   Dim sngStart As Single
   Dim sngEnd As Single
   Dim sngNotPrepared As Single
   Dim sngPrepared As Single
   Dim intLoop As Integer

   ' Open a connection.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set cnn1 = New ADODB.Connection
   cnn1.Open strCnn

   ' Create two command objects for the same
   ' command - one prepared and one not prepared.
   strCmd = "SELECT title, type FROM Titles ORDER BY type"

   Set cmd1 = New ADODB.Command
   Set cmd1.ActiveConnection = cnn1
   cmd1.CommandText = strCmd

   Set cmd2 = New ADODB.Command
   Set cmd2.ActiveConnection = cnn1
   cmd2.CommandText = strCmd
   cmd2.Prepared = True

   ' Set a timer, then execute the unprepared
   ' command 20 times.
   sngStart = Timer
   For intLoop = 1 To 20
      cmd1.Execute
   Next intLoop
   sngEnd = Timer
   sngNotPrepared = sngEnd - sngStart

   ' Reset the timer, then execute the prepared
   ' command 20 times.
   sngStart = Timer
   For intLoop = 1 To 20
      cmd2.Execute
   Next intLoop
   sngEnd = Timer
   sngPrepared = sngEnd - sngStart

   ' Display performance results.
   MsgBox "Performance Results:" & vbCr & _
      "   Not Prepared: " & Format(sngNotPrepared, _
      "##0.000") & " seconds" & vbCr & _
      "   Prepared: " & Format(sngPrepared, _
      "##0.000") & " seconds"

   cnn1.Close

End Sub
'EndPreparedVB

See Also

Command Object | Prepared Property


Provider and DefaultDatabase Properties Example (VB)

This example demonstrates the Provider property by opening three Connection objects using different providers. It also uses the DefaultDatabase property to set the default database for the Microsoft ODBC Provider.

'BeginProviderVB
Public Sub ProviderX()

   Dim cnn1 As ADODB.Connection
   Dim cnn2 As ADODB.Connection
   Dim cnn3 As ADODB.Connection
   Dim strCnn As String

   ' Open a connection using the Microsoft ODBC provider.
   Set cnn1 = New ADODB.Connection
   cnn1.ConnectionString = "driver={SQL Server};" & _
      "server=srv;uid=sa;pwd=pwd"
   cnn1.Open strCnn
   cnn1.DefaultDatabase = "Pubs"

   ' Display the provider.
   MsgBox "Cnn1 provider: " & cnn1.Provider

   ' Open a connection using the Microsoft Jet provider.
   Set cnn2 = New ADODB.Connection
   cnn2.Provider = "Microsoft.Jet.OLEDB.3.51"
   cnn2.Open "C:\Samples\northwind.mdb", "admin", ""

   ' Display the provider.
   MsgBox "Cnn2 provider: " & cnn2.Provider

   ' Open a connection using the Microsoft SQL Server provider.
   Set cnn3 = New ADODB.Connection
   cnn3.Provider = "sqloledb"
   cnn3.Open "Data Source=MyServer;Initial Catalog=Pubs;", "sa", ""

   ' Display the provider.
   MsgBox "Cnn3 provider: " & cnn3.Provider

   cnn1.Close
   cnn2.Close
   cnn3.Close

End Sub
'EndProviderVB

See Also

Connection Object | DefaultDatabase Property | Provider Property


Read, ReadText, Write, and WriteText Methods Example (VB)

This example demonstrates how to read the contents of a text box into both a text Stream and a binary Stream. Other properties and methods shown include Position, Size, Charset, and SetEOS.

'BeginReadVB
Public Sub ReadX()
    'Declare variables
    Dim objStream As Stream
    Dim varA As Variant
    Dim bytA() As Byte
    Dim i As Integer
    Dim strBytes As String

    'Instantiate and Open Stream
    Set objStream = New Stream
    objStream.Open

    'Write the text content of a textbox to the stream
    objStream.WriteText Text1.Text

    'Display the text contents and size of the stream
    objStream.Position = 0
    Debug.Print "Default text:"
    Debug.Print objStream.ReadText
    Debug.Print objStream.Size

    'Switch character set and display
    objStream.Position = 0
    objStream.Charset = "Windows-1252"
    Debug.Print "New Charset text:"
    Debug.Print objStream.ReadText
    Debug.Print objStream.Size

    'Switch to a binary stream and display
    objStream.Position = 0
    objStream.Type = adTypeBinary
    Debug.Print "Binary:"
    Debug.Print objStream.Read
    Debug.Print objStream.Size

    'Load an array of bytes with the text box text
    ReDim bytA(Len(Text1.Text))
    For i = 1 To Len(Text1.Text)
        bytA(i - 1) = CByte(Asc(Mid(Text1.Text, i, 1)))
    Next

    'Write the buffer to the binary stream and display
    objStream.Position = 0
    objStream.Write bytA()
    objStream.SetEOS
    objStream.Position = 0
    Debug.Print "Binary after Write:"
    Debug.Print objStream.Read
    Debug.Print objStream.Size

    'Switch back to a text stream and display
    Debug.Print "Translated back:"
    objStream.Position = 0
    objStream.Type = adTypeText
    Debug.Print objStream.ReadText
    Debug.Print objStream.Size
End Sub
'EndReadVB

See Also

Charset Property | Position Property | Read Method | ReadText Method | SetEOS Method | Size Property (ADO Stream) | Stream Object | Write Method | WriteText Method


Refresh Method Example (VB)

This example demonstrates using the Refresh method to refresh the Parameters collection for a stored procedure Command object.

'BeginRefreshVB
Public Sub RefreshX()

   Dim cnn1 As ADODB.Connection
   Dim cmdByRoyalty As ADODB.Command
   Dim rstByRoyalty As ADODB.Recordset
   Dim rstAuthors As ADODB.Recordset
   Dim intRoyalty As Integer
   Dim strAuthorID As String
   Dim strCnn As String

   ' Open connection.
   Set cnn1 = New ADODB.Connection
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   cnn1.Open strCnn

   ' Open a command object for a stored procedure
   ' with one parameter.
   Set cmdByRoyalty = New ADODB.Command
   Set cmdByRoyalty.ActiveConnection = cnn1
   cmdByRoyalty.CommandText = "byroyalty"
   cmdByRoyalty.CommandType = adCmdStoredProc
   cmdByRoyalty.Parameters.Refresh

   ' Get paramater value and execute the command,
   ' storing the results in a recordset.
   intRoyalty = Trim(InputBox("Enter royalty:"))
   cmdByRoyalty.Parameters(1) = intRoyalty
   Set rstByRoyalty = cmdByRoyalty.Execute()

   ' Open the Authors table to get author names for display.
   Set rstAuthors = New ADODB.Recordset
   rstAuthors.Open "Authors", cnn1, , , adCmdTable

   ' Print current data in the recordset, adding
   ' author names from Authors table.
   Debug.Print "Authors with " & intRoyalty & " percent royalty"
   Do While Not rstByRoyalty.EOF
      strAuthorID = rstByRoyalty!au_id
      Debug.Print "   " & rstByRoyalty!au_id & ", ";
      rstAuthors.Filter = "au_id = '" & strAuthorID & "'"
      Debug.Print rstAuthors!au_fname & " " & _
         rstAuthors!au_lname
      rstByRoyalty.MoveNext
   Loop

   rstByRoyalty.Close
   rstAuthors.Close
   cnn1.Close

End Sub
'EndRefreshVB

See Also

Command Object | Parameters Collection | Refresh Method


Resync Method Example (VB)

This example demonstrates using the Resync method to refresh data in a static recordset.

'BeginResyncVB
Public Sub ResyncX()

   Dim strCnn As String
   Dim rstTitles As ADODB.Recordset

   ' Open connections.
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "

   ' Open recordset for Titles table.
   Set rstTitles = New ADODB.Recordset
   rstTitles.CursorLocation = adUseClient
   rstTitles.CursorType = adOpenDynamic
   rstTitles.LockType = adLockBatchOptimistic
   rstTitles.Open "Titles", strCnn, , , adCmdTable

   ' Change the type of the first title in the recordset.
   rstTitles!Type = "database"

   ' Display the results of the change.
   MsgBox "Before resync: " & vbCr & vbCr & _
      "Title - " & rstTitles!title & vbCr & _
      "Type - " & rstTitles!Type

   ' Resync with database and redisplay results.
   rstTitles.Resync
   MsgBox "After resync: " & vbCr & vbCr & _
      "Title - " & rstTitles!title & vbCr & _
      "Type - " & rstTitles!Type

   rstTitles.CancelBatch
   rstTitles.Close

End Sub
'EndResyncVB

See Also

Recordset Object | Resync Method


Save and Open Methods Example (VB)

These three examples demonstrate how the Save and Open methods can be used together.

Assume you are going on a business trip and want to take along a table from a database. Before you go, you access the data as a Recordset and save it in a transportable form. When you arrive at your destination, you access the Recordset as a local, disconnected Recordset. You make changes to the Recordset, then save it again. Finally, when you return home, you connect to the database again and update it with the changes you made on the road.

First, access and save the Authors table.

'BeginSaveVB
Public Sub SaveX1()
Dim rst As ADODB.Recordset
Set rst = New ADODB.Recordset

rst.Open "SELECT au_id, au_lname, au_fname, city, phone FROM Authors", _
         "Provider=SQLOLEDB;Data Source=MyServer;User Id=uid;" & _
         "Password=pwd;Initial Catalog=pubs;", _
         adOpenDynamic, adLockOptimistic, adCmdText

'For sake of illustration, save the Recordset to a diskette in XML
'format.
rst.Save "a:\Pubs.xml", adPersistXML

rst.Close
End Sub
'EndSaveVB

At this point, you have arrived at your destination. You will access the Authors table as a local, disconnected Recordset. Don't forget you must have the MSPersist provider on the machine that you are using in order to access the saved file, a:\Pubs.xml.

'BeginSave2VB
Public Sub SaveX2()
Dim rst As ADODB.Recordset
Set rst = New ADODB.Recordset

'For sake of illustration, we specify all parameters.
rst.Open "a:\Pubs.xml", "Provider=MSPersist;", , , adCmdFile

'Now you have a local, disconnected Recordset. Edit it as you desire.
'(In this example, the change makes no difference).
rst.Find "au_lname = 'Carson'"
If rst.EOF Then
   Debug.Print "Name not found."
   Exit Sub
End If

rst!city = "Berkeley"
rst.Update

'Save changes in ADTG format this time, purely for sake of illustration.
'Note that the previous version is still on the diskette, as a:\Pubs.xml.
rst.Save "a:\Pubs.adtg", adPersistADTG
rst.Close
End Sub
'EndSave2VB

Finally, you return home. Now update the database with your changes.

'BeginSave3VB
Public Sub SaveX3()
Dim cnn As New ADODB.Connection
Dim rst As ADODB.Recordset
Set rst = New ADODB.Recordset

'If there is no ActiveConnection, you can open with defaults.
rst.Open "a:\Pubs.adtg"

'Connect to the database, associate the Recordset with the connection,
'then update the database table with the changed Recordset.
cnn.Open "Provider=SQLOLEDB;Data Source=MyServer;User Id=uid;" & _
         "Password=pwd;Initial Catalog=pubs;"
rst.ActiveConnection = cnn
rst.UpdateBatch

rst.Close
cnn.Close
End Sub
'EndSave3VB

See Also

Open Method (ADO Recordset) | Recordset Object | Recordset Persistence | Save Method


Seek Method and Index Property Example (VB)

This example uses the Recordset object's Seek method and Index property in conjunction with a given Employee ID, to locate the employee's name in the Employees table of the Nwind.mdb database.

'BeginSeekVB
Public Sub SeekX()
Dim rst As ADODB.Recordset
Dim strID As String
Dim strPrompt As String
strPrompt = "Enter an EmployeeID (e.g., 0 to 9)"

Set rst = New ADODB.Recordset
rst.CursorLocation = adUseServer
rst.Open "employees", _
            "Provider=Microsoft.Jet.OLEDB.4.0;" & _
            "Data Source=E:\Program Files\Microsoft Office\Office\Samples\northwind.mdb;" & _
            "user id=admin;password=;", _
            adOpenKeyset, adLockReadOnly, adCmdTableDirect

' Does this provider support Seek and Index?
If rst.Supports(adIndex) And rst.Supports(adSeek) Then
    rst.Index = "PrimaryKey"
    ' Display all the employees.
        rst.MoveFirst
        Do While rst.EOF = False
            Debug.Print rst!EmployeeId; ": "; rst!firstname; " "; _
                rst!LastName
            rst.MoveNext
        Loop

' Prompt the user for an EmployeeID between 0 and 9.
      rst.MoveFirst
      Do
         strID = LCase(Trim(InputBox(strPrompt, "Seek Example")))
         ' Quit if strID is a zero-length string (CANCEL, null, etc.)
         If Len(strID) = 0 Then Exit Do
         If Len(strID) = 1 And strID >= "0" And strID <= "9" Then
            rst.Seek Array(strID), adSeekFirstEQ
            If rst.EOF Then
               Debug.Print "Employee not found."
            Else
               Debug.Print strID; ": Employee='"; rst!firstname; " "; _
               rst!LastName; "'"
            End If
         End If
      Loop
End If

rst.Close
End Sub
'EndSeekVB

See Also

Index Property | Recordset Object | Seek Method


Sort Property Example (VB)

This example uses the Recordset object's Sort property to reorder the rows of a Recordset derived from the Authors table of the Pubs database. A secondary utility routine prints each row.

'BeginSortVB
Public Sub SortX()
Dim cnn As New ADODB.Connection
Dim rst As New ADODB.Recordset

rst.CursorLocation = adUseClient
cnn.Open "DSN=Pubs;Provider=MSDASQL;uid=sa;pwd=;"
rst.Open "SELECT * FROM Authors", cnn, _
         adOpenStatic, adLockReadOnly, adCmdText
SortXprint "Initial Order", rst

rst.Sort = "au_lname ASC, au_fname ASC"
SortXprint "Last Name Ascending", rst

rst.Sort = "au_lname DESC, au_fname ASC"
SortXprint "Last Name Descending", rst

rst.Close
cnn.Close
End Sub
'EndSortVB

This is the secondary utility routine that prints the given title, and the contents of the specified Recordset.

'BeginSort2VB
Public Sub SortXprint(title As String, rstp As ADODB.Recordset)
Debug.Print "---------------" & title & "---------------"
Debug.Print "First Name  Last Name" & vbCr & _
            "---------------------------------------------------"
rstp.MoveFirst
While Not rstp.EOF
   Debug.Print rstp!au_fname & " " & rstp!au_lname
   rstp.MoveNext
Wend
Debug.Print
End Sub
'EndSort2VB

See Also

Recordset Object | Sort Property


Source Property Example (VB)

This example demonstrates the Source property by opening three Recordset objects based on different data sources.

'BeginSourceVB
Public Sub SourceX()

   Dim cnn1 As ADODB.Connection
   Dim rstTitles As ADODB.Recordset
   Dim rstPublishers As ADODB.Recordset
   Dim rstPublishersDirect As ADODB.Recordset
   Dim rstTitlesPublishers As ADODB.Recordset
   Dim cmdSQL As ADODB.Command
   Dim strCnn As String
   Dim strSQL As String

   ' Open a connection.
   Set cnn1 = New ADODB.Connection
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   cnn1.Open strCnn

   ' Open a recordset based on a command object.
   Set cmdSQL = New ADODB.Command
   Set cmdSQL.ActiveConnection = cnn1
   cmdSQL.CommandText = "Select title, type, pubdate " & _
      "FROM Titles ORDER BY title"
   Set rstTitles = cmdSQL.Execute()

   ' Open a recordset based on a table.
   Set rstPublishers = New ADODB.Recordset
   rstPublishers.Open "publishers", strCnn, , , adCmdTable

   ' Open a recordset based on a table.
   Set rstPublishersDirect = New ADODB.Recordset
   rstPublishersDirect.Open "publishers", strCnn, , , adCmdTableDirect

   ' Open a recordset based on an SQL string.
   Set rstTitlesPublishers = New ADODB.Recordset
   strSQL = "SELECT title_ID AS TitleID, title AS Title, " & _
      "publishers.pub_id AS PubID, pub_name AS PubName " & _
      "FROM publishers INNER JOIN Titles " & _
      "ON publishers.pub_id = Titles.pub_id " & _
      "ORDER BY Title"
   rstTitlesPublishers.Open strSQL, strCnn, , , adCmdText

   ' Use the Source property to display the source of each recordset.
   MsgBox "rstTitles source: " & vbCr & _
      rstTitles.Source & vbCr & vbCr & _
      "rstPublishers source: " & vbCr & _
      rstPublishers.Source & vbCr & vbCr & _
      "rstPublishersDirect source: " & vbCr & _
      rstPublishersDirect.Source & vbCr & vbCr & _
      "rstTitlesPublishers source: " & vbCr & _
      rstTitlesPublishers.Source

   rstTitles.Close
   rstPublishers.Close
   rstTitlesPublishers.Close
   cnn1.Close

End Sub
'EndSourceVB

See Also

Recordset Object | Source Property (ADO Recordset)


State Property Example (VB)

This example uses the State property to display a message while asynchronous connections are opening and asynchronous commands are executing.

'BeginStateVB
Public Sub StateX()

   Dim cnn1 As ADODB.Connection
   Dim cnn2 As ADODB.Connection
   Dim cmdChange As ADODB.Command
   Dim cmdRestore As ADODB.Command
   Dim strCnn As String

   ' Open two asynchronous connections, displaying
   ' a message while connecting.
   Set cnn1 = New ADODB.Connection
   Set cnn2 = New ADODB.Connection
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "

   cnn1.Open strCnn, , , adAsyncConnect
   While (cnn1.State = adStateConnecting)
      Debug.Print "Opening first connection...."
   Wend

   cnn2.Open strCnn, , , adAsyncConnect
   While (cnn2.State = adStateConnecting)
      Debug.Print "Opening second connection...."
   Wend

   ' Create two command objects.
   Set cmdChange = New ADODB.Command
   cmdChange.ActiveConnection = cnn1
   cmdChange.CommandText = "UPDATE Titles SET type = 'self_help' " & _
      "WHERE type = 'psychology'"

   Set cmdRestore = New ADODB.Command
   cmdRestore.ActiveConnection = cnn2
   cmdRestore.CommandText = "UPDATE Titles SET type = 'psychology' " & _
      "WHERE type = 'self_help'"

   ' Executing the commands, displaying a message
   ' while they are executing.
   cmdChange.Execute , , adAsyncExecute
   While (cmdChange.State = adStateExecuting)
      Debug.Print "Change command executing...."
   Wend

   cmdRestore.Execute , , adAsyncExecute
   While (cmdRestore.State = adStateExecuting)
      Debug.Print "Restore command executing...."
   Wend

   cnn1.Close
   cnn2.Close

End Sub
'EndStateVB

See Also

Recordset Object | State Property


Status Property Example (Field) (VB)

The following example opens a document from a read/write folder using the Internet Publishing Provider. The Status property of a Field object of the Record will first be set to adFieldPendingInsert, then be updated to adFieldOk.

'BeginStatusFieldVB
Sub insert()
   Dim rec As New Record
   ' Open a read/write document
   rec.Open "http://websrv/folder/subfolder", , adModeReadWrite, _
      adCreateCollection Or adOpenIfExists

   Debug.Print "Append couple of fields"
   rec.fields.Append "chektest:fld1", adWChar, 42, adFldUpdatable, "fld1"
   rec.fields.Append "chektest:fld2", adWChar, 42, adFldUpdatable, "fld2"
   Debug.Print "status for the fields"
   Debug.Print rec.fields("chektest:fld1").Status 'adfldpendinginsert
   Debug.Print rec.fields("chektest:fld2").Status 'adfldpendinginsert
   rec.fields.Update
   Debug.Print "Update succeeds"
   Debug.Print rec.fields("chektest:fld1").Status
      ' adFieldOK if the update succeeds
   Debug.Print rec.fields("chektest:fld2").Status
      ' adFieldOK if succeeds
End Sub
'EndStatusFieldVB

The following example deletes a known Field from a Record opened from a document. The Status property will first be set to adFieldOK, then adFieldPendingUnknown.

'BeginStatusField2VB
Sub delete()
   Dim rec As New Record
   Dim fld As Field
   ' Open a read/write document
   rec.Open "http://websrv/folder/subfolder", , adModeReadWrite, _
      adCreateCollection Or adOpenIfExists
   Debug.Print rec.fields("chektest:fld1").Status ' should be adFldOK
   ' Delete a field which already exists in the collection
   rec.fields.delete "chektest:fld1"
   Set fld = rec.fields("chektest:fld1")
   Debug.Print rec.fields("chektest:fld1").Status   ' Pending delete
   rec.fields.Update
   Debug.Print "Deleted"
   Debug.Print fld.Status   ' Pending unknown
End Sub
'EndStatusField2VB

The following code deletes a Field from a Record opened on a read-only document. Status will be set to adFieldPendingDelete. At Update, the delete will fail and Status will be adFieldPendingDelete plus adFieldPermissionDenied. CancelUpdate clears the pending Status setting.

'BeginStatusField3VB
Sub delete2()
   Dim rec As New Record
   Dim fld As Field
   ' Open a read/write document
   rec.Open "http://websrv/folder/subfolder", , adModeReadWrite, _
      adCreateCollection Or adOpenIfExists

   Debug.Print "Try to delete something without permission"
   rec.fields.delete ("RESOURCE_PARSENAME")
   Set fld = rec.fields("RESOURCE_PARSENAME")
   Debug.Print "Pending delete"
   Debug.Print fld.Status   ' Pending delete
   Debug.Print "Delete should fail on Update"
   rec.fields.Update   ' Should fail
   Debug.Print fld.Status   ' Pending delete plus error
   rec.fields.CancelUpdate
   Debug.Print fld.Status   ' Okay
End Sub
'EndStatusField3VB

See Also

Field Object | Record Object | Status Property (ADO Field)


Status Property Example (Recordset) (VB)

This example uses the Status property to display which records have been modified in a batch operation before a batch update has occurred.

'BeginStatusRecordsetVB
Public Sub StatusX()

   Dim rstTitles As ADODB.Recordset
   Dim strCnn As String

   ' Open recordset for batch update.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstTitles = New ADODB.Recordset
   rstTitles.CursorType = adOpenKeyset
   rstTitles.LockType = adLockBatchOptimistic
   rstTitles.Open "Titles", strCnn, , , adCmdTable

   ' Change the type of psychology titles.
   Do Until rstTitles.EOF
      If Trim(rstTitles!Type) = "psychology" Then
         rstTitles!Type = "self_help"
      End If
      rstTitles.MoveNext
   Loop

   ' Display Title ID and status.
   rstTitles.MoveFirst
   Do Until rstTitles.EOF
      If rstTitles.Status = adRecModified Then
         Debug.Print rstTitles!title_id & " - Modified"
      Else
         Debug.Print rstTitles!title_id
      End If
      rstTitles.MoveNext
   Loop

   ' Cancel the update because this is a demonstration.
   rstTitles.CancelBatch
   rstTitles.Close

End Sub
'EndStatusRecordsetVB

See Also

Status Property (ADO Recordset)


StayInSync Property Example (VB)

This example demonstrates how the StayInSync property facilitates accessing rows in a hierarchical Recordset.

The outer loop displays each author's first and last name, state, and identification. The appended Recordset for each row is retrieved from the Fields collection and automatically assigned to rstTitleAuthor by the StayInSync property whenever the parent Recordset moves to a new row. The inner loop displays four fields from each row in the appended recordset.

'BeginStayInSyncVB
Public Sub StayInSyncX()
Dim cnn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim rstTitleAuthor As New ADODB.Recordset

Set cnn = New ADODB.Connection
Set rst = New ADODB.Recordset
cnn.Open "DSN=Pubs;Provider=MSDataShape;Data Provider=MSDASQL;"
rst.StayInSync = True
rst.Open "SHAPE  {select * from Authors} " & _
               "APPEND ({select * from titleauthor}" & _
               "RELATE au_id TO au_id) AS chapTitleAuthor", _
               cnn, , , adCmdText
Set rstTitleAuthor = rst("chapTitleAuthor").Value
While Not rst.EOF
   Debug.Print rst!au_fname & " " & rst!au_lname & " " & _
               rst!State & " " & rst!au_id
   While Not rstTitleAuthor.EOF
      Debug.Print rstTitleAuthor(0) & " " & rstTitleAuthor(1) & " " & _
               rstTitleAuthor(2) & " " & rstTitleAuthor(3)
      rstTitleAuthor.MoveNext
   Wend
   rst.MoveNext
Wend
End Sub
'EndStayInSyncVB

See Also

Fields Collection | Recordset Object | StayInSync Property


Supports Method Example (VB)

This example uses the Supports method to display the options supported by a recordset opened with different cursor types. The DisplaySupport procedure is required for this procedure to run.

'BeginSupportsVB
Public Sub SupportsX()

   Dim aintCursorType(4) As Integer
   Dim rstTitles As ADODB.Recordset
   Dim strCnn As String
   Dim intIndex As Integer

   ' Open connections.
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "

   ' Fill array with CursorType constants.
   aintCursorType(0) = adOpenForwardOnly
   aintCursorType(1) = adOpenKeyset
   aintCursorType(2) = adOpenDynamic
   aintCursorType(3) = adOpenStatic

   ' Open recordset using each CursorType and
   ' optimistic locking. Then call the DisplaySupport
   ' procedure to display the supported options.
   For intIndex = 0 To 3
      Set rstTitles = New ADODB.Recordset
      rstTitles.CursorType = aintCursorType(intIndex)
      rstTitles.LockType = adLockOptimistic
      rstTitles.Open "Titles", strCnn, , , adCmdTable

      Select Case aintCursorType(intIndex)
         Case adOpenForwardOnly
            Debug.Print "ForwardOnly cursor supports:"
         Case adOpenKeyset
            Debug.Print "Keyset cursor supports:"
         Case adOpenDynamic
            Debug.Print "Dynamic cursor supports:"
         Case adOpenStatic
            Debug.Print "Static cursor supports:"
      End Select

      DisplaySupport rstTitles
      rstTitles.Close
   Next intIndex

End Sub
'EndSupportsVB

'BeginSupports2VB
Public Sub DisplaySupport(rstTemp As ADODB.Recordset)

   Dim alngConstants(11) As Long
   Dim booSupports As Boolean
   Dim intIndex As Integer

   ' Fill array with cursor option constants.
   alngConstants(0) = adAddNew
   alngConstants(1) = adApproxPosition
   alngConstants(2) = adBookmark
   alngConstants(3) = adDelete
   alngConstants(4) = adFind
   alngConstants(5) = adHoldRecords
   alngConstants(6) = adMovePrevious
   alngConstants(7) = adNotify
   alngConstants(8) = adResync
   alngConstants(9) = adUpdate
   alngConstants(10) = adUpdateBatch

   For intIndex = 0 To 10
      booSupports = _
         rstTemp.Supports(alngConstants(intIndex))
      If booSupports Then
         Select Case alngConstants(intIndex)
            Case adAddNew
               Debug.Print "   AddNew"
            Case adApproxPosition
               Debug.Print "   AbsolutePosition and AbsolutePage"
            Case adBookmark
               Debug.Print "   Bookmark"
            Case adDelete
               Debug.Print "   Delete"
            Case adFind
               Debug.Print "   Find"
            Case adHoldRecords
               Debug.Print "   Holding Records"
            Case adMovePrevious
               Debug.Print "   MovePrevious and Move"
            Case adNotify
               Debug.Print "   Notifications"
            Case adResync
               Debug.Print "   Resyncing data"
            Case adUpdate
               Debug.Print "   Update"
            Case adUpdateBatch
               Debug.Print "   batch updating"
         End Select
      End If
   Next intIndex

End Sub
'EndSupports2VB

See Also

Recordset Object | Supports Method


Type Property Example (Field) (VB)

This example demonstrates the Type property by displaying the name of the constant that corresponds to the value of the Type property of all the Field objects in the Employees table. The FieldType function is required for this procedure to run.

'BeginTypeFieldVB
Public Sub TypeX()

   Dim rstEmployees As ADODB.Recordset
   Dim fldLoop As ADODB.Field
   Dim strCnn As String

   ' Open recordset with data from Employees table.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstEmployees = New ADODB.Recordset
   rstEmployees.Open "employee", strCnn, , , adCmdTable

   Debug.Print "Fields in Employees Table:" & vbCr

   ' Enumerate Fields collection of Employees table.
   For Each fldLoop In rstEmployees.fields
      Debug.Print "  Name: " & fldLoop.Name & vbCr & _
         "  Type: " & FieldType(fldLoop.Type) & vbCr
   Next fldLoop

End Sub
'EndTypeFieldVB

'BeginTypeField2VB
Public Function FieldType(intType As Integer) As String

   Select Case intType
      Case adChar
         FieldType = "adChar"
      Case adVarChar
         FieldType = "adVarChar"
      Case adSmallInt
         FieldType = "adSmallInt"
      Case adUnsignedTinyInt
         FieldType = "adUnsignedTinyInt"
      Case adDBTimeStamp
         FieldType = "adDBTimeStamp"
   End Select

End Function
'EndTypeField2VB

See Also

Field Object | Type Property


Type Property Example (Property) (VB)

This example demonstrates the Type property. It is a model of a utility for listing the names and types of a collection, like Properties, Fields, etc.

We do not need to open the Recordset to access its Properties collection; they come into existence when the Recordset object is instantiated. However, setting the CursorLocation property to adUseClient adds several dynamic properties to the Recordset object's Properties collection, making the example a little more interesting. For sake of illustration, we explicitly use the Item property to access each Property object.

'BeginTypePropertyVB
Public Sub TypeX()
Dim rst As ADODB.Recordset
Dim prop As ADODB.Property
Dim ix As Integer
Dim strMsg As String

Set rst = New ADODB.Recordset
rst.CursorLocation = adUseClient
For ix = 0 To rst.Properties.count - 1
Set prop = rst.Properties.Item(ix)
Select Case prop.Type
   Case adBigInt
      strMsg = "adBigInt"
   Case adBinary
      strMsg = "adBinary"
   Case adBoolean
      strMsg = "adBoolean"
   Case adBSTR
      strMsg = "adBSTR"
   Case adChapter
      strMsg = "adChapter"
   Case adChar
      strMsg = "adChar"
   Case adCurrency
      strMsg = "adCurrency"
   Case adDate
      strMsg = "adDate"
   Case adDBDate
      strMsg = "adDBDate"
   Case adDBTime
      strMsg = "adDBTime"
   Case adDBTimeStamp
      strMsg = "adDBTimeStamp"
   Case adDecimal
      strMsg = "adDecimal"
   Case adDouble
      strMsg = "adDouble"
   Case adEmpty
      strMsg = "adEmpty"
   Case adError
      strMsg = "adError"
   Case adFileTime
      strMsg = "adFileTime"
   Case adGUID
      strMsg = "adGUID"
   Case adIDispatch
      strMsg = "adIDispatch"
   Case adInteger
      strMsg = "adInteger"
   Case adIUnknown
      strMsg = "adIUnknown"
   Case adLongVarBinary
      strMsg = "adLongVarBinary"
   Case adLongVarChar
      strMsg = "adLongVarChar"
   Case adLongVarWChar
      strMsg = "adLongVarWChar"
   Case adNumeric
      strMsg = "adNumeric"
   Case adPropVariant
      strMsg = "adPropVariant"
   Case adSingle
      strMsg = "adSingle"
   Case adSmallInt
      strMsg = "adSmallInt"
   Case adTinyInt
      strMsg = "adTinyInt"
   Case adUnsignedBigInt
      strMsg = "adUnsignedBigInt"
   Case adUnsignedInt
      strMsg = "adUnsignedInt"
   Case adUnsignedSmallInt
      strMsg = "adUnsignedSmallInt"
   Case adUnsignedTinyInt
      strMsg = "adUnsignedTinyInt"
   Case adUserDefined
      strMsg = "adUserDefined"
   Case adVarBinary
      strMsg = "adVarBinary"
   Case adVarChar
      strMsg = "adVarChar"
   Case adVariant
      strMsg = "adVariant"
   Case adVarNumeric
      strMsg = "adVarNumeric"
   Case adVarWChar
      strMsg = "adVarWChar"
   Case adWChar
      strMsg = "adWChar"
   Case Else
      strMsg = "*UNKNOWN*"
End Select
Debug.Print "Property " & ix & ": " & prop.Name & _
            ", Type = " & strMsg
Next ix
End Sub
'EndTypePropertyVB

See Also

Property Object | Type Property


Update and CancelUpdate Methods Example (VB)

This example demonstrates the Update method in conjunction with the CancelUpdate method.

'BeginUpdateVB
Public Sub UpdateX()

   Dim rstEmployees As ADODB.Recordset
   Dim strOldFirst As String
   Dim strOldLast As String
   Dim strMessage As String
   Dim strCnn As String

   ' Open recordset with names from Employees table.
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstEmployees = New ADODB.Recordset
   rstEmployees.CursorType = adOpenKeyset
   rstEmployees.LockType = adLockOptimistic
   rstEmployees.Open "SELECT fname, lname " & _
      "FROM Employee ORDER BY lname", strCnn, , , adCmdText

   ' Store original data.
   strOldFirst = rstEmployees!fname
   strOldLast = rstEmployees!lname
   ' Change data in edit buffer.
   rstEmployees!fname = "Linda"
   rstEmployees!lname = "Kobara"

   ' Show contents of buffer and get user input.
   strMessage = "Edit in progress:" & vbCr & _
      "  Original data = " & strOldFirst & " " & _
      strOldLast & vbCr & "  Data in buffer = " & _
      rstEmployees!fname & " " & rstEmployees!lname & vbCr & vbCr & _
      "Use Update to replace the original data with " & _
      "the buffered data in the Recordset?"

   If MsgBox(strMessage, vbYesNo) = vbYes Then
      rstEmployees.Update
   Else
      rstEmployees.CancelUpdate
   End If

   ' Show the resulting data.
   MsgBox "Data in recordset = " & rstEmployees!fname & " " & _
      rstEmployees!lname

   ' Restore original data because this is a demonstration.
   If Not (strOldFirst = rstEmployees!fname And _
         strOldLast = rstEmployees!lname) Then
      rstEmployees!fname = strOldFirst
      rstEmployees!lname = strOldLast
      rstEmployees.Update
   End If

   rstEmployees.Close

End Sub

Public Sub UpdateX2()

   Dim cnn1 As ADODB.Connection
   Dim rstEmployees As ADODB.Recordset
   Dim strEmpID As String
   Dim strOldFirst As String
   Dim strOldLast As String
   Dim strMessage As String
   Dim strCnn As String

   ' Open a connection.
   Set cnn1 = New ADODB.Connection
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   cnn1.Open strCnn

   ' Open recordset with data from Employees table.
   Set rstEmployees = New ADODB.Recordset
   rstEmployees.CursorType = adOpenKeyset
   rstEmployees.LockType = adLockOptimistic
   rstEmployees.Open "employee", cnn1, , , adCmdTable

   rstEmployees.AddNew
   strEmpID = "B-S55555M"
   rstEmployees!emp_id = strEmpID
   rstEmployees!fname = "Bill"
   rstEmployees!lname = "Sornsin"

   ' Show contents of buffer and get user input.
   strMessage = "AddNew in progress:" & vbCr & _
      "Data in buffer = " & rstEmployees!emp_id & ", " & _
      rstEmployees!fname & " " & rstEmployees!lname & vbCr & vbCr & _
      "Use Update to save buffer to recordset?"

   If MsgBox(strMessage, vbYesNoCancel) = vbYes Then
      rstEmployees.Update
      ' Go to the new record and show the resulting data.
      MsgBox "Data in recordset = " & rstEmployees!emp_id & ", " & _
          rstEmployees!fname & " " & rstEmployees!lname
   Else
      rstEmployees.CancelUpdate
      MsgBox "No new record added."
   End If

   ' Delete new data because this is a demonstration.
   cnn1.Execute "DELETE FROM employee WHERE emp_id = '" & strEmpID & "'"

   rstEmployees.Close

End Sub
'EndUpdateVB

This example demonstrates the Update method in conjunction with the AddNew method.

Attribute VB_Name = "Update"

See Also

CancelUpdate Method | Recordset Object | Update Method


UpdateBatch and CancelBatch Methods Example (VB)

This example demonstrates the UpdateBatch method in conjunction with the CancelBatch method.

'BeginUpdateBatchVB
Public Sub UpdateBatchX()

   Dim rstTitles As ADODB.Recordset
   Dim strCnn As String
   Dim strTitle As String
   Dim strMessage As String

   ' Assign connection string to variable.
      strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "

   Set rstTitles = New ADODB.Recordset
   rstTitles.CursorType = adOpenKeyset
   rstTitles.LockType = adLockBatchOptimistic
   rstTitles.Open "Titles", strCnn, , , adCmdTable

   rstTitles.MoveFirst

   ' Loop through recordset and ask user if she wants
   ' to change the type for a specified title.
   Do Until rstTitles.EOF
      If Trim(rstTitles!Type) = "psychology" Then
         strTitle = rstTitles!title
         strMessage = "Title: " & strTitle & vbCr & _
            "Change type to self help?"

         If MsgBox(strMessage, vbYesNo) = vbYes Then
            rstTitles!Type = "self_help"
         End If
      End If

      rstTitles.MoveNext
   Loop

   ' Ask the user if she wants to commit to all the
   ' changes made above.
   If MsgBox("Save all changes?", vbYesNo) = vbYes Then
      rstTitles.UpdateBatch
   Else
      rstTitles.CancelBatch
   End If

   ' Print current data in recordset.
   rstTitles.Requery
   rstTitles.MoveFirst
   Do While Not rstTitles.EOF
      Debug.Print rstTitles!title & " - " & rstTitles!Type
      rstTitles.MoveNext
   Loop

   ' Restore original values because this is a demonstration.
   rstTitles.MoveFirst
   Do Until rstTitles.EOF
      If Trim(rstTitles!Type) = "self_help" Then
         rstTitles!Type = "psychology"
      End If
      rstTitles.MoveNext
   Loop
   rstTitles.UpdateBatch

   rstTitles.Close

End Sub
'EndUpdateBatchVB

See Also

CancelBatch Method | UpdateBatch Method


Value Property Example (VB)

This example demonstrates the Value property with Field and Property objects by displaying field and property values for the Employees table.

'BeginValueVB
Public Sub ValueX()

   Dim rstEmployees As ADODB.Recordset
   Dim fldLoop As ADODB.Field
   Dim prpLoop As ADODB.Property
   Dim strCnn As String

   ' Open recordset with data from Employees table.
   strCnn = "Provider=sqloledb;" & _
      "Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=; "
   Set rstEmployees = New ADODB.Recordset
   rstEmployees.Open "employee", strCnn, , , adCmdTable

   Debug.Print "Field values in rstEmployees"
   ' Enumerate the Fields collection of the Employees
   ' table.
   For Each fldLoop In rstEmployees.fields
      ' Because Value is the default property of a
      ' Field object, the use of the actual keyword
      ' here is optional.
      Debug.Print "   " & fldLoop.Name & " = " & fldLoop.Value
   Next fldLoop

   Debug.Print "Property values in rstEmployees"
   ' Enumerate the Properties collection of the
   ' Recordset object.
   For Each prpLoop In rstEmployees.Properties
      ' Because Value is the default property of a
      ' Property object, the use of the actual keyword
      ' here is optional.
      Debug.Print "   " & prpLoop.Name & " = " & prpLoop.Value
   Next prpLoop

   rstEmployees.Close

End Sub
'EndValueVB

See Also

Field Object | Property Object | Value Property


Version Property Example (VB)

This example uses the Version property of a Connection object to display the current ADO version. It also uses several dynamic properties to show:

  • the current DBMS name and version.
  • OLE DB version.
  • provider name and version.
  • ODBC version.
  • ODBC driver name and version.
'BeginVersionVB
Public Sub VersionX()

   Dim cnn1 As ADODB.Connection
   Dim strCnn As String
   Dim strVersionInfo As String

   ' Open connection.
   Set cnn1 = New ADODB.Connection
   strCnn = "driver={SQL Server};server=srv;" & _
      "user id=sa;password=;database=Pubs;"
   cnn1.Open strCnn

   strVersionInfo = "ADO Version: " & cnn1.Version & vbCr & _
   "DBMS Name: " & cnn1.Properties("DBMS Name") & vbCr & _
   "DBMS Version: " & cnn1.Properties("DBMS Version") & vbCr & _
   "OLE DB Version: " & cnn1.Properties("OLE DB Version") & vbCr & _
   "Provider Name: " & cnn1.Properties("Provider Name") & vbCr & _
   "Provider Version: " & cnn1.Properties("Provider Version") & vbCr & _
   "Driver Name: " & cnn1.Properties("Driver Name") & vbCr & _
   "Driver Version: " & cnn1.Properties("Driver Version") & vbCr & _
   "Driver ODBC Version: " & cnn1.Properties("Driver ODBC Version")

   MsgBox strVersionInfo

   cnn1.Close

End Sub
'EndVersionVB

See Also

Connection Object | Version Property


ADO Code Examples in Microsoft Visual Basic Scripting Edition

Use the following code examples to learn about how to use the ADO methods when writing in Visual Basic Scripting Edition (VBScript).

Note Paste the entire code example, from beginning to end, into your code editor. The example may not run correctly if partial examples are used or if paragraph formatting is lost.

Methods

  • AddNew Method Example
  • Clone Method Example
  • Delete Method Example
  • Execute, Requery, and Clear Methods Example
  • Move Method Example
  • MoveFirst, MoveLast, MoveNext, and MovePrevious Methods Example
  • Open and Close Methods Example

See Also

ADO Code Examples in Microsoft Visual Basic | ADO Code Examples in Microsoft Visual C++ | ADO Code Examples in Microsoft Visual J++ | Sample Applications


AddNew Method Example (VBScript)

This example uses the AddNew method to create a new record with the specified name.

Use the following example in an Active Server Page (ASP). Use Find to locate the file Adovbs.inc and place it in the directory you plan to use. Cut and paste the following code to Notepad or another text editor, and save it as AddNewVBS.asp. You can view the result in any client browser.

To exercise the example, add a new fictional record in the HTML form. Click Add New. See the Delete Method Example to remove unwanted records.

<!-- BeginAddNewVBS -->
<%@Language = VBScript %>
<!-- #Include file="ADOVBS.INC" -->
<HTML>
<HEAD>
   <TITLE>ADO AddNew Method (VBScript)</TITLE>
   <STYLE>
   <!--
   body {
      font-family: 'Verdana','Arial','Helvetica',sans-serif;
      BACKGROUND-COLOR:white;
      COLOR:black;
       }
   TH {
      background-color: #008080;
      font-family: 'Arial Narrow','Arial',sans-serif;
      font-size: xx-small;
      color: white;
      }
   TD {
      text-align: center;
      background-color: #f7efde;
      font-family: 'Arial Narrow','Arial',sans-serif;
      font-size: xx-small;
       }
   -->
   </STYLE>
</HEAD>
<BODY>

<H1>ADO AddNew Method (VBScript)</H1>

<!-- ADO Connection Object used to create recordset-->
<%
   strCnn = "Provider=sqloledb;Data Source=" & _
      Request.ServerVariables("SERVER_NAME") & _
      ";Initial Catalog=Northwind;User Id=sa;Password=;"

   'Create and Open Connection Object
   Set OBJdbConn = Server.CreateObject("ADODB.Connection")
   OBJdbConn.Open strCnn

   'Create and Open Recordset Object
   Set RsCustomerList = Server.CreateObject("ADODB.Recordset")
   RsCustomerList.ActiveConnection = OBJdbConn
   RsCustomerList.CursorLocation = adUseClient
   RsCustomerList.CursorType = adOpenKeyset
   RsCustomerList.LockType = adLockOptimistic
   RsCustomerList.Source = "Customers"
   RsCustomerList.Open

   'If this is first time page is open, Form collection
   'will be empty when data is entered. run AddNew method
   If Not IsEmpty(Request.Form) Then
      If Not Request.Form("CompanyName") = "" Then
         RsCustomerList.AddNew
         RsCustomerList("CustomerID") = Request.Form("CompanyID")
         RsCustomerList("CompanyName") = Request.Form("CompanyName")
         RsCustomerList("ContactName") = Request.Form("FirstName") & _
            " " & Request.Form("LastName")
         RsCustomerList("Phone") = Request.Form("PhoneNumber")
         RsCustomerList("City") = Request.Form("City")
         RsCustomerList("Region") = Request.Form("State")
         On Error Resume Next
         RsCustomerList.Update
         If OBJdbConn.Errors.Count > 0 Then
            For Each objError in OBJdbConn.Errors
               Response.Write("Error " & objError.SQLState & ": " & _
                  objError.Description & " | " & objError.NativeError)
            Next
            OBJdbConn.Errors.Clear
            RsCustomerList.CancelUpdate
         End If
         On Error GoTo 0
         RsCustomerList.MoveFirst
      End If
   End If
%>

<TABLE COLSPAN="8" CELLPADDING=5 BORDER=1 ALIGN="center"><!-- BEGIN column header row for Customer Table-->
   <TR>
      <TH>Customer ID</TH>
      <TH>Company Name</TH>
      <TH>Contact Name</TH>
      <TH>Phone Number</TH>
      <TH>City</TH>
      <TH>State/Province</TH>
      </TR>

      <!--
         Display ADO Data from Customer Table
         one row on each pass through recordset
      -->
   <%
      Do While Not RsCustomerList.EOF
         Response.Write("<TR>")
         Response.Write("<TD>" & RSCustomerList("CustomerID") & "</TD>")
         Response.Write("<TD>" & RSCustomerList("CompanyName")& "</TD>")
         Response.Write("<TD>" & RScustomerList("ContactName") & "</TD>")
         Response.Write("<TD>" & RScustomerList("Phone") & "</TD>")
         Response.Write("<TD>" & RScustomerList("City") & "</TD>")
         Response.Write("<TD>" & RScustomerList("Region") & "</TD>")
         Response.Write("</TR>")
         RScustomerList.MoveNext
      Loop
   %>
</TABLE>

<HR>

<!--
   Form to enter new record posts variables
   back to this page
-->
<FORM Method=post Action="AddNewVbs.asp" Name=Form>
   <TABLE>
      <TR>
         <TD>Company ID:</TD>
         <TD><INPUT Size="5" Name="CompanyID" maxLength=5  ></TD>
      </TR>
      <TR>
         <TD>Company Name:</TD>
         <TD><INPUT Size="50" Name="CompanyName" ></TD>
      </TR>
      <TR>
         <TD>Contact First Name:</TD>
         <TD><INPUT Size="50" Name="FirstName" ></TD>
      </TR>
      <TR>
         <TD>Contact Last Name:</TD>
         <TD><INPUT Size="50" Name="LastName" ></TD>
      </TR>
      <TR>
         <TD>Contact Phone:</TD>
         <TD><INPUT Size="50" Name="PhoneNumber" ></TD>
      </TR>
      <TR>
         <TD>City:</TD>
         <TD><INPUT Size="50" Name="City" ></TD>
      </TR>
      <TR>
         <TD>State / Province:</TD>
         <TD><INPUT Size="5" Name="State" ></TD>
      </TR>
      <TR>
         <TD Align="right"><INPUT Type="submit" Value="Add New"></TD>
         <TD Align="left"><INPUT Type="reset" Value="Reset Form"></TD>
      </TR>
   </TABLE>
</FORM>

<%
   'Show location of data source
   Response.Write(OBJdbConn)
%>

<SCRIPT Language = "VBScript">
Sub Form_OnSubmit
   MsgBox "Sending New Record to Server",,"ADO-ASP _Example"
End Sub
</SCRIPT>
</BODY>
</HTML>
<!-- EndAddNewVBS -->

See Also

AddNew Method | Recordset Object


Clone Method Example (VBScript)

This example uses the Clone method to create copies of a Recordset and then lets the user position the record pointer of each copy independently.

Use the following example in an Active Server Page (ASP). This example uses the Northwind database distributed with Microsoft Access. Use Find to locate the file Adovbs.inc and place it in the directory you plan to use. Cut and paste the following code to Notepad or another text editor and save it as Clone.asp. You can view the result in any client browser.

To exercise the example, change the line RsCustomerList.Source = "Customers" to RsCustomerList.Source = "Products" to count a larger table.

<!-- BeginCloneVBS -->
<% Language = VBScript %>
<!-- #Include file="ADOVBS.INC" -->
<HTML>
<HEAD>
<TITLE>ADO Clone Method</TITLE>
</HEAD>

<BODY>

<H3>ADO Clone Method</H3>
<%
   ' ADO Connection Object used to Create recordset
   src = "\program files\microsoft office\office\samples\northwind.mdb"
   sConnStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & src

   'Create and Open Connection Object
   Set OBJdbConn = Server.CreateObject("ADODB.Connection")
   OBJdbConn.Open  sConnStr
   'Create and open Recordset object
   Set RsCustomerList = Server.CreateObject("ADODB.Recordset")
   RsCustomerList.ActiveConnection = OBJdbConn
   RsCustomerList.CursorType = adOpenKeyset
   RsCustomerList.LockType = adLockOptimistic
   RsCustomerList.Source = "Customers"
   RsCustomerList.Open

   ' Loop through Customers Table, adding 1 to the Counter variable each pass
   Set MyRecordset = RSCustomerList.Clone
   Counter = 0
   Do Until MyRecordset.EOF
      Counter = Counter + 1
      MyRecordset.MoveNext
   Loop
%>

<!-- Display Results -->
<H3>There Are <%=Counter %> Records in the Customers Table</H3>
<BR><HR>
<H4>Location of DSN Datbase</H4>

<%
   ' Show location of DSN data source
   Response.Write(OBJdbConn)
%>
</BODY>
</HTML>
<!-- EndCloneVBS -->

See Also

Clone Method | Recordset Object


Delete Method Example (VBScript)

This example uses the Delete method to remove a specified record from a Recordset.

Use the following example in an Active Server Page (ASP). To view this fully functional example, you must either have the data source AdvWorks.mdb (installed with the SDK) located at C:\Program Files\Microsoft Platform SDK\Samples\DataAccess\Rds\RDSTest\advworks.mdb or edit the path in the example code to reflect the actual location of this file. This is a Microsoft Access database file.

Use Find to locate the file Adovbs.inc and place it in the directory you plan to use. Cut and paste the following code into Notepad or another text editor, and save it as DeleteVBS.asp. You can view the result in any client browser.

To exercise the example, try using the AddNew example first to add some records. Then you can try to delete them. View the result in any client browser.

<!-- BeginDeleteVBS -->
<%@ Language=VBScript %>
<!-- #Include file="ADOVBS.INC" -->
<HTML>
<HEAD>
<TITLE>ADO Delete Method</TITLE>
</HEAD>
<STYLE>
<!--
TH {
   background-color: #008080;
   font-family: 'Arial Narrow','Arial',sans-serif;
   font-size: xx-small;
   color: white;
   }
TD {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Arial Narrow','Arial',sans-serif;
   font-size: xx-small;
    }
-->
</STYLE>

<BODY>
<H3>ADO Delete Method</H3>

<!--- ADO Connection Object used to Create recordset-->
<%
   ' Remember to change the path to advworks.mdb if you
   ' installed it in other than the default location.
   sSrc = "C:\Program Files\Microsoft Platform SDK\Samples\DataAccess\rds\RDSTest\advworks.mdb"
   sConnStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & sSrc

   'Create and Open Connection Object
   Set objDBConn = Server.CreateObject("ADODB.Connection")
   objDBConn.Open  sConnStr
   'Create and Open Recordset Object
   Set RsCustomerList = Server.CreateObject("ADODB.Recordset")
   rsCustomerList.ActiveConnection = objDBConn
   rsCustomerList.CursorType = adOpenKeyset
   rsCustomerList.LockType = adLockOptimistic
   rsCustomerList.Source = "Customers"
   rsCustomerList.Open
%>

<%
   ' Move to designated record and delete it
   If Not IsEmpty(Request.Form("WhichRecord")) Then
      'Get value to move from Form Post method
      Moves = Request.Form("WhichRecord")

      RsCustomerList.Move CInt(Moves)
      If Not RsCustomerList.EOF or RsCustomerList.BOF Then
         RsCustomerList.Delete 1
         RsCustomerList.MoveFirst
      Else
         Response.Write "Not a Valid Record Number"
         RsCustomerList.MoveFirst
      End If
   End If
%>

<!-- BEGIN column header row for Customer Table-->
<TABLE COLSPAN=8 CELLPADDING=5 BORDER=0>
<TR>
   <TH>Rec. #</TH>
   <TH>Company Name</TH>
   <TH>Contact Name</TH>
   <TH>Phone Number</TH>
   <TH>City</TH>
   <TH>State/Province</TH>
</TR>

   <%
   ' Display ADO Data from Customer Table Loop through Recordset adding
   ' One Row to HTML Table each pass

   iCount = 0
   Do While Not RsCustomerList.EOF %>
   <TR>
     <TD> <%= CStr(iCount) %>
     <TD> <%= RSCustomerList("CompanyName")%> </TD>
     <TD> <%= RScustomerList("ContactLastName") & ", " %>
         <%= RScustomerList("ContactFirstName") %>
     </TD>
     <TD> <%= RScustomerList("PhoneNumber")%> </TD>
     <TD> <%= RScustomerList("City")%> </TD>
     <TD> <%= RScustomerList("StateOrProvince")%> </TD>
   </TR>
   <!-Next Row = Record Loop and add to html table-->
   <%
     iCount = iCount + 1
     RScustomerList.MoveNext
   Loop
   %>
   </TABLE>

<!-- Do Client side Input Data Validation Move to named record and Delete it -->
<Center>
<H4>Clicking Button Will Remove Designated Record</H4>
<H5>There are <%=RsCustomerList.RecordCount%> Records in this Set</H5>

<Form Method=Post Action="Deletevbs.asp" Name=Form>
   <Input Type=Text Name="WhichRecord" Size=3>
   <Input Type=Button Name=cmdDelete Value="Delete Record">
</Form>

</BODY>

<Script Language = "VBScript">
Sub cmdDelete_OnClick
   If IsNumeric(Document.Form.WhichRecord.Value) Then
      Document.Form.WhichRecord.Value = CInt(Document.Form.WhichRecord.Value)
      Dim Response
      Response = MsgBox("Are You Sure About Deleting This Record?", vbYesNo,  "ADO-ASP Example")

      If Response = vbYes Then
         Document.Form.Submit
      End If
   Else
      MsgBox "You Must Enter a Valid Record Number",,"ADO-ASP Example"
   End If
End Sub
</Script>
</HTML>
<!-- EndDeleteVBS -->

See Also

Delete Method (ADO Recordset) | Recordset Object


Execute, Requery, and Clear Methods Example (VBScript)

This example demonstrates the Execute method when run from both a Command object and a Connection object. It also uses the Requery method to retrieve current data in a recordset, and the Clear method to clear the contents of the Errors collection. The ExecuteCommand and PrintOutput procedures are required for this procedure to run.

Use the following example in an Active Server Page (ASP). To view this fully functional example, you must either have the data source AdvWorks.mdb (installed with the SDK) located at C:\Program Files\Microsoft Platform SDK\Samples\DataAccess\Rds\RDSTest\advworks.mdb or edit the path in the example code to reflect the actual location of this file. This is a Microsoft Access database file.

Use Find to locate the file Adovbs.inc and place it in the directory you plan to use. Cut and paste the following code into Notepad or another text editor, and save it as ExecuteVBS.asp. You can view the result in any client browser.

<!-- BeginExecuteVBS -->
<%@ Language=VBScript %>
<!--#include file="adovbs.inc"-->
<HTML>
<HEAD>
<META name="VI60_DefaultClientScript"  content=VBScript>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
<title>ADO Execute Method</title>
<STYLE>
<!--
BODY {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</STYLE>
</HEAD>

<BODY>
<H3>ADO Execute Method</H3>
<HR>
<H4>Recordset Retrieved Using Connection Object</H4>
<!--- Recordsets retrieved using Execute method of Connection and Command Objects-->
<%
' Remember to change the path to advworks.mdb if you
' installed it in other than the default location.
sDsrc="C:\Program Files\Microsoft Platform SDK\Samples\DataAccess\Rds\RDSTest\advworks.mdb"
sConnStr="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & sDsrc

Set OBJdbConnection = Server.CreateObject("ADODB.Connection")
OBJdbConnection.Open sConnStr
SQLQuery = "SELECT * FROM Customers"
'1st Recordset RSCustomerList using conn execute mthd
Set RSCustomerList = OBJdbConnection.Execute(SQLQuery)

Set OBJdbCommand = Server.CreateObject("ADODB.Command")
OBJdbCommand.ActiveConnection = OBJdbConnection
SQLQuery2 = "SELECT * From Products"
OBJdbCommand.CommandText = SQLQuery2
'2nd Recordset RSProductList using conn execute mthd
Set RSProductList = OBJdbCommand.Execute
%>
<TABLE COLSPAN=8 CELLPADDING=5 BORDER=0 ALIGN=CENTER>
<!-- BEGIN column header row for Customer Table-->
<TR CLASS=thead>
  <TH>Company Name</TH>
  <TH>Contact Name</TH>
  <TH>E-mail address</TH>
  <TH>City</TH>
  <TH>State/Province</TH>
</TR>

<!--Display ADO Data from Customer Table-->
<%
Do While Not RScustomerList.EOF %>
  <TR CLASS=tbody>
  <TD>
  <%= RSCustomerList("CompanyName")%>
  </TD>
  <TD>
  <%= RScustomerList("ContactLastName") & ", " %>
  <%= RScustomerList("ContactFirstName") %>
  </TD>
  <TD>
  <%= RScustomerList("ContactLastName")%>
  </TD>
  <TD>
  <%= RScustomerList("City")%>
  </TD>
  <TD>
  <%= RScustomerList("StateOrProvince")%>
  </TD>
  </TR>
  <!-Next Row = Record Loop and add to html table-->
  <%
  RScustomerList.MoveNext
Loop
RScustomerList.Close
%>
</TABLE>

<HR>
<H4>Recordset Retrieved Using Command Object</H4>

<TABLE CELLPADDING=5 BORDER=0 ALIGN=CENTER WIDTH="80%">

<!-- BEGIN column header row for Product List Table-->
<TR CLASS=thead2>
  <TH>Product Type</TH>
  <TH>Product Name</TH>
  <TH>Product Description</TH>
  <TH>Unit Price</TH>
</TR>

<!-- Display ADO Data Product List-->
<% Do While Not RsProductList.EOF %>
  <TR CLASS=tbody>
  <TD>
  <%= RsProductList("ProductType")%>
  </TD>
  <TD>
  <%= RsProductList("ProductName")%>
  </TD>
  <TD align="left">
   <%= RsProductList("ProductDescription")%>
  </TD>
  <TD>
  <%= RsProductList("UnitPrice")%>
  </TD>
  <!--  Next Row = Record -->
<%
  RsProductList.MoveNext
Loop
'Remove objects from memory to free resources
RsProductList.Close
OBJdbConnection.Close
Set ObJdbCommand = Nothing
Set RsProductList = Nothing
Set OBJdbConnection = Nothing
%>
</TABLE>

</BODY>
</HTML>
<!-- EndExecuteVBS -->

See Also

Clear Method | Command Object | Connection Object | Error Object | Errors Collection | Execute Method (ADO Command) | Execute Method (ADO Connection) | Recordset Object | Requery Method


Move Method Example (VBScript)

This example uses the Move method to position the record pointer, based on user input.

Use the following example in an Active Server Page (ASP). To view this fully functional example, you must either have the data source AdvWorks.mdb (installed with the SDK) located at C:\Program Files\Microsoft Platform SDK\Samples\DataAccess\Rds\RDSTest\advworks.mdb or edit the path in the example code to reflect the actual location of this file. This is a Microsoft Access database file.

Use Find to locate the file Adovbs.inc and place it in the directory you plan to use. Cut and paste the following code to Notepad or another text editor, and save it as Move.asp. You can view the result in any browser.

Try entering a letter or noninteger to see the error handling work.

<!-- BeginMoveVBS -->
<%@ Language=VBScript %>
<!-- #Include file="ADOVBS.INC" -->
<HTML>
<HEAD>
<TITLE>ADO Move Methods</TITLE>
<STYLE>
<!--
BODY {
   font-family: "MS SANS SERIF",sans-serif;
    }
.thead1 {
   background-color: #008080;
   font-family: 'Arial Narrow','Arial',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Arial Narrow','Arial',sans-serif;
   font-size: x-small;
    }
-->
</STYLE>
</HEAD>
<BODY>
<H3>ADO Move Methods</H3>
<%
' Remember to change the path to advworks.mdb if you
' installed it in other than the default location.
src = "C:\program files\Microsoft Platform SDK\samples\dataaccess\rds\rdstest\advworks.mdb"
sConnStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & src

'Create and Open Connection Object
Set OBJdbConn = Server.CreateObject("ADODB.Connection")
OBJdbConn.Open  sConnStr

'Create and Open Recordset Object
Set RsCustomerList = Server.CreateObject("ADODB.Recordset")
RsCustomerList.ActiveConnection = OBJdbConn
RsCustomerList.CursorType = adOpenKeyset
RsCustomerList.LockType = adLockOptimistic
RsCustomerList.Source = "Customers"

RsCustomerList.Open

'Check number of user moves this session.
'Increment by amount in Form.
Session("Clicks") = Session("Clicks") + Request.Form("MoveAmount")
Clicks = Session("Clicks")
' Move to last known recordset position plus amount passed
' by Form Post method.
RsCustomerList.Move CInt(Clicks)

'Error Handling
   If RsCustomerList.EOF Then
         Session("Clicks") = RsCustomerList.RecordCount
         Response.Write "This is the Last Record"
         RsCustomerList.MoveLast
      Else If RsCustomerList.BOF Then
         Session("Clicks") = 1
         RsCustomerList.MoveFirst
         Response.Write "This is the First Record"
      End If
   End If
%>

<H3>Current Record Number is <BR>
<%
If Session("Clicks") = 0 Then
   Session("Clicks") = 1
End If
Response.Write(Session("Clicks") )%> of <%=RsCustomerList.RecordCount%></H3>
<HR>

<TABLE COLSPAN=8 CELLPADDING=5 BORDER=0>

<!-- BEGIN column header row for Customer Table-->

<TR CLASS=thead1>
   <TD>Company Name</TD>
   <TD>Contact Name</TD>
   <TD>Phone Number</TD>
   <TD>City</TD>
   <TD>State/Province</TD>
</TR>

<!--Display ADO Data from Customer Table-->

<TR CLASS=tbody>
  <TD> <%= RSCustomerList("CompanyName")%> </TD>
  <TD> <%= RScustomerList("ContactLastName") & ", " %>
      <%= RScustomerList("ContactFirstName") %> </TD>
  <TD> <%= RScustomerList("PhoneNumber")%> </TD>
  <TD> <%= RScustomerList("City")%> </TD>
  <TD> <%= RScustomerList("StateOrProvince")%> </TD>
</TR>
</TABLE>

<HR>
<Input Type=Button Name=cmdDown  Value="&lt;  ">
<Input Type=Button Name=cmdUp Value=" &gt;">
<H5>Click Direction Arrows for Previous or Next Record
<BR> Click Move Amount to use Move Method
Enter Number of Records to Move + or - </H5>

<TABLE>

<FORM Method = Post Action="MoveVbs.asp" Name=Form>
<TR>
   <TD><Input Type="Button" Name=Move Value="Move Amount "></TD>
   <TD></TD>
   <TD><Input Type="Text" Size="4" Name="MoveAmount" Value=0></TD>
<TR>
</FORM>
</TABLE>

</BODY>

<Script Language = "VBScript">

Sub Move_OnClick
   ' Make sure move value entered is an integer.
   If IsNumeric(Document.Form.MoveAmount.Value)Then
      Document.Form.MoveAmount.Value = CInt(Document.Form.MoveAmount.Value)
      Document.Form.Submit
   Else
      MsgBox "You Must Enter a Number", ,"ADO-ASP Example"
      Document.Form.MoveAmount.Value = 0
   End If
End Sub

Sub cmdDown_OnClick
   Document.Form.MoveAmount.Value = -1
   Document.Form.Submit
End Sub

Sub cmdUp_OnClick
   Document.Form.MoveAmount.Value = 1
   Document.Form.Submit
End Sub
</Script>
<!-- EndMoveVBS -->

See Also

Move Method | Recordset Object


MoveFirst, MoveLast, MoveNext, and MovePrevious Methods Example (VBScript)

This example uses the MoveFirst, MoveLast, MoveNext, and MovePrevious methods to move the record pointer of a Recordset based on the supplied command.

Cut and paste the following code into Notepad or another text editor, and save it as MoveOne.asp. You can view the result in any browser.

<!-- BeginMoveFirstVBS -->
<%@ Language=VBScript %>
<!--#include file="adovbs.inc"-->
<HTML>
<HEAD>
<TITLE>ADO MoveNext, MovePrevious, MoveLast, MoveFirst Methods</TITLE>

<SCRIPT LANGUAGE="VBScript">
<!--
   Sub cmdDown_OnClick
      'Set Values in Form Input Boxes and Submit Form
      Document.form.MoveAction.Value = "MovePrev"
      Document.Form.Submit
   End Sub

   Sub cmdUp_OnClick
      Document.form.MoveAction.Value = "MoveNext"
      Document.Form.Submit
   End Sub

   Sub cmdFirst_OnClick
      Document.form.MoveAction.Value = "MoveFirst"
      Document.Form.Submit
   End Sub

   Sub cmdLast_OnClick
      Document.form.MoveAction.Value = "MoveLast"
      Document.Form.Submit
   End Sub
//-->
</SCRIPT>
</HEAD>

<body bgcolor="white">
<h1>ADO MoveNext, MovePrevious, MoveLast, MoveFirst Methods Example</h1>
<!-- Create Connection and Recordset Objects on Server -->
<%
   sConnStr = "Provider=sqloledb;" & _
              "Data Source=" & Request.ServerVariables("SERVER_NAME") & _
              ";Initial Catalog=Northwind;User Id=sa;Password=;"

   'Create and Open Connection Object
   Set objConn = Server.CreateObject("ADODB.Connection")
   objConn.Open  sConnStr
   'Create and Open Recordset Object
   Set rsEmployee = Server.CreateObject("ADODB.Recordset")
   rsEmployee.ActiveConnection = objConn
   rsEmployee.CursorType = adOpenKeyset
   rsEmployee.CursorLocation = adUseClient
   rsEmployee.LockType = adLockOptimistic
   rsEmployee.Source = "employees"
   rsEmployee.Open

   rsEmployee.MoveFirst

   If Not IsEmpty(Request.Form("MoveAction")) Then
      strAction = Request.Form("MoveAction")
      varPosition  = Request.Form("Position")

      rsEmployee.AbsolutePosition = varPosition

      Select Case strAction

        Case "MoveNext"

         rsEmployee.MoveNext
         If rsEmployee.EOF Then
            rsEmployee.MoveLast
            strMessage = "Can't move beyond the last record."
         End If

        Case "MovePrev"

         rsEmployee.MovePrevious
         If rsEmployee.BOF Then
            rsEmployee.MoveFirst
            strMessage = "Can't move beyond the first record."
         End If

        Case "MoveLast"

         rsEmployee.MoveLast

        Case "MoveFirst"

         rsEmployee.MoveFirst

      End Select
   End If

%>

<!-- Display Current Record Number and Recordset Size -->
<h2>Record Number <%=RsEmployee.AbsolutePosition%> of <%=RsEmployee.RecordCount%></H2>
<hr>
<table cellpadding=5 border=0>
<!-- BEGIN column header row for Customer Table-->
<tr>
   <th>Name</th>
   <th>Hire Date</th>
</tr>

<!--Display ADO Data from Customer Table-->
<tr>
  <td><%= rsEmployee("LastName") & ", " %>
      <%= rsEmployee("FirstName") & " " %></td>
  <td><%= rsEmployee("HireDate")%></td>
</tr>
<tr>
  <td colspan=2><%=strMessage%></td>
</tr>
</table>

<hr>

<form Method=Post
      Action="MoveFirstVbs.asp"
      Name=Form>
<Input Type=Button Name=cmdDown Value="<">
<Input Type=Button Name=cmdUp Value=">">
<BR>
<Input Type=Button Name=cmdFirst Value="First Record">
<Input Type=Button Name=cmdLast Value="Last Record">

<H3>Click Direction Arrows to Use MovePrevious or MoveNext</H3>

<!-- Use Hidden Form Fields to send values to Server -->

<input Type="Hidden" Size="4" Name="MoveAction" Value="Move">
<input Type="Hidden" Size="4" Name="Position" Value="<%= rsEmployee.AbsolutePosition %>">
</form>

<HR>
</BODY>
</HTML>
<!-- EndMoveFirstVBS -->

See Also

MoveFirst, MoveLast, MoveNext, and MovePrevious Methods | Recordset Object


Open and Close Methods Example (VBScript)

This example uses the Open and Close methods on both Recordset and Connection objects that have been opened.

Use the following example in an Active Server Page (ASP). To view this fully functional example, you must either have the data source AdvWorks.mdb (installed with the SDK) located at C:\Program Files\Microsoft Platform SDK\Samples\DataAccess\Rds\RDSTest\advworks.mdb or edit the path in the example code to reflect the actual location of this file. This is a Microsoft Access database file.

Use Find to locate the file Adovbs.inc and place it in the directory you plan to use. Cut and paste the following code into Notepad or another text editor, and save it as MoveOne.asp. You can view the result in any browser.

<!-- BeginOpenVBS -->
<%@ Language=VBScript %>
<!--#include file="adovbs.inc"-->
<HTML>
<HEAD>
<META name="VI60_DefaultClientScript"  content=VBScript>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
<title>ADO Open Method</title>
<STYLE>
<!--
BODY {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</STYLE>
</HEAD>

<BODY>
<H3>ADO Open Method</H3>

<TABLE WIDTH=600 BORDER=0>
<TR>
<TD VALIGN=TOP COLSPAN=3>
<FONT SIZE=2>
<!--- ADO Connection used to create 2 recordsets-->
<%
' Remember to change the path to advworks.mdb if you
' installed it in other than the default location.
src = "C:\Program Files\Microsoft Platform SDK\Samples\DataAccess\Rds\RDSTest\advworks.mdb"
sConnStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & src

'Create and Open Connection Object
Set OBJdbConn = Server.CreateObject("ADODB.Connection")
OBJdbConn.Open  sConnStr
SQLQuery = "SELECT * FROM Customers"

'First Recordset RsCustomerList
Set RsCustomerList = OBJdbConn.Execute(SQLQuery)

'Second Recordset RsProductist
Set RsProductList = Server.CreateObject("ADODB.Recordset")
RsProductList.CursorType = adOpenDynamic
RsProductList.LockType = adLockOptimistic
RsProductList.Open "Products", OBJdbConn
%>

<TABLE COLSPAN=8 CELLPADDING=5 BORDER=0>
<!-- BEGIN column header row for Customer Table-->
<TR CLASS=thead>
   <TD>Company Name</TD>
   <TD>Contact Name</TD>
   <TD>E-mail address</TD>
   <TD>City</TD>
   <TD>State/Province</TD>
</TR>

<!--Display ADO Data from Customer Table-->
<% Do While Not RScustomerList.EOF %>
<TR CLASS=tbody>
  <TD> <%= RSCustomerList("CompanyName")%> </TD>
  <TD> <%= RScustomerList("ContactLastName") & ", " %>
       <%= RScustomerList("ContactFirstName") %> </TD>
  <TD> <%= RScustomerList("ContactLastName")%> </TD>
  <TD> <%= RScustomerList("City")%> </TD>
  <TD> <%= RScustomerList("StateOrProvince")%> </TD>
</TR>
<!-Next Row = Record Loop and add to html table-->
<%
   RScustomerList.MoveNext
Loop
RScustomerList.Close
%>
</TABLE>

<HR>

<TABLE COLSPAN=8 CELLPADDING=5 BORDER=0>
<!-- BEGIN column header row for Product List Table-->

<TR CLASS=thead2>
   <TD>Product Type</TD>
   <TD>Product Name</TD>
   <TD>Product Description</TD>
   <TD>Unit Price</TD>
</TR>
<!-- Display ADO Data Product List-->
<% Do While Not RsProductList.EOF %>
  <TR CLASS=tbody>
  <TD> <%= RsProductList("ProductType")%> </TD>
  <TD> <%= RsProductList("ProductName")%> </TD>
  <TD> <%= RsProductList("ProductDescription")%> </TD>
  <TD> <%= RsProductList("UnitPrice")%> </TD>
  </TR>
  <!--  Next Row = Record -->
<%
  RsProductList.MoveNext
Loop
OBJdbConn.Close

'Remove Objects from Memory Freeing
Set RsProductList = Nothing
Set OBJdbConn = Nothing
%>
</TABLE>

</BODY>
</HTML>
<!-- EndOpenVBS -->

See Also

Close Method | Connection Object | Open Method (ADO Connection) | Open Method (ADO Recordset) | Recordset Object


ADO Code Examples in Microsoft Visual C++

Use the following code examples to learn how to use the ADO methods, properties, and events when writing in Microsoft Visual C++.

Note Paste the entire code example, from beginning to end, into your code editor. The example may not run correctly if partial examples are used or if paragraph formatting is lost.

Methods

  • AddNew Method Example
  • Append and CreateParameter Methods Example
  • AppendChunk and GetChunk Methods Example
  • BeginTrans, CommitTrans, and RollbackTrans Methods Example
  • Cancel Method Example
  • Clone Method Example
  • CompareBookmarks Method Example
  • Delete Method Example
  • Execute, Requery, and Clear Methods Example
  • Find Method Example
  • GetRows Method Example
  • GetString Method Example
  • MoveFirst, MoveLast, MoveNext, and MovePrevious Methods Example
  • NextRecordset Method Example
  • Open and Close Methods Example
  • OpenSchema Method Example
  • Refresh Method Example
  • Resync Method Example
  • 6Save and Open Methods Example
  • Seek Method and Index Property Example
  • Supports Method Example
  • Update and CancelUpdate Methods Example
  • UpdateBatch and CancelBatch Methods Example

Properties

  • AbsolutePage, PageCount, and PageSize Properties Example
  • AbsolutePosition and CursorLocation Properties Example
  • ActiveCommand Property Example
  • ActiveConnection, CommandText, CommandTimeout, CommandType, Size, and Direction Properties Example
  • ActualSize and DefinedSize Properties Example
  • Attributes and Name Properties Example
  • BOF, EOF, and Bookmark Properties Example
  • CacheSize Property Example
  • ConnectionString, ConnectionTimeout, and State Properties Example
  • Count Property Example
  • CursorType, LockType, and EditMode Properties Example
  • Description, HelpContext, HelpFile, NativeError, Number, Source, and SQLState Properties Example
  • Filter and RecordCount Properties Example
  • Index Property and Seek Method Example
  • IsolationLevel and Mode Properties Example
  • Item Property Example
  • MarshalOptions Property Example
  • MaxRecords Property Example
  • NumericScale and Precision Properties Example
  • Optimize Property Example
  • OriginalValue and UnderlyingValue Properties Example
  • Prepared Property Example
  • Provider and DefaultDatabase Properties Example
  • Sort Property Example
  • Source Property Example
  • State Property Example
  • Status Property Example
  • StayInSync Property Example
  • Type Property Example (Fields)
  • Type Property Example (Property)
  • Value Property Example
  • Version Property Example

Other

  • ADO Events Model Example

See Also

ADO Code Examples in Microsoft Visual Basic | ADO Code Examples in Microsoft Visual Basic Scripting Edition | ADO Code Examples in Microsoft Visual J++ | Sample Applications


ADO Events Model Example (VC++)

The Visual C++ section of ADO Event Instantiation by Language gives a general description of how to instantiate the ADO event model. The following is a specific example of instantiating the event model within the environment created by the #import directive.

The general description uses adoint.h as a reference for method signatures. However, a few details in the general description change slightly as a result of using the #import directive:

  • The #import directive resolves typedef's, and method signature data types and modifiers to their fundamental forms.
  • The pure virtual methods that must be overwritten are all prefixed by "raw_".

Some of the code simply reflects coding style.

  • The pointer to IUnknown used by the Advise method is obtained explicitly with a call to QueryInterface.
  • You don't need to explicitly code a destructor in the class definitions.
  • You may want to code more robust implementations of QueryInterface, AddRef, and Release.
  • The __uuidof() directive is used extensively to obtain interface IDs.

Finally, the example contains some working code.

  • The example is written as a console application.
  • You should insert your own code under the comment, "// Do some work".
  • All the event handlers default to doing nothing, and canceling further notifications. You should insert the appropriate code for your application, and allow notifications if required.
// eventmodel.cpp : Defines the entry point for the console application.
//

#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
   no_namespace rename("EOF", "EndOfFile")
#include <comdef.h>
#include <stdio.h>

//----The Connection events----------------------------------------------

class CConnEvent : public ConnectionEventsVt
{
private:
      ULONG   m_cRef;
   public:
      CConnEvent() { m_cRef = 0; };
      ~CConnEvent() {};

      STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

      STDMETHODIMP raw_InfoMessage(
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Connection *pConnection);

      STDMETHODIMP raw_BeginTransComplete(
         LONG TransactionLevel,
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Connection *pConnection);

   STDMETHODIMP raw_CommitTransComplete(
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Connection *pConnection);

      STDMETHODIMP raw_RollbackTransComplete(
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Connection *pConnection);

      STDMETHODIMP raw_WillExecute(
         BSTR *Source,
         CursorTypeEnum *CursorType,
         LockTypeEnum *LockType,
         long *Options,
         EventStatusEnum *adStatus,
         struct _Command *pCommand,
         struct _Recordset *pRecordset,
         struct _Connection *pConnection);

      STDMETHODIMP raw_ExecuteComplete(
         LONG RecordsAffected,
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Command *pCommand,
         struct _Recordset *pRecordset,
         struct _Connection *pConnection);

   STDMETHODIMP raw_WillConnect(
         BSTR *ConnectionString,
         BSTR *UserID,
         BSTR *Password,
         long *Options,
         EventStatusEnum *adStatus,
         struct _Connection *pConnection);

      STDMETHODIMP raw_ConnectComplete(
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Connection *pConnection);

      STDMETHODIMP raw_Disconnect(
         EventStatusEnum *adStatus,
         struct _Connection *pConnection);
};

//-----The Recordset events----------------------------------------------

class CRstEvent : public RecordsetEventsVt
    {
   private:
      ULONG m_cRef;
    public:
      CRstEvent() { m_cRef = 0; };
      ~CRstEvent() {};

         STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
         STDMETHODIMP_(ULONG) AddRef(void);
         STDMETHODIMP_(ULONG) Release(void);

         STDMETHODIMP raw_WillChangeField(
         LONG cFields,
         VARIANT Fields,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset);

      STDMETHODIMP raw_FieldChangeComplete(
         LONG cFields,
         VARIANT Fields,
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset);

      STDMETHODIMP raw_WillChangeRecord(
         EventReasonEnum adReason,
         LONG cRecords,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset);

      STDMETHODIMP raw_RecordChangeComplete(
         EventReasonEnum adReason,
         LONG cRecords,
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset);

      STDMETHODIMP raw_WillChangeRecordset(
         EventReasonEnum adReason,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset);

      STDMETHODIMP raw_RecordsetChangeComplete(
         EventReasonEnum adReason,
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset);

      STDMETHODIMP raw_WillMove(
         EventReasonEnum adReason,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset);

      STDMETHODIMP raw_MoveComplete(
         EventReasonEnum adReason,
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset);

      STDMETHODIMP raw_EndOfRecordset(
         VARIANT_BOOL *fMoreData,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset);

      STDMETHODIMP raw_FetchProgress(
         long Progress,
         long MaxProgress,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset);

      STDMETHODIMP raw_FetchComplete(
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset);
};

//-----Implement each connection method----------------------------------

      STDMETHODIMP CConnEvent::raw_InfoMessage(
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Connection *pConnection)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CConnEvent::raw_BeginTransComplete(
         LONG TransactionLevel,
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Connection *pConnection)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CConnEvent::raw_CommitTransComplete(
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Connection *pConnection)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CConnEvent::raw_RollbackTransComplete(
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Connection *pConnection)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

   STDMETHODIMP CConnEvent::raw_WillExecute(
         BSTR *Source,
         CursorTypeEnum *CursorType,
         LockTypeEnum *LockType,
         long *Options,
         EventStatusEnum *adStatus,
         struct _Command *pCommand,
         struct _Recordset *pRecordset,
         struct _Connection *pConnection)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CConnEvent::raw_ExecuteComplete(
         LONG RecordsAffected,
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Command *pCommand,
         struct _Recordset *pRecordset,
         struct _Connection *pConnection)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CConnEvent::raw_WillConnect(
         BSTR *ConnectionString,
         BSTR *UserID,
         BSTR *Password,
         long *Options,
         EventStatusEnum *adStatus,
         struct _Connection *pConnection)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CConnEvent::raw_ConnectComplete(
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Connection *pConnection)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CConnEvent::raw_Disconnect(
         EventStatusEnum *adStatus,
         struct _Connection *pConnection)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

//-----Implement each recordset method-----------------------------------

      STDMETHODIMP CRstEvent::raw_WillChangeField(
         LONG cFields,
         VARIANT Fields,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CRstEvent::raw_FieldChangeComplete(
         LONG cFields,
         VARIANT Fields,
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CRstEvent::raw_WillChangeRecord(
         EventReasonEnum adReason,
         LONG cRecords,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CRstEvent::raw_RecordChangeComplete(
         EventReasonEnum adReason,
         LONG cRecords,
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CRstEvent::raw_WillChangeRecordset(
         EventReasonEnum adReason,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CRstEvent::raw_RecordsetChangeComplete(
         EventReasonEnum adReason,
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CRstEvent::raw_WillMove(
         EventReasonEnum adReason,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CRstEvent::raw_MoveComplete(
         EventReasonEnum adReason,
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CRstEvent::raw_EndOfRecordset(
         VARIANT_BOOL *fMoreData,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CRstEvent::raw_FetchProgress(
         long Progress,
         long MaxProgress,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

      STDMETHODIMP CRstEvent::raw_FetchComplete(
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Recordset *pRecordset)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

//-----Implement QueryInterface, AddRef, and Release---------------------

   STDMETHODIMP CRstEvent::QueryInterface(REFIID riid, void ** ppv)
   {
      *ppv = NULL;
      if (riid == __uuidof(IUnknown) ||
              riid == __uuidof(RecordsetEventsVt)) *ppv = this;
      if (*ppv == NULL)
         return ResultFromScode(E_NOINTERFACE);
      AddRef();
      return NOERROR;
   }
   STDMETHODIMP_(ULONG) CRstEvent::AddRef(void) { return ++m_cRef; };
   STDMETHODIMP_(ULONG) CRstEvent::Release()
                              {
                              if (0 != --m_cRef) return m_cRef;
                              delete this;
                              return 0;
                              }

   STDMETHODIMP CConnEvent::QueryInterface(REFIID riid, void ** ppv)

   {
      *ppv = NULL;
      if (riid == __uuidof(IUnknown) ||
          riid == __uuidof(ConnectionEventsVt)) *ppv = this;
      if (*ppv == NULL)
         return ResultFromScode(E_NOINTERFACE);
      AddRef();
      return NOERROR;
   }
   STDMETHODIMP_(ULONG) CConnEvent::AddRef() { return ++m_cRef; };
   STDMETHODIMP_(ULONG) CConnEvent::Release()
                              {
                              if (0 != --m_cRef) return m_cRef;
                              delete this;
                              return 0;
                              }

//-----Write your main block of code-------------------------------------

int main(int argc, char* argv[])
{
   HRESULT hr;
   DWORD   dwConnEvt;
   DWORD   dwRstEvt;
   IConnectionPointContainer   *pCPC = NULL;
   IConnectionPoint         *pCP = NULL;
   IUnknown               *pUnk = NULL;
   CRstEvent               *pRstEvent = NULL;
   CConnEvent               *pConnEvent= NULL;
   int                     rc = 0;
   _RecordsetPtr            pRst;
   _ConnectionPtr            pConn;

   ::CoInitialize(NULL);

   hr = pConn.CreateInstance(__uuidof(Connection));
   if (FAILED(hr)) return rc;

   hr = pRst.CreateInstance(__uuidof(Recordset));
   if (FAILED(hr)) return rc;

   // Start using the Connection events

   hr = pConn->QueryInterface(__uuidof(IConnectionPointContainer),
      (void **)&pCPC);
   if (FAILED(hr)) return rc;
   hr = pCPC->FindConnectionPoint(__uuidof(ConnectionEvents), &pCP);
   pCPC->Release();
   if (FAILED(hr)) return rc;

   pConnEvent = new CConnEvent();
   hr = pConnEvent->QueryInterface(__uuidof(IUnknown), (void **) &pUnk);
   if (FAILED(hr)) return rc;
   hr = pCP->Advise(pUnk, &dwConnEvt);
   pCP->Release();
   if (FAILED(hr)) return rc;

   // Start using the Recordset events

   hr = pRst->QueryInterface(__uuidof(IConnectionPointContainer),
      (void **)&pCPC);
   if (FAILED(hr)) return rc;
   hr = pCPC->FindConnectionPoint(__uuidof(RecordsetEvents), &pCP);
   pCPC->Release();
   if (FAILED(hr)) return rc;

   pRstEvent = new CRstEvent();
   hr = pRstEvent->QueryInterface(__uuidof(IUnknown), (void **) &pUnk);
   if (FAILED(hr)) return rc;
   hr = pCP->Advise(pUnk, &dwRstEvt);
   pCP->Release();
   if (FAILED(hr)) return rc;

   // Do some work

   pConn->Open("dsn=Pubs;", "sa", "", adConnectUnspecified);
   pRst->Open("SELECT * FROM authors", (IDispatch *) pConn,
         adOpenStatic, adLockReadOnly, adCmdText);
   pRst->MoveFirst();
   while (pRst->EndOfFile == FALSE)
   {
      wprintf(L"Name = '%s'\n", (wchar_t*)
         ((_bstr_t) pRst->Fields->GetItem("au_lname")->Value));
      pRst->MoveNext();
   }

   pRst->Close();
   pConn->Close();

   // Stop using the Connection events

   hr = pConn->QueryInterface(__uuidof(IConnectionPointContainer),
      (void **) &pCPC);
   if (FAILED(hr)) return rc;
   hr = pCPC->FindConnectionPoint(__uuidof(ConnectionEvents), &pCP);
   pCPC->Release();
   if (FAILED(hr)) return rc;
   hr = pCP->Unadvise( dwConnEvt );
   pCP->Release();
   if (FAILED(hr)) return rc;

   // Stop using the Recordset events
   hr = pRst->QueryInterface(__uuidof(IConnectionPointContainer),
      (void **) &pCPC);
   if (FAILED(hr)) return rc;
   hr = pCPC->FindConnectionPoint(__uuidof(RecordsetEvents), &pCP);
   pCPC->Release();
   if (FAILED(hr)) return rc;
   hr = pCP->Unadvise( dwRstEvt );
   pCP->Release();
   if (FAILED(hr)) return rc;

   CoUninitialize();
   return 1;
}


AbsolutePage, PageCount, and PageSize Properties Example (VC++)

This example uses the AbsolutePage, PageCount, and PageSize properties to display names and hire dates from the Employee table, five records at a time.

// BeginAbsolutePageCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>
#include <ole2.h>
#include "conio.h"
#include "AbsolutepageX.h"

//Function Declarations.
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void AbsolutePageX(void);
void PrintProviderError(_ConnectionPtr pConnection);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    AbsolutePageX();

    ::CoUninitialize();
}

/////////////////////////////////////////////////////////////
//                                                         //
//      AbsolutePageX Function                             //
//                                                         //
/////////////////////////////////////////////////////////////

void AbsolutePageX(void)
{

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr   pRstEmployees   = NULL;

    //Define Other Variables
    //Interface Pointer declared.(VC++ Extensions)
    IADORecordBinding   *picRs = NULL;
    CEmployeeRs emprs;          //C++ class object
    HRESULT hr = S_OK;
    _bstr_t strMessage;

    //Open a recordset using a Client Cursor
    //For the Employee Table

    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
        "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        // Open a recordset.
        TESTHR(hr = pRstEmployees.CreateInstance(__uuidof(Recordset)));

        // Use client cursor to enable Absoluteposition property.
        pRstEmployees->CursorLocation = adUseClient;

        // You have to explicitly pass the default Cursor type
        // and LockType to the Recordset here
        TESTHR(hr =   pRstEmployees->Open("employee",
            strCnn,adOpenForwardOnly,adLockReadOnly,adCmdTable));

        //Open an IADORecordBinding interface pointer which we'll use for
        //Binding Recordset to a class
        TESTHR(hr = pRstEmployees->QueryInterface
            (__uuidof(IADORecordBinding), (LPVOID*)&picRs));

        //Bind the Recordset to a C++ Class here
        TESTHR(hr = picRs->BindToRecordset(&emprs));

        //Display Names and hire dates, five records at a time
        pRstEmployees->PageSize = 5;

        int intPageCount = pRstEmployees->PageCount;

        for(int intPage=1;intPage<=intPageCount;intPage++)
        {
            pRstEmployees->put_AbsolutePage((enum PositionEnum)intPage);
            strMessage = "";

            for(int intRecord=1;
                intRecord<=pRstEmployees->PageSize;intRecord++)
            {
                printf("\t%s %s %.10s\n",
                    emprs.lau_fnameStatus == adFldOK ?
                    emprs.m_szau_fname : "<NULL>",
                    emprs.lau_lnameStatus == adFldOK ?
                    emprs.m_szau_lname : "<NULL>",
                    emprs.lau_hiredateStatus == adFldOK ?
                    emprs.m_szau_hiredate : "<NULL>");

                pRstEmployees->MoveNext();

                if(pRstEmployees->EndOfFile)
                    break;
                }

            printf("\n Press any key to continue...");getch();

            //Clear the Screen for the next Display
            system("cls");
        }

        // Clean up objects before exit.
        pRstEmployees->Close();

        //Release the IADORecordset Interface here
        if (picRs)
            picRs->Release();
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _variant_t vtConnect = pRstEmployees->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                printf("Error:\n");
                printf("Code = %08lx\n", e.Error());
                printf("Message = %s\n", e.ErrorMessage());
                printf("Source = %s\n", (LPCSTR) e.Source());
                printf("Description = %s\n", (LPCSTR) e.Description());
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr  pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        printf("Error:\n");
        for(long iError = 0; iError < nCount; iError++)
        {
            pErr = pConnection->Errors->GetItem(iError);
            printf("\t Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}
//EndAbsolutePageCpp

AbsolutePageX.h:

// BeginAbsolutePageH
#include "icrsint.h"

//This Class extracts only fname,lastname and hire_date

class CEmployeeRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CEmployeeRs)

   //Column fname is the 2nd field in the table
   ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szau_fname,
         sizeof(m_szau_fname), lau_fnameStatus, FALSE)

   ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_szau_lname,
         sizeof(m_szau_lname), lau_lnameStatus, TRUE)

   ADO_VARIABLE_LENGTH_ENTRY2(8, adVarChar, m_szau_hiredate,
         sizeof(m_szau_hiredate), lau_hiredateStatus, TRUE)

END_ADO_BINDING()

public:
   CHAR   m_szau_lname[41];
   ULONG  lau_lnameStatus;
   CHAR   m_szau_fname[41];
   ULONG  lau_fnameStatus;
   CHAR   m_szau_hiredate[40];
   ULONG  lau_hiredateStatus;

};
// EndAbsolutePageH

See Also

AbsolutePage Property | PageCount Property | PageSize Property | Recordset Object


AbsolutePosition and CursorLocation Properties Example (VC++)

This example demonstrates how the AbsolutePosition property can track the progress of a loop that enumerates all the records of a Recordset. It uses the CursorLocation property to enable the AbsolutePosition property by setting the cursor to a client cursor.

// BeginAbsolutePositionCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include "conio.h"
#include "AbsolutePositionX.h"

//Function Declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void AbsolutePositionX(void);
void AbsolutePosition2X(void);
void PrintProviderError(_ConnectionPtr pConnection);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    AbsolutePositionX();

    //Clear the screen for the next display
    printf("Press any key to continue...");
    getch();
    system("cls");

    AbsolutePosition2X();

    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      AbsolutePositionX Function                       //
//                                                       //
///////////////////////////////////////////////////////////

void AbsolutePositionX(void)
{
    HRESULT hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr  pRstEmployees  = NULL;

    //Define Other Variables
    //Interface Pointer declared.(VC++ Extensions)
    IADORecordBinding *picRs = NULL;
    CEmployeeRs emprs;                  //C++ class object
    _bstr_t strMessage;
    char chKey;

    //Open a recordset using a Client Cursor
    //For the Employee Table

    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
        "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        //Open a recordset.
        TESTHR(pRstEmployees.CreateInstance(__uuidof(Recordset)));

        //Use client cursor to enable Absoluteposition property.
        pRstEmployees->CursorLocation = adUseClient;

        //You have to explicitly pass the default Cursor type
        //and LockType to the Recordset.
        TESTHR(  pRstEmployees->Open("employee",
        strCnn,adOpenForwardOnly,adLockReadOnly,adCmdTable));

        // Open an IADORecordBinding interface pointer which we'll use
        // for Binding Recordset to a class.
        TESTHR(pRstEmployees->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        //Bind the Recordset to a C++ Class here
        TESTHR(picRs->BindToRecordset(&emprs));

        strMessage= "";

        //Enumerate recordset
        do
        {
            //Display Current Record Information
            printf("Employee : %s \n record %ld of %d",
                emprs.lau_lnameStatus == adFldOK ?
                emprs.m_szau_lname : "<NULL>",
                pRstEmployees->AbsolutePosition,
                    pRstEmployees->RecordCount);

            printf("\nContinue?(y/n)  :");

            do
            {
                chKey = getch();
            }while(chKey != 'y' && chKey !='n');

            //Clear the Screen for the next output
            system("cls");

            if(chKey == 'n')
                break;

            strMessage = "";
            pRstEmployees->MoveNext();
        }while(!(pRstEmployees->EndOfFile));

        // Clean up objects before exit.
        pRstEmployees->Close();

        //Release the IADORecordset Interface here
        if (picRs)
            picRs->Release();
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _variant_t vtConnect = pRstEmployees->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                printf("Error:\n");
                printf("Code = %08lx\n", e.Error());
                printf("Message = %s\n", e.ErrorMessage());
                printf("Source = %s\n", (LPCSTR) e.Source());
                printf("Description = %s\n", (LPCSTR) e.Description());
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      AbsolutePosition2X Function                       //
//                                                       //
///////////////////////////////////////////////////////////

void AbsolutePosition2X(void)
{
    HRESULT hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr  pRstEmployees  = NULL;

    //Define Other Variables
    //Interface Pointer declared.(VC++ Extensions)
    IADORecordBinding *picRs = NULL;
    CEmployeeRs emprs;                  //C++ class object
    _bstr_t strMessage;
    char chKey;

    //Open a recordset using a Client Cursor
    //For the Employee Table

    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
        "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        //Open a recordset.
        TESTHR(pRstEmployees.CreateInstance(__uuidof(Recordset)));

        //Use client cursor to enable Absoluteposition property.
        pRstEmployees->CursorLocation = adUseClient;

        //You have to explicitly pass the default Cursor type
        //and LockType to the Recordset.
        TESTHR(pRstEmployees->Open("employee",
          strCnn,adOpenStatic,adLockReadOnly,adCmdTable));

        // Open an IADORecordBinding interface pointer which we'll use
        // for Binding Recordset to a class.
        TESTHR(pRstEmployees->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        //Bind the Recordset to a C++ Class here
        TESTHR(picRs->BindToRecordset(&emprs));

        long lGoToPos = 21;

        pRstEmployees->AbsolutePosition = (PositionEnum)lGoToPos;

        //Display Current Record Information
        printf("Employee : %s \n record %ld of %d",
            emprs.lau_lnameStatus == adFldOK ? emprs.m_szau_lname : "<NULL>", pRstEmployees->AbsolutePosition,
            pRstEmployees->RecordCount);

        printf("\nPress any key to continue:");
        getch();

        // Clean up objects before exit.
        pRstEmployees->Close();

        //Release the IADORecordset Interface here
        if (picRs)
            picRs->Release();
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _variant_t vtConnect = pRstEmployees->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                printf("Error:\n");
                printf("Code = %08lx\n", e.Error());
                printf("Message = %s\n", e.ErrorMessage());
                printf("Source = %s\n", (LPCSTR) e.Source());
                printf("Description = %s\n", (LPCSTR) e.Description());
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;
    long      nCount  = 0;
    long      i     = 0;

    if( (pConnection->Errors->Count) > 0)
    {
        nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s",
                pErr->Number,(LPCSTR) pErr->Description);
        }
    }
}

// EndAbsolutePositionCpp

AbsolutePositionX.h:

// BeginAbsolutePositionH
#include <ole2.h>
#include <stdio.h>
#include "icrsint.h"

//This Class extracts lastname.

class CEmployeeRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CEmployeeRs)

   //Column lname is the 4th field in the recordset

   ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_szau_lname,
         sizeof(m_szau_lname), lau_lnameStatus, TRUE)

END_ADO_BINDING()

public:
   CHAR   m_szau_lname[41];
   ULONG  lau_lnameStatus;

};
// EndAbsolutePositionH

See Also

AbsolutePosition Property | CursorLocation Property | Recordset Object


ActiveCommand Property Example (VC++)

This example demonstrates the ActiveCommand property.

A subroutine is given a Recordset object whose ActiveCommand property is used to display the command text and parameter that created the Recordset.

// BeginActiveCommandCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>
#include "ActiveCommandX.h"

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void ActiveCommandX(void);
void ActiveCommandXprint(_RecordsetPtr   pRst);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//        Main Function                                 //
//                                                      //
//////////////////////////////////////////////////////////

void main()
{
   if(FAILED(::CoInitialize(NULL)))
      return;

    ActiveCommandX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//            ActiveCommandX Function                   //
//                                                      //
//////////////////////////////////////////////////////////

void ActiveCommandX(void)
{

   HRESULT   hr = S_OK;

   // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
   _ConnectionPtr  pConnection      = NULL;
   _CommandPtr    pCmd      = NULL;
   _RecordsetPtr  pRstAuthors    = NULL;

   //Definitions of other variables
   _bstr_t strCnn("Provider=SQLOLEDB;Data Source=MyServer;Initial Catalog=pubs;User Id=sa;Password=;");
   _bstr_t      strPrompt;
   _bstr_t      strName;
   CHAR strcharName[50];

   try
   {
      // Open connection.
      TESTHR(pConnection.CreateInstance(__uuidof(Connection)));

      TESTHR(pRstAuthors.CreateInstance(__uuidof(Recordset)));

      TESTHR(pCmd.CreateInstance(__uuidof(Command)));

      printf ("ActiveCommandX Example\n\n");
      strPrompt = "Enter an author's name (e.g., Ringer): ";
      printf(strPrompt);
      gets(strcharName);
      char *tempStr = strtok(strcharName, " \t");
      strName = tempStr;

      pCmd->CommandText = "SELECT * FROM authors WHERE au_lname = ?";
      pCmd->Parameters->Append(pCmd->CreateParameter("LastName", adChar, adParamInput, 20, strName));

      pConnection->Open (strCnn, "", "", adConnectUnspecified);

      pCmd->PutActiveConnection(_variant_t((IDispatch*)pConnection));

      pRstAuthors = pCmd->Execute(NULL,NULL,adCmdText);

      ActiveCommandXprint(pRstAuthors);

      // Clean up objects before exit.
      pRstAuthors->Close();
      pConnection->Close();

   }   // End Try statement.

   catch(_com_error &e)
   {
      // Notify the user of errors if any.
      // Pass a connection pointer accessed from the Recordset.
      PrintProviderError(pConnection);
      PrintComError(e);
   }

}

//////////////////////////////////////////////////////////
//                                                      //
//              ActiveCommandXprint Function            //
//                                                      //
//////////////////////////////////////////////////////////

void ActiveCommandXprint(_RecordsetPtr   pRst = NULL)
{
   // Varible Declaraion & initialization
   IADORecordBinding   *picRs  = NULL;  //Interface Pointer declared.
   CAuthorsRs autrs;           //C++ class object
   HRESULT hr;
   _bstr_t strName;

   //Open an IADORecordBinding interface pointer which
   //we'll use for Binding Recordset to a class
   TESTHR(pRst->QueryInterface(__uuidof(IADORecordBinding),(LPVOID*)&picRs));

   //Bind the Recordset to a C++ Class here
   TESTHR(picRs->BindToRecordset(&autrs));

   strName = ((_CommandPtr)pRst->GetActiveCommand())->GetParameters()->GetItem("LastName")->Value;
   printf("Command text = '%s'\n", (LPCSTR)((_CommandPtr)pRst->GetActiveCommand())->CommandText);
   printf("Parameter = '%s'\n", (LPCSTR)strName);

   if (pRst->BOF)
      printf("Name = '%s'not found.", (LPCSTR)strName);
   else
   {
      printf ("Name = '%s  %s' author ID = '%s'",
         autrs.lau_fnameStatus == adFldOK ? autrs.m_au_fname : "<NULL>",
         autrs.lau_lnameStatus == adFldOK ? autrs.m_au_lname : "<NULL>",
         autrs.lau_idStatus == adFldOK ? autrs.m_au_id : "<NULL>");
   }

   //Release IADORecordset Interface
   if (picRs)
   picRs->Release();

}

//////////////////////////////////////////////////////////
//                                                      //
//        PrintProviderError Function                   //
//                                                      //
//////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
   // Print Provider Errors from Connection object.
   // pErr is a record object in the Connection's Error collection.
   ErrorPtr    pErr  = NULL;
   long      nCount  = 0;
   long      i     = 0;

   if( (pConnection->Errors->Count) > 0)
   {
      nCount = pConnection->Errors->Count;
      // Collection ranges from 0 to nCount -1.
      for(i = 0; i < nCount; i++)
      {
         pErr = pConnection->Errors->GetItem(i);
         printf("\t Error number: %x\t%s\n", pErr->Number, (LPCSTR)pErr->Description);
      }
   }
}

//////////////////////////////////////////////////////////
//                                                      //
//       PrintComError Function                         //
//                                                      //
//////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

   // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);

}
// EndActiveCommandCpp

ActiveCommandX.h

// BeginActiveConnectionH
#include "icrsint.h"

//This Class extracts  fname,lastname

class CEmployeeRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CEmployeeRs)

   //Column au_id is the 1st field in the recordset
   ADO_VARIABLE_LENGTH_ENTRY2(1, adVarChar, m_szau_id,
         sizeof(m_szau_id), lau_idStatus, TRUE)

    ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szau_lname,
         sizeof(m_szau_lname), lau_lnameStatus, TRUE)

   ADO_VARIABLE_LENGTH_ENTRY2(3, adVarChar, m_szau_fname,
         sizeof(m_szau_fname), lau_fnameStatus, TRUE)

END_ADO_BINDING()

public:

   CHAR m_szau_id[20];
   ULONG lau_idStatus;

   CHAR m_szau_fname[40];
   ULONG lau_fnameStatus;

   CHAR   m_szau_lname[40];
   ULONG  lau_lnameStatus;

};
// EndActiveConnectionH

See Also

ActiveCommand Property | Recordset Object


ActiveConnection, CommandText, CommandTimeout, CommandType, Size, and Direction Properties Example (VC++)

This example uses the ActiveConnection, CommandText, CommandTimeout, CommandType, Size, and Direction properties to execute a stored procedure.

// BeginActiveConnectionCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#define TESTHR(x) if FAILED(x) _com_issue_error(hr)

#include <stdio.h>
#include <ole2.h>
#include "conio.h"
#include "ActiveConnectionX.h"

//Function declaration
void ActiveConnectionX(VOID);
void PrintProviderError(_ConnectionPtr pConnection);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////
void main()
{
   if(FAILED(::CoInitialize(NULL)))
      return;

   ActiveConnectionX();

   //Wait here for user to see the output..
   printf("\n\nPress any key to continue..");
   getch();

   ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      ActiveConnectionX Function                       //
//                                                       //
///////////////////////////////////////////////////////////
VOID ActiveConnectionX(VOID)
{
   HRESULT hr = S_OK;

   // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
   _ConnectionPtr  pConnection      = NULL;
   _CommandPtr     pCmdByRoyalty  = NULL;
   _RecordsetPtr  pRstByRoyalty  = NULL;
   _RecordsetPtr  pRstAuthors   = NULL;
   _ParameterPtr    pPrmByRoyalty  = NULL;

   //Define Other variables
   IADORecordBinding   *picRs = NULL;  //Interface Pointer declared.(VC++ Extensions)  TCS(SPA)
   CEmployeeRs emprs;          //C++ class object  TCS(SPA)
   _bstr_t strAuthorId;
   int intRoyalty;
   VARIANT vtroyal ;

   _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
         "Initial Catalog=pubs;User Id=sa;Password=;");

try
{
   //Define a command object for a stored procedure.

   TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
   hr = pConnection->Open(strCnn,"","",adConnectUnspecified);

   TESTHR(pCmdByRoyalty.CreateInstance(__uuidof(Command)));

   pCmdByRoyalty->ActiveConnection = pConnection;
   pCmdByRoyalty->CommandText = "byRoyalty";
   pCmdByRoyalty->CommandType = adCmdStoredProc;
   pCmdByRoyalty->CommandTimeout = 15;

   //Define stored procedure's input parameter.
   printf("Enter Royalty :  ");
   scanf("%d",&intRoyalty);

   //Assign Integer value
   vtroyal.vt = VT_I2;
   vtroyal.iVal  = intRoyalty;

   TESTHR(pPrmByRoyalty.CreateInstance(__uuidof(Parameter)));
   pPrmByRoyalty->Type = adInteger;
   pPrmByRoyalty->Size = 3;
   pPrmByRoyalty->Direction = adParamInput;
   pPrmByRoyalty->Value = vtroyal;
   pCmdByRoyalty->Parameters->Append(pPrmByRoyalty);

   //Create a recordset by executing a command.
   pRstByRoyalty = pCmdByRoyalty->Execute(NULL,NULL,adCmdStoredProc);

   //Open the authors table to get author names for display.

   TESTHR(pRstAuthors.CreateInstance(__uuidof(Recordset)));
   hr = pRstAuthors->Open("authors",strCnn,adOpenForwardOnly,adLockReadOnly,adCmdTable);

   //Open an IADORecordBinding interface pointer which we'll use for Binding Recordset to a class
   TESTHR(pRstAuthors->QueryInterface(__uuidof(IADORecordBinding),(LPVOID*)&picRs));

   //Bind the Recordset to a C++ Class here
   TESTHR(picRs->BindToRecordset(&emprs));

   //Print current data in the recordset ,adding author names from author table.
   printf("Authors With  %d  Percent Royalty",intRoyalty);

   while(!(pRstByRoyalty->EndOfFile))
   {
      strAuthorId = pRstByRoyalty->Fields->Item["au_id"]->Value;
      pRstAuthors->Filter = "au_id = '"+strAuthorId+"'";

      printf("\n\t%s, %s %s",emprs.lau_idStatus == adFldOK ? emprs.m_szau_id : "<NULL>",\
      emprs.lau_fnameStatus == adFldOK ? emprs.m_szau_fname : "<NULL>",\
            emprs.lau_lnameStatus == adFldOK ? emprs.m_szau_lname : "<NULL>");

      pRstByRoyalty->MoveNext();
   }

   // Clean up objects before exit
   pRstByRoyalty->Close();
   pRstAuthors->Close();

   //Release the IADORecordset Interface here
   if (picRs)
       picRs->Release();
       pConnection->Close();
}

catch(_com_error &e)
{
   // Notify the user of errors if any.
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    PrintProviderError(pConnection);
     printf("Source : %s \n Description : %s \n",(LPCSTR)bstrSource,(LPCSTR)bstrDescription);
}

}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

VOID PrintProviderError(_ConnectionPtr pConnection)
{
   // Print Provider Errors from Connection object.
   // pErr is a record object in the Connection's Error collection.
   ErrorPtr    pErr  = NULL;
   long      nCount  = 0;
   long      i     = 0;

   if( (pConnection->Errors->Count) > 0)
   {
      nCount = pConnection->Errors->Count;
      // Collection ranges from 0 to nCount -1.
      for(i = 0; i < nCount; i++)
      {
         pErr = pConnection->Errors->GetItem(i);
         printf("Error number: %x\t%s", pErr->Number,(LPCSTR)pErr->Description);
      }
   }
}

// EndActiveConnectionCpp

ActiveConnectionX.h:

// BeginActiveConnectionH
#include "icrsint.h"

//This Class extracts  fname,lastname

class CEmployeeRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CEmployeeRs)

   //Column au_id is the 1st field in the recordset
   ADO_VARIABLE_LENGTH_ENTRY2(1, adVarChar, m_szau_id,
         sizeof(m_szau_id), lau_idStatus, TRUE)

    ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szau_lname,
         sizeof(m_szau_lname), lau_lnameStatus, TRUE)

   ADO_VARIABLE_LENGTH_ENTRY2(3, adVarChar, m_szau_fname,
         sizeof(m_szau_fname), lau_fnameStatus, TRUE)

END_ADO_BINDING()

public:

   CHAR m_szau_id[20];
   ULONG lau_idStatus;

   CHAR m_szau_fname[40];
   ULONG lau_fnameStatus;

   CHAR   m_szau_lname[40];
   ULONG  lau_lnameStatus;

};
// EndActiveConnectionH

See Also

ActiveConnection Property | CommandText Property | CommandTimeout Property | CommandType Property | Direction Property | Size Property


ActualSize and DefinedSize Properties Example (VC++)

This example uses the ActualSize and DefinedSize properties to display the defined size and actual size of a field.

// BeginActualSizeCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include "conio.h"

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void ActualSizeX(VOID);
void PrintProviderError(_ConnectionPtr pConnection);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;
    ActualSizeX();
    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      ActualSizeX Function                             //
//                                                       //
///////////////////////////////////////////////////////////
VOID   ActualSizeX(VOID)
{
    HRESULT hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr pRstStores = NULL;

    //Define Other variables
    _bstr_t strMessage;
    _bstr_t strCnn("Provider=sqloledb;Data Source=srv;"
        "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        //Open a recordset for the stores table.
        TESTHR(pRstStores.CreateInstance(__uuidof(Recordset)));

        //You have to explicitly pass the Cursor type and LockType
        //to the Recordset here.
        hr = pRstStores->Open("stores",
                strCnn,adOpenForwardOnly,adLockReadOnly,adCmdTable);

        //Loop through the recordset displaying the contents
        //of the stor_name field, the field's defined size,
        //and its actual size.

        pRstStores->MoveFirst();

        while(!(pRstStores->EndOfFile))
        {
            strMessage = "Store Name: ";
            strMessage += (_bstr_t)pRstStores->Fields->
                             Item["stor_name"]->Value + "\n";
            strMessage += "Defined Size: ";
            strMessage += (_bstr_t)pRstStores->Fields->
                             Item["stor_name"]->DefinedSize + "\n";
            strMessage += "Actual Size: ";
            strMessage += (_bstr_t) pRstStores->Fields->
                             Item["stor_name"]->ActualSize + "\n";

            printf("%s\n",(LPCSTR)strMessage);
            printf("Press any key to continue...");
            getch();
            //Clear the screen for the next display
            system("cls");
            pRstStores->MoveNext();
        }

    // Clean up objects before exit.
    pRstStores->Close();
    }

    catch(_com_error &e)
    {
        _variant_t vtConnect = pRstStores->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
        case VT_BSTR:
                printf("Error:\n");
                printf("Code = %08lx\n", e.Error());
                printf("Message = %s\n", e.ErrorMessage());
                printf("Source = %s\n", (LPCSTR) e.Source());
                printf("Description = %s\n", (LPCSTR) e.Description());
                break;
        case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
        default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

VOID PrintProviderError(_ConnectionPtr pConnection)
{
   // Print Provider Errors from Connection object.
   // pErr is a record object in the Connection's Error collection.
   ErrorPtr    pErr  = NULL;
   long      nCount   = 0;
   long      i     = 0;

   if( (pConnection->Errors->Count) > 0)
   {
      nCount = pConnection->Errors->Count;
      // Collection ranges from 0 to nCount -1.
      for(i = 0; i < nCount; i++)
      {
         pErr = pConnection->Errors->GetItem(i);
         printf("\t Error number: %x\t%s", pErr->Number,(LPCSTR ) pErr->Description);
      }
   }
}
// EndActualSizeCpp

See Also

ActualSize Property | DefinedSize Property


AddNew Method Example (VC++)

This example uses the AddNew method to create a new record with the specified name.

// BeginAddNewCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include "conio.h"
#include "AddNewX.h"

//Function declaration
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void AddNewX(VOID);
void PrintProviderError(_ConnectionPtr pConnection);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////
void main()
{
   HRESULT  hr = S_OK;

   if(FAILED(::CoInitialize(NULL)))
      return;

   if (SUCCEEDED(hr))
   {
       AddNewX();

      //Wait here for the user to see the output
      printf("Press any key to continue...");
      getch();

       ::CoUninitialize();
   }
}

///////////////////////////////////////////////////////////
//                                                       //
//      AddNewX Function                                 //
//                                                       //
///////////////////////////////////////////////////////////
VOID  AddNewX(VOID)
{

   // Define ADO object pointers.
    // Initialize pointers on define.
   _RecordsetPtr pRstEmployees = NULL;
   _ConnectionPtr pConnection  = NULL;

   //Define Other variables
   IADORecordBinding   *picRs = NULL;  //Interface Pointer declared.(VC++ Extensions)
   CEmployeeRs emprs;    //C++ class object

   HRESULT hr = S_OK;

                           //Replace Data Source value with your server name.
   _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;Initial Catalog=pubs;User Id=sa;Password=;");
   _bstr_t strId;
    _bstr_t strMessage;

   try
   {
      //Open a connection
      TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
      pConnection->Open(strCnn,"","",adConnectUnspecified);

      //Open employee table
      TESTHR(pRstEmployees.CreateInstance(__uuidof(Recordset)));

      //You have to explicitly pass the Cursor type and LockType to the Recordset here
      pRstEmployees->Open("employee",_variant_t((IDispatch *) pConnection, true),adOpenKeyset,adLockOptimistic,adCmdTable);

      //Open an IADORecordBinding interface pointer which we'll use for Binding Recordset to a class
      TESTHR(pRstEmployees->QueryInterface(__uuidof(IADORecordBinding),(LPVOID*)&picRs));

       //Bind the Recordset to a C++ Class here
      TESTHR(picRs->BindToRecordset(&emprs));

      // Get data from the user.The employee id must be formatted as
      // first,middle and last initial,five numbers,then M or F to
      // signify the gender.For example,the employee id for
      // Bill A. Sorensen would be "BAS55555M".

      printf("Enter Employee Id: ");scanf("%s",emprs.m_sz_empid);
      strId = emprs.m_sz_empid;
      printf("Enter First Name: ");scanf("%s",emprs.m_sz_fname);
      printf("Enter Last Name:");scanf("%s",emprs.m_sz_lname);

      //Proceed if the user actually entered some thing
      //for the id, the first and the last name.

      if(strcmp(emprs.m_sz_empid,"")  && strcmp(emprs.m_sz_fname,"") && strcmp(emprs.m_sz_lname,""))
      {
         //This adds a new record to the table
             //if (FAILED(hr = picRs->AddNew(&emprs)))
            //_com_issue_error(hr);
            TESTHR(picRs->AddNew(&emprs));

            //Show the newly added data
             printf("New Record: %s  %s  %s \n",\
               emprs.lemp_empidStatus == adFldOK ? emprs.m_sz_empid : "<NULL>",\
               emprs.lemp_fnameStatus == adFldOK ? emprs.m_sz_fname : "<NULL>",\
               emprs.lemp_lnameStatus == adFldOK ? emprs.m_sz_lname : "<NULL>");
      }
      else
         printf("Please enter an employee id, first name and last name.\n");

      //Delete the new record because this is a demonstration.
      pConnection->Execute("DELETE FROM EMPLOYEE WHERE emp_id = '"+strId+"'",NULL,adCmdText);

      //Release the IADORecordset Interface here
      if (picRs)
      picRs->Release();

      // Clean up objects before exit.
      pRstEmployees->Close();
      pConnection->Close();

   }

   catch(_com_error &e)
   {
      // Notify the user of errors if any.
      _variant_t vtConnect = pRstEmployees->GetActiveConnection();

      // GetActiveConnection returns connect string if connection
      // is not open, else returns Connection object.
      switch(vtConnect.vt)
      {
      case VT_BSTR:
            printf("Error:\n");
            printf("Code = %08lx\n", e.Error());
            printf("Message = %s\n", e.ErrorMessage());
            printf("Source = %s\n", (LPCSTR) e.Source());
            printf("Description = %s\n", (LPCSTR) e.Description());
            break;
      case VT_DISPATCH:
            PrintProviderError(vtConnect);
            break;
      default:
            printf("Errors occured.");
            break;
      }

   }

}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

VOID PrintProviderError(_ConnectionPtr pConnection)
{
   // Print Provider Errors from Connection object.
   // pErr is a record object in the Connection's Error collection.
   ErrorPtr  pErr = NULL;
   long      nCount = 0;
   long      i = 0;

   if( (pConnection->Errors->Count) > 0)
   {
      nCount = pConnection->Errors->Count;
      // Collection ranges from 0 to nCount -1.
      for(i = 0; i < nCount; i++)
      {
         pErr = pConnection->Errors->GetItem(i);
         printf("\n\t Error number: %x\t%s", pErr->Number, (LPCSTR)pErr->Description);
      }
   }
}
// EndAddNewCpp

AddNewX.h:

// BeginAddNewH
#include "icrsint.h"

//This Class extracts empid, fname and lastname

class CEmployeeRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CEmployeeRs)

   //Column empid is the 1st field in the recordset

   ADO_VARIABLE_LENGTH_ENTRY2(1, adVarChar, m_sz_empid,
         sizeof(m_sz_empid), lemp_empidStatus, TRUE)

   ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_sz_fname,
         sizeof(m_sz_fname), lemp_fnameStatus, TRUE)

   ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_sz_lname,
         sizeof(m_sz_lname), lemp_lnameStatus, TRUE)

END_ADO_BINDING()

public:

   CHAR  m_sz_empid[10];
   ULONG lemp_empidStatus;
   CHAR   m_sz_fname[40];
   ULONG  lemp_fnameStatus;
   CHAR   m_sz_lname[41];
   ULONG  lemp_lnameStatus;

};
// EndAddNewH

See Also

AddNew Method | Recordset Object


Append and CreateParameter Methods Example (VC++)

This example uses the Append and CreateParameter methods to execute a stored procedure with an input parameter.

// BeginAppendCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include "conio.h"
#include "AppendX.h"

//Function declaration
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void AppendX(VOID);
void PrintProviderError(_ConnectionPtr pConnection);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////
void main()
{
   HRESULT  hr = S_OK;

   if(FAILED(::CoInitialize(NULL)))
      return;

   AppendX();

   //Wait here for the user to see the output.
   printf("\n\nPress any key to continue...");
   getch();
   ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      AppendX Function                                 //
//                                                       //
///////////////////////////////////////////////////////////
VOID  AppendX(VOID)
{

     HRESULT hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr pRstByRoyalty = NULL;
    _RecordsetPtr pRstAuthors = NULL;
    _CommandPtr   pcmdByRoyalty = NULL;
    _ParameterPtr pprmByRoyalty = NULL;
    _ConnectionPtr pConnection = NULL;

     //Define Other variables
     IADORecordBinding   *picRs = NULL;  //Interface Pointer declared.(VC++ Extensions)
     CEmployeeRs emprs;          //C++ class object

    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
         "Initial Catalog=pubs;User Id=sa;Password=;");

    _bstr_t strMessage, strAuthorID;

     int intRoyalty;
     VARIANT vtRoyalty;

   try
   {
      //Open a Connection.
      TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
      hr = pConnection->Open(strCnn,"","",adConnectUnspecified);
      pConnection->CursorLocation = adUseClient;

      //Open Command Object with one Parameter
      TESTHR(pcmdByRoyalty.CreateInstance(__uuidof(Command)));
      pcmdByRoyalty->CommandText = "byroyalty";
      pcmdByRoyalty->CommandType = adCmdStoredProc;

      //Get parameter value and append parameter
      printf("Enter Royalty: ");
      scanf("%d",&intRoyalty);

      //Define Integer/variant.
      vtRoyalty.vt = VT_I2;
      vtRoyalty.iVal = intRoyalty;
      pprmByRoyalty = pcmdByRoyalty->CreateParameter("percentage",adInteger,adParamInput,sizeof(int),vtRoyalty);
      pcmdByRoyalty->Parameters->Append(pprmByRoyalty);

      pprmByRoyalty->Value  = vtRoyalty;

      //Create Recordset by executing the command
      pcmdByRoyalty->ActiveConnection = pConnection;
      pRstByRoyalty = pcmdByRoyalty->Execute(NULL,NULL,adCmdStoredProc);

      //Open the authors table to get author names for display
      TESTHR(pRstAuthors.CreateInstance(__uuidof(Recordset)));

      //You have to explicitly pass the default Cursor type and LockType to the Recordset here
      hr = pRstAuthors->Open("authors",_variant_t((IDispatch*)pConnection,true),adOpenForwardOnly,adLockReadOnly,adCmdTable);

      //Open an IADORecordBinding interface pointer which we'll use for Binding Recordset to a class
      TESTHR(pRstAuthors->QueryInterface(__uuidof(IADORecordBinding),(LPVOID*)&picRs));

       //Bind the Recordset to a C++ Class here
      TESTHR(picRs->BindToRecordset(&emprs));

      //Print current data in the recordset, adding
      //author names from author table.
      printf("Authors with %d percent royalty ",intRoyalty);

      while(!(pRstByRoyalty->EndOfFile))
      {
         strAuthorID = pRstByRoyalty->Fields->Item["au_id"]->Value;
         pRstAuthors->Filter = "au_id = '"+strAuthorID+"'";

         printf("\n"  "%s, %s  %s",emprs.lau_idStatus == adFldOK ? emprs.m_szau_id : "<NULL>",\
               emprs.lau_fnameStatus == adFldOK ? emprs.m_szau_fname : "<NULL>",\
               emprs.lau_lnameStatus == adFldOK ? emprs.m_szau_lname : "<NULL>");

         pRstByRoyalty->MoveNext();

      }

      //Release the IADORecordset Interface here
      if (picRs)
      picRs->Release();

      pRstByRoyalty->Close();
      pRstAuthors->Close();
      pConnection->Close();
   }

   catch(_com_error &e)
   {
      _bstr_t bstrSource(e.Source());
      _bstr_t bstrDescription(e.Description());

      PrintProviderError(pConnection);

      printf("\n Source : %s \n Description : %s \n",(LPCSTR)bstrSource,(LPCSTR)bstrDescription);
   }

}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

VOID PrintProviderError(_ConnectionPtr pConnection)
{
   // Print Provider Errors from Connection object.
   // pErr is a record object in the Connection's Error collection.
   ErrorPtr    pErr  = NULL;
   long      nCount  = 0;
   long      i     = 0;

   if( (pConnection->Errors->Count) > 0)
   {
      nCount = pConnection->Errors->Count;
      // Collection ranges from 0 to nCount -1.
      for(i = 0; i < nCount; i++)
      {
         pErr = pConnection->Errors->GetItem(i);
         printf("Error number: %x\n Error Description: %s"\n, pErr->Number,(LPCSTR) pErr->Description);
      }
   }
}
// EndAppendCpp

AppendX.h:

// BeginAppendH
#include "icrsint.h"

//This Class extracts only author id,fname,lastname

class CEmployeeRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CEmployeeRs)

   //Column au_id is the 1st field in the recordset

   ADO_VARIABLE_LENGTH_ENTRY2(1, adVarChar, m_szau_id,
         sizeof(m_szau_id), lau_idStatus, TRUE)

   ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szau_lname,
         sizeof(m_szau_lname), lau_lnameStatus, TRUE)

   ADO_VARIABLE_LENGTH_ENTRY2(3, adVarChar, m_szau_fname,
         sizeof(m_szau_fname), lau_fnameStatus, TRUE)

END_ADO_BINDING()

public:

   CHAR m_szau_id[20];
   ULONG lau_idStatus;

   CHAR m_szau_fname[40];
   ULONG lau_fnameStatus;

   CHAR   m_szau_lname[40];
   ULONG  lau_lnameStatus;

};
// EndAppendH

See Also

Append Method | CreateParameter Method


AppendChunk and GetChunk Methods Example (VC++)

This example uses the AppendChunk and GetChunk methods to fill an image field with data from another record.

// BeginAppendChunkCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#define ChunkSize   100

#include <ole2.h>
#include <stdio.h>
#include "conio.h"
#include "malloc.h"
#include "AppendChunkX.h"

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void AppendChunkX(VOID);
void PrintProviderError(_ConnectionPtr pConnection);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////
void main()
{
   HRESULT  hr = S_OK;

   if(FAILED(::CoInitialize(NULL)))
      return;

   AppendChunkX();

   //Wait here for the user to see the output
   printf("\n\nPress any key to continue..");
   getch();
   ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      AppendChunkX Function                            //
//                                                       //
///////////////////////////////////////////////////////////
VOID  AppendChunkX(VOID)
{
   // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr pRstPubInfo = NULL;
    _ConnectionPtr pConnection = NULL;

     //Define other variables
     IADORecordBinding   *picRs = NULL;  //Interface Pointer declared.(VC++ Extensions)
     CPubInfoRs pubrs;          //C++ class object

     HRESULT hr = S_OK;
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
         "Initial Catalog=pubs;User Id=sa;Password=;");

    _bstr_t strMessage,strPubID,strPRInfo;
    _variant_t varChunk;
    long lngOffSet,lngLogoSize;
     char pubId[50];
    lngOffSet = 0;

    UCHAR chData;
    CHAR ch;
    SAFEARRAY FAR *psa;
    SAFEARRAYBOUND rgsabound[1];
    rgsabound[0].lLbound = 0;
    rgsabound[0].cElements = ChunkSize;

    try
    {
        //Open a Connection.
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
        hr = pConnection->Open(strCnn,"","",adConnectUnspecified);

        TESTHR(hr= pRstPubInfo.CreateInstance(__uuidof(Recordset)));

        pRstPubInfo->CursorType = adOpenKeyset;
        pRstPubInfo->LockType = adLockOptimistic;

        hr = pRstPubInfo->Open("pub_info",
            _variant_t((IDispatch*)pConnection,true),
            adOpenKeyset,adLockOptimistic,adCmdTable);

        //Open an IADORecordBinding interface pointer which we'll use
        //for Binding Recordset to a class
        TESTHR(pRstPubInfo->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs);

        //Bind the Recordset to a C++ Class here
        TESTHR(picRs->BindToRecordset(&pubrs));

        //Display the available logos here
        strMessage = "Available logos are: " + (_bstr_t)"\n\n";
        printf(strMessage);
        int Counter = 0;
        while(!(pRstPubInfo->EndOfFile))
        {
            printf("\n%s",pubrs.m_sz_pubid);
            printf("\n%s",strtok(pubrs.m_sz_prinfo,","));

            //Display 5 records at a time and wait for user to continue..
            if (++Counter >= 5)
            {
                Counter = 0;
                printf("\nPress any key to continue...");
                getch();
            }
            pRstPubInfo->MoveNext();
        }

        //Prompt For a Logo to Copy
        printf("\nEnter the ID of a logo to copy: ");
        scanf("%s",pubId);
        strPubID = pubId;

        //Copy the logo to a variable in chunks

        pRstPubInfo->Filter = "pub_id = '"  + strPubID + "'";
        lngLogoSize = pRstPubInfo->Fields->Item["logo"]->ActualSize;

        //Create a safe array to store the array of BYTES
        rgsabound[0].cElements = lngLogoSize;
        psa = SafeArrayCreate(VT_UI1,1,rgsabound);

        long index1 = 0;
        while(lngOffSet < lngLogoSize)
        {
            varChunk = pRstPubInfo->Fields->
                        Item["logo"]->GetChunk(ChunkSize);

            //Copy the data only upto the Actual Size of Field.
            for(long index=0;index<=(ChunkSize-1);index++)
            {
                hr= SafeArrayGetElement(varChunk.parray,&index,&chData);
                if(SUCCEEDED(hr))
                {
                    //Take BYTE by BYTE and advance Memory Location
                    hr = SafeArrayPutElement(psa,&index1,&chData);
                    index1++;
                }
                else
                    break;
            }
            lngOffSet = lngOffSet + ChunkSize;
        }
        lngOffSet = 0;

        printf("Enter a new Pub Id: ");
        scanf("%s",pubrs.m_sz_pubid);
        strPubID = pubrs.m_sz_pubid;
        printf("Enter descriptive text: " );
        scanf("%c",&ch);
        gets(pubrs.m_sz_prinfo);

        pRstPubInfo->AddNew();
        pRstPubInfo->Fields->GetItem("pub_id")->PutValue(strPubID);
        pRstPubInfo->Fields->GetItem("pr_info")->
            PutValue(pubrs.m_sz_prinfo);

        //Assign the Safe array  to a variant.
        varChunk.vt = VT_ARRAY|VT_UI1;
        varChunk.parray = psa;
        hr = pRstPubInfo->Fields->GetItem("logo")->
                AppendChunk(varChunk);

        //Update the table
        pRstPubInfo->Update();

        lngLogoSize = pRstPubInfo->Fields->Item["logo"]->ActualSize;

        //Show the newly added record.
        printf("New Record : %s\n Description : %s\n Logo Size : %s",
            pubrs.m_sz_pubid,
            pubrs.m_sz_prinfo,(LPCSTR)(_bstr_t)pRstPubInfo->Fields->
            Item["logo"]->ActualSize);

        //Delete new record because this is demonstration.
        pConnection->Execute("DELETE FROM PUB_INFO WHERE pub_id = '"
                        + strPubID +"'",NULL,adCmdText);

        pRstPubInfo->Close();
        pConnection->Close();
    }

    catch(_com_error &e)
   {
       // Notify the user of errors if any.
      _bstr_t bstrSource(e.Source());
      _bstr_t bstrDescription(e.Description());

      PrintProviderError(pConnection);
      printf("Source : %s \n Description : %s\n",(LPCSTR)bstrSource,(LPCSTR)bstrDescription);

   }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

VOID PrintProviderError(_ConnectionPtr pConnection)
{
   // Print Provider Errors from Connection object.
   // pErr is a record object in the Connection's Error collection.
   ErrorPtr    pErr  = NULL;
   long      nCount   = 0;
   long      i     = 0;

   if( (pConnection->Errors->Count) > 0)
   {
      nCount = pConnection->Errors->Count;
      // Collection ranges from 0 to nCount -1.
      for(i = 0; i < nCount; i++)
      {
         pErr = pConnection->Errors->GetItem(i);
         printf("\t Error number: %x\t%s", pErr->Number,(LPCSTR) pErr->Description);
      }
   }
}

// EndAppendChunkCpp

AppendChunkX.h:

// BeginAppendChunkH
#include "icrsint.h"

//This Class extracts pubid,prinfo.

class CPubInfoRs : public CADORecordBinding
{
   BEGIN_ADO_BINDING(CPubInfoRs)

      ADO_VARIABLE_LENGTH_ENTRY2(1, adVarChar, m_sz_pubid,
         sizeof(m_sz_pubid), l_pubid, TRUE)

      ADO_VARIABLE_LENGTH_ENTRY2(3, adVarChar, m_sz_prinfo,
         sizeof(m_sz_prinfo), l_prinfo, TRUE)

   END_ADO_BINDING()

public:
   CHAR   m_sz_pubid[10];
   ULONG  l_pubid;
   CHAR   m_sz_prinfo[200];
   ULONG l_prinfo;
};
// EndAppendChunkH

See Also

AppendChunk Method | Field Object | GetChunk Method


Attributes and Name Properties Example (VC++)

This example displays the value of the Attributes property for Connection, Field, and Property objects. It uses the Name property to display the name of each Field and Property object.

// BeginAttributesCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include<conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void AttributesX();
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////

void main()
{
   if(FAILED(::CoInitialize(NULL)))
      return;

   AttributesX();

   //Wait here for user to see the output..
   printf("\nPress any key to continue...");
   getch();

   ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      AttributesX Function                             //
//                                                       //
///////////////////////////////////////////////////////////

void AttributesX()
{
   // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace
    _RecordsetPtr   pRstEmployee  = NULL;
    _ConnectionPtr pConnection = NULL;
    FieldsPtr  fldLoop = NULL;
    PropertiesPtr proLoop = NULL;

    //Define Other Variables
   HRESULT  hr = S_OK;
     _variant_t Index;
   Index.vt = VT_I2;
   int j=0;
    //Open a recordset using a Client Cursor
   //For the Employee Table
      _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
   "Initial Catalog=pubs;User Id=sa;Password=;");

     try
   {
      // open connection and record set
      TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
      pConnection->Open(strCnn,"","",adConnectUnspecified);

      TESTHR(pRstEmployee.CreateInstance(__uuidof(Recordset)));
      pRstEmployee->Open("Employee", _variant_t((IDispatch *)pConnection,true), adOpenForwardOnly,
         adLockReadOnly, adCmdTable);

      // Display the attributes of Connection.
      printf("Connection attributes: %d \n", pConnection->Attributes);

      // Display the attribute of the employee table's
      //fields
      printf("\nFields attributes:\n");
      fldLoop = pRstEmployee->GetFields();

      for (int i = 0; i < (int)fldLoop->GetCount(); i++)
      {
         Index.iVal=i;
         printf ("   %s = %d \n",(LPSTR)fldLoop->GetItem(Index)->GetName(),
            (int)fldLoop->GetItem(Index)->GetAttributes());
      }

      // Display Fields of the Employee table which are NULLBALE
      printf("\nNULLABLE Fields :");

      for (int i1 = 0; i1 < (int)fldLoop->GetCount(); i1++)
      {
         Index.iVal = i1;

         if (fldLoop->GetItem(Index)->GetAttributes()  & adFldIsNullable)
            {
               printf ("%s  \n", (LPSTR)fldLoop->GetItem(Index)->GetName());
            }
      }

       // Display the attributes of the Employee tables's
       // properties
       printf("\nProperty attributes:\n");
       proLoop = pRstEmployee->GetProperties();

       for (int i2 = 0; i2 < (int)proLoop->GetCount(); i2++)
      {
         j= j+1;
         Index.iVal=i2;
         printf (" %s = %d \n", (LPSTR)(_bstr_t)proLoop->GetItem(Index)->GetName()
         ,(int)proLoop->GetItem(Index)->GetAttributes());

         if (((j % 23) == 0) || ( i2==6))
         {
            printf("\nPress any key to continue...");
            getch();

            //Clear the screen for the next display
            system("cls");
            j=0;
         }

      }
     // Clean up objects before exit.
     pRstEmployee->Close();
     pConnection->Close();
   }

   catch(_com_error &e)
   {
      // Notify the user of errors if any.

      PrintProviderError(pConnection);
      PrintComError(e);
   }

}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
   // Print Provider Errors from Connection object.

   // pErr is a record object in the Connection's Error collection.
   ErrorPtr    pErr  = NULL;
   long      nCount  = 0;
   long       i    = 0;

   if( (pConnection->Errors->Count) > 0)
   {
      nCount = pConnection->Errors->Count;

      // Collection ranges from 0 to nCount -1.
      for(i = 0; i < nCount; i++)
      {
         pErr = pConnection->Errors->GetItem(i);
         printf("\t Error number: %x\t%s", (LPCSTR) pErr->Number, (LPCSTR) pErr->Description);
      }
   }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

VOID PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

   // Print Com errors.
   printf("\nError\n");
   printf("Code = %08lx\n", e.Error());
   printf("Code meaning = %s\n", e.ErrorMessage());
   printf("Source = %s\n", (LPCSTR) bstrSource);
   printf("Description = %s\n", (LPCSTR) bstrDescription);
}
// EndAttributesCpp

AttributesX.h:

// BeginAttributesH
#include "icrsint.h"

//This class extracts LastName, FirstName, FaxPhone from Employees table

class CEmployeeRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CEmployeeRs)

        // Column LastName is the 2nd field in the table
        ADO_VARIABLE_LENGTH_ENTRY2(2,adVarChar,m_szemp_LastName,
            sizeof(m_szemp_LastName),lemp_LastNameStatus,TRUE)

        // Column FirstName is the 17th field in the table
        ADO_VARIABLE_LENGTH_ENTRY2(17,adVarChar,m_szemp_FirstName,
            sizeof(m_szemp_FirstName),lemp_FirstNameStatus,TRUE)

        // Column FaxPhone is the 18th field in the table
        ADO_VARIABLE_LENGTH_ENTRY2(18,adVarChar,m_szemp_Faxphone,
            sizeof(m_szemp_Faxphone),lemp_FaxphoneStatus,TRUE)

END_ADO_BINDING()

public:
    CHAR  m_szemp_LastName[21];
    ULONG lemp_LastNameStatus;
    CHAR  m_szemp_FirstName[11];
    ULONG lemp_FirstNameStatus;
    CHAR  m_szemp_Faxphone[25];
    ULONG lemp_FaxphoneStatus;
};

// EndAttributesH

See Also

Attributes Property | Connection Object | Field Object | Name Property | Property Object


BeginTrans, CommitTrans, and RollbackTrans Methods Example (VC++)

This example changes the book type of all psychology books in the Titles table of the database. After the BeginTrans method starts a transaction that isolates all the changes made to the Titles table, the CommitTrans method saves the changes. You can use the Rollback method to undo changes that you saved using the Update method.

// BeginBeginTransCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>
#include <ole2.h>
#include <conio.h>
#include <assert.h>
#include <malloc.h>
#include "BeginTransX.h"

inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void BeginTransX(void);
void PrintProviderError(_ConnectionPtr pConnection);

void main()
{
    if(FAILED(::CoInitialize(NULL)))
      return;

    BeginTransX();

    //Wait here for user to see the output.
    printf("\nPress any key to continue...");
    getch();

    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      BeginTransX Function                             //
//                                                       //
///////////////////////////////////////////////////////////

void BeginTransX()
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr rstTitles = NULL;
    _ConnectionPtr pConnection = NULL;

    //Define Other Variables
    HRESULT hr = S_OK;
    IADORecordBinding   *picRs = NULL;  //Interface Pointer declared...
    CTitlesRs titlrs;
    _bstr_t strTitle;
    _bstr_t strMessage;
    LPSTR p_TempStr = NULL;
    char chKey;
    int i = 0;

    try
    {
        // open connection.
        _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));

        TESTHR(pConnection->Open(strCnn,"","",adConnectUnspecified));

        rstTitles.CreateInstance(__uuidof(Recordset));

        rstTitles->CursorType = adOpenDynamic;
        rstTitles->LockType = adLockPessimistic;

        // open Titles table
        TESTHR(rstTitles->Open("titles",
            _variant_t((IDispatch*)pConnection,true),
            adOpenDynamic, adLockPessimistic,adCmdTable));

        rstTitles->MoveFirst();
        pConnection->BeginTrans();

        //Open an IADORecordBinding interface pointer which
        //we'll use for Binding Recordset to a class
        TESTHR(rstTitles->QueryInterface(
            __uuidof(IADORecordBinding), (LPVOID*)&picRs));

        //Bind the Recordset to a C++ Class here
        TESTHR(picRs->BindToRecordset(&titlrs));

        // Loop through recordset and ask user if he wants
        // to change the type for a specified title.
          // Allocate memory to p_TempStr string pointer.
        p_TempStr = (LPSTR) malloc(sizeof(titlrs.m_szT_type));

        // Check for null string.
        assert(p_TempStr != NULL);
        while (VARIANT_FALSE == rstTitles->EndOfFile)
        {
            // Remove blank string
            strcpy(p_TempStr,strtok(titlrs.m_szT_type," "));

            // Compare type with psychology
            if (!strcmp(p_TempStr,"psychology"))
            {
                strTitle = titlrs.m_szT_title;
                strMessage = "Title: " + strTitle +
                        "\n Change type to Self help?(y/n)";

                // Change the title for specified employee
                printf("%s\n",(LPCSTR)strMessage);
                do
                {
                    chKey = getch();
                }while(chKey != 'y' && chKey !='n');
                if(chKey == 'y')
                {
                    // Copy "self_help" title field
                    strcpy(titlrs.m_szT_type,"self_help");
                    picRs->Update(&titlrs);
                }
            }
            rstTitles->MoveNext();
        }
        // Ask if the User wants to commit to all the
        //Changes made above
        printf("\n\n Save all changes(y/n)?");
        do
        {
            chKey = getch();
        }while(chKey != 'y' && chKey !='n');

        if(chKey == 'y')

            // Save the changes to the title table
            pConnection->CommitTrans();
        else
            // Unsave the changes to the title table
            pConnection->RollbackTrans();

        // Print current data in recordset.
        rstTitles->Requery(0);

        // Move to the first record of the title table
        rstTitles->MoveFirst();
        printf("\n\nPress any key to continue...");
        getch();

        // Clear the screen for the next display
        system("cls");

        // Open IADORecordBinding interface pointer again
        // for binding Recordset to a class.
        TESTHR(rstTitles->QueryInterface(
            __uuidof(IADORecordBinding),
            (LPVOID*)&picRs));

        // Rebind the Recordset to a C++ Class.
        TESTHR(picRs->BindToRecordset(&titlrs));

        while (!rstTitles->EndOfFile)
        {
            i= i+1;
            if (i % 23 == 0)
            {
                printf("\nPress any key to continue...");
                getch();

                //Clear the screen for the next display
                system("cls");
            }
            printf("%s -  %s\n",titlrs.m_szT_title,titlrs.m_szT_type);
            rstTitles->MoveNext();
        }
        // Restore original data becasue this is a demonstration.
        rstTitles->MoveFirst();

        while (VARIANT_FALSE == rstTitles->EndOfFile)
        {
            strcpy(p_TempStr,titlrs.m_szT_type);
            p_TempStr = strtok(p_TempStr," ");

            if (!strcmp(p_TempStr,"self_help"))
            {
                strcpy(titlrs.m_szT_type,"psychology");
                picRs->Update(&titlrs);
            }
            rstTitles->MoveNext();
        }
        // Deallocate the memory
            free(p_TempStr);

        // Clean up objects before exit.
        rstTitles->Close();
        pConnection->Close();

        //Release IADORecordset Interface
        if (picRs)
            picRs->Release();
    }
    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        PrintProviderError(pConnection);

        printf("Source : %s\n",(LPCSTR)bstrSource);
        printf("Description : %s\n",(LPCSTR)bstrDescription);
    }
};

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr  pErr    = NULL;
    long      nCount  = 0;
    long      i       = 0;

    if( (pConnection->Errors->Count) > 0)
    {
        nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,(LPCSTR) pErr->Description);
        }
    }
}
// EndBeginTransCpp

BeginTransX.h:

// BeginBeginTransH
#include "icrsint.h"

//This Class extracts only title and type
class CTitlesRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CTitlesRs)
    //Column title is the 2nd field in the recordset
    ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szT_title,
         sizeof(m_szT_title), lT_titleStatus, FALSE)

    //Column type is the 3rd field in the recordset
    ADO_VARIABLE_LENGTH_ENTRY2(3, adVarChar, m_szT_type,
         sizeof(m_szT_type), lT_typeStatus, TRUE)
END_ADO_BINDING()

public:
   CHAR   m_szT_title[150];
   ULONG  lT_titleStatus;
   CHAR   m_szT_type[40];
   ULONG  lT_typeStatus;
};
// EndBeginTransH

See Also

BeginTrans, CommitTrans, and RollbackTrans Methods | Update Method


BOF, EOF, and Bookmark Properties Example (VC++)

The first function in this example uses the BOF and EOF properties to display a message if a user tries to move past the first or last record of a Recordset. It uses the Bookmark property to let the user flag a record in a Recordset and return to it later.

The second function uses the Bookmark property to place the Bookmark of every other record from a Recordset into an array, and then filters the Recordset using the array.

// BeginBOFCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>
#include "BofEofBookmark.h"

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void BOFX(void);
void BookmarkX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      BOFX Function                                    //
//                                                       //
///////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    BOFX();

    //Clear the screen for the next display
    system("cls");

    BookmarkX();

    printf("Press any key to continue...");
    getch();

    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      BOFX Function                                    //
//                                                       //
///////////////////////////////////////////////////////////

void BOFX(void)
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace
    _RecordsetPtr rstPublishers = NULL;

    //Define Other Variables
    HRESULT hr = S_OK;
    IADORecordBinding  *picRs = NULL;    //Interface Pointer declared.
    CPublishers Publs;

    bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
        "Initial Catalog=pubs;User Id=sa;Password=;");
    _bstr_t strMessage;
    _variant_t VarBookmark;
    int intCommand = 0;
    _variant_t TempPublisher;

    try
    {
        // Open recordset with data from Publishers table.
        TESTHR(rstPublishers.CreateInstance(__uuidof(Recordset)));
        rstPublishers->CursorType = adOpenStatic;

        // Use client cursor to enable absolutePosition property.
        rstPublishers->CursorLocation = adUseClient;
        rstPublishers->Open("select pub_id, pub_name from publishers"
            " order by pub_name", strCnn, adOpenStatic,
            adLockBatchOptimistic, adCmdText);

        //Open an IADORecordBinding interface pointer
        //which will be used for Binding Recordset to a class
        TESTHR(rstPublishers->QueryInterface(
            __uuidof(IADORecordBinding), (LPVOID*)&picRs));

        //Bind the Recordset to a C++ Class here
        TESTHR(picRs->BindToRecordset(&Publs));

        rstPublishers->MoveFirst();

         while (true)    // Continuous loop.
        {
            // Display information about the current record
            // and get user input
            printf("Publisher:%s \n Record %d of %d\n\n",
                Publs.lP_pubnameStatus  == adFldOK ?
                Publs.m_szP_pubname : "<NULL>",
                rstPublishers->AbsolutePosition,
                rstPublishers->RecordCount);
            printf("Enter command:\n ");
            printf("[1 - next          /  2 - previous       /\n");
            printf(" 3 - set bookmark  /  4 - go to bookmark /\n");
            printf(" 5 - quit                                ]\n");

            scanf("%d", &intCommand);
            if ((intCommand < 1) || (intCommand > 4))
                break;    // Out of range entry exits program loop.

            switch(intCommand)
            {
                // Move forward or backward, trapping for BOF or EOF
                case 1:
                    rstPublishers->MoveNext();
                    if (rstPublishers->EndOfFile)
                    {
                        printf("\nCannot move past the last record."
                               " Try again...\n");
                        rstPublishers->MoveLast();
                    }
                    break;

                case 2:
                    rstPublishers->MovePrevious();
                    if (rstPublishers->BOF)
                    {
                        printf("\nCannot move before the first record."
                               " Try again...\n");
                        rstPublishers->MoveFirst();
                    }
                    break;

                // store the bookmark of the current record.
                case 3:
                    VarBookmark = rstPublishers->Bookmark;
                    // Go to the record indicated by the
                    // stored bookmark
                    break;

                case 4:
                    // Check for whether bookmark set for a record
                    if (VarBookmark.vt == VT_EMPTY)
                        printf("No Bookmark set!\n");
                    else
                        rstPublishers->Bookmark = VarBookmark;
                    break;

                default:
                    break;
            }
        }
        // Clean up objects before exit.
        rstPublishers->Close();

        //Release the IADORecordset Interface here
        if (picRs)
            picRs->Release();
    }

    catch (_com_error &e)
    {
        printf("Error in BOFx...\n");
        // Notify the user of errors if any.
        _variant_t vtConnect = rstPublishers->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                printf("Error:\n");
                printf("Code = %08lx\n", e.Error());
                printf("Message = %s\n", e.ErrorMessage());
                printf("Source = %s\n", (LPCSTR) e.Source());
                printf("Description = %s\n", (LPCSTR) e.Description());
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
        printf("Press any key to continue...");
        getch();
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      BookmarkX Function                               //
//                                                       //
///////////////////////////////////////////////////////////

void BookmarkX(void)
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr rstAuthors = NULL;

    //Define Other Variables
    IADORecordBinding *picRs = NULL; //Interface Pointer declared.
    CAuthors Authrs;
    HRESULT  hr = S_OK;
    _bstr_t  strCnn("Provider=sqloledb;Data Source=MyServer;"
              "Initial Catalog=pubs;User Id=sa;Password=foo;");
    _variant_t vBookmark;

    // Variable declaration for safe arrays.
    SAFEARRAY FAR* psa;

    // define ARRAY/ VARIANT variant.
    vBookmark.vt = VT_ARRAY|VT_VARIANT;
    SAFEARRAYBOUND rgsabound[1];
    rgsabound[0].lLbound = 0;
    rgsabound[0].cElements = 11;
    long ii = 0;

    try
    {
        rstAuthors.CreateInstance(__uuidof(Recordset));
        // Set The Cursor Location
        rstAuthors->CursorLocation  = adUseClient;
        rstAuthors->PutActiveConnection((_variant_t)strCnn);

        // Open Authors table
        TESTHR(rstAuthors->Open("select * from authors",strCnn,
            adOpenStatic,adLockBatchOptimistic,adCmdText));

        //Open an IADORecordBinding interface pointer
        //which we'll use for binding Recordset to a class
        TESTHR(rstAuthors->QueryInterface(__uuidof(IADORecordBinding),
                (LPVOID*)&picRs));

        //Bind the Recordset to a C++ Class here
        TESTHR(picRs->BindToRecordset(&Authrs));

        printf("Number of Records before filtering:  %d\n",
                rstAuthors->RecordCount);

        // Create safearrays to store array of variant
        psa = SafeArrayCreate(VT_VARIANT,1,rgsabound);

        // Store bookmark of every other record into an array.
        while ((!rstAuthors->EndOfFile) && (ii < 11))
        {
            SafeArrayPutElement(psa,&ii,&rstAuthors->Bookmark);
            //ii = ii +1;
            ii++;
            rstAuthors->Move(2);
        }

        vBookmark.parray = psa;

        // Filter the Record with the array of bookmarks.
        rstAuthors->put_Filter(vBookmark);
        printf("Number of Records after filtering:  %d\n",
            rstAuthors->RecordCount);
        rstAuthors->MoveFirst();

        while (!rstAuthors->EndOfFile)
        {
            printf("%d    %s\n",rstAuthors->AbsolutePosition,
                Authrs.lau_lnameStatus == adFldOK ?
                Authrs.m_szau_lname : "<NULL>");
            rstAuthors->MoveNext();
        }

        //Release the IADORecordset Interface.
        if (picRs)
            picRs->Release();

        // Clean up objects before exit.
        rstAuthors->Close();
    }

    catch (_com_error &e)
    {
        // Notify the user of errors if any.
        _variant_t vtConnect = rstAuthors->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                printf("Error:\n");
                printf("Code    = %08lx\n", e.Error());
                printf("Message = %s\n", e.ErrorMessage());
                printf("Source  = %s\n", (LPCSTR) e.Source());
                printf("Description = %s\n", (LPCSTR) e.Description());
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr  pErr    = NULL;
    long      nCount  = 0;
    long      i       = 0;

    if( (pConnection->Errors->Count) > 0)
    {
        nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}
// EndBOFCpp

BofEofBookmark.h:

// BeginBOFEOFH
#include "icrsint.h"

//This Class extracts only pubid,lastname and hire_date
class CPublishers : public CADORecordBinding
{
BEGIN_ADO_BINDING(CPublishers)

    //Column title is the 2nd field in the recordset
    ADO_VARIABLE_LENGTH_ENTRY2(1, adVarChar, m_szP_pubid,
        sizeof(m_szP_pubid), lP_pubidStatus, FALSE)

    //Column type is the 3rd field in the recordset
    ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szP_pubname,
        sizeof(m_szP_pubname), lP_pubnameStatus, TRUE)

END_ADO_BINDING()

public:
    CHAR    m_szP_pubid;
    ULONG   lP_pubidStatus;
    CHAR    m_szP_pubname[40];
    ULONG   lP_pubnameStatus;
};

//This Class extracts only authorlastname

class CAuthors : public CADORecordBinding
{
BEGIN_ADO_BINDING(CAuthors)

    //Column authorlname is the 2nd field in the recordset
    ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar,  m_szau_lname,
         sizeof(m_szau_lname), lau_lnameStatus, FALSE)

END_ADO_BINDING()

public:
    CHAR    m_szau_lname[40];
    ULONG   lau_lnameStatus;
};
// EndBOFEOFH

See Also

BOF, EOF Properties | Bookmark Property | Recordset Object


CacheSize Property Example (VC++)

This example uses the CacheSize property to show the difference in performance for an operation performed with and without a 30-record cache.

// BeginCacheSizeCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>
#include <winbase.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void CacheSizeX();
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    CacheSizeX();

    //Wait here for user to see the output..
    printf("\nPress any key to continue...");
    getch();

    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      CacheSizeX Function                              //
//                                                       //
///////////////////////////////////////////////////////////

void CacheSizeX()
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace
     _RecordsetPtr    pRstRoySched  = NULL;

     //Define Other Variables
    HRESULT  hr = S_OK;
    DWORD sngStart;
    DWORD sngEnd;
    float sngNoCache;
    float sngCache;
    int intLoop = 0;
    _bstr_t strTemp;

    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
    "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        //Open the RoySched table.
        TESTHR(pRstRoySched.CreateInstance(__uuidof(Recordset)));
        pRstRoySched->Open("roysched", strCnn, adOpenForwardOnly,
            adLockReadOnly, adCmdTable);

        // Enumerate the Recordset object twice and record
        // the elapsed time.
        sngStart = GetTickCount();

        for (intLoop = 1; intLoop < 2; intLoop++)
        {
            pRstRoySched->MoveFirst();

            while(!(pRstRoySched->EndOfFile))
            {
                    // Execute a simple operation for the
                    // performance test.
                    strTemp = pRstRoySched->Fields->
                        Item["title_id"]->Value;
                    pRstRoySched->MoveNext();
            }
        }
        sngEnd = GetTickCount();
        sngNoCache = (float)(sngEnd - sngStart)/(float)1000;

        // Cache records in groups of 30 records.
        pRstRoySched->MoveFirst();
        pRstRoySched->CacheSize = 30;

        sngStart =  GetTickCount();

        // Enumerate the Recordset object twice and record
        // the elapsed time.
        for (intLoop = 1;intLoop < 2; intLoop++)
        {
            pRstRoySched->MoveFirst();
            while(!(pRstRoySched->EndOfFile))
            {
                    // Execute a simple operation for the
                    // performance test.
                    strTemp = pRstRoySched->Fields->
                        Item["title_id"]->Value;
                    pRstRoySched->MoveNext();
            }
        }
        sngEnd =  GetTickCount();
        sngCache = (float)(sngEnd - sngStart)/(float)1000;

        // Display performance results.
        printf("Caching Performance Results:\n");
        printf("No cache: %6.3f  seconds \n", sngNoCache);
        printf("30-record cache: %6.3f  seconds \n", sngCache);

        // Clean up objects before exit.
        pRstRoySched->Close();
    }
    catch(_com_error &e)
    {
         // Notify the user of errors if any.
        _variant_t vtConnect = pRstRoySched->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
               // Pass a connection pointer accessed from the Recordset.
               PrintProviderError(vtConnect);
               break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.

    // pErr is a record object in the Connection's Error collection.
    ErrorPtr pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s", pErr->Number,
                pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);

}
// EndCacheSizeCpp

See Also

CacheSize Property | Recordset Object


Cancel Method Example (VC++)

This example uses the Cancel method to cancel a command executing on a Connection object if the connection is busy.

// BeginCancelCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include<conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void CancelX();
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    CancelX();

    //Wait here for user to see the output..
    printf("\nPress any key to continue...");
    getch();

    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      CancelX Function                                 //
//                                                       //
///////////////////////////////////////////////////////////

void CancelX()
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace
     _ConnectionPtr pConnection = NULL;

     //Define Other Variables
    HRESULT  hr = S_OK;
    _bstr_t strCmdChange;
    _bstr_t strCmdRestore;
    BOOL booChanged = FALSE;

    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
    "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        // open a connection.
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
        pConnection->Open(strCnn,"","",adConnectUnspecified);

        // Define command strings.
        strCmdChange = "UPDATE titles SET type = 'self_help' "
            "WHERE type = 'psychology'";

        strCmdRestore = "UPDATE titles SET type = 'psychology' "
            "WHERE type = 'self_help'";

        // Begin a transaction, then execute a command asynchronously.
        pConnection->BeginTrans();
        pConnection->Execute(strCmdChange,NULL,adAsyncExecute);

        // do something else for a little while - this could be changed
        for (int i = 1; i<=100000 ;i++)
        {
           // i = i + i;
            printf("%d\n", i);
        }

        // If the command has NOT completed, cancel the execute
        // and roll back the transaction. Otherwise, commit the
        // transaction.
        if ((pConnection->GetState()))
        {
            pConnection->Cancel();
            pConnection->RollbackTrans();
            booChanged = FALSE;
            printf("Update canceled.\n");
            printf("GetState = %d\n", pConnection->GetState());
        }
        else
        {
            pConnection->CommitTrans();
            booChanged = TRUE;
            printf("Update complete.\n");

        }

        // If the change was made, restore the data
        // because this is a demonstration.
        if (booChanged)
        {
            pConnection->Execute(strCmdRestore,NULL,0);
            printf("Data restored.\n");
        }
        // Cleanup object before exit
        pConnection->Close ();
    }

    catch(_com_error &e)
    {
        // Notify user of any errors that result from
        // executing the query.
        // Pass a connection pointer accessed from the Connection.
        PrintProviderError(pConnection);
        PrintComError(e);
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;

        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s", pErr->Number,
                pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndCancelCpp

See Also

Cancel Method | Connection Object


Clone Method Example (VC++)

This example uses the Clone method to create copies of a Recordset and then lets the user position the record pointer of each copy independently.

// BeginCloneCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>
#include <ole2.h>
#include <conio.h>
#include "CloneX.h"

// Function Declarations.
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void CloneX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    CloneX();

    //Wait here for user to see the output..
    printf("\nPress any key to continue...");
    getch();
    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      CloneX Function                                  //
//                                                       //
///////////////////////////////////////////////////////////

void  CloneX()
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr  arstStores[3];

    //Define Other Variables
    HRESULT hr = S_OK;
    int intLoop = 0;
    _bstr_t strSQL;
    _bstr_t strMessage;
    _bstr_t strFind;
    int intLoop1 = 0;
    char *tempStr;
    bool boolFlag = TRUE;
    char m_szS_stor_name[150];

    // Assign SQL statement and connection string to variables.
    strSQL = "SELECT stor_name FROM Stores ORDER BY stor_name";

    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        // Open recordset as a static cursor type recordset.
        arstStores[0].CreateInstance(__uuidof(Recordset));
        arstStores[0]->CursorType = adOpenStatic;
        arstStores[0]->LockType = adLockBatchOptimistic;

        TESTHR(arstStores[0]->Open(strSQL,strCnn, adOpenStatic,
            adLockBatchOptimistic,adCmdText));

        // Create two clones of the original Recordset.
        arstStores[1] = arstStores[0]->Clone(adLockUnspecified);
        arstStores[2] = arstStores[0]->Clone(adLockUnspecified);

        while (boolFlag)
        {
            // Loop through the array so that on each pass,
            // the user is searching a different copy of the
            // same Recordset.
            for (intLoop = 1; intLoop <= 3 ; intLoop++)
            {
                // Ask for search string while showing where
                // the current record pointer is for each Recordset.
                printf("Recordsets from stores table:\n");

                _bstr_t str1 = arstStores[0]->Fields->
                    GetItem("stor_name")->Value;
                printf("\t1 - Original - Record pointer at %s",
                    (LPCSTR)str1);

                _bstr_t str2 = arstStores[1]->Fields->
                    GetItem("stor_name")->Value;
                printf("\n\t2 - Clone - Record pointer at %s",
                    (LPCSTR)str2);

                _bstr_t str3 = arstStores[2]->Fields->
                    GetItem("stor_name")->Value;
                printf("\n\t3 - Clone - Record pointer at %s",
                    (LPCSTR)str3);

                printf("\n\nEnter search string for # %d, "
                    "or press Enter to quit.\n", intLoop);
                gets(m_szS_stor_name);

                // Trim the String.
                tempStr = strtok(m_szS_stor_name, "  \t");
                strMessage = tempStr;
                if (tempStr == NULL)
                {
                    boolFlag = FALSE;
                    break;
                }

                // Find the search string; if there's no
                // match, jump to the last record.
                intLoop1 = intLoop - 1;

                arstStores[intLoop1]->Filter = "stor_name >= '" +
                    strMessage + "'";

                if (arstStores[intLoop1]->EndOfFile)
                {
                    arstStores[intLoop1]->Filter = (long)adFilterNone;
                    arstStores[intLoop1]->MoveLast();
                }
            }
        } // End of While Loop

        // Clean up objects before exit.
        arstStores[0]->Close();
        arstStores[1]->Close();
        arstStores[2]->Close();
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _variant_t vtConnect;

        vtConnect = arstStores[0]->GetActiveConnection();

        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
};

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr pErr = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
};

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
};
// EndCloneCpp

CloneX.h:

// BeginCloneH
#include "icrsint.h"

// This Class extracts only store name
// from "stores" table.
class CStores : public CADORecordBinding
{
BEGIN_ADO_BINDING(CStores)

    //Column stor_name is the 1st field in the recordset

    ADO_VARIABLE_LENGTH_ENTRY2(1, adVarChar, m_szS_stor_name,
         sizeof(m_szS_stor_name), lS_stor_nameStatus, FALSE)

END_ADO_BINDING()

public:
    CHAR    m_szS_stor_name[150];
    ULONG   lS_stor_nameStatus;
};

// EndCloneH

See Also

Clone Method | Recordset Object


CompareBookmarks Method Example (VC++)

This example demonstrates the CompareBookmarks method. The relative value of bookmarks is seldom needed unless a particular bookmark is somehow special.

Designate a random row of a Recordset derived from the Authors table as the target of a search. Then display the position of each row relative to that target.

// BeginCompareBookmarksCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>
#include <time.h>
#include <stdlib.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void CompareBookMarksX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//        Main Function                                 //
//                                                      //
//////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    CompareBookMarksX();

    printf("Press any key to continue...");
    getch();
    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//           CompareBookMarksX Function                 //
//                                                      //
//////////////////////////////////////////////////////////

void CompareBookMarksX(void)
{
    HRESULT hr = S_OK;

    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
        "Initial Catalog=pubs;User Id=sa;Password=;");

    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr pRstAuthors = NULL;
    _variant_t vTarget;
    _bstr_t strAns;
    _bstr_t strTitle;
    strTitle = "CompareBookmarks Example";
    try
    {
        TESTHR(pRstAuthors.CreateInstance(__uuidof(Recordset)));

        pRstAuthors->Open("SELECT * FROM authors ORDER BY au_id", strCnn,
            adOpenStatic, adLockReadOnly, adCmdText);

        long count = pRstAuthors->RecordCount;
        printf("Rows in the Recordset = %d\n", count);
        if (count == 0)
            exit(1);     //Exit if an empty recordset

        srand( (unsigned)time( NULL ) ); //Randomize

        count = int(rand() % (count-1)); //Get position between 1 and count-1
        if(!count) {count++;};

        printf("Randomly chosen row position = %d\n", count);

        _variant_t vtBookMark = (short)adBookmarkFirst;

        pRstAuthors->Move(count,vtBookMark); //Move row to random position

        vTarget = pRstAuthors->Bookmark;    //Remember the mystery row.
        count = 0;
        long intLineCnt = 3;
        pRstAuthors->MoveFirst();

        while (!(pRstAuthors->EndOfFile))       //Loop through recordset
        {
            intLineCnt++;
            if (intLineCnt%20 == 0)
            {
                printf("\nPress any key to continue...\n");
                getch();
            }
            long result = pRstAuthors->CompareBookmarks(
                pRstAuthors->Bookmark, vTarget);

            if (result == adCompareNotEqual)
                printf("Row %d: Bookmarks are not equal. %d\n", count, result);
            else if  (result == adCompareNotComparable)
                printf("Row %d: Bookmarks are not comparable.\n", count);
            else
            {
                switch(result)
                {
                    case adCompareLessThan:
                        strAns = "less than";
                        break;
                    case adCompareEqual:
                        strAns = "equal to";
                        break;
                    case adCompareGreaterThan:
                        strAns = "greater than";
                        break;
                    default:
                        strAns = "in error comparing to";
                        break;
                }
                printf ("Row position  %d  is  %s  the target.\n",
                    count,(LPCSTR)strAns);
            }
            count++;
            pRstAuthors->MoveNext();
        }

        // Clean up objects before exit.
        pRstAuthors->Close();
   }

   catch(_com_error &e)
   {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstAuthors->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//            PrintProviderError Function               //
//                                                      //
//////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr pErr = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                pErr->Description);
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//        PrintComError Function                        //
//                                                      //
//////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndCompareBookmarksCpp

See Also

CompareBookmarks Method | Recordset Object


ConnectionString, ConnectionTimeout, and State Properties Example (VC++)

This example demonstrates different ways of using the ConnectionString property to open a Connection object. It also uses the ConnectionTimeout property to set a connection timeout period, and the State property to check the state of the connections. The GetState function is required for this procedure to run.

// BeingConnectionStringCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void ConnectionStringX();
_bstr_t GetState(int intState);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    ConnectionStringX();

    //Wait here for user to see the output..
    printf("\nPress any key to continue...");
    getch();

    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      ConnectionStringX Function                       //
//                                                       //
///////////////////////////////////////////////////////////

void ConnectionStringX()
{
    // Define Connection object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace
     _ConnectionPtr pConnection1 = NULL;
     _ConnectionPtr pConnection2 = NULL;
     _ConnectionPtr pConnection3 = NULL;
     _ConnectionPtr pConnection4 = NULL;

    //Define Other Variables
    HRESULT  hr = S_OK;

    try
    {
        // Open a connection using OLE DB syntax.
        TESTHR(pConnection1.CreateInstance(__uuidof(Connection)));
        pConnection1->ConnectionString = "provider=SQLOLEDB;"
            "Data Source=MyServer;Initial Catalog=Pubs;"
          "User Id=sa;Password=;";
        pConnection1->ConnectionTimeout = 30;
        pConnection1->Open("","","",adConnectUnspecified);

        // Open a connection using a DSN and ODBC tags.
        TESTHR(pConnection2.CreateInstance(__uuidof(Connection)));
        pConnection2->ConnectionString = "DSN=Pubs;UID=sa;PWD=;";
        pConnection2->Open("","","",adConnectUnspecified);

        // Open a connection using a DSN and OLE DB tags.
        TESTHR(pConnection3.CreateInstance(__uuidof(Connection)));
        pConnection3->ConnectionString = "Data Source=Pubs;User ID=sa;"
            "Password=;";
        pConnection3->Open("","","",adConnectUnspecified);

        // Open a connection using a DSN and individual
        // arguments instead of a connection string.
        TESTHR(pConnection4.CreateInstance(__uuidof(Connection)));
        pConnection4->Open("Pubs","sa","",adConnectUnspecified);

        // Display the state of the connections.
        printf("cnn1 state: %s\n",
            (LPCTSTR)GetState(pConnection1->State));
        printf("cnn2 state: %s\n",
            (LPCTSTR)GetState(pConnection2->State));
        printf("cnn3 state: %s\n",
            (LPCTSTR)GetState(pConnection3->State));
        printf("cnn4 state: %s\n",
            (LPCTSTR)GetState(pConnection4->State));

        //Cleanup objects before exit.
        pConnection4->Close();
        pConnection3->Close();
        pConnection2->Close();
        pConnection1->Close();
    }

    catch(_com_error &e)
    {
        // Notify user of any errors.
        // Pass a connection pointer accessed from the Connection.
        PrintProviderError(pConnection1);
        if(pConnection2)
            PrintProviderError(pConnection2);
        if(pConnection3)
            PrintProviderError(pConnection3);
        if(pConnection4)
            PrintProviderError(pConnection4);
        PrintComError(e);
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      GetState Function                                //
//                                                       //
///////////////////////////////////////////////////////////

_bstr_t GetState(int intState)
{
    _bstr_t strState;
    switch(intState)
    {
        case adStateClosed:
            strState = "adStateClosed";
            break;
        case adStateOpen:
            strState = "adStateOpen";
            break;
        default:
        ;
    }
    return strState;
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr  pErr = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;

        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR)pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndConnectionStringCpp

See Also

Connection Object | ConnectionString Property | ConnectionTimeout Property | State Property


Count Property Example (VC++)

This example demonstrates the Count property with two collections in the Employee database. The property obtains the number of objects in each collection, and sets the upper limit for loops that enumerate these collections. Another way to enumerate these collections without using the Count property would be to use For Each...Next statements.

// BeginCountCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include<conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void CountX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    CountX();

    //Wait here for user to see the output..
    printf("\nPress any key to Exit...");
    getch();

    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      CountX Function                                  //
//                                                       //
///////////////////////////////////////////////////////////

void CountX()
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace
    _RecordsetPtr pRstEmployee = NULL;

    //Define Other Variables
    HRESULT hr = S_OK;
    _variant_t Index;
    Index.vt = VT_I2;
    int j = 0;

    _bstr_t strCnn("Provider=SQLOLEDB;Data Source=MyServer;"
        "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        // Open recordset with data from Employee table.
        TESTHR(pRstEmployee.CreateInstance(__uuidof(Recordset)));
        pRstEmployee->Open("Employee", strCnn, adOpenForwardOnly,
            adLockReadOnly, adCmdTable);

        // Print information about Fields collection.
        printf("%d Fields in Employee\n", pRstEmployee->Fields->Count);
        for (int intLoop = 0;
             intLoop <= (pRstEmployee->Fields->Count-1);
             intLoop++)
        {
            Index.iVal = intLoop;
            printf(" %s\n",(LPSTR) pRstEmployee->Fields->
                GetItem(Index)->Name);
        }

        // Print information about Properties collection.
        printf("\n%d Properties in Employee\n", pRstEmployee->
            Properties->Count);
        for (intLoop = 0;
             intLoop <= (pRstEmployee->Properties->Count - 1);
             intLoop++)
        {
            j++;
            Index.iVal = intLoop;
            printf(" %s\n" ,(LPSTR)pRstEmployee->Properties->
                GetItem(Index)->Name);
            if (((j % 23) == 0) || (intLoop == 11))
            {
                printf("\nPress any key to continue...");
                getch();

                //Clear the screen for the next display
                system("cls");
                j = 0;
            }
        }
        // Clean up objects before exit.
        pRstEmployee->Close();
    }

    catch(_com_error &e)
    {
        // Notify user of any errors that result from
        // executing the query.
        // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstEmployee->GetActiveConnection();

        switch(vtConnect.vt)
        {
        case VT_BSTR:
            PrintComError(e);
            break;
        case VT_DISPATCH:
            PrintProviderError(vtConnect);
            break;
        default:
            printf("Errors occured.");
            break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr pErr = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;

        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n",pErr->Number,
                pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndCountCpp

See Also

Count Property


CursorType, LockType, and EditMode Properties Example (VC++)

This example demonstrates setting the CursorType and LockType properties before opening a Recordset. It also shows the value of the EditMode property under various conditions. The EditModeOutput function is required for this procedure to run.

// BeginEditModeCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declaration
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void EditModeX(void);
void EditModeOutput(char *, int);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

     EditModeX();

    // Wait here for the user to see the output
    printf("\n\nPress any key to continue...");
    getch();

    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      EditModeX Function                               //
//                                                       //
///////////////////////////////////////////////////////////
void EditModeX(void)
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr     pRstEmployees = NULL;
    _ConnectionPtr    pConnection  = NULL;

    HRESULT hr = S_OK;

    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
        "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        // Open a connection
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
        hr = pConnection->Open(strCnn,"","",adConnectUnspecified);

        // Open recordset with data from employee table
        TESTHR(pRstEmployees.CreateInstance(__uuidof(Recordset)));

        pRstEmployees->CursorLocation = adUseClient;
        pRstEmployees->CursorType = adOpenStatic;
        pRstEmployees->LockType  = adLockBatchOptimistic;

        pRstEmployees->Open("employee",
             _variant_t((IDispatch *) pConnection,true),
            adOpenStatic,adLockBatchOptimistic,adCmdTable);

        // Show the EditMode property under different editing states.
        pRstEmployees->AddNew ();
        pRstEmployees->Fields->Item["emp_id"]->Value =
            (_bstr_t)("T-T55555M");
        pRstEmployees->Fields->Item["fname"]->Value =
            (_bstr_t)("temp_fname");
        pRstEmployees->Fields->Item["lname"]->Value =
            (_bstr_t)("temp_lname");
        EditModeOutput("After AddNew: ", pRstEmployees->EditMode);

        pRstEmployees->UpdateBatch(adAffectCurrent);
        EditModeOutput("After Update: ", pRstEmployees->EditMode);

        pRstEmployees->Fields->Item["fname"]->Value = (_bstr_t)("test");
        EditModeOutput("After Edit: ", pRstEmployees->EditMode);

        // Delete new record because this is a demonstration.
        pConnection->Execute("DELETE FROM employee WHERE emp_id = "
            "'T-T55555M'", NULL, adCmdText);

        // Clean up objects before exit.
        pRstEmployees->Close();
        pConnection->Close();
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        // Pass a connection pointer accessed from the Recordset.
        PrintProviderError(pConnection);
        PrintComError(e);
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      EditModeOutput() Function                        //
//                                                       //
///////////////////////////////////////////////////////////
void EditModeOutput(char *strTemp, int intEditMode)
{
    // Print report based on the value of the EditMode property.
    printf("\n%s", strTemp);
    printf("\n  EditMode = ");

    switch (intEditMode)
    {
        case adEditNone :
            printf("adEditNone");
            break;
        case adEditInProgress :
            printf("adEditInProgress");
            break;
        case adEditAdd :
            printf("adEditAdd");
            break;
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr  pErr = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\n\t Error number: %x\t%s\n", pErr->Number,
                (LPCSTR)pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}

// EndEditModeCpp

See Also

CursorType Property | EditMode Property | LockType Property | Recordset Object


Delete Method Example (VC++)

This example uses the Delete method to remove a specified record from a Recordset.

// BeginDeleteCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>
#include <ole2.h>
#include <conio.h>
#include "DeleteX.h"

//Function Declarations.
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void DeleteX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    DeleteX();

    // Wait here for the user to see the output
    printf("\n\nPress any key to continue...");
    getch();

    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      DeleteX Function                                 //
//                                                       //
///////////////////////////////////////////////////////////

void DeleteX(void)
{

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr  pRstRoySched = NULL;

    // Define Other Variables
    IADORecordBinding   *picRs = NULL;  //Interface Pointer declared.
    CRoySchedRs    royschrs;            //C++ class object
    HRESULT hr = S_OK;

    char  strTitleID[10], strTmpTitleID[10]="";
    long  longHiRange;
    int   intRoyalty, intLoRange, cnt=0;
    bool  blnFound = TRUE;

    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        // Open RoySched table
        TESTHR(pRstRoySched.CreateInstance(__uuidof(Recordset)));

        pRstRoySched->CursorLocation = adUseClient;
        pRstRoySched->CursorType = adOpenStatic;
        pRstRoySched->LockType  = adLockBatchOptimistic;

        TESTHR(pRstRoySched->Open("SELECT * FROM roysched WHERE"
            " royalty = 20",strCnn,adOpenStatic,adLockBatchOptimistic,
            adCmdText));

        // Prompt for a record to delete.
        printf("Before delete there are %d titles with 20 percent "
            "royalty :\n", pRstRoySched->RecordCount);

        // Open an IADORecordBinding interface pointer which we'll use
        // for Binding Recordset to a class.
        TESTHR(pRstRoySched->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        // Bind the Recordset to a C++ Class here
        TESTHR(picRs->BindToRecordset(&royschrs));

        while(!(pRstRoySched->EndOfFile))
        {
            printf("%s\n", royschrs.lemp_titleidStatus == adFldOK ?
                royschrs.m_sz_titleid : "<NULL>");
            pRstRoySched->MoveNext();
        }

        printf("\nEnter the ID of a record to delete: ");
        gets(strTitleID);

        // Converting the title_id to upper case
        for( cnt=0; cnt<10; cnt++)
        {
            if(strTitleID[cnt] != NULL)
            {
                if( IsCharAlpha( strTitleID[cnt]) )
                {
                    if( islower( strTitleID[cnt]) )
                        strTmpTitleID[cnt] = _toupper(strTitleID[cnt]);
                    else
                        strTmpTitleID[cnt] = strTitleID[cnt];
                }
                else
                {
                    strTmpTitleID[cnt] = strTitleID[cnt];
                }
            }
        }

        // Move to the record and save data so it can be restored.
        pRstRoySched->PutFilter ("title_id = '" +
            (_bstr_t)(LPCSTR)strTmpTitleID + "'");

        if(pRstRoySched->RecordCount != 0)
        {
            intLoRange = royschrs.m_sz_lorange;
            longHiRange = royschrs.m_sz_hirange;
            intRoyalty = royschrs.m_sz_royalty;

            // Delete the record
            pRstRoySched->Delete(adAffectCurrent);
            pRstRoySched->UpdateBatch(adAffectCurrent);
        }
        else
        {
            blnFound = FALSE;
            printf("This Title ID not available");
        }

        // Show the results.
        VARIANT varFilter;
        varFilter.vt = VT_I2;
        varFilter.iVal = adFilterNone;
        pRstRoySched->PutFilter (varFilter);
        pRstRoySched->Requery(-1);

        // Bind the Recordset to a C++ Class here.
        TESTHR(picRs->BindToRecordset(&royschrs));

        printf("\nAfter delete there are %d titles with 20 percent"
            " royalty: ", pRstRoySched->RecordCount);

        while(!(pRstRoySched->EndOfFile))
        {
            printf("\n%s", royschrs.lemp_titleidStatus == adFldOK ?
                royschrs.m_sz_titleid : "<NULL>");
            pRstRoySched->MoveNext();
        }

        // Restore the data because this is a demonstration.
        if(blnFound)
        {
            strcpy((char*)royschrs.m_sz_titleid,
                (const char*) strTmpTitleID);
            royschrs.m_sz_lorange = intLoRange;
            royschrs.m_sz_hirange = longHiRange;
            royschrs.m_sz_royalty = intRoyalty;

            TESTHR(picRs->AddNew(&royschrs));

            pRstRoySched->UpdateBatch(adAffectCurrent);
        }

        // Clean up objects before exit.
        pRstRoySched->Close();

        // Release the IADORecordset Interface here
        if (picRs)
            picRs->Release();
}

    catch(_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstRoySched->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr  pErr = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndDeleteCpp

DeleteX.h:

// BeginDeleteH
#include "icrsint.h"

// This Class extracts titleid, lorange, hirange and royalty
// from the "roysched" table.

class CRoySchedRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CRoySchedRs)

    //Column empid is the 1st field in the recordset

    ADO_VARIABLE_LENGTH_ENTRY2(1, adVarChar, m_sz_titleid,
         sizeof(m_sz_titleid), lemp_titleidStatus, TRUE)

    ADO_VARIABLE_LENGTH_ENTRY2(2, adInteger, m_sz_lorange,
         sizeof(m_sz_lorange), lemp_lorangeStatus, TRUE)

    ADO_VARIABLE_LENGTH_ENTRY2(3, adInteger, m_sz_hirange,
         sizeof(m_sz_hirange), lemp_hirangeStatus, TRUE)

    ADO_VARIABLE_LENGTH_ENTRY2(4, adInteger, m_sz_royalty,
         sizeof(m_sz_royalty), lemp_royaltyStatus, TRUE)

END_ADO_BINDING()

public:

    CHAR    m_sz_titleid[10];
    ULONG   lemp_titleidStatus;
    ULONG   m_sz_lorange;
    ULONG   lemp_lorangeStatus;
    ULONG   m_sz_hirange;
    ULONG   lemp_hirangeStatus;
    ULONG   m_sz_royalty;
    ULONG   lemp_royaltyStatus;
};
// EndDeleteH

See Also

Delete Method (ADO Recordset) | Recordset Object


Description, HelpContext, HelpFile, NativeError, Number, Source, and SQLState Properties Example (VC++)

This example triggers an error, traps it, and displays the Description, HelpContext, HelpFile, NativeError, Number, Source, and SQLState properties of the resulting Error object.

// BeginDescriptionCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include<conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void DescriptionX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    DescriptionX();

    //Wait here for user to see the output..
    printf("\nPress any key to continue...");
    getch();
    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      DescriptionX Function                            //
//                                                       //
///////////////////////////////////////////////////////////

void DescriptionX()
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace
    _ConnectionPtr pConnection = NULL;
    ErrorPtr errorLoop = NULL;

    //Define Other Variables
    HRESULT hr = S_OK;

    try
    {
        // Intentionally trigger an error.
        // open connection
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));

        if (FAILED(hr = pConnection->Open("nothing","","",adConnectUnspecified)))
        {
            _com_issue_error(hr);
            exit(1);
        }

        // Cleanup object before exit.
        pConnection->Close();
    }

    catch(_com_error)
    {
        // Pass a connection pointer.
        PrintProviderError(pConnection);
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    //Define Other Variables
    HRESULT  hr = S_OK;
    _bstr_t  strError;
    ErrorPtr  pErr = NULL;

    try
    {
        // Enumerate Errors collection and display
        // properties of each Error object.
        long nCount = pConnection->Errors->Count;

        // Collection ranges from 0 to nCount - 1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error #%d\n", pErr->Number);
            printf(" %s\n",(LPCSTR)pErr->Description);
            printf(" (Source: %s)\n",(LPCSTR)pErr->Source);
            printf(" (SQL State: %s)\n",(LPCSTR)pErr->SQLState);
            printf(" (NativeError: %d)\n",(LPCSTR)pErr->NativeError);
            if ((LPCSTR)pErr->GetHelpFile() == NULL)
            {
                printf("\tNo Help file available\n");
            }
            else
            {
                printf("\t(HelpFile: %s\n)" ,pErr->HelpFile);
                printf("\t(HelpContext: %s\n)" , pErr->HelpContext);
            }
        }
    }
    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        PrintComError(e);
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   // Notify the user of errors if any.
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.

   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndDescriptionCpp

See Also

Description Property | Error Object | HelpContext Property | HelpFile Property | NativeError Property | Number Property | Source Property (ADO Error) | SQLState Property


Execute, Requery, and Clear Methods Example (VC++)

This example demonstrates the Execute method when run from both a Command object and a Connection object. It also uses the Requery method to retrieve current data in a recordset, and the Clear method to clear the contents of the Errors collection. The ExecuteCommand and PrintOutput functions are required for this example to run.

// BeginExecuteCpp
#include <ole2.h>
#include <stdio.h>

#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void ExecuteX(void);
void ExecuteCommand(_CommandPtr pCmdTemp, _RecordsetPtr pRstTemp);
void PrintOutput(_RecordsetPtr pRstTemp);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

////////////////////////////////
//      Main Function         //
////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    ExecuteX();

    ::CoUninitialize();
}

///////////////////////////////////
//      ExecuteX Function        //
///////////////////////////////////

void ExecuteX(void)
{
   HRESULT    hr = S_OK;

    // Define string variables.
   _bstr_t strSQLChange("UPDATE Titles SET Type = "
            "'self_help' WHERE Type = 'psychology'");
   _bstr_t strSQLRestore("UPDATE Titles SET Type = "
            "'psychology' WHERE Type = 'self_help'");
   _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _ConnectionPtr  pConnection = NULL;
    _CommandPtr     pCmdChange  = NULL;
    _RecordsetPtr   pRstTitles  = NULL;

    try
    {
        // Open connection.
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
        pConnection->Open (strCnn, "", "", adConnectUnspecified);

        // Create command object.
        TESTHR(pCmdChange.CreateInstance(__uuidof(Command)));
        pCmdChange->ActiveConnection = pConnection;
        pCmdChange->CommandText = strSQLChange;

        // Open titles table, casting Connection pointer to an
        // IDispatch type so converted to correct type of variant.
        TESTHR(pRstTitles.CreateInstance(__uuidof(Recordset)));
        pRstTitles->Open ("Titles", _variant_t((IDispatch *) pConnection,
            true), adOpenStatic, adLockOptimistic, adCmdTable);

        // Print report of original data.
        printf(
            "\n\nData in Titles table before executing the query: \n");

        // Call function to print loop recordset contents.
        PrintOutput(pRstTitles);

        // Clear extraneous errors from the Errors collection.
        pConnection->Errors->Clear();

        // Call ExecuteCommand subroutine to execute pCmdChange command.
        ExecuteCommand(pCmdChange, pRstTitles);

        // Print report of new data.
        printf(
            "\n\n\tData in Titles table after executing the query: \n");
        PrintOutput(pRstTitles);

        // Use the Connection object's execute method to
        // execute SQL statement to restore data.
        pConnection->Execute(strSQLRestore, NULL, adExecuteNoRecords);

        // Retrieve the current data by requerying the recordset.
        pRstTitles->Requery(adCmdUnknown);

        // Print report of restored data.
        printf(
            "\n\n\tData after exec. query to restore original info: \n");
        PrintOutput(pRstTitles);

        // Clean up objects before exit.
        pRstTitles->Close();
        pConnection->Close();
    }

    catch (_com_error &e)
    {
        PrintProviderError(pConnection);
        PrintComError(e);
    }
}

//////////////////////////////////////////
//      ExecuteCommand Function         //
//////////////////////////////////////////

void ExecuteCommand(_CommandPtr pCmdTemp, _RecordsetPtr pRstTemp)
{
    try
    {
        // CommandText property already set before function was called.
        pCmdTemp->Execute(NULL, NULL, adCmdText);

        // Retrieve the current data by requerying the recordset.
        pRstTemp->Requery(adCmdUnknown);
    }

    catch(_com_error &e)
    {
        // Notify user of any errors that result from
        // executing the query.
        // Pass a connection pointer accessed from the Recordset.
        PrintProviderError(pRstTemp->GetActiveConnection());
        PrintComError(e);
    }
}

/////////////////////////////////////
//      PrintOutput Function       //
/////////////////////////////////////

void PrintOutput(_RecordsetPtr pRstTemp)
{
    // Ensure at top of recordset.
    pRstTemp->MoveFirst();

    // If EOF is true, then no data and skip print loop.
    if( pRstTemp->EndOfFile )
    {
        printf("\tRecordset empty\n");
    }
    else
    {
        // Define temporary strings for output conversions.
        // Initialize to first record's values.
        _bstr_t bstrTitle;
        _bstr_t bstrType;

        // Enumerate Recordset and print from each.
        while(!(pRstTemp->EndOfFile))
            {
            // Convert variant string to convertable string type.
            bstrTitle = pRstTemp->Fields->GetItem("Title")->Value;
            bstrType  = pRstTemp->Fields->GetItem("Type")->Value;
            printf("\t%s, %s \n",
                (LPCSTR) bstrTitle,
                (LPCSTR) bstrType);

            pRstTemp->MoveNext();
        }
    }
}

///////////////////////////////////////////////
//      PrintProviderError Function          //
///////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr  pErr = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                pErr->Description);
        }
    }
}

//////////////////////////////////////
//      PrintComError Function      //
//////////////////////////////////////

void PrintComError(_com_error &e)
{
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndExecuteCpp

See Also

Clear Method | Command Object | Connection Object | Errors Collection | Execute Method (ADO Command) | Execute Method (ADO Connection) | Requery Method


Filter and RecordCount Properties Example (VC++)

This example uses the Filter property to open a new Recordset based on a specified condition applied to an existing Recordset. It uses the RecordCount property to show the number of records in the two Recordsets. The FilterField function is required for this procedure to run.

// BeginFilterCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>
#include <ole2.h>
#include <conio.h>
#include "FilterX.h"

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void FilterX(void);
_RecordsetPtr FilterField(_RecordsetPtr rstTemp, _bstr_t strField, _bstr_t strFilter);
void FilterX2(void);
void PrintProviderError(_ConnectionPtr pCnn1);
void PrintComError(_com_error &e);

void main()
{
    HRESULT hr = S_OK;

    if(FAILED(::CoInitialize(NULL)))
        return;

    FilterX();

    //Wait here for user to see the output..
    printf("\nPress any key to Continue...");
    getch();

    //Clear the screen for the next display
    system("cls");

    FilterX2();

    //Wait here for user to see the output..
    printf("\nPress any key to Exit...");
    getch();
    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      FilterX Function                                 //
//                                                       //
///////////////////////////////////////////////////////////

void  FilterX()
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr rstPublishers = NULL;
    _RecordsetPtr rstPublishersCountry = NULL;

    //Define Other Variables
    HRESULT hr = S_OK;
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");
    int intPublisherCount = 0;
    long recCount = 0;
    _bstr_t strCountry;
    _bstr_t strMessage;
    char *tempStr;
    CHAR sz_CountryName[50];
    bool boolFlag = TRUE;
    try
    {
        // Open recordset with data from Publishers table.
        rstPublishers.CreateInstance(__uuidof(Recordset));
        rstPublishersCountry.CreateInstance(__uuidof(Recordset));

        rstPublishers->CursorType = adOpenStatic;

        TESTHR(   rstPublishers->Open("publishers",strCnn,
             adOpenStatic , adLockReadOnly,adCmdTable));

        // Populate the Recordset.
        intPublisherCount = rstPublishers->RecordCount;

        // Get user input.
        printf("Enter a country to filter on:");
        gets(sz_CountryName);

        // Trim the string
        tempStr = strtok(sz_CountryName, "  \t");
        strCountry = tempStr;
        if (tempStr == NULL)
        {
            boolFlag = FALSE;
        }

        if (boolFlag)
        {
            if (strcmp(sz_CountryName,""))
            {
                // Open a filtered Recordset object.
                rstPublishersCountry = FilterField(rstPublishers,
                    "Country", strCountry);
                recCount = rstPublishersCountry->GetRecordCount();
                if (recCount==0)
                {
                    printf("\nNo publishers from that country.\n");
                }
                else
                {
                    // Print number of records for the original
                    // Recordset object and the filtered Recordset
                    // object.
                    printf("\nOrders in original recordset:\n%d",
                        intPublisherCount);
                    printf("\nOrders in filtered recordset "
                        "(Country = '%s'): \n%d\n\n", (LPCSTR)strCountry ,
                        rstPublishersCountry->RecordCount);
                }
                rstPublishersCountry->Close();
            }
        }
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _variant_t vtConnect = rstPublishers->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

_RecordsetPtr FilterField(_RecordsetPtr rstTemp,_bstr_t strField,
    _bstr_t strFilter)
{
        // Set a filter on the specified Recordset object and then
        // open a new Recordset object.
        rstTemp->Filter  = strField + " = '" + strFilter + "'";
        return rstTemp;
}

void FilterX2(void)
{
    _RecordsetPtr rstPublishers;
    CPublishers publishers;

    //Define Other Variables
    HRESULT hr = S_OK;
    IADORecordBinding   *picRs = NULL;  //Interface Pointer declared...

    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");
    try
    {
        // Open recordset with data from Publishers table.
        rstPublishers.CreateInstance(__uuidof(Recordset));

        rstPublishers->CursorType = adOpenStatic;

        TESTHR(rstPublishers->Open("SELECT * FROM publishers WHERE "
            "Country='USA'",strCnn,adOpenStatic,adLockReadOnly,
            adCmdText));

        //Open an IADORecordBinding interface pointer
        //which we'll use for Binding Recordset to a class
        TESTHR(rstPublishers->QueryInterface(
            __uuidof(IADORecordBinding), (LPVOID*)&picRs));

        //Bind the Recordset to a C++ Class here
        TESTHR(picRs->BindToRecordset(&publishers));

        // Print current data in recordset.
        rstPublishers->MoveFirst();

        while (!rstPublishers->EndOfFile)
        {
            printf("%s, %s\n",
                publishers.lP_pubnameStatus == adFldOK ?
                publishers.m_szP_pubname: "<NULL>",
                publishers.lP_countryStatus == adFldOK ?
                publishers.m_szP_country: "<NULL>");

            rstPublishers->MoveNext();
        }
        //Release the IADORecordset Interface.
        if (picRs)
            picRs->Release();

        // Clean up object before exit.
        rstPublishers->Close();
    }

    catch (_com_error &e)
    {
        // Notify the user of errors if any.
        _variant_t vtConnect = rstPublishers->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pCnn1)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr  pErr  = NULL;

    if( (pCnn1->Errors->Count) > 0)
    {
        long nCount = pCnn1->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pCnn1->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndFilterCpp

FilterX.h:

// BeginFilterH
#include "icrsint.h"

//This Class extracts only Pub Name and Country Name.
class CPublishers : public CADORecordBinding
{
BEGIN_ADO_BINDING(CPublishers)

    //Column Pub Name is the 2nd field in the recordset
    ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szP_pubname,
         sizeof(m_szP_pubname), lP_pubnameStatus, TRUE)

    //Column Country Name is the 5th field in the recordset
    ADO_VARIABLE_LENGTH_ENTRY2(5, adVarChar, m_szP_country ,
        sizeof(m_szP_country), lP_countryStatus, TRUE)

END_ADO_BINDING()

public:
    CHAR    m_szP_pubname[50];
    ULONG   lP_pubnameStatus;
    CHAR    m_szP_country[50];
    ULONG   lP_countryStatus;
};
// EndFilterH

See Also

Filter Property | RecordCount Property | Recordset Object


Find Method Example (VC++)

This example uses the Recordset object's Find method to locate and count the number of business titles in the Pubs database. The example assumes the underlying provider does not support similar functionality.

// BeginFindCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>
#include "FindX.h"

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void FindX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//      Main Function                                   //
//                                                      //
//////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    FindX();

    //Wait here for the user to see the output.
    printf("Press any key to continue...");
    getch();
    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//             FindX Function                           //
//                                                      //
//////////////////////////////////////////////////////////

void FindX(void)
{
    HRESULT    hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _ConnectionPtr    pConnection  = NULL;
    _RecordsetPtr     pRstTitles   = NULL;
    IADORecordBinding   *picRs     = NULL;  //Interface Pointer declared.
    CTitlesRs titlers;                      //C++ class object

    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        // Open connection.
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
        pConnection->Open (strCnn, "", "", adConnectUnspecified);

        // Open title Table
        TESTHR(pRstTitles.CreateInstance(__uuidof(Recordset)));

        pRstTitles->Open("SELECT title_id FROM titles",
            _variant_t((IDispatch *)pConnection),
            adOpenStatic, adLockReadOnly, adCmdText);

        // The default parameters are sufficient to search forward
        // through a Recordset.

        pRstTitles->Find ("title_id LIKE 'BU%'",0,adSearchForward,"");

        //Open an IADORecordBinding interface pointer which
        //we'll use for Binding Recordset to a class
        TESTHR(pRstTitles->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        //Bind the Recordset to a C++ Class here
        TESTHR(picRs->BindToRecordset(&titlers));

        // Skip the current record to avoid finding the same
        // row repeatedly. The bookmark is redundant because Find
        // searches from the current position.
        int count = 0;

        //Continue if last find succeeded.
        while (!(pRstTitles->EndOfFile))
        {
            printf("Title ID: %s\n",titlers.lt_titleidStatus == adFldOK ?
                titlers.m_szt_titleid : "<NULL>");
            count++;                       //Count the last title found.

            _variant_t mark = pRstTitles->Bookmark;  //Note current pos.
            pRstTitles->Find("title_id LIKE 'BU%'", 1, adSearchForward,
                mark);
        }

        //Release IADORecordset Interface
        if (picRs)
            picRs->Release();

        // Clean up objects before exit.
        pRstTitles->Close();
        pConnection->Close();
        printf("The number of business titles is %d\n",count);

   }

   catch(_com_error &e)
   {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
       PrintProviderError(pConnection);
       PrintComError(e);
   }
}

//////////////////////////////////////////////////////////
//                                                      //
//       PrintProviderError Function                    //
//                                                      //
//////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                (LPCSTR)pErr->Description);
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//       PrintComError Function                         //
//                                                      //
//////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndFindCpp

FindX.h

// BeginFindH
#include "icrsint.h"

//This Class extracts only titleId from Titles table.
class CTitlesRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CTitlesRs)

    // Column title_id is the 1st field in the recordset
    // from Titles table.
    ADO_VARIABLE_LENGTH_ENTRY2(1, adVarChar, m_szt_titleid,
         sizeof(m_szt_titleid), lt_titleidStatus, FALSE)

END_ADO_BINDING()

public:
   CHAR   m_szt_titleid[150];
   ULONG   lt_titleidStatus;
};
// EndFindH

See Also

Find Method | Recordset Object


GetRows Method Example (VC++)

This example uses the GetRows method to retrieve a specified number of rows from a Recordset and to fill an array with the resulting data. The GetRows method will return less than the desired number of rows in two cases: either if EOF has been reached, or if GetRows tried to retrieve a record that was deleted by another user. The function returns False only if the second case occurs. The GetRowsOK function is required for this procedure to run.

// BeginGetRowsCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>
#include <ole2.h>
#include <conio.h>

// Function Declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void GetRowsX(void);
bool GetRowsOK(_RecordsetPtr pRstTemp,int intNumber,
        _variant_t& avarData);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

     GetRowsX();

    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      GetRowsX Function                                //
//                                                       //
///////////////////////////////////////////////////////////

void GetRowsX(void)
{
    HRESULT hr = S_OK;

    // Define string variables.
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr pRstEmployees = NULL;

    try
    {
        // Open recordset with names and hire dates from employee table.
        TESTHR(pRstEmployees.CreateInstance(__uuidof(Recordset)));

        pRstEmployees->Open("SELECT fName, lName, hire_date "
            "FROM Employee ORDER BY lName",strCnn,
            adOpenStatic, adLockReadOnly,adCmdText);

         while (true) //continuous loop
        {
            int intLines = 0;

            // Get user input for number of rows.
            printf("\nEnter number of rows to retrieve (0 to exit): ");
            int intRows;
            scanf("%d", &intRows);

            if (intRows <= 0)
                break;

            //Clear the screen for the next display
            system("cls");

            // If GetRowsOK is successful, print the results,
            // noting if the end of the file was reached.
            _variant_t avarRecords;

            if (GetRowsOK(pRstEmployees, intRows, avarRecords))
            {
                long lUbound;
                HRESULT hr = SafeArrayGetUBound(avarRecords.parray,
                    2,&lUbound);

                if (hr == 0)
                {
                    if (intRows > lUbound + 1)
                    {
                        printf("\n(Not enough records in Recordset to "
                            "retrieve %d rows)\n", intRows);
                    }
                }
                printf("%d records found.", lUbound+1);

                // Print the retrieved data.
                for (int intRecords = 0;intRecords < lUbound+1;
                     intRecords++)
                {
                    printf("\n ");

                    long rgIndices[2];
                    rgIndices[0] = 0;
                    rgIndices[1] = intRecords;
                    _variant_t result;
                    result.vt = VT_BSTR;

                    hr= SafeArrayGetElement(avarRecords.parray,
                        rgIndices, &result);

                    if (hr == 0){printf("%s ",(LPCSTR)(_bstr_t)result);}

                    rgIndices[0] = 1;

                    hr= SafeArrayGetElement(avarRecords.parray,
                        rgIndices, &result);

                    if (hr == 0){printf("%s, ",(LPCSTR)(_bstr_t)result);}

                    rgIndices[0] = 2;

                    hr= SafeArrayGetElement(avarRecords.parray,
                        rgIndices, &result);

                    if (hr == 0){printf("%s",(LPCSTR)(_bstr_t)result);}

                    intLines ++;

                    if (intLines % 10 == 0)
                    {
                        printf("\nPress any key to continue...");
                        getch();

                        intLines = 0;

                        //Clear the screen for the next display
                        system("cls");
                    }
                }
            }
            else
            {
                // Assuming the GetRows error was due to data
                // changes by another user, use Requery to
                // refresh the Recordset and start over.
                printf("GetRows failed--retry?\n");
                char chKey;
                do
                {
                    chKey = getch();
                }while(toupper(chKey) != 'Y'  && toupper(chKey) != 'N');

                if(toupper(chKey) == 'Y')
                {
                    pRstEmployees->Requery(adOptionUnspecified);
                }
                else
                {
                    printf("GetRows failed!\n");
                    break;
                }
            }

            // Because using GetRows leaves the current
            // record pointer at the last record accessed,
            // move the pointer back to the beginning of the
            // Recordset before looping back for another search.
            pRstEmployees->MoveFirst();
        }
        //Clean up objects before exit.
        pRstEmployees->Close();
    }
    catch(_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstEmployees->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

bool GetRowsOK(_RecordsetPtr pRstTemp,int intNumber,
        _variant_t& avarData)
{
    // Store results of GetRows method in array.
    avarData = pRstTemp->GetRows(intNumber);

    // Return False only if fewer than the desired
    // number of rows were returned, but not because the
    // end of the Recordset was reached.
    long lUbound;
    HRESULT hr = SafeArrayGetUBound(avarData.parray,2,&lUbound);
    if (hr == 0)
    {
        if ((intNumber > lUbound + 1) && (!(pRstTemp->EndOfFile)))
            return false;
        else
            return true;
    }
    else
    {
        printf ("\nUnable to Get the Array's Upper Bound\n");
        return false;
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr  pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndGetRowsCpp

See Also

BOF, EOF Properties | GetRows Method | Recordset Object


GetString Method Example (VC++)

This example demonstrates the GetString method.

Assume you are debugging a data access problem and want a quick, simple way of printing the current contents of a small Recordset.

// BeginGetStringCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void GetStringX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//      Main Function                                   //
//                                                      //
//////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    GetStringX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//          GetStringX Function                         //
//                                                      //
//////////////////////////////////////////////////////////

void GetStringX(void)
{
    HRESULT  hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
   _ConnectionPtr  pConnection   = NULL;
   _RecordsetPtr   pRstAuthors   = NULL;

    // Define string variables.
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=foo;");
    _bstr_t varOutput;
    char *strPrompt = "Enter a state (CA, IN, KS, MD, MI, OR, TN, UT): ";
    char strState[2], *pState;

    try
    {
       // Open connection.
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));

        // Open a command object for a stored procedure,
        // with one parameter.
        TESTHR(pRstAuthors.CreateInstance(__uuidof(Recordset)));

        printf("%s",strPrompt);
        gets(strState);

        pState = strtok(strState," \t");

        char strQry[100] = "SELECT au_fname, au_lname, address, city "
            "FROM authors WHERE state = ";

        strcat(strQry,"'");
        strcat(strQry,pState);
        strcat(strQry,"'");

        _bstr_t strQuery(strQry);

        pConnection->Open (strCnn, "", "", adConnectUnspecified);

        pRstAuthors->Open(strQuery, _variant_t((IDispatch*)pConnection,
            true),
        adOpenStatic, adLockReadOnly, adCmdText);

        if (pRstAuthors->RecordCount > 0)
        {
            // Use all defaults: get all rows, TAB column delimiter,
            // CARRIAGE RETURN row delimiter, empty-string null delimiter
            long lRecCount =  pRstAuthors->RecordCount;
            _bstr_t varTab("\t");
            _bstr_t varRet("\r");
            _bstr_t varNull("");
            varOutput = pRstAuthors->GetString(adClipString,lRecCount,
                varTab,varRet,varNull);
            printf("State = '%s'\n", strState);
            printf ("Name               Address               City\n");
            printf ("%s\n", (LPCTSTR)varOutput);
        }
        else
        {
            printf("\nNo rows found for state = '%s'\n",pState);
        }
        // Clean up objects before exit.
        pRstAuthors->Close();
        pConnection->Close();
   }

   catch(_com_error &e)
   {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
       PrintProviderError(pConnection);
       PrintComError(e);
   }
}

//////////////////////////////////////////////////////////
//                                                      //
//        PrintProviderError Function                   //
//                                                      //
//////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr   pErr = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                pErr->Description);
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//      PrintComError Function                          //
//                                                      //
//////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);

}
// EndGetStringCpp

See Also

GetString Method | Recordset Object


IsolationLevel and Mode Properties Example (VC++)

This example uses the Mode property to open an exclusive connection, and the IsolationLevel property to open a transaction that is conducted in isolation of other transactions.

// BeginIsolationLevelCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll"\
    no_namespace rename("EOF","EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>
#include "IsolationLevelX.h"

// Function Declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void IsolationLevelX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

/////////////////////////////////////////////////////////////////
//                                                             //
//                                                             //
//                        Main Function                        //
//                                                             //
//                                                             //
/////////////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    IsolationLevelX();

    printf("Press any key to continue...");
    getch();
    ::CoUninitialize();
}

/////////////////////////////////////////////////////////////////
//                                                             //
//      IsolationLevelX()  Function                            //
//                                                             //
/////////////////////////////////////////////////////////////////

void IsolationLevelX(void)
{
    // Define ADO ObjectPointers
    // Initialize Pointers on define
    // These are in the ADODB :: namespace
    _RecordsetPtr  pRstTitles  = NULL;
    _ConnectionPtr pConnection = NULL;

    // Define other Variables
    HRESULT hr = S_OK;
    IADORecordBinding *picRs  = NULL;  // Interface Pointer Declared
    CTitleRs titlers;                  // C++ Class Object

    //Assign Connection String to Variable
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
                    "Initial Catalog=pubs;User Id=sa;Password=foo;");

    try
    {
        // Open Connection and Titles Table
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
        pConnection->Mode = adModeShareExclusive;
        pConnection->IsolationLevel = adXactIsolated;
        pConnection->Open(strCnn,"","",adConnectUnspecified);

        TESTHR(pRstTitles.CreateInstance(__uuidof(Recordset)));
        pRstTitles->CursorType = adOpenDynamic;
        pRstTitles->LockType = adLockPessimistic;

        pRstTitles->Open("titles",_variant_t((IDispatch*) pConnection,
            true),adOpenDynamic,adLockPessimistic,adCmdTable);

        pConnection->BeginTrans();

        // Display Connection Mode
        if(pConnection->Mode == adModeShareExclusive)
        {
            printf("Connection Mode Is Exclusive");
        }
        else
        {
            printf("Connection Mode Is Not Exclusive");
        }

        // Display Isolation Level
        if(pConnection->IsolationLevel == adXactIsolated)
        {
            printf("\n\nTransaction is Isolated");
            printf("\n\nPress any key to continue...\n\n");
            getch();
        }
        else
        {
            printf("\n\nTransaction is not Isolated");
            printf("\n\nPress any key to continue...\n\n");
            getch();
        }

        // Open an IADORecordBinding interface pointer which
        // we will use for binding Recordset to a class
        TESTHR(pRstTitles->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        // Bind the Recordset to a C++ class here
        TESTHR(picRs->BindToRecordset(&titlers));

        // Change the type of psychology titles.
        LPSTR p_TempStr = (LPSTR) malloc(sizeof(titlers.m_szau_Type));

        while (!(pRstTitles->EndOfFile))
        {
            // Remove blank string
            strcpy(p_TempStr,strtok(titlers.m_szau_Type," "));

            // Compare type with psychology
            if (!strcmp(p_TempStr,"psychology"))
            {
                    // Copy "self_help" title field
                    strcpy(titlers.m_szau_Type,"self_help");
                    picRs->Update(&titlers);
            }
            pRstTitles->MoveNext();
        }
        // Print current data in recordset.
        pRstTitles->Requery(adOptionUnspecified);

        // Open again IADORecordBinding interface pointer for Binding
        // Recordset to a class.
        TESTHR(pRstTitles->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        // ReBinding the Recordset to a C++ Class
        TESTHR(picRs->BindToRecordset(&titlers));

        // Move to the first record of the title table
        pRstTitles->MoveFirst();

        //Clear the screen for the next display
        system("cls");

        while (!pRstTitles->EndOfFile)
        {
            printf("%s -  %s\n",titlers.lau_TitleStatus == adFldOK ?
                titlers.m_szau_Title :"<NULL>",
                titlers.lau_TypeStatus == adFldOK ?
                titlers.m_szau_Type :"<NULL>");
            pRstTitles->MoveNext();
        }

        // Restore Original Data
        pConnection->RollbackTrans();

        // Deallocate the memory
        free(p_TempStr);
        pRstTitles->Close();

        // Release the IADORecordset Interface here
        if(picRs)
            picRs->Release();
        pConnection->Close();
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        PrintProviderError(pConnection);
        PrintComError(e);
    }
}

/////////////////////////////////////////////////////////////////
//                                                             //
//                  PrintProviderError Function                //
//                                                             //
/////////////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    //Print Provider Errors from Connection object
    //pErr is a record object in the Connection's Error collection
    ErrorPtr    pErr = NULL;

    if((pConnection->Errors->Count)>0)
    {
        long nCount = pConnection->Errors->Count;

        //Collection ranges from 0 to nCount-1
        for(long i = 0;i < nCount;i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error Number :%x \t %s",pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndIsolationLevelCpp

IsolationLevelX.h:

// BeginIsolationLevelH

#include "icrsint.h"

//This class extracts titles and type from Title table

class CTitleRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CTitleRs)
        // Column title is the 2nd field in the table
        ADO_VARIABLE_LENGTH_ENTRY2(2,adVarChar,m_szau_Title,
            sizeof(m_szau_Title),lau_TitleStatus,FALSE)
        // Column type is the 3rd field in the table
        ADO_VARIABLE_LENGTH_ENTRY2(3,adVarChar,m_szau_Type,
            sizeof(m_szau_Type),lau_TypeStatus,TRUE)
END_ADO_BINDING()

public:
    CHAR  m_szau_Title[81];
    ULONG lau_TitleStatus;
    CHAR  m_szau_Type[13];
    ULONG lau_TypeStatus;
};
// EndIsolationLevelH

See Also

IsolationLevel Property | Mode Property


Item Property Example (VC++)

This example demonstrates how the Item property accesses members of a collection. The example opens the Authors table of the Pubs database with a parameterized command.

The parameter in the command issued against the database is accessed from the Command object's Parameters collection by index and name. Then the fields of the returned Recordset are accessed from that object's Fields collection by index and name.

// BeginItemCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void ItemX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//       Main Function                                  //
//                                                      //
//////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

        ItemX();

     //Wait here for the user to see the output
     printf("Press any key to continue...");
     getch();
    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//       ItemX Function                                 //
//                                                      //
//////////////////////////////////////////////////////////

void ItemX(void)
{
    HRESULT  hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
   _ConnectionPtr  pConnection  = NULL;
   _RecordsetPtr   pRst     = NULL;
   _CommandPtr     pCmd     = NULL;
   _ParameterPtr   pPrm     = NULL;
    FieldPtr       pFld     = NULL;

    // Other Variables.
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");
    _variant_t    Column[9];
    _variant_t    vIndex;

   try
    {
       // Open connection.
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));

        TESTHR(pRst.CreateInstance(__uuidof(Recordset)));

        TESTHR(pCmd.CreateInstance(__uuidof(Command)));

        //Set the array with the authors table field (column) names
        Column[0] = "au_id";
        Column[1] = "au_lname";
        Column[2] = "au_fname";
        Column[3] = "phone";
        Column[4] = "address";
        Column[5] = "city";
        Column[6] = "state";
        Column[7] = "zip";
        Column[8] = "contract";

        _bstr_t strText("SELECT * FROM authors WHERE state = ?");
        pCmd->CommandText = strText;

        pPrm = pCmd->CreateParameter("ItemXparm", adChar, adParamInput,
            2, "CA");
        pCmd->Parameters->Append(pPrm);

        pConnection->Open(strCnn, "", "", adConnectUnspecified);
        pCmd->ActiveConnection = pConnection;

        // Connection and CommandType are omitted because
        // a Command object is provided.
        _variant_t Conn;
        Conn.vt = VT_ERROR;
        Conn.scode = DISP_E_PARAMNOTFOUND;

        pRst->Open((_variant_t((IDispatch *) pCmd)),Conn,adOpenStatic,
            adLockReadOnly, -1);

        printf("The Parameters collection accessed by index...\n");

        vIndex = (short) 0;
        pPrm = pCmd->Parameters->GetItem(&vIndex);
        printf("Parameter name = '%s', value = '%s'\n",
            (LPCSTR)pPrm->Name, (LPSTR)(_bstr_t)pPrm->Value);

        printf("\nThe Parameters collection accessed by name...\n");
        pPrm = pCmd->Parameters->Item["ItemXparm"];
        printf("Parameter name = '%s', value = '%s'\n",
            (LPCSTR)pPrm->Name, (LPSTR)(_bstr_t)pPrm->Value);

        printf("\nThe Fields collection accessed by index...\n");
        pRst->MoveFirst();
        long limit = 0;
        limit = ((pRst->Fields->Count) - 1);
        int intLineCnt = 8;
        for (short iIndex = 0; iIndex <= limit; iIndex++)
        {
            vIndex = iIndex;
            intLineCnt++;
            if (intLineCnt%15 == 0)
            {
                printf("\nPress any key to continue...\n");
                getch();
            }
            pFld = pRst->Fields->GetItem(&vIndex);
            printf("Field %d : Name =  '%s', ", iIndex,
                (LPCSTR)pFld->GetName());
            _variant_t FldVal = pFld->GetValue();

            // Because Value is the default property of a
            // Property object,the use of the actual keyword
            // here is optional.
            switch(FldVal.vt)
            {
                case (VT_BOOL):
                    if(FldVal.boolVal)
                    {
                        printf("Value = 'True'");
                    }
                    else
                    {
                        printf("Value = 'False'");
                    }
                    printf("\n");
                    break;
                case (VT_BSTR):
                    printf("Value = '%s'",
                        (LPCSTR)(_bstr_t)FldVal.bstrVal);
                    printf("\n");
                    break;
                case (VT_I4):
                    printf("Value = '%s'",(LPCSTR)FldVal.iVal);
                    printf("\n");
                    break;
                case (VT_EMPTY):
                    printf("Value = '%s'",(LPCSTR)FldVal.lVal);
                    printf("\n");
                    break;
                default:
                    break;
            }
        }

        printf("\nThe Fields collection accessed by name...\n");
        pRst->MoveFirst();
        for (iIndex = 0; iIndex <= 8; iIndex++)
        {
            intLineCnt++;
            if (intLineCnt%15 == 0)
            {
                printf("\nPress any key to continue...\n");
                getch();
            }
            pFld = pRst->Fields->GetItem(Column[iIndex]);
            printf("Field name = '%s', ",(LPCSTR)pFld->GetName());
            _variant_t FldVal = pFld->GetValue();

            // Because Value is the default property of a
            // Property object,the use of the actual keyword
            // here is optional.
            switch(FldVal.vt)
            {
                case (VT_BOOL):
                    if(FldVal.boolVal)
                    {
                        printf("Value = 'True'");
                    }
                    else
                    {
                        printf("Value = 'False'");
                    }
                    printf("\n");
                    break;
                case (VT_BSTR):
                    printf("Value = '%s'",
                        (LPCSTR)(_bstr_t)FldVal.bstrVal);
                    printf("\n");
                    break;
                case (VT_I4):
                    printf("Value = '%s'",(LPCSTR)FldVal.iVal);
                    printf("\n");
                    break;
                case (VT_EMPTY):
                    printf("Value = '%s'",(LPCSTR)FldVal.lVal);
                    printf("\n");
                    break;
                default:
                    break;
            }
        }
        // Clean up objects before exit.
        pRst->Close();
        pConnection->Close();
   }

   catch(_com_error &e)
   {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
       PrintProviderError(pConnection);
       PrintComError(e);
   }
}

//////////////////////////////////////////////////////////
//                                                      //
//      PrintProviderError Function                     //
//                                                      //
//////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                pErr->Description);
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//       PrintComError Function                         //
//                                                      //
//////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndItemCpp

See Also

Command Object | Fields Collection | Item Property | Parameters Collection | Recordset Object


MarshalOptions Property Example (VC++)

This example uses the MarshalOptions property to specify what rows are sent back to the server—All Rows or only Modified Rows.

// BeginMarshalOptionsCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>
#include <ole2.h>
#include <conio.h>
#include <malloc.h>
#include "MarshalOptionsX.h"

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void MarshalOptionsX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//               Main Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

      MarshalOptionsX();

    printf("Press any key to continue...");
    getch();
    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      Marshal Options Function                         //
//                                                       //
///////////////////////////////////////////////////////////

void   MarshalOptionsX(void)
{

    // Define string variables
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace
    _RecordsetPtr pRstEmployees = NULL;

    // Define Other Variables
    IADORecordBinding   *picRs  = NULL;  //Interface Pointer declared
    CEmployeeRs emprs;               //C++ Class Object
    HRESULT hr = S_OK;

    try
    {
        // Open recordset with names from Employee table.
        TESTHR(pRstEmployees.CreateInstance(__uuidof(Recordset)));
        pRstEmployees->CursorType = adOpenKeyset;
        pRstEmployees->LockType = adLockOptimistic;
        pRstEmployees->CursorLocation = adUseClient;
        pRstEmployees->Open("SELECT fname, lname FROM Employee "
            "ORDER BY lname",strCnn,
            adOpenKeyset, adLockOptimistic,adCmdText);

        // Open an IADORecordBinding interface pointer which
        // we'll use for Binding Recordset to a class.
        TESTHR(pRstEmployees->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        // Bind the Recordset to a C++ Class here.
        TESTHR(picRs->BindToRecordset(&emprs));

        // Store Original Data
        LPSTR strOldFirst = (LPSTR) malloc(sizeof(emprs.m_szemp_fname));
        LPSTR strOldLast  = (LPSTR) malloc(sizeof(emprs.m_szemp_lname));
        strcpy(strOldFirst,strtok(emprs.m_szemp_fname," "));
        strcpy(strOldLast,strtok(emprs.m_szemp_lname," "));

        //Change Data in Edit Buffer
        strcpy(emprs.m_szemp_fname,"Linda");
        strcpy(emprs.m_szemp_lname,"Kobara");

        // Show contents of buffer and get user input
        printf("Edit in Progress:\n");
        printf("Original Data = %s  %s \n",strOldFirst,strOldLast);
        printf("Data in buffer = %s  %s \n", emprs.lemp_fnameStatus ==
            adFldOK ? emprs.m_szemp_fname : "<NULL>",
            emprs.lemp_lnameStatus == adFldOK ?
            emprs.m_szemp_lname : "<NULL>");
        printf("Use Update to replace the original data with the "
            "buffered data in the Recordset?\nEnter (y/n) :?");
        char opt1=getch();

        if(toupper(opt1)=='Y')
        {
                printf("\nWould you like to send all the rows in "
                    "the recordset back to the server?\nEnter (y/n):");
                char opt2 = getch();
                if(toupper(opt2) == 'Y')
                {
                    {
                        pRstEmployees->MarshalOptions = adMarshalAll;
                        picRs->Update(&emprs);
                    }
                }
                else
                {
                    printf("\nWould you like to send only modified "
                        "rows back to the server?\nEnter (y/n):");
                    char opt3=getch();
                    if(toupper(opt3) == 'Y')
                        {
                            pRstEmployees->MarshalOptions =
                                adMarshalModifiedOnly;
                            picRs->Update(&emprs);
                        }
                }
        }
        // Show the resulting data
        printf("\nData In the Recordset = %s  %s\n",
            emprs.lemp_fnameStatus == adFldOK ?
            emprs.m_szemp_fname : "<NULL>",
            emprs.lemp_lnameStatus == adFldOK ?
            emprs.m_szemp_lname : "<NULL>");

        // Restore original data because this is a demonstration
        if((strcmp(strOldFirst,emprs.m_szemp_fname)) &&
           (strcmp(strOldLast,emprs.m_szemp_lname)))
        {
            strcpy(emprs.m_szemp_fname,strOldFirst);
            strcpy(emprs.m_szemp_lname,strOldLast);
            picRs->Update(&emprs);
        }
        // Deallocate memory
        free(strOldFirst);
        free(strOldLast);

        // Release IADORecordset Interface
        if (picRs)
            picRs->Release();

        // Clean up objects before exit.
        pRstEmployees->Close();
    }

    catch(_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstEmployees->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr     pErr   = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());

     // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndMarshalOptionsCpp

MarshalOptionsX.h:

// BeginMarshalOptionsH

#include "icrsint.h"

//This Class extracts only fname,lname from employees table
class CEmployeeRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CEmployeeRs)

    //Column fname is the 1st field in the recordset
    ADO_VARIABLE_LENGTH_ENTRY2(1, adVarChar, m_szemp_fname,
         sizeof(m_szemp_fname), lemp_fnameStatus, TRUE)

     //Column lname is the 2nd field in the recordset
    ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szemp_lname,
         sizeof(m_szemp_lname), lemp_lnameStatus, TRUE)

END_ADO_BINDING()

public:
   CHAR   m_szemp_fname[21];
   ULONG   lemp_fnameStatus;
   CHAR   m_szemp_lname[31];
   ULONG   lemp_lnameStatus;
};
// EndMarshalOptionsH

See Also

MarshalOptions Property


MaxRecords Property Example (VC++)

This example uses the MaxRecords property to open a Recordset containing the 10 most expensive titles in the Titles table.

// BeginMaxRecordsCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll"\
    no_namespace rename("EOF","EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>
#include "MaxRecordsX.h"

// Function Declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void MaxRecordsX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////////
//                                                          //
//                        Main Function                     //
//                                                          //
//////////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    MaxRecordsX();

    printf("Press any key to continue...");
    getch();
    ::CoUninitialize();
}

//   MaxRecordsX()  Function
void  MaxRecordsX(void)
{
    // Define ADO ObjectPointers
    // Initialize Pointers on define
    // These are in the ADODB :: namespace
    _RecordsetPtr pRstTemp    = NULL;

    // Define Other Variables
    IADORecordBinding *picRs  = NULL;   // Interface Pointer Declared
    CTitleRs titlers;            // C++ Class Object
    HRESULT hr = S_OK;

    try
    {
        //Assign Connection String to Variable
        _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
                    "Initial Catalog=pubs;User Id=sa;Password=;");

        // Open Recordset containing the 10 most expensive titles in the
        // Titles table.
        TESTHR(pRstTemp.CreateInstance(__uuidof(Recordset)));

        pRstTemp->MaxRecords=10;

        pRstTemp->Open("SELECT title,price FROM Titles "
            "ORDER BY Price DESC",strCnn,adOpenForwardOnly,
            adLockReadOnly,adCmdText);

        // Open an IADORecordBinding interface pointer which
        // we will use for binding Recordset to a class
        TESTHR(pRstTemp->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        // Bind the Recordset to a C++ class here
        TESTHR(picRs->BindToRecordset(&titlers));

        // Display the contents of the Recordset
        printf("Top Ten Titles by Price:\n\n");

        while(!(pRstTemp->EndOfFile))
        {
            printf("%s ---  %6.2lf\n\n",titlers.lau_TitleStatus ==
                adFldOK ? titlers.m_szau_Title : "<NULL>",
                    titlers.lau_PriceStatus == adFldOK ?
                    titlers.m_szau_Price : 0.00);
            pRstTemp->MoveNext();
        }
        // Clean up objects before exit.
        pRstTemp->Close();
        if(picRs)
            picRs->Release();
    }
    catch(_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstTemp->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
};

//////////////////////////////////////////////////////////////
//                                                          //
//                  PrintProviderError Function             //
//                                                          //
//////////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    //Print Provider Errors from Connection object
    //pErr is a record object in the Connection's Error collection
    ErrorPtr    pErr = NULL;

    if((pConnection->Errors->Count)>0)
    {
        long nCount = pConnection->Errors->Count;

        //Collection ranges from 0 to nCount-1
        for(long i = 0;i < nCount;i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error Number :%x \t %s",pErr->Number,
                pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndMaxRecordsCpp

MaxRecordsX.h:

// BeginMaxRecordsH
// MaxRecordsX.h

#include "icrsint.h"

// This class extracts titles and type from the titles table

class CTitleRs : public CADORecordBinding
{
    BEGIN_ADO_BINDING(CTitleRs)
        // Column title is the 1st field in the Recordset
        ADO_VARIABLE_LENGTH_ENTRY2(1,adVarChar,m_szau_Title,
            sizeof(m_szau_Title),lau_TitleStatus,FALSE)
        // Column price is the 2nd field in the Recordset
        ADO_VARIABLE_LENGTH_ENTRY2(2,adDouble,m_szau_Price,
            sizeof(m_szau_Price),lau_PriceStatus,FALSE)
END_ADO_BINDING()

public:
    CHAR m_szau_Title[81];
    ULONG lau_TitleStatus;
    DOUBLE m_szau_Price;
    ULONG lau_PriceStatus;
};

// EndMaxRecordsH

See Also

MaxRecords Property | Recordset Object


Move Method Example (VC++)

This example uses the Move method to position the record pointer based on user input.

// BeginMoveCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>
#include <ole2.h>
#include <stdlib.h>
#include <conio.h>
#include "MoveX.h"

// Function Declaration.
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void MoveX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

     MoveX();

     ::CoUninitialize();
}

//////////////////////////////////////////////////
//                                              //
//      MoveX Function                          //
//                                              //
//////////////////////////////////////////////////

void MoveX(void)
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr  pRstAuthors    = NULL;

    // Define Other Variables
    IADORecordBinding   *picRs = NULL;  //Interface Pointer declared
    CAuthorsRs    authorsrs;         //C++ class object
    HRESULT hr = S_OK;

    // Open Authors table
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        // Open recordset from Authors table.
        TESTHR(pRstAuthors.CreateInstance(__uuidof(Recordset)));

        pRstAuthors->CursorType = adOpenStatic;
        // Use client cursor to allow use of AbsolutePosition property.
        pRstAuthors->CursorLocation = adUseClient;

        pRstAuthors->Open("SELECT au_id, au_fname, au_lname, city, "
            "state FROM Authors ORDER BY au_lname", strCnn, adOpenStatic,
            adLockReadOnly, adCmdText);

        // Open an IADORecordBinding interface pointer which we'll use
        // for Binding Recordset to a class.
        TESTHR(pRstAuthors->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        // Bind the Recordset to a C++ Class here.
        TESTHR(picRs->BindToRecordset(&authorsrs));

        pRstAuthors->MoveFirst();

        while(true)
        {
            // Display information about current record and ask how many
            // records to move.
            printf("Record %ld of %d\n", pRstAuthors->AbsolutePosition,
                pRstAuthors->RecordCount);
            printf("Author: %s %s\n",
                authorsrs.lemp_fnameStatus == adFldOK ?
                authorsrs.m_au_fname : "<NULL>",
                authorsrs.lemp_lnameStatus == adFldOK ?
                authorsrs.m_au_lname : "<NULL>");
            printf("Location: %s, %s\n\n",
                authorsrs.lemp_cityStatus == adFldOK ?
                authorsrs.m_au_city : "<NULL>",
                authorsrs.lemp_stateStatus == adFldOK ?
                authorsrs.m_au_state : "<NULL>");

            char *strMove;
            char strTemp[5];
            printf("Enter number of records to Move "
                "\n(positive or negative, Enter to quit): ");
            gets(strTemp);

            strMove = strtok(strTemp," ");

            if (strMove == NULL)
                break;

            // if the input is not numeric then notify the user.
            if(!atol(strMove))
            {
                printf("Expecting numeric value...\n");
                continue;
            }

            // Store bookmark in case the Move goes too far
            // forward or backward.
            _variant_t varBookmark = pRstAuthors->Bookmark;

            // Move method requires parameter of data type Long.
            long lngMove = atol(strMove);

            pRstAuthors->Move(lngMove);

            // Trap for BOF or EOF.
            if(pRstAuthors->BOF)
            {
                printf("Too far backward! Returning to current"
                    " record.\n");
                pRstAuthors->Bookmark = varBookmark;
            }

            if(pRstAuthors->EndOfFile)
            {
                printf("Too far forward! Returning to current"
                    " record.\n");
                pRstAuthors->Bookmark = varBookmark;
            }
        }
        // Clean up objects before exit.
        pRstAuthors->Close();
        // Release the IADORecordset Interface here.
        if (picRs)
            picRs->Release();
    }

    catch(_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstAuthors->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr  pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                 (LPCSTR) pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndMoveCpp

MoveX.h:

// BeginMoveH
// MoveX.h

#include "icrsint.h"

// This Class extracts fname, lname, city and state
// from the "authors" table.

class CAuthorsRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CAuthorsRs)

    // Column au_id is the 1st field in the recordset

    ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_au_fname,
         sizeof(m_au_fname), lemp_fnameStatus, TRUE)

    ADO_VARIABLE_LENGTH_ENTRY2(3, adVarChar, m_au_lname,
         sizeof(m_au_lname), lemp_lnameStatus, TRUE)

    ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_au_city,
         sizeof(m_au_city), lemp_cityStatus, TRUE)

    ADO_VARIABLE_LENGTH_ENTRY2(5, adChar, m_au_state,
         sizeof(m_au_state), lemp_stateStatus, TRUE)

END_ADO_BINDING()

public:
    char     m_au_fname[21];
    ULONG    lemp_fnameStatus;
    char     m_au_lname[41];
    ULONG    lemp_lnameStatus;
    char     m_au_city[21];
    ULONG    lemp_cityStatus;
    char     m_au_state[3];
    ULONG    lemp_stateStatus;
};

// EndMoveH

See Also

Move Method


MoveFirst, MoveLast, MoveNext, and MovePrevious Methods Example (VC++)

This example uses the MoveFirst, MoveLast, MoveNext, and MovePrevious methods to move the record pointer of a Recordset based on the supplied command. The MoveAny function is required for this example to run.

// BeginMoveFirstCpp
#include <ole2.h>
#include <stdio.h>

#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void MoveFirstX();
void MoveAny(int intChoice, _RecordsetPtr pRstTemp);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

/////////////////////////////////
//      Main Function          //
/////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    MoveFirstX();

    ::CoUninitialize();
}

//////////////////////////////////////
//      MoveFirstX Function         //
//////////////////////////////////////

void MoveFirstX()
{
    HRESULT    hr = S_OK;
    _RecordsetPtr    pRstAuthors  = NULL;
    _bstr_t     strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");
    _bstr_t strMessage("UPDATE Titles SET Type = "
            "'psychology' WHERE Type = 'self_help'");
    int intCommand = 0;

    // Temporary string variable for type conversion for printing.
    _bstr_t  bstrFName;
    _bstr_t  bstrLName;

    try
    {
        // Open recordset from Authors table.
        TESTHR(pRstAuthors.CreateInstance(__uuidof(Recordset)));
        pRstAuthors->CursorType = adOpenStatic;

        // Use client cursor to enable AbsolutePosition property.
        pRstAuthors->CursorLocation = adUseClient;
        pRstAuthors->Open("Authors", strCnn, adOpenStatic,
            adLockBatchOptimistic, adCmdTable);

        // Show current record information and get user's method choice.
        while (true)  // Continuous loop.
        {
            // Convert variant string to convertable string type.
            bstrFName = pRstAuthors->Fields->Item["au_fName"]->Value;
            bstrLName = pRstAuthors->Fields->Item["au_lName"]->Value;

            printf("Name: %s %s\n Record %d of %d\n\n",
                (LPCSTR) bstrFName,
                (LPCSTR) bstrLName,
                pRstAuthors->AbsolutePosition,
                pRstAuthors->RecordCount);
            printf("[1 - MoveFirst, 2 - MoveLast, \n");
            printf(" 3 - MoveNext, 4 - MovePrevious, 5 - Quit]\n");

            scanf("%d", &intCommand);

            if ((intCommand < 1) || (intCommand > 4))
                break;   // Out of range entry exits program loop.

            // Call method based on user's input.
            MoveAny(intCommand, pRstAuthors);
        }

        pRstAuthors->Close();
    }
    catch (_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstAuthors->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

/////////////////////////////////
//      MoveAny Function       //
/////////////////////////////////

void MoveAny(int intChoice, _RecordsetPtr pRstTemp)
{
    // Use specified method, trapping for BOF and EOF
    try
    {
        switch(intChoice)
        {
            case 1:
                pRstTemp->MoveFirst();
                break;
            case 2:
                pRstTemp->MoveLast();
                break;
            case 3:
                pRstTemp->MoveNext();
                if (pRstTemp->EndOfFile)
                {
                    printf("\nAlready at end of recordset!\n");
                    pRstTemp->MoveLast();
                }    //End If
                break;
            case 4:
                pRstTemp->MovePrevious();
                if (pRstTemp->BOF)
                {
                    printf("\nAlready at beginning of recordset!\n");
                    pRstTemp->MoveFirst();
                }
                break;
            default:
                ;
        }
    }

    catch(_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstTemp->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

////////////////////////////////////////////
//      PrintProviderError Function       //
////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.

    // pErr is a record object in the Connection's Error collection.
    ErrorPtr  pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount - 1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                pErr->Description);
        }
    }
}

//////////////////////////////////////
//      PrintComError Function      //
//////////////////////////////////////

void PrintComError(_com_error &e)
{
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndMoveFirstCpp

See Also

MoveFirst, MoveLast, MoveNext, and MovePrevious Methods | Recordset Object


NextRecordset Method Example (VC++)

This example uses the NextRecordset method to view the data in a recordset that uses a compound command statement made up of three separate SELECT statements.

// BeginNextRecordsetCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>
#include <ole2.h>
#include <conio.h>
#include <stdlib.h>

//Function Declaration.
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void NextRecordsetX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    NextRecordsetX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////
//                                              //
//      NextRecordsetX Function                 //
//                                              //
//////////////////////////////////////////////////

void NextRecordsetX(void)
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr    pRstCompound = NULL;

    // Define Other Variables
    HRESULT hr = S_OK;
    _variant_t index;
    index.vt = VT_I2;

    // Assign connection string to a variable.
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        // Open recordset from Authors table.
        TESTHR(pRstCompound.CreateInstance(__uuidof(Recordset)));

        // Pass the Cursor type and Lock type to the Recordset.
        pRstCompound->Open("SELECT * FROM authors; SELECT * FROM stores;"
            "SELECT * FROM jobs", strCnn, adOpenForwardOnly,
            adLockReadOnly, adCmdText);

        // Display results from each SELECT statement.
        int intCount = 1;
        while(!(pRstCompound==NULL))
        {
            printf("\n\nContents of recordset #%d\n", intCount);

            while(!pRstCompound->EndOfFile)
            {
                index.iVal = 0;
                printf("%s\t", (LPCSTR)(_bstr_t)pRstCompound->\
                            GetFields()->GetItem(& index)->Value);
                index.iVal = 1;
                printf("%s\n", (LPCSTR)(_bstr_t)pRstCompound->\
                                Fields->GetItem(& index)->Value);

                pRstCompound->MoveNext();

                int intLine = intLine + 1;
                if (intLine % 22 == 0)
                {
                    printf("\nPress any key to continue...");
                    getch();

                    //Clear the screen for the next display.
                    system("cls");
                }
            }
            long    lngRec = 0;
            pRstCompound = pRstCompound->
                NextRecordset((VARIANT *)lngRec);

            printf("\nPress any key to continue...");
            getch();
            intCount = intCount + 1;
        }
    }

    catch(_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstCompound->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr   = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndNextRecordsetCpp

See Also

NextRecordset Method


NumericScale and Precision Properties Example (VC++)

This example uses the NumericScale and Precision properties to display the numeric scale and precision of fields in the Discounts table of the Pubs database.

// BeginNumericScaleCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void NumericScaleX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    NumericScaleX();

    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      NumericScaleX Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void NumericScaleX(void)
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace
    _RecordsetPtr  pRstDiscounts  = NULL;
     FieldsPtr  fldTemp  = NULL;

    //Define Other Variables
    HRESULT  hr = S_OK;
    _variant_t Index;
    Index.vt = VT_I2;

    _bstr_t strCnn("Provider=sqloledb;Data Source=srv;"
         "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        // Open recordset.
        TESTHR(pRstDiscounts.CreateInstance(__uuidof(Recordset)));
        pRstDiscounts->Open("discounts", strCnn, adOpenForwardOnly,
            adLockReadOnly, adCmdTable);

        // Display numeric scale and precision of
        // numeric and small integer fields.
        fldTemp = pRstDiscounts->GetFields();

        for (int intLoop=0;intLoop < (int)fldTemp->GetCount();intLoop++)
        {
            Index.iVal = intLoop;

            if ((fldTemp->GetItem(Index)->Type == adNumeric)
                || (fldTemp->GetItem(Index)->Type == adSmallInt))
            {
                printf("Field: %s\n" ,(LPCSTR)fldTemp->
                    GetItem(Index)->GetName());
                printf("Numeric scale: %d\n", fldTemp->
                    GetItem(Index)->GetNumericScale());
                printf("Precision: %d\n", fldTemp->
                    GetItem(Index)->GetPrecision());
            }
        }
      // Clean up objects before exit.
      pRstDiscounts->Close();
    }

    catch(_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstDiscounts->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;

        // Collection ranges from 0 to nCount -1.
        for(long i = 0;i < nCount;i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print COM errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndNumericScaleCpp

See Also

NumericScale Property | Precision Property


Open and Close Methods Example (VC++)

This example uses the Open and Close methods on both Recordset and Connection objects that have been opened.

// BeginOpenCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <oledb.h>
#include <stdio.h>
#include <conio.h>
#include "OpenX.h"

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void OpenX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    OpenX();

    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//               OpenX Function                          //
//                                                       //
///////////////////////////////////////////////////////////

void OpenX(void)
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace
     _RecordsetPtr    pRstEmployee  = NULL;
     _ConnectionPtr pConnection    = NULL;

    // Define string variables.
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
        "Initial Catalog=pubs;User Id=sa;Password=;");

    // Define Other Variables.
    HRESULT  hr = S_OK;
    IADORecordBinding   *picRs  = NULL;  // Interface Pointer declared.
    CEmployeeRs emprs;       // C++ Class object
    DBDATE varDate;

    try
    {
        // open connection and record set
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
        pConnection->Open(strCnn,"","",adConnectUnspecified);

        TESTHR(pRstEmployee.CreateInstance(__uuidof(Recordset)));
        pRstEmployee->Open("Employee",
            _variant_t((IDispatch *)pConnection,true), adOpenKeyset,
            adLockOptimistic, adCmdTable);

        // Open an IADORecordBinding interface pointer which we'll
        // use for Binding Recordset to a class.
        TESTHR(pRstEmployee->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        //Bind the Recordset to a C++ Class here.
        TESTHR(picRs->BindToRecordset(&emprs));

        // Assign the first employee record's hire date
        // to a variable, then change the hire date.
        varDate = emprs.m_sze_hiredate;
        printf("\nOriginal data\n");
        printf("\tName - Hire Date\n");
        printf("  %s  %s - %d/%d/%d\n\n",
            emprs.le_fnameStatus == adFldOK ?
            emprs.m_sze_fname : "<NULL>",
            emprs.le_lnameStatus == adFldOK ?
            emprs.m_sze_lname : "<NULL>",
            emprs.le_hiredateStatus == adFldOK ?
            emprs.m_sze_hiredate.month : 0,
            emprs.le_hiredateStatus == adFldOK ?
            emprs.m_sze_hiredate.day : 0,
            emprs.le_hiredateStatus == adFldOK ?
            emprs.m_sze_hiredate.year : 0);

        emprs.m_sze_hiredate.year=1900;
        emprs.m_sze_hiredate.month=1;
        emprs.m_sze_hiredate.day=1;
        picRs->Update(&emprs);

        printf("\nChanged data\n");
        printf("\tName - Hire Date\n");
        printf("  %s %s - %d/%d/%d\n\n",
            emprs.le_fnameStatus == adFldOK ?
            emprs.m_sze_fname : "<NULL>",
            emprs.le_lnameStatus == adFldOK ?
            emprs.m_sze_lname : "<NULL>",
            emprs.le_hiredateStatus == adFldOK ?
            emprs.m_sze_hiredate.month : 0,
            emprs.le_hiredateStatus == adFldOK ?
            emprs.m_sze_hiredate.day : 0,
            emprs.le_hiredateStatus == adFldOK ?
            emprs.m_sze_hiredate.year : 0);

        // Requery Recordset and reset the hire date.
        pRstEmployee->Requery(adOptionUnspecified);
        // Open an IADORecordBinding interface pointer which we'll
        // use for Binding Recordset to a class.
        TESTHR(pRstEmployee->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        // Rebind the Recordset to a C++ Class here.
        TESTHR(picRs->BindToRecordset(&emprs));
        emprs.m_sze_hiredate = varDate;
        picRs->Update(&emprs);
        printf("\nData after reset\n");
        printf("\tName - Hire Date\n");
        printf("  %s %s - %d/%d/%d",emprs.le_fnameStatus == adFldOK ?
            emprs.m_sze_fname : "<NULL>",
            emprs.le_lnameStatus == adFldOK ?
            emprs.m_sze_lname : "<NULL>",
            emprs.le_hiredateStatus == adFldOK ?
            emprs.m_sze_hiredate.month : 0,
            emprs.le_hiredateStatus == adFldOK ?
            emprs.m_sze_hiredate.day : 0,
            emprs.le_hiredateStatus == adFldOK ?
            emprs.m_sze_hiredate.year : 0);

        // Clean up objects before exit.
        pRstEmployee->Close();
        pConnection->Close();
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        // Pass a connection pointer accessed from the Connection.
        PrintProviderError(pConnection);
        PrintComError(e);
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0;i < nCount;i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print COM errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndOpenCpp

OpenX.h:

// BeginOpenH
#include "icrsint.h"

// This Class extracts only fname,lastname and
// hire_date from employee table
class CEmployeeRs : public CADORecordBinding
{

BEGIN_ADO_BINDING(CEmployeeRs)

    // Column fname is the 2nd field in the table
   ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_sze_fname,
         sizeof(m_sze_fname), le_fnameStatus, FALSE)

    // Column lname is the 4th field in the table.
   ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_sze_lname,
         sizeof(m_sze_lname), le_lnameStatus, FALSE)

    // Column hiredate is the 8th field in the table.
   ADO_VARIABLE_LENGTH_ENTRY2(8, adDBDate,m_sze_hiredate,
         sizeof(m_sze_hiredate), le_hiredateStatus, TRUE)

END_ADO_BINDING()

public:
   CHAR        m_sze_fname[21];
   ULONG    le_fnameStatus;
   CHAR        m_sze_lname[31];
   ULONG    le_lnameStatus;
   DBDATE    m_sze_hiredate;
   ULONG    le_hiredateStatus;
};
// EndOpenH

See Also

Close Method | Connection Object | Open Method (ADO Connection) | Open Method (ADO Recordset) | Recordset Object


OpenSchema Method Example (VC++)

This example uses the OpenSchema method to display the name and type of each table in the Pubs database.

// BeginOpenSchemaCpp
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <oleauto.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void OpenSchemaX(void);
void OpenSchemaX2(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    OpenSchemaX();

    printf("Press any key to see the results of 2nd "
        "function...\n\n");
    getch();

    OpenSchemaX2();

    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      OpenSchemaX Function                             //
//                                                       //
///////////////////////////////////////////////////////////

void OpenSchemaX()
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _ConnectionPtr  pConnection    = NULL;
    _RecordsetPtr  pRstSchema  = NULL;

    //Other Variables
    HRESULT  hr = S_OK;

    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

   try
    {
        // Open connection.
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
        pConnection->Open (strCnn, "", "", adConnectUnspecified);

        pRstSchema = pConnection->OpenSchema(adSchemaTables);

        while(!(pRstSchema->EndOfFile))
        {
            _bstr_t table_name = pRstSchema->Fields->
                GetItem("TABLE_NAME")->Value;

            printf("Table Name: %s\n",(LPCSTR) table_name);

            _bstr_t table_type = pRstSchema->Fields->
                GetItem("TABLE_TYPE")->Value;

            printf("Table type: %s\n\n",(LPCSTR) table_type);

            pRstSchema->MoveNext();

            int intLine = intLine + 1;
            if (intLine % 5 == 0)
            {
                printf("\nPress any key to continue...");
                getch();
                //Clear the screen for the next display
                system("cls");
            }
        }
        // Clean up objects before exit.
        pRstSchema->Close();
        pConnection->Close();
    }

    catch (_com_error &e)
    {
        // Notify the user of errors if any.
        // Pass a connection pointer accessed from the Connection.
        PrintProviderError(pConnection);
        PrintComError(e);
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      OpenSchemaX2 Function                            //
//                                                       //
///////////////////////////////////////////////////////////
void OpenSchemaX2()
{
    HRESULT  hr = S_OK;
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _ConnectionPtr  pConnection2  = NULL;
    _RecordsetPtr   pRstSchema  = NULL;

    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

   try
    {
        // Open connection.
        TESTHR(pConnection2.CreateInstance(__uuidof(Connection)));
        pConnection2->Open (strCnn, "", "", adConnectUnspecified);

        // Create a safearray which takes four elements,and pass it as
        // 2nd parameter in OpenSchema method.
        SAFEARRAY FAR* psa = NULL;
        SAFEARRAYBOUND rgsabound;
        _variant_t  var;
        _variant_t  Array;
        rgsabound.lLbound = 0;
        rgsabound.cElements = 4;
        psa = SafeArrayCreate(VT_VARIANT, 1, &rgsabound);
        var.vt = VT_EMPTY;

        long ix;
        ix = 0;
        SafeArrayPutElement(psa, &ix, &var);

        ix= 1;
        SafeArrayPutElement(psa, &ix, &var);

        ix = 2;
        SafeArrayPutElement(psa, &ix, &var);

        var.vt = VT_BSTR;
        char * s1 = "VIEW";
        _bstr_t str = s1;
        var.bstrVal = str;

        ix = 3;
        SafeArrayPutElement(psa, &ix, &var);

        Array.vt = VT_ARRAY|VT_VARIANT;
        Array.parray = psa;

        pRstSchema = pConnection2->OpenSchema(adSchemaTables,&Array);

        while(!(pRstSchema->EndOfFile))
        {
            printf("Table Name: %s\n", (LPCSTR) (_bstr_t) pRstSchema->
                Fields->GetItem("TABLE_NAME")->Value);

            printf("Table type: %s\n\n",(LPCSTR) (_bstr_t) pRstSchema->
                Fields->GetItem("TABLE_TYPE")->Value);

            pRstSchema->MoveNext();

            int intLine = intLine + 1;
                if (intLine % 5 == 0)
                {
                    printf("\nPress any key to continue...");
                    getch();
                    //Clear the screen for the next display
                    system("cls");
                }
        }
        // Clean up objects before exit.
        pRstSchema->Close();
        pConnection2->Close();
    }    // End Try statement.

    catch (_com_error &e)
    {
        // Notify the user of errors if any.
        // Pass a connection pointer accessed from the Connection.
        PrintProviderError(pConnection2);
        PrintComError(e);
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0;i < nCount;i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print COM errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndOpenSchemaCpp

See Also

OpenSchema Method


Optimize Property Example (VC++)

This example demonstrates the Field object dynamic Optimize property. The zip field of the Authors table in the Pubs database is not indexed. Setting the Optimize property to True on the zip field authorizes ADO to build an index that improves the performance of the Find method.

// BeginOptimizeCpp
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void OptimizeX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//    Main Function                                     //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    OptimizeX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//           OptimizeX Function                         //
//                                                      //
//////////////////////////////////////////////////////////
void OptimizeX(void)
{
    HRESULT   hr = S_OK;

    // Define string variables.
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr   pRst = NULL;

    try
    {
        TESTHR(pRst.CreateInstance(__uuidof(Recordset)));

        // Enable Index creation.
        pRst->CursorLocation = adUseClient;
        pRst->Open ("SELECT * FROM authors", strCnn,
            adOpenStatic, adLockReadOnly, adCmdText);

        // Create the index
        pRst->Fields->GetItem("zip")->Properties->
            GetItem("Optimize")->PutValue("True");

        // Find Akiko Yokomoto
        pRst->Find("zip = '94595'",1,adSearchForward);
        printf("\n%s %s    %s %s %s\n",
            (LPSTR) (_bstr_t) pRst->Fields->GetItem("au_fname")->Value,
            (LPSTR) (_bstr_t) pRst->Fields->GetItem("au_lname")->Value,
            (LPSTR) (_bstr_t) pRst->Fields->GetItem("address")->Value,
            (LPSTR) (_bstr_t) pRst->Fields->GetItem("city")->Value,
            (LPSTR) (_bstr_t) pRst->Fields->GetItem("state")->Value);

        // Delete the index
        pRst->Fields->GetItem("zip")->Properties->
            GetItem("Optimize")->PutValue("False");
        pRst->Close();
    }

    catch (_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRst->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;

        // Collection ranges from 0 to nCount -1.
        for(long i = 0;i < nCount;i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////
void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print COM errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndOptimizeCpp

See Also

Field Object | Optimize Property—Dynamic (ADO)


OriginalValue and UnderlyingValue Properties Example (VC++)

This example demonstrates the OriginalValue and UnderlyingValue properties by displaying a message if a record's underlying data has changed during a Recordset batch update.

// BeginOriginalValueCpp
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>
#include "OriginalValueX.h"

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void OriginalValueX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

   OriginalValueX();

    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//            OriginalValueX Function                    //
//                                                       //
///////////////////////////////////////////////////////////

void OriginalValueX(void)
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _ConnectionPtr   pConnection    = NULL;
     FieldPtr      pFldType   = NULL;
    _RecordsetPtr   pRstTitles   = NULL;

    // Define string variables.
    _bstr_t strSQLChange("UPDATE Titles SET Type = "
            "'sociology' WHERE Type = 'psychology'");
    _bstr_t strSQLRestore("UPDATE Titles SET Type = "
            "'psychology' WHERE Type = 'sociology'");

    // Define Other Variables
    HRESULT    hr = S_OK;
    IADORecordBinding   *picRs  = NULL;  //Interface Pointer declared
    CTitleRs titlers;        // C++ Class object

    try
    {
        _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

        // Open connection.
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
        pConnection->Open (strCnn, "", "", adConnectUnspecified);

        // Open Recordset for batch update.
        TESTHR(pRstTitles.CreateInstance(__uuidof(Recordset)));
        pRstTitles->PutActiveConnection(
            _variant_t((IDispatch *)pConnection,true));
        pRstTitles->CursorType = adOpenKeyset;
        pRstTitles->LockType = adLockBatchOptimistic;

        // Cast Connection pointer to an IDispatch type so converted
        // to correct type of variant.
        pRstTitles->Open("Titles",
            _variant_t((IDispatch *)pConnection,true),
            adOpenKeyset, adLockBatchOptimistic, adCmdTable);

        //Open an IADORecordBinding interface pointer which
        //we'll use for Binding Recordset to a class.
        TESTHR(pRstTitles->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        //Bind the Recordset to a C++ Class here
        TESTHR(picRs->BindToRecordset(&titlers));

        // Set field object variable for Type field.
        pFldType = pRstTitles->Fields->GetItem("type");

        // Change the type of psychology titles.
        while(!(pRstTitles->EndOfFile))
        {
            if (!strcmp(strtok((char *)titlers.m_szau_Type," "),
                "psychology"))
            {
                pFldType->Value = "self_help";
            }
            pRstTitles->MoveNext();
        }

        // Simulate a change by another user by updating data
        // using a command string.
        pConnection->Execute(strSQLChange,NULL,0);

        // Check for changes.
        pRstTitles->MoveFirst();
        while(!(pRstTitles->EndOfFile))
        {
            if (strcmp(pFldType->OriginalValue.pcVal,
                pFldType->UnderlyingValue.pcVal))
            {
                printf("\n\nData has changed!");

                printf("\n\nTitle ID: %s",titlers.lau_Title_idStatus ==
                    adFldOK ? titlers.m_szau_Title_id : "<NULL>");

                printf("\n\nCurrent Value: %s",
                    (LPCSTR) (_bstr_t) pFldType->Value);

                printf("\n\nOriginal Value: %s",
                    (LPCSTR) (_bstr_t) pFldType->OriginalValue);

                printf("\n\nUnderlying Value: %s\n\n",
                    (LPCSTR) (_bstr_t) pFldType->UnderlyingValue);

                printf("Press any key to continue...");
                getch();

                system("cls");
            }
            pRstTitles->MoveNext();
        }

        // Cancel the update because this is a demonstration.
        pRstTitles->CancelBatch(adAffectAll);

        // Restore Original Values.
        pConnection->Execute(strSQLRestore,NULL,0);

        // Clean up objects before exit.
        pRstTitles->Close();
        pConnection->Close();

        //Release IADORecordset Interface
        if (picRs)
            picRs->Release();
    }

    catch (_com_error &e)
    {
        // Notify the user of errors if any.
        // Pass a connection pointer accessed from the Connection.
        PrintProviderError(pConnection);
        PrintComError(e);
    }
};

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;

        // Collection ranges from 0 to nCount -1.
        for(long i = 0;i < nCount;i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////
void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print COM errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndOriginalValueCpp

OriginalValueX.h:

// BeginOriginalValueH
#include "icrsint.h"

//This class extracts title_id and type from titles table
class CTitleRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CTitleRs)
        // Column title_id is the 1st field in the Recordset
        ADO_VARIABLE_LENGTH_ENTRY2(1, adVarChar, m_szau_Title_id,
            sizeof(m_szau_Title_id), lau_Title_idStatus, FALSE)

        // Column type is the 3rd field in the Recordset
        ADO_VARIABLE_LENGTH_ENTRY2(3, adVarChar, m_szau_Type,
            sizeof(m_szau_Type), lau_TypeStatus, TRUE)
END_ADO_BINDING()

public:
    CHAR m_szau_Title_id[7];
    ULONG lau_Title_idStatus;
    CHAR m_szau_Type[13];
    ULONG lau_TypeStatus;
};
// EndOriginalValueH

See Also

OriginalValue Property | Recordset Object | UnderlyingValue Property


Prepared Property Example (VC++)

This example demonstrates the Prepared property by opening two Command objects—one prepared and one not prepared.

// BeginPreparedCpp
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>
#include <winbase.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void PreparedX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////////////////
//                                                       //
//      Main Function                                    //
//                                                       //
///////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    PreparedX();

    ::CoUninitialize();
}

///////////////////////////////////////////////////////////
//                                                       //
//      PreparedX Function                               //
//                                                       //
///////////////////////////////////////////////////////////

void PreparedX(void)
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _ConnectionPtr   pConnection    =NULL;
    _CommandPtr      pCmd1    =NULL;
    _CommandPtr      pCmd2    =NULL;

    // Define string variables.
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    //Define Other Variables
    HRESULT   hr = S_OK;

   try
    {
        // Open a connection.
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
        pConnection->Open (strCnn, "", "", adConnectUnspecified);

        _bstr_t strCmd ("SELECT title,type FROM titles ORDER BY type");

        // Create two command objects for the same
        // command - one prepared and one not prepared.
        TESTHR(pCmd1.CreateInstance(__uuidof(Command)));
        pCmd1->ActiveConnection = pConnection;
        pCmd1->CommandText = strCmd;

        TESTHR(pCmd2.CreateInstance(__uuidof(Command)));
        pCmd2->ActiveConnection = pConnection;
        pCmd2->CommandText = strCmd;
        pCmd2->PutPrepared(true);

        // Set a timer,then execute the unprepared command 20 times.
        DWORD sngStart=GetTickCount();

        for(int intLoop=1;intLoop<=20;intLoop++)
        {
            pCmd1->Execute(NULL,NULL,adCmdText);
        }
        DWORD sngEnd=GetTickCount();

        float sngNotPrepared = (float)(sngEnd - sngStart)/(float)1000;

        // Reset the timer,then execute the prepared command 20 times
        sngStart=GetTickCount();
        for(intLoop=1;intLoop<=20;intLoop++)
        {
            pCmd2->Execute(NULL,NULL,adCmdText);
        }
        sngEnd=GetTickCount();

        float sngPrepared = (float)(sngEnd - sngStart)/(float)1000;

        // Display performance results
        printf("\n\nPerformance Results:");
        printf("\n\nNot Prepared: %6.3f seconds",sngNotPrepared);
        printf("\n\nPrepared:     %6.3f seconds\n\n",sngPrepared);

        pConnection->Close();
    }

    catch (_com_error &e)
    {
        // Notify the user of errors if any.
        // Pass a connection pointer accessed from the Connection.
        PrintProviderError(pConnection);
        PrintComError(e);
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;

        // Collection ranges from 0 to nCount -1.
        for(long i = 0;i < nCount;i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////
void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print COM errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndPreparedCpp

See Also

Command Object | Prepared Property


Provider and DefaultDatabase Properties Example (VC++)

This example demonstrates the Provider property by opening three Connection objects using different providers. It also uses the DefaultDatabase property to set the default database for the Microsoft ODBC Provider.

// BeginProviderCpp
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void ProviderX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////
//                           //
//    Main Function          //
//                           //
///////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    ProviderX();

    ::CoUninitialize();
}

/////////////////////////////////
//                             //
//    ProviderX Function       //
//                             //
/////////////////////////////////

void ProviderX(void)
{
    HRESULT    hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _ConnectionPtr    pConnection1    = NULL;
    _ConnectionPtr    pConnection2     = NULL;
    _ConnectionPtr    pConnection3     = NULL;

    try
    {
        // Open a Connection using the Microsoft ODBC provider.
        TESTHR(pConnection1.CreateInstance(__uuidof(Connection)));
        pConnection1->ConnectionString = "driver={SQL Server};"
            "server=srv;uid=sa;pwd=;";
        pConnection1->Open("","","",adConnectUnspecified);
        pConnection1->DefaultDatabase = "pubs";

        // Display the provider
        printf("\n\nConnection1 provider: %s \n\n",
            (LPCSTR)pConnection1->Provider);

        // Open a connection using the OLE DB Provider for Microsoft Jet.
        TESTHR(pConnection2.CreateInstance(__uuidof(Connection)));
        pConnection2->Provider = "Microsoft.Jet.OLEDB.3.51";

        char *sConn = "c:\\Program Files\\Microsoft Office\\Office\\"
            "Samples\\Northwind.mdb";

        pConnection2->Open(sConn,"admin","",NULL);

        // Display the provider
        printf("Connection2 provider: %s \n\n",(LPCSTR)pConnection2->
            Provider);

        // Open a Connection using the Microsoft SQL Server provider.
        TESTHR(pConnection3.CreateInstance(__uuidof(Connection)));
        pConnection3->Provider = "sqloledb";
        pConnection3->Open("Data Source=MyServer;Initial Catalog=pubs;",
            "sa","",NULL);

        // Display the provider.
        printf("Connection3 provider: %s\n\n",(LPCSTR)pConnection3->
            Provider);

        pConnection1->Close();
        pConnection2->Close();
        pConnection3->Close();
    }

    catch (_com_error &e)
    {
        // Notify the user of errors if any.
        PrintProviderError(pConnection1);
        if(pConnection2) PrintProviderError(pConnection2);
        if(pConnection3) PrintProviderError(pConnection3);
        PrintComError(e);
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;

        // Collection ranges from 0 to nCount -1.
        for(long i = 0;i < nCount;i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////
void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print COM errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndProviderCpp

See Also

Connection Object | DefaultDatabase Property | Provider Property


Refresh Method Example (VC++)

This example demonstrates using the Refresh method to refresh the Parameters collection for a stored procedure Command object.

// BeginRefreshCpp
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>
#include "RefreshX.h"

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void RefreshX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////
//                          //
//    Main Function         //
//                          //
//////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    RefreshX();

    ::CoUninitialize();
}

////////////////////////////////////////////
//                                        //
//             RefreshX Function          //
//                                        //
////////////////////////////////////////////

void RefreshX(void)
{
    HRESULT  hr = S_OK;

    // Define string variables.
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _ConnectionPtr    pConnection      = NULL;
    _CommandPtr     pCmdByRoyalty  = NULL;
    _RecordsetPtr  pRstByRoyalty  = NULL;
    _RecordsetPtr  pRstAuthors    = NULL;
    IADORecordBinding   *picRs    = NULL;  //Interface Pointer declared.
    CAuthorsRs authorsrs;            //C++ class object
    try
    {
        // Open connection.
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
        pConnection->Open (strCnn, "", "", adConnectUnspecified);

        // Open a command object for a stored procedure,
        // with one parameter.
        TESTHR(pCmdByRoyalty.CreateInstance(__uuidof(Command)));
        pCmdByRoyalty->ActiveConnection = pConnection;
        pCmdByRoyalty->CommandText = "byroyalty";
        pCmdByRoyalty->CommandType = adCmdStoredProc;
        pCmdByRoyalty->Parameters->Refresh();

        // Get parameter value and execute the command,
        // storing the results in a recordset.
        char *strRoyalty;
        char strTemp[5];
        do
        {
            printf("\n\nEnter royalty : ");
            gets(strTemp);
            strRoyalty = strtok(strTemp," ");
            if(strRoyalty == NULL)
            {
                exit(1);
            }

            // if the input is not numeric then notify the user.
            if(!atoi(strRoyalty))
            {
                printf("\nExpecting numeric value...");
                continue;
            }
        }while(!atoi(strRoyalty));

        _variant_t vtroyal;
        vtroyal.vt = VT_I2;
        vtroyal.iVal = atoi(strRoyalty);
        _variant_t Index;
        Index.vt = VT_I2;
        Index.iVal = 1;
        pCmdByRoyalty->GetParameters()->GetItem(Index)->
            PutValue(vtroyal);
        pRstByRoyalty = pCmdByRoyalty->
            Execute(NULL,NULL,adCmdStoredProc);

        // Open the Authors table to get author names for display.
        TESTHR(pRstAuthors.CreateInstance(__uuidof(Recordset)));
        pRstAuthors->Open ("authors",
            _variant_t((IDispatch *) pConnection, true),
            adOpenForwardOnly, adLockReadOnly, adCmdTable);

        //Open an IADORecordBinding interface pointer which we'll use for
        //Binding Recordset to a class.
        TESTHR(pRstAuthors->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        //Bind the Recordset to a C++ Class here.
        TESTHR(picRs->BindToRecordset(&authorsrs));

        // Print current data in the recordset,adding
        // author names from Authors table.
        printf("\n\nAuthors with %s percent royalty\n\n",strRoyalty);
        while(!(pRstByRoyalty->EndOfFile))
        {
            _bstr_t strAuthorID = pRstByRoyalty->Fields->GetItem(
                "au_id")->Value;

            printf("  %s",(LPCSTR) (_bstr_t) pRstByRoyalty->Fields->
                GetItem("au_id")->Value);

            pRstAuthors->Filter = "au_id ='"+strAuthorID+"'";
            printf(", %s %s\n\n",authorsrs.lau_fnameStatus == adFldOK ?
                authorsrs.m_szau_fname : "<NULL>",
                authorsrs.lau_lnameStatus == adFldOK ?
                authorsrs.m_szau_lname : "<NULL>");
            pRstByRoyalty->MoveNext();
        }
        pRstByRoyalty->Close();
        pRstAuthors->Close();
        pConnection->Close();
    }

    catch (_com_error &e)
    {
        PrintProviderError(pConnection);
        PrintComError(e);
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;

        // Collection ranges from 0 to nCount -1.
        for(long i = 0;i < nCount;i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////
void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print COM errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndRefreshCpp

RefreshX.h

// BeginRefreshH
#include "icrsint.h"

//This Class extracts  lname,fname from authors table.
class CAuthorsRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CAuthorsRs)
    // Column lname is the 2nd field in the recordset
     ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szau_lname,
         sizeof(m_szau_lname), lau_lnameStatus, TRUE)
    // Column fname is the 3rd field in the recordset.
   ADO_VARIABLE_LENGTH_ENTRY2(3, adVarChar, m_szau_fname,
         sizeof(m_szau_fname), lau_fnameStatus, TRUE)

END_ADO_BINDING()

public:
    CHAR m_szau_fname[21];
    ULONG lau_fnameStatus;
    CHAR   m_szau_lname[41];
    ULONG  lau_lnameStatus;
};
// EndRefreshH

See Also

Command Object | Parameters Collection | Refresh Method


Resync Method Example (VC++)

This example demonstrates using the Resync method to refresh data in a static recordset.

// BeginResyncCpp
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void ResyncX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

/////////////////////////////
//                         //
//    Main Function        //
//                         //
/////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    ResyncX();

    ::CoUninitialize();
}

/////////////////////////////////
//                             //
//     ResyncX Function        //
//                             //
/////////////////////////////////

void ResyncX(void)
{
    HRESULT  hr = S_OK;

    // Define string variables.
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr  pRstTitles  = NULL;

     try
    {
        // Open recordset for titles table.
        TESTHR(pRstTitles.CreateInstance(__uuidof(Recordset)));
        pRstTitles->CursorLocation = adUseClient;
        pRstTitles->CursorType = adOpenStatic;
        pRstTitles->LockType = adLockBatchOptimistic;
        pRstTitles->Open ("titles",strCnn,
            adOpenStatic, adLockBatchOptimistic, adCmdTable);

        // Change the type of the first title in the recordset.
        pRstTitles->Fields->GetItem("type")->Value =
            (_bstr_t) ("database");

        // Display the results of the change.
        printf("\nBefore resync: \n\n");

        printf("Title - %s\n\n",(LPSTR) (_bstr_t) pRstTitles->
            Fields->GetItem("title")->Value);

        printf("Type - %s\n\n",(LPSTR) (_bstr_t) pRstTitles->
            Fields->GetItem("type")->Value);

        // Resync with database.
        pRstTitles->Resync(adAffectAll,adResyncAllValues);

        // Display the results of the resynch.
        printf("\n\nAfter resync: \n\n");

        printf("Title - %s\n\n",(LPSTR) (_bstr_t) pRstTitles->
            Fields->GetItem("title")->Value);

        printf("Type - %s\n\n",(LPSTR) (_bstr_t) pRstTitles->
            Fields->GetItem("type")->Value);

        pRstTitles->CancelBatch(adAffectAll);
        pRstTitles->Close();
    }

    catch (_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstTitles->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;

        // Collection ranges from 0 to nCount -1.
        for(long i = 0;i < nCount;i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////
void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print COM errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndResyncCpp

See Also

Resync Method


Save and Open Methods Example (VC++)

These three examples demonstrate how the Save and Open methods can be used together.

Assume you are going on a business trip and want to take along a table from a database. Before you go, you access the data as a Recordset and save it in a transportable form. When you arrive at your destination, you access the Recordset as a local, disconnected Recordset. You make changes to the Recordset, then save it again. Finally, when you return home, you connect to the database again and update it with the changes you made on the road.

// BeginSaveCpp
#import "c:\Program Files\Common Files\system\ado\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>
#include <io.h>

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
bool FileExists(void);
void SaveX1(void);
void SaveX2(void);
void SaveX3(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//        Main Function                                 //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    //If File exists in the specified directory, then display error
    if (!FileExists())
    {
        SaveX1();
        SaveX2();
        SaveX3();
    }

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//     SaveX1 Function                                  //
//                                                      //
//////////////////////////////////////////////////////////

//First, access and save the authors table.
void SaveX1()
{
    HRESULT hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr pRstAuthors = NULL;

    //Definitions of other variables
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        TESTHR(pRstAuthors.CreateInstance(__uuidof(Recordset)));

        pRstAuthors->Open("SELECT * FROM authors",strCnn,
            adOpenDynamic,adLockBatchOptimistic,adCmdText);

        // For sake of illustration, save the Recordset to a diskette
        // in XML format.
        pRstAuthors->Save("a:\\pubs.xml",adPersistXML);

        pRstAuthors->Close();
    }
    catch(_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstAuthors->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//       SaveX2 Function                                //
//                                                      //
//////////////////////////////////////////////////////////
//At this point, you have arrived at your destination.
//You will access the authors table as a local, disconnected Recordset.
//Don't forget you must have the MSPersist provider on the machine you
//are using in order to access the saved file, a:\pubs.xml.
void SaveX2()
{
    HRESULT hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr pRstAuthors = NULL;

    try
    {
        TESTHR(pRstAuthors.CreateInstance(__uuidof(Recordset)));

        //For sake of illustration, we specify all parameters.
        pRstAuthors->Open("a:\\pubs.xml","Provider=MSPersist;",
            adOpenForwardOnly,adLockOptimistic,adCmdFile);

        //Now you have a local, disconnected Recordset.
        //Edit it as you desire.
        //(In this example, the change makes no difference).
        pRstAuthors->Find("au_lname = 'Carson'",NULL,adSearchForward);
        if (pRstAuthors->EndOfFile)
        {
            printf("Name not found ...\n");
            pRstAuthors->Close();
            return;
        }
        pRstAuthors->GetFields()->GetItem("City")->PutValue("Berkeley");
        pRstAuthors->Update();

        // Save changes in ADTG format this time, purely for sake of
        // illustration. Note that the previous version is still on the
        // diskette as a:\pubs.xml.
        pRstAuthors->Save("a:\\pubs.adtg",adPersistADTG);

        pRstAuthors->Close();
    }
    catch(_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstAuthors->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//   SaveX3 Function                                    //
//                                                      //
//////////////////////////////////////////////////////////
//Finally, you return home. Now update the database with
//your changes.
void SaveX3()
{
    HRESULT hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr pRstAuthors = NULL;
    _ConnectionPtr pCnn = NULL;

    //Definitions of other variables
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        TESTHR(pCnn.CreateInstance(__uuidof(Connection)));

        TESTHR(pRstAuthors.CreateInstance(__uuidof(Recordset)));

        //If there is no ActiveConnection, you can open with defaults.
        pRstAuthors->Open("a:\\pubs.adtg","Provider=MSPersist;",
            adOpenForwardOnly,adLockOptimistic,adCmdFile);

        //Connect to the database, associate the Recordset with
        //the connection, then update the database table with the
        //changed Recordset.
        pCnn->Open(strCnn,"","",NULL);

        pRstAuthors->PutActiveConnection(_variant_t((IDispatch *) pCnn));
        pRstAuthors->UpdateBatch(adAffectAll);

        pRstAuthors->Close();
        pCnn->Close();
    }
    catch(_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstAuthors->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;

        // Collection ranges from 0 to nCount -1.
        for(long i = 0;i < nCount;i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print COM errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}

bool FileExists()
{
    struct _finddata_t xml_file;
    long hFile;

    if( (hFile = _findfirst("a:\\pubs.xml", &xml_file )) != -1L)
    {
       printf( "File already exists!\n" );
       return(true);
    }
    else
        return (false);
}
// EndSaveCpp

See Also

Open Method (ADO Recordset) | Recordset Object | Save Method


Seek Method and Index Property Example (VC++)

This example uses the Recordset object's Seek method and Index property in conjunction with a given Employee ID, to locate the employee's name in the Employees table of the Nwind.mdb database.

// BeginSeekCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>
#include <ole2.h>
#include <conio.h>
#include <string.h>
#include "SeekX.h"

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void SeekX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//       Main Function                                  //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    SeekX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//     SeekX Function                                   //
//                                                      //
//////////////////////////////////////////////////////////
void SeekX()
{
    HRESULT hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.

    _RecordsetPtr pRstEmp = NULL;

    IADORecordBinding    *picRs = NULL;  // Interface Pointer declared
    CEmployeeRs EmpRs; //C++ class object

    //Definitions of other variables
    _bstr_t strPrompt("Enter an EmployeeID (e.g., 1 to 9)");
    char strEmpId[1];

    try
    {
        TESTHR(pRstEmp.CreateInstance(__uuidof(Recordset)));
        pRstEmp->CursorLocation = adUseServer;
        pRstEmp->Open("employees", "Provider=Microsoft.Jet.OLEDB.4.0;"
            "Data Source=C:\\Program Files\\Microsoft Office\\Office\\"
            "Samples\\Northwind.mdb;user id=admin;password=;",
            adOpenKeyset,adLockReadOnly,adCmdTableDirect);

        //Open an IADORecordBinding interface pointer which
        //we'll use for binding Recordset to a Class
        TESTHR(pRstEmp->QueryInterface(
            __uuidof(IADORecordBinding), (LPVOID*)&picRs));

        //Bind the Recordset to a C++ Class
        TESTHR(picRs->BindToRecordset(&EmpRs));

        //Does this provider support Seek and Index?
        if (pRstEmp->Supports(adIndex) && pRstEmp->Supports(adSeek))
        {
            pRstEmp->Index = "EmployeeId";

            //Display all the employees.
            pRstEmp->MoveFirst();
            while (!pRstEmp->EndOfFile)
            {
                  printf("%d : %s %s\n",
                      EmpRs.le_empidStatus == adFldOK ?
                      EmpRs.m_ie_empid : 0,
                      EmpRs.le_fnameStatus == adFldOK ?
                      EmpRs.m_sze_fname : "<NULL>",
                      EmpRs.le_lnameStatus == adFldOK ?
                      EmpRs.m_sze_lname : "<NULL>");

                  pRstEmp->MoveNext();
            }
            //Prompt the user for an EmployeeID between 1 and 9.
            do
            {
                pRstEmp->MoveFirst();
                printf("\n\n%s\t",(LPCSTR) strPrompt);
                gets(strEmpId);

                //Quit if strEmpID is a zero-length string
                //(CANCEL, null, etc.)
                char *strTemp = strtok(strEmpId," \t");
                if (strTemp == NULL) break;
                if (strlen(strTemp) == 1 && atoi(strTemp) >= 1 &&
                    atoi(strTemp) <= 9)
                {
                    _variant_t strEmployeeId(strTemp);
                    pRstEmp->Seek(strEmployeeId, adSeekAfterEQ);
                    if (pRstEmp->EndOfFile)
                    {
                        printf("Employee not found.\n");
                    }
                    else
                    {
                        printf("%d : Employee='%s %s'\n",
                            EmpRs.le_empidStatus == adFldOK ?
                            EmpRs.m_ie_empid : 0,
                            EmpRs.le_fnameStatus == adFldOK ?
                            EmpRs.m_sze_fname : "<NULL>",
                            EmpRs.le_lnameStatus == adFldOK ?
                            EmpRs.m_sze_lname : "<NULL>");
                    }
                }
            }
            while(true);
        }

        //Release IADORecordset Interface
        if (picRs)
            picRs->Release();

        // Clean up objects before exit.
        pRstEmp->Close();
    }
    catch(_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstEmp->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintProviderError Function                      //
//                                                       //
///////////////////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;

        // Collection ranges from 0 to nCount -1.
        for(long i = 0;i < nCount;i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                pErr->Description);
        }
    }
}

///////////////////////////////////////////////////////////
//                                                       //
//      PrintComError Function                           //
//                                                       //
///////////////////////////////////////////////////////////
void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print COM errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndSeekCpp

SeekX.h

// BeginSeekH
#include "icrsint.h"

// This Class extracts only EmployeeId,FirstName and LastName
// from employees table
class CEmployeeRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CEmployeeRs)

   // Column hiredate is the 1st field in the table.
   ADO_VARIABLE_LENGTH_ENTRY2(1, adInteger,m_ie_empid,
        sizeof(m_ie_empid), le_empidStatus, FALSE)

    // Column LastName is the 2nd field in the table.
   ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_sze_lname,
         sizeof(m_sze_lname), le_lnameStatus, FALSE)

    // Column FirstName is the 3rd field in the table.
   ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_sze_fname,
         sizeof(m_sze_fname), le_fnameStatus, FALSE)
END_ADO_BINDING()

public:
   INT    m_ie_empid;
   ULONG  le_empidStatus;
   CHAR    m_sze_fname[11];
   ULONG  le_fnameStatus;
   CHAR    m_sze_lname[21];
   ULONG  le_lnameStatus;
};
// EndSeekH

See Also

Index Property | Recordset Object | Seek Method


Sort Property Example (VC++)

This example uses the Recordset object's Sort property to reorder the rows of a Recordset derived from the Authors table of the Pubs database. A secondary utility routine prints each row.

// BeginSortCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void SortX(void);
void SortXprint(_bstr_t title, _RecordsetPtr rstp);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//    Main Function                                     //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    SortX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//        SortX Function                                //
//                                                      //
//////////////////////////////////////////////////////////
void SortX(void)
{
    HRESULT  hr = S_OK;

    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _ConnectionPtr  pConnection      = NULL;
    _RecordsetPtr  pRstAuthors    = NULL;

    // Define string variables.
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=foo;");

    try
    {
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));

        TESTHR(pRstAuthors.CreateInstance(__uuidof(Recordset)));

        pRstAuthors->CursorLocation = adUseClient;
        pConnection->Open (strCnn, "", "", adConnectUnspecified);
        pRstAuthors->Open("SELECT * FROM authors",
            _variant_t((IDispatch *) pConnection),
             adOpenStatic, adLockReadOnly, adCmdText);

        SortXprint("    Initial Order    ", pRstAuthors);

        //Clear the screen for the next display.
        printf("\nPress any key to continue...");
        getch();
        system("cls");

        pRstAuthors->Sort = "au_lname ASC, au_fname ASC";
        SortXprint("Last Name Ascending", pRstAuthors);

        //Clear the screen for the next display.
        printf("\nPress any key to continue...");
        getch();
        system("cls");

        pRstAuthors->Sort = "au_lname DESC, au_fname ASC";
        SortXprint("Last Name Descending", pRstAuthors);

        // Clean up objects before exit.
        pRstAuthors->Close();
        pConnection->Close();
    }

    catch(_com_error &e)
    {
        PrintProviderError(pConnection);
        PrintComError(e);
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//        SortXprint Function                           //
//                                                      //
//////////////////////////////////////////////////////////
//This is the secondary utility routine that prints
//the given title, and the contents of the specified Recordset.
void SortXprint(_bstr_t title, _RecordsetPtr rstp)
{
    printf("---------------%s---------------\n", (LPCSTR)title);
    printf("First Name  Last Name\n"
        "---------------------------------------------------\n");
    rstp->MoveFirst();
    int intLineCnt = 4;
    while (!(rstp->EndOfFile))
    {
        _bstr_t aufname;
        _bstr_t aulname;
        aufname = rstp->GetFields()->GetItem("au_fname")->Value;
        aulname = rstp->GetFields()->GetItem("au_lname")->Value,
        printf("%s    %s\n",(LPCSTR) aufname, (LPCSTR) aulname);
        rstp->MoveNext();
        intLineCnt++;
        if (intLineCnt % 20 ==0)
            {
                printf("\nPress any key to continue...\n");
                getch();
            }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//     PrintProviderError Function                      //
//                                                      //
//////////////////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//  PrintComError Function                              //
//                                                      //
//////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);

}

// EndSortCpp

See Also

Recordset Object | Sort Property


Source Property Example (VC++)

This example demonstrates the Source property by opening three Recordset objects based on different data sources.

// BeginSourceCpp
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void SourceX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////
//                       //
//    Main Function      //
//                       //
///////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    SourceX();

    ::CoUninitialize();
}

///////////////////////////////////////////////
//                                           //
//    SourceX Function                       //
//                                           //
///////////////////////////////////////////////
void SourceX(void)
{
   HRESULT  hr = S_OK;

   // Define string variables.
   _bstr_t strCmdSQL("Select title,type,pubdate "
            "FROM titles ORDER BY title");
   _bstr_t strSQL("SELECT title_ID AS TitleID, title AS Title, "
            "publishers.pub_id AS PubID, pub_name AS PubName "
            "FROM publishers INNER JOIN titles "
            "ON publishers.pub_id = titles.pub_id "
            "ORDER BY Title");
   _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
   _ConnectionPtr  pConnection          = NULL;
   _RecordsetPtr  pRstTitles        = NULL;
   _RecordsetPtr  pRstPublishers      = NULL;
   _RecordsetPtr  pRstPublishersDirect    = NULL;
   _RecordsetPtr  pRstTitlesPublishers    = NULL;
   _CommandPtr     pCmdSQL          = NULL;

     try
    {
        // Open a connection.
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
        pConnection->Open (strCnn, "", "", adConnectUnspecified);

        // Open a recordset based on a command object.
        TESTHR(pCmdSQL.CreateInstance(__uuidof(Command)));
        pCmdSQL->ActiveConnection = pConnection;
        pCmdSQL->CommandText = strCmdSQL;
        pRstTitles = pCmdSQL->Execute(NULL,NULL,adCmdText);

        // Open a recordset based on a a table
        TESTHR(pRstPublishers.CreateInstance(__uuidof(Recordset)));
        pRstPublishers->Open ("publishers",
            _variant_t((IDispatch *) pConnection, true),
            adOpenForwardOnly, adLockReadOnly, adCmdTable);

        // Open a recordset based on a table
        TESTHR(pRstPublishersDirect.CreateInstance(
            __uuidof(Recordset)));
        pRstPublishersDirect->Open ("publishers",
            _variant_t((IDispatch *) pConnection, true),
            adOpenForwardOnly, adLockReadOnly, adCmdTableDirect);

        // Open a recordset based on a SQL string.
        TESTHR(pRstTitlesPublishers.CreateInstance(
            __uuidof(Recordset)));
        pRstTitlesPublishers->Open(strSQL,
            _variant_t((IDispatch *) pConnection, true),
            adOpenForwardOnly, adLockReadOnly, adCmdText);

        // Use the Source property to display the source of
        // each recordset.
        printf("rstTitles source: \n%s\n\n",
            (LPCSTR)(_bstr_t) pRstTitles->GetSource().bstrVal);
        printf("rstPublishers source: \n%s\n\n",
            (LPCSTR)(_bstr_t) pRstPublishers->GetSource().bstrVal);
        printf("rstPublishersDirect source: \n%s\n\n",
            (LPCSTR)(_bstr_t) pRstPublishersDirect->GetSource().bstrVal);
        printf("rstTitlesPublishers source: \n%s\n\n",
            (LPCSTR)(_bstr_t) pRstTitlesPublishers->GetSource().bstrVal);

        pRstTitles->Close();
        pRstPublishers->Close();
        pRstPublishersDirect->Close();
        pRstTitlesPublishers->Close();
        pConnection->Close();
    }
    catch (_com_error &e)
    {
        // Notify the user of errors if any.
        PrintProviderError(pConnection);
        PrintComError(e);
    }
}

///////////////////////////////////////////////
//                                           //
//    PrintProviderError Function            //
//                                           //
///////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

///////////////////////////////////////////////
//                                           //
//    PrintComError Function                 //
//                                           //
///////////////////////////////////////////////
void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);

}
// EndSourceCpp

See Also

Recordset Object | Source Property (ADO Recordset)


State Property Example (VC++)

This example uses the State property to display a message while asynchronous connections are opening and asynchronous commands are executing.

// BeginStateCpp
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void StateX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////
//                                           //
//    Main Function                          //
//                                           //
///////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    StateX();

    ::CoUninitialize();
}

///////////////////////////////////////////////
//                                           //
//        StateX Function                    //
//                                           //
///////////////////////////////////////////////
void StateX(void)
{
    HRESULT  hr = S_OK;

    // Define string variables.
    _bstr_t strSQLChange("UPDATE Titles SET Type = "
            "'self_help' WHERE Type = 'psychology'");
    _bstr_t strSQLRestore("UPDATE Titles SET Type = "
            "'psychology' WHERE Type = 'self_help'");
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _ConnectionPtr    pConnection        = NULL;
    _ConnectionPtr    pConnection2        = NULL;
    _CommandPtr         pCmdChange    = NULL;
    _CommandPtr         pCmdRestore    = NULL;

    try
    {
        // Open two asynchronous connections,displaying
        // a message while connecting.
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
        TESTHR(pConnection2.CreateInstance(__uuidof(Connection)));

        pConnection->Open (strCnn, "", "", adAsyncConnect);
        while(pConnection->State == adStateConnecting)
        {
            printf("Opening first connection....\n\n");
        }

        pConnection2->Open (strCnn, "", "", adAsyncConnect);
        while(pConnection2->State == adStateConnecting)
        {
            printf("Opening second connection....\n\n");
        }

        // Create two command objects.
        TESTHR(pCmdChange.CreateInstance(__uuidof(Command)));
        pCmdChange->ActiveConnection = pConnection;
        pCmdChange->CommandText = strSQLChange;

        TESTHR(pCmdRestore.CreateInstance(__uuidof(Command)));
        pCmdRestore->ActiveConnection = pConnection2;
        pCmdRestore->CommandText = strSQLRestore;

        // Executing the commands,displaying a message
        // while they are executing.
        pCmdChange->Execute(NULL,NULL,adAsyncExecute);
        while(pCmdChange->State == adStateExecuting)
        {
            printf("Change command executing...\n\n");
        }

        pCmdRestore->Execute(NULL,NULL,adAsyncExecute);
        while(pCmdRestore->State == adStateExecuting)
        {
            printf("Restore command executing...\n\n");
        }

        pConnection->Close();
        pConnection2->Close();
    }
    catch (_com_error &e)
    {
        // Notify the user of errors if any.
        PrintProviderError(pConnection);
        PrintComError(e);
    }
}

///////////////////////////////////////////////
//                                           //
//    PrintProviderError Function            //
//                                           //
///////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

///////////////////////////////////////////////
//                                           //
//    PrintComError Function                 //
//                                           //
///////////////////////////////////////////////
void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndStateCpp

See Also

State Property


Status Property Example (VC++)

This example uses the Status property to display which records have been modified in a batch operation before a batch update has occurred.

// BeginStatusCpp
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>
#include "StatusX.h"

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void StatusX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////
//                                           //
//    Main Function                          //
//                                           //
///////////////////////////////////////////////

void main()
{
    if(FAILED(CoInitialize(NULL)))
        return;

    StatusX();

    ::CoUninitialize();
}

///////////////////////////////////////////////
//                                           //
//    StatusX Function                       //
//                                           //
///////////////////////////////////////////////
void StatusX(void)
{
    HRESULT  hr = S_OK;

    // Define string variables.
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=foo;");

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
     IADORecordBinding *picRs    = NULL;  // Interface Pointer Declared
     CTitleRs titlers;           // C++ Class Object
    _RecordsetPtr  pRstTitles    = NULL;
    LPSTR p_TempStr        = NULL;

       try
    {
        // Open recordset for batch update
        TESTHR(hr = pRstTitles.CreateInstance(__uuidof(Recordset)));
        pRstTitles->CursorType = adOpenKeyset;
        pRstTitles->LockType = adLockBatchOptimistic;
        pRstTitles->Open ("titles", strCnn,
            adOpenKeyset, adLockBatchOptimistic, adCmdTable);

        // Open an IADORecordBinding interface pointer which
        // we will use for binding Recordset to a class.
        TESTHR(hr = pRstTitles->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        // Bind the Recordset to a C++ class here
        TESTHR(hr = picRs->BindToRecordset(&titlers));

        p_TempStr = (LPSTR) malloc(sizeof(titlers.m_szt_Type));

        // Change the type of psychology titles.
        while(!(pRstTitles->EndOfFile))
        {
            // Remove blank string(trim)
            strcpy(p_TempStr,strtok(titlers.m_szt_Type," "));

            // Compare the type of psychology titles
            if (!strcmp(p_TempStr,"psychology"))
            {
                    // Copy "self_help" title field
                    pRstTitles->Fields->GetItem("type")->Value =
                        (_bstr_t) ("self_help");
            }
            pRstTitles->MoveNext();
        }

        // Display Title ID and status
        pRstTitles->MoveFirst();
        while(!(pRstTitles->EndOfFile))
        {
            if(pRstTitles->Status == adRecModified)
            {
                printf("%s - Modified\n",titlers.lt_Title_idStatus ==
                    adFldOK ? titlers.m_szt_Title_id : "<NULL>");
            }
            else
            {
                printf("%s\n",titlers.lt_Title_idStatus == adFldOK ?
                    titlers.m_szt_Title_id : "<NULL>");
            }
            pRstTitles->MoveNext();
        }

        // Deallocate the memory
        free(p_TempStr);

        // Cancel the update because this is a demonstration.
        pRstTitles->CancelBatch(adAffectAll);
        pRstTitles->Close();
    }
    catch (_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstTitles->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////
//                                           //
//    PrintProviderError Function            //
//                                           //
///////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

///////////////////////////////////////////////
//                                           //
//    PrintComError Function                 //
//                                           //
///////////////////////////////////////////////
void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndStatusCpp

StatusX.h

// BeginStatusH
#include "icrsint.h"

//This class extracts title_id and type from titles table.
class CTitleRs : public CADORecordBinding
{
    BEGIN_ADO_BINDING(CTitleRs)
        // Column title_id is the 1st field in the table
        ADO_VARIABLE_LENGTH_ENTRY2(1,adVarChar,m_szt_Title_id,
            sizeof(m_szt_Title_id),lt_Title_idStatus,FALSE)
        // Column type is the 3rd field in the table
        ADO_VARIABLE_LENGTH_ENTRY2(3,adVarChar,m_szt_Type,
            sizeof(m_szt_Type),lt_TypeStatus,TRUE)
END_ADO_BINDING()

public:
    CHAR m_szt_Title_id[7];
    ULONG lt_Title_idStatus;
    CHAR m_szt_Type[13];
    ULONG lt_TypeStatus;
};
// EndStatusH

See Also

Status Property (ADO Recordset)


StayInSync Property Example (VC++)

This example demonstrates how the StayInSync property facilitates accessing rows in a hierarchical Recordset.

The outer loop displays each author's first and last name, state, and identification. The appended Recordset for each row is retrieved from the Fields collection and automatically assigned to rstTitleAuthor by the StayInSync property whenever the parent Recordset moves to a new row. The inner loop displays four fields from each row in the appended recordset.

// BeginStayInSyncCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void StayInSyncX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//        Main Function                                 //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    StayInSyncX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//       StayInSyncX Function                           //
//                                                      //
//////////////////////////////////////////////////////////
void StayInSyncX(void)
{
    HRESULT  hr = S_OK;

    // Define string variables.
    _bstr_t strCnn("Provider=MSDataShape;Data Provider=sqloledb;"
        "Data Source=MyServer;Initial Catalog=pubs;User Id=sa;Password=;");

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _ConnectionPtr  pConnection      = NULL;
    _RecordsetPtr  pRst      = NULL;
    _RecordsetPtr  pRstTitleAuthor    = NULL;

    try
    {
        TESTHR(pRstTitleAuthor.CreateInstance(__uuidof(Recordset)));

        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));

        TESTHR(pRst.CreateInstance(__uuidof(Recordset)));

        // Open connection.
        pConnection->Open (strCnn, "", "", adConnectUnspecified);
        pRst->PutStayInSync(true);

        // Open recordset with names from Author & titleauthor table.
        pRst->Open("SHAPE  {select * from authors} "
              "APPEND ({select * from titleauthor}"
              "RELATE au_id TO au_id) AS chapTitleAuthor",
              _variant_t((IDispatch*)pConnection,true),
            adOpenStatic, adLockReadOnly, adCmdText);

        pRstTitleAuthor = pRst->GetFields()->GetItem("chapTitleAuthor")->
            Value;
        int intLineCnt=0;
        while(!(pRst->EndOfFile))
        {
            printf("\n%s  %s  %s    %s\n", (LPCSTR)(_bstr_t)pRst->
               Fields->GetItem("au_fname")->Value,
               (LPCSTR)(_bstr_t)pRst->Fields->GetItem("au_lname")->Value,
               (LPCSTR)(_bstr_t)pRst->Fields->GetItem("state")->Value,
               (LPCSTR)(_bstr_t)pRst->Fields->GetItem("au_id")->Value);
            intLineCnt++;

            if (intLineCnt%15 == 0)
            {
                printf("\nPress any key to continue...\n");
                getch();
            }

            _variant_t vIndex;
            while(!(pRstTitleAuthor->EndOfFile))
            {
                vIndex = (short) 0;
                printf("%s    ",(LPCSTR)(_bstr_t)pRstTitleAuthor->
                    Fields->Item[&vIndex]->Value);
                vIndex = (short) 1;
                printf("%s    ",(LPCSTR)(_bstr_t)pRstTitleAuthor->
                    Fields->Item[&vIndex]->Value);
                vIndex = (short) 2;
                printf("%s    ",(LPCSTR)(_bstr_t)pRstTitleAuthor->
                    Fields->Item[&vIndex]->Value);
                vIndex = (short) 3;
                printf("%s\n",(LPCSTR)(_bstr_t)pRstTitleAuthor->
                    Fields->Item[&vIndex]->Value);
                intLineCnt++;

                if (intLineCnt%15 == 0)
                {
                    printf("\nPress any key to continue...\n");
                    getch();
                }
                pRstTitleAuthor->MoveNext();
            }
            pRst->MoveNext();
        }
        // Clean up objects before exit.
        pRst->Close();
        pConnection->Close();
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        // Pass a connection pointer accessed from the Recordset.
        PrintProviderError(pConnection);
        PrintComError(e);
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//        PrintProviderError Function                   //
//                                                      //
//////////////////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//     PrintComError Function                           //
//                                                      //
//////////////////////////////////////////////////////////
void PrintComError(_com_error &e)
{

    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);

}
// EndStayInSyncCpp

See Also

Fields Collection | Recordset Object | StayInSync Property


Supports Method Example (VC++)

This example uses the Supports method to display the options supported by a recordset opened with different cursor types. The DisplaySupport function is required for this example to run.

// BeginSupportsCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>
#include <ole2.h>
#include <conio.h>

//Function Declarations.
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void SupportsX(void);
void DisplaySupport(_RecordsetPtr pRstTemp);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////
//                                           //
//    Main Function                          //
//                                           //
///////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    SupportsX();

    ::CoUninitialize();
}

///////////////////////////////////////////////
//                                           //
//    SupportsX Function                     //
//                                           //
///////////////////////////////////////////////
void SupportsX(void)
{
    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr  pRstTitles  = NULL;

    // Define Other Variables
    HRESULT hr = S_OK;

    // Assign connection string to a variable
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    try
    {
        // Open a recordset from Titles table
        TESTHR(pRstTitles.CreateInstance(__uuidof(Recordset)));

        // Fill array with CursorType constants.
        int  aintCursorType[4];
        aintCursorType[0] = adOpenForwardOnly;
        aintCursorType[1] = adOpenKeyset;
        aintCursorType[2] = adOpenDynamic;
        aintCursorType[3] = adOpenStatic;

        // Open recordset using each CursorType and optimistic locking.
        // Then call the DisplaySupport procedure to display the
        // supported options.
        for (int intIndex=0; intIndex <= 3; intIndex++)
        {
            pRstTitles->CursorType =
               (enum CursorTypeEnum)aintCursorType[intIndex];
            pRstTitles->LockType = adLockOptimistic;

            // Pass the Cursor type and LockType to the Recordset.
            pRstTitles->Open ("titles", strCnn,
                (enum CursorTypeEnum)aintCursorType[intIndex],
                adLockOptimistic, adCmdTable);

            switch(aintCursorType[intIndex])
            {
                case adOpenForwardOnly:
                    printf("\nForwardOnly cursor supports:\n");
                    break;

                case adOpenKeyset:
                    printf("\nKeyset cursor supports:\n");
                    break;

                case adOpenDynamic:
                    printf("\nDynamic cursor supports:\n");
                    break;

                case adOpenStatic:
                    printf("\nStatic cursor supports:\n");
                    break;

                default :
                    break;
            }

            DisplaySupport(pRstTitles);

            printf("\n\nPress any key to continue...");
            getch();

            //Clear the screen for the next display.
            system("cls");

            // Clean up objects before exit.
            pRstTitles->Close();
        }
    }

    catch(_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstTitles->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////
//                                           //
//    DisplaySupport Function                //
//                                           //
///////////////////////////////////////////////
void DisplaySupport (_RecordsetPtr  pRstTemp)
{
    // Fill array with cursor option constants.
    long  alngConstants[11];
    alngConstants[0] = adAddNew;
    alngConstants[1] = adApproxPosition;
    alngConstants[2] = adBookmark;
    alngConstants[3] = adDelete;
    alngConstants[4] = adFind;
    alngConstants[5] = adHoldRecords;
    alngConstants[6] = adMovePrevious;
    alngConstants[7] = adNotify;
    alngConstants[8] = adResync;
    alngConstants[9] = adUpdate;
    alngConstants[10] = adUpdateBatch;

    for(int intIndex=0; intIndex <= 10; intIndex++)
    {
        bool booSupports = pRstTemp->
            Supports( (enum CursorOptionEnum)alngConstants[intIndex] );

        if(booSupports)
        {
            switch(alngConstants[intIndex])
            {
                case adAddNew :
                    printf("\n  AddNew");
                    break;

                case adApproxPosition :
                    printf("\n  AbsolutePosition and AbsolutePage");
                    break;

                case adBookmark :
                    printf("\n  Bookmark");
                    break;

                case adDelete :
                    printf("\n  Delete");
                    break;

                case adFind :
                    printf("\n  Find");
                    break;

                case adHoldRecords :
                    printf("\n  Holding Records");
                    break;

                case adMovePrevious :
                    printf("\n  MovePrevious and Move");
                    break;

                case adNotify :
                    printf("\n  Notifications");
                    break;

                case adResync :
                    printf("\n  Resyncing data");
                    break;

                case adUpdate :
                    printf("\n  Update");
                    break;

                case adUpdateBatch :
                    printf("\n  Batch updating");
                    break;

                default :
                    break;
            }
        }
    }
}

///////////////////////////////////////////////
//                                           //
//    PrintProviderError Function            //
//                                           //
///////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

///////////////////////////////////////////////
//                                           //
//    PrintComError Function                 //
//                                           //
///////////////////////////////////////////////
void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndSupportsCpp

See Also

Recordset Object | Supports Method


Type Property Example (Field) (VC++)

This example demonstrates the Type property by displaying the name of the constant that corresponds to the value of the Type property of all the Field objects in the Employees table. The FieldType function is required for this procedure to run.

// BeginTypeFieldCpp
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void TypeX(void);
_bstr_t FieldType(int intType);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

///////////////////////////////////////////////
//                                           //
//    Main Function                          //
//                                           //
///////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    TypeX();

    ::CoUninitialize();
}

///////////////////////////////////////////////
//                                           //
//             TypeX Function                //
//                                           //
///////////////////////////////////////////////
void TypeX(void)
{
   HRESULT  hr = S_OK;

    // Define string variables.
   _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
  _RecordsetPtr  pRstEmployees  = NULL;
   FieldsPtr  pFldLoop      = NULL;

     try
    {
        // Open recordset with data from Employee table.
        TESTHR(pRstEmployees.CreateInstance(__uuidof(Recordset)));
        pRstEmployees->Open ("employee",strCnn,
            adOpenForwardOnly, adLockReadOnly, adCmdTable);

        printf("Fields in Employee Table:\n\n");

        // Enumerate the Fields collection of the Employees table.
        pFldLoop = pRstEmployees->GetFields();
        int intLine = 0;
        for (int intFields = 0; intFields < (int)pFldLoop->
            GetCount(); intFields++)
        {
            _variant_t Index;
            Index.vt = VT_I2;
            Index.iVal = intFields;
            printf("  Name: %s\n" ,
                (LPCSTR) pFldLoop->GetItem(Index)->GetName());
            printf("  Type: %s\n\n",
                (LPCTSTR)FieldType(pFldLoop->GetItem(Index)->Type));
            intLine++;
            if(intLine % 5 == 0)
            {
                printf("Press any key to continue...");
                getch();
                system("cls");
            }
        }
        pRstEmployees->Close();
    }

    catch (_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstEmployees->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

///////////////////////////////////////////////
//                                           //
//             FieldType Function            //
//                                           //
///////////////////////////////////////////////
_bstr_t FieldType(int intType)
{
        _bstr_t strType;
        switch(intType)
        {
            case adChar:
                 strType = "adChar";
                break;
            case adVarChar:
                strType = "adVarChar";
                break;
            case adSmallInt:
                strType = "adSmallInt";
                break;
            case adUnsignedTinyInt:
                strType = "adUnsignedTinyInt";
                break;
            case adDBTimeStamp:
                strType = "adDBTimeStamp";
                break;
            default:
                break;
        }
        return strType;
}

///////////////////////////////////////////////
//                                           //
//    PrintProviderError Function            //
//                                           //
///////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

///////////////////////////////////////////////
//                                           //
//    PrintComError Function                 //
//                                           //
///////////////////////////////////////////////
void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndTypeFieldCpp

See Also

Field Object | Type Property


Type Property Example (Property) (VC++)

This example demonstrates the Type property. It is a model of a utility for listing the names and types of a collection, like Properties, Fields, etc.

We do not need to open the Recordset to access its Properties collection; they come into existence when the Recordset object is instantiated. However, setting the CursorLocation property to adUseClient adds several dynamic properties to the Recordset object's Properties collection, making the example a little more interesting. For sake of illustration, we explicitly use the Item property to access each Property object.

// BeginTypePropertyCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include<conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void TypeX();
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//     Main Function                                    //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    TypeX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//        TypeX Function                                //
//                                                      //
//////////////////////////////////////////////////////////
void TypeX()
{
    HRESULT  hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace
    _RecordsetPtr  pRst  = NULL;
    PropertyPtr pProperty = NULL;

    //Define Other Variables
    _bstr_t strMsg;
    _variant_t vIndex;
    int intLineCnt = 0;

      try
    {
        TESTHR(pRst.CreateInstance (__uuidof(Recordset)));

        // Set the Recordset Cursor Location
        pRst->CursorLocation = adUseClient;

        for (short iIndex = 0; iIndex <= (pRst->Properties->
            GetCount() - 1);iIndex++)
        {
            vIndex = iIndex;
            pProperty = pRst->Properties->GetItem(vIndex);

            int propType = (int)pProperty->GetType();
            switch(propType)
            {
                case adBigInt:
                    strMsg = "adBigInt";
                    break;
                case adBinary:
                    strMsg = "adBinary";
                    break;
                case adBoolean:
                    strMsg = "adBoolean";
                    break;
                case adBSTR:
                    strMsg = "adBSTR";
                    break;
                case adChapter:
                    strMsg = "adChapter";
                    break;
                case adChar:
                    strMsg = "adChar";
                    break;
                case adCurrency:
                    strMsg = "adCurrency";
                    break;
                case adDate:
                    strMsg = "adDate";
                    break;
                case adDBDate:
                    strMsg = "adDBDate";
                    break;
                case adDBTime:
                    strMsg = "adDBTime";
                    break;
                case adDBTimeStamp:
                    strMsg = "adDBTimeStamp";
                    break;
                case adDecimal:
                    strMsg = "adDecimal";
                    break;
                case adDouble:
                    strMsg = "adDouble";
                    break;
                case adEmpty:
                    strMsg = "adEmpty";
                    break;
                case adError:
                    strMsg = "adError";
                    break;
                case adFileTime:
                    strMsg = "adFileTime";
                    break;
                case adGUID:
                    strMsg = "adGUID";
                    break;
                case adIDispatch:
                    strMsg = "adIDispatch";
                    break;
                case adInteger:
                    strMsg = "adInteger";
                    break;
                case adIUnknown:
                    strMsg = "adIUnknown";
                    break;
                case adLongVarBinary:
                    strMsg = "adLongVarBinary";
                    break;
                case adLongVarChar:
                    strMsg = "adLongVarChar";
                    break;
                case adLongVarWChar:
                    strMsg = "adLongVarWChar";
                    break;
                case adNumeric:
                    strMsg = "adNumeric";
                    break;
                case adPropVariant:
                    strMsg = "adPropVariant";
                    break;
                case adSingle:
                    strMsg = "adSingle";
                    break;
                case adSmallInt:
                    strMsg = "adSmallInt";
                    break;
                case adTinyInt:
                    strMsg = "adTinyInt";
                    break;
                case adUnsignedBigInt:
                    strMsg = "adUnsignedBigInt";
                    break;
                case adUnsignedInt:
                    strMsg = "adUnsignedInt";
                    break;
                case adUnsignedSmallInt:
                    strMsg = "adUnsignedSmallInt";
                    break;
                case adUnsignedTinyInt:
                    strMsg = "adUnsignedTinyInt";
                    break;
                case adUserDefined:
                    strMsg = "adUserDefined";
                    break;
                case adVarBinary:
                    strMsg = "adVarBinary";
                    break;
                case adVarChar:
                    strMsg = "adVarChar";
                    break;
                case adVariant:
                    strMsg = "adVariant";
                    break;
                case adVarNumeric:
                    strMsg = "adVarNumeric";
                    break;
                case adVarWChar:
                    strMsg = "adVarWChar";
                    break;
                case adWChar:
                    strMsg = "adWChar";
                    break;
                default:
                    strMsg = "*UNKNOWN*";
                    break;
            }

            intLineCnt++;
            if (intLineCnt%20 == 0)
            {
                printf("\nPress any key to continue...\n");
                getch();
            }
            printf ("Property %d : %s,Type = %s\n",iIndex,
                (LPCSTR)pProperty->GetName(),(LPCSTR)strMsg);
        }
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        PrintComError(e);
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//      PrintComError Function                          //
//                                                      //
//////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{

    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndTypePropertyCpp

See Also

Property Object | Type Property


Update and CancelUpdate Methods Example (VC++)

This example demonstrates the Update method in conjunction with the CancelUpdate method.

// BeginUpdateCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>
#include <ole2.h>
#include <malloc.h>
#include <conio.h>
#include "UpdateX.h"

// Function Declartion.
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void UpdateX(void);
void UpdateX2(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

     UpdateX();

     //Wait here for user to see the output..
     printf("\nPress any key to continue...");
     getch();

     //Clear the screen for the next display
     system("cls");

     UpdateX2();

     ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//      UpdateX Function                                //
//                                                      //
//////////////////////////////////////////////////////////
void UpdateX(void)
{
    HRESULT hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr pRstEmployees  = NULL;

    // Define string variables.
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    IADORecordBinding   *picRs   = NULL;  // Interface Pointer declared
    CEmployeeRs emprs;             // C++ Class object.

    try
    {
        // Open recordset with names from Employee table.
        TESTHR(pRstEmployees.CreateInstance(__uuidof(Recordset)));
        pRstEmployees->CursorType = adOpenKeyset;
        pRstEmployees->LockType = adLockOptimistic;
        pRstEmployees->Open("SELECT fname, lname FROM Employee "
            "ORDER BY lname",strCnn,adOpenKeyset,adLockOptimistic,
            adCmdText);

        // Store original data.
        _bstr_t strOldFirst = pRstEmployees->Fields->
            GetItem("fname")->Value;
        _bstr_t strOldLast  = pRstEmployees->Fields->
            GetItem("lname")->Value;

        //Change data in edit buffer.
        pRstEmployees->Fields->GetItem("fname")->Value =
            (_bstr_t)("Linda");
        pRstEmployees->Fields->GetItem("lname")->Value =
            (_bstr_t)("Kobara");

        // Show contents of buffer and get user input.
        printf("\n\nEdit in progress:\n\n");

        printf("Original data =  %s %s \n",
            (LPSTR)strOldFirst,(LPSTR)strOldLast);

        printf("Data in buffer =  %s %s",
            (LPSTR)(_bstr_t) pRstEmployees->Fields->
            GetItem("fname")->Value,\
            (LPSTR) (_bstr_t) pRstEmployees->Fields->
            GetItem("lname")->Value);

        // Ask if the User wants to Update
        printf("\n\nUse Update to replace the original data with the"
               " buffered data in the Recordset? (y/n): ");
        char chKey = getch();

        if(toupper(chKey) == 'Y')
            pRstEmployees->Update();
        else
            pRstEmployees->CancelUpdate();

        //Open an IADORecordBinding interface pointer which
        //we'll use for binding Recordset to a class.
        TESTHR(pRstEmployees->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        //Bind the Recordset to a C++ Class here.
        TESTHR(picRs->BindToRecordset(&emprs));

        pRstEmployees->MoveFirst();

        // Show the resulting data.
        printf("\nData in recordset =  %s %s", emprs.le_fnameStatus ==
            adFldOK ? emprs.m_sze_fname : "<NULL>",
            emprs.le_lnameStatus == adFldOK ?
            emprs.m_sze_lname : "<NULL>");

        // Restore original data because this is a demonstration.
        if ((strcmp((char *)strOldFirst,emprs.m_sze_fname) &&
             strcmp((char *)strOldLast,emprs.m_sze_lname)))
        {
            pRstEmployees->Fields->GetItem("fname")->Value = strOldFirst;
            pRstEmployees->Fields->GetItem("lname")->Value = strOldLast;
            pRstEmployees->Update();
        }

        //Release IADORecordset Interface
        if (picRs)
            picRs->Release();

        // Clean up objects before exit.
        pRstEmployees->Close();
    }

    catch(_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstEmployees->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

// The next example demonstrates the Update method
// in conjunction with the AddNew method.

//////////////////////////////////////////////////////////
//                                                      //
//           UpdateX2 Function                          //
//                                                      //
//////////////////////////////////////////////////////////
void UpdateX2(void)
{
    HRESULT hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _ConnectionPtr pConnection        = NULL;
    _RecordsetPtr pRstEmployees = NULL;

    // Define string variables.
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    IADORecordBinding   *picRs  = NULL;  // Interface Pointer declared
    CEmployeeRs1 emprs;            // C++ Class object.

    try
    {
        // Open a connection.
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
         pConnection->Open(strCnn,"","",NULL);

        // Open recordset with data from Employee table.
        TESTHR(pRstEmployees.CreateInstance(__uuidof(Recordset)));
        pRstEmployees->CursorType = adOpenKeyset;
        pRstEmployees->LockType = adLockOptimistic;
        pRstEmployees->Open("employee",
            _variant_t((IDispatch*)pConnection,true),
            adOpenKeyset, adLockOptimistic,adCmdTable);

        pRstEmployees->AddNew();
        _bstr_t strEmpID = "B-S55555M";
        pRstEmployees->Fields->GetItem("emp_id")->Value = strEmpID;
        pRstEmployees->Fields->GetItem("fname")->Value =
            (_bstr_t) ("Bill");
        pRstEmployees->Fields->GetItem("lname")->Value =
            (_bstr_t) ("Sornsin");

        // Show contents of buffer and get user input.
        printf("\n\nAddNew in progress:\n\n");

        printf("Data in buffer = %s ,  %s %s",
            (LPSTR) (_bstr_t) pRstEmployees->Fields->
            GetItem("emp_id")->Value,
            (LPSTR) (_bstr_t) pRstEmployees->Fields->
            GetItem("fname")->Value,
            (LPSTR) (_bstr_t) pRstEmployees->Fields->
            GetItem("lname")->Value);

        printf("\n\nUse Update to save buffer to recordset?(y/n):");
        char chKey = getch();

        if(toupper(chKey) == 'Y')
        {
            pRstEmployees->Update();

            //Open an IADORecordBinding interface pointer which
            //we'll use for binding Recordset to a class.
            TESTHR(pRstEmployees->QueryInterface(
                __uuidof(IADORecordBinding),(LPVOID*)&picRs));

            //Bind the Recordset to a C++ Class here
            TESTHR(picRs->BindToRecordset(&emprs));

            // Go to the new record and show the resulting data.
            printf ("\n\nData in recordset =  %s ,  %s %s",
                emprs.le_empidStatus == adFldOK ?
                emprs.m_sze_empid : "<NULL>",
                emprs.le_fnameStatus == adFldOK ?
                emprs.m_sze_fname : "<NULL>",
                emprs.le_lnameStatus == adFldOK ?
                emprs.m_sze_lname : "<NULL>");
        }
        else
        {
            pRstEmployees->CancelUpdate();
            printf("\n\nNo new record added.\n");
        }
        // Delete new data because this is a demonstration.
        _bstr_t strSQLDelete("DELETE FROM employee WHERE emp_id = '" +
            strEmpID + "'");
         pConnection->Execute(strSQLDelete ,NULL,adExecuteNoRecords);

        //Release IADORecordset Interface
        if (picRs)
            picRs->Release();

        // Clean up objects before exit.
        pRstEmployees->Close();
        pConnection->Close();
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        // Pass a connection pointer accessed from the Connection.
        PrintProviderError(pConnection);
        PrintComError(e);
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//       PrintProviderError Function                    //
//                                                      //
//////////////////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//      PrintComError Function                          //
//                                                      //
//////////////////////////////////////////////////////////
void PrintComError(_com_error &e)
{

    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndUpdateCpp

UpdateX.h

// BeginUpdateH
#include "icrsint.h"

//This Class extracts only fname,lname from employee table.
class CEmployeeRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CEmployeeRs)
    // fname is the 1st field in the recordset
    ADO_VARIABLE_LENGTH_ENTRY2(1, adVarChar, m_sze_fname,
         sizeof(m_sze_fname), le_fnameStatus, FALSE)
    // lname is the 2nd field in the recordset.
    ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_sze_lname,
         sizeof(m_sze_lname), le_lnameStatus, FALSE)

END_ADO_BINDING()

public:
   CHAR   m_sze_lname[31];
   ULONG  le_lnameStatus;
   CHAR   m_sze_fname[21];
   ULONG  le_fnameStatus;
};

//This Class extracts only empid,fname,lname,from employee table.
class CEmployeeRs1 : public CADORecordBinding
{
BEGIN_ADO_BINDING(CEmployeeRs1)
    // emp_id is the 1st field in the table.
    ADO_VARIABLE_LENGTH_ENTRY2(1, adVarChar, m_sze_empid,
        sizeof(m_sze_empid), le_empidStatus, FALSE)
    // fname is the 2nd field in the table.
   ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_sze_fname,
         sizeof(m_sze_fname), le_fnameStatus, FALSE)
    // lname is the 4rt field in the table.
   ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_sze_lname,
         sizeof(m_sze_lname), le_lnameStatus, FALSE)

END_ADO_BINDING()

public:
   CHAR   m_sze_empid[10];
   ULONG  le_empidStatus;
   CHAR   m_sze_lname[31];
   ULONG  le_lnameStatus;
   CHAR   m_sze_fname[21];
   ULONG  le_fnameStatus;
};
// EndUpdateH

See Also

CancelUpdate Method | Update Method


UpdateBatch and CancelBatch Methods Example (VC++)

This example demonstrates the UpdateBatch method in conjunction with the CancelBatch method.

// BeginUpdateBatchCpp
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>
#include "UpdateBatchX.h"

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void UpdateBatchX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//       Main Function                                  //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    UpdateBatchX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//      UpdateBatchX Function                           //
//                                                      //
//////////////////////////////////////////////////////////
void UpdateBatchX(void)
{
   HRESULT  hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr  pRstTitles   = NULL;

    // Define string variables.
    _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    IADORecordBinding *picRs    = NULL;    // Interface Pointer Declared
    CTitleRs titlers;             // C++ Class Object

     try
    {
        // Open titles table.
        TESTHR(pRstTitles.CreateInstance(__uuidof(Recordset)));
        pRstTitles->CursorType = adOpenKeyset;
        pRstTitles->LockType = adLockBatchOptimistic;
        pRstTitles->Open ("titles", strCnn,
            adOpenKeyset, adLockBatchOptimistic, adCmdTable);

        // Open IADORecordBinding interface pointer for binding
        // Recordset to a class
        TESTHR(pRstTitles->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        // Binding the Recordset to a C++ Class
        TESTHR(picRs->BindToRecordset(&titlers));
        pRstTitles->MoveFirst();

        // Loop through recordset and ask user if she wants,
        // to change the type for a specified title.
        while (!(pRstTitles->EndOfFile))
        {
            // Compare type with psychology
            if (!strcmp( (char *)strtok(titlers.m_szt_Type," "),
                "psychology" ))
            {
                printf("\n\nTitle: %s \nChange type to self_help?(y/n):",
                    titlers.m_szt_Title);
                char chKey;
                chKey = getch();
                if(toupper(chKey) == 'Y')
                {
                    // Change type to self_help.
                    pRstTitles->Fields->GetItem("type")->Value =
                        (_bstr_t)("self_help");
                }
            }
            pRstTitles->MoveNext();
        }

        // Ask the user if she wants to commit to all the
        // changes made above.
        printf("\n\nSave all changes?");
        char chKey;
        chKey = getch();
        if(toupper(chKey) == 'Y')
        {
            pRstTitles->UpdateBatch(adAffectAll);
        }
        else
        {
            pRstTitles->CancelBatch(adAffectAll);
        }

        // Print current data in recordset.
        pRstTitles->Requery(adOptionUnspecified);

        // Open IADORecordBinding interface pointer for Binding Recordset to a class
        TESTHR(pRstTitles->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        // ReBinding the Recordset to a C++ Class.
        TESTHR(picRs->BindToRecordset(&titlers));

        // Move to the first record of the title table
        pRstTitles->MoveFirst();

        //Clear the screen for the next display.
        system("cls");

        while (!pRstTitles->EndOfFile)
        {
            printf("%s -  %s\n",
                titlers.lt_TitleStatus == adFldOK ?
                titlers.m_szt_Title :"<NULL>",
                titlers.lt_TypeStatus == adFldOK ?
                titlers.m_szt_Type :"<NULL>");
            pRstTitles->MoveNext();
        }

        pRstTitles->MoveFirst();

        // Restore original data because this is demonstration.
        while (!(pRstTitles->EndOfFile))
        {
            // Compare type with psychology
            if(!strcmp( (char *)strtok(titlers.m_szt_Type," "),
                "self_help" ))
            {
                // Change type to psychology.
                pRstTitles->Fields->GetItem("type")->Value =
                    (_bstr_t)("psychology");
            }
            pRstTitles->MoveNext();
        }

        pRstTitles->UpdateBatch(adAffectAll);
        pRstTitles->Close();
    }

    catch (_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstTitles->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//    PrintProviderError Function                       //
//                                                      //
//////////////////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//      PrintComError Function                          //
//                                                      //
//////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndUpdateBatchCpp

UpdateBatchX.h

// BeginUpdateBatchH
#include "icrsint.h"

//This class extracts titles and type from Titles table
class CTitleRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CTitleRs)
        // Column title is the 2nd field in the table
        ADO_VARIABLE_LENGTH_ENTRY2(2,adVarChar,m_szt_Title,
            sizeof(m_szt_Title),lt_TitleStatus,FALSE)
        // Column type is the 3rd field in the table
        ADO_VARIABLE_LENGTH_ENTRY2(3,adVarChar,m_szt_Type,
            sizeof(m_szt_Type),lt_TypeStatus,TRUE)
END_ADO_BINDING()

public:
    CHAR m_szt_Title[81];
    ULONG lt_TitleStatus;
    CHAR m_szt_Type[13];
    ULONG lt_TypeStatus;
};
// EndUpdateBatchH

See Also

CancelBatch Method | UpdateBatch Method


Value Property Example (VC++)

This example demonstrates the Value property with Field and Property objects by displaying field and property values for the Employees table.

// BeginValueCpp
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void ValueX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//    Main Function                                     //
//                                                      //
//////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    ValueX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//              ValueX Function                         //
//                                                      //
//////////////////////////////////////////////////////////
void ValueX(void)
{
   HRESULT  hr = S_OK;

    // Define string variables.
   _bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
            "Initial Catalog=pubs;User Id=sa;Password=;");

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr  pRstEmployees  = NULL;
    FieldsPtr    pFldLoop    = NULL;
    PropertiesPtr  pPrpLoop    = NULL;
    _variant_t vtIndex;
    vtIndex.vt = VT_I2;

       try
    {
        // Open recordset with data from Employee table.
        TESTHR(pRstEmployees.CreateInstance(__uuidof(Recordset)));
        pRstEmployees->Open ("employee",strCnn ,
            adOpenForwardOnly, adLockReadOnly, adCmdTable);

        printf("Field values in rstEmployees\n\n");

        // Enumerate the Fields collection of the Employees table.
        pFldLoop = pRstEmployees->GetFields();

        for (int intFields = 0; intFields < (int)pFldLoop->
    GetCount(); intFields++)
        {
            vtIndex.iVal = intFields;

            // Because Value is the default property of a
            // Field object,the use of the actual keyword
            // here is optional.
            printf(" %s = %s\n\n" ,
                (LPCSTR) pFldLoop->GetItem(vtIndex)->GetName(),
                (LPCSTR) (_bstr_t) pFldLoop->GetItem(vtIndex)->Value);
        }

        printf("Press any key to continue...\n\n");
        getch();
        printf("Property values in rstEmployees\n\n");

        // Enumerate the Properties collection of the Recordset object.
        pPrpLoop = pRstEmployees->GetProperties();
        int intLine = 0;

        for (int intProperties = 0; intProperties < (int)pPrpLoop->
            GetCount(); intProperties++)
        {
            vtIndex.iVal = intProperties;

            // Because Value is the default property of a
            // Property object,the use of the actual keyword
            // here is optional.
            _variant_t propValue = pPrpLoop->GetItem(vtIndex)->Value;
            switch(propValue.vt)
            {

            case (VT_BOOL):
                     if(propValue.boolVal)
                     {
                        printf(" %s = True\n\n",(LPCSTR) pPrpLoop->
                            GetItem(vtIndex)->GetName());
                     }
                     else
                     {
                         printf(" %s = False\n\n",(LPCSTR) pPrpLoop->
                            GetItem(vtIndex)->GetName());
                     }
                break;

            case (VT_I4):
                printf(" %s = %d\n\n",(LPCSTR) pPrpLoop->
                    GetItem(vtIndex)->GetName(),
                    pPrpLoop->GetItem(vtIndex)->Value.lVal);
                break;

            case (VT_EMPTY):
                printf(" %s = \n\n",(LPCSTR) pPrpLoop->
                    GetItem(vtIndex)->GetName());
                break;

            default:
                break;
            }

            intLine++;
                if (intLine % 10 == 0)
                {
                    printf("\nPress any key to continue...");
                     getch();

                    //Clear the screen for the next display
                    system("cls");
                }
        }

        pRstEmployees->Close();
    }

    catch (_com_error &e)
    {
       // Notify the user of errors if any.
       // Pass a connection pointer accessed from the Recordset.
        _variant_t vtConnect = pRstEmployees->GetActiveConnection();

        // GetActiveConnection returns connect string if connection
        // is not open, else returns Connection object.
        switch(vtConnect.vt)
        {
            case VT_BSTR:
                PrintComError(e);
                break;
            case VT_DISPATCH:
                PrintProviderError(vtConnect);
                break;
            default:
                printf("Errors occured.");
                break;
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//    PrintProviderError Function                       //
//                                                      //
//////////////////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//      PrintComError Function                          //
//                                                      //
//////////////////////////////////////////////////////////
void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndValueCpp

See Also

Field Object | Property Object | Value Property


Version Property Example (VC++)

This example uses the Version property of a Connection object to display the current ADO version. It also uses several dynamic properties to show:

  • the current DBMS name and version.
  • OLE DB version.
  • provider name and version.
  • ODBC version.
  • ODBC driver name and version.
// BeginVersionCpp
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void VersionX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//       Main Function                                  //
//                                                      //
//////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    VersionX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//         VersionX Function                            //
//                                                      //
//////////////////////////////////////////////////////////
void VersionX(void)
{
   HRESULT   hr = S_OK;

    // Define string variables.
   _bstr_t strCnn("driver={SQL Server};server=srv;"
            "user id=sa;password=;database=pubs;");

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _ConnectionPtr    pConnection     = NULL;

       try
    {
        // Open connection.
        TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
        pConnection->Open (strCnn, "", "", adConnectUnspecified);

        printf("ADO Version   : %s\n\n",(LPCSTR) pConnection->Version);
        printf("DBMS Name   : %s\n\n",(LPCSTR) (_bstr_t)
            pConnection->Properties->GetItem("DBMS Name")->Value);
        printf("DBMS Version   : %s\n\n",(LPCSTR) (_bstr_t)
         pConnection->Properties->GetItem("DBMS Version")->Value);
        printf("OLE DB Version   : %s\n\n",(LPCSTR) (_bstr_t)
            pConnection->Properties->GetItem("OLE DB Version")->Value);
        printf("Provider Name   : %s\n\n",(LPCSTR) (_bstr_t)
            pConnection->Properties->GetItem("Provider Name")->Value);
        printf("Provider Version   : %s\n\n",(LPCSTR) (_bstr_t)
            pConnection->Properties->GetItem("Provider Version")->Value);
        printf("Driver Name   : %s\n\n",(LPCSTR) (_bstr_t)
            pConnection->Properties->GetItem("Driver Name")->Value);
        printf("Driver Version   : %s\n\n",(LPCSTR) (_bstr_t)
            pConnection->Properties->GetItem("Driver Version")->Value);
        printf("Driver ODBC Version   : %s\n\n",(LPCSTR) (_bstr_t)
           pConnection->Properties->GetItem("Driver ODBC Version")->Value);

        pConnection->Close();

    }

    catch (_com_error &e)
    {
        // Notify the user of errors if any.
        PrintProviderError(pConnection);
        PrintComError(e);
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//     PrintProviderError Function                      //
//                                                      //
//////////////////////////////////////////////////////////
void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("Error number: %x\t%s\n", pErr->Number,
                (LPCSTR) pErr->Description);
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//      PrintComError Function                          //
//                                                      //
//////////////////////////////////////////////////////////
void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndVersionCpp

See Also

Connection Object | Version Property


ADO Code Examples in Microsoft Visual J++

Use the following code examples to learn how to use the ADO methods and properties when writing in Microsoft Visual J++.

Note Paste the entire code example, from beginning to end, into your code editor. The example may not run correctly if partial examples are used or if paragraph formatting is lost.

Methods

  • AddNew Method Example
  • Append and CreateParameter Methods Example
  • AppendChunk and GetChunk Methods Example
  • BeginTrans, CommitTrans, and RollbackTrans Methods Example
  • Cancel Method Example
  • Clone Method Example
  • CompareBookmarks Method Example
  • Delete Method Example
  • Execute, Requery, and Clear Methods Example
  • Find Method Example
  • GetRows Method Example
  • GetString Method Example
  • MoveFirst, MoveLast, MoveNext, and MovePrevious Methods Example
  • NextRecordset Method Example
  • Open and Close Methods Example
  • OpenSchema Method Example
  • Refresh Method Example
  • Resync Method Example
  • Save and Open Methods Example
  • Supports Method Example
  • Update and CancelUpdate Methods Example
  • UpdateBatch and CancelBatch Methods Example

Properties

  • AbsolutePage, PageCount, and PageSize Properties Example
  • AbsolutePosition and CursorLocation Properties Example
  • ActiveCommand Property Example
  • ActiveConnection, CommandText, CommandTimeout, CommandType, Size, and Direction Properties Example
  • ActualSize and DefinedSize Properties Example
  • Attributes and Name Properties Example
  • BOF, EOF, and Bookmark Properties Example
  • CacheSize Property Example
  • ConnectionString, ConnectionTimeout, and State Properties Example
  • Count Property Example
  • 4CursorType, LockType, and EditMode Properties Example
  • Description, HelpContext, HelpFile, NativeError, Number, Source, and SQLState Properties Example
  • Filter and RecordCount Properties Example
  • IsolationLevel and Mode Properties Example
  • Item Property Example
  • MarshalOptions Property Example
  • MaxRecords Property Example
  • NumericScale and Precision Properties Example
  • Optimize Property Example
  • OriginalValue and UnderlyingValue Properties Example
  • Prepared Property Example
  • Provider and DefaultDatabase Properties Example
  • Sort Property Example
  • Source Property Example
  • State Property Example
  • Status Property Example
  • StayInSync Property Example
  • Type Property Example (Fields)
  • Type Property Example (Property)
  • Value Property Example
  • Version Property Example

See Also

ADO Code Examples in Microsoft Visual Basic | ADO Code Examples in Microsoft Visual Basic Scripting Edition | ADO Code Examples in Microsoft Visual C++ | Sample Applications


AbsolutePage, PageCount, and PageSize Properties Example (VJ++)

This example uses the AbsolutePage, PageCount, and PageSize properties to display names and hire dates from the Employees table, five records at a time.

// BeginAbsolutePageJ
// The WFC class includes the ADO objects.

import com.ms.wfc.data.*;
import java.io.* ;

public class AbsolutePageX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      AbsolutePageX();
      System.exit(0);
   }

   // AbsolutePageX function

   static void AbsolutePageX()
   {

      // Define ADO Objects.
      Recordset rstEmployees = null;

      // Declarations.
      BufferedReader in = new BufferedReader (new
         InputStreamReader(System.in));
      String line = null;
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
         + "Initial Catalog=pubs;User Id=sa;Password=;";

      String strName;
      String strFName;
      String strLName;
      String strHDate;
      int intPage;
      int intRecord;

      try
      {
         rstEmployees = new Recordset();

         // Use client cursor to enable AbsolutePosition property.
         rstEmployees.setCursorLocation( AdoEnums.CursorLocation.CLIENT);

         // Open a recordset using client cursor for the Employees table.
         rstEmployees.open("employee", strCnn,
            AdoEnums.CursorType.FORWARDONLY,
            AdoEnums.LockType.READONLY,
            AdoEnums.CommandType.TABLE);

         // Display names and hire dates, five records at a time.

         rstEmployees.setPageSize(5);
         int intPageCount = rstEmployees.getPageCount();
         for ( intPage = 1; intPage <= intPageCount; intPage++)
         {
            strName = "";
            rstEmployees.setAbsolutePage(intPage);
            for ( intRecord = 1; intRecord <= rstEmployees.getPageSize();
               intRecord++)
            {
               strFName = rstEmployees.getField("fname").getString();
               strLName = rstEmployees.getField("lname").getString();
               strHDate = rstEmployees.getField("hire_date").getString();

               strHDate = strHDate.substring(5,7) + "/" +
                  strHDate.substring(8,10) +
                  "/" + strHDate.substring(2,4);

               strName = strName + "\n" + strFName + " " + strLName +
                  "  " + strHDate;
               rstEmployees.moveNext();
               if ( rstEmployees.getEOF())
                  break;
            }
            System.out.println(strName);
            // Get user input to display next records.

            System.out.println("\n\nPress <Enter> key to Continue.");
            line = in.readLine();
         }

         // Cleanup objects before exit.
         rstEmployees.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // Check for null pointer for connection object.
         if (rstEmployees.getActiveConnection()==null)
            System.out.println("Exception: " + ae.getMessage());

         // As passing a Recordset, check for null pointer first.
         if (rstEmployees != null)
         {
            PrintProviderError(rstEmployees.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   //.PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndAbsolutePageJ

See Also

AbsolutePage Property | PageCount Property | PageSize Property | Recordset Object


AbsolutePosition and CursorLocation Properties Example (VJ++)

This example demonstrates how the AbsolutePosition property can track the progress of a loop that enumerates all the records of a Recordset. It uses the CursorLocation property to enable the AbsolutePosition property by setting the cursor to a client cursor.

// BeginAboslutePositionJ
import com.ms.wfc.data.*;
import java.io.*;

public class AbsolutePositionX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      AbsolutePositionX();
      System.exit(0);
   }

   //.AbsolutePositionX function

   static void AbsolutePositionX()
   {

      // define ADO Objects.
      Recordset rstEmployees = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));
      String line = null;
      String strCnn = "Provider=sqloledb;Data Source=MyServer;" +
         "Initial Catalog=pubs;User Id=sa;Password=;";

      String strLName;
      String strMessage;
      String strAbsolutePosition,strRecordCount;
      int intAbsolutePosition;
      int intRecordCount;
      int intChoice;

      try
      {

         rstEmployees = new Recordset();

         // Use client cursor to enable AbsolutePosition property.
         rstEmployees.setCursorLocation( AdoEnums.CursorLocation.CLIENT);

         // Open a recordset for Employees table using a client cursor.
         rstEmployees.open("employee", strCnn,
            AdoEnums.CursorType.FORWARDONLY ,
            AdoEnums.LockType.READONLY,
            AdoEnums.CommandType.TABLE);

         // Enumerate Recordset.
         while ( !rstEmployees.getEOF()) // continuous loop
         {
            intRecordCount = rstEmployees.getRecordCount();
            strRecordCount = Integer.toString(intRecordCount);

            // Read data field in the variables.
            strLName = rstEmployees.getField("lname").getString();
            intAbsolutePosition = rstEmployees.getAbsolutePosition();
            strAbsolutePosition = Integer.toString(intAbsolutePosition);

            // Display current record information.
            strMessage = "\nEmployee: " + strLName + "\n" + "(Record " +
               strAbsolutePosition + " of " +strRecordCount + " )";
            System.out.println(strMessage);
            System.out.println(
               "\nDo you want to continue (1 -> Yes / 2 -> No)?");
            //user types a number followed by enter (cr-lf).
            line = in.readLine().trim();
            intChoice = Integer.parseInt(line);
            if ( intChoice != 1)
               break;
            rstEmployees.moveNext();
         }

         //Cleanup objects before exit.
         rstEmployees.close();
      }

      catch( NumberFormatException ne)
      {
         System.out.println("\nException : Integer Input required.");
         System.exit(0);
      }

      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // Check for null pointer for connection object.
         if (rstEmployees.getActiveConnection()== null)
            System.out.println("Exception: " + ae.getMessage());
         // As passing a Recordset, check for null pointer first.
         if (rstEmployees != null)
         {
            PrintProviderError(rstEmployees.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   //.PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}

// EndAbsolutePositionJ

See Also

AbsolutePosition Property | CursorLocation Property | Recordset Object


ActiveCommand Property Example (VJ++)

This example demonstrates the ActiveCommand property.

A subroutine is given a Recordset object whose ActiveCommand property is used to display the command text and parameter that created the Recordset.

// BeginActiveCommandJ
import com.ms.wfc.data.*;
import java.io.* ;

public class ActiveCommandX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      ActiveCommandX();
      System.exit(0);
   }

   // ActiveCommandX function

   static void ActiveCommandX()
   {
      // Define ADO Objects.
      Connection cnConn1 = null;
      Command cmd = null;
      Recordset rstAuthors = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String strCnn = "DSN=pubs;Provider=MSDASQL;uid=sa;pwd=;";
      String strName;

      try
      {
         System.out.println("Enter an author's name (e.g., Ringer): ");
         strName = in.readLine().trim();
         cmd = new Command();
         cmd.setCommandText("SELECT * FROM authors WHERE au_lname = ?");
         cmd.getParameters().append(cmd.createParameter("LastName",
            AdoEnums.DataType.CHAR,
            AdoEnums.ParameterDirection.INPUT, 20, strName));
         cnConn1 = new Connection();
         cnConn1.open(strCnn);
         cmd.setActiveConnection(cnConn1);
         rstAuthors = cmd.execute(null,AdoEnums.CommandType.TEXT);
         ActiveCommandXprint(rstAuthors);

         // Cleanup objects before exit.
         rstAuthors.close();
         cnConn1.close();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstAuthors != null)
         {
            PrintProviderError(rstAuthors.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // ActiveCommandXprint function
   static void ActiveCommandXprint(Recordset rstp)
   {
      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String strName;

      try
      {
         strName = rstp.getActiveCommand().getParameters().
            getItem("LastName").getValue().toString();
         System.out.println("\nCommand text = '" +
            rstp.getActiveCommand().getCommandText() + "'");
         System.out.println("Parameter = '" + strName + "'");
         if(rstp.getBOF())
         {
            System.out.println("Name = '" + strName + "', not found.");
         }
         else
         {
            System.out.println("Name = '" +
               rstp.getField("au_fname").getString() + " " +
               rstp.getField("au_lname").getString() +
               "', author ID = '" +
               rstp.getField("au_id").getString() + "'");
         }
         System.out.println("\nPress <Enter> to continue..");
         in.readLine();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstp != null)
         {
            PrintProviderError(rstp.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   //.PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}

// EndActiveCommandJ

See Also

ActiveCommand Property | Recordset Object


ActiveConnection, CommandText, CommandTimeout, CommandType, Size, and Direction Properties Example (VJ++)

This example uses the ActiveConnection, CommandText, CommandTimeout, CommandType, Size, and Direction properties to execute a stored procedure.

// BeginActiveConnectionJ
import com.ms.wfc.data.*;
import java.io.*;

public class ActiveConnectionX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      ActiveConnectionX();
      System.exit(0);
   }

   //.ActiveConnectionX function

   static void ActiveConnectionX()
   {
      // Define ADO Objects.
      Connection cnConn1 = null;
      Command cmdByRoyalty = null;
      Parameter prmByRoyalty = null;
      Recordset rstByRoyalty = null;
      Recordset rstAuthors = null;

      //Declarations.
      String strCnn;
      String strAuthorID;
      String strFName;
      String strLName;
      int intRoyalty ;
      BufferedReader in = new BufferedReader
         (new InputStreamReader (System.in));
      String line = null;

      try
      {
         // Open a connection.

         strCnn = "Provider=sqloledb;Data Source=MyServer;"
            + "Initial Catalog=pubs;User Id=sa;Password=;";
         cnConn1 = new Connection();
         cnConn1.open(strCnn,"","",AdoEnums.CommandType.UNSPECIFIED);

         // Define a command object for stored procedure.
         cmdByRoyalty = new Command();
         cmdByRoyalty.setActiveConnection(cnConn1);
         cmdByRoyalty.setCommandText("byRoyalty");
         cmdByRoyalty.setCommandType(AdoEnums.CommandType.STOREDPROC);
         cmdByRoyalty.setCommandTimeout(15);

         //Define the stored procedure's input parameter.
         System.out.println ("\nEnter Royalty : ");
         line = in.readLine().trim();
         intRoyalty = Integer.parseInt(line);
         prmByRoyalty = new Parameter ();
         prmByRoyalty.setType(AdoEnums.DataType.INTEGER);
         prmByRoyalty.setSize(3);
         prmByRoyalty.setDirection(AdoEnums.ParameterDirection.INPUT);
         prmByRoyalty.setValue(new Integer(intRoyalty));
         cmdByRoyalty.getParameters().append(prmByRoyalty);

         // Create a recordset by executing the command.

         rstByRoyalty = cmdByRoyalty.execute();

         // Open the Authors table to get author names for display.
         rstAuthors = new Recordset ();
         rstAuthors.open("authors",strCnn,
            AdoEnums.CursorType.FORWARDONLY,
            AdoEnums.LockType.READONLY ,AdoEnums.CommandType.TABLE );

         // Print current data in the recordset,
         // adding author names from Authors table.
         System.out.println("\nAuthors with " + intRoyalty +
            " percent royalty");
         while (!rstByRoyalty.getEOF())
         {
            strAuthorID =  rstByRoyalty.getField("au_id").getString();
            rstAuthors.setFilter("au_id ='" +  strAuthorID + "'");
            strFName = rstAuthors.getField("au_fname").getString();
            strLName = rstAuthors.getField("au_lname").getString();
            System.out.println("\t" + strAuthorID + ", " + strFName
               + " " + strLName);
            rstByRoyalty.moveNext();
         }
         System.out.println("\n\nPress <Enter> key to continue.");
         line = in.readLine();

         //Cleanup objects before exit.
         rstByRoyalty.close();
         rstAuthors.close();
         cnConn1.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // Check for null pointer for connection object
         if(rstByRoyalty.getActiveConnection()==null)
            System.out.println("Exception: " + ae.getMessage());
         if(rstAuthors.getActiveConnection()==null)
            System.out.println("Exception: " + ae.getMessage());
         // As passing a Recordset, check for null pointer first.
         if (rstByRoyalty != null)
         {
            PrintProviderError(rstByRoyalty.getActiveConnection());
         }
         if (rstAuthors != null)
         {
            PrintProviderError(rstAuthors.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // This catch is required if input string cannot be converted to
      // Integer data type.
      catch ( java.lang.NumberFormatException ne)
      {
         System.out.println("\nException: Integer Input required.");
      }
      // System Read requires this catch.
      catch( java.io.IOException je )
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   //.PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}

// EndActiveConnectionJ

See Also

ActiveConnection Property | CommandText Property | CommandTimeout Property | CommandType Property | Direction Property | Size Property


ActualSize and DefinedSize Properties Example (VJ++)

This example uses the ActualSize and DefinedSize properties to display the defined size and actual size of a field.

// BeginActualSizeJ
import com.ms.wfc.data.*;
import java.io.*;

public class ActualSizeX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      ActualSizeX();
      System.exit(0);
   }

   // ActualSizeX function

   static void ActualSizeX()
   {

      // Define ADO Objects.
      Recordset rstStores = null;

      // Declarations.
      BufferedReader in = new
         BufferedReader(new InputStreamReader(System.in));
      String line = null;
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
         + "Initial Catalog=pubs;User Id=sa;Password=;";

      String strStoreName;
      String strMessage;
      String strDSize,strASize;
      int intDefinedSize;
      int intActualSize;
      int intChoice = 0;

      try
      {
         // Open recordset with Stores table.
         rstStores = new Recordset();
         rstStores.open("stores", strCnn,
            AdoEnums.CursorType.FORWARDONLY ,
            AdoEnums.LockType.READONLY ,
            AdoEnums.CommandType.TABLE);

         // Loop through the Recordset displaying the contents
         // of the stor_name field, the field's defined size
         // and it's actual size.

         while ( !(rstStores.getEOF( ))) // continuous loop
         {
            // Read data field in the variables.
            strStoreName = rstStores.getField("stor_name").getString();
            intDefinedSize =
               rstStores.getField("stor_name").getDefinedSize();
            strDSize = Integer.toString(intDefinedSize);
            intActualSize = rstStores.getField
               ("stor_name").getActualSize ();
            strASize = Integer.toString(intActualSize);

            // Display current record information.
            strMessage = "\nStore name: " + strStoreName + "\n"
                + "Defined Size : " + strDSize + "\n"
                + "Actual Size : " + strASize;

            System.out.println(strMessage);
            System.out.println("\nPress <Enter> key to continue.");
            in.readLine();
            rstStores.moveNext();
         }

         // Cleanup objects before exit.
         rstStores.close();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // Check for null pointer for connection object.
         if (rstStores.getActiveConnection()==null)
            System.out.println("Exception: " + ae.getMessage());
         // As passing a Recordset, check for null pointer first.
         if (rstStores != null)
         {
            PrintProviderError(rstStores.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   //.PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndActualSizeJ

See Also

ActualSize Property | DefinedSize Property


AddNew Method Example (VJ++)

This example uses the AddNew method to create a new record with the specified name.

// BeginAddNewJ
import com.ms.wfc.data.*;
import java.io.*;

public class AddNewX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      AddNewX();
      System.exit(0);
   }

   // AddNewX function

   static void AddNewX()
   {
      // Define ADO Objects.
      Connection cnConn1 = null;
      Recordset rstEmployees = null;

      //Declarations.
      String strCnn;
      String strID;
      String strFirstName;
      String strLastName;
      boolean booRecordAdded ;
      BufferedReader in =
new BufferedReader (new InputStreamReader (System.in));
      String line = null;

      try
      {
         // Open a connection.
         strCnn = "Provider=sqloledb;Data Source=MyServer;"
            + "Initial Catalog=pubs;User Id=sa;Password=;";
         cnConn1 = new Connection();
         cnConn1.open(strCnn);//,"","",AdoEnums.CommandType.UNSPECIFIED);

         // Open Employees table.
         rstEmployees = new Recordset ();
         rstEmployees.open("employee", cnConn1,
            AdoEnums.CursorType.KEYSET , AdoEnums.LockType.OPTIMISTIC ,
            AdoEnums.CommandType.TABLE );

         /* Get data from the user. The employee ID must be formatted as
         first,middle, and last initial, five numbers, then M or F to
         signify the gender. For example, the employee id for Bill
         Sornsin would be "B-S55555M". */
         System.out.println("\nEnter employee ID :");
         strID = in.readLine().trim();
         System.out.println("\nEnter first name :");
         strFirstName = in.readLine().trim();
         System.out.println("\nEnter last name :");
         strLastName = in.readLine().trim();

         // Proceed only if the user actually entered something
         // for both the first and last names.

         if ( !(strID.compareTo("") == 0) &
            !(strFirstName.compareTo("") == 0) &
            !(strLastName.compareTo("")== 0))
         {
            // Add new record.
            rstEmployees.addNew();
            rstEmployees.getField("emp_id").setString(strID);
            rstEmployees.getField("fname").setString(strFirstName);
            rstEmployees.getField("lname").setString(strLastName);

            // update the record with the new data.
            rstEmployees.update();
            booRecordAdded = true;

            // Show the newly added data.
            System.out.println("\nNew record : "
               + rstEmployees.getField("emp_id").getString()+ " "
               + rstEmployees.getField("fname").getString()+ " "
               + rstEmployees.getField("lname").getString());
         }
         else
         {
            System.out.println("\nPlease enter an employee ID," +
               "first name, and last name.");
         }

         System.out.println("\n\nPress <Enter> key to continue.");
         line = in.readLine();

         // Delete the new record because this is a demonstration.
         cnConn1.execute("DELETE FROM employee WHERE "
            + "emp_id = '" + strID + "'");

         //Cleanup objects before exit.
         rstEmployees.close();
         cnConn1.close();
         System.exit(0);
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         //Check for null pointer for connection object.
         if(rstEmployees.getActiveConnection()==null)
            System.out.println("Exception: " + ae.getMessage());
         // As passing a Recordset, check for null pointer first.
         if (rstEmployees != null)
         {
            PrintProviderError(rstEmployees.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
         System.exit(0);
      }
      // System Read requires this catch.
      catch( java.io.IOException je )
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   //.PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}

// EndAddNewJ

See Also

AddNew Method | Recordset Object


Append and CreateParameter Methods Example (VJ++)

This example uses the Append and CreateParameter methods to execute a stored procedure with an input parameter.

// BeginAppendJ
import com.ms.wfc.data.*;
import java.io.*;
import com.ms.com.*;

public class AppendX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      AppendX();
   }

   // AppendX function

   static void AppendX()
   {
      // Define ADO Objects.
      Connection cnConn1 = null;
      Command cmdByRoyalty = null;
      Parameter prmByRoyalty = null;
      Recordset rstByRoyalty = null;
      Recordset rstAuthors = null;

      //Declarations.
      String strCnn;
      String strAuthorID;
      String strFName;
      String strLName;
      int intRoyalty ;
      BufferedReader in =
         new BufferedReader (new InputStreamReader (System.in));
      String line = null;
      Variant varRoyalty;

      try
      {
         // Open a connection.

         strCnn = "Provider=sqloledb;Data Source=MyServer;"
            + "Initial Catalog=pubs;User Id=sa;Password=;";
         cnConn1 = new Connection();
         cnConn1.open(strCnn);
         cnConn1.setCursorLocation(AdoEnums.CursorLocation.CLIENT);

         // Open command object with one parameter.
         cmdByRoyalty = new Command();
         cmdByRoyalty.setCommandText("byRoyalty");
         cmdByRoyalty.setCommandType(AdoEnums.CommandType.STOREDPROC);

         //Get parameter value and append parameter.
         System.out.println ("\nEnter Royalty : ");
         line = in.readLine().trim();
         intRoyalty = Integer.parseInt(line);
         varRoyalty = new Variant(intRoyalty);
         prmByRoyalty = cmdByRoyalty.createParameter ("percentage",
            AdoEnums.DataType.INTEGER,
            AdoEnums.ParameterDirection.INPUT,4,varRoyalty);

         cmdByRoyalty.getParameters().append(prmByRoyalty);
         prmByRoyalty.setValue(varRoyalty);

         // Create a recordset by executing the command.
         cmdByRoyalty.setActiveConnection(cnConn1);
         rstByRoyalty = cmdByRoyalty.execute();

         // Open the Authors table to get author names for display.
         rstAuthors = new Recordset ();
         rstAuthors.open("authors", cnConn1,
            AdoEnums.CursorType.FORWARDONLY,
            AdoEnums.LockType.READONLY,
            AdoEnums.CommandType.TABLE );

         // Print current data in the recordset,
         // adding author names from Authors table.
         System.out.println("\nAuthors with " + intRoyalty +
            " percent royalty");
         while (!rstByRoyalty.getEOF())
         {
            strAuthorID =  rstByRoyalty.getField("au_id").getString();
            rstAuthors.setFilter("au_id ='" +  strAuthorID + "'");
            strFName = rstAuthors.getField("au_fname").getString();
            strLName = rstAuthors.getField("au_lname").getString();
            System.out.println("\t" + strAuthorID + ", " + strFName
               + " " + strLName);
            rstByRoyalty.moveNext();
         }
         System.out.println("\n\nPress <Enter> key to continue.");
         line = in.readLine();

         //Cleanup objects before exit.
         rstByRoyalty.close();
         rstAuthors.close();
         cnConn1.close();
         System.exit(0);//required here only.

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // Check for null pointer for connection object.
         if(rstByRoyalty.getActiveConnection()==null)
            System.out.println("Exception: " + ae.getMessage());
         // As passing a Recordset, check for null pointer first.
         if (rstByRoyalty != null)
         {
            PrintProviderError(rstByRoyalty.getActiveConnection());
         }
         if (rstAuthors != null)
         {
            if (rstAuthors.getActiveConnection()==null)
               System.out.println("Exception: " + ae.getMessage());
            else
               PrintProviderError(rstAuthors.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // This catch is required if input string cannot be converted to
      // Integer data type.
      catch( java.lang.NumberFormatException ne)
      {
         System.out.println("\nException: Integer Number required.");
         System.exit(0);
      }
      // System Read requires this catch.
      catch( java.io.IOException je )
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   //.PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}

// EndAppendJ

See Also

Append Method | CreateParameter Method


AppendChunk and GetChunk Methods Example (VJ++)

This example uses the AppendChunk and GetChunk methods to fill an image field with data from another record.

// BeginAppendChunkJ
import com.ms.wfc.data.*;
import java.io.*;
import com.ms.com.*;
import java.util.*;

public class AppendChunkX
{

   // The main entry point for the application.

   public static void main (String[] args)
   {
      AppendChunkX();
      System.exit(0);
   }

   // AppendChunkX function

   static void AppendChunkX()
   {
      // Define ADO Objects.
      Connection cnConn1 = null;
      Recordset rstPubInfo = null;

      //Declarations.
      String strCnn;
      String strPubID;
      String strPRInfo;
      String strMessage = "";
      long lngOffset = 0;
      long lngLogoSize;
      final  int conChunkSize = 100;
      byte[] varChunk = new byte[conChunkSize];
      int intCommand = 0 ;
      int intMulChunkSize,intLastChunkSize;
      Vector varLogo = new Vector();
      BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));
      String line = null;
      int noOfRecords;
      int noOfRecordesDisplayed = 5;
      int recordCount = 0;
      String info;
      int indexOfComma ;

      try
      {
         // Open a connection.
         strCnn = "Provider=sqloledb;Data Source=MyServer;"
            + "Initial Catalog=pubs;User Id=sa;Password=;";
         cnConn1 = new Connection();
         cnConn1.open(strCnn,"","",AdoEnums.CommandType.UNSPECIFIED);

         // Open the pub_info Table.
         rstPubInfo = new Recordset();
         rstPubInfo.setCursorType(AdoEnums.CursorType.KEYSET );
         rstPubInfo.setLockType(AdoEnums.LockType.OPTIMISTIC );

         rstPubInfo.open("pub_info", cnConn1,AdoEnums.CursorType.KEYSET ,
            AdoEnums.LockType.OPTIMISTIC, AdoEnums.CommandType.TABLE );
         System.out.println ("Available logos are : \n");

         noOfRecords = rstPubInfo.getRecordCount();

         // Prompt for the Logo to copy.
         for ( int i = 0; i < noOfRecords; i++)
         {
            recordCount++;
            strMessage = strMessage +
               rstPubInfo.getField("pub_id").getString() + "\n";
            indexOfComma =
               rstPubInfo.getField("pr_info").getString().indexOf(",");
            info =
               rstPubInfo.getField("pr_info").getString().substring(0,
               indexOfComma );
            strMessage = strMessage + info + "\n\n";
            // Display five records at a time.
            if ( recordCount == noOfRecordesDisplayed)
            {
               System.out.println(strMessage);
               System.out.println("\n\nPress <Enter> to continue..");
               line = in.readLine();
               strMessage = "";
               recordCount = 0;
            }
            rstPubInfo.moveNext();
            // Display last records and exit if last record.
            if(rstPubInfo.getEOF())
            {
               System.out.println(strMessage);
               break;
            }
         }
         System.out.println ("\nEnter the ID of a logo to copy :\n"  );
         strPubID = in.readLine();

         // Copy the logo to a variable in chunks.
         rstPubInfo.setFilter("pub_id = '" + strPubID + "'");
         lngLogoSize = rstPubInfo.getField("logo").getActualSize();
         while (lngOffset < lngLogoSize)
         {
            varChunk = rstPubInfo.getField("logo")
               .getByteChunk(conChunkSize);
            int i = 0;
            while (i < conChunkSize && varLogo.size() < (int)lngLogoSize)
            {
               varLogo.addElement(new Byte(varChunk[i]));
               i++;
            }
            lngOffset =  lngOffset + conChunkSize ;
         }
            //Get the data for New ID from the user.
            System.out.println
               ("\nEnter a new pub ID [must be > 9899 & < 9999]:");
            strPubID =  in.readLine().trim();

            System.out.println ("\nEnter descriptive text :");
            strPRInfo =  in.readLine().trim();

            //Temporarily add new publisher to publishers table to
            //avoid getting error foreign key constraint.
            cnConn1.execute("INSERT publishers(pub_id) VALUES('" +
               strPubID + "')");

            //Add a new record.
            rstPubInfo.addNew();
            rstPubInfo.getField("pub_id").setString(strPubID);
            rstPubInfo.getField("pr_info").setString(strPRInfo);

            //Copy the selected logo to the new logo in chunks.
            lngOffset = 0;

            //Divide logosize in multiples of constant chunk size.
            intMulChunkSize =
               (varLogo.size()/conChunkSize) * conChunkSize;
            intLastChunkSize = varLogo.size()- intMulChunkSize ;
            byte[] arrChunk  = new byte[conChunkSize];
            byte[] lastChunk = new byte[intLastChunkSize];

            while ( lngOffset < varLogo.size () )
            {
               int ii = 0 ;
               // Copy the logo in constant chunk size.
               if ( (int)lngOffset < intMulChunkSize)
               {
                  while (ii < conChunkSize &&
                     (int)lngOffset < varLogo.size () )
                  {
                     arrChunk[ii] =
                        ((Byte)varLogo.elementAt
                        ((int)lngOffset)).byteValue();
                     ii++;
                     lngOffset++;
                  }
                  rstPubInfo.getField("logo").appendChunk(arrChunk);
               }
               // Copy the last remaining chunk.
               else
               {
                  while (ii < intLastChunkSize &&
                     (int)lngOffset < varLogo.size () )
                  {
                     lastChunk[ii] =
                        ((Byte)varLogo.elementAt
                        ((int)lngOffset)).byteValue();
                     ii++;
                     lngOffset++;

                  }
                  rstPubInfo.getField("logo").appendChunk(lastChunk);
               }

            }

            // Update the new recordset with new logo.

            rstPubInfo.update();

            //Show the newly added data.
            System.out.println ("\nNew Record : " +
               rstPubInfo.getField("pub_id").getString() + "\n");
            System.out.println ("Description : " +
               rstPubInfo.getField("pr_info").getString() + "\n");
            System.out.println ("Logo Size : " +
               rstPubInfo.getField("logo").getActualSize() );

            System.out.println ("\n\nPress <Enter> key to continue.");
            in.readLine();

            //Delete new records because this is a demonstration.
            rstPubInfo.requery();
            cnConn1.execute("DELETE FROM pub_info WHERE  pub_id = '" +
               strPubID + "'");
            cnConn1.execute("DELETE FROM publishers WHERE  pub_id = '" +
               strPubID + "'");

            //Cleanup objects before exit.
            rstPubInfo.close();
            cnConn1.close();
      }
      catch( AdoException ae )
         {
            // Notify user of any errors that result from ADO.

            // Check for null pointer for connection object.
            if(rstPubInfo.getActiveConnection()==null)
                  System.out.println("Exception: " + ae.getMessage());
            // As passing a Recordset, check for null pointer first.
            if (rstPubInfo != null)
            {
               PrintProviderError(rstPubInfo.getActiveConnection());
               PrintADOError(ae);
            }
            else
            {
               System.out.println("Exception: " + ae.getMessage());
            }
      }
      // System Read requires this catch.
      catch( java.io.IOException je )
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }

   // PrintADOError Function

   static void PrintADOError(AdoException ae)
   {
      System.out.println("\t Error Source = " + ae.getSource() + "\n");
      System.out.println("\t Description = " + ae.getMessage() + "\n");
   }
}
// EndAppendChunkJ

See Also

AppendChunk Method | Field Object | GetChunk Method


Attributes and Name Properties Example (VJ++)

This example displays the value of the Attributes property for Connection, Field, and Property objects. It uses the Name property to display the name of each Field and Property object.

// BeginAttributesJ
import com.ms.wfc.data.*;
import java.io.*;

public class AttributesX
{
   // The main entry point for the application
   public static void main (String[] args)
   {
      AttributesX();
      System.exit(0);
   }

   // AttributeX Function

   static void AttributesX()
   {
      // Define ADO Objects.
      Connection cnConn1 = null;
      Recordset rstEmployees = null;
      Fields listOfFields = null;
      AdoProperties listOfProperties = null;

      //Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String line = null;
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
         + "Initial Catalog=pubs;User Id=sa;Password=;";
      int recordDisplaySize = 15;
      int propertyCount = 0;
      try
      {
         // Open connection and recordset.
         cnConn1 = new Connection();
         cnConn1.open(strCnn);

         rstEmployees = new Recordset ();
         rstEmployees.open("employee",
            cnConn1,AdoEnums.CursorType.FORWARDONLY,
            AdoEnums.LockType.READONLY, AdoEnums.CommandType.TABLE);

         // Display the attributes of the connection.
         System.out.println("Connection attributes = "
            + cnConn1.getAttributes());
         // Display the attributes of the Employees table's fields.
         System.out.println("\n\nField attributes : " + "\n");

         listOfFields = rstEmployees.getFields();

         for ( int i=0; i < listOfFields.getCount();i++)
         {
            System.out.println("\t\t" + listOfFields.getItem(i).getName()
               + " = " + listOfFields.getItem(i).getAttributes());
         }

         // Display fields of the Employees table which are NULLABLE.
         System.out.println("\n\nNULLABLE Fields : " + "\n");
         for ( int i=0; i < listOfFields.getCount();i++)
         {
            if ((listOfFields.getItem(i).getAttributes() &
               AdoEnums.FieldAttribute.ISNULLABLE) > 0)
               System.out.println("\t\t" +
               listOfFields.getItem(i).getName());

         }

         System.out.println ("\n\nPress <Enter> key to continue..");
         line = in.readLine();

         // Display the attributes of the Employees table's properties.
         System.out.println("\n\nProperty attributes : " );
         listOfProperties = rstEmployees.getProperties();
         for ( int i=0; i < listOfProperties.getCount() ;i++)
         {
            System.out.println("\t\t" +
               listOfProperties.getItem(i).getName()
               + " = " + listOfProperties.getItem(i).getAttributes());

            if (propertyCount == recordDisplaySize)
            {
               System.out.println ("\n\nPress <Enter> key to continue.");
               line = in.readLine();
               propertyCount = 0;
            }
            propertyCount++;
         }

         System.out.println ("\n\nPress <Enter> key to continue.");
         line = in.readLine();

         //Cleanup objects before exit.
         rstEmployees.close();
         cnConn1.close();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // Check for null pointer for connection object.
         if (rstEmployees.getActiveConnection()==null)
            System.out.println("Exception: " + ae.getMessage());

         // As passing a Recordset, check for null pointer first.

         if (rstEmployees != null)
         {
            PrintProviderError(rstEmployees.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   //.PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}

// EndAttributesJ

See Also

Attributes Property | Connection Object | Field Object | Name Property | Property Object


BeginTrans, CommitTrans, and RollbackTrans Methods Example (VJ++)

This example changes the book type of all psychology books in the Titles table of the database. After the BeginTrans method starts a transaction that isolates all the changes made to the Titles table, the CommitTrans method saves the changes. You can use the Rollback method to undo changes that you saved using the Update method.

// BeginBeginTransJ
import com.ms.wfc.data.*;
import java.io.*;

public class BeginTransX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      BeginTransX();
      System.exit(0);
   }

   // BeginTransX function
   static void BeginTransX()
   {
      // Define ADO Objects.
      Connection cnConn1 = null;
      Recordset rstTitles = null;

      //Declarations.
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
         + "Initial Catalog=pubs;User Id=sa;Password=;";
      String strTitle;
      String strType;
      BufferedReader in =
         new BufferedReader (new InputStreamReader (System.in));
      String line = null;
      String strMessage="";
      int intChoice = 0;
      int recordDisplaySize = 15;
      int recordCount = 0;

      try
      {
         // Open a connection.

         cnConn1 = new Connection();
         cnConn1.open(strCnn,"","",AdoEnums.CommandType.UNSPECIFIED);

         // Open the Titles table.
         rstTitles = new Recordset ();
         rstTitles.setCursorType(AdoEnums.CursorType.DYNAMIC);
         rstTitles.setLockType(AdoEnums.LockType.PESSIMISTIC);
         rstTitles.open("titles",cnConn1,AdoEnums.CursorType.DYNAMIC ,
            AdoEnums.LockType.PESSIMISTIC ,AdoEnums.CommandType.TABLE );

         rstTitles.moveFirst();
         cnConn1.beginTrans();

         // Loop through recordset and ask user if he/she wants
         // to change the type for a specified title.

         while (!rstTitles.getEOF())
         {
            strType = rstTitles.getField("type").getString().trim();
            if ((rstTitles.getField("type").getString().
               trim()).compareTo("psychology")== 0)
            {
               strTitle = rstTitles.getField("title").getString().trim();
               System.out.println("\nTitle : " + strTitle + "\n\n"
                  +  "Change type to self help (1 -> Yes / 2 -> No) ?");
               // Change the title for the specified book.
               line = in.readLine().trim();
               intChoice = Integer.parseInt(line);
               if ( intChoice == 1)
               {
                  rstTitles.getField("type").setString("self_help");
                  rstTitles.update();
               }
            }
            rstTitles.moveNext();
         }

         // Ask if the user wants to commit to all the
         // changes made above.
         System.out.println
            ("\n\nSave all changes (1 -> Yes / 2 -> No) ?");
         line = in.readLine().trim();
         intChoice = Integer.parseInt(line);
         if ( intChoice == 1)
            cnConn1.commitTrans();
         else
            cnConn1.rollbackTrans();

         // Print current data in recordset.
         rstTitles.requery();
         rstTitles.moveFirst();

         while(true)
         {
            strMessage = strMessage +"\n " +
               rstTitles.getField("title").getString()+" - "
               + rstTitles.getField("type").getString() ;

            if ( recordCount == recordDisplaySize)
            {
               System.out.println(strMessage);
               System.out.println("\n\nPress <Enter> key to continue..");
               line = in.readLine();
               strMessage = "";
               recordCount = 0;
            }
            recordCount++;
            rstTitles.moveNext();
            if(rstTitles.getEOF())
            {
               System.out.println(strMessage);
               break;
            }
         }
         System.out.println("\n\nPress <Enter> key to continue..");
         line = in.readLine();

         // Restore original data because this
         // is a demonstration.
         rstTitles.moveFirst();
         while (!rstTitles.getEOF())
         {
            if ((rstTitles.getField("type").getString().
               trim().compareTo("self_help"))== 0)
            {
               rstTitles.getField("type").setString("psychology");
               rstTitles.update();
            }
            rstTitles.moveNext();
         }
         //Cleanup objects before exit.
         rstTitles.close();
         cnConn1.close();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // Check for null pointer for connection object.
         if(rstTitles.getActiveConnection()==null)
            System.out.println("Exception: " + ae.getMessage());

         // As passing a Recordset, check for null pointer first.
         if (rstTitles != null)
         {
            PrintProviderError(rstTitles.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }
      // System Read requires this catch.
      catch( java.io.IOException je )
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   //.PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}

// EndBeginTransJ

See Also

BeginTrans, CommitTrans, and RollbackTrans Methods | Update Method


BOF, EOF, and Bookmark Properties Example (VJ++)

This example uses the BOF and EOF properties to display a message if a user tries to move past the first or last record of a Recordset. It uses the Bookmark property to let the user flag a record in a Recordset and return to it later.

// BeginBOFEOFJ
import com.ms.wfc.data.*;
import java.io.*;
import com.ms.com.*;

public class BOFEOFBookmark
{
   Variant varBookmark;
   BufferedReader in =
      new BufferedReader(new InputStreamReader(System.in));
   String line = null;

   // The main entry point for the application.

   public static void main (String[] args)
   {
      BOFEOFBookmark b1 = new BOFEOFBookmark ();
      b1.BOFX();
      b1.BOFX2();
      System.exit(0);
      try
      {
         b1.finalize();
      }
      catch(Throwable te)
      {
      }
   }

   // The main entry point for the application.

   public void BOFX()
   {
      // Declarations.
      Recordset rstPublishers = null;
      String strCnn;
      String strMessage;
      int intCommand = 0;

      strCnn = "Provider=sqloledb;Data Source=MyServer;"
         + "Initial Catalog=pubs;User Id=sa;Password=;";
      try
      {
         // Open a recordset with data from Publishers table.
         rstPublishers = new Recordset();
         rstPublishers.setCursorType(AdoEnums.CursorType.STATIC );
         rstPublishers.setCursorLocation(
            AdoEnums.CursorLocation.CLIENT);

         // Use client cursor to enable AbsolutePosition property.

         rstPublishers.open(new String(
            "SELECT pub_id,pub_name FROM Publishers ORDER BY pub_name"),
            strCnn, AdoEnums.CursorType.STATIC ,
            AdoEnums.LockType.BATCHOPTIMISTIC,
            AdoEnums.CommandType.TEXT );
         rstPublishers.moveFirst();

         //Display information about current record and get user input.
         while ( true)
         {
            strMessage = "\nPublisher :" +
               rstPublishers.getField("pub_name").getString() + "\n"
               + " (Record " + rstPublishers.getAbsolutePosition()
               + " of " + rstPublishers.getRecordCount() + ")" + "\n\n"
               + "Enter command : " + "\n"
               + "[1 - next / 2 - previous /" + "\n"
               + "3 - set bookmark / 4 - go to bookmark]" ;
            System.out.println (strMessage);
            line = in.readLine();
            //No entry exits loop.
            if (line.length() == 0)
               break;
            //convert string entry to int.
            intCommand = Integer.parseInt(line);
            //out of range entry exits loop.
            if ((intCommand < 1) || (intCommand > 4)) break ;

            //Call method based on user's validated selection.
            MoveAny(intCommand, rstPublishers);
         }
         rstPublishers.close();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstPublishers != null)
         {
            PrintProviderError(rstPublishers.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // This catch is required if input string cannot be converted to
      // Integer data type. "TCS[VSD]"

      catch ( java.lang.NumberFormatException ne)
      {
         System.out.println("\nException: Integer Input required." );
      }

      // System Read requires this catch.
      catch( java.io.IOException je )
      {
         PrintIOError(je);
      }
   }

   // MoveAny Function

   public void MoveAny(int intChoice, Recordset rsTemp)
   {
      // Move Forward or backword per selection from user,
      // trapping for BOF and EOF.
      try
      {
         switch(intChoice)
         {
            case 1:   // Equals char of 1.
               rsTemp.moveNext();
               if (rsTemp.getEOF())
               {
                  System.out.println (
                     "\nMoving past the last record \nTry again." );
                  rsTemp.moveLast();
               }
               break;
            case 2:   // Equals char of 2.
               rsTemp.movePrevious();
               if (rsTemp.getBOF())
               {
                  System.out.println (
                     "\nMoving past the first record \nTry again." );
                  rsTemp.moveFirst();
               }
               break;
            case 3:   // Equals char of 3.
               // Store the bookmark of the current record.
               varBookmark = (Variant)rsTemp.getBookmark();
               break;
            case 4:   // Equals char of 4.
               // Go to the record indicated by the stored bookmark.
               if (varBookmark == null)
                  System.out.println ("\nNo bookmark set!");
               else
                  rsTemp.setBookmark((Object)varBookmark);
               break;
            default:
               break;
         }
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rsTemp != null)
         {
            PrintProviderError(rsTemp.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   //.PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }

       ////////////////////////////////////////////
      //            BOFX2() Function.           //
     ////////////////////////////////////////////

      public void BOFX2()
      {
         Recordset rs = null ;

      try
         {
            // Declarations.
            rs = new Recordset();
            Variant[] arrbmk = new Variant[11];
            rs.setCursorLocation( AdoEnums.CursorLocation.CLIENT);
            String strCnn = "Provider=MSDASQL;Data Source=Pubs DSN;"
                  + "Initial Catalog=pubs;User Id=sa;Password=;";
            rs.setActiveConnection (strCnn);
            // Open recorset with data from authors table.
            rs.open((new  String("SELECT * FROM authors")),strCnn,AdoEnums.CursorType.STATIC ,AdoEnums.LockType.BATCHOPTIMISTIC,AdoEnums.CommandType.TEXT );
            System.out.println ("\nNumber of records before filtering : " + rs.getRecordCount() );
            int ii = 0;

            // Create array of bookmarks.
            while (rs.getEOF() != true && ii < 11)
            {
               arrbmk[ii] = (Variant)rs.getBookmark();
               ii++;
               rs.move (2);
            }

            // set Filter to recordset.
            Variant bmk=new Variant();
            bmk.putVariantArray(arrbmk);
            rs.setFilter(bmk);
            System.out.println ("\nNumber of records after filtering : " + rs.getRecordCount() );

            // Dislay the records after filtering.
            rs.moveFirst();
            while (!rs.getEOF())
            {
               System.out.println ("\t" +rs.getAbsolutePosition() + "  " +rs.getField("au_lname").getString());
               rs.moveNext();
            }
            rs.close();
            System.out.println ("\n\nPress <Enter> key to continue.");
            in.readLine();

         }
         catch( AdoException ae )
         {
            // Notify user of any errors that result from ADO.

            // As passing a Recordset, check for null pointer first.
            if (rs != null)
            {
               PrintProviderError(rs.getActiveConnection());
               System.out.println("Exception: " + ae.getMessage());
            }
            else
            {
               System.out.println("Exception: " + ae.getMessage());
            }
         }
         // System Read requires this catch.
         catch( java.io.IOException je )
         {
            PrintIOError(je);
         }
      }
   }

// EndBOFEOFJ

See Also

BOF, EOF Properties | Bookmark Property | Recordset Object


CacheSize Property Example (VJ++)

This example uses the CacheSize property to show the difference in performance for an operation performed with and without a 30-record cache.

// BeginCacheSizeJ
import com.ms.wfc.data.*;
import java.io.* ;

public class CacheSizeX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      CacheSizeX();
      System.exit(0);
   }

   // CacheSizeX function

   static void CacheSizeX()
   {

      // Define ADO Objects.
      Recordset rstRoySched = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String line = null;
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
         + "Initial Catalog=Pubs;User Id=sa;Password=;";

      long timeStart;
      long timeEnd;
      float timeNoCache;
      float timeCache;
      int   intLoop;
        String strTemp;

      try
      {
         // Open the RoySched table.
         rstRoySched = new Recordset();
         rstRoySched.open("roysched", strCnn,
            AdoEnums.CursorType.FORWARDONLY,
            AdoEnums.LockType.READONLY,
            AdoEnums.CommandType.TABLE);

         // Enumerate the Recordset object twice and record
         // the elapsed time.

         timeStart = System.currentTimeMillis();
         for ( intLoop = 0; intLoop < 2; intLoop++)
         {
            rstRoySched.moveFirst();
            while (!rstRoySched.getEOF())
            {
               // Execute a simple operation for the
               // performance test.
               strTemp = rstRoySched.getField("title_id").getString();
               rstRoySched.moveNext();
            }

         }

         timeEnd = System.currentTimeMillis();
         timeNoCache =(float)(timeEnd - timeStart)/1000f;

         // Cache records in groups of 30 records.
         rstRoySched.moveFirst();
         rstRoySched.setCacheSize(30);
         timeStart = System.currentTimeMillis();

         // Enumerate the Recordset object twice and record
         // the elapsed time.
         for ( intLoop = 0; intLoop < 2; intLoop++)
         {
            rstRoySched.moveFirst();
            while (!rstRoySched.getEOF())
            {
               // Execute a simple operation for the
               // performance test.

               strTemp = rstRoySched.getField("title_id").getString();
               rstRoySched.moveNext();
            }

         }

         timeEnd = System.currentTimeMillis();
         timeCache = (float)(timeEnd - timeStart)/1000f;

         // Display performance results.
         System.out.println("\nCaching Performance Results:");
         System.out.println("\n\tNo Cache: " + timeNoCache + " seconds");
         System.out.println("\n\t30-record cache: " + timeCache +
            " seconds");
         System.out.println("\n\nPress <Enter> to continue..");
         in.readLine();

         // Cleanup objects before exit.
         rstRoySched.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // Check for null pointer for connection object.
         if (rstRoySched.getActiveConnection()==null)
            System.out.println("Exception: " + ae.getMessage());

         // As passing a Recordset, check for null pointer first.
         if (rstRoySched != null)
         {
            PrintProviderError(rstRoySched.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }

   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}

// EndCacheSizeJ

See Also

CacheSize Property | Recordset Object


Cancel Method Example (VJ++)

This example uses the Cancel method to cancel a command executing on a Connection object if the connection is busy.

// BeginCancelJ
import com.ms.wfc.data.*;
import java.io.* ;

public class CancelX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      CancelX();
      System.exit(0);
   }

   // CancelX function

   static void CancelX()
   {
      // Define command strings.
      String strCmdChange = "UPDATE titles SET type = 'self_help' "
         + "WHERE type = 'psychology'";
      String strCmdRestore = "UPDATE titles SET type = 'psychology' "
         + "WHERE type = 'self_help'";
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
         + "Initial Catalog=Pubs;User Id=sa;Password=;";

      // Define ADO Objects.
      Connection cnConn1 = null;

      //Declarations.
      boolean booChanged = false;
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String line = null;

      try
      {
         // Open a connection.
         cnConn1 = new Connection();
         cnConn1.open(strCnn);

         // Begin a transaction, then execute a command asynchronously.
         cnConn1.beginTrans();
         cnConn1.execute(strCmdChange,
            AdoEnums.ExecuteOption.ASYNCEXECUTE);

         // do something else for a little while - this could be changed.

         for (int intLoop = 0; intLoop < 10; intLoop++)
         {
            System.out.println(intLoop);
         }

         // If the command has NOT completed, cancel the execute
         // and roll back the transaction. Otherwise, commit the
         // transaction.

         if ((cnConn1.getState() & AdoEnums.ObjectState.EXECUTING) > 0)
         {
            cnConn1.cancel();
            cnConn1.rollbackTrans();
            booChanged = false;
            System.out.println("\nUpdate canceled.");
         }
         else
         {
            cnConn1.commitTrans();
            booChanged = true;
            System.out.println("\nUpdate complete.");
         }

         //If the change was made, restore the data
         // because this is a demonstration.

         if(booChanged )
         {
            cnConn1.execute(strCmdRestore);
            System.out.println("\nData restored.");
         }

         System.out.println("\n\nPress <Enter> to continue..");
         in.readLine();

         // Cleanup objects before exit.
         cnConn1.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a connection, check for null pointer first.
         if (cnConn1 != null)
         {
            PrintProviderError(cnConn1);
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}

// EndCancelJ

See Also

Cancel Method | Connection Object


Clone Method Example (VJ++)

This example uses the Clone method to create copies of a Recordset and then lets the user position the record pointer of each copy independently.

// BeginCloneJ
import com.ms.wfc.data.*;
import java.io.* ;

public class CloneX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      CloneX();
      System.exit(0);
   }

   // CloneX function

   static void CloneX()
   {
      // Assign SQL statement and connection string to variables.
      String strSQL = "SELECT stor_name FROM Stores "
         + "ORDER BY stor_name";

      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
         + "Initial Catalog=Pubs;User Id=sa;Password=;";

      // Define ADO Objects.
      Recordset[] arstStores = null;

      //Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String line = null;
      String strMessage;
      String strFind;
      int  intLoop;
      boolean booExit = true;

      try
      {
         // Open recordset as a static cursor type recordset.
         arstStores = new Recordset[3];
         arstStores[0] = new Recordset();
         arstStores[0].setCursorType(AdoEnums.CursorType.STATIC);
         arstStores[0].setLockType(AdoEnums.LockType.BATCHOPTIMISTIC);
         arstStores[0].open(strSQL,strCnn,AdoEnums.CursorType.STATIC,
            AdoEnums.LockType.BATCHOPTIMISTIC,AdoEnums.CommandType.TEXT);

         // Create two clones of the original Recordset.
         arstStores[1] = (Recordset)arstStores[0].clone
            (AdoEnums.LockType.BATCHOPTIMISTIC);
         arstStores[2] = (Recordset)arstStores[0].clone
            (AdoEnums.LockType.BATCHOPTIMISTIC);

         while(booExit)
         {
            // Loop through the array so that on each pass,
            // the user is searching a different copy of the
            // same Recordset.
            for (intLoop = 0; intLoop < 3; intLoop++)
            {
               // Ask for search string while showing where
               // the current record pointer is for each Recordset
               strMessage = "\nRecordsets from stores table:" + "\n"
                  +  "  1 - Original - Record pointer at "
                  + arstStores[0].getField("stor_name").getString()
                  + "\n" + "  2 - Clone - Record pointer at "
                  + arstStores[1].getField("stor_name").getString()
                  + "\n" + "  3 - Clone - Record pointer at "
                  + arstStores[2].getField("stor_name").getString()
                  + "\n";
               System.out.println(strMessage);
               System.out.println("Enter search string for #"
                  + (intLoop+1) + "(Press <Enter> to Exit.)");

            strFind = in.readLine().trim();
               if(strFind.length() == 0)
               {
                  booExit = false;
                  break;
               }

               // Find the search string; if there's no
               // match, jump to the last record.
               arstStores[intLoop].setFilter("stor_name >= '" +
                  strFind + "'");
               if (arstStores[intLoop].getEOF())
               {
                  arstStores[intLoop].setFilter
                     (new Integer(AdoEnums.FilterGroup.NONE));
                  arstStores[intLoop].moveLast();
               }
            }
         }
         // Cleanup objects before exit.
         arstStores[0].close();
         arstStores[1].close();
         arstStores[2].close();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a connection, check for null pointer first.
         if (arstStores[0] != null)
         {
            PrintProviderError(arstStores[0].getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }

   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndCloneJ

See Also

Clone Method | Recordset Object


CompareBookmarks Method Example (VJ++)

This example demonstrates the CompareBookmarks method. The relative value of bookmarks is seldom needed unless a particular bookmark is somehow special.

Designate a random row of a Recordset derived from the Authors table as the target of a search. Then display the position of each row relative to that target.

// BeginCompareBookmarksJ
import com.ms.wfc.data.*;
import java.io.* ;
import com.ms.com.*;

public class CompareBookmarksX //
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      CompareBookmarksX();
      System.exit(0);
   }

   // CompareBookmarksX function

   static void CompareBookmarksX()
   {
      // Define ADO Objects.
      Recordset rstAuthors = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String strCnn = "DSN=Pubs;Provider=MSDASQL;uid=sa;pwd=;";
      int intCount;
      Variant varTarget = null;
      int intResult;
      String strAns;
      int intDisplaySize = 15;

      try
      {
         rstAuthors = new Recordset();
         rstAuthors.open("SELECT * FROM authors",
            strCnn,
            AdoEnums.CursorType.STATIC,
            AdoEnums.LockType.READONLY,
            AdoEnums.CommandType.TEXT);
         intCount = rstAuthors.getRecordCount();
         System.out.println("Rows in the Recordset = " +
            Integer.toString(intCount));

         // Exit if an empty recordset.
         if(intCount == 0)
            System.exit(0);

         // Randomize.
         intCount = (int)(intCount * Math.random());
         // Get position between 0 and count-1.
         System.out.println("\nRandomly chosen row position = " +
            Integer.toString(intCount)+ "\n");
         rstAuthors.move(intCount,new Integer(AdoEnums.Bookmark.FIRST));          // Move row to random position.
         varTarget = (Variant)rstAuthors.getBookmark();
         // Remember the mystery row.
         intCount = 0;
         rstAuthors.moveFirst();

         // Loop through recordset.
         while(!rstAuthors.getEOF())
         {
            intResult = rstAuthors.compareBookmarks
               ((Variant)rstAuthors.getBookmark(), varTarget);
            if(intResult == AdoEnums.Compare.NOTEQUAL)
               System.out.println("Row " +
                  Integer.toString(intCount) +
                  ": Bookmarks are not equal.");
            else if(intResult == AdoEnums.Compare.NOTCOMPARABLE)
               System.out.println("Row " +
                  Integer.toString(intCount) +
                  ": Bookmarks are not comparable.");
            else
            {
               switch(intResult)
               {
               case AdoEnums.Compare.LESSTHAN :
                  strAns = "less than";
                  break;
               case AdoEnums.Compare.EQUAL :
                  strAns = "equal to";
                  break;
               case AdoEnums.Compare.GREATERTHAN :
                  strAns = "greater than";
                  break;
               default :
                  strAns = "in error comparing to";
                  break;
               }
               System.out.println("Row position " +
                  Integer.toString(intCount) +
                  " is " + strAns + " the target.");
            }
            if(intCount % intDisplaySize == 0 && intCount > 0)
            {
               System.out.println("\nPress <Enter> to continue..");
               in.readLine();
            }
            intCount++;
            rstAuthors.moveNext();
         }

         // Cleanup objects before exit.
         rstAuthors.close();
         System.out.println("\nPress <Enter> to continue..");
         in.readLine();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstAuthors != null)
         {
            PrintProviderError(rstAuthors.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}

// EndCompareBookmarksJ

See Also

CompareBookmarks Method | Recordset Object


ConnectionString, ConnectionTimeout, and State Properties Example (VJ++)

This example demonstrates different ways of using the ConnectionString property to open a Connection object. It also uses the ConnectionTimeout property to set a connection timeout period, and the State property to check the state of the connections. The GetState function is required for this procedure to run.

// BeginConnectionStringJ
import com.ms.wfc.data.*;
import java.io.* ;

public class ConnectionStringX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      ConnectionStringX();
      System.exit(0);
   }

   // ConnectionStringX function

   static void ConnectionStringX()
   {
      // Define ADO Objects.
      Connection cnConn1 = null;
      Connection cnConn2 = null;
      Connection cnConn3 = null;
      Connection cnConn4 = null;

      //Declarations.

      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String line = null;
      String strTemp;

      try
      {
         // Open a connection using OLE DB syntax.
         cnConn1 = new Connection();
         cnConn1.setConnectionString("provider=SQLOLEDB;"
            + "Data Source=MyServer;Initial Catalog=Pubs;"
            + "User Id=sa;Password=;");
         cnConn1.setCommandTimeout(30);
         cnConn1.open();

         // Open a connection using a DSN and ODBC tags.
         cnConn2 = new Connection();
         cnConn2.setConnectionString("DSN=Pubs;UID=sa;PWD=;");
         cnConn2.open();

         // Open a connection using a DSN and OLE DB tags.
         cnConn3 = new Connection();
         cnConn3.setConnectionString
            ("Data Source=Pubs;User ID=sa;Password=;");
         cnConn3.open();

         // Open a connection using a DSN and individual
         // arguments instead of a connection string.
         cnConn4 = new Connection();
         cnConn4.open("Pubs", "sa", "");

         // Display the state of the connections.
         strTemp = getState(cnConn1.getState());
         System.out.println("CnConn1 state: " + strTemp);
         strTemp = getState(cnConn2.getState());
         System.out.println("CnConn2 state: " + strTemp);
         strTemp = getState(cnConn3.getState());
         System.out.println("CnConn3 state: " + strTemp);
         strTemp = getState(cnConn4.getState());
         System.out.println("CnConn4 state: " + strTemp);

         System.out.println("\n\nPress <Enter> to continue..");
         in.readLine();

         // Cleanup objects before exit.
         cnConn1.close();
         cnConn2.close();
         cnConn3.close();
         cnConn4.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         System.out.println("Exception: " + ae.getMessage());

      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // getState Function

   static String getState(int intState)
   {
      // Returns current state of the connection object.
      String strState=null;

      switch(intState)
      {
      case AdoEnums.ObjectState.CLOSED :
         strState = new String("adStateClosed");
         break;
      case AdoEnums.ObjectState.OPEN  :
         strState = new String("adStateOpen");
         break;
      default :
         break;
      }
      return strState;
   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}

// EndConnectionStringJ

See Also

Connection Object | ConnectionString Property | ConnectionTimeout Property | State Property


Count Property Example (VJ++)

This example demonstrates the Count property with two collections in the Employees database. The property obtains the number of objects in each collection, and sets the upper limit for loops that enumerate these collections. Another way to enumerate these collections without using the Count property would be to use For Each...Next statements.

// BeginCountJ
import com.ms.wfc.data.*;
import java.io.* ;

public class CountX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      CountX();
      System.exit(0);
   }

   // CountX function

   static void CountX()
   {

      // Define ADO Objects.
      Recordset rstEmployees = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String line = null;
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
         + "Initial Catalog=Pubs;User Id=sa;Password=;";

      int intLoop;
      int intDisplaySize = 20;
      int recCount=0;

      try
      {
         rstEmployees = new Recordset();

         // Open recordset with data from Employees table.
         rstEmployees.open("employee", strCnn,
            AdoEnums.CursorType.FORWARDONLY,
            AdoEnums.LockType.READONLY,
            AdoEnums.CommandType.TABLE);

         // Print information about Fields collection.
         System.out.println(rstEmployees.getFields().getCount() +
            " Fields in Employees");
         for ( intLoop = 0; intLoop <
            rstEmployees.getFields().getCount(); intLoop++)
         {
            System.out.println("\t" +
               rstEmployees.getFields().getItem(intLoop).getName());
         }
         System.out.println("\n\nPress <Enter> to continue..");
         in.readLine();

         // Print information about Properties collection.
         System.out.println(rstEmployees.getProperties().getCount() +
            " Properties in Employees");
         for ( intLoop = 0; intLoop <
            rstEmployees.getProperties().getCount(); intLoop++)
         {
            System.out.println("\t" +
               rstEmployees.getProperties().getItem(intLoop).getName());
            recCount++;
            if ( recCount >= intDisplaySize)
            {
               System.out.println("\n\nPress <Enter> to continue..");
               in.readLine();
               recCount = 0;
            }
         }
         System.out.println("\n\nPress <Enter> to continue..");
         in.readLine();

         // Cleanup objects before exit.
         rstEmployees.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // Check for null pointer for connection object.
         if (rstEmployees.getActiveConnection()==null)
         {
            System.out.println("Exception: " + ae.getMessage());
         }
         else
         {
            // As passing a Recordset, check for null pointer first.
            if (rstEmployees != null)
            {
               PrintProviderError(rstEmployees.getActiveConnection());
            }
            else
            {
               System.out.println("Exception: " + ae.getMessage());
            }
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}

// EndCountJ

See Also

Count Property


CursorType, LockType, and EditMode Properties Example (VJ++)

This example demonstrates setting the CursorType and LockType properties before opening a Recordset. It also shows the value of the EditMode property under various conditions. The EditModeOutput function is required for this procedure to run.

// BeginEditModeJ
import com.ms.wfc.data.*;
import java.io.* ;

public class EditModeX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      EditModeX ();
      System.exit(0);
   }

   // EditModeX function

   static void EditModeX ()
   {

      // Define ADO Objects.
      Connection cnConn1 = null;
      Recordset rstEmployees = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String line = null;
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
            + "Initial Catalog=Pubs;User Id=sa;Password=;";

      try
      {
         // Open recordset with data from Employees table.

         cnConn1 = new Connection();
         cnConn1.open(strCnn);
         rstEmployees = new Recordset();
         rstEmployees.setActiveConnection(cnConn1);
         rstEmployees.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rstEmployees.setCursorType(AdoEnums.CursorType.STATIC);
         rstEmployees.setLockType(AdoEnums.LockType.BATCHOPTIMISTIC);
         rstEmployees.open("employee", cnConn1,
            AdoEnums.CursorType.STATIC,
            AdoEnums.LockType.BATCHOPTIMISTIC,
            AdoEnums.CommandType.TABLE);

         // Show the EditMode property under different editing states.

         rstEmployees.addNew();
         rstEmployees.getField("emp_id").setString("T-T55555M");
         rstEmployees.getField("fname").setString("temp_fname");
         rstEmployees.getField("lname").setString("temp_lname");
         EditModeOutput("After AddNew:", rstEmployees.getEditMode());
         rstEmployees.updateBatch();
         EditModeOutput("After Update:", rstEmployees.getEditMode());
         rstEmployees.getField("fname").setString("test");
         EditModeOutput("After Edit:", rstEmployees.getEditMode());

         System.out.println("\n\nPress <Enter> to continue..");
         in.readLine();

         // Delete new record because this is a demonstration.
         cnConn1.execute(
            "DELETE FROM employee WHERE emp_id = 'T-T55555M'");

         // Cleanup objects before exit.
         rstEmployees.close();
         cnConn1.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // Check for null pointer for connection object.
         if (cnConn1==null)
            System.out.println("Exception: " + ae.getMessage());

         // As passing a Recordset, check for null pointer first.
         if (rstEmployees != null)
         {
            PrintProviderError(rstEmployees.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // EditModeOutput Function

   static void EditModeOutput(String strTemp, int intEditMode)
   {
      String strMessage="";
      // Print report based on the value of the EditMode
      // property.

      System.out.println("\n" + strTemp);
      strMessage ="\n\tEditMode = ";
      switch(intEditMode)
      {
      case AdoEnums.EditMode.NONE :
         strMessage+="adEditNone";
         break;
      case AdoEnums.EditMode.INPROGRESS :
         strMessage+="adEditInProgress";
         break;
      case AdoEnums.EditMode.ADD :
         strMessage+="adEditAdd";
         break;
      default :
         break;
      }
      System.out.println(strMessage);
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndEditModeJ

See Also

CursorType Property | EditMode Property | LockType Property | Recordset Object


Delete Method Example (VJ++)

This example uses the Delete method to remove a specified record from a Recordset.

// BeginDeleteJ
// The WFC class includes the ADO objects.

import com.ms.wfc.data.*;
import java.io.* ;

public class DeleteX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      DeleteX();
      System.exit(0);
   }

   // DeleteX function

   static void DeleteX()
   {

      // Define ADO Objects.
      Recordset rstRoySched = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String line = null;
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
         + "Initial Catalog=Pubs;User Id=sa;Password=;";

      String  strMessage="";
      String  strTitleID;
      int intLoRange =0;
      int intHiRange =0;
      int intRoyalty =0;
      boolean bolFound;

      try
      {
         rstRoySched = new Recordset();
         rstRoySched.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rstRoySched.setCursorType(AdoEnums.CursorType.STATIC);
         rstRoySched.setLockType(AdoEnums.LockType.BATCHOPTIMISTIC);

         // Open RoySched table.
         rstRoySched.open("SELECT * FROM roysched " +
            "WHERE royalty = 20", strCnn,
            AdoEnums.CursorType.STATIC,
            AdoEnums.LockType.BATCHOPTIMISTIC,
            AdoEnums.CommandType.TEXT);

         // Prompt for a record to delete.
         strMessage = "Before delete there are "
            + rstRoySched.getRecordCount()
            + " titles with 20 percent royalty:" + "\n\n";
         while(!rstRoySched.getEOF())
         {
            strMessage += rstRoySched.getField("title_id").getString() +
               "\n\n";
            rstRoySched.moveNext();
         }
         strMessage += "Enter the ID of a record to delete:" + "\n";
         System.out.println(strMessage);
         strTitleID = in.readLine().trim().toUpperCase();

         // Move to the record and save data so it can be restored.
         rstRoySched.setFilter("title_id = '" + strTitleID + "'");
         if(!(rstRoySched.getRecordCount()==0))
         {
            intLoRange = rstRoySched.getField("lorange").getInt();
            intHiRange = rstRoySched.getField("hirange").getInt();
            intRoyalty = rstRoySched.getField("royalty").getInt();
            bolFound = true;
         }
         else
         {
            System.out.println("\nIncorrect ID. No Record deleted." );
            bolFound = false;
         }

         // Delete the record.
         if(bolFound)
         {
            rstRoySched.delete();
            rstRoySched.updateBatch();
         }
         // Show the results.
         rstRoySched.setFilter(new Integer(AdoEnums.FilterGroup.NONE));
         rstRoySched.requery();
         strMessage ="";
         strMessage = "\n\nAfter delete there are "
            + rstRoySched.getRecordCount()
            + " titles with 20 percent royalty:" + "\n\n";
         while(!rstRoySched.getEOF())
         {
            strMessage += rstRoySched.getField("title_id").getString() +
               "\n\n";
            rstRoySched.moveNext();
         }
         System.out.println(strMessage);
         System.out.println("\nPress <Enter> to continue..");
         in.readLine();

         // Restore the data because this is a demonstration.
         if(bolFound)
         {
            rstRoySched.addNew();
            rstRoySched.getField("title_id").setString(strTitleID);
            rstRoySched.getField("lorange").setInt(intLoRange);
            rstRoySched.getField("hirange").setInt(intHiRange);
            rstRoySched.getField("royalty").setInt(intRoyalty);
            rstRoySched.updateBatch();
         }

         // Cleanup objects before exit.
         rstRoySched.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
            if (rstRoySched != null)
            {
               PrintProviderError(rstRoySched.getActiveConnection());
               System.out.println("Exception: " + ae.getMessage());
            }
            else
            {
               System.out.println("Exception: " + ae.getMessage());
            }

      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }

   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}

// EndDeleteJ

See Also

Delete Method (ADO Recordset) | Recordset Object


Description, HelpContext, HelpFile, NativeError, Number, Source, and SQLState Properties Example (VJ++)

This example triggers an error, traps it, and displays the Description, HelpContext, HelpFile, NativeError, Number, Source, and SQLState properties of the resulting Error object.

// BeginDescriptionJ
// The WFC class includes the ADO objects.

import com.ms.wfc.data.*;
import java.io.* ;

public class DescriptionX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      DescriptionX();
      System.exit(0);
   }

   // DescriptionX function

   static void DescriptionX()
   {

      // Declarations.
      BufferedReader in = new
         BufferedReader(new InputStreamReader(System.in));

     // Define ADO Objects.
      Connection cnConn1 = null;

      try
      {
         // Intentionally trigger an error.
         cnConn1 = new Connection();
         cnConn1.open("nothing");
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.
         PrintProviderError(cnConn1);
      }

     try
     {
       System.out.println("\nPress <Enter> key to continue.");
       in.readLine();
     }
      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }

   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndDescriptionJ

See Also

Description Property | Error Object | HelpContext Property | HelpFile Property | NativeError Property | Number Property | Source Property (ADO Error) | SQLState Property


Execute, Requery, and Clear Methods Example (VJ++)

This example demonstrates the Execute method when run from both a Command object and a Connection object. It also uses the Requery method to retrieve current data in a recordset, and the Clear method to clear the contents of the Errors collection. The ExecuteCommand and PrintOutput procedures are required for this procedure to run.

// BeginExecuteJ
// The WFC class includes the ADO objects.
import com.ms.wfc.data.*;
import java.io.*;

public class ExecuteX
{
// Main Function

public static void main (String[] args)
{
   ExecuteX();
}

// ExecuteX Function

static void ExecuteX()
{
   // Define string variables.
   String strSQLChange = "UPDATE Titles SET Type = "
         + "'self_help' WHERE Type = 'psychology'";
   String strSQLRestore = "UPDATE Titles SET Type = "
         + "'psychology' WHERE Type = 'self_help'";
   String strCnn = "Provider=sqloledb;Data Source=MyServer;"
         + "Initial Catalog=Pubs;User Id=sa;Password=;";

   // Define ADO objects.
   Connection  cnConn1    = null;
   Command  cmdChange  = null;
   Recordset  rsTitles  = null;

   try
   {
      // Open connection.
      cnConn1 = new Connection ();
      cnConn1.open(strCnn, "", "", AdoEnums.CommandType.UNSPECIFIED);

      // Create command object.
      cmdChange = new Command();
      cmdChange.setActiveConnection (cnConn1);
      cmdChange.setCommandText (strSQLChange);

      // Open recordset with Titles table.
      rsTitles = new Recordset();
      rsTitles.open("Titles", cnConn1,
                 AdoEnums.CursorType.STATIC,
                 AdoEnums.LockType.OPTIMISTIC,
                 AdoEnums.CommandType.TABLE);

      // Print report of original data.
      System.out.println("\n\n\tData in Titles table "
                     + "before executing the query: \n");
      PrintOutput(rsTitles);

      // Clear extraneous errors from the Errors collection.
      cnConn1.getErrors().clear();

      // Call the ExecuteCommand subroutine to
      // execute cmdChange command.
      ExecuteCommand(cmdChange, rsTitles);

      // Print report of new data.
      System.out.println("\n\n\tData in Titles table after "
         + "executing the query: \n");
      PrintOutput(rsTitles);

      // Use the Connection object's execute method to
      // execute SQL statement to restore data.
      cnConn1.execute(strSQLRestore);

      // Print report of restored data.
      System.out.println("\n\n\tData after executing the query "
         + "to restore the original information: \n");
      PrintOutput(rsTitles);

      // Clean up objects before exit.
      rsTitles.close();
      cnConn1.close();
   }  // End Try statement.
   catch( AdoException ae )
   {
      // Notify user of any errors that result from ADO.

      // As passing a Recordset, check for null pointer first.
      if (rsTitles != null)
      {
         PrintProviderError(rsTitles.getActiveConnection());
      }
      else
      {
         System.out.println("Exception: " + ae.getMessage());
      }
   }
}

// ExecuteCommand Function

static void ExecuteCommand(Command cmdTemp, Recordset rstTemp)
{
   try
   {
      // CommandText property already set before function was called.
      cmdTemp.setCommandType(AdoEnums.CommandType.TEXT);
      cmdTemp.execute();

      // Retrieve the current data by requerying the recordset.
      rstTemp.requery(AdoEnums.CommandType.UNKNOWN);
   }
   catch( AdoException ae )
   {
      // Notify user of any errors that result from ADO.
      PrintProviderError(rstTemp.getActiveConnection());
   }
}

//  PrintOutput Function

static void PrintOutput(Recordset rstTemp)
{
   // Declarations.
   BufferedReader in = new
      BufferedReader(new InputStreamReader(System.in));

   // Ensure at top of recordset.
   rstTemp.moveFirst();

   // If EOF is true, then no data and skip print loop.
   if( rstTemp.getEOF() )
   {
      System.out.println("\tRecordset empty\n");
   }
   else
   {
      // Enumerate Recordset and print data from each.
      while( !(rstTemp.getEOF()) )
      {
         // Convert variant string to convertable string type.
         System.out.println("\t"
            + rstTemp.getFields().getItem("Title").getValue() + " "
            + rstTemp.getFields().getItem("Type").getValue() + "\n");
         rstTemp.moveNext();
      }
   }
   try
   {
      System.out.println("\nPress <Enter> key to continue.");
      in.readLine();
   }
   // System read requires this catch.
   catch( java.io.IOException je)
   {
      PrintIOError(je);
   }
}

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndExecuteJ

See Also

Clear Method | Command Object | Connection Object | Errors Collection | Execute Method (ADO Command) | Execute Method (ADO Connection) | Requery Method


Filter and RecordCount Properties Example (VJ++)

This example uses the Filter property to open a new Recordset based on a specified condition applied to an existing Recordset. It uses the RecordCount property to show the number of records in the two Recordsets. The FilterField function is required for this procedure to run.

// BeginFilterJ
// The WFC class includes the ADO objects.

import com.ms.wfc.data.*;
import java.io.* ;

public class FilterX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      FilterX();
      FilterX2();
      System.exit(0);
   }

   // FilterX function
   static void FilterX()
   {

      // Define ADO Objects.
      Recordset rstPublishers = null;
      Recordset rstPublishersCountry = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String line = null;
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
            + "Initial Catalog=Pubs;User Id=sa;Password=;";

      int intPublisherCount;
      String strCountry;
      String strMessage;

      try
      {
         rstPublishers = new Recordset();

         // Open recordset with data from Publishers table.
         rstPublishers.setCursorType(AdoEnums.CursorType.STATIC);
         rstPublishers.open("publishers", strCnn,
                    AdoEnums.CursorType.STATIC,
                    AdoEnums.LockType.READONLY,
                    AdoEnums.CommandType.TABLE);

         // Populate the Recordset.
         intPublisherCount = rstPublishers.getRecordCount();

         // Get user input.
         System.out.println("Enter a country to filter on:");
         strCountry = in.readLine().trim();

         if(!strCountry.equals(""))
         {
            // Open a filtered Recordset object.
            rstPublishersCountry =
               FilterField(rstPublishers, "Country", strCountry);
            if(rstPublishersCountry.getRecordCount()==0)
               System.out.println("\nNo publishers from that country.");
            else
            {
               // Print number of records for the original
               // Recordset object and the filtered Recordset
               // object.
               strMessage = "\nOrders in original recordset: " + "\n"
                  + intPublisherCount + "\n"
                  + "Orders in filtered recordset (Country = '"
                  + strCountry + "'): \n"
                  + rstPublishersCountry.getRecordCount();
               System.out.println(strMessage);
            }
            rstPublishersCountry.close();
         }
         System.out.println("\n\nPress <Enter> to continue..");
         in.readLine();

         // Cleanup objects before exit.
         rstPublishers.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstPublishers != null)
         {
            PrintProviderError(rstPublishers.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }

      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }

   }

   // FilterField Function

   static Recordset FilterField(Recordset rstTemp,String strField,
                         String strFilter)
   {
      // Set a filter on the specified Recordset object and then
      // open a new Recordset object.
      rstTemp.setFilter(strField + " = '" + strFilter + "'");
      return rstTemp;
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }

   // FilterX2 function

   static void FilterX2()
   {

      // Define ADO Objects.
      Recordset rstPublishers = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String line = null;
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
            + "Initial Catalog=Pubs;User Id=sa;Password=;";

      try
      {
         rstPublishers = new Recordset();

         // Open recordset with data from Publishers table.
         rstPublishers.setCursorType(AdoEnums.CursorType.STATIC);
         rstPublishers.open("SELECT * FROM publishers " +
                        "WHERE Country = 'USA'", strCnn,
                        AdoEnums.CursorType.STATIC,
                        AdoEnums.LockType.READONLY,
                        AdoEnums.CommandType.TEXT);

         // Print current data in recordset.
         rstPublishers.moveFirst();
         while(!rstPublishers.getEOF())
         {
            System.out.println(rstPublishers.getField("pub_name").getString()
                        +", "
                        + rstPublishers.getField("country").getString());
            rstPublishers.moveNext();
         }
         System.out.println("\n\nPress <Enter> to continue..");
         in.readLine();

         // Cleanup objects before exit.
         rstPublishers.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // Check for null pointer for connection object.
         if (rstPublishers.getActiveConnection()==null)
         {
               System.out.println("Exception: " + ae.getMessage());
         }
         else
         {
            // As passing a Recordset, check for null pointer first.
            if (rstPublishers != null)
            {
               PrintProviderError(rstPublishers.getActiveConnection());
            }
            else
            {
               System.out.println("Exception: " + ae.getMessage());
            }
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }

   }
}
// EndFilterJ

See Also

Filter Property | RecordCount Property | Recordset Object


Find Method Example (VJ++)

This example uses the Recordset object's Find method to locate and count the number of business titles in the Pubs database. The example assumes the underlying provider does not support similar functionality.

// BeginFindJ
// The WFC class includes the ADO objects.
import com.ms.wfc.data.*;
import java.io.* ;
import com.ms.com.*;

public class FindX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      FindX();
      System.exit(0);
   }

   // FindX function

   static void FindX()
   {

      // Define ADO Objects.
      Connection cnConn1 = null;
      Recordset rstTitles = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String strCnn = "DSN=Pubs;Provider=MSDASQL;uid=sa;pwd=;";
      Variant varMark = null;
      int intCount = 0;

      try
      {
         cnConn1 = new Connection();
         cnConn1.open(strCnn);
         rstTitles = new Recordset();
         rstTitles.open("SELECT title_id FROM titles",
               cnConn1,
               AdoEnums.CursorType.STATIC,
               AdoEnums.LockType.READONLY,
               AdoEnums.CommandType.TEXT);

         //  The default parameters are sufficient to search forward
         //  through a Recordset.
         rstTitles.find("title_id LIKE 'BU%'");

         // Skip current record to avoid finding the same row repeatedly.
         // The bookmark is redundant because Find searches from current
         // position.
         while(!rstTitles.getEOF()) // Continue if last find succeeded.
         {
            System.out.println("Title ID: "
                           + rstTitles.getField("title_id").getString());
            intCount++; // Count the last title found.
            varMark = (Variant)rstTitles.getBookmark();
            // Note current position.
            rstTitles.find("title_id LIKE 'BU%'",
                   1,
                   AdoEnums.SearchDirection.FORWARD,
                   varMark);
         }

         // Cleanup objects before exit.
         rstTitles.close();
         cnConn1.close();

         System.out.println("\nThe number of business titles is " +
                        Integer.toString(intCount));
         System.out.println("\nPress <Enter> to continue..");
         in.readLine();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstTitles != null)
         {
            PrintProviderError(rstTitles.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }

   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndFindJ

See Also

Find Method | Recordset Object


GetRows Method Example (VJ++)

This example uses the GetRows method to retrieve a specified number of rows from a Recordset and to fill an array with the resulting data. The GetRows method will return less than the desired number of rows in two cases: either if EOF has been reached, or if GetRows tried to retrieve a record that was deleted by another user. The function returns False only if the second case occurs. The GetRowsOK function is required for this procedure to run.

// BeginGetRowsJ
// The WFC class includes the ADO objects.

import com.ms.wfc.data.*;
import java.io.* ;
import com.ms.com.*;

public class GetRowsX
{
    // The main entry point for the application.

   static Variant avarRecords = null;

   public static void main (String[] args)
   {
      GetRowsX();
      System.exit(0);
   }

   // GetRowsX  function

   static void GetRowsX()
   {

      // Define ADO Objects.
      Recordset rstEmployees = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String line = null;
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
            + "Initial Catalog=Pubs;User Id=sa;Password=;";

      int intRows;
      int intRecord;
      int intUBound;
      int intDisplaysize = 15;

      try
      {
         // Open recordset with names and hire dates from Employees table.
         rstEmployees = new Recordset();
         rstEmployees.open("SELECT fName, lName, hire_date " +
                        "FROM Employee ORDER BY lName", strCnn,
                        AdoEnums.CursorType.FORWARDONLY,
                        AdoEnums.LockType.READONLY,
                        AdoEnums.CommandType.TEXT);

         while(true)
         {
            // Get user input for number of rows.
            System.out.println(
            "\nEnter number of rows to retrieve. (<= 0 to Exit)");
            line = in.readLine().trim();

            // Convert string entry to int.
            intRows = Integer.parseInt(line);

            // Exit the application if intRows is negative or zero.
            if(intRows <= 0)
               break;

            // If GetRowsOK is successful, print the results,
            // noting if the end of the file was reached.
            if(GetRowsOK(rstEmployees,intRows))
            {
               SafeArray sa = avarRecords.toSafeArray();
               intUBound = sa.getUBound(2);
               if ( intRows > (intUBound + 1))
                  System.out.println("\n(Not enough records in " +
                     "Recordset to retrieve " + intRows + " rows.)");
               System.out.println("\n" + (intUBound+ 1) +
                              " records found.\n");

               // Print the retrieved data.

               for ( intRecord = sa.getLBound();
                   intRecord  <= intUBound; intRecord++)
               {

                  System.out.println(
                     " " + sa.getString(0, intRecord) + " " +
                     sa.getString(1, intRecord) + ", " +
                     sa.getString(2, intRecord));
                  if ( ((intRecord +1) % intDisplaysize) == 0)
                  {
                     System.out.println("\nPress <Enter> to continue..");
                     in.readLine();
                  }

               }
            }
            else
            {
               // Assuming the GetRows error was due to data
               // changes by another user, use Requery to
               // refresh the Recordset and start over.
               System.out.println("\nGetRows failed--retry? (Y/N)");
               if(in.readLine().trim().toUpperCase().equals("Y"))
                  rstEmployees.requery();
               else
               {
                  System.out.println("GetRows failed!");
                  break;
               }
            }

            // Because using GetRows leaves the current
            // record pointer at the last record accessed,
            // move the pointer back to the beginning of the
            // Recordset before looping back for another search.
            rstEmployees.moveFirst();
         }

         // Cleanup objects before exit.
         rstEmployees.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstEmployees != null)
         {
            PrintProviderError(rstEmployees.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }

      // Display Error that the application has attempted to convert
      // a string of inappropriate format to one of the numeric types.
      catch(java.lang.NumberFormatException ne)
      {
         System.out.println(
            "Exception: Must specify an Integer value." );
      }

   }

   // GetRowsOK Function

   static boolean GetRowsOK(Recordset rstTemp,int intNumber)
      {
      // Store results of GetRows method in array.
      avarRecords = rstTemp.getRows(intNumber);

      // Return False only if fewer than the desired
      // number of rows were returned, but not because the
      // end of the Recordset was reached.
      if ( intNumber > (avarRecords.toSafeArray().getUBound(2)+ 1)
               && !(rstTemp.getEOF()))
         return false;
      else
         return true;

   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndGetRowsJ

See Also

BOF, EOF Properties | GetRows Method | Recordset Object


GetString Method Example (VJ++)

This example demonstrates the GetString method.

Assume you are debugging a data access problem and want a quick, simple way of printing the current contents of a small Recordset.

// BeginGetStringJ
// The WFC class includes the ADO objects.
import com.ms.wfc.data.*;
import java.io.* ;

public class GetStringX
{
    // The main entry point for the application.

   public static void main (String[] args)
   {
      GetStringX ();
      System.exit(0);
   }

   // GetStringX  function
   static void GetStringX()
   {
      // Define ADO Objects.
      Connection cnConn1 = null;
      Recordset rstAuthors = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String strCnn = "DSN=Pubs;Provider=MSDASQL;uid=sa;pwd=;";
      String strOutput;

      try
      {
         // Get the user input for state.
         System.out.println(
            "Enter a state (CA, IN, KS, MD, MI, OR, TN, UT): ");
         String strState = in.readLine().trim();
         String strQuery =
            "SELECT au_fname, au_lname, address, city FROM Authors " +
                    "WHERE state = '" + strState + "'";

         // Open recordset with data from Authors table.
         cnConn1 = new Connection();
         cnConn1.open(strCnn);
         rstAuthors = new Recordset();
         rstAuthors.open(strQuery,
                cnConn1,
                AdoEnums.CursorType.STATIC,
                AdoEnums.LockType.READONLY,
                AdoEnums.CommandType.TEXT);

         if (rstAuthors.getRecordCount() > 0)
         {
            // Use all defaults: get all rows, TAB column delimiter,
            // CARRIAGE RETURN
            // row delimiter, empty-string null delimiter.
            strOutput =
               rstAuthors.getString(AdoEnums.StringFormat.CLIPSTRING,
               rstAuthors.getRecordCount(),
               "\t ",
               "\n",
               "").trim();
            System.out.println("\nState = '" + strState + "'" +
               "\n\n" +
               "Name             Address             City" +
               "\n");
            System.out.println(strOutput);
         }
         else
            System.out.println("\nNo rows found for state = '" +
               strState + "'\n");

         // Cleanup objects before exit.
         rstAuthors.close();
         cnConn1.close();

         System.out.println("\nPress <Enter> to continue..");
         in.readLine();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstAuthors != null)
         {
            PrintProviderError(rstAuthors.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }

   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndGetStringJ

See Also

GetString Method | Recordset Object


IsolationLevel and Mode Properties Example (VJ++)

This example uses the Mode property to open an exclusive connection, and the IsolationLevel property to open a transaction that is conducted in isolation of other transactions.

// BeginIsolationLevelJ
import com.ms.wfc.data.*;
import java.io.*;

public class IsolationLevelX
{
// The main entry point for the application.
public static void main (String[] args)
{
   IsolationLevelX();
   System.exit(0);
}

// IsolationLevelX Function

static void IsolationLevelX()
{
   // Define ADO Objects
   Connection cnn1 = null;
   Recordset rstTitles = null;

   // Assign connection string to variable
   String strCnn = "Provider=sqloledb;Data Source=MyServer;"+
               "Initial Catalog=Pubs;User Id=sa;Password=;";

   // Declarations
   BufferedReader in =
      new BufferedReader ( new InputStreamReader(System.in));
   String line = null;

   try
   {
      // Open connection and Titles table
      cnn1 = new Connection();

      cnn1.setMode(AdoEnums.ConnectMode.SHAREEXCLUSIVE);
      cnn1.setIsolationLevel(AdoEnums.IsolationLevel.ISOLATED);
      cnn1.open(strCnn);

      rstTitles = new Recordset();
      rstTitles.setCursorType(AdoEnums.CursorType.DYNAMIC);
      rstTitles.setLockType(AdoEnums.LockType.PESSIMISTIC);
      rstTitles.open("Titles", cnn1, AdoEnums.CursorType.DYNAMIC,
         AdoEnums.LockType.PESSIMISTIC);

      cnn1.beginTrans();

      // Display the connection mode
      if (cnn1.getMode() == AdoEnums.ConnectMode.SHAREEXCLUSIVE)
         System.out.println("\n\tConnection mode is exclusive");
      else
         System.out.println("\n\tConnection mode is not exclusive");
      System.out.println("\nPress <Enter> to continue..");
      in.readLine();

      // Display the Isolation level
      if ( cnn1.getIsolationLevel() == AdoEnums.IsolationLevel.ISOLATED)
         System.out.println("\tTransaction is Isolated\n");
      else
         System.out.println("\tTransaction is not Isolated\n");
      System.out.println("\nPress <Enter> to continue..");
      in.readLine();

      // Change the type of psychology titles
      while(!rstTitles.getEOF())
      {

         if(rstTitles.getField("Type").getString().trim().
            equals(new String("psychology")))
         {
            rstTitles.getField("Type").setString("self_help");
            rstTitles.update();
         }
         rstTitles.moveNext();

      }

      // Print current data in recordset
      rstTitles.requery();
      while(!rstTitles.getEOF())
      {
         System.out.println(rstTitles.getField("Title").getString() +
            " - " + rstTitles.getField("Type").getString() );
         rstTitles.moveNext();
      }
      System.out.println("\nPress <Enter> to continue..");
      in.readLine();

      // Restore original data
      cnn1.rollbackTrans();

      // Cleanup objects before exit
      rstTitles.close();
      cnn1.close();
   }
   catch(AdoException ae)
   {
      // Notify the user of any errors that result from ADO

      // As passing a connection, check for null pointer first
      if(cnn1 !=null)
      {
         PrintProviderError(cnn1);
      }
      else
      {
         System.out.println("Exception:" + ae.getLocalizedMessage());
      }
   }

   // System Read requires this catch
   catch(java.io.IOException je)
   {
      PrintIOError(je);

   }
}

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndIsolationLevelJ

See Also

IsolationLevel Property | Mode Property


Item Property Example (VJ++)

This example demonstrates how the Item property accesses members of a collection. The example opens the Authors table of the Pubs database with a parameterized command.

The parameter in the command issued against the database is accessed from the Command object's Parameters collection by index and name. Then the fields of the returned Recordset are accessed from that object's Fields collection by index and name.

// BeginItemJ
import com.ms.wfc.data.*;
import java.io.* ;
import com.ms.com.*;

public class ItemX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      ItemX();
      System.exit(0);
   }

   // ItemX  function

   static void ItemX()
   {

      // Define ADO Objects.
      Connection cnConn1 = null;
      Recordset rstAuthors = null;
      Command cmd = null;
      Parameter prm = null;
      Field fld = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String strCnn = "DSN=Pubs;Provider=MSDASQL;uid=sa;pwd=;";
      Variant [] varColumn = null;
      int intIndex;
      int intLimit;

      try
      {
         cnConn1 = new Connection();
         rstAuthors = new Recordset();
         cmd = new Command();

         //  Set the array with the Authors table field (column) names.
         varColumn = new Variant[9];
         varColumn[0] = new Variant("au_id");
         varColumn[1] = new Variant("au_lname");
         varColumn[2] = new Variant("au_fname");
         varColumn[3] = new Variant("phone");
         varColumn[4] = new Variant("address");
         varColumn[5] = new Variant("city");
         varColumn[6] = new Variant("state");
         varColumn[7] = new Variant("zip");
         varColumn[8] = new Variant("contract");

         cmd.setCommandText("SELECT * FROM Authors WHERE state = ?");
         prm = cmd.createParameter("ItemXparm",
                             AdoEnums.DataType.CHAR,
                             AdoEnums.ParameterDirection.INPUT,
                             2,
                             "CA");
         cmd.getParameters().append(prm);

         cnConn1.open(strCnn);
         cmd.setActiveConnection(cnConn1);

         // Connection and CommandType are omitted
         // because a Command Object is provided.
         rstAuthors.open(cmd,
                null ,
                AdoEnums.CursorType.STATIC,
                AdoEnums.LockType.READONLY);

         System.out.println(
            "The Parameters collection accessed by index...");
         prm = cmd.getParameters().getItem(0);
         System.out.println("Parameter name = '" +
                        prm.getName() +
                        "', value = '" +
                        prm.getValue().toString() + "'\n");

         System.out.println(
            "The Parameters collection accessed by name...");
         prm = cmd.getParameters().getItem("ItemXparm");
         System.out.println("Parameters name = '" +
                        prm.getName() +
                        "', value = '" +
                        prm.getValue().toString() + "'\n");
         System.out.println("Press <Enter> to continue..");
         in.readLine();

         System.out.println(
            "The Fields collection accessed by index...");
         rstAuthors.moveFirst();
         intLimit = rstAuthors.getFields().getCount() - 1;
         for(intIndex = 0; intIndex <= intLimit; intIndex++)
         {
            fld = rstAuthors.getFields().getItem(intIndex);
            short intVtType = fld.getValue().getvt();
            String strFieldValue;
            switch(intVtType)
            {
            case Variant.VariantString :
               strFieldValue = fld.getValue().toString();
               break;
            case Variant.VariantBoolean :
               if(fld.getValue().getBoolean())
                  strFieldValue = "True";
               else
                  strFieldValue = "False";
               break;
            default :
               strFieldValue = fld.getValue().toString();
               break;
            }
            System.out.println("Field " +
                           Integer.toString(intIndex) +
                           " : Name = '" +
                           fld.getName() +
                           "', value = '" +
                           strFieldValue +
                           "'");
         }
         System.out.println("\nPress <Enter> to continue..");
         in.readLine();

         System.out.println("The Fields collection accessed by name...");
         rstAuthors.moveFirst();
         for(intIndex = 0; intIndex <= 8; intIndex++)
         {
            fld = rstAuthors.getFields().getItem
               (varColumn[intIndex].toString());
            short intVtType = fld.getValue().getvt();
            String strFieldValue;
            switch(intVtType)
            {
            case Variant.VariantString :
               strFieldValue = fld.getValue().toString();
               break;
            case Variant.VariantBoolean :
               if(fld.getValue().getBoolean())
                  strFieldValue = "True";
               else
                  strFieldValue = "False";
               break;
            default :
               strFieldValue = fld.getValue().toString();
               break;
            }
            System.out.println("Field " +
                           "name = '" +
                           fld.getName() +
                           "', value = '" +
                           strFieldValue +
                           "'");
         }
         System.out.println("\nPress <Enter> to continue..");
         in.readLine();

         // Cleanup objects before exit.
         rstAuthors.close();
         cnConn1.close();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstAuthors != null)
         {
            PrintProviderError(rstAuthors.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndItemJ

See Also

Command Object | Fields Collection | Item Property | Parameters Collection | Recordset Object


MarshalOptions Property Example (VJ++)

This example uses the MarshalOptions property to specify what rows are sent back to the server—All Rows or only Modified Rows.

// BeginMarshalOptionsJ
import java.io.*;
import com.ms.wfc.data.*;

public class MarshalOptionsX
{
// The main entry point for the application.
public static void main (String[] args)
{
   MarshalOptionsX();
   System.exit(0);
}
// MarshalX Function

static void MarshalOptionsX()
{
   // Define ADO Objects
   Recordset rstEmployees = null;

   // Declarations
   BufferedReader in =
      new BufferedReader(new InputStreamReader(System.in));
   String line = null;

   try
   {

      // Open Recordset with names from Employees Table.
      String strCnn = " Provider=sqloledb;Data Source=MyServer;" +
             "Initial Catalog=Pubs;User Id=sa;Password=;";

      rstEmployees = new Recordset();
      rstEmployees.setCursorType(AdoEnums.CursorType.KEYSET);
      rstEmployees.setLockType(AdoEnums.LockType.OPTIMISTIC);
      rstEmployees.setCursorLocation(AdoEnums.CursorLocation.CLIENT);

      rstEmployees.open(
         "SELECT fname,lname from Employee ORDER BY lname",
         strCnn,AdoEnums.CursorType.KEYSET,
         AdoEnums.LockType.OPTIMISTIC, AdoEnums.CommandType.TEXT);

      // Store original data
      String strOldFirst = rstEmployees.getField("fname").getString();
      String strOldLast = rstEmployees.getField("lname").getString();

      // Change data in edit buffer
      rstEmployees.getField("fname").setString("Linda");
      rstEmployees.getField("lname").setString("Kobara");

      // Show contents of buffer and get user input
      String strMessage = "Edit in progress: "+ "\n"+
                "Original Data  = \t"+ strOldFirst +" "+
                strOldLast + "\n" + "Data in Buffer = \t"+
                rstEmployees.getField("fname").getString()+ " " +
                rstEmployees.getField("lname").getString()+"\n"+"\n"+
                "Use Update to replace the original data with " +
                "the buffered data in the recordset";
      String strMarshalAll = "Would you like to send all the rows" +
                  " in the recordset back to the server";
      String strMarshalModified = "Would you like to send only "+
                      " modified rows back to the server";

      System.out.println(strMessage + "\nEnter (Y/N)...");

      if (in.readLine().equalsIgnoreCase("Y"))
      {
         System.out.println(strMarshalAll);
         System.out.println("\nEnter (Y/N)...");

         if(in.readLine().equalsIgnoreCase("Y"))
         {
            rstEmployees.setMarshalOptions(AdoEnums.MarshalOptions.ALL);
            rstEmployees.update();
         }
         else
         {
            System.out.println(strMarshalModified);
            System.out.println("\nEnter (Y/N)...");

            if (in.readLine().equalsIgnoreCase("Y"))
            {
               rstEmployees.setMarshalOptions(
                  AdoEnums.MarshalOptions.MODIFIEDONLY);
               rstEmployees.update();
            }
         }

      }

      // Show the resulting data
      System.out.println("\nData in recordset = " +
         rstEmployees.getField("fname").getString() +
         " " + rstEmployees.getField("lname").getString());

      // Restore original data because this is a demonstration
      if (!((strOldFirst.equals(rstEmployees.getField("fname")))
         &&(strOldLast.equals(rstEmployees.getField("lname")))))
      {
         rstEmployees.getField("fname").setString(strOldFirst);
         rstEmployees.getField("lname").setString(strOldLast);
         rstEmployees.update();
      }
   // Cleanup objects before exit
   rstEmployees.close();
   System.out.println("\n\nPress <Enter> to continue..");
   in.readLine();
   }

   catch(AdoException ae)
   {
      // Notify the user of any errors that result from ADO

      // As passing a connection, check for null pointer first
      if(rstEmployees!= null)
      {
         PrintProviderError(rstEmployees.getActiveConnection());
      }
      else
      {
         System.out.println("Exception: " + ae.getLocalizedMessage());
      }
   }

   // System Read requires this catch
   catch(java.io.IOException je)
   {
      PrintIOError(je);
   }
}

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndMarshalOptionsJ

See Also

MarshalOptions Property


MaxRecords Property Example (VJ++)

This example uses the MaxRecords property to open a Recordset containing the 10 most expensive titles in the Titles table.

// BeingMaxRecordsJ
import java.io.*;
import com.ms.wfc.data.*;

public class MaxRecordsX
{
// The main entry point for the application.

public static void main (String[] args)
{
   MaxRecordsX();
   System.exit(0);
}
// MaxRecordsX Function

static void MaxRecordsX()
{
   //  Define ADO Objects
   Recordset rstTemp = null;

   try
   {
      // Declarations
      BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));

      // Open recordset containing the 10 most expensive
      // titles in the Titles table.
      String strCnn = " Provider=sqloledb;Data Source=MyServer;"+
         " Initial Catalog=Pubs;User Id=sa;Password=;";
      rstTemp = new Recordset();
      rstTemp.setMaxRecords(10);
      rstTemp.open("select title,price from Titles" +
         " order by price desc", strCnn,AdoEnums.CursorType.FORWARDONLY,
         AdoEnums.LockType.READONLY, AdoEnums.CommandType.TEXT);

      // Display the contents of the recordset.
      System.out.println("Top Ten Titles by Price:\n");
      while (!rstTemp.getEOF())
      {
         System.out.println(" "+ rstTemp.getField("title").getString() +
            " - " + rstTemp.getField("Price").getString());
         rstTemp.moveNext();
      }
      // Cleanup objects before exit.
      rstTemp.close();

      System.out.println("\n\nPress <Enter> to continue..");
      in.readLine();
   }

   catch(AdoException ae)
   {
      // Notify the user of any errors that result from ADO.

      // As passing a connection, check for null pointer first.
      if (rstTemp!=null)
      {
         PrintProviderError(rstTemp.getActiveConnection());
      }
      else
      {
         System.out.println("Exception: " + ae.getLocalizedMessage());
      }
   }

   // System read requires this catch.
   catch(java.io.IOException je)
   {
      PrintIOError(je);
   }
}
   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndMaxRecordsJ

See Also

MaxRecords Property | Recordset Object


Move Method Example (VJ++)

This example uses the Move method to position the record pointer based on user input.

// BeginMoveJ
import java.io.*;
import com.ms.wfc.data.*;
import com.ms.com.*;

public class MoveX
{
// The main entry point for the application.

public static void main (String[] args)
{
   MoveX();
   System.exit(0);
}
// MoveX Function

static void MoveX()
{
   // Define ADO Objects
   Recordset rstAuthors = null;

   // Declarations
   String line = null;
   Variant varBookmark;
   String strCommand = null;
   int lngMove;
   BufferedReader in =
      new BufferedReader(new InputStreamReader(System.in));

   try
   {
      //  Open recordset from Authors table.
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"+
         "Initial Catalog=Pubs;User Id=sa;Password=;";

      rstAuthors = new Recordset();
      rstAuthors.setCursorType(AdoEnums.CursorType.STATIC);

      // Use client cursor to allow use of
      // Absolute Position property.
      rstAuthors.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
      rstAuthors.open("select au_id,au_fname,au_lname,city,state " +
         "from Authors order by au_lname",
         strCnn,AdoEnums.CursorType.STATIC,
         AdoEnums.CursorLocation.CLIENT, AdoEnums.CommandType.TEXT);

      rstAuthors.moveFirst();

      while(true)
      {
         // Display information about current record and
         // ask how many records to move.
         strCommand = "Record:\t\t"+ rstAuthors.getAbsolutePosition() +
            " of " + rstAuthors.getRecordCount() + "\n" + "\tAuthor:\t\t"
             + rstAuthors.getField("au_fname").getString() +
             " " + rstAuthors.getField("au_lname").getString() +
            "\n" +"\tLocation:\t" +
            rstAuthors.getField("city").getString() +
             ", " +rstAuthors.getField("state").getString()
             +"\n\n"+"\tEnter number of records to move" +
            " (positive or negative).";

         System.out.print("\t"+ strCommand + "\t");
         line =in.readLine();

         // No entry exits program loop.
         if (line.length()== 0)
            break;

         // Converts string entry to int.
         lngMove = Integer.parseInt(line);

         // Store bookmark in case the move goes too far
         // forward or backward.
         varBookmark =(Variant)rstAuthors.getBookmark();

         // Move method requires parameter of data type int.
         rstAuthors.move(lngMove);

         // Trap for BOF and EOF.
         if (rstAuthors.getBOF())
         {
            System.out.println("\tToo far backward!  " +
               "Returning to the current record.");
            rstAuthors.setBookmark(varBookmark);
         }
         if (rstAuthors.getEOF())
         {
            System.out.println("\tToo far forward!  " +
               "Returning to the current record.");
            rstAuthors.setBookmark(varBookmark);
         }
      }
      // Cleanup objects before exit.
      rstAuthors.close();
      System.out.println("\tPress <Enter> to continue..");
      in.readLine();
   }

   catch(AdoException ae)
   {
      // Notify user of any errors that result from ADO.

      // As passing a recordset, check for null pointer.
      if (rstAuthors!=null)
      {
         PrintProviderError(rstAuthors.getActiveConnection());
      }
      else
      {
         System.out.println(" Exception: "+ ae.getMessage());
      }
   }
   // System Read requires this catch.
   catch(java.io.IOException je)
   {
      PrintIOError(je);
   }
   // Required if the user enter non integer value.
   catch(java.lang.NumberFormatException ne)
   {
      System.out.println("\n\nPlease enter integer values!");
      rstAuthors.close();
   }
}
   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndMoveJ

See Also

Move Method


MoveFirst, MoveLast, MoveNext, and MovePrevious Methods Example (VJ++)

This example uses the MoveFirst, MoveLast, MoveNext, and MovePrevious methods to move the record pointer of a Recordset based on the supplied command. The MoveAny procedure is required for this procedure to run.

// BeginMoveFirstJ
import com.ms.wfc.data.*;
import java.io.*;

public class MoveFirstX  // DLL name.
{
// Main Function

public static void main( String rgArg[] )
{
   MoveFirstX();
}

// MoveFirstX Function

static void MoveFirstX()
{
   // Declarations
   Recordset rsAuthors = null;
   BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));
   String  line = null;

   String strCnn = "Provider=sqloledb;data source=MyServer;"
      + "Initial Catalog=Pubs;User Id=sa;Password=;";

   String strMessage = "UPDATE Titles SET type = 'psychology' "
      +"WHERE type = 'self_help'";

   int intCommand = 0;
   String strFName;
   String strLName;

   try
   {
      // Open recordset from Authors table.
      rsAuthors = new Recordset();
      rsAuthors.setCursorLocation( AdoEnums.CursorLocation.CLIENT );

      // Use client cursor to enable AbsolutePosition property.
      rsAuthors.open( "Authors", strCnn, AdoEnums.CursorType.STATIC,
         AdoEnums.LockType.BATCHOPTIMISTIC, AdoEnums.CommandType.TABLE );

      // Get user's move requests and show current record information.
      while( true )   // Continuous loop.
      {
      // Assign field information to variable to simplify output code.
         strFName = rsAuthors.getField("au_fname").getString();
         strLName = rsAuthors.getField("au_lname").getString();

         System.out.println
            ( "\nName: " + strFName + " " + strLName + "\n"
            + "Record " + rsAuthors.getAbsolutePosition()
            + " of " + rsAuthors.getRecordCount() + "\n\n" );
         System.out.println( "[1 - MoveFirst, 2 - MoveLast, \n");
         System.out.println( " 3 - MoveNext, 4 - MovePrevious]\n");

         // User types a number followed by enter (cr-lf).
         line = in.readLine();

         // No entry exits program loop.
         if (line.length() == 0) break;
         // Convert string entry to int.
         intCommand = Integer.parseInt(line);
         // Out of range entry exits program loop.
         if ((intCommand < 1) || (intCommand > 4)) break;

         // Call method based on user's validated selection.
         MoveAny(intCommand, rsAuthors);
      }
      rsAuthors.close();
   }
   catch( AdoException ae )
   {
      // Notify user of any errors that result from ADO.

      // As passing a Recordset, check for null pointer first.
      if (rsAuthors != null)
      {
         PrintProviderError(rsAuthors.getActiveConnection());
      }
      else
      {
         System.out.println("Exception: " + ae.getMessage());
      }
   }
   // System Read requires this catch.
   catch( java.io.IOException je )
   {
      PrintIOError(je);
   }
}

// MoveAny Function

static void MoveAny(int intChoice, Recordset rsTemp)
{
   // Move per selection from user, checking for BOF and EOF.
   try
   {
      switch(intChoice)
      {
      case 1:   // Equals char of 1.
         rsTemp.moveFirst();
         break;
      case 2:   // Equals char of 2.
         rsTemp.moveLast();
         break;
      case 3:   // Equals char of 3.
         rsTemp.moveNext();
         if(rsTemp.getEOF())
         {
            System.out.println("\nAlready at end of recordset!\n");
            rsTemp.moveLast();
         }
         break;
      case 4:   // Equals char of 4.
         rsTemp.movePrevious();
         if(rsTemp.getBOF())
         {
            System.out.println
               ("\nAlready at beginning of recordset!\n");
            rsTemp.moveFirst();
         }
         break;
      default:
         break;
      }
   }
   catch( AdoException ae )
   {
      // Notify user of any errors that result from ADO.

      // As passing a Recordset, check for null pointer first.
      if (rsTemp != null)
      {
         PrintProviderError(rsTemp.getActiveConnection());
      }
      else
      {
         System.out.println("Exception: " + ae.getMessage());
      }
   }
}

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndMoveFirstJ

See Also

MoveFirst, MoveLast, MoveNext, and MovePrevious Methods | Recordset Object


NextRecordset Method Example (VJ++)

This example uses the NextRecordset method to view the data in a recordset that uses a compound command statement made up of three separate SELECT statements.

// BeginNextRecordsetJ
import java.io.*;
import com.ms.wfc.data.*;

public class NextRecordsetX
{
// The main entry point for the application.

public static void main (String[] args)
{
   NextRecordsetX();
   System.exit(0);

}
// NextRecordsetX Function
static void NextRecordsetX()
{
   // Define ADO Object
   Recordset rstCompound = null;

   // Declarations
   BufferedReader in =
      new BufferedReader(new InputStreamReader(System.in));
   String strCnn;
   int intCount;
   int intDisplayRecords = 15;
   int intRecordCount;

   try
   {
      // Open compound recordset.
      strCnn = "Provider=sqloledb;Data Source=MyServer;" +
          "Initial Catalog=Pubs;User Id=sa;Password=;";

      rstCompound = new Recordset();
      rstCompound.open("select * from Authors;" +
          "select * from stores;" +
          "select * from jobs", strCnn, AdoEnums.CursorType.FORWARDONLY,
         AdoEnums.LockType.READONLY,AdoEnums.CommandType.TEXT);

      // Display results from each select statement.
      intCount=1;

         while (rstCompound != null)
         {
            System.out.println(
               "Contents of recordset #" + intCount + "\n");
            intRecordCount = 0;
            while(rstCompound.getEOF() != true )
               {
                  System.out.println(
                     rstCompound.getField(0).getString()+" " +
                     rstCompound.getField(1).getString());
                  intRecordCount++;
                  if ( intRecordCount == intDisplayRecords)
                  {
                     System.out.println("\nPress <Enter> to continue..");
                     in.readLine();
                     intRecordCount = 0;
                  }
                  rstCompound.moveNext();
               }
            System.out.println("\nPress <Enter> to continue..");
            in.readLine();

            rstCompound = rstCompound.nextRecordset();

            intCount++;
         }
   }
   // System read requires this catch.
   catch(java.io.IOException je)
   {
      PrintIOError(je);
   }
   catch(AdoException ae)
   {
      // Notify the user of any errors that result from ADO.

      // As passing a recordset. check for the null pointer first
      if(rstCompound!=null)
      {
         PrintProviderError(rstCompound.getActiveConnection());
      }
      else
      {
         System.out.println("Exception: " + ae.getMessage());
      }
   }
   catch(java.lang.NullPointerException ne)
   {
      System.out.println("Error Description: " + ne.getMessage());
   }

}

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndNextRecordsetJ

See Also

NextRecordset Method


NumericScale and Precision Properties Example (VJ++)

This example uses the NumericScale and Precision properties to display the numeric scale and precision of fields in the Discounts table of the Pubs database.

// BeginNumericScaleJ
import com.ms.wfc.data.*;
import java.io.* ;

public class NumericScaleX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      NumericScaleX();
      System.exit(0);
   }

   // NumericScaleX function

   static void NumericScaleX()
   {

      // Define ADO Objects.
      Recordset rstDiscounts = null;
      Field fldTemp = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
          + "Initial Catalog=Pubs;User Id=sa;Password=;";
      int intLoop;

      try
      {
         rstDiscounts = new Recordset();

         // Open recordset.
         rstDiscounts.open("Discounts", strCnn,
                    AdoEnums.CursorType.FORWARDONLY,
                    AdoEnums.LockType.READONLY,
                    AdoEnums.CommandType.TABLE);

         // Display numeric scale and precision of
         // numeric and small integer fields.
         for ( intLoop=0; intLoop <
            rstDiscounts.getFields().getCount();intLoop++)
         {
            fldTemp = rstDiscounts.getFields().getItem(intLoop);
            if((fldTemp.getType()== AdoEnums.DataType.NUMERIC) |
               (fldTemp.getType()== AdoEnums.DataType.SMALLINT))
            {
               System.out.println("\nField: "
                  + fldTemp.getName());
               System.out.println("\nNumeric scale: "
                  + fldTemp.getNumericScale());
               System.out.println("\nPrecision: "
                  + fldTemp.getPrecision());
               System.out.println("\n\nPress <Enter> to continue..");
               in.readLine();
            }
         }

         // Cleanup objects before exit.
         rstDiscounts.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstDiscounts != null)
         {
            PrintProviderError(rstDiscounts.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }

      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }

   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndNumericScaleJ

See Also

NumericScale Property | Precision Property


Open and Close Methods Example (VJ++)

This example uses the Open and Close methods on both Recordset and Connection objects that have been opened.

// BeginOpenJ
import com.ms.wfc.data.*;
import java.io.* ;
import com.ms.com.*;

public class OpenX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      OpenX();
      System.exit(0);
   }

   // OpenX function

   static void OpenX()
   {

      // Define ADO Objects.
      Connection cnConn1 = null;
      Recordset rstEmployees = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
                  + "Initial Catalog=Pubs;User Id=sa;Password=;";
      Variant varDate;
      String strHDate;

      try
      {
         // Open connection.
         cnConn1 = new Connection();
         cnConn1.open(strCnn);

         // Open Employees table.
         rstEmployees = new Recordset();
         rstEmployees.setCursorType(AdoEnums.CursorType.KEYSET);
         rstEmployees.setLockType(AdoEnums.LockType.OPTIMISTIC);
         rstEmployees.open("Employee", cnConn1,
                    AdoEnums.CursorType.KEYSET,
                    AdoEnums.LockType.OPTIMISTIC,
                    AdoEnums.CommandType.TABLE);

         // Assign the first employee record's hire date
         // to a variable, then change the hire date.
         varDate = rstEmployees.getField("hire_date").getOriginalValue();
         System.out.println("Original data\n");
         System.out.println("\tName - Hire Date");
         strHDate = rstEmployees.getField("hire_date").getString();
         strHDate = strHDate.substring(5,7) + "/" +
            strHDate.substring(8,10)
             + "/" + strHDate.substring(2,4);
         System.out.println("\t" +
            rstEmployees.getField("fName").getString()+ " "
             + rstEmployees.getField("lName").getString()+ " - "
             + strHDate);
         System.out.println("\nPress <Enter> to continue..");
         in.readLine();
         rstEmployees.getField("hire_date").setString("1/1/1900");
         rstEmployees.update();
         System.out.println("Changed data\n");
         System.out.println("\tName - Hire Date");
         strHDate = rstEmployees.getField("hire_date").getString();
         strHDate = strHDate.substring(5,7) + "/" +
            strHDate.substring(8,10)
             + "/" + strHDate.substring(0,4);
         System.out.println("\t" +
            rstEmployees.getField("fName").getString()+ " "
             + rstEmployees.getField("lName").getString()+ " - "
             + strHDate);
         System.out.println("\nPress <Enter> to continue..");
         in.readLine();

         // Requery Recordset and reset the hire date.
         rstEmployees.requery();
         rstEmployees.getField("hire_date").setValue(varDate);
         rstEmployees.update();
         System.out.println("Data after reset\n");
         System.out.println("\tName - Hire Date");
         strHDate = rstEmployees.getField("hire_date").getString();
         strHDate = strHDate.substring(5,7) + "/" +
            strHDate.substring(8,10)
             + "/" + strHDate.substring(2,4);
         System.out.println("\t" +
            rstEmployees.getField("fName").getString()+ " "
             + rstEmployees.getField("lName").getString()+ " - "
             + strHDate);
         System.out.println("\nPress <Enter> to continue..");
         in.readLine();

         // Cleanup objects before exit.
         rstEmployees.close();
         cnConn1.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstEmployees != null)
         {
            PrintProviderError(rstEmployees.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }

      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }

   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndOpenJ

See Also

Close Method | Connection Object | Open Method (ADO Connection) | Open Method (ADO Recordset) | Recordset Object


OpenSchema Method Example (VJ++)

This example uses the OpenSchema method to display the name and type of each table in the Pubs database.

// BeginOpenSchemaJ
import com.ms.wfc.data.*;
import java.io.*;
import com.ms.com.*;

public class OpenSchemaX
{
 // The main entry point of the application.

public static void main (String[] args)
{
   OpenSchemaX();
   OpenSchemaX2();
   System.exit(0);
}

// OpenSchemaX Function
static void OpenSchemaX()
{
   // Define ADO Objects
   Connection cnn1 = null;
   Recordset rstSchema = null;

   //  Declarations
   String strCnn;
   BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
   int intDisplayRecords = 5;
   int intRecordCount = 0;

   try
   {

      cnn1 = new Connection();
      strCnn = "Provider = Microsoft.Jet.OLEDB.4.0;" +
         "Data Source=C:\\Program Files\\Microsoft " +
         "Office\\Office\\Samples\\Northwind.mdb;" +
         "User Id=admin;Password=;";
      cnn1.open(strCnn);
      rstSchema = cnn1.openSchema(AdoEnums.Schema.TABLES);

      while (!rstSchema.getEOF())
      {

         System.out.println("Table Name: " +
            rstSchema.getField("TABLE_NAME").getString()+"\n"+
            "Table Type: " +
            rstSchema.getField("TABLE_TYPE").getString()+"\n");
         intRecordCount++;
         if ( intRecordCount == intDisplayRecords)
         {
            System.out.println("Press <Enter> to continue..");
            in.readLine();
            intRecordCount = 0;
         }
         rstSchema.moveNext();

      }
      System.out.println("Press <Enter> to continue..");
      in.readLine();
      // Cleanup objects before exit
      rstSchema.close();
      cnn1.close();

   }
   catch(AdoException ae)
   {
      // Notify user of any errors that result from ADO.

      // As passing a Recordset, check for null pointer first.
      if(rstSchema != null)
      {
         PrintProviderError(rstSchema.getActiveConnection());
      }
      else
      {
         System.out.println("Exception: " + ae.getMessage());
      }
   }
   // System read requires this catch.
   catch(java.io.IOException je)
   {
      PrintIOError(je);
   }
}

// OpenSchemaX2 Function

static void OpenSchemaX2()
{
   // Define ADO Objects
   Connection cnn2 = null;
   Recordset rstSchema = null;

   // Declarations
   String strCnn;
   BufferedReader in =
      new BufferedReader(new InputStreamReader(System.in));
   int intDisplayRecords = 5;
   int intRecordCount = 0;

   try
   {
      cnn2 = new Connection();
      strCnn = "Provider = Microsoft.Jet.OLEDB.4.0;" +
         "Data Source=C:\\Program Files\\Microsoft " +
         "Office\\Office\\Samples\\Northwind.mdb;" +
         "User Id=admin;Password=;";
      cnn2.open(strCnn);

      Variant[] va = new Variant[4];
      va[0] = new Variant();
      va[1] = new Variant();
      va[2] = new Variant();
      va[3] = new Variant("VIEW");
      rstSchema = cnn2.openSchema(AdoEnums.Schema.TABLES,(Object[])va);

      while (!rstSchema.getEOF())
      {
         System.out.println("Table Name: " +
            rstSchema.getField("TABLE_NAME").getString()+"\n"+
            "Table Type: " +
            rstSchema.getField("TABLE_TYPE").getString()+"\n");
         intRecordCount++;
         if ( intRecordCount == intDisplayRecords)
         {
            System.out.println("Press <Enter> to continue..");
            in.readLine();
            intRecordCount = 0;
         }
         rstSchema.moveNext();

      }
      System.out.println("Press <Enter> to continue..");
      in.readLine();
      // Cleanup Objects before exit.
      rstSchema.close();
      cnn2.close();

   }
   catch(AdoException ae)
   {
      // Notify user of any errors that result from ADO.

      // As passing a Recordset, check for null pointer first.
      if(rstSchema != null)
      {
         PrintProviderError(rstSchema.getActiveConnection());
      }
      else
      {
         System.out.println("Exception: " + ae.getMessage());
      }
   }
   // System read requires this catch.
   catch(java.io.IOException je)
   {
      PrintIOError(je);
   }

}
   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndOpenSchemaJ

See Also

OpenSchema Method


Optimize Property Example (VJ++)

This example demonstrates the Field object dynamic Optimize property. The zip field of the Authors table in the Pubs database is not indexed. Setting the Optimize property to True on the zip field authorizes ADO to build an index that improves the performance of the Find method.

// BeginOptimizeJ
import com.ms.wfc.data.*;
import java.io.* ;

public class OptimizeX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      OptimizeX();
      System.exit(0);
   }

   // OptimizeX function

   static void OptimizeX()
   {

      // Define ADO Objects.
      Recordset rstAuthors = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String strCnn = "DSN=Pubs;Provider=MSDASQL;uid=sa;pwd=;";

      try
      {
         rstAuthors = new Recordset();
         rstAuthors.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         // Enable index creation.
         rstAuthors.open("SELECT * FROM Authors",
               strCnn,
               AdoEnums.CursorType.STATIC,
               AdoEnums.LockType.READONLY,
               AdoEnums.CommandType.TEXT);
         rstAuthors.getField("zip").getProperties().
            getItem("Optimize").setBoolean(true); // Create the index.
         rstAuthors.find("zip = '94595'");   // Find Akiko Yokomoto.
         System.out.println(rstAuthors.getField("au_fname").getString() +
            " " + rstAuthors.getField("au_lname").getString() + " " +
            rstAuthors.getField("address").getString() + " " +
            rstAuthors.getField("city").getString() + " " +
            rstAuthors.getField("state").getString() + " ");
         rstAuthors.getField("zip").getProperties().
            getItem("Optimize").setBoolean(false); // Delete the index.

         // Cleanup objects before exit.
         rstAuthors.close();
         System.out.println("\nPress <Enter> to continue..");
         in.readLine();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstAuthors != null)
         {
            PrintProviderError(rstAuthors.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndOptimizeJ

See Also

Field Object | Optimize Property—Dynamic (ADO)


OriginalValue and UnderlyingValue Properties Example (VJ++)

This example demonstrates the OriginalValue and UnderlyingValue properties by displaying a message if a record's underlying data has changed during a Recordset batch update.

// BeginOriginalValueJ
import com.ms.wfc.data.*;
import java.io.* ;

public class OriginalValueX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      OriginalValueX();
      System.exit(0);
   }

   // OriginalValueX function

   static void OriginalValueX()
   {
      // Define ADO Objects.
      Connection cnConn1 = null;
      Recordset rstTitles = null;
      Field fldType = null;

      // Declarations.
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
         + "Initial Catalog=Pubs;User Id=sa;Password=;";
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));

      try
      {
         // Open a connection.
         cnConn1 = new Connection();
         cnConn1.open(strCnn);

         // Open recordset for batch update.
         rstTitles = new Recordset();
         rstTitles.setActiveConnection(cnConn1);
         rstTitles.setCursorType(AdoEnums.CursorType.KEYSET);
         rstTitles.setLockType(AdoEnums.LockType.BATCHOPTIMISTIC);
         rstTitles.open("Titles",cnConn1,
                     AdoEnums.CursorType.KEYSET,
                     AdoEnums.LockType.BATCHOPTIMISTIC,
                     AdoEnums.CommandType.TABLE);

         // Set field object variable for Type field.
         fldType = rstTitles.getField("type");

         // Change the type of psychology titles.
         while(!rstTitles.getEOF())
         {
            if(rstTitles.getField("type").getString().
               trim().equals("psychology"))
               fldType.setString("self_help");
            rstTitles.moveNext();
         }

         // Similate a change by another user by updating
         // data using a command string.
         cnConn1.execute("UPDATE Titles SET type = 'sociology' "
                        + "WHERE type = 'psychology'");

         // Check for changes.
         rstTitles.moveFirst();

         while(!rstTitles.getEOF())
         {
            String strOriginalValue =
               fldType.getOriginalValue().toString().trim();
            String strUnderlyingValue =
               fldType.getUnderlyingValue().toString().trim();
            if(!(strOriginalValue.equals(strUnderlyingValue)))
            {
               System.out.println("Data has changed!" + "\n\n");
               System.out.println("\tTitle ID: "
                  + rstTitles.getField("title_id").getString().trim() +
                  "\n");
               System.out.println("\tCurrent value: "
                     + fldType.getString()+ "\n");
               System.out.println("\tOriginal value: "
                     + fldType.getOriginalValue().toString()+ "\n");
               System.out.println("\tUnderlying value: "
                     + fldType.getUnderlyingValue().toString()+ "\n");
               System.out.println("\n\nPress <Enter> to continue..");
               in.readLine();
            }
            rstTitles.moveNext();
         }
         // Cancel the update because this is a demonstration.

         rstTitles.cancelBatch();
         rstTitles.close();

         // Restore original values.
         cnConn1.execute("UPDATE Titles SET type = 'psychology' "
                     + "WHERE type = 'sociology'");

         // Cleanup objects before exit.
         cnConn1.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstTitles!= null)
         {
            PrintProviderError(rstTitles.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }

   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndOriginalValueJ

See Also

OriginalValue Property | Recordset Object | UnderlyingValue Property


Prepared Property Example (VJ++)

This example demonstrates the Prepared property by opening two Command objects—one prepared and one not prepared.

// BeginPreparedJ
import com.ms.wfc.data.*;
import java.io.* ;

public class PreparedX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      PreparedX();
      System.exit(0);
   }

   // PreparedX function

   static void PreparedX()
   {
      // Define string variables.
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"
            + "Initial Catalog=Pubs;User Id=sa;Password=;";
      String strCmd = "SELECT title, type FROM Titles ORDER BY type";

      // Define ADO Objects.
      Connection cnConn1 = null;
      Command cmd1 = null;
      Command cmd2 = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      long timeStart;
      long timeEnd;
      float timeNotPrepared ;
      float timePrepared;
      int   intLoop;
      String strTemp;

      try
      {
         // Open a connection.
         cnConn1 = new Connection();
         cnConn1.open(strCnn);

         // Create two command objects for the same
         // command - one prepared and one not prepared.
         cmd1 = new Command();
         cmd1.setActiveConnection(cnConn1);
         cmd1.setCommandType(AdoEnums.CommandType.TEXT);
         cmd1.setCommandText(strCmd);

         cmd2 = new Command();
         cmd2.setActiveConnection(cnConn1);
         cmd2.setCommandType(AdoEnums.CommandType.TEXT);
         cmd2.setCommandText(strCmd);
         cmd2.setPrepared(true);

         // Set a timer, then execute the unprepared
         // command 20 times.
         timeStart = System.currentTimeMillis();
         for ( intLoop = 0; intLoop < 20; intLoop++)
            cmd1.execute();
         timeEnd = System.currentTimeMillis();
         timeNotPrepared =(float)(timeEnd - timeStart)/1000f;

         // Reset the timer, then execute the prepared
         // command 20 times.
         timeStart = System.currentTimeMillis();
         for ( intLoop = 0; intLoop < 20; intLoop++)
            cmd2.execute();
         timeEnd = System.currentTimeMillis();
         timePrepared =(float)(timeEnd - timeStart)/1000f;

         // Display performance results.
         System.out.println("\nPerformance Results:");
         System.out.println("\n\tNot Prepared: " + timeNotPrepared +
            " seconds");
         System.out.println("\n\tPrepared: " + timePrepared +
            " seconds");
         System.out.println("\n\nPress <Enter> to continue..");
         in.readLine();

         // Cleanup objects before exit.
         cnConn1.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Connection, check for null pointer first.
         if (cnConn1!= null)
         {
            PrintProviderError(cnConn1);
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }

   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndPreparedJ

See Also

Command Object | Prepared Property


Provider and DefaultDatabase Properties Example (VJ++)

This example demonstrates the Provider property by opening three Connection objects using different providers. It also uses the DefaultDatabase property to set the default database for the Microsoft ODBC Provider.

// BeginProviderJ
import java.io.*;
import com.ms.wfc.data.*;

public class ProviderX
{
   //    The main entry point of the application.
   public static void main (String[] args)
   {
      ProviderX();
      System.exit(0);
   }
   // ProviderX Function
   static void ProviderX()
   {
      // Define ADO Objects.
      Connection cnn1 = null;
      Connection cnn2 = null;
      Connection cnn3 = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));
      try
      {
         // Open a connection using the Microsoft ODBC Provider.
         cnn1 = new Connection();
         cnn1.setConnectionString("driver={SQL Server};"+
             "server=srv;User id=sa;Password=;");
         cnn1.open();
         cnn1.setDefaultDatabase("Pubs");

         // Display the provider.
         System.out.println("\n\n\tCnn1 provider: "+ cnn1.getProvider());

         // Open connection using the OLE DB Provider for Microsoft Jet.
         cnn2 = new Connection();
         cnn2.setProvider("Microsoft.Jet.OLEDB.3.51");
         cnn2.open("C:\\Program Files\\Microsoft "
         "Office\\Office\\Samples\\Northwind.mdb","admin","");

         // Display the provider.
         System.out.println("\n\n\tCnn2 provider: " +
            cnn2.getProvider());

         // Open a connection using the Microsoft SQL Server Provider.
         cnn3 = new Connection();
         cnn3.setProvider("sqloledb");
         cnn3.open("Data Source=MyServer;Initial Catalog= Pubs;","sa","");

         // Display the provider.
         System.out.println("\n\n\tCnn3 provider: " +
            cnn3.getProvider());

         // Cleanup Objects before exit.
         cnn1.close();
         cnn2.close();
         cnn3.close();

         System.out.println("\n\n\tPress <Enter> to continue..");
         in.readLine();
      }
      catch(AdoException ae)
      {
         // Notify the user of any errors that result from ADO.

         // As passing a Connection, check for null pointer first.
         if(cnn1 != null)
         {
            PrintProviderError(cnn1);
         }
         else
         {
            System.out.println("Exception: " + ae.getLocalizedMessage());
         }
      }
      // System read requires needs this catch.
      catch(java.io.IOException je)
      {
         PrintIOError(je);
      }
   }
   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndProviderJ

See Also

Connection Object | DefaultDatabase Property | Provider Property


Refresh Method Example (VJ++)

This example demonstrates using the Refresh method to refresh the Parameters collection for a stored procedure Command object.

// BeginRefreshJ
import  com.ms.wfc.data.*;
import java.io.*;

public class RefreshX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      RefreshX();
      System.exit(0);
   }

   // RefreshX function

   static void RefreshX()
   {
      // Define ADO Objects.
      Connection cnConn1 = null;
      Command cmdByRoyalty = null;
      Recordset rstByRoyalty = null;
      Recordset rstAuthors = null;

      //Declarations.
      String strAuthorID;
      String strFName;
      String strLName;
      int intRoyalty ;
      BufferedReader in =
         new BufferedReader (new InputStreamReader (System.in));
      String line = null;

      try
      {
         // Open a connection.
         String strCnn = "Provider=sqloledb;Data Source=MyServer;"
                     + "Initial Catalog=Pubs;User Id=sa;Password=;";

         cnConn1 = new Connection();
         cnConn1.open(strCnn);

         // Open a command object for a stored procedure
         // with one parameter.
         cmdByRoyalty = new Command();
         cmdByRoyalty.setActiveConnection(cnConn1);
         cmdByRoyalty.setCommandText("byRoyalty");
         cmdByRoyalty.setCommandType(AdoEnums.CommandType.STOREDPROC);
         cmdByRoyalty.getParameters().refresh();

         // Get Parameter value and execute the command
         // storing the results in the recordset.
         System.out.println ("\nEnter Royalty : ");
         line = in.readLine().trim();
         intRoyalty = Integer.parseInt(line);
         cmdByRoyalty.getParameters().getItem(1).setInt(intRoyalty);

         // Create a recordset by executing the command.
         rstByRoyalty = cmdByRoyalty.execute();

         // Open the Authors table to get author names for display.
         rstAuthors = new Recordset ();
         rstAuthors.open(
            "Authors",strCnn,AdoEnums.CursorType.FORWARDONLY,
            AdoEnums.LockType.READONLY, AdoEnums.CommandType.TABLE );

         // Print current data in the recordset,
         // adding author names from Authors table.
         System.out.println("\nAuthors with " + intRoyalty +
                        " percent royalty");
         while (!rstByRoyalty.getEOF())
         {
            strAuthorID =  rstByRoyalty.getField("au_id").getString();
            rstAuthors.setFilter("au_id ='" +  strAuthorID + "'");
            strFName = rstAuthors.getField("au_fname").getString();
            strLName = rstAuthors.getField("au_lname").getString();
            System.out.println("\t" + strAuthorID + ", " + strFName
                           + " " + strLName);
            rstByRoyalty.moveNext();
         }
         System.out.println("\n\nPress <Enter> key to continue..");
         line = in.readLine();

         //Cleanup objects before exit.
         rstByRoyalty.close();
         rstAuthors.close();
         cnConn1.close();

      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstByRoyalty != null)
         {
            PrintProviderError(rstByRoyalty.getActiveConnection());
         }
         else if (rstAuthors != null)
         {
            PrintProviderError(rstAuthors.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // This catch is required if input string cannot be converted to
      // Integer data type.
      catch ( java.lang.NumberFormatException ne)
      {
            System.out.println("\nException: Integer Input required.");
      }
      // System Read requires this catch.
      catch( java.io.IOException je )
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndRefreshJ

See Also

Command Object | Parameters Collection | Refresh Method


Resync Method Example (VJ++)

This example demonstrates using the Resync method to refresh data in a static recordset.

// BeginResyncJ
import java.io.*;
import com.ms.wfc.data.*;

public class ResyncX
{
   // The main entry point of the application.
   public static void main (String[] args)
   {
      ResyncX();
      System.exit(0);
   }
   static void ResyncX()
   {
      // Define ADO Objects.
      Recordset rstTitles = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));
      String strCnn = "Provider=sqloledb;Data Source=MyServer;" +
                  "Initial Catalog=Pubs;User Id=sa;Password=;";

      try
      {
         // Open recordset for Titles table.
         rstTitles = new Recordset();
         rstTitles.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rstTitles.setCursorType(AdoEnums.CursorType.STATIC);
         rstTitles.setLockType(AdoEnums.LockType.BATCHOPTIMISTIC);
         rstTitles.open("Titles", strCnn, AdoEnums.CursorType.STATIC,
            AdoEnums.LockType.BATCHOPTIMISTIC,
            AdoEnums.CommandType.TABLE);

         // Change the type of the first title in the recordset.
         rstTitles.getField("type").setString("database");

         // Display the results of the change.
         System.out.println("\n\n\tBefore resync:\n" + "\tTitle - " +
            rstTitles.getField("title").getString() +
            "\n\tType  - " + rstTitles.getField("type").getString());

         // Resync with database and redisplay the results.
         rstTitles.resync();
         System.out.println("\n\n\tAfter resync:\n" + "\tTitle - " +
            rstTitles.getField("title").getString() +
            "\n\tType  - " +
            rstTitles.getField("type").getString()+"\n");
         rstTitles.cancelBatch();

         // Cleanup Objects before exit.
         rstTitles.close();

         System.out.println("\tPress <Enter> to continue..");
         in.readLine();

      }
      catch(AdoException ae)
      {
         // Notify user of any errors that result from ADO.

         // As passing a recordset, check for null pointer first.
         if(rstTitles != null)
         {
            PrintProviderError(rstTitles.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }
      // System read requires this catch.
      catch(java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndResyncJ

See Also

Resync Method


Save and Open Methods Example (VJ++)

These three examples demonstrate how the Save and Open methods can be used together.

Assume you are going on a business trip and want to take along a table from a database. Before you go, you access the data as a Recordset and save it in a transportable form. When you arrive at your destination, you access the Recordset as a local, disconnected Recordset. You make changes to the Recordset, then save it again, along with your changes. Finally, when you return home, you connect to the database again and update it with the changes you made on the road.

// BeginSaveJ
import com.ms.wfc.data.*;
import java.io.* ;

public class SaveX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      SaveX1();
      SaveX2();
      SaveX3();
      System.exit(0);
   }

   // First, access and save the Authors table.

   // SaveX1 function

   static void SaveX1()
   {
      // Define ADO Objects.
      Recordset rstAuthors = null;

      // Declarations.
      String strCnn = "DSN=Pubs;Provider=MSDASQL;uid=sa;pwd=;";
      BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));
      File file;

      try
      {
         rstAuthors = new Recordset();
         rstAuthors.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rstAuthors.open("SELECT * FROM Authors",
                        strCnn,
                        AdoEnums.CursorType.DYNAMIC,
                        AdoEnums.LockType.OPTIMISTIC,
                        AdoEnums.CommandType.TEXT);

         // For the sake of illustration, save the recordset to a
         //diskette in XML format.
         file = new File("a:\\Pubs.xml");
         if(!file.exists())
            rstAuthors.save("a:\\Pubs.xml",AdoEnums.PersistFormat.XML);
         else
         {
            System.out.println("\nFile already exists.");
            System.out.println("\nPress <Enter> to continue..");
            in.readLine();
            System.exit(0);
         }
         // Cleanup objects before exit.
         rstAuthors.close();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstAuthors != null)
         {
            PrintProviderError(rstAuthors.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }
      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // At this point, you have arrived at your destination. You will
   // access the Authors table as a local, disconnected Recordset.
   // Don't forget you must have the MSPersist provider on the machine
   // you are using in order to access the saved file, a:\Pubs.xml.

   // SaveX2 function

   static void SaveX2()
   {
      // Define ADO Objects.
      Recordset rstAuthors = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));

      try
      {
         rstAuthors = new Recordset();

         // For sake of illustration, we specify all parameters.
         rstAuthors.open("a:\\Pubs.xml",
               "Provider=MSPersist;",
               AdoEnums.CursorType.FORWARDONLY,
               AdoEnums.LockType.OPTIMISTIC,
               AdoEnums.CommandType.FILE);

         // Now you have a local, disconnected recordset.
         // Edit it as you desire.
         // (In this example, the change makes no difference).
         rstAuthors.find("au_lname = 'Carson'");
         if(rstAuthors.getEOF())
         {
            System.out.println("Name not found.");
            System.out.println("\nPress <Enter> to continue..");
            in.readLine();
            return;
         }
         rstAuthors.getField("city").setString("Berkeley");
         rstAuthors.update();

         // Save changes in ADTG format this time, for illustration.
         // Note that previous version on the diskette, as a:\Pubs.xml.
         rstAuthors.save("a:\\Pubs.adtg",AdoEnums.PersistFormat.ADTG);

         // Cleanup objects before exit.
         rstAuthors.close();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstAuthors != null)
         {
            PrintProviderError(rstAuthors.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // Finally, update the database with your changes.

   // SaveX3 function

   static void SaveX3()
   {
      // Define ADO Objects.
      Connection cnConn1 = null;
      Recordset rstAuthors = null;

      // Declarations.
      String strCnn = "DSN=Pubs;Provider=MSDASQL;uid=sa;pwd=;";

      try
      {
         // If there is no ActiveConnection, you can open with defaults.
         rstAuthors = new Recordset();
         rstAuthors.open("a:\\Pubs.adtg");

         // Connect to the database, associate the Recordset with
         // connection, then update the database table with the changed
         // Recordset.
         cnConn1 = new Connection();
         cnConn1.open(strCnn);
         rstAuthors.setActiveConnection(cnConn1);
         rstAuthors.updateBatch();

         // Cleanup objects before exit.
         rstAuthors.close();
         cnConn1.close();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstAuthors != null)
         {
            PrintProviderError(rstAuthors.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndSaveJ


Sort Property Example (VJ++)

This example uses the Recordset object's Sort property to reorder the rows of a Recordset derived from the Authors table of the Pubs database. A secondary utility routine prints each row.

// BeginSortJ
import com.ms.wfc.data.*;
import java.io.* ;

public class SortX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      SortX();
      System.exit(0);
   }

   // SortX function

   static void SortX()
   {
      // Define ADO Objects.
      Connection cnConn1 = null;
      Recordset rstAuthors = null;

      // Declarations.
      String strCnn = "DSN=Pubs;Provider=MSDASQL;uid=sa;pwd=;";

      try
      {
         cnConn1 = new Connection();
         cnConn1.open(strCnn);
         rstAuthors = new Recordset();
         rstAuthors.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rstAuthors.open("SELECT * FROM Authors",
               cnConn1,
               AdoEnums.CursorType.STATIC,
               AdoEnums.LockType.READONLY,
               AdoEnums.CommandType.TEXT);
         SortXprint("Initial Order",rstAuthors);

         rstAuthors.setSort("au_lname ASC, au_fname ASC");
         SortXprint("Last Name Ascending",rstAuthors);

         rstAuthors.setSort("au_lname DESC, au_fname ASC");
         SortXprint("Last Name Descending",rstAuthors);

         // Cleanup objects before exit.
         rstAuthors.close();
         cnConn1.close();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstAuthors != null)
         {
            PrintProviderError(rstAuthors.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

   }

   // SortXprint function

   static void SortXprint(String strTitle,Recordset rstp)
   {
      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      int intDisplaysize = 15;
      int intCount = 1;
      try
      {
         System.out.println("---------------" +
                        strTitle +
                        "---------------");
         System.out.println("First Name   Last Name" + "\n" +
                        "-------------------------" +
                        "-------------------------");
         rstp.moveFirst();
         while(!rstp.getEOF())
         {
            System.out.println(rstp.getField("au_fname").getString() +
                           "   " +
                           rstp.getField("au_lname").getString());
            if(intCount % intDisplaysize == 0)
            {
               System.out.println("\nPress <Enter> to continue..");
               in.readLine();
               intCount = 0;
            }
            intCount++;
            rstp.moveNext();
         }
         System.out.println("\nPress <Enter> to continue..");
         in.readLine();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstp != null)
         {
            PrintProviderError(rstp.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }
   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndSortJ

See Also

Recordset Object | Sort Property


Source Property Example (VJ++)

This example demonstrates the Source property by opening three Recordset objects based on different data sources.

// BeginSourceJ
import java.io.*;
import com.ms.wfc.data.*;

public class SourceX
{
   // The main entry point of the application.
   public static void main (String[] args)
   {
      SourceX();
      System.exit(0);
   }
   // SourceX Function
   static void SourceX()
   {
      // Define string variables.
      String strCnn = "Provider=sqloledb;Data Source=MyServer;" +
                  "Initial Catalog=Pubs;User Id=sa;Password=;";
      String strSQL = "SELECT title_ID AS TitleID, title AS Title,"+
                "publishers.pub_id AS PubID, pub_name AS PubName "+
                "FROM publishers INNER JOIN Titles "+
                "ON publishers.pub_id=Titles.pub_id "+
                "ORDER BY Title";

      // Define ADO Objects.
      Connection cnn1 = null;
      Recordset rstTitles = null;
      Recordset rstPublishers = null;
      Recordset rstPublishersDirect = null;
      Recordset rstTitlesPublishers = null;
      Command cmdSQL = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));

      try
      {
         // Open a connection.
         cnn1 = new Connection();
         cnn1.open(strCnn);

         // Open a recordset based on a command object.
         cmdSQL = new Command();
         cmdSQL.setActiveConnection(cnn1);
         cmdSQL.setCommandText("Select title,type,pubdate " +
             "FROM Titles ORDER BY title");

         rstTitles = new Recordset();
         rstTitles = cmdSQL.execute();

         // Open a recordset based on a table.
         rstPublishers = new Recordset();
         rstPublishers.open("publishers", strCnn,
            AdoEnums.CursorType.FORWARDONLY, AdoEnums.LockType.READONLY,
            AdoEnums.CommandType.TABLE);

         // Open a recordset based on a table.
         rstPublishersDirect = new Recordset();
         rstPublishersDirect.open("publishers", strCnn,
            AdoEnums.CursorType.FORWARDONLY, AdoEnums.LockType.READONLY,
            AdoEnums.CommandType.TABLEDIRECT);

         // Open a recordset based on an SQL String.
         rstTitlesPublishers = new Recordset();

         rstTitlesPublishers.open(strSQL, strCnn,
            AdoEnums.CursorType.FORWARDONLY, AdoEnums.LockType.READONLY,
            AdoEnums.CommandType.TEXT);

         // Use Source property to display the source of each recordset.
         System.out.println("\nrstTitles source: \n"+
            rstTitles.getSource());
         System.out.println("\nrstPublishers source: \n"+
            rstPublishers.getSource());
         System.out.println("\nrstPublishersDirect source:\n" +
            rstPublishersDirect.getSource() );
         System.out.println("\nrstTitlesPublishers source: \n" +
            rstTitlesPublishers.getSource());

         // Cleanup Objects before exit.
         rstTitles.close();
         rstPublishers.close();
         rstPublishersDirect.close();
         rstTitlesPublishers.close();
         cnn1.close();

         System.out.println("\n\nPress <Enter> to continue..");
         in.readLine();
      }
      catch(AdoException ae)
      {
         // Notify the user of any errors that result from ADO.

         // As passing a recordset, check for null pointer first.

         if(rstPublishers != null)
         {
            PrintProviderError(rstPublishers.getActiveConnection());
         }
         else if(rstPublishersDirect != null)
         {
            PrintProviderError(
               rstPublishersDirect.getActiveConnection());
         }
         else if(rstTitles != null)
         {
            PrintProviderError(rstTitles.getActiveConnection());
         }
         else if(rstTitlesPublishers != null)
         {
            PrintProviderError(
               rstTitlesPublishers.getActiveConnection());
         }
         else
            System.out.println("Exception: " + ae.getMessage());

      }
      // System read requires this catch.
      catch(java.io.IOException je)
      {
         PrintIOError(je);
      }

   }
   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndSourceJ

See Also

Recordset Object | Source Property (ADO Recordset)


State Property Example (VJ++)

This example uses the State property to display a message while asynchronous connections are opening and asynchronous commands are executing.

// BeginStateJ
import com.ms.wfc.data.*;
import java.io.*;

public class StateX
{
   // The main entry point of the application.
   public static void main (String[] args)
   {
      StateX();
      System.exit(0);
   }
   // StateX Function
   static void StateX()
   {
      // Define ADO Objects.
      Connection cnn1 = null;
      Connection cnn2 = null;
      Command cmdChange = null;
      Command cmdRestore = null;

      // Declarations.
      String strCnn = "Provider=sqloledb; Data Source=MyServer;"+
                  "Initial Catalog = Pubs;User Id = sa; Password=;";
      BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));

      try
      {
         // Open two Asynchronous connections, displaying
         // a message while connecting.
         cnn1 = new Connection();
         cnn2 = new Connection();

         cnn1.open(strCnn,"","",AdoEnums.ConnectOption.ASYNCCONNECT);
         while(cnn1.getState()==AdoEnums.ObjectState.CONNECTING)
            System.out.println("Opening the first connection....");

         cnn2.open(strCnn,"","",AdoEnums.ConnectOption.ASYNCCONNECT);
         while(cnn2.getState()==AdoEnums.ObjectState.CONNECTING)
            System.out.println("Opening the second connection....");

         // Create two command Objects.
         cmdChange = new Command();
         cmdChange.setActiveConnection(cnn1);
         cmdChange.setCommandText("UPDATE Titles SET type = 'self_help'"+
                            "WHERE type = 'psychology'");

         cmdRestore = new Command();
         cmdRestore.setActiveConnection(cnn2);
         cmdRestore.setCommandText(
            "UPDATE Titles SET type = 'psychology'"+
            "WHERE type = 'self_help'");

         // Executing the commands, displaying a message
         // while they are executing.
         cmdChange.execute(null,AdoEnums.ExecuteOption.ASYNCEXECUTE);

         while(cmdChange.getState() == AdoEnums.ObjectState.EXECUTING)
            System.out.println("Change command executing....");

         cmdRestore.execute(null,AdoEnums.ExecuteOption.ASYNCEXECUTE);

         while(cmdRestore.getState() == AdoEnums.ObjectState.EXECUTING)
            System.out.println("Restore command executing....");

         // Cleanup Objects before exit.
         cnn1.close();
         cnn2.close();

         System.out.println("Press <Enter> to continue..");
         in.readLine();
      }
      // System read requires this catch.
      catch(java.io.IOException je)
      {
         PrintIOError(je);
      }
      catch(AdoException ae)
      {
         // Notify user of any errors resulting from ADO.

         // As passing a connection, check for null pointer first.
         if(cnn1 != null)
         {
            System.out.println("The error has occured in cnn1:");
            PrintProviderError(cnn1);
         }
         else if(cnn2 != null)
         {
            System.out.println("The error has occured in cnn2:");
            PrintProviderError(cnn2);
         }
         else
         {
            System.out.println("Exception: "+ ae.getMessage());
         }
      }

   }
   // PrintProviderError Function
   static void PrintProviderError(Connection cnn1)
   {
      // Print Provider Errors from Connection Object.
      // ErrItem is an item object in the Connection’s Errors Collection.
      com.ms.wfc.data.Error               ErrItem = null;
      long                                 nCount = 0;
      int                                       i = 0;

      nCount = cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if ( nCount > 0)
      {
         // Collection ranges from 0 to nCount-1.
         for ( i=0;i<nCount; i++)
         {
            ErrItem = cnn1.getErrors().getItem(i);
            System.out.println("\t Error Number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription());
         }
      }
   }
   // PrintIOError Function
   static void PrintIOError(java.io.IOException je)
   {
      System.out.println("Error: \n");
      System.out.println("\t Source: " + je.getClass() + "\n");
      System.out.println("\t Description: "+ je.getMessage() + "\n");
   }
}
// EndStateJ

See Also

State Property


Status Property Example (VJ++)

This example uses the Status property to display which records have been modified in a batch operation before a batch update has occurred.

// BeginStatusJ
import java.io.*;
import com.ms.wfc.data.*;

public class StatusX
{
   // The main entry point of the application.

   public static void main (String[] args)
   {
      StatusX();
      System.exit(0);
   }
   // StatusX Function

   static void StatusX()
   {
      // Define ADO Objects.
      Recordset rstTitles = null;

      // Declarations.
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"+
                  "Initial Catalog=Pubs;User Id =sa;Password=;";
      BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));

      try
      {
         // Open Recordset for batch update.
         rstTitles = new Recordset();
         rstTitles.setCursorType(AdoEnums.CursorType.KEYSET);
         rstTitles.setLockType(AdoEnums.LockType.BATCHOPTIMISTIC);
         rstTitles.open("Titles", strCnn, AdoEnums.CursorType.KEYSET,
            AdoEnums.LockType.BATCHOPTIMISTIC,
            AdoEnums.CommandType.TABLE);

         // Change the type of psychology titles.
         while(!rstTitles.getEOF())
         {
            if(rstTitles.getField("Type").getString().trim().
               equals(new String("psychology")))
               rstTitles.getField("Type").setString("self_help");

            rstTitles.moveNext();
         }

         // Display Title ID and status.
         rstTitles.moveFirst();

         while(!rstTitles.getEOF())
         {
            if(rstTitles.getStatus()==AdoEnums.RecordStatus.MODIFIED)
               System.out.println(rstTitles.getField("title_id").
                  getString() + "-  Modified");
            else
               System.out.println(rstTitles.getField("title_id").
                  getString());
            rstTitles.moveNext();
         }

         // Cancel the update because this is a demonstration.
         rstTitles.cancelBatch();

         // Clean up objects before exit.
         rstTitles.close();

         System.out.println("Press <Enter> to continue..");
         in.readLine();
      }
      catch(AdoException ae)
      {
         // Notify the user of any errors that result from ADO.

         // As passing a Recordset, check for the null pointer first.
         if(rstTitles != null)
         {
            PrintProviderError(rstTitles.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }
      // System read requires this catch.
      catch(java.io.IOException je)
      {
         PrintIOError(je);
      }
   }
   // PrintProviderError Function
   static void PrintProviderError(Connection cnn1)
   {
      // Print Provider Errors from Connection Object.
      // ErrItem is an item object in the Connection’s Errors Collection.
      com.ms.wfc.data.Error               ErrItem = null;
      long                                 nCount = 0;
      int                                       i = 0;

      nCount = cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if ( nCount > 0)
      {
         // Collection ranges from 0 to nCount-1.
         for ( i=0;i<nCount; i++)
         {
            ErrItem = cnn1.getErrors().getItem(i);
            System.out.println("\t Error Number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription());
         }
      }
   }
   // PrintIOError Function
   static void PrintIOError(java.io.IOException je)
   {
      System.out.println("Error: \n");
      System.out.println("\t Source: " + je.getClass() + "\n");
      System.out.println("\t Description: "+ je.getMessage() + "\n");
   }
}
// EndStatusJ

See Also

Status Property (ADO Recordset)


StayInSync Property Example (VJ++)

This example demonstrates how the StayInSync property facilitates accessing rows in a hierarchical Recordset.

The outer loop displays each author's first and last name, state, and identification. The appended Recordset for each row is retrieved from the Fields collection and automatically assigned to rstTitleAuthor by the StayInSync property whenever the parent Recordset moves to a new row. The inner loop displays four fields from each row in the appended recordset.

// BeginStayInSyncJ
import com.ms.wfc.data.*;
import java.io.* ;
import com.ms.com.*;

public class StayInSyncX
{
    // The main entry point for the application.

   public static void main (String[] args)
   {
      StayInSyncX();
      System.exit(0);
   }

   // StayInSyncX function

   static void StayInSyncX()
   {

      // Define ADO Objects.
      Connection cnConn1 = null;
      Recordset rstAuthors = null;
      Recordset rstTitleAuthor = null;

      // Declarations.
      BufferedReader in = new
         BufferedReader (new InputStreamReader(System.in));
      String strCnn = "DSN=Pubs;Provider=MSDataShape;" +
         "Data Provider=MSDASQL;uid=sa;pwd=;";

      try
      {
         cnConn1 = new Connection();
         cnConn1.open(strCnn);
         rstAuthors = new Recordset();
         rstAuthors.setStayInSync(true);
         rstAuthors.open("SHAPE  {select * from Authors} " +
                "APPEND ({select * from titleauthor}" +
                "RELATE au_id TO au_id) AS chapTitleAuthor",
               cnConn1,
               AdoEnums.CursorType.STATIC,
               AdoEnums.LockType.READONLY,
               AdoEnums.CommandType.TEXT);

         Variant varRstTitleAuthor = rstAuthors.getFields().
            getItem("chapTitleAuthor").getValue();
         rstTitleAuthor =new Recordset(varRstTitleAuthor.toObject());
         int intCount =0;
         while(!rstAuthors.getEOF())
         {
            System.out.println("\n" +
               rstAuthors.getField("au_fname").getString() + " " +
               rstAuthors.getField("au_lname").getString() + " " +
                rstAuthors.getField("state").getString() + " " +
                rstAuthors.getField("au_id").getString());
            while(!rstTitleAuthor.getEOF())
            {
               System.out.println(rstTitleAuthor.getField(0).
                  getString() + " " +
                  rstTitleAuthor.getField(1).getString() + " " +
                  rstTitleAuthor.getField(2).getString() + " " +
                  rstTitleAuthor.getField(3).getString());
               rstTitleAuthor.moveNext();
            }
            if(++intCount % 5 == 0)
            {
               System.out.println("\nPress <Enter> to continue..");
               in.readLine();
            }
            rstAuthors.moveNext();
         }
         // Cleanup objects before exit.
         rstTitleAuthor.close();
         rstAuthors.close();
         cnConn1.close();

         System.out.println("\nPress <Enter> to continue..");
         in.readLine();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstAuthors != null)
         {
            PrintProviderError(rstAuthors.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function
   static void PrintProviderError(Connection cnn1)
   {
      // Print Provider Errors from Connection Object.
      // ErrItem is an item object in the Connection’s Errors Collection.
      com.ms.wfc.data.Error               ErrItem = null;
      long                                 nCount = 0;
      int                                       i = 0;

      nCount = cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if ( nCount > 0)
      {
         // Collection ranges from 0 to nCount-1.
         for ( i=0;i<nCount; i++)
         {
            ErrItem = cnn1.getErrors().getItem(i);
            System.out.println("\t Error Number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription());
         }
      }
   }
   // PrintIOError Function
   static void PrintIOError(java.io.IOException je)
   {
      System.out.println("Error: \n");
      System.out.println("\t Source: " + je.getClass() + "\n");
      System.out.println("\t Description: "+ je.getMessage() + "\n");
   }
}
// EndStayInSyncJ

See Also

Fields Collection | Recordset Object | StayInSync Property


Supports Method Example (VJ++)

This example uses the Supports method to display the options supported by a recordset opened with different cursor types. The DisplaySupport function is required for this example to run.

// BeginSupportsJ
import com.ms.wfc.data.*;
import java.io.*;

public class SupportsX
{
   // The main entry point of the application.

   public static void main (String[] args)
   {
      SupportsX();
      System.exit(0);
   }
   // SupportsX Function

   static void SupportsX()
   {
      // Define ADO Objects.
      Recordset rstTitles = null;

      // Declarations.
      int[] aintCursorType = new int[4];
      String strCnn;
      int intIndex;
      BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));

      try
      {
         // Open connections.
         strCnn = "Provider= sqloledb;Data Source=MyServer;"+
                "Initial Catalog=Pubs;User Id=sa;Password=;";

         // Fill array with CursorType constants.
         aintCursorType[0] = AdoEnums.CursorType.FORWARDONLY;
         aintCursorType[1] = AdoEnums.CursorType.KEYSET;
         aintCursorType[2] = AdoEnums.CursorType.DYNAMIC;
         aintCursorType[3] = AdoEnums.CursorType.STATIC;

         // Open recordset using each CursorType and
         // Optimistic Locking.  Then call the DisplaySupport
         // procedure to display the supported options.
         for(intIndex = 0; intIndex < 4; intIndex++)
         {
            rstTitles = new Recordset();
            rstTitles.setCursorType(aintCursorType[intIndex]);
            rstTitles.setLockType(AdoEnums.LockType.OPTIMISTIC);
            rstTitles.open("Titles", strCnn, aintCursorType[intIndex],
               AdoEnums.LockType.OPTIMISTIC, AdoEnums.CommandType.TABLE);

            switch(aintCursorType[intIndex])
            {
            case AdoEnums.CursorType.FORWARDONLY:
               System.out.println("ForwardOnly cursor supports:");
               break;
            case AdoEnums.CursorType.KEYSET:
               System.out.println("Keyset cursor supports:");
               break;
            case AdoEnums.CursorType.DYNAMIC:
               System.out.println("Dynamic cursor supports:");
               break;
            case AdoEnums.CursorType.STATIC:
               System.out.println("Static cursor supports:");
               break;
            default:
               break;
            }
            DisplaySupport(rstTitles);
            rstTitles.close();
            System.out.println("Press <Enter> to continue..");
            in.readLine();
         }
      }
      catch(AdoException ae)
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstTitles!= null)
         {
            PrintProviderError(rstTitles.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }
      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }
   // DisplaySupport Function

   static void DisplaySupport( Recordset rstTemp )
   {
      long[] alngConstants = new long[11];
      boolean booSupports;
      int intIndex;
      try
      {
         // Fill array with cursor option constants.
         alngConstants[0]  = AdoEnums.CursorOption.ADDNEW;
         alngConstants[1]  = AdoEnums.CursorOption.APPROXPOSITION;
         alngConstants[2]  = AdoEnums.CursorOption.BOOKMARK;
         alngConstants[3]  = AdoEnums.CursorOption.DELETE;
         alngConstants[4]  = AdoEnums.CursorOption.FIND;
         alngConstants[5]  = AdoEnums.CursorOption.HOLDRECORDS;
         alngConstants[6]  = AdoEnums.CursorOption.MOVEPREVIOUS;
         alngConstants[7]  = AdoEnums.CursorOption.NOTIFY;
         alngConstants[8]  = AdoEnums.CursorOption.RESYNC;
         alngConstants[9]  = AdoEnums.CursorOption.UPDATE;
         alngConstants[10] = AdoEnums.CursorOption.UPDATEBATCH;

         for(intIndex = 0; intIndex <= 10; intIndex++)
         {
            booSupports = rstTemp.supports((int)alngConstants[intIndex]);
            if (booSupports)
            {
               switch((int)alngConstants[intIndex])
               {
               case AdoEnums.CursorOption.ADDNEW:
                     System.out.println("    AddNew");
                     break;
               case AdoEnums.CursorOption.APPROXPOSITION:
                     System.out.println(
                        "    AbsolutePosition and AbsolutePage");
                     break;
               case AdoEnums.CursorOption.BOOKMARK:
                     System.out.println("    Bookmark");
                     break;
               case AdoEnums.CursorOption.DELETE:
                     System.out.println("    Delete");
                     break;
               case AdoEnums.CursorOption.FIND:
                     System.out.println("    Find");
                     break;
               case AdoEnums.CursorOption.HOLDRECORDS:
                     System.out.println("    Holding Records");
                     break;
               case AdoEnums.CursorOption.MOVEPREVIOUS:
                     System.out.println("    MovePrevious and Move");
                     break;
               case AdoEnums.CursorOption.NOTIFY:
                     System.out.println("    Notifications");
                     break;
               case AdoEnums.CursorOption.RESYNC:
                     System.out.println("    Resyncing Data");
                     break;
               case AdoEnums.CursorOption.UPDATE:
                     System.out.println("    Update");
                     break;
               case AdoEnums.CursorOption.UPDATEBATCH:
                     System.out.println("    Batch Updating");
                     break;
               default:
                     break;
               }
            }
         }
      }
      catch(AdoException ae)
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstTemp!= null)
         {
            PrintProviderError(rstTemp.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }
   }

   // PrintProviderError Function
   static void PrintProviderError(Connection cnn1)
   {
      // Print Provider Errors from Connection Object.
      // ErrItem is an item object in the Connection’s Errors Collection.
      com.ms.wfc.data.Error               ErrItem = null;
      long                                 nCount = 0;
      int                                       i = 0;

      nCount = cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if ( nCount > 0)
      {
         // Collection ranges from 0 to nCount-1.
         for ( i=0;i<nCount; i++)
         {
            ErrItem = cnn1.getErrors().getItem(i);
            System.out.println("\t Error Number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription());
         }
      }
   }
   // PrintIOError Function
   static void PrintIOError(java.io.IOException je)
   {
      System.out.println("Error: \n");
      System.out.println("\t Source: " + je.getClass() + "\n");
      System.out.println("\t Description: "+ je.getMessage() + "\n");
   }
}
// EndSupportsJ

See Also

Recordset Object | Supports Method


Type Property Example (Field) (VJ++)

This example demonstrates the Type property by displaying the name of the constant that corresponds to the value of the Type property of all the Field objects in the Employees table. The FieldType function is required for this procedure to run.

// BeginFieldTypeJ
import java.io.*;
import com.ms.wfc.data.*;

public class TypeX
{

   // The main entry point of the application.

   public static void main (String[] args)
   {
      TypeX();
      System.exit(0);
   }
   // TypeX Function

   static void TypeX()
   {
      // Define ADO Objects.
      Recordset rstEmployees = null;
      Field fldLoop = null;

      // Declarations.
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"+
                "Initial Catalog = Pubs;User Id=sa;Password=;";
      int intLoop;
      int intRecordCount = 0;
      BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));

      try
      {
         // Open the Recordset with data from Employees table.
         rstEmployees = new Recordset();
         rstEmployees.open("employee", strCnn,
            AdoEnums.CursorType.FORWARDONLY, AdoEnums.LockType.READONLY,
            AdoEnums.CommandType.TABLE);

         System.out.println("Fields in the Employees table:\n");

         // Enumerate fields collection of Employees table.
         for(intLoop = 0;intLoop <
            rstEmployees.getFields().getCount();intLoop++)
         {
            intRecordCount++;
            // Loop needed for display of records
            if((intRecordCount % 6)==0)
            {
               System.out.println("Press <Enter> to continue..");
               in.readLine();
            }

            fldLoop = rstEmployees.getFields().getItem(intLoop);
            System.out.println("  Name:" + fldLoop.getName() + "\n"+
               "  Type:" + FieldType(fldLoop.getType()) + "\n");

         }
         System.out.println("Press <Enter> to continue");
         in.readLine();
      }
      catch(AdoException ae)
      {
         // Notify the user of any errors that result from ADO.

         // As passing a Recordset, check for the null pointer first.
         if(rstEmployees != null)
         {
            PrintProviderError(rstEmployees.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }
      // System read requires this catch.
      catch(java.io.IOException je)
      {
         PrintIOError(je);
      }
   }
   // FieldType Function
   static String FieldType( int intType )
   {
      String strLoop = null;

         switch(intType)
         {
         case AdoEnums.DataType.CHAR:
            strLoop = "adChar";
            break;
         case AdoEnums.DataType.VARCHAR:
            strLoop ="adVarChar";
            break;
         case AdoEnums.DataType.SMALLINT:
            strLoop = "adSmallInt";
            break;
         case AdoEnums.DataType.UNSIGNEDTINYINT:
            strLoop = "adUnsignedTinyInt" ;
            break;
         case AdoEnums.DataType.DBTIMESTAMP:
            strLoop = "adDBTimeStamp";
            break;
         default:
            break;
         }

      return strLoop;
   }

   // PrintProviderError Function
   static void PrintProviderError(Connection cnn1)
   {
      // Print Provider Errors from Connection Object.
      // ErrItem is an item object in the Connection’s Errors Collection.
      com.ms.wfc.data.Error               ErrItem = null;
      long                                 nCount = 0;
      int                                       i = 0;

      nCount = cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if ( nCount > 0)
      {
         // Collection ranges from 0 to nCount-1.
         for ( i=0;i<nCount; i++)
         {
            ErrItem = cnn1.getErrors().getItem(i);
            System.out.println("\t Error Number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription());
         }
      }
   }
   // PrintIOError Function
   static void PrintIOError(java.io.IOException je)
   {
      System.out.println("Error: \n");
      System.out.println("\t Source: " + je.getClass() + "\n");
      System.out.println("\t Description: "+ je.getMessage() + "\n");
   }
}
// EndFieldTypeJ

See Also

Field Object | Type Property


Type Property Example (Property) (VJ++)

This example demonstrates the Type property. It is a model of a utility for listing the names and types of a collection, like Properties, Fields, etc.

We do not need to open the Recordset to access its Properties collection; they come into existence when the Recordset object is instantiated. However, setting the CursorLocation property to adUseClient adds several dynamic properties to the Recordset object's Properties collection, making the example a little more interesting. For sake of illustration, we explicitly use the Item property to access each Property object.

// BegintTypePropertyJ
import com.ms.wfc.data.*;
import java.io.* ;

public class TypeX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      TypeX();
      System.exit(0);
   }

   // TypeX  function

   static void TypeX()
   {
      // Define ADO Objects.
      Recordset rst = null;
      AdoProperty prop = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      String strCnn = "DSN=Pubs;Provider=MSDASQL;uid=sa;pwd=;";
      String strMsg;
      int intIndex;
      int intDisplaysize = 15;

      try
      {
         rst = new Recordset();
         rst.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         for(intIndex = 0;
            intIndex <= rst.getProperties().getCount() - 1;intIndex++)
         {
            prop = rst.getProperties().getItem(intIndex);
            switch(prop.getType())
            {
            case AdoEnums.DataType.BIGINT :
                  strMsg = "adBigInt";
                  break;
            case AdoEnums.DataType.BINARY :
                  strMsg = "adBinary";
                  break;
            case AdoEnums.DataType.BOOLEAN :
                  strMsg = "adBoolean";
                  break;
            case AdoEnums.DataType.BSTR :
                  strMsg = "adBSTR";
                  break;
            case AdoEnums.DataType.CHAPTER :
                  strMsg = "adChapter";
                  break;
            case AdoEnums.DataType.CHAR :
                  strMsg = "adChar";
                  break;
            case AdoEnums.DataType.CURRENCY :
                  strMsg = "adCurrency";
                  break;
            case AdoEnums.DataType.DATE :
                  strMsg = "adDate";
                  break;
            case AdoEnums.DataType.DBDATE :
                  strMsg = "adDBDate";
                  break;
            case AdoEnums.DataType.DBTIME :
                  strMsg = "adDBTime";
                  break;
            case AdoEnums.DataType.DBTIMESTAMP :
                  strMsg = "adDBTimeStamp";
                  break;
            case AdoEnums.DataType.DECIMAL :
                  strMsg = "adDecimal";
                  break;
            case AdoEnums.DataType.DOUBLE :
                  strMsg = "adDouble";
                  break;
            case AdoEnums.DataType.EMPTY :
                  strMsg = "adEmpty";
                  break;
            case AdoEnums.DataType.ERROR :
                  strMsg = "adError";
                  break;
            case AdoEnums.DataType.FILETIME :
                  strMsg = "adFileTime";
                  break;
            case AdoEnums.DataType.GUID :
                  strMsg = "adGUID";
                  break;
            case AdoEnums.DataType.IDISPATCH :
                  strMsg = "adIDispatch";
                  break;
            case AdoEnums.DataType.INTEGER :
                  strMsg = "adInteger";
                  break;
            case AdoEnums.DataType.IUNKNOWN :
                  strMsg = "adIUnknown";
                  break;
            case AdoEnums.DataType.LONGVARBINARY :
                  strMsg = "adLongVarBinary";
                  break;
            case AdoEnums.DataType.LONGVARCHAR :
                  strMsg = "adLongVarChar";
                  break;
            case AdoEnums.DataType.LONGVARWCHAR :
                  strMsg = "adLongVarWChar";
                  break;
            case AdoEnums.DataType.NUMERIC :
                  strMsg = "adNumeric";
                  break;
            case AdoEnums.DataType.PROPVARIANT :
                  strMsg = "adPropVariant";
                  break;
            case AdoEnums.DataType.SINGLE :
                  strMsg = "adSingle";
                  break;
            case AdoEnums.DataType.SMALLINT :
                  strMsg = "adSmallInt";
                  break;
            case AdoEnums.DataType.TINYINT :
                  strMsg = "adTinyInt";
                  break;
            case AdoEnums.DataType.UNSIGNEDBIGINT :
                  strMsg = "adUnsignedBigInt";
                  break;
            case AdoEnums.DataType.UNSIGNEDINT :
                  strMsg = "adUnsignedInt";
                  break;
            case AdoEnums.DataType.UNSIGNEDSMALLINT :
                  strMsg = "adUnsignedSmallInt";
                  break;
            case AdoEnums.DataType.UNSIGNEDTINYINT :
                  strMsg = "adUnsignedTinyInt";
                  break;
            case AdoEnums.DataType.USERDEFINED :
                  strMsg = "adUserDefined";
                  break;
            case AdoEnums.DataType.VARBINARY :
                  strMsg = "adVarBinary";
                  break;
            case AdoEnums.DataType.VARCHAR :
                  strMsg = "adVarChar";
                  break;
            case AdoEnums.DataType.VARIANT :
                  strMsg = "adVariant";
                  break;
            case AdoEnums.DataType.VARNUMERIC :
                  strMsg = "adVarNumeric";
                  break;
            case AdoEnums.DataType.VARWCHAR :
                  strMsg = "adVarWChar";
                  break;
            case AdoEnums.DataType.WCHAR :
                  strMsg = "adWChar";
                  break;
            default:
                  strMsg = "*UNKNOWN*";
                  break;
            }
            System.out.println("Property " +
                           Integer.toString(intIndex) +
                           " : " +
                           prop.getName() +
                           ", Type = " +
                           strMsg);
            if(intIndex % intDisplaysize == 0 && intIndex != 0)
            {
               System.out.println("\nPress <Enter> to continue..");
               in.readLine();
            }
         }

         System.out.println("\nPress <Enter> to continue..");
         in.readLine();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rst != null)
         {
            PrintProviderError(rst.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function
   static void PrintProviderError(Connection cnn1)
   {
      // Print Provider Errors from Connection Object.
      // ErrItem is an item object in the Connection’s Errors Collection.
      com.ms.wfc.data.Error               ErrItem = null;
      long                                 nCount = 0;
      int                                       i = 0;

      nCount = cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if ( nCount > 0)
      {
         // Collection ranges from 0 to nCount-1.
         for ( i=0;i<nCount; i++)
         {
            ErrItem = cnn1.getErrors().getItem(i);
            System.out.println("\t Error Number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription());
         }
      }
   }
   // PrintIOError Function
   static void PrintIOError(java.io.IOException je)
   {
      System.out.println("Error: \n");
      System.out.println("\t Source: " + je.getClass() + "\n");
      System.out.println("\t Description: "+ je.getMessage() + "\n");
   }
}
// EndTypePropertyJ

See Also

Property Object | Type Property


Update and CancelUpdate Methods Example (VJ++)

This example demonstrates the Update method in conjunction with the CancelUpdate method.

// BeginUpdateJ
import java.io.*;
import com.ms.wfc.data.*;

public class UpdateX
{
   // The main entry point of the application.

   public static void main (String[] args)
   {
      UpdateX();
      UpdateX2();
      System.exit(0);
   }
   // UpdateX Function

   static void UpdateX()
   {
      // Define ADO objects.
      Recordset rstEmployees = null;

      // Declarations.
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"+
                  "Initial Catalog=Pubs;User Id=sa;Password=;";
      String strOldFirst;
      String strOldLast;
      String strMessage;
      BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));

      try
      {
         // Open Recordset with names from Employees table.
         rstEmployees = new Recordset();
         rstEmployees.setCursorType(AdoEnums.CursorType.KEYSET);
         rstEmployees.setLockType(AdoEnums.LockType.OPTIMISTIC);
         rstEmployees.open("SELECT fname,lname FROM Employees " +
            "ORDER BY lname", strCnn, AdoEnums.CursorType.KEYSET,
            AdoEnums.LockType.OPTIMISTIC, AdoEnums.CommandType.TEXT);

         // Store Original data.
         strOldFirst = rstEmployees.getField("fname").getString();
         strOldLast = rstEmployees.getField("lname").getString();

         // Change data in edit buffer.
         rstEmployees.getField("fname").setString("Linda");
         rstEmployees.getField("lname").setString("Kobara");

         // Show contents of buffer and get user input.
         strMessage = "Edit in Progress :\n" +
            "\tOriginal Data = " + strOldFirst + "  " + strOldLast +
            "\n\tData in Buffer = " +
            rstEmployees.getField("fname").getString() + " " +
            rstEmployees.getField("lname").getString() + "\n\n" +
             "Use Update to replace the original data with " +
            "the buffered data in the Recordset?Enter (Y/N)";

         System.out.println(strMessage);
         if(in.readLine().trim().equalsIgnoreCase("Y"))
            rstEmployees.update();
         else
            rstEmployees.cancelUpdate();

         // Show the resulting data.
         System.out.println("Data in Recordset = " +
            rstEmployees.getField("fname").getString() +
            "  " + rstEmployees.getField("lname").getString()+ "\n");

         // Restore original data because this is a demonstration.
         if(!(strOldFirst.equals(
            rstEmployees.getField("fname").getString()) &&
            strOldLast.equals(
            rstEmployees.getField("lname").getString())))
         {
            rstEmployees.getField("fname").setString(strOldFirst);
            rstEmployees.getField("lname").setString(strOldLast);
            rstEmployees.update();
         }
         // Cleanup Objects before exit.
         rstEmployees.close();

         System.out.println("Press <Enter> to continue..");
         in.readLine();

      }
      catch(AdoException ae)
      {
         // Notify the user of any errors that result from ADO.

         // As passing a recordset, check for null pointer first.
         if(rstEmployees != null)
         {
            PrintProviderError(rstEmployees.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: "+ ae.getMessage());
         }
      }
      // System read requires this catch.
      catch(java.io.IOException je)
      {
         PrintIOError(je);
      }
   }
   // UpdateX2 Function

   static void UpdateX2()
   {
      // This example demonstrates the Update method in conjunction
      // with the AddNew method.

      // Define ADO Objects.
      Connection cnn1 = null;
      Recordset rstEmployees = null;

      // Declarations.
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"+
                  "Initial Catalog=Pubs;User Id=sa;Password=;";
      String strEmpID;
      String strOldFirst;
      String strOldLast;
      String strMessage;
      BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));

      try
      {
         // Open a connection.
         cnn1 = new Connection();
         cnn1.open(strCnn);

         // Open Recordset with data from Employees table.
         rstEmployees = new Recordset();
         rstEmployees.setCursorType(AdoEnums.CursorType.KEYSET);
         rstEmployees.setLockType(AdoEnums.LockType.OPTIMISTIC);
         rstEmployees.open("employee", cnn1, AdoEnums.CursorType.KEYSET,
            AdoEnums.LockType.OPTIMISTIC, AdoEnums.CommandType.TABLE);

         rstEmployees.addNew();
         strEmpID="B-S55555M";
         rstEmployees.getField("emp_id").setString(strEmpID);
         rstEmployees.getField("fname").setString("Bill");
         rstEmployees.getField("lname").setString("Sornsin");

         // Show contents of buffer and get user input.
         strMessage = "AddNew in progress : " + "\n" +
            "\tData in Buffer = " +
            rstEmployees.getField("emp_id").getString() +
            "  " + rstEmployees.getField("fname").getString() + "  " +
            rstEmployees.getField("lname").getString()+ "\n\n" +
            "Use Update to save buffer to recordset?Enter (Y/N)";
         System.out.println(strMessage);
         if(in.readLine().trim().equalsIgnoreCase("Y"))
         {
            rstEmployees.update();
            // Go to the new record and show the resulting data.
            System.out.println("Data in recordset = " +
               rstEmployees.getField("emp_id").getString()+
               "  " + rstEmployees.getField("fname").getString() +
               "  " + rstEmployees.getField("lname").getString() + "\n");
         }
         else
         {
            rstEmployees.cancelUpdate();
            System.out.println("No new Record added.\n");
         }
         // Delete new data because this is a demonstration.
         cnn1.execute(
            "DELETE FROM employee WHERE emp_id='" + strEmpID +"'");

         // Cleanup Objects before exit
         rstEmployees.close();

         System.out.println("Press <Enter> to continue..");
         in.readLine();
      }
      catch(AdoException ae)
      {
         // Notify the user of any errors that result from ADO.

         // As passing a recordset, check for null pointer first.
         if(rstEmployees != null)
         {
            PrintProviderError(rstEmployees.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: "+ ae.getMessage());
         }
      }
      // System read requires this catch.
      catch(java.io.IOException je)
      {
         PrintIOError(je);
      }
   }
   // PrintProviderError Function
   static void PrintProviderError(Connection cnn1)
   {
      // Print Provider Errors from Connection Object.
      // ErrItem is an item object in the Connection’s Errors Collection.
      com.ms.wfc.data.Error               ErrItem = null;
      long                                 nCount = 0;
      int                                       i = 0;

      nCount = cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if ( nCount > 0)
      {
         // Collection ranges from 0 to nCount-1.
         for ( i=0;i<nCount; i++)
         {
            ErrItem = cnn1.getErrors().getItem(i);
            System.out.println("\t Error Number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription());
         }
      }
   }
   // PrintIOError Function
   static void PrintIOError(java.io.IOException je)
   {
      System.out.println("Error: \n");
      System.out.println("\t Source: " + je.getClass() + "\n");
      System.out.println("\t Description: "+ je.getMessage() + "\n");
   }
}
// EndUpdateJ

See Also

CancelUpdate Method | Update Method


UpdateBatch and CancelBatch Methods Example (VJ++)

This example demonstrates the UpdateBatch method in conjunction with the CancelBatch method.

// BeginUpdateBatchJ
import java.io.*;
import com.ms.wfc.data.*;

public class UpdateBatchX
{
   // The main entry point of the application.
   public static void main (String[] args)
   {
      UpdateBatchX();
      System.exit(0);
   }
   // UpdateBatchX Function

   static void UpdateBatchX()
   {
      // Define ADO Objects.
      Recordset rstTitles = null;

      // Declarations.
      String strCnn = "Provider=sqloledb;Data Source=MyServer;"+
                  "Initial Catalog=Pubs;User Id=sa;Password=;";
      String strTitle;
      String strMessage;
      BufferedReader in =
         new BufferedReader(new InputStreamReader(System.in));

      try
      {
         rstTitles = new Recordset();
         rstTitles.setCursorType(AdoEnums.CursorType.KEYSET);
         rstTitles.setLockType(AdoEnums.LockType.BATCHOPTIMISTIC);
         rstTitles.open("Titles", strCnn, AdoEnums.CursorType.KEYSET,
            AdoEnums.LockType.BATCHOPTIMISTIC,
            AdoEnums.CommandType.TABLE);

         rstTitles.moveFirst();

         // Loop through recordset and ask user if she wants
         // to change the type for the specified table.
         while(!rstTitles.getEOF())
         {
            if(rstTitles.getField("Type").getString().
               trim().equalsIgnoreCase("psychology"))
            {
               strTitle = rstTitles.getField("Title").getString();
               strMessage = "Title: "+ strTitle + "\n" +
                  "Change type to self_help?Enter (Y/N)";
               System.out.println(strMessage);
               if(in.readLine().trim().equalsIgnoreCase("Y"))
                  rstTitles.getField("type").setString("self_help");
            }
            rstTitles.moveNext();
         }
         // Ask the user if she wants to commit to all the
         // changes made above.
         System.out.println("Save all changes?Enter (Y/N)");
         if(in.readLine().trim().equalsIgnoreCase("Y"))
            rstTitles.updateBatch();
         else
            rstTitles.cancelBatch();

         // Print current data in recordset.
         rstTitles.requery();
         rstTitles.moveFirst();
         while(!rstTitles.getEOF())
         {
         System.out.println(rstTitles.getField("title").getString() +
            " - " + rstTitles.getField("type").getString());
         rstTitles.moveNext();
         }

         // Restore original values because this is a demonstration.
         rstTitles.moveFirst();
         while(!rstTitles.getEOF())
         {
            if(rstTitles.getField("type").getString().
               trim().equalsIgnoreCase("self_help"))
               rstTitles.getField("type").setString("psychology");
            rstTitles.moveNext();
         }
         rstTitles.updateBatch();

         // Clean up objects before exit.
         rstTitles.close();

         System.out.println("Press <Enter> to continue..");
         in.readLine();

      }
      catch(AdoException ae)
      {
         // Notify the user of any errors that result from ADO.

         // As passing a recordset, check for null pointer first.
         if(rstTitles != null)
         {
            PrintProviderError(rstTitles.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }
      // System read requires this catch.
      catch(java.io.IOException je)
      {
         PrintIOError(je);
      }
   }

   // PrintProviderError Function
   static void PrintProviderError(Connection cnn1)
   {
      // Print Provider Errors from Connection Object.
      // ErrItem is an item object in the Connection’s Errors Collection.
      com.ms.wfc.data.Error               ErrItem = null;
      long                                 nCount = 0;
      int                                       i = 0;

      nCount = cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if ( nCount > 0)
      {
         // Collection ranges from 0 to nCount-1.
         for ( i=0;i<nCount; i++)
         {
            ErrItem = cnn1.getErrors().getItem(i);
            System.out.println("\t Error Number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription());
         }
      }
   }
   // PrintIOError Function
   static void PrintIOError(java.io.IOException je)
   {
      System.out.println("Error: \n");
      System.out.println("\t Source: " + je.getClass() + "\n");
      System.out.println("\t Description: "+ je.getMessage() + "\n");
   }
}
// EndUpdateBatchJ

See Also

CancelBatch Method | UpdateBatch Method


Value Property Example (VJ++)

This example demonstrates the Value property with Field and Property objects by displaying field and property values for the Employees table.

// BeginValueJ
import java.io.*;
import com.ms.wfc.data.*;
import com.ms.com.*;

public class ValueX
{
   // Main Function
   public static void main (String[] args)
   {
      ValueX();
      System.exit(0);
   }
   static void ValueX()
   {
      // Define ADO Objects.
      Recordset rstEmployees = null;
      Field    fldLoop     = null;
      AdoProperty  prpLoop   = null;

      // Declarations.
      String strCnn = "Provider=sqloledb; Data Source=MyServer;" +
         "Initial Catalog=Pubs; User Id=sa; Password=;";
      int intLoop;
      BufferedReader in = new
         BufferedReader(new InputStreamReader(System.in));
      Variant varPropertyValue;
      String strMessage;

      try
      {
         // Open a Recordset with data from Employees table.
         rstEmployees = new Recordset();
         rstEmployees.open("employee", strCnn,
            AdoEnums.CursorType.FORWARDONLY, AdoEnums.LockType.READONLY,
            AdoEnums.CommandType.TABLE);

         System.out.println("Field values in rstEmployees\n");

         // Enumerate the Fields collection of the Employees
         // table.
         for(intLoop = 0;
            intLoop<rstEmployees.getFields().getCount();intLoop++)
         {
            fldLoop = rstEmployees.getFields().getItem(intLoop);
            // Because Value is the default property of a
            // Field object, the use of the actual keyword
            // here is optional.
            System.out.println("\t" + fldLoop.getName() + " = " +
               fldLoop.getValue());
         }
         System.out.println("\nPress <Enter> to continue..");
         in.readLine();
         System.out.println("Property values in rstEmployees\n");

         // Enumerate the Properties collection of the
         // Recordset object.
         int intCount = 0;
         for(intLoop = 0;
            intLoop<rstEmployees.getProperties().getCount();intLoop++)
         {
            prpLoop = rstEmployees.getProperties().getItem(intLoop);
            // Because Value is the default property of a
            // Field object, the use of the actual keyword
            // here is optional.
            strMessage = "\t" + prpLoop.getName() + " =  ";
            varPropertyValue = prpLoop.getValue();
            short vttype =varPropertyValue.getvt();
            switch (vttype)
            {
            case Variant.VariantBoolean :
               {
                  if (varPropertyValue.getBoolean())
                     strMessage +="True";
                  else
                     strMessage +="False";
               }
               break;
            case Variant.VariantInt :
               strMessage += varPropertyValue.getInt();
               break;
            default :
               break;
            }
            System.out.println(strMessage);
            intCount++;
            // Loop used to display records
            if (intCount % 15 == 0)
            {
               System.out.println("\nPress <Enter> to continue..");
               in.readLine();
               intCount = 0;
            }

         }
         // Cleanup objects before exit.
         rstEmployees.close();
         System.out.println("\nPress <Enter> to continue..");
         in.readLine();
      }
      // System read requires this catch.
      catch(java.io.IOException je)
      {
         PrintIOError(je);
      }
      catch(AdoException ae)
      {
         // Notify the user of any errors that result from ADO.

         // As passing a recordset, check for null pointer first.
         if(rstEmployees != null)
         {
            PrintProviderError(rstEmployees.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

   }
   // PrintProviderError Function
   static void PrintProviderError(Connection cnn1)
   {
      // Print Provider Errors from Connection Object.
      // ErrItem is an item object in the Connection’s Errors Collection.
      com.ms.wfc.data.Error               ErrItem = null;
      long                                 nCount = 0;
      int                                       i = 0;

      nCount = cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if ( nCount > 0)
      {
         // Collection ranges from 0 to nCount-1.
         for ( i=0;i<nCount; i++)
         {
            ErrItem = cnn1.getErrors().getItem(i);
            System.out.println("\t Error Number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription());
         }
      }
   }
   // PrintIOError Function
   static void PrintIOError(java.io.IOException je)
   {
      System.out.println("Error: \n");
      System.out.println("\t Source: " + je.getClass() + "\n");
      System.out.println("\t Description: "+ je.getMessage() + "\n");
   }
}
// EndValueJ

See Also

Field Object | Property Object | Value Property


Version Property Example (VJ++)

This example uses the Version property of a Connection object to display the current ADO version. It also uses several dynamic properties to show:

  • the current DBMS name and version.
  • OLE DB version.
  • provider name and version.
  • ODBC version.
  • ODBC driver name and version.
// BeginVersionJ
import com.ms.wfc.data.*;
import java.io.*;

public class VersionX
{
   // The main entry point of the application.
   public static void main (String[] args)
   {
      VersionX();
      System.exit(0);
   }
   // VersionX Function
   static void VersionX()
   {
      // Define ADO Objects.
      Connection cnn1 = null;

      // Declarations.
      String strCnn = "Driver={SQL Server};Server=srv;" +
                  "User Id=sa; Password=;database=Pubs;";
      String strVersionInfo;
      BufferedReader in = new
         BufferedReader(new InputStreamReader(System.in));
      try
      {
         // Open connection.
         cnn1 = new Connection();
         cnn1.open(strCnn);

         strVersionInfo = "\tADO Version:\t\t" +
            cnn1.getVersion().toString()+"\n"+
            "\tDBMS Name:\t\t" +
            cnn1.getProperties().getItem("DBMS Name").getString() +"\n"+
            "\tDBMS Version:\t\t"+
            cnn1.getProperties().getItem("DBMS Version").getString() +
            "\n" + "\tOLE DB Version:\t\t" +
            cnn1.getProperties().getItem("OLE DB Version").getString()+
            "\n" + "\tProvider Name:\t\t" +
            cnn1.getProperties().getItem("Provider Name").getString() +
            "\n" + "\tProvider Version:\t" +
            cnn1.getProperties().getItem("Provider Version").
            getString() + "\n" + "\tDriver Name:\t\t" +
            cnn1.getProperties().getItem("Driver Name").getString() +
            "\n" + "\tDriver Version:\t\t" +
            cnn1.getProperties().getItem("Driver Version").getString()+
            "\n" + "\tDriver ODBC Version:\t" +
            cnn1.getProperties().getItem(
            "Driver ODBC Version").getString()+ "\n";
         System.out.println("\n\n" + strVersionInfo);

         // Cleanup Objects before exit.
         cnn1.close();
         System.out.println("Press <Enter> to continue..");
         in.readLine();
      }
      // System read requires this catch.
      catch(java.io.IOException je)
      {
         PrintIOError(je);
      }
      catch(AdoException ae)
      {
         // Notify the user of any errors that result from ADO.

         // As passing a recordset, check for null pointer first.
         if(cnn1!= null)
         {
            PrintProviderError(cnn1);
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

   }
   // PrintProviderError Function
   static void PrintProviderError(Connection cnn1)
   {
      // Print Provider Errors from Connection Object.
      // ErrItem is an item object in the Connection’s Errors Collection.
      com.ms.wfc.data.Error               ErrItem = null;
      long                                 nCount = 0;
      int                                       i = 0;

      nCount = cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if ( nCount > 0)
      {
         // Collection ranges from 0 to nCount-1.
         for ( i=0;i<nCount; i++)
         {
            ErrItem = cnn1.getErrors().getItem(i);
            System.out.println("\t Error Number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription());
         }
      }
   }
   // PrintIOError Function
   static void PrintIOError(java.io.IOException je)
   {
      System.out.println("Error: \n");
      System.out.println("\t Source: " + je.getClass() + "\n");
      System.out.println("\t Description: "+ je.getMessage() + "\n");
   }
}
// EndVersionJ

See Also

Connection Object | Version Property


ADO Code Examples in Microsoft JScript

Use the following code examples to learn how to use the ADO methods, properties, and events when writing in JScript.

Note Paste the entire code example, from beginning to end, into your code editor. The example may not run correctly if partial examples are used or if paragraph formatting is lost.

Methods

  • AddNew Method Example
  • Append and CreateParameter Methods Example
  • Execute, Requery, and Clear Methods Example
  • Find Method Example
  • GetRows Method Example

Properties

  • AbsolutePage, PageCount, and PageSize Properties Example
  • AbsolutePosition and CursorLocation Properties Example
  • ActiveCommand Property Example
  • ActiveConnection, CommandText, CommandTimeout, CommandType, Size, and Direction Properties Example
  • ActualSize and DefinedSize Properties Example
  • CacheSize Property Example
  • Filter and RecordCount Properties Example

See Also

ADO Code Examples in Microsoft Visual Basic | ADO Code Examples in Microsoft Visual Basic Scripting Edition | ADO Code Examples in Microsoft Visual C++ | ADO Code Examples in Microsoft Visual J++ | Sample Applications


AbsolutePage, PageCount, and PageSize Properties Example (JScript)

This example demonstrates the AbsolutePage, PageCount and PageSize properties.

<!-- BeginAbsolutePageJS -->
<% @LANGUAGE="JScript" %>
<!-- Include file for JScript ADO Constants -->
<!--#include File="adojavas.inc"-->

<html>

<head>
<title>AbsolutePage, PageSize, and PageSize Properties (JScript)</title>
<style>
<!--
BODY {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body bgcolor="White">
<h1>AbsolutePage, PageSize, and PageSize Properties (JScript)</h1>
<%
   // Open the connection.
   var Connect = "Provider=sqloledb;Data Source=" +
   Request.ServerVariables("SERVER_NAME")+ ";" +
   "Initial Catalog=Northwind;User Id=sa;Password=;"

   // Open a recordset on the Employee table using
   // a client-side cursor to enable AbsolutePage property.
   var RsEmployee = Server.CreateObject("ADODB.Recordset");
   RsEmployee.CursorLocation = adUseClient;
   RsEmployee.Open("employees", Connect, adOpenStatic, adLockOptimistic, adCmdTable);

   // Set PageSize to five, so we can display names
   // and hire dates, five employees at a time.
   RsEmployee.PageSize = 5;

   // strMessage is used to build up each row of
   // the table before writing to document.
   var strMessage;
   var iPageCount = RsEmployee.PageCount;
   var iRecord;

   // Write header information to the document.
   Response.Write('<p align="center">There are ' + iPageCount);
   Response.Write(" pages, each containing ");
   Response.Write(RsEmployee.PageSize + " records.</p>");
   Response.Write('<table border="0" align="center">');
   Response.Write('<tr class="thead2">');
   Response.Write("<th>Page</th><th>Name</th><th>Hire Date</th></tr>");

   for (var i=1; i<=iPageCount; i++)
   {
      RsEmployee.AbsolutePage = i;

      for (iRecord=1; iRecord <= RsEmployee.PageSize; iRecord++)
      {
         strMessage = "";

            // Start a new table row.
            strMessage = '<tr class="tbody">';

            // First column in row contains page number on
            // first record of each page. Otherwise, the column
            // contains a non-breaking space.
            if (iRecord == 1)
               strMessage += "<td>Page " + i + " of " + RsEmployee.PageCount + "</td>"
            else
               strMessage += "<td>&nbsp;</td>";

            // First and last name are in first column.
            strMessage += "<TD>" + RsEmployee.Fields("FirstName") + " ";
            strMessage += RsEmployee.Fields("LastName") + " " + "</td>";

            // Hire date in second column.
            strMessage += "<td>" + RsEmployee.Fields("HireDate") + "</td>";

            // End the row.
            strMessage += "</tr>";

            // Write line to document.
            Response.Write(strMessage);

            // Get next record.
            RsEmployee.MoveNext;

            if (RsEmployee.EOF)
               break;
      }
   }

   // Finish writing table.
   Response.Write("</table>");

   // Close table.
   RsEmployee.Close;
%>

</body>

</html>
<!-- EndAbsolutePageJS -->

See Also

AbsolutePage Property | PageCount Property | PageSize Property | Recordset Object


AbsolutePosition and CursorLocation Properties Example (JScript)

This example demonstrates how the AbsolutePosition property can track the progress of a loop that enumerates all the records of a Recordset. It uses the CursorLocation property to enable the AbsolutePosition property by setting the cursor to a client cursor.

<!-- BeginAbsolutePositionJS -->
<% @LANGUAGE="JScript" %>
<!-- Include file for JScript ADO Constants -->
<!--#include File="adojavas.inc"-->

<html>

<head>
<title>AbsolutePosition and CursorLocation Properties Example (JScript)</title>
<style>
<!--
BODY {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<h1>AbsolutePosition and CursorLocation Properties Example (JScript)</h1>
<%
   var Connect = "Provider=sqloledb;Data Source=" +
   Request.ServerVariables("SERVER_NAME") + ";" +
   "Initial Catalog=Northwind;User Id=sa;Password=;"

   // Open a recordset on the Employee table using
   // a client-side cursor to enable AbsolutePosition property.
   var RsEmployee = Server.CreateObject("ADODB.Recordset");
   RsEmployee.CursorLocation = adUseClient;
   RsEmployee.Open("employees", Connect, adOpenStatic, adLockOptimistic, adCmdTable);

   var strMessage;      // used to build up each row of the table before writing to document.

   // Write beginning of table to the document.
   Response.Write('<table border="0" align="center">');
   Response.Write('<tr class="thead2">');
   Response.Write("<th>AbsolutePosition</th><th>Name</th><th>Hire Date</th></tr>");

   while (!RsEmployee.EOF)
   {
      strMessage = "";

      // Start a new table row.
      strMessage = '<tr class="tbody">';

      // First column in row contains AbsolutePosition value.
      strMessage += "<td>" + RsEmployee.AbsolutePosition + "</td>"

      // First and last name are in first column.
      strMessage += "<td>" + RsEmployee.Fields("FirstName") + " ";
      strMessage += RsEmployee.Fields("LastName") + " " + "</td>";

      // Hire date in second column.
      strMessage += "<td>" + RsEmployee.Fields("HireDate") + "</td>";

      // End the row.
      strMessage += "</tr>";

      // Write line to document.
      Response.Write(strMessage);

      // Get next record.
      RsEmployee.MoveNext;
   }

   // Finish writing document.
   Response.Write("</table>");

   // Close table.
   RsEmployee.Close;
%>

</html>
<!-- EndAbsolutePositionJS -->

See Also

AbsolutePosition Property | CursorLocation Property | Recordset Object


ActiveCommand Property Example (JScript)

This example demonstrates the ActiveCommand property.

<!-- BeginActiveCommandJS -->
<% @LANGUAGE="JScript" %>
<%
   Response.Write(Request.Form("ContactName"));

   strName = new String(Request.Form("ContactName"))
%>

<html>

<head>
<title>ActiveCommand Property Example (JScript)</title>
<style>
<!--
BODY {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
-->
</style>
</head>

<body bgcolor="White">

<h1>ActiveCommand Property Example (JScript)</h1>

<%
   if (strName.length > 0)
   {
      // Open the connection.
      var Connect = "Provider=sqloledb;Data Source=" +
                    Request.ServerVariables("SERVER_NAME") + ";" +
                    "Initial Catalog=Northwind;User Id=sa;Password=;"

      // Open a recordset on the Customers table using
      // a command object.
      var cnn = Server.CreateObject("ADODB.Connection");
      var cmdContact = Server.CreateObject("ADODB.Command");
      var rsContact = Server.CreateObject("ADODB.Recordset");

      cnn.Open(Connect);

      cmdContact.CommandText = "SELECT * FROM Customers WHERE ContactName = ?";
      cmdContact.Parameters.Append(cmdContact.CreateParameter("ContactName", adChar, adParamInput, 30, strName));
      cmdContact.ActiveConnection = cnn;

      rsContact = cmdContact.Execute();

      var strMessage;      // used to build up line before writing to document.

      while (!rsContact.EOF)
      {
         // Start a new line.
         strMessage = "<P>";

         // First and last name are in first column.
         strMessage += rsContact.Fields("ContactName")

         // End the line.
         strMessage += "</P>";

         // Write line to document.
         Response.Write(strMessage);

         // Get next record.
         rsContact.MoveNext;

      }

      // Close table.
      rsContact.Close;
   }
%>

<hr>

<form method="POST" action="ActiveCommandJS.asp">
  <p align="left">Enter last name of author to find (e.g., Ringer): <input type="text" name="ContactName" size="40" value=""></p>
  <p align="left"><input type="submit" value="Submit" name="B1"><input type="reset" value="Reset" name="B2"></p>
</form>
</body>

</html>
<!-- EndActiveCommandJS -->

See Also

ActiveCommand Property | Command Object | Recordset Object


ActiveConnection, CommandText, CommandTimeout, CommandType, Size, and Direction Properties Example (JScript)

This example uses the ActiveConnection, CommandText, CommandTimeout, CommandType, Size, and Direction properties to execute a stored procedure.

<!-- BeginActiveConnectionJS -->
<% @LANGUAGE="JScript" %>
<!-- Include file for JScript ADO Constants -->
<!--#include File="adojavas.inc"-->

<%
   var iRoyalty = parseInt(Request.Form("RoyaltyValue"));

%>

<html>

<head>
   <title>ActiveConnection, CommandText, CommandTimeout, CommandType, Size, and Direction Properties</title>
<style>
<!--
BODY {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body bgcolor="White">

<%
   if (iRoyalty > -1)
   {
      // Open the connection.
      var Connect = "Provider=sqloledb;Data Source=" +
      Request.ServerVariables("SERVER_NAME") + ";" +
      "Initial Catalog=Pubs;User Id=sa;Password=;"

      // Open a recordset on the author table using
      // a command object.
      var cnn = Server.CreateObject("ADODB.Connection");
      var cmdByRoyalty = Server.CreateObject("ADODB.Command");
      var RsByRoyalty = Server.CreateObject("ADODB.Recordset");
      var RsAuthor = Server.CreateObject("ADODB.Recordset");

      cnn.Open(Connect);

      cmdByRoyalty.CommandText = "byroyalty";
      cmdByRoyalty.CommandType = adCmdStoredProc;
      cmdByRoyalty.CommandTimeOut = 15;

      prmByRoyalty = Server.CreateObject("ADODB.Parameter.2.5");
      prmByRoyalty.Type = adInteger;
      prmByRoyalty.Size = 3;
      prmByRoyalty.Direction = adParamInput;
      prmByRoyalty.Value = iRoyalty;
      cmdByRoyalty.Parameters.Append(prmByRoyalty);

      cmdByRoyalty.ActiveConnection = cnn;

      RsByRoyalty = cmdByRoyalty.Execute();

      RsAuthor.Open("Authors", cnn);

      var strMessage;      // used to build up each line before writing to document.

      while (!RsByRoyalty.EOF)
      {
         RsAuthor.Filter = "au_id='" + RsByRoyalty.Fields("au_id") + "'";

         // Start a new line.
         strMessage = "<P>";

         // First and last name are in first column.
         strMessage += RsAuthor.Fields("au_fname") + " ";
         strMessage += RsAuthor.Fields("au_lname") + " ";

         // End the line.
         strMessage += "</P>";

         // Write line to document.
         Response.Write(strMessage);

         // Get next record.
         RsByRoyalty.MoveNext;

      }

      // Close table.
      RsByRoyalty.Close;
      RsAuthor.Close
   }
%>

<hr>

<form method="POST" action="ActiveConnectionJS.asp">
  <p align="left">Enter royalty percentage to find (e.g., 40): <input type="text" name="RoyaltyValue" size="5"></p>
  <p align="left"><input type="submit" value="Submit" name="B1"><input type="reset" value="Reset" name="B2"></p>
</form>
&nbsp;

</body>

</html>
<!-- EndActiveConnectionJS -->

See Also

ActiveCommand Property | Command Object | CommandText Property | CommandTimeout Property | CommandType Property | Connection Object | Direction Property | Parameter Object | Record Object | Recordset Object | Size Property


ActualSize and DefinedSize Properties Example (JScript)

This example uses the ActualSize and DefinedSize properties to display the defined size and actual size of a field.

<!-- BeginActualSizeJS -->
<% @LANGUAGE="JScript" %>
<html>

<head>
   <title>ActualSize and DefinedSize Properties Example (JScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body bgcolor="White">

<h1>ADO ActualSize and DefinedSize Properties (JScript)</h1>
<%
   // Open the connection.
   var Connect = "Provider=sqloledb;Data Source=" +
   Request.ServerVariables("SERVER_NAME") + ";" +
   "Initial Catalog=Northwind;User Id=sa;Password=;"

   // Open a recordset on the stores table
   var rsSuppliers = Server.CreateObject("ADODB.Recordset.2.5");

   rsSuppliers.Open("Suppliers", Connect);

   var strMessage;      // used to build up each line before writing to document.

   Response.Write("<table>");
   Response.Write('<tr class="thead2"><th>Field Value</th>');
   Response.Write("<th>Defined Size</th>");
   Response.Write("<th>Actual Size</th></tr>");

   while (!rsSuppliers.EOF)
   {
      // Start a new line.
      strMessage = '<tr class="tbody">';

      // Display the contents of the
      // stor_name field, the field's defined size,
      // and its actual size.
      fldCompanyName = rsSuppliers.Fields("CompanyName");
      strMessage += '<td align="left">' + fldCompanyName.Value + "</td>"
      strMessage += "<td>" + fldCompanyName.DefinedSize + "</td>";
      strMessage += "<td>" + fldCompanyName.ActualSize + "</td>";

      // End the line.
      strMessage += "</tr>";

      // Write line to document.
      Response.Write(strMessage);

      // Get next record.
      rsSuppliers.MoveNext;

   }

   Response.Write("</table>");

   // Close table.
   rsSuppliers.Close();
%>

</body>

</html>
<!-- EndActualSizeJS -->

See Also

ActualSize Property | DefinedSize Property | Field Object


AddNew Method Example (JScript)

This example uses the AddNew method to create a new record with the specified name. Save the file as AddNewJS.asp.

<!-- BeginAddNewJS -->
<% @LANGUAGE="JScript" %>
<!-- Include file for JScript ADO Constants -->
<!--#include File="adojavas.inc"-->

<%
   var EmpID = String(Request.Form("EmpID").Value);
   var fName = String(Request.Form("FirstName").value);
   var mInit = String(Request.Form("MInit").Value);
   var lName = String(Request.Form("LastName").Value);

%>

<html>

<head>
   <title>Add New Method Example (JScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
-->
</style>
</head>

<body>
<h1>AddNew Method Example (JScript)</h1>
<%
   //if (EmpID.length > 0)
   if (Request.Form("Addit") == "AddNew")
   {

      var Connect = "Provider=sqloledb;Data Source=" +
      Request.ServerVariables("SERVER_NAME") + ";" +
      "Initial Catalog=Northwind;User Id=sa;Password=;"

      // Open a recordset on the Employee table using
      // a client-side cursor.
      var RsEmployee = Server.CreateObject("ADODB.Recordset");
      RsEmployee.CursorLocation = adUseClient;
      RsEmployee.Open("Employees", Connect, adOpenKeyset, adLockOptimistic, adCmdTable);

      RsEmployee.AddNew();
      RsEmployee.Fields("FirstName") = fName;
      RsEmployee.Fields("LastName") = lName;
      RsEmployee.Update;

      // Of course, you would normally do error
      // handling here!!
      Response.Write("New record added.")

      // Close table.
      RsEmployee.Close;
   }
%>

<form method="post" action="AddNewJS.asp" id=form1 name=form1>
<table>
<tr>
   <td colspan="2">
Get data from the user.
   </td>
</tr>
<tr>
   <td>
      First Name:
   </td>
   <td>
      <input name="FirstName" maxLength=20>
   </td>
</tr>
<tr>
   <td>
      Last Name:
   </td>
   <td>
      <input name="LastName" size="30" maxLength=30>
   </td>
</tr>
<tr>
   <td align="right">
      <input type="submit" value="Submit" name="Submit">
   </td>
    <TD align="left">
      <INPUT type="reset" value="Reset" name="Reset">
    </TD>
</tr>
</table>
<INPUT type="hidden" value="AddNew" name="Addit">
</form>
</body>
</HTML>
<!-- EndAddNewJS -->

See Also

AddNew Method | Recordset Object


Append and CreateParameter Methods Example (JScript)

This example uses the Append and CreateParameter methods to execute a stored procedure with an input parameter.

<!-- BeginAppendJS -->
<% @LANGUAGE="JScript" %>
<!-- Include file for JScript ADO Constants -->
<!--#include File="adojavas.inc"-->

<%
   var iRoyalty = parseInt(Request.Form("RoyaltyValue"));

%>

<html>

<head>
   <title>Append and CreateParameter Methods Example (JScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
-->
</style>
</head>

<body bgcolor="White">
<h1>Append and CreateParameter Methods Example (JScript)</h1>
<%
   if (iRoyalty > -1)
   {
      // Open the connection.
      var Connect = "Provider=sqloledb;Data Source=" +
      Request.ServerVariables("SERVER_NAME") + ";" +
      "Initial Catalog=Pubs;User Id=sa;Password=;"
      var cnn = Server.CreateObject("ADODB.Connection.2.5");
      cnn.Open(Connect);
      cnn.CursorLocation = adUseClient;

      var cmdByRoyalty = Server.CreateObject("ADODB.Command.2.5");
      var RsByRoyalty = Server.CreateObject("ADODB.Recordset.2.5");
      var RsAuthor = Server.CreateObject("ADODB.Recordset.2.5");

      cmdByRoyalty.CommandText = "byroyalty";
      cmdByRoyalty.CommandType = adCmdStoredProc;

      // Create the new parameter and append to
      // the Command object's parameters collection.
      var prmByRoyalty = cmdByRoyalty.CreateParameter("percentage", adInteger, adParamInput);
      cmdByRoyalty.Parameters.Append(prmByRoyalty);
      prmByRoyalty.Value = iRoyalty;

      cmdByRoyalty.ActiveConnection = cnn;

      // Execute the command.
      RsByRoyalty = cmdByRoyalty.Execute();

      // Display the results.
      RsAuthor.Open("Authors", cnn);

      var strMessage;      // used to build up each line before writing to document.

      while (!RsByRoyalty.EOF)
      {
         RsAuthor.Filter = "au_id='" + RsByRoyalty.Fields("au_id") + "'";

         // Start a new line.
         strMessage = "<P>";

         // First and last name are in first column.
         strMessage += RsAuthor.Fields("au_fname") + " ";
         strMessage += RsAuthor.Fields("au_lname") + " ";

         // End the line.
         strMessage += "</P>";

         // Write line to document.
         Response.Write(strMessage);

         // Get next record.
         RsByRoyalty.MoveNext;

      }

      // Close table.
      RsByRoyalty.Close;
      RsAuthor.Close
   }
%>

<hr>

<form method="POST" action="ActiveConnectionJS.asp" id=form1 name=form1>
  <p align="left">Enter royalty percentage to find (e.g., 40): <input type="text" name="RoyaltyValue" size="5"></p>
  <p align="left"><input type="submit" value="Submit" name="B1"><input type="reset" value="Reset" name="B2"></p>
</form>
&nbsp;

</body>

</html>
<!-- EndAppendJS -->

See Also

Append Method | CreateParameter Method | Field Object | Fields Collection | Parameter Object


CacheSize Property Example (JScript)

This example uses the CacheSize property to show the difference in performance for an operation performed with and without a 30-record cache.

<!-- BeginCacheSizeJS -->
<%@ Language="JScript" %>
<!-- Include file for JScript ADO Constants -->
<!--#include File="adojavas.inc"-->
<HTML>
<HEAD>
<title>CacheSize Property Example (JScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</HEAD>
<BODY>
<h1>CacheSize Property Example (JScript)</h1>
<%
   // Don't forget to change the settings for Data Source,
   // User ID, and Password to match the settings on your server.
   var Connect = "Provider=sqloledb;Data Source=" +
   Request.ServerVariables("SERVER_NAME") + ";" +
   "Initial Catalog=Northwind;User Id=sa;Password=;"

   // Open a recordset on the Employee table using
   // a client-side cursor.
   var rsCustomer = Server.CreateObject("ADODB.Recordset");
   rsCustomer.CursorLocation = adUseClient;
   rsCustomer.Open("Customers", Connect);

   var Now = new Date();
   var sngStart = Now.getTime();

   // Enumerate the recordset 20 times.
   for (var i=1; i<=20; i++)
   {
      rsCustomer.MoveFirst();
      while (!rsCustomer.EOF)
      {
         // Do something with the record.
         var strTemp = new String(rsCustomer.Fields("title_id"));
         rsCustomer.MoveNext();
      }
   }

   Now = new Date();
   var sngEnd = Now.getTime();
   var sngNoCache = sngEnd - sngStart;

   // Cache records in groups of 30.
   rsCustomer.MoveFirst();
   rsCustomer.CacheSize = 30;

   Now = new Date();
   sngStart = Now.getTime();

   // Enumerate the recordset 20 times.
   for (var i=1; i<=20; i++)
   {
      rsCustomer.MoveFirst();
      while (!rsCustomer.EOF)
      {
         // Do something with the record.
         var strTemp = new String(rsCustomer.Fields("title_id"));
         rsCustomer.MoveNext();
      }
   }

   Now = new Date();
   sngEnd = Now.getTime();
   var sngCache = sngEnd - sngStart;
%>

   <table border="2">
      <tr class="thead2">
         <th>No Cache</th>
         <th>30 Record Cache</th>
      </tr>
      <tr class="tbody">
         <td><%=sngNoCache%></td>
         <td><%=sngCache%></td>
      </tr>
   </table>

</BODY>
</HTML>
<!-- EndCacheSizeJS -->

See Also

CacheSize Property | Recordset Object


Execute, Requery, and Clear Methods Example (JScript)

This example demonstrates the Execute method when run from both a Command object and a Connection object. It also uses the Requery method to retrieve current data in a Recordset, and the Clear method to clear the contents of the Errors collection. (The Errors collection is accessed via the Connection object of the ActiveConnection property of the Recordset.) Name the file ExecuteJS.asp.

<!-- BeginExecuteJS -->
<% @LANGUAGE="JScript" %>

<%
   strLastName = new String(Request.Form("AuthorLName"));

   if (strLastName.indexOf("undefined") > -1)
      strLastName = "";
%>

<html>

<head>
<title>Execute, Requery and Clear Methods Example (JScript)</title>
<style>
<!--
BODY {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
-->
</style>
</head>

<body bgcolor="White">
<h1>Execute, Requery and Clear Methods Example (JScript)</h1>
<%
   if (strLastName.length > 0)
   {
      // Open the connection.
      var Connect = "Provider=sqloledb;Data Source=" +
      Request.ServerVariables("SERVER_NAME") + ";" +
      "Initial Catalog=Pubs;User Id=sa;Password=;"

      // Open a recordset on the author table using
      // a command object.
      var cnn = Server.CreateObject("ADODB.Connection");
      var cmdAuthor = Server.CreateObject("ADODB.Command");
      var RsAuthor = Server.CreateObject("ADODB.Recordset");

      cnn.Open(Connect);

      cmdAuthor.CommandText = "SELECT * FROM Authors WHERE au_lname = ?";
      cmdAuthor.Parameters.Append(cmdAuthor.CreateParameter("Last Name", adChar, adParamInput, 20, strLastName));
      cmdAuthor.ActiveConnection = cnn;

      RsAuthor = cmdAuthor.Execute();

      var strMessage;      // used to build up line before writing to document.

      while (!RsAuthor.EOF)
      {
         // Start a new line.
         strMessage = "<P>";

         // First and last name are in first column.
         strMessage += RsAuthor.Fields("au_fname") + " "
         strMessage += RsAuthor.Fields("au_lname") + " ";

         // End the line.
         strMessage += "</P>";

         // Write line to document.
         Response.Write(strMessage);

         // Get next record.
         RsAuthor.MoveNext;

      }

      // Close table.
      RsAuthor.Close;
   }
%>

<hr>

<form method="POST" action="ActiveCommandJS.asp" id=form1 name=form1>
  <p align="left">Enter last name of author to find (e.g., Ringer): <input type="text" name="AuthorLName" size="40"></p>
  <p align="left"><input type="submit" value="Submit" name="B1"><input type="reset" value="Reset" name="B2"></p>
</form>
</body>

</html>
<!-- EndExecuteJS -->

See Also

Clear Method | Command Object | Connection Object | Error Object | Execute Method (ADO Command) | Execute Method (ADO Connection) | Recordset Object | Requery Method


Filter and RecordCount Properties Example (JScript)

This example opens a Recordset on the Companies table of the Northwind database and then uses the Filter property to limit the records visible to those where the CompanyName field starts with the letter D.

<!-- BeginFilterJS -->
<%@  Language=JavaScript %>
<!-- Include file for JScript ADO Constants -->
<!--#include File="adojavas.inc"-->

<html>

<head>
<title>ADO Recordset.Filter Example</title>
<style>
<!--
BODY {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body bgcolor="White">

<h1>ADO Recordset.Filter Example</h1>
<!-- Page text goes here -->
<%
   myConnect = "Provider=sqloledb;Data Source=" +
      Request.ServerVariables("SERVER_NAME") + ";" +
      "Initial Catalog=Northwind;User Id=sa;Password=;";
   mySQL = "select * from Customers;";
   showBlank = " ";
   showNull = "-NULL-";

   var connTemp = Server.CreateObject("ADODB.Connection");
   connTemp.Open(myConnect);
   rsTemp = Server.CreateObject("ADODB.Recordset");
   rsTemp.ActiveConnection = connTemp;
   rsTemp.CursorLocation = adUseClient;
   rsTemp.CursorType = adOpenKeyset;
   rsTemp.LockType = adLockOptimistic;
   rsTemp.Source = mySQL;
   rsTemp.Open();

   rsTemp.MoveFirst();
   rsTemp.Filter = "CompanyName like 'd*'";

   if (rsTemp.RecordCount < 1) {
      Response.Write("No records matched ");
      Response.Write (mySQL + "So cannot make table...");
      connTemp.Close();
      Response.End
   } else {
      Response.Write('<table width="100%" border="2">');
      Response.Write('<tr class="thead2">');
      // Put Headings On The Table for each Field Name
      for (var i=0; i<rsTemp.Fields.Count; i++) {
         var fieldObject = rsTemp.Fields(i);
         Response.Write('<th width="' + Math.floor(100 / rsTemp.Fields.Count) + '%">' + fieldObject.Name + "</th>");
      }
      Response.Write("</tr>");

      while(!rsTemp.EOF) {
         Response.Write('<tr class="tbody">');
         for (var thisField=0; thisField<rsTemp.Fields.Count; thisField++) {
            fieldObject = rsTemp.Fields(thisField);
            strField = fieldObject.Value;
            if (strField == null)
               strField = shownull;
            if (strField == "")
               thisField=showblank;
            Response.Write("<td>" + strField + "</td>")
         }
         rsTemp.MoveNext();
         Response.Write("</tr>");
      }
      Response.Write("</table>");
   }

   rsTemp.Close();
   connTemp.Close();
%>

</body>

</html>
<!-- EndFilterJS -->

See Also

Filter Property | RecordCount Property | Recordset Object


Find Method Example (JScript)

This example uses the Recordset object's Find method to locate and display the companies in the Northwind database whose name begins with the letter G.

<!-- BeginFindJS -->
<%@  Language=JavaScript %>
<!-- Include file for JScript ADO Constants -->
<!--#include File="adojavas.inc"-->

<html>

<head>
<title>ADO Recordset.Find Example</title>
<style>
<!--
BODY {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body bgcolor="white">

<h1>ADO Recordset.Find Example</h1>
   <!-- Page text goes here -->
<%
   myConnect = "Provider=sqloledb;Data Source=" +
      Request.ServerVariables("SERVER_NAME") + ";" +
      "Initial Catalog=Northwind;User Id=sa;Password=;";
   mySQL = "select * from Customers;";
   showblank = " ";
   shownull = "-null-";

   var connTemp = Server.CreateObject("ADODB.Connection");
   connTemp.Open(myConnect);
   var rsTemp = Server.CreateObject("ADODB.Recordset");
   rsTemp.ActiveConnection = connTemp;
   rsTemp.CursorLocation = adUseClient;
   rsTemp.CursorType = adOpenKeyset;
   rsTemp.LockType = adLockOptimistic;
   rsTemp.Source = mySQL;
   rsTemp.Open();

   rsTemp.MoveFirst();
   rsTemp.Find("CompanyName like 'g%'");

   if (rsTemp.EOF) {
      Response.Write("No records matched ");
      Response.Write(mySQL & "So cannot make table...");
      connTemp.Close();
      Response.End();
   } else {
      Response.Write('<table width="100%" border="2">');
      Response.Write('<tr class="thead2">');
      // Put Headings On The Table for each Field Name
      for (thisField = 0; thisField < rsTemp.Fields.Count; thisField++) {
         fieldObject = rsTemp.Fields(thisField);
         Response.Write('<th width="' + Math.floor(100 / rsTemp.Fields.Count) + '%">' + fieldObject.Name + "</th>");
      }
      Response.Write("</tr>");

      while (!rsTemp.EOF) {
         Response.Write('<tr class="tbody">');
         for(thisField=0; thisField<rsTemp.Fields.Count; thisField++) {
            fieldObject = rsTemp.Fields(thisField);
            strField = fieldObject.Value;
            if (strField == null)
               strField = shownull;
            if (strField == "")
               strField = showblank;
            Response.Write("<td>" + strField + "</td>");
         }
         rsTemp.Find("CompanyName like 'g%'", 1, adSearchForward)
         Response.Write("</tr>");
      }
      Response.Write("</table>");
   }

   rsTemp.Close();
   connTemp.Close();
%>

</body>

</html>
<!-- EndFindJS -->

See Also

Find Method | Recordset Object


GetRows Method Example (JScript)

This example uses the GetRows method to retrieve all rows of the Custiomers table from a Recordset and to fill an array with the resulting data. The GetRows method will return fewer than the desired number of rows in two cases: either if EOF has been reached, or if GetRows tried to retrieve a record that was deleted by another user. The function returns False only if the second case occurs.

<!-- BeginGetRowsJS -->
<%@ LANGUAGE="JScript" %>
<!-- Include file for JScript ADO Constants -->
<!--#include File="adojavas.inc"-->

<html>

<head>
<title>ADO Recordset.GetRows Example (JScript)</title>
<style>
<!--
BODY {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body bgcolor="white">

<h1>ADO Recordset.GetRows Example (JScript)</h1>
   <!-- Page text goes here -->
<%
   var Connect = "Provider=sqloledb;Data Source=" +
      Request.ServerVariables("SERVER_NAME") + ";" +
      "Initial Catalog=Northwind;User Id=sa;Password=;";
   var mySQL = "select * from customers;";
   var showblank = " ";
   var shownull = "-null-";

   var connTemp = Server.CreateObject("ADODB.Connection");
   connTemp.Open(Connect);
   var rsTemp = Server.CreateObject("ADODB.Recordset");
   rsTemp.ActiveConnection = connTemp;
   rsTemp.CursorLocation = adUseClient;
   rsTemp.CursorType = adOpenKeyset;
   rsTemp.LockType = adLockOptimistic;
   rsTemp.Open(mySQL);

   rsTemp.MoveFirst();

   if (rsTemp.RecordCount == 0)
   {
      Response.Write("No records matched ");
      Response.Write (mySQL & "So cannot make table...");
      connTemp.Close();
      Response.End();
   } else
   {
      Response.Write('<table width="100%" border="2">');
      Response.Write('<tr class="thead2">');

      //  Headings On The Table for each Field Name
      for (var i=0; i<rsTemp.Fields.Count; i++)
      {
         fieldObject = rsTemp.fields(i);
         Response.Write('<td width="' + Math.floor(100 / rsTemp.Fields.Count) + '%">' + fieldObject.name + "</td>");
      }

      Response.Write("</tr>");

      // JScript doesn't support multi-dimensional arrays
      // so we'll convert the returned array to a single
      // dimensional JScript array and then display the data.
      tempArray = rsTemp.GetRows();
      recArray = tempArray.toArray();

      var col = 1;
      var maxCols = rsTemp.Fields.Count;

      for (var thisField=0; thisField<recArray.length; thisField++)
      {
         if (col == 1)
            Response.Write('<tr class="tbody">');
         if (recArray[thisField] == null)
               recArray[thisField] = shownull;
         if (recArray[thisField] == "")
               recArray[thisField] = showblank;
         Response.Write("<td>" + recArray[thisField] + "</td>");
         col++
         if (col > maxCols)
         {
            Response.Write("</tr>");
            col = 1;
         }
      }
      Response.Write("</table>");
   }

   rsTemp.Close();
   connTemp.Close();
%>

</body>

</html>
<!-- EndGetRowsJS -->

See Also

GetRows Method | Recordset Object


RDS API Reference

RDS API Reference

This section of the ADO documentation contains topics for each RDS object, property, method, and event. For more information, search for a specific topic in the index or refer to the following topics:

  • RDS Objects
  • RDS Properties
  • RDS Methods
  • RDS Events


RDS Objects

DataControl (RDS) | Binds a data query Recordset to one or more controls (for example, a text box, grid control, or combo box) to display the Recordset data on a Web page.
DataFactory (RDSServer) | Implements methods that provide read/write data access to specified data sources for client-side applications.
DataSpace (RDS) | Creates client-side proxies to custom business objects located on the middle tier.


DataControl Object (RDS)

Binds a data query Recordset to one or more controls (for example, a text box, grid control, or combo box) to display the Recordset data on a Web page.

Syntax

<OBJECT CLASSID="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33" ID="DataControl"
   <PARAM NAME="Connect" VALUE="DSN=DSNName;UID=usr;PWD=pw;">
   <PARAM NAME="Server" VALUE="http://awebsrvr">
   <PARAM NAME="SQL" VALUE="QueryText">
</OBJECT>

Remarks

The class ID for the RDS.DataControl object is BD96C556-65A3-11D0-983A-00C04FC29E33.

Note If you get an error that an RDS.DataSpace or RDS.DataControl object doesn't load, make sure you are using the correct class ID. The class IDs for these objects have changed from version 1.0 and 1.1.

For a basic scenario, you need to set only the SQL, Connect, and Server properties of the RDS.DataControl object, which will automatically call the default business object, RDSServer.DataFactory.

All the properties in the RDS.DataControl are optional because custom business objects can replace their functionality.

Note If you query for multiple results, only the first Recordset is returned. If multiple result sets are needed, assign each to its own DataControl. An example of a query for multiple results could be the following:

"Select * from Authors, Select * from Topics"

Adding "DFMode=20;" to your connection string when using the RDS.DataControl object can improve your server's performance when updating data. With this setting, the RDSServer.DataFactory object on the server uses a less resource-intensive mode. However, the following features are not available in this configuration:

  • Using parameterized queries.
  • Getting parameter or column information before calling the Execute method.
  • Setting Transact Updates to True.
  • Getting row status.
  • Calling the Resync method.
  • Refreshing (explicitly or automatically) via the Update Resync property.
  • Setting Command or Recordset properties.
  • Using adCmdTableDirect.

The RDS.DataControl object runs in asynchronous mode by default. If you require synchronous execution for your application, set the ExecuteOptions parameter equal to adcExecSync and the FetchOptions parameter equal to adcFetchUpFront, as shown in the following example.

<OBJECT CLASSID="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
    ID="DataControl"
   <PARAM NAME="Connect" VALUE="DSN=DSNName;UID=usr;PWD=pw;">
   <PARAM NAME="Server" VALUE="http://awebsrvr">
   <PARAM NAME="SQL" VALUE="QueryText">
   <PARAM NAME="ExecuteOptions" VALUE="1">
   <PARAM NAME="FetchOptions" VALUE="1">
</OBJECT>

Use one RDS.DataControl object to link the results of a single query to one or more visual controls. For example, suppose you code a query requesting customer data such as Name, Residence, Place of Birth, Age, and Priority Customer Status. You can use a single RDS.DataControl object to display a customer's Name, Age, and Region in three separate text boxes; Priority Customer Status in a check box; and all the data in a grid control.

Use different RDS.DataControl objects to link the results of multiple queries to different visual controls. For example, suppose you use one query to obtain information about a customer, and a second query to obtain information about merchandise that the customer has purchased. You want to display the results of the first query in three text boxes and one check box, and the results of the second query in a grid control. If you use the default business object (RDSServer.DataFactory), you need to do the following:

  • Add two RDS.DataControl objects to your Web page.
  • Write two queries, one for each SQL property of the two RDS.DataControl objects. One RDS.DataControl object will contain an SQL query requesting customer information; the second will contain a query requesting a list of merchandise the customer has purchased.
  • In each of the bound controls' OBJECT tags, specify the DATAFLD value to set the values for the data you want to display in each visual control.

There is no count restriction on the number of RDS.DataControl objects that you can embed via OBJECT tags on a single Web page.

When you define the RDS.DataControl object on a Web page, use nonzero Height and Width values such as 1 (to avoid the inclusion of extra space).

Remote Data Service client components are already included as part of Internet Explorer 4.0; therefore, you don't need to include a CODEBASE parameter in your RDS.DataControl object tag.

With Internet Explorer 4.0 or later, you can bind to data by using HTML controls and ActiveX® controls only if they are marked as apartment model controls.

Microsoft Visual Basic Users The RDS.DataControl is used only in Web-based applications. A Visual Basic client application has no need for it.

See Also

VBScript Example

Properties, Methods, and Events


Properties, Methods, and Events

RDS API Reference

DataControl Object (RDS) Properties, Methods, and Events

Properties

Connect Property (RDS)

ExecuteOptions Property (RDS)

FetchOptions Property (RDS)

FilterColumn Property (RDS)

FilterCriterion Property (RDS)

FilterValue Property (RDS)

Handler Property (RDS)

InternetTimeout Property (RDS)

ReadyState Property (RDS)

Recordset, SourceRecordset Properties (RDS)

Server Property (RDS)

SortColumn Property (RDS)

SortDirection Property (RDS)

SQL Property (RDS)

URL Property (RDS)

Methods

Cancel Method (RDS)

CancelUpdate Method (RDS)

CreateRecordset Method (RDS)

MoveFirst, MoveLast, MoveNext, and MovePrevious Methods (RDS)

Refresh Method (RDS)

Reset Method (RDS)

SubmitChanges Method (RDS)

Events

None.

See Also

Applies To: DataControl Object (RDS)


DataFactory Object (RDSServer)

This default server-side business object implements methods that provide read/write data access to specified data sources for client-side applications.

Remarks

The RDSServer.DataFactory object is designed as a server-side Automation object that receives client requests. In an Internet implementation, it resides on a Web server and is instantiated by the ADISAPI component. The RDSServer.DataFactory object provides read and write access to specified data sources, but doesn't contain any validation or business rules logic.

If you use a method that is available in both the RDSServer.DataFactory and RDS.DataControl objects, Remote Data Service uses the RDS.DataControl version by default. The default assumes a basic programming scenario, where the RDSServer.DataFactory serves as a generic server-side business object.

If you want your Web application to handle task-specific server-side processing, you can replace the RDSServer.DataFactory with a custom business object.

You can create server-side business objects that call the RDSServer.DataFactory methods, such as Query and CreateRecordset. This is helpful if you want to add functionality to your business objects, but take advantage of existing Remote Data Service technologies.

The class ID for the RDSServer.DataFactory object is 9381D8F5-0288-11D0-9501-00AA00B911A5.

See Also

VBScript Example

Properties, Methods, and Events


Properties, Methods, and Events

RDS API Reference

DataFactory Object (RDSServer) Properties, Methods, and Events

Properties

None.

Methods

ConvertToString Method (RDS)

CreateRecordset Method (RDS)

Query Method (RDS)

SubmitChanges Method (RDS)

Events

None.

See Also

Applies To: DataFactory Object (RDSServer)


DataSpace Object (RDS)

Creates client-side proxies to custom business objects located on the middle tier.

Remarks

Remote Data Service needs business object proxies so that client-side components can communicate with business objects located on the middle tier. Proxies facilitate the packaging, unpackaging, and transport (marshaling) of the application's Recordset data across process or machine boundaries.

Remote Data Service uses the RDS.DataSpace object's CreateObject method to create business object proxies. The business object proxy is dynamically created whenever an instance of its middle-tier business object counterpart is created. Remote Data Service supports the following protocols: HTTP, HTTPS (HTTP Secure Sockets), DCOM, and in-process (client components and the business object reside on the same computer).

Note RDS behaves in a "stateless" manner when the RDS.DataSpace object uses the HTTP or HTTPS protocols. That is, any internal information about a client request is discarded after the server returns a response.

Although the business object appears to exist for the lifetime of the business object proxy, the business object actually exists only until a response is sent to a request. When a request is issued (that is, a method is invoked on the business object), the proxy opens a new connection to the server and the server creates a new instance of the business object. After the business object responds to the request, the server destroys the business object and closes the connection.

This behavior means you cannot pass data from one request to another using a business object property or variable. You must employ some other mechanism, such as a file or a method argument, to persist state data.

The class ID for the RDS.DataSpace object is BD96C556-65A3-11D0-983A-00C04FC29E36.

See Also

VBScript Example

Properties, Methods, and Events


Properties, Methods, and Events

RDS API Reference

DataSpace Object (RDS) Properties, Methods, and Events

Properties

InternetTimeout Property (RDS)

Methods

CreateObject Method (RDS)

Events

None.

See Also

Applies To: DataSpace Object (RDS)


RDS Properties

Connect (RDS) | Indicates the database name from which the query and update operations are run.
ExecuteOptions (RDS) | Indicates whether asynchronous execution is enabled.
FetchOptions (RDS) | Indicates the type of asynchronous fetching.
FilterColumn (RDS) | Indicates the column on which to evaluate the filter criteria.
FilterCriterion (RDS) | Indicates the evaluation operator to use in the filter value.
FilterValue (RDS) | Indicates the value to filter records.
Handler (RDS) | Indicates the name of a server-side customization program (handler) that extends the functionality of the RDSServer.DataFactory, and any parameters used by the handler.
InternetTimeout (RDS) | Indicates the number of milliseconds to wait before a request times out.
ReadyState (RDS) | Indicates the progress of a DataControl object as it fetches data into its Recordset object.
Recordset and SourceRecordset (RDS) | Indicates the Recordset object returned from a custom business object.
Server (RDS) | Indicates the Internet Information Services (IIS) name and communication protocol.
SortColumn (RDS) | Indicates by which column to sort the records.
SortDirection (RDS) | Indicates whether a sort order is ascending or descending.
SQL (RDS) | Indicates the query string used to retrieve the Recordset.
URL (RDS) | Indicates a string that contains a relative or absolute URL.


Connect Property (RDS)

Indicates the database name from which the query and update operations are run.

You can set the Connect property at design time in the RDS.DataControl object's OBJECT tags, or at run time in scripting code (for instance, VBScript).

Syntax

Design time: <PARAM NAME="Connect" VALUE="ConnectionString">
Run time: DataControl.Connect = "ConnectionString"

Parameters

ConnectionString
A valid connection string. For more general information about connection strings, see the ConnectionString property or your provider documentation.

Note Specifying MS Remote as the provider for the RDS.DataControl would create a four-tier scenario. Scenarios greater than three tiers have not been tested and should not be needed.

DataControl
An object variable that represents an RDS.DataControl object.

See Also

VBScript Example

Query Method (RDS) | Refresh Method (RDS) | SubmitChanges Method (RDS)

Applies To: DataControl Object (RDS)


ExecuteOptions Property (RDS)

Indicates whether asynchronous execution is enabled.

Settings and Return Values

Sets or returns one of the following values.

Constant | Description
adcExecSync | Executes the next refresh of the Recordset synchronously.
adcExecAsync | Default. Executes the next refresh of the Recordset asynchronously.

Note Each client-side executable file that uses these constants must provide declarations for them. You can cut and paste the constant declarations that you want from the file Adcvbs.inc, located in the C:\Program Files\Common Files\System\MSADC folder.

Remarks

If ExecuteOptions is set to adcExecAsync, then this asynchronously executes the next Refresh call on the RDS.DataControl object's Recordset.

If you try to call Reset, Refresh, SubmitChanges, CancelUpdate, or Recordset while another asynchronous operation that might change the RDS.DataControl object's Recordset is executing, an error occurs.

If an error occurs during an asynchronous operation, the RDS.DataControl object's ReadyState value changes from adcReadyStateLoaded to adcReadyStateComplete, and the Recordset property value remains Nothing.

See Also

VBScript Example

Cancel Method (RDS)

Applies To: DataControl Object (RDS)


FetchOptions Property (RDS)

Indicates the type of asynchronous fetching.

Setting and Return Values

Sets or returns one of the following values.

Constant | Description
adcFetchUpFront | All the records of the Recordset are fetched before control is returned to the application. The complete Recordset is fetched before the application is allowed to do anything with it.
adcFetchBackground | Control can return to the application as soon as the first batch of records has been fetched. A subsequent read of the Recordset that attempts to access a record not fetched in the first batch will be delayed until the sought record is actually fetched, at which time control returns to the application.
adcFetchAsync | Default. Control returns immediately to the application while records are fetched in the background. If the application attempts to read a record that hasn't yet been fetched, the record closest to the sought record will be read and control will return immediately, indicating that the current end of the Recordset has been reached. For example, a call to MoveLast will move the current record position to the last record actually fetched, even though more records will continue to populate the Recordset.

Note Each client-side executable file that uses these constants must provide declarations for them. You can cut and paste the constant declarations you want from the file Adcvbs.inc, located in the C:\Program Files\Common Files\System\MSADC folder.

Remarks

In a Web application, you will usually want to use adcFetchAsync (the default value), because it provides better performance. In a compiled client application, you will usually want to use adcFetchBackground.

See Also

VBScript Example

Cancel Method (RDS)

Applies To: DataControl Object (RDS)


FilterColumn Property (RDS)

Indicates the column on which to evaluate the filter criteria.

Syntax

DataControl.FilterColumn = String

Parameters

DataControl
An object variable that represents an RDS.DataControl object.
String
A String value that specifies the column on which to evaluate the filter criteria. The filter criteria are specified in the FilterCriterion property.

Remarks

The SortColumn, SortDirection, FilterValue, FilterCriterion, and FilterColumn properties provide sorting and filtering functionality on the client-side cache. The sorting functionality orders records by values from one column. The filtering functionality displays a subset of records based on find criteria, while the full Recordset is maintained in the cache. The Reset method will execute the criteria and replace the current Recordset with an updatable Recordset.

See Also

VBScript Example

FilterCriterion Property (RDS) | FilterValue Property (RDS) | SortColumn Property (RDS) | SortDirection Property (RDS)

Applies To: DataControl Object (RDS)


FilterCriterion Property (RDS)

Indicates the evaluation operator to use in the filter value.

Syntax

DataControl.FilterCriterion = String

Parameters

DataControl
An object variable that represents an RDS.DataControl object.
String
A String value that specifies the evaluation operator of the FilterValue to the records. Can be any one of the following: <, <=, >, >=, =, or <>.

Remarks

The SortColumn, SortDirection, FilterValue, FilterCriterion, and FilterColumn properties provide sorting and filtering functionality on the client-side cache. The sorting functionality orders records by values from one column. The filtering functionality displays a subset of records based on find criteria, while the full Recordset is maintained in the cache. The Reset method will execute the criteria and replace the current Recordset with an updatable Recordset.

The "!=" operator is not valid for FilterCriterion; instead, use "<>".

If both the filter and sort properties are set and you call the Reset method, the rowset is first filtered and then it is sorted. For ascending sorts, the null values are at the top; for descending sorts, null values are at the bottom (ascending is default behavior).

See Also

VBScript Example

FilterColumn Property (RDS) | FilterValue Property (RDS) | SortColumn Property (RDS) | SortDirection Property (RDS)

Applies To: DataControl Object (RDS)


FilterValue Property (RDS)

Indicates the value with which to filter records.

Syntax

DataControl.FilterValue = String

Parameters

DataControl
An object variable that represents an RDS.DataControl object.
String
A String value that represents a data value with which to filter records (for example, 'Programmer' or 125).

Remarks

The SortColumn, SortDirection, FilterValue, FilterCriterion, and FilterColumn properties provide sorting and filtering functionality on the client-side cache. The sorting functionality orders records by values from one column. The filtering functionality displays a subset of records based on find criteria, while the full Recordset is maintained in the cache. The Reset method will execute the criteria and replace the current Recordset with an updatable Recordset.

Null values result in a type mismatch error.

See Also

VBScript Example

FilterColumn Property (RDS) | FilterCriterion Property (RDS) | SortColumn Property (RDS) | SortDirection Property (RDS)

Applies To: DataControl Object (RDS)


Handler Property (RDS)

Indicates the name of a server-side customization program (handler) that extends the functionality of the RDSServer.DataFactory, and any parameters used by the handler.

Syntax

DataControl.Handler = String

Parameters

DataControl
An object variable that represents an RDS.DataControl object.
String
A String value that contains the name of the handler and any parameters, all separated by commas (for example, "handlerName,parm1,parm2,...,parm N").

Remarks

This property supports customization, a functionality that requires setting the CursorLocation property to adUseClient.

The name of the handler and its parameters, if any, are separated by commas (","). Unpredictable behavior will result if a semicolon (";") appears anywhere within String. You can write your own handler, provided it supports the IDataFactoryHandler interface.

The name of the default handler is MSDFMAP.Handler, and its default parameter is a customization file named MSDFMAP.INI. Use this property to invoke alternate customization files created by your server administrator.

The alternative to setting the Handler property is to specify a handler and parameters in the ConnectionString property; that is, "Handler=handlerName,parm1,parm2,...;".

See Also

Visual Basic Example

DataFactory Customization | DataFactory Object (RDSServer)

Applies To: DataControl Object (RDS)


InternetTimeout Property (RDS)

Indicates the number of milliseconds to wait before a request times out.

Settings and Return Values

Sets or returns a Long value that represents the number of milliseconds before a request will time out.

Remarks

This property applies only to requests sent with the HTTP or HTTPS protocols.

Requests in a three-tier environment can take several minutes to execute. Use this property to specify additional time for long-running requests.

See Also

Visual Basic Example | VC++ Example | VJ++ Example

Applies To: DataControl Object (RDS) | DataSpace Object (RDS)


ReadyState Property (RDS)

Indicates the progress of a DataControl object as it retrieves data into its Recordset object.

Settings and Return Values

Sets or returns one of the following values.

Value | Description
adcReadyStateLoaded | The current query is still executing and no rows have been fetched. The DataControl object's Recordset is not available for use.
adcReadyStateInteractive | An initial set of rows retrieved by the current query has been stored in the DataControl object's Recordset and are available for use. The remaining rows are still being fetched.
adcReadyStateComplete | All rows retrieved by the current query have been stored in the DataControl object's Recordset and are available for use. This state will also exist if an operation aborted due to an error, or if the Recordset object is not initialized.

Note Each client-side executable file that uses these constants must provide declarations for them. You can cut and paste the constant declarations you want from the file Adcvbs.inc, located in the C:\Program Files\Common Files\System\MSADC folder.

Remarks

Use the onReadyStateChange event to monitor changes in the ReadyState property during an asynchronous query operation. This is more efficient than periodically checking the value of the property.

If an error occurs during an asynchronous operation, the ReadyState property changes to adcReadyStateComplete, the State property changes from adStateExecuting to adStateClosed, and the Recordset object Value property remains Nothing.

See Also

VBScript Example

Cancel Method (RDS) | ExecuteOptions Property (RDS)

Applies To: DataControl Object (RDS)


Recordset, SourceRecordset Properties (RDS)

Indicates the Recordset object returned from a custom business object.

Syntax

DataControl.SourceRecordset = Recordset
Recordset = DataControl.Recordset

Parameters

DataControl
An object variable that represents an RDS.DataControl object.
Recordset
An object variable that represents a Recordset object.

Remarks

You can set the SourceRecordset property to a Recordset returned from a custom business object.

These properties allow an application to handle the binding process by means of a custom process. They receive a rowset wrapped in a Recordset so that you can interact directly with the Recordset, performing actions such as setting properties or iterating through the Recordset.

You can set the SourceRecordset property or read the Recordset property at run time in scripting code.

SourceRecordset is a write-only property, in contrast to the Recordset property, which is a read-only property.

See Also

VBScript Example

CreateRecordset Method (RDS) | Query Method (RDS)

Applies To: DataControl Object (RDS)


Server Property (RDS)

Indicates the Internet Information Services (IIS) name and communication protocol.

You can set the Server property at design time in the RDS.DataControl object's OBJECT tags, or at run time in scripting code.

Syntax

Protocol Design-time syntax
HTTP
<PARAM NAME="Server" VALUE="http://awebsrvr:port">
HTTPS
<PARAM NAME="Server" VALUE="https://awebsrvr:port">
DCOM
<PARAM NAME="Server" VALUE="computername">
In-process
<PARAM NAME="Server" VALUE="">
Protocol Run-time syntax
HTTP
DataControl.Server="http://awebsrvr:port"
HTTPS
DataControl.Server="https://awebsrvr:port"
DCOM
DataControl.Server="computername"
In-process
DataControl.Server=""

Parameters

awebsrvr or computername
A String value that contains an Internet or intranet path, or computer name, if the server is on a remote computer; or, an empty string if the server is on the local computer.
port
Optional. A port that is used to connect to an IIS server. The port number is set in Internet Explorer (on the View menu, click Options, and then select the Connection tab) or in IIS.
DataControl
An object variable that represents an RDS.DataControl object.

Remarks

The server is the location where the RDS.DataControl request (that is, a query or update) is processed. By default, all requests are processed by the RDSServer.DataFactory object, MSDFMAP.Handler component, and MSDFMAP.INI file on the specified server. Remember that when changing servers to reconcile settings in the old and new MSDFMAP.INI files. Incompatibilities may cause requests that succeed on one server to fail on another. If the Server property is set to "", these objects will be used on the local machine.

See Also

VBScript Example

Connect Property (RDS) | SQL Property (RDS) | SubmitChanges Method (RDS)

Applies To: DataControl Object (RDS)


SortColumn Property (RDS)

Indicates by which column to sort the records.

Syntax

DataControl.SortColumn = String

Parameters

DataControl
An object variable that represents an RDS.DataControl object.
String
A String value that represents the name or alias of the column by which to sort the records.

Remarks

The SortColumn, SortDirection, FilterValue, FilterCriterion, and FilterColumn properties provide sorting and filtering functionality on the client-side cache. The sorting functionality orders records by values from one column. The filtering functionality displays a subset of records based on find criteria, while the full Recordset is maintained in the cache. The Reset method will execute the criteria and replace the current Recordset with an updatable Recordset.

To sort on a Recordset, you must first save any pending changes. If you are using the RDS.DataControl, you can use the SubmitChanges method. For example, if your RDS.DataControl is named ADC1, your code would be ADC1.SubmitChanges. If you are using an ADO Recordset, you can use its UpdateBatch method. Using UpdateBatch is the recommended method for Recordset objects created with the CreateRecordset method. For example, your code could be myRS.UpdateBatch or ADC1.Recordset.UpdateBatch.

See Also

VBScript Example

Sort Property | SortDirection Property (RDS)

Applies To: DataControl Object (RDS)


SortDirection Property (RDS)

Indicates whether a sort order is ascending or descending.

Syntax

DataControl.SortDirection = value

Parameters

DataControl
An object variable that represents an RDS.DataControl object.
Value
A Boolean value that, when set to True, indicates the sort direction is ascending. False indicates descending order.

Remarks

The SortColumn, SortDirection, FilterValue, FilterCriterion, and FilterColumn properties provide sorting and filtering functionality on the client-side cache. The sorting functionality orders records by values from one column. The filtering functionality displays a subset of records based on find criteria, while the full Recordset is maintained in the cache. The Reset method will execute the criteria and replace the current Recordset with an updatable Recordset.

See Also

VBScript Example

Sort Property | SortColumn Property (RDS)

Applies To: DataControl Object (RDS)


SQL Property (RDS)

Indicates the query string used to retrieve the Recordset.

You can set the SQL property at design time in the RDS.DataControl object's OBJECT tags, or at run time in scripting code.

Syntax

Design time: <PARAM NAME="SQL" VALUE="QueryString">
Run time: DataControl.SQL = "QueryString"

Parameters

QueryString
A String value that contains a valid SQL data request.
DataControl
An object variable that represents an RDS.DataControl object.

Remarks

In general, this is an SQL statement (using the dialect of the database server), such as "Select * from NewTitles". To ensure that records are matched and updated accurately, an updatable query must contain a field other than a Long Binary field or a computed field.

The SQL property is optional if a custom server-side business object retrieves the data for the client.

See Also

VBScript Example

Connect Property (RDS) | Query Method (RDS) | Refresh Method (RDS) | SubmitChanges Method (RDS)

Applies To: DataControl Object (RDS)


URL Property (RDS)

Indicates a string that contains a relative or absolute URL.

You can set the URL property at design time in the DataControl object's OBJECT tag, or at run time in scripting code.

Syntax

Design time: <PARAM NAME="URL" VALUE="Server">
Run time: DataControl.URL="Server"

Parameters

Server
A String value that contains a valid URL.
DataControl
An object variable that represents a DataControl object.

Remarks

Typically, the URL identifies an Active Server Page (.asp) file that can produce and return a Recordset. Therefore, the user can obtain a Recordset without having to invoke the server-side DataFactory object, or program a custom business object.

If the URL property has been set, SubmitChanges will submit changes to the location specified by the URL.

See Also

VBScript Example

Applies To: DataControl Object (RDS)


RDS Methods

Cancel (RDS) | Cancels execution of a pending, asynchronous method call.
CancelUpdate (RDS) | Cancels any changes made to the current or new row of a Recordset object.
ConvertToString (RDS) | Converts a Recordset to a MIME string that represents the recordset data.
CreateObject (RDS) | Creates the proxy for the target business object and returns a pointer to it.
CreateRecordset (RDS) | Creates an empty, disconnected Recordset.
MoveFirst, MoveLast, MoveNext, MovePrevious (RDS) | Moves to the first, last, next, or previous record in a specified Recordset object.
Query (RDS) | Uses a valid SQL query string to return a Recordset.
Refresh (RDS) | Requeries the data source specified in the Connect property and updates the query results.
Reset (RDS) | Executes the sort or filter on a client-side Recordset, based on the specified sort and filter properties.
SubmitChanges (RDS) | Submits pending changes of the locally cached and updatable Recordset to the data source specified in the Connect property.


Cancel Method (RDS)

Cancels execution of a pending, asynchronous method call.

Syntax

RDS.DataControl.Cancel

Remarks

When you call Cancel, ReadyState is automatically set to adcReadyStateLoaded, and the Recordset will be empty.

See Also

VBScript Example

Cancel Method | CancelBatch Method | CancelUpdate Method | CancelUpdate Method (RDS) | ExecuteOptions Property (RDS)

Applies To: DataControl Object (RDS)


CancelUpdate Method (RDS)

Cancels any changes made to the current or new row of a Recordset object.

Syntax

DataControl.CancelUpdate

Parameters

DataControl
An object variable that represents an RDS.DataControl object.

Remarks

The Cursor Service for OLE DB keeps both a copy of the original values and a cache of changes. When you call CancelUpdate, the cache of changes is reset to empty, and any bound controls are refreshed with the original data.

See Also

VBScript Example

Address Book Command Buttons | Cancel Method | Cancel Method (RDS) | CancelBatch Method | CancelUpdate Method | Refresh Method (RDS) | SubmitChanges Method (RDS)

Applies To: DataControl Object (RDS)


ConvertToString Method (RDS)

Converts a Recordset to a MIME string that represents the recordset data.

Syntax

DataFactory.ConvertToString(Recordset)

Parameters

DataFactory
An object variable that represents an RDSServer.DataFactory object.
Recordset
An object variable that represents a Recordset object.

Remarks

With .asp files, use ConvertToString to embed the Recordset in an HTML page generated on the server to transport it to a client computer.

ConvertToString first loads the Recordset into the Cursor Service tables, and then generates a stream in MIME format.

On the client, Remote Data Service can convert the MIME string back into a fully functioning Recordset. It works well for handling fewer than 400 rows of data with no more than 1024 bytes width per row. You shouldn't use it for streaming BLOB data and large result sets over HTTP. No wire compression is performed on the string, so very large data sets will take considerable time to transport over HTTP when compared to the wire-optimized tablegram format defined and deployed by Remote Data Service as its native transport protocol format.

Note If you are using Active Server Pages to embed the resulting MIME string in a client HTML page, be aware that versions of VBScript earlier than version 2.0 limit the string's size to 32K. If this limit is exceeded, an error is returned. Keep the query scope relatively small when using MIME embedding via .asp files. To fix this, download the latest version of VBScript from the Microsoft Windows Script Technologies Web site.

See Also

Visual Basic Example | VBScript Example

Applies To: DataFactory Object (RDSServer)


CreateObject Method (RDS)

Creates the proxy for the target business object and returns a pointer to it. The proxy packages and marshals data to the server-side stub for communications with the business object to send requests and data over the Internet. For in-process component objects, no proxies are used, just a pointer to the object is provided.

Syntax

Remote Data Service supports the following protocols: HTTP, HTTPS (HTTP over Secure Socket Layer), DCOM, and in-process.

Protocol Syntax
HTTP
Set object = DataSpace.CreateObject("ProgId", "http://awebsrvr")
HTTPS
Set object = DataSpace.CreateObject("ProgId", "https://awebsrvr")
DCOM
Set object = DataSpace.CreateObject("ProgId", "computername")
In-process
Set object = DataSpace.CreateObject("ProgId", "")

Parameters

Object
An object variable that evaluates to an object that is the type specified in ProgID.
DataSpace
An object variable that represents an RDS.DataSpace object used to create an instance of the new object.
ProgID
A String value that contains the programmatic identifier specifying a server-side business object that implements your application's business rules.
awebsrvr or computername
A String value that represents a URL identifying the Internet Information Services (IIS) Web server where an instance of the server business object is created.

Remarks

The HTTP protocol is the standard Web protocol; HTTPS is a secure Web protocol. Use the DCOM protocol when running a local-area network without HTTP. The in-process protocol is a local dynamic-link library (DLL); it does not use a network.

See Also

DataFactory Object, Query Method, and CreateObject Method Example (VBScript) | DataSpace Object and CreateObject Method Example (VBScript)

CreateRecordset Method (RDS)

Applies To: DataSpace Object (RDS)


CreateRecordset Method (RDS)

Creates an empty, disconnected Recordset.

Syntax

object.CreateRecordset(ColumnInfos)

Parameters

Object
An object variable that represents an RDSServer.DataFactory or RDS.DataControl object.
ColumnsInfos
A Variant array of attributes that defines each column in the Recordset created. Each column definition contains an array of four required attributes.
Attribute | Description
Name | Name of the column header.
Type | Integer of the data type.
Size | Integer of the width in characters, regardless of data type.
Nullability | Boolean value.

The set of column arrays is then grouped into an array, which defines the Recordset.

Remarks

The server-side business object can populate the resulting Recordset with data from a non-OLE DB data provider, such as an operating system file containing stock quotes.

The following table lists the DataTypeEnum values supported by the CreateRecordset method. The number listed is the reference number used to define fields.

Each of the data types is either fixed length or variable length. Fixed-length types should be defined with a size of –1, because the size is predetermined and a size definition is still required. Variable-length data types allow a size from 1 to 32767.

For some of the variable data types, the type may be coerced to the type noted in the Substitution column. You won't see the substitutions until after the Recordset is created and filled. Then you can check for the actual data type, if necessary.

Length | Constant | Number | Substitution
Fixed | adTinyInt | 16 |
Fixed | adSmallInt | 2 |
Fixed | adInteger | 3 |
Fixed | adBigInt | 20 |
Fixed | adUnsignedTinyInt | 17 |
Fixed | adUnsignedSmallInt | 18 |
Fixed | adUnsignedInt | 19 |
Fixed | adUnsignedBigInt | 21 |
Fixed | adSingle | 4 |
Fixed | adDouble | 5 |
Fixed | adCurrency | 6 |
Fixed | adDecimal | 14 |
Fixed | adNumeric | 131 |
Fixed | adBoolean | 11 |
Fixed | adError | 10 |
Fixed | adGuid | 72 |
Fixed | adDate | 7 |
Fixed | adDBDate | 133 |
Fixed | adDBTime | 134 |
Fixed | adDBTimestamp | 135 | 7
Variable | adBSTR | 8 | 130
Variable | adChar | 129 | 200
Variable | adVarChar | 200 |
Variable | adLongVarChar | 201 | 200
Variable | adWChar | 130 |
Variable | adVarWChar | 202 | 130
Variable | adLongVarWChar | 203 | 130
Variable | adBinary | 128 |
Variable | adVarBinary | 204 |
Variable | adLongVarBinary | 205 | 204

See Also

Visual Basic Example | VBScript Example

CreateObject Method (RDS)

Applies To: DataControl Object (RDS) | DataFactory Object (RDSServer)


MoveFirst, MoveLast, MoveNext, and MovePrevious Methods (RDS)

Moves to the first, last, next, or previous record in a specified Recordset object.

Syntax

DataControl.Recordset.{MoveFirst | MoveLast | MoveNext | MovePrevious}

Parameters

DataControl
An object variable that represents an RDS.DataControl object.

Remarks

You can use the Move methods with the RDS.DataControl object to navigate through the data records in the data-bound controls on a Web page. For example, suppose you display a Recordset in a grid by binding to an RDS.DataControl object. You can then include First, Last, Next, and Previous buttons that users can click to move to the first, last, next, or previous record in the displayed Recordset. You do this by calling the MoveFirst, MoveLast, MoveNext, and MovePrevious methods of the RDS.DataControl object in the onClick procedures for the First, Last, Next, and Previous buttons, respectively. The Address Book example shows how to do this.

See Also

Move Method | MoveFirst, MoveLast, MoveNext, and MovePrevious Methods | MoveRecord Method

Applies To: DataControl Object (RDS)


Query Method (RDS)

Uses a valid SQL query string to return a Recordset.

Syntax

Set Recordset = DataFactory.Query(Connection, Query)

Parameters

Recordset
An object variable that represents a Recordset object.
DataFactory
An object variable that represents an RDSServer.DataFactory object.
Connection
A String value that contains the server connection information. This is similar to the Connect property.
Query
A String that contains the SQL query.

Remarks

The query should use the SQL dialect of the database server. A result status is returned if there is an error with the query that was executed. The Query method doesn't perform any syntax checking on the Query string.

See Also

VBScript Example

Applies To: DataFactory Object (RDSServer)


Refresh Method (RDS)

Requeries the data source specified in the Connect property and updates the query results.

Syntax

DataControl.Refresh

Parameters

DataControl
An object variable that represents an RDS.DataControl object.

Remarks

You must set the Connect, Server, and SQL properties before you use the Refresh method. All data-bound controls on the form associated with an RDS.DataControl object will reflect the new set of records. Any pre-existing Recordset object is released, and any unsaved changes are discarded. The Refresh method automatically makes the first record the current record.

It's a good idea to call the Refresh method periodically when you work with data. If you retrieve data, and then leave it on your client machine for a while, it is likely to become out of date. It's possible that any changes you make will fail, because someone else might have changed the record and submitted changes before you.

See Also

Visual Basic Example | VBScript Example

Address Book Command Buttons | CancelUpdate Method (RDS) | Refresh Method | SubmitChanges Method (RDS)

Applies To: DataControl Object (RDS)


Reset Method (RDS)

Executes the sort or filter on a client-side Recordset based on the specified sort and filter properties.

Syntax

DataControl.Reset(value)

Parameters

DataControl
An object variable that represents an RDS.DataControl object.
value
Optional. A Boolean value that is True (default) if you want to filter on the current "filtered" rowset. False indicates that you filter on the original rowset, removing any previous filter options.

Remarks

The SortColumn, SortDirection, FilterValue, FilterCriterion, and FilterColumn properties provide sorting and filtering functionality on the client-side cache. The sorting functionality orders records by values from one column. The filtering functionality displays a subset of records based on a find criteria, while the full Recordset is maintained in the cache. The Reset method will execute the criteria and replace the current Recordset with an updatable Recordset.

If there are changes to the original data that haven't yet been submitted, the Reset method will fail. First, use the SubmitChanges method to save any changes in a read/write Recordset, and then use the Reset method to sort or filter the records.

If you want to perform more than one filter on your rowset, you can use the optional Boolean argument with the Reset method. The following example shows how to do this:

ADC.SQL = "Select au_lname from authors"
ADC.Refresh    ' Get the new rowset.

ADC.FilterColumn = "au_lname"
ADC.FilterCriterion = "<"
ADC.FilterValue = "'M'"
ADC.Reset         ' Rowset now has all Last Names < "M".

ADC.FilterCriterion = ">"
ADC.FilterValue = "'F'"
' Passing True is not necessary, because it is the
' default filter on the current "filtered" rowset.
ADC.Reset(TRUE)     ' Rowset now has all Last
                    ' Names < "M" and > "F".

ADC.FilterCriterion = ">"
ADC.FilterValue = "'T'"
' Filter on the original rowset, throwing out the
' previous filter options.
ADC.Reset(FALSE)   ' Rowset now has all Last Names > "T".

See Also

VBScript Example

SubmitChanges Method (RDS)

Applies To: DataControl Object (RDS)


SubmitChanges Method (RDS)

Submits pending changes of the locally cached and updatable Recordset to the data source specified in the Connect property or the URL property.

Syntax

DataControl.SubmitChanges
DataFactory.SubmitChanges Connection, Recordset

Parameters

DataControl
An object variable that represents an RDS.DataControl object.
DataFactory
An object variable that represents an RDSServer.DataFactory object.
Connection
A String value that represents the connection created with the RDS.DataControl object's Connect property.
Recordset
An object variable that represents a Recordset object.

Remarks

The Connect, Server, and SQL properties must be set before you can use the SubmitChanges method with the RDS.DataControl object.

If you call the CancelUpdate method after you have called SubmitChanges for the same Recordset object, the CancelUpdate call fails because the changes have already been committed.

Only the changed records are sent for modification, and either all of the changes succeed or all of them fail together.

You can use SubmitChanges only with the default RDSServer.DataFactory object. Custom business objects can't use this method.

If the URL property has been set, SubmitChanges will submit changes to the location specified by the URL.

See Also

VBScript Example

Address Book Command Buttons | CancelUpdate Method (RDS) | Refresh Method (RDS)

Applies To: DataControl Object (RDS) | DataFactory Object (RDSServer)


RDS Events

onError (RDS) | Called whenever an error occurs during an operation.
onReadyStateChange (RDS) | Called whenever the value of the ReadyState property changes.


onError Event (RDS)

The onError event is called whenever an error occurs during an operation.

Syntax

onError SCode, Description, Source, CancelDisplay

Parameters

SCode
An integer that indicates the status code of the error.
Description
A String that indicates a description of the error.
Source
A String that indicates the query or command that caused the error.
CancelDisplay
A Boolean value, which if set to True, that prevents the error from being displayed in a dialog box.

See Also

Visual C++ Example

ADO Event Handler Summary

Applies To: DataControl Object (RDS)


onReadyStateChange Event (RDS)

The onReadyStateChange event is called whenever the value of the ReadyState property changes.

Syntax

onReadyStateChange

Parameters

None.

Remarks

The ReadyState property reflects the progress of an RDS.DataControl object as it asynchronously retrieves data into its Recordset object. Use the onReadyStateChange event to monitor changes in the ReadyState property whenever they occur. This is more efficient than periodically checking the property's value.

See Also

Visual C++ Example

ADO Event Handler Summary

Applies To: DataControl Object (RDS)


RDS Code Examples

Use the following code examples to learn how to use the RDS objects, methods, properties, and events.

Note Paste the entire code example into your code editor. The example may not run correctly if partial examples are used or if paragraph formatting is lost.

  • RDS Code Examples in Microsoft Visual Basic
  • RDS Code Examples in Microsoft Visual Basic Scripting Edition
  • RDS Code Examples in Microsoft Visual C++
  • RDS Code Examples in Microsoft Visual J++


RDS Code Examples in Microsoft Visual Basic

Use the following code examples to learn how to use RDS properties when writing in Visual Basic.

Note Paste the entire code example, from Sub to End Sub, into your code editor. The example may not run correctly if partial examples are used or if paragraph formatting is lost.

Properties

  • Handler Property Example
  • InternetTimeout Property Example


Handler Property Example (VB)

This example demonstrates the RDS DataControl object Handler property. (See DataFactory Customization for more details.)

Assume that the following sections in the parameter file, Msdfmap.ini, are located on the server:

[connect AuthorDataBase]
Access=ReadWrite
Connect="DSN=Pubs"
[sql AuthorById]
SQL="SELECT * FROM Authors WHERE au_id = ?"

Your code looks like the following. The command assigned to the SQL property will match the AuthorById identifier and will retrieve a row for author Michael O'Leary. The DataControl object Recordset property is assigned to a disconnected Recordset object purely as a coding convenience.

'BeginHandlerVB
Public Sub HandlerX()
Dim dc As New DataControl
Dim rst As ADODB.Recordset

dc.Handler = "MSDFMAP.Handler"
dc.ExecuteOptions = 1
dc.FetchOptions = 1
dc.Server = "http://YourServer"
dc.Connect = "Data Source=AuthorDataBase"
dc.SQL = "AuthorById(267-41-2394)"
dc.Refresh                  'Retrieve the record
Set rst = dc.Recordset      'Use another Recordset as a convenience
Debug.Print "Author is '" & rst!au_fname & " " & rst!au_lname & "'"

End Sub
'EndHandlerVB

See Also

DataControl Object (RDS) | Handler Property (RDS)


InternetTimeout Property Example (VB)

This example demonstrates the InternetTimeout property, which exists on the DataControl and DataSpace objects. This example uses the DataControl object and sets the timeout to 20 seconds.

'BeginInternetTimeoutVB
Public Sub InternetTimeoutX()
Dim dc As New DataControl
Dim rst As ADODB.Recordset

dc.Server = "http://yourServer"
dc.ExecuteOptions = 1
dc.FetchOptions = 1
dc.Connect = "DSN=Pubs"
dc.SQL = "SELECT * FROM Authors"
dc.InternetTimeout = 20000  'Wait at least 20 seconds.
dc.Refresh
Set rst = dc.Recordset      'Use another Recordset as a convenience
While Not rst.EOF
   Debug.Print rst!au_fname & " " & rst!au_lname
   rst.MoveNext
Wend

rst.Close
End Sub
'EndInternetTimeoutVB

See Also

DataControl Object (RDS) | DataSpace Object (RDS) | InternetTimeout Property (RDS)


RDS Code Examples in Microsoft Visual Basic Scripting Edition

Use the following code examples to learn how to use the RDS objects, methods, and properties when writing in Microsoft Visual Basic Scripting Edition (VBScript).

Note Paste the entire code example into your code editor. The example may not run correctly if partial examples are used or if paragraph formatting is lost.

Objects

  • DataControl Object Example
  • DataSpace Object Example
  • DataFactory Object Example

Methods

  • Cancel Method Example
  • CancelUpdate Method Example
  • ConvertToString Method Example
  • CreateObject Method
  • CreateRecordset Method Example
  • Query Method Example
  • Refresh Method Example
  • Reset Method Example
  • SubmitChanges Method Example

Properties

  • Connect Property Example
  • ExecuteOptions and FetchOptions Properties Example
  • FilterColumn, FilterCriterion, FilterValue, SortColumn, and SortDirection Properties Example
  • ReadyState Property Example
  • Recordset and SourceRecordset Properties Example
  • Server Property Example
  • SQL Property Example
  • URL Property Example


Cancel Method Example (VBScript)

The following example shows how to read the Cancel method at run time.

<!-- BeginCancelVBS -->
<Script Language="VBScript">
<!--
Sub cmdCancelAsync
   ' Terminates currently running AsyncExecute,
   ' ReadyState property set to adcReadyStateLoaded,
   ' Recordset set to Nothing
  ADC.Cancel
End Sub
-->
</Script>
<!-- EndCancelVBS -->

See Also

Cancel Method


CancelUpdate Method Example (VBScript)

To test this example, cut and paste this code between the <Body> and </Body> tags in a normal HTML document and name it CancelUpdateVBS.asp. ASP script will identify your internet server. You will need to edit the name of the server to reflect your own setup. Simply change the value in the connect string from MyServer to the name of your SQL Server installation.

<!-- BeginCancelUpdateVBS -->
<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
<TITLE></TITLE>
</HEAD>
<BODY>

<Center>
<H2>RDS API Code Examples</H2>
<HR>
<H3>Remote Data Service SubmitChanges and CancelUpdate Methods</H3>

<!-- RDS.DataControl with parameters set at design time -->
<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
   ID=RDS HEIGHT=1 WIDTH=1>
   <PARAM NAME="SQL" VALUE="Select * from Employees">
   <PARAM NAME="SERVER" VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>">
   <PARAM NAME="CONNECT" VALUE="Provider=SQLOLEDB;Data Source=MyServer;User Id=sa;Password=;Initial Catalog=Northwind">
</OBJECT>

<TABLE DATASRC="#RDS">
<THEAD>
<TR ID="ColHeaders">
   <TH>ID</TH>
   <TH>FName</TH>
   <TH>LName</TH>
   <TH>Title</TH>
   <TH>Hire Date</TH>
   <TH>Birth Date</TH>
   <TH>Extension</TH>
   <TH>Home Phone</TH>
</TR>
</THEAD>

<TBODY>
<TR>
   <TD> <INPUT DATAFLD="EmployeeID" size=4> </TD>
   <TD> <INPUT DATAFLD="FirstName" size=10> </TD>
   <TD> <INPUT DATAFLD="LastName" size=10> </TD>
   <TD> <INPUT DATAFLD="Title" size=20> </TD>
   <TD> <INPUT DATAFLD="HireDate" size=10> </TD>
   <TD> <INPUT DATAFLD="BirthDate" size=10> </TD>
   <TD> <INPUT DATAFLD="Extension" size=10> </TD>
   <TD> <INPUT DATAFLD="HomePhone" size=8> </TD>
</TR>
</TBODY>
</TABLE>

<HR>
<INPUT TYPE=BUTTON NAME="SubmitChange" VALUE="Submit Changes"><INPUT TYPE=BUTTON NAME="CancelChange" VALUE="Cancel Update"><BR>
<H4>Add a new person or alter a current entry on the grid. Move off that Row. <BR>
Submit the Changes to your DBMS or cancel the updates. </H4>
</Center>
<Script Language="VBScript">
<!--
' Set parameters of RDS.DataControl at Run Time
Sub SubmitChange_OnClick
   RDS.SubmitChanges
   RDS.Refresh
End Sub

Sub CancelUpdate_OnClick
   RDS.CancelUpdate
End Sub
-->
</Script>

</BODY>
</HTML>
<!-- EndCancelUpdateVBS -->

See Also

CancelUpdate Method


Connect Property Example (VBScript)

This code shows how to set the Connect property at design time:

<OBJECT CLASSID="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33" ID="ADC1">
.
   <PARAM NAME="SQL" VALUE="Select * from Sales">
   <PARAM NAME="CONNECT" VALUE="Provider=SQLOLEDB;User Id=sa;Password=;Initial Catalog=Pubs">
   <PARAM NAME="Server" VALUE="http://MyWebServer">
.
</OBJECT>

The following example shows how to set the Connect property at run time in VBScript code.

To test this example, cut and paste this code between the <Body> and </Body> tags in a normal HTML document and name it ConnectVBS.asp. ASP script will identify your server.

<!-- BeginConnectVBS -->
<%@ Language=VBScript %>
<HTML>
<HEAD>
<title>ADO Connect Property</title>
<STYLE>
<!--
BODY {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }

.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</STYLE>
</HEAD>

<BODY>
<h1>ADO Connect Property (RDS)</h1>

<HR>
<H3>Set Connect Property at Run Time</H3>

<!-- RDS.DataControl with no parameters set at design time -->
<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
   ID=RDS HEIGHT=1 WIDTH=1>
</OBJECT>

<TABLE DATASRC=#RDS>
<TBODY>
  <TR class="tbody">
    <TD><SPAN DATAFLD="FirstName"></SPAN></TD>
    <TD><SPAN DATAFLD="LastName"></SPAN></TD>
  </TR>
</TBODY>
</TABLE>

<FORM Name=Form>
SERVER: <Input Size=70 Name="txtServer" Value="http://<%=Request.ServerVariables("SERVER_NAME")%>"><BR>
DATA SOURCE: <Input Size=70 Name="txtDataSource" Value="http://<%=Request.ServerVariables("SERVER_NAME")%>"><BR>
CONNECT: <Input Size=70 Name="txtConnect"><BR>
SQL: <Input Size=70 Name="txtSQL" Value="Select FirstName, LastName from Employees">
<BR>
<INPUT TYPE=BUTTON NAME="Run" VALUE="Run">
<BR>
</FORM>
To Fill Grid enter Connect String in middle text box, try<BR>
<B>Provider=SQLOLEDB;User Id=sa;Password=;Initial Catalog=Northwind</B>

<Script Language="VBScript">
<!--
' Set parameters of RDS.DataControl at Run Time
Sub Run_OnClick
   RDS.Server = Form.txtServer.Value
   RDS.SQL = Form.txtSQL.Value
   RDS.Connect = "Data Source=" & _
      txtDataSource & ";" & _
      Form.txtConnect.Value
   RDS.Refresh
End Sub
-->
</Script>

</BODY>
</HTML>
<!-- EndConnectVBS -->

See Also

Connect Property (RDS)


ConvertToString Method Example (VBScript)

The following example shows how to convert a Recordset into a MIME-encoded string using the RDSServer.DataFactory ConvertToString method. It then shows how the string can be converted back into a Recordset.

<!-- BeginConvertToStringVBS -->
<HTML>
<HEAD><TITLE>ConvertToString Example</TITLE><HEAD>
<BODY>

<SCRIPT LANGUAGE=VBSCRIPT>
Sub ConvertToStringX()
    Dim objRs, objDF, strServer, vString
    Const adcExecSync = 1
    Const adcFetchUpFront = 1

    ' Replace value below with your server name to use without ASP.
    strServer = "http://myserver"

    Set objDF = RDS1.CreateObject("RDSServer.DataFactory", strServer)
    Set objRs = objDF.Query(txtConnect.Value,txtQueryRecordset.Value)

    ' convert Recordset to MIME encoded string
    vString = objDF.ConvertToString(objRs)

    ' display MIME string for demo purposes
    txtRS.value = vString

    ' convert MIME string back to useable ADO Recordset
    ' using RDS.DataControl
    RDC1.SQL = vString

    RDC1.ExecuteOptions = adcExecSync
    RDC1.FetchOptions = adcFetchUpFront
    RDC1.Refresh

    MsgBox "RecordCount = " & RDC1.Recordset.RecordCount
End Sub
</SCRIPT>

Connect String:
 <INPUT TYPE=Text NAME=txtConnect SIZE=50
    VALUE="Provider=sqloledb;Initial Catalog=pubs;User Id=sa;Password=;">
 <BR>

Query:
 <INPUT TYPE=Text NAME=txtQueryRecordset SIZE=50
    VALUE="select * from jobs">
 <BR>

 <INPUT TYPE=Button VALUE="ConvertToString" OnClick="ConvertToStringX()">
 <BR>

MIME Encoded RS: <BR>
 <TEXTAREA NAME=txtRS ROWS=15 COLS=50 WRAP=virtual></TEXTAREA>

<!-- RDS.DataSpace  ID RDS1 -->
 <OBJECT ID="RDS1" WIDTH=1 HEIGHT=1
     CLASSID="CLSID:BD96C556-65A3-11D0-983A-00C04FC29E36">
 </OBJECT>

<!-- RDS.DataControl ID RDC1 -->
 <OBJECT ID="RDC1" WIDTH=1 HEIGHT=1
     CLASSID="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33">
 </OBJECT>
</BODY>
</HTML>
<!-- EndConvertToStringVBS -->

See Also

ConvertToString Method (RDS) | Recordset Object


CreateRecordset Method Example (VBScript)

This code example creates a Recordset on the server side. It has two columns with four rows each.

<!-- BeginCreateRecordsetVBS -->
<%@ Language=VBScript %>
<html>
<head>
    <meta name="VI60_DefaultClientScript"  content=VBScript>
    <meta name="GENERATOR" content="Microsoft Visual Studio 6.0">
    <title>CreateRecordset Method Example (VBScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<h1>CreateRecordset Method Example (VBScript)</h1>

<%
   Sub CreateARecordSet
      Dim ColInfo(1), c0(3), c1(3)

      c0(0) = "Name"         ' Column name.
      c0(1) = CInt(129)      ' Column type (129 = adChar).
      c0(2) = CInt(40)      ' Column size.
      c0(3) = False         ' Is the column nullable?

      c1(0) = "Age"         ' Column name.
      c1(1) = CInt(3)      ' Column type (3 = adInteger).
      c1(2) = CInt(-1)      ' Column size.
      c1(3) = True         ' Is the column nullable?

      ' Add the columns to the recordset definition.
      ColInfo(0) = c0
      ColInfo(1) = c1

      ADC1.SourceRecordset = ADF1.CreateRecordset(ColInfo)
   End Sub
%>

</body>
</html>
<!-- EndCreateRecordsetVBS -->

See Also

CreateRecordset Method (RDS) | Recordset Object


DataControl Object Example (VBScript)

The following code shows how to set the RDS.DataControl parameters at design time and bind them to a data-aware control. Cut and paste this code between the <Body> and </Body> tags in a normal HTML document and name it DataControlDesignVBS.asp. ASP script will identify your server.

<!-- BeginDataControlDesignVBS -->
<%@ Language=VBScript %>
<HTML>
<HEAD>
<META name="VI60_DefaultClientScript"  content=VBScript>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
<title>RDS DataControl</title>
<STYLE>
<!--
BODY {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</STYLE>
</HEAD>

<BODY>
<H2>RDS API Code Examples</H2>
<HR>
<H3>Remote Data Service</H3>
<TABLE DATASRC=#RDS>
<TBODY>
   <TR>
      <TD><SPAN DATAFLD="FirstName"></SPAN></TD>
      <TD><SPAN DATAFLD="LastName"></SPAN></TD>
   </TR>
</TBODY>
</TABLE>
<!-- Remote Data Service with Parameters set at Design Time -->

<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
   ID=RDS>
   <PARAM NAME="SQL" VALUE="Select * from Employees for browse">
   <PARAM NAME="SERVER" VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>">
   <PARAM NAME="CONNECT" VALUE="Provider=SQLOLEDB;User Id=sa;Password=;Initial Catalog=Northwind">
</OBJECT>

</BODY>
</HTML>
<!-- EndDataControlDesignVBS -->

The following example shows how to set the necessary parameters of RDS.DataControl at run time. To test this example, cut and paste this code between the <Body> and </Body> tags in a normal HTML document and name it DataControlRuntimeVBS.asp. ASP script will identify your server.

<!-- BeginDataControlRuntimeVBS -->
<%@ Language=VBScript %>
<html>
<head>
    <meta name="VI60_DefaultClientScript"  content=VBScript>
    <meta name="GENERATOR" content="Microsoft Visual Studio 6.0">
    <title>Data Control Object Example (VBScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<h1>Data Control Object Example (VBScript)</h1>

<H2>RDS API Code Examples</H2>
<HR>
<H3>Remote Data Service Run Time</H3>

<TABLE DATASRC=#RDS>
<TBODY>
  <TR>
    <TD><SPAN DATAFLD="FirstName"></SPAN></TD>
    <TD><SPAN DATAFLD="LastName"></SPAN></TD>
  </TR>
</TBODY>
</TABLE>

<!-- RDS.DataControl with no parameters set at design time -->
<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
   ID=RDS HEIGHT=1 WIDTH=1>
</OBJECT>
<HR>
<Input Size=70 Name="txtServer" Value="http://<%=Request.ServerVariables("SERVER_NAME")%>"><BR>
<Input Size=70 Name="txtConnect" Value="Provider=SQLOLEDB;User Id=rdsdemo;Password=rdsdemo;Initial Catalog=AddrBookDB">
<BR>
<Input Size=70 Name="txtSQL" Value="Select * from Employee">
<HR>
<INPUT TYPE=BUTTON NAME="Run" VALUE="Run"><BR>
<H4>Fill Grid with these values or change them to see data from another ODBC data source on your server</H4>

<Script Language="VBScript">
<!--
' Set parameters of RDS.DataControl at Run Time
Sub Run_OnClick
   RDS.Server = txtServer.Value
   RDS.SQL = txtSQL.Value
   RDS.Connect = txtConnect.Value
   RDS.Refresh
End Sub
-->
</Script>

</body>
</html>
<!-- EndDataControlRuntimeVBS -->

See Also

DataControl Object (RDS)


DataSpace Object and CreateObject Method Example (VBScript)

The following example shows how to use the CreateObject method of the RDS.DataSpace with the default business object, RDSServer.DataFactory. To test this example, cut and paste this code between the <Body> and </Body> tags in a normal HTML document and name it DataSpaceVBS.asp. ASP script will identify your server.

<!-- BeginDataSpaceVBS -->
<!--#include file="adovbs.inc"-->
<html>
<head>
    <meta name="VI60_DefaultClientScript"  content=VBScript>
    <meta name="GENERATOR" content="Microsoft Visual Studio 6.0">
    <title>DataSpace Object and CreateObject Method Example (VBScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<h1>DataSpace Object and CreateObject Method Example (VBScript)</h1>

<H2>RDS API Code Examples</H2>
<HR>
<H3>Using Query Method of RDSServer.DataFactory</H3>

<!-- RDS.DataSpace  ID RDS1-->
<OBJECT ID="RDS1" WIDTH=1 HEIGHT=1
CLASSID="CLSID:BD96C556-65A3-11D0-983A-00C04FC29E36">
</OBJECT>

<!-- RDS.DataControl with parameters set at run time -->
<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
   ID=RDS WIDTH=1 HEIGHT=1>
</OBJECT>

<TABLE DATASRC=#RDS>
<TBODY>
  <TR>
    <TD><SPAN DATAFLD="FirstName"></SPAN></TD>
    <TD><SPAN DATAFLD="LastName"></SPAN></TD>
  </TR>
</TBODY>
</TABLE>

<HR>
<INPUT TYPE=BUTTON NAME="Run" VALUE="Run">

<H4>Click Run. The CreateObject Method of the RDS.DataSpace Object Creates an instance of the RDSServer.DataFactory.
The Query Method of the RDSServer.DataFactory is used to bring back a Recordset.</H4>

<Script Language="VBScript">
<!--
Dim DF
Dim strServer
Dim strConnect
Dim strSQL
strServer = "http://<%=Request.ServerVariables("SERVER_NAME")%>"
strConnect = "Provider=SQLOLEDB;User Id=adcdemo;Password=adcdemo;" & _
              "Initial Catalog=AddrBookDB;Data Source=MyServer;"
strSQL = "Select * from Employee"

Sub Run_OnClick()
   Dim objADORs      'Create Recordset Object
   Set DF = RDS1.CreateObject("RDSServer.DataFactory", strServer)      'Get Recordset
   Set objADORs = DF.Query(strConnect, strSQL)
   ' Use  RDS.DataControl to bind Recordset to Data
   ' Aware Grid Control

   RDS.SourceRecordset = objADORs

End Sub
-->
</Script>

</body>
</html>
<!-- EndDataSpaceVBS -->

The following example shows how to use the CreateObject method to create an instance of a custom business object, VbBusObj.VbBusObjCls. It also uses the Active Server Pages scripting to identify the Web server name.

To see the complete example, open the sample applications selector. In the Client Tier column, select VBScript in Internet Explorer. In the Middle Tier column, select Custom Visual Basic Business Object.

Sub Window_OnLoad()
   strServer = "http://<%=Request.ServerVariables("SERVER_NAME")%>"
   Set BO = ADS1.CreateObject("VbBusObj.VbBusObjCls", strServer)
   txtConnect.Value = "dsn=Pubs;uid=sa;pwd=;"
   txtGetRecordset.Value = "Select * From authors for Browse"
End Sub

See Also

CreateObject Method (RDS) | DataSpace Object (RDS)


DataFactory Object, Query Method, and CreateObject Method Example (VBScript)

This example creates an RDSServer.DataFactory object using the CreateObject method of the RDS.DataSpace object. To test this example, cut and paste this code between the <Body> and </Body> tags in a normal HTML document and name it DataFactoryVBS.asp. ASP script will identify your server.

<!-- BeginDataFactoryVBS -->
<HTML>
<HEAD>
<META name="VI60_DefaultClientScript" Content="VBScript">

<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
<TITLE>DataFactory Object, Query Method, and
CreateObject Method Example (VBScript)</TITLE>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</HEAD>
<BODY>
<h1>DataFactory Object, Query Method, and
CreateObject Method Example (VBScript)</h1>
<H2>RDS API Code Examples</H2>
<HR>
<H3>Using Query Method of RDSServer.DataFactory</H3>

<!-- RDS.DataSpace  ID RDS1-->
<OBJECT ID="RDS1" WIDTH=1 HEIGHT=1
CLASSID="CLSID:BD96C556-65A3-11D0-983A-00C04FC29E36">
</OBJECT>

<!-- RDS.DataControl with parameters
set at run time -->
<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
   ID=RDS WIDTH=1 HEIGHT=1>
</OBJECT>

<TABLE DATASRC=#RDS>
<TBODY>
  <TR>
    <TD><SPAN DATAFLD="FirstName"></SPAN></TD>
    <TD><SPAN DATAFLD="LastName"></SPAN></TD>
  </TR>
</TBODY>
</TABLE>

<HR>
<INPUT TYPE=BUTTON NAME="Run" VALUE="Run">
<BR>
<H4>Click Run. The CreateObject Method of the
RDS.DataSpace Object Creates an instance of the
RDSServer.DataFactory.
The Query Method of the RDSServer.DataFactory is used
to bring back a Recordset. </H4>

<Script Language="VBScript">
<!--
Dim DF
Dim strServer
Dim strConnect
Dim strSQL

strServer = "http://<%=Request.ServerVariables("SERVER_NAME")%>"
strConnect = "Provider=SQLOLEDB;User Id=rdsdemo;Password=rdsdemo;Initial Catalog=AddrBookDB"
strSQL = "Select * from Employee"

Sub Run_OnClick()
' Create RDSServer.DataFactory Object
   Dim objADORs
' Get Recordset
   Set DF = RDS1.CreateObject("RDSServer.DataFactory", strServer)
   Set objADORs = DF.Query(strConnect, strSQL)
' Set parameters of RDS.DataControl at Run Time
   RDS.Server = strServer
   RDS.SQL = strSQL
   RDS.Connect = strConnect
   RDS.Refresh
End Sub
-->
</Script>

</BODY>
</HTML>
<!-- EndDataFactoryVBS -->

See Also

CreateObject Method (RDS) | DataFactory Object (RDSServer) | DataSpace Object (RDS) | Query Method (RDS)


ExecuteOptions and FetchOptions Properties Example (VBScript)

The following code shows how to set the ExecuteOptions and FetchOptions properties at design time. If left unset, ExecuteOptions defaults to adcExecSync. This setting indicates that when the ADC.Refresh method is called, it will be executed on the current calling thread—that is, synchronously.

<!-- BeginExecuteOptionsDesignVBS -->
<%@ Language=VBScript %>
<html>
<head>
    <meta name="VI60_DefaultClientScript"  content=VBScript>
    <meta name="GENERATOR" content="Microsoft Visual Studio 6.0">
    <title>ExecuteOptions and FetchOptions Properties Example</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<h1>ExecuteOptions and FetchOptions Properties Example</h1>

<OBJECT CLASSID="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33" ID=RDS height=1 width=1>
.
<PARAM NAME="SQL" VALUE="Select FirstName, LastName from Employees Order by LastName">
<PARAM NAME="Connect" VALUE="PROVIDER="SQLOLEDB";UID=sa;PWD=;Initial Catalog=Northwind">
<PARAM NAME="Server" VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>">
<PARAM NAME="Data Source" VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>">
<PARAM NAME="ExecuteOptions" VALUE="1">
<PARAM NAME="FetchOptions" VALUE="3">
.
</OBJECT>

<TABLE DATASRC=#RDS>
<TBODY>
   <TR class="thead2">
      <TH>First Name</TH>
      <TH>Last Name</TH>
   </TR>
   <TR class="tbody">
     <TD><SPAN DATAFLD="FirstName"></SPAN></TD>
     <TD><SPAN DATAFLD="LastName"></SPAN></TD>
   </TR>
</TBODY>
</TABLE>

</body>
</html>
<!-- EndExecuteOptionsDesignVBS -->

The following example shows how to set the ExecuteOptions and FetchOptions properties at run time in VBScript code. See the Refresh method for a working example of these properties.

<!-- BeginExecuteOptionsRuntimeVBS -->
<%@ Language=VBScript %>
<html>
<head>
    <meta name="VI60_DefaultClientScript"  content=VBScript>
    <meta name="GENERATOR" content="Microsoft Visual Studio 6.0">
    <title>ExecuteOptions and FetchOptions Properties Example</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<h1>ExecuteOptions and FetchOptions Properties Example</h1>

<Script Language="VBScript">
<!--
Const adcExecSync = 1
Const adcFetchAsynch = 3

Sub ExecuteHow
  ' Execute next refresh of Recordset asynchronously
  ' to the calling thread.
  RDS1.ExecuteOptions = adcExecSync
  RDS1.FetchOptions = adcFetchAsynch
  RDS.Refresh
End Sub
-->
</Script>
</body>
</html>
<!-- EndExecuteOptionsRuntimeVBS -->

See Also

ExecuteOptions Property (RDS) | FetchOptions Property (RDS)


FilterColumn, FilterCriterion, FilterValue, SortColumn, and SortDirection Properties and Reset Method Example (VBScript)

The following code shows how to set the RDS.DataControl Server parameter at design time and bind it to a data-aware HTML table using a data source.

<!-- BeginFilterColumnVBS -->
<HTML>
<HEAD>
<META name="VI60_DefaultClientScript" Content="VBScript">

<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
<TITLE>FilterColumn, FilterCriterion, FilterValue, SortColumn, and SortDirection
Properties and Reset Method Example (VBScript)</TITLE>
</HEAD>
<BODY>
<h1>FilterColumn, FilterCriterion, FilterValue, SortColumn, and SortDirection
Properties and Reset Method Example (VBScript)</h1>

<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
   ID=RDS HEIGHT=1 WIDTH=1>
   <PARAM NAME="SQL" VALUE="Select * from Employee">
   <PARAM NAME="SERVER" VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>">
   <PARAM NAME="DATA SOURCE" VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>">
   <PARAM NAME="CONNECT" VALUE="Provider=SQLOLEDB;User Id=rdsdemo;Password=rdsdemo;Initial Catalog=AddrBookDB">
</OBJECT>

Sort Column: <SELECT NAME="cboSortColumn">
              <OPTION VALUE=""></OPTION>
                  <OPTION VALUE=ID>ID</OPTION>
                  <OPTION VALUE=FirstName>FirstName</OPTION>
                  <OPTION VALUE=LastName>LastName</OPTION>
                  <OPTION VALUE=Title>Title</OPTION>
                  <OPTION VALUE=Type>Type</OPTION>
                  <OPTION VALUE=Email>Email</OPTION>
                  <OPTION VALUE=Building>Building</OPTION>
                  <OPTION VALUE=Room>Room</OPTION>
                  <OPTION VALUE=Phone>Phone</OPTION>
             </SELECT>
             <br>
Sort Direction: <SELECT NAME="cboSortDir">
              <OPTION VALUE=""></OPTION>
                  <OPTION VALUE=TRUE>TRUE</OPTION>
                  <OPTION VALUE=FALSE>FALSE</OPTION>
                </SELECT>
<HR WIDTH="25%">
Filter Column: <SELECT NAME="cboFilterColumn">
              <OPTION VALUE=""></OPTION>
                  <OPTION VALUE=ID>ID</OPTION>
                  <OPTION VALUE=FirstName>FirstName</OPTION>
                  <OPTION VALUE=LastName>LastName</OPTION>
                  <OPTION VALUE=Title>Title</OPTION>
                  <OPTION VALUE=Type>Type</OPTION>
                  <OPTION VALUE=Email>Email</OPTION>
                  <OPTION VALUE=Building>Building</OPTION>
                  <OPTION VALUE=Room>Room</OPTION>
                  <OPTION VALUE=Phone>Phone</OPTION>
             </SELECT>
             <br>
Filter Criterion: <SELECT NAME="cboCriterion">
                    <OPTION VALUE=""></OPTION>
                    <OPTION VALUE="=">=</OPTION>
                    <OPTION VALUE="&gt;">&gt;</OPTION>
                    <OPTION VALUE="&lt;">&lt;</OPTION>
                    <OPTION VALUE="&gt;=">&gt;=</OPTION>
                    <OPTION VALUE="&lt;=">&lt;=</OPTION>
                    <OPTION VALUE="&lt;&gt;">&lt;&gt;</OPTION>
                  </SELECT>
              <br>
Filter Value: <INPUT NAME="txtFilterValue">
<HR WIDTH="25%">
<INPUT TYPE=BUTTON NAME=Clear VALUE="CLEAR ALL"> &nbsp;
<INPUT TYPE=BUTTON NAME=SortFilter VALUE="APPLY">

<HR>
<TABLE DATASRC=#RDS ID="DataTable">
<THEAD>
  <TR>
    <TH>ID</TH>
    <TH>FirstName</TH>
    <TH>LastName</TH>
    <TH>Title</TH>
    <TH>Type</TH>
    <TH>Email</TH>
    <TH>Building</TH>
    <TH>Room</TH>
    <TH>Phone</TH>
  </TR>
</THEAD>
<TBODY>
  <TR>
    <TD><SPAN DATAFLD="ID"></SPAN></TD>
    <TD><SPAN DATAFLD="FirstName"></SPAN></TD>
    <TD><SPAN DATAFLD="LastName"></SPAN></TD>
    <TD><SPAN DATAFLD="Title"></SPAN></TD>
    <TD><SPAN DATAFLD="Type"></SPAN></TD>
    <TD><SPAN DATAFLD="Email"></SPAN></TD>
    <TD><SPAN DATAFLD="Building"></SPAN></TD>
    <TD><SPAN DATAFLD="Room"></SPAN></TD>
    <TD><SPAN DATAFLD="Phone"></SPAN></TD>
  </TR>
</TBODY>
</TABLE>

<Script Language="VBScript">
<!--
Const adFilterNone = 0

Sub SortFilter_OnClick
   Dim vCriterion
   Dim vSortDir
   Dim vSortCol
   Dim vFilterCol

   ' The value of SortColumn will be the
   ' value of what the user picks in the
   ' cboSortColumn box.
   vSortCol = cboSortColumn.options(cboSortColumn.selectedIndex).value

   If(vSortCol <> "") then
      RDS.SortColumn = vSortCol
   End If

   ' The value of SortDirection will be the
   ' value of what the user specifies in the
   ' cboSortdirection box.

   If (vSortCol <> "") then
      vSortDir = cboSortDir.options(cboSortDir.selectedIndex).value
      If (vSortDir = "") then
         MsgBox "True or False are only acceptable Sort Direction values."
         Exit Sub
      Else
         RDS.SortDirection = vSortDir
      End If
   End If

   ' The value of FilterColumn will be the
   ' value of what the user specifies in the
   ' cboFilterColumn box.
   vFilterCol = cboFilterColumn.options(cboFilterColumn.selectedIndex).value

   If(vFilterCol <> "") then
      RDS.FilterColumn = vFilterCol
   End If

   ' The value of FilterCriterion will be the
   ' text value of what the user specifies in the
   ' cboCriterion box.
   vCriterion = cboCriterion.options(cboCriterion.selectedIndex).value
   If (vCriterion <> "") Then
      RDS.FilterCriterion = vCriterion
   End If

   ' txtFilterValue is a rich text box
   ' control. The value of FilterValue will be the
   ' text value of what the user specifies in the
   ' txtFilterValue box.
   If (txtFilterValue.value <> "") Then
      RDS.FilterValue = txtFilterValue.value
   End If

   ' Execute the sort and filter on a client-side
   ' Recordset based on the specified sort and filter
   ' properties. Calling Reset refreshes the result set
   ' that is displayed in the data-bound controls to
   ' display the filtered, sorted recordset.
   RDS.Reset
End Sub

Sub Clear_onClick()
   'clear the HTML input controls
   cboSortColumn.selectedIndex = 0
   cboSortDir.selectedIndex = 0
   cboFilterColumn.selectedIndex = 0
   cboCriterion.selectedIndex = 0
   txtFilterValue.value = ""

   'clear the filter
   RDS.FilterCriterion = ""
   RDS.Reset(FALSE)
End Sub
-->
</Script>

</BODY>
</HTML>
<!-- EndFilterColumnVBS -->

See Also

DataControl Object (RDS) | FilterColumn Property (RDS) | FilterCriterion Property (RDS) | FilterValue Property (RDS) | Reset Method (RDS) | SortColumn Property (RDS) | SortDirection Property (RDS)


ReadyState Property Example (VBScript)

The following example shows how to read the ReadyState property of the RDS.DataControl object at run time in VBScript code. ReadyState is a read-only property.

To test this example, cut and paste this code between the <Body> and </Body> tags in a normal HTML document and name it RDSReadySt.asp. Use Find to locate the file Adovbs.inc and place it in the directory you plan to use. ASP script will identify your server.

<!-- BeginReadyStateVBS -->
<%@ Language=VBScript %>
<!--#include file="adovbs.inc"-->
<html>
<head>
    <meta name="VI60_DefaultClientScript"  content=VBScript>
    <meta name="GENERATOR" content="Microsoft Visual Studio 6.0">
    <title>RDS.DataControl ReadyState Property</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<H1>RDS.DataControl ReadyState Property</H1>
<H2>RDS API Code Examples </H2>
<HR>
<!-- RDS.DataControl with parameters set at design time -->
<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
   ID=RDS>
   <PARAM NAME="SQL" VALUE="Select * from Employee for browse">
   <PARAM NAME="SERVER" VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>">
   <PARAM NAME="CONNECT" VALUE="Provider=SQLOLEDB;User Id=rdsdemo;Password=rdsdemo;Initial Catalog=AddrBookDB">
   <PARAM NAME="ExecuteOptions" VALUE="adcExecAsync">
   <PARAM NAME="FetchOptions" VALUE="adcFetchAsync">
</OBJECT>

<Script Language="VBScript">

Sub Window_OnLoad
   Select Case RDS.ReadyState
      case 2: MsgBox "Executing Query"
      case 3: MsgBox "Fetching records in background"
      case 4: MsgBox "All records fetched"
   End Select
End Sub
</Script>

</body>
</html>
<!-- EndReadyStateVBS -->

See Also

DataControl Object (RDS) | ReadyState Property (RDS)


Recordset and SourceRecordset Properties Example (VBScript)

The following example shows how to set the necessary parameters of the RDSServer.DataFactory default business object at run time.

To test this example, cut and paste this code between the <Body> and </Body> tags in a normal HTML document and name it RecordsetVBS.asp. ASP script will identify your server.

<!-- BeginRecordSetVBS -->
<%@ Language=VBScript %>
<html>
<head>
    <meta name="VI60_DefaultClientScript"  content=VBScript>
    <meta name="GENERATOR" content="Microsoft Visual Studio 6.0">
    <title>Recordset and SourceRecordset Properties Example (VBScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<h1>Recordset and SourceRecordset Properties Example (VBScript)</h1>

<Center>
<H2>RDS API Code Examples</H2>
<HR>
<H3>Using SourceRecordset and Recordset with RDSServer.DataFactory</H3>
<!-- RDS.DataSpace ID RDS1 -->
<OBJECT ID="RDS1" WIDTH=1 HEIGHT=1
CLASSID="CLSID:BD96C556-65A3-11D0-983A-00C04FC29E36">
</OBJECT>

<!-- RDS.DataControl with parameters set at Run Time -->
<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
   ID=RDC WIDTH=1 HEIGHT=1>
</OBJECT>

<TABLE DATASRC=#RDC>
   <TR>
      <TD> <INPUT DATAFLD="FirstName" SIZE=15> </TD>
      <TD> <INPUT DATAFLD="LastName" SIZE=15 id=text1 name=text1> </TD>
      <TD> <INPUT DATAFLD="Title" SIZE=15 id=text2 name=text2> </TD>
      <TD> <INPUT DATAFLD="Type" SIZE=15 id=text3 name=text3> </TD>
      <TD> <INPUT DATAFLD="Email" SIZE=15 id=text4 name=text4> </TD>
   </TR>
</TABLE>
<HR>
<Input Size=70 Name="txtServer" Value="http://<%=Request.ServerVariables("SERVER_NAME")%>"><BR>
<Input Size=70 Name="txtConnect" Value="Provider=SQLOLEDB;User Id=rdsdemo;Password=rdsdemo;Initial Catalog=AddrBookDB"><BR>
<Input Size=70 Name="txtSQL" Value="Select * from Employee">
<HR>
<INPUT TYPE=BUTTON NAME="Run" VALUE="Run"><BR>

</Center>
<Script Language="VBScript">
<!--
Dim ADF
Dim strServer
strServer = "http://<%=Request.ServerVariables("SERVER_NAME")%>"

Sub Run_OnClick()
   Dim objADORs
   ' Create RDSServer.DataFactory Object
   Set RDF = RDS1.CreateObject("RDSServer.DataFactory", strServer)
   ' Get Recordset
   Set objADORs = RDF.Query(txtConnect.Value,txtSQL.Value)

   ' Set parameters of RDS.DataControl at run time.
   RDC.Server = txtServer.Value
   RDC.SQL = txtSQL.Value
   RDC.Connect = txtConnect.Value
   RDC.Refresh
End Sub
-->
</Script>

</body>
</html>
<!-- EndRecordsetVBS -->

See Also

DataFactory Object (RDSServer) | Recordset, SourceRecordset Properties (RDS)


Refresh Method Example (VBScript)

The following example shows how to set the necessary parameters of RDS.DataControl at run time. The manner in which a Recordset is retrieved using the Refresh method is determined by the settings of the ExecuteOptions and FetchOptions properties. To test this example, cut and paste the following code into a normal ASP document and name it RefreshVBS.asp. Use Find to locate the file Adovbs.inc and place it in the directory you plan to use. ASP script will identify your server.

<!-- BeginRefreshVBS -->
<%@ Language=VBScript %>
<!--#include file="adovbs.inc"-->
<html>
<head>
    <meta name="VI60_DefaultClientScript"  content=VBScript>
    <meta name="GENERATOR" content="Microsoft Visual Studio 6.0">
    <title>Refresh Method Example (VBScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<h1>Refresh Method Example (VBScript)</h1>

<H2>RDS API Code Examples </H2>
<HR>
<TABLE DATASRC=#RDC>
   <TR>
      <TD> <INPUT DATAFLD="FirstName" SIZE=15 id=text5 name=text5> </TD>
      <TD> <INPUT DATAFLD="LastName" SIZE=15 id=text1 name=text1> </TD>
      <TD> <INPUT DATAFLD="Title" SIZE=15 id=text2 name=text2> </TD>
      <TD> <INPUT DATAFLD="Type" SIZE=15 id=text3 name=text3> </TD>
      <TD> <INPUT DATAFLD="Email" SIZE=15 id=text4 name=text4> </TD>
   </TR>
</TABLE>

<!-- RDS.DataControl with no parameters set at design time -->
<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
   ID=RDC HEIGHT=1 WIDTH=1>
</OBJECT>
<HR>
Server: <Input Size=70 Name="txtServer" Value="http://<%=Request.ServerVariables("SERVER_NAME")%>"><BR>
Connect: <Input Size=70 Name="txtConnect" Value="Provider=SQLOLEDB;User Id=rdsdemo;Password=rdsdemo;Initial Catalog=AddrBookDB"><BR>
SQL: <Input Size=70 Name="txtSQL" Value="Select * from Employee">
<HR>
<TABLE BORDER=1 WIDTH="60%">
<TR>
   <TD COLSPAN=3 BGCOLOR=silver>
   Choose if you want the Recordset brought back Synchronously on the
   current calling thread or Asynchronously on another thread.
   </TD>
</TR>
<TR>
   <TD>Synchronously: <BR>
      <Input Type="Radio" Name="optExecuteOptions" Checked OnClick="SetExO('adcExecSync')">
   </TD>
   <TD>Asynchronously: <BR>
      <Input Type="Radio" Name="optExecuteOptions"  OnClick="SetExO('adcExecAsync')">
   </TD>
   <TD>&nbsp;</TD>
</TR>
<TR>
   <TD COLSPAN=3 BGCOLOR=silver>
   Fetch Up Front, Background Fetch with Blocking or Background Fetch
   without Blocking
   </TD>
<TR>
   <TD>Up Front:<BR>
       <Input Type="Radio" Name="optFetchOptions"  OnClick="SetFO('adcFetchUpFront')">
   </TD>
   <TD>Background w/ Blocking:<BR>
       <Input Type="Radio" Name="optFetchOptions" Checked OnClick="SetFO('adcFetchBackground')">
   </TD>
   <TD>Background w/o Blocking:<BR>
      <Input Type="Radio" Name="optFetchOptions"  OnClick="SetFO('adcFetchAsync')">
   </TD>
</TR>
</TABLE>

<INPUT TYPE=BUTTON NAME="Run" VALUE="Run"><BR>

<Script Language="VBScript">
<!--
Dim EO         'ExecuteOptions
Dim FO         'FetchOptions
EO = "adcExecSync"   'Default value
FO = "adcFetchBackground"   'Default value

Sub SetExO(NewEO)
   EO = NewEO
End Sub

Sub SetFO(NewFO)
   FO = NewFO
End Sub

' Set parameters of RDS.DataControl at Run Time
Sub Run_OnClick
   RDC.Server = txtServer.Value
   RDC.SQL = txtSQL.Value
   RDC.Connect = txtConnect.Value
   If EO = "adcExecSync" Then   'Determine which ExecuteOption chosen
      RDC.ExecuteOptions = adcExecSync
      MsgBox "Recordset brought in on current calling thread Syncronously"
   Else
      RDC.ExecuteOptions = adcExecAsync
      MsgBox "Recordset brought in on another thread Asyncronously"
   End If

   If FO = "adcFetchBackground" Then      'Determine                 ‘which FetchOption chosen
      RDC.FetchOptions = adcFetchBackground
      MsgBox "Control goes back to user after first batch of records returned"
   ElseIf FO = " adcFetchUpFront" Then
      RDC.FetchOptions = adcFetchUpFront
      MsgBox "All records returned before control goes back to user"
   Else
      RDC.FetchOptions = adcFetchAsync
      MsgBox "Control goes back to user immediately"
   End If

   RDC.Refresh
End Sub
-->
</Script>

</body>
</html>
<!-- EndRefreshVBS -->

See Also

DataControl Object (RDS) | ExecuteOptions Property (RDS) | FetchOptions Property (RDS) | Recordset Object | Refresh Method


Server Property Example (VBScript)

The following code shows how to set the RDS.DataControl parameter at design time and bind it to a data-aware control using the SQLOLEDB provider. Cut and paste this code into a normal ASP document and name it ServerDesignVBS.asp. ASP script will identify your server.

<!-- BeginServerDesignVBS -->
<%@ Language=VBScript %>
<html>
<head>
    <meta name="VI60_DefaultClientScript"  content=VBScript>
    <meta name="GENERATOR" content="Microsoft Visual Studio 6.0">
    <title>Server Property Example (VBScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<h1>Server Property Example (VBScript)</h1>

<TABLE DATASRC=#RDS>
   <TR>
      <TD> <SPAN DATAFLD="FirstName"></SPAN> </TD>
      <TD> <SPAN DATAFLD="LastName"></SPAN> </TD>
      <TD> <SPAN DATAFLD="Title"></SPAN> </TD>
      <TD> <SPAN DATAFLD="Type"></SPAN> </TD>
      <TD> <SPAN DATAFLD="Email"></SPAN> </TD>
   </TR>
</TABLE>
<!-- Remote Data Service with Parameters set at Design Time -->
<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
   ID=RDS HEIGHT=1 WIDTH=1>
   <PARAM NAME="SQL" VALUE="Select * from Employee for browse">
   <PARAM NAME="SERVER" VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>">
   <PARAM NAME="CONNECT" VALUE="Provider=SQLOLEDB;User Id=rdsdemo;Password=rdsdemo;Initial Catalog=AddrBookDB">
</OBJECT>

</body>
</html>
<!-- EndServerDesignVBS -->

The following example shows how to set the necessary parameters of RDS.DataControl at run time. To test this example, cut and paste this code into a normal ASP document and name it ServerRuntimeVBS.asp. ASP script will identify your server.

<!-- BeginServerRuntimeVBS -->
<%@ Language=VBScript %>
<html>
<head>
    <meta name="VI60_DefaultClientScript"  content=VBScript>
    <meta name="GENERATOR" content="Microsoft Visual Studio 6.0">
    <title>Server Property Example (VBScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<h1>Server Property Example (VBScript)</h1>

<H2>RDS API Code Examples</H2>

<H3>Remote Data Service Server Property Set at Run Time</H3>

<!-- RDS.DataControl with no parameters set at design time -->
<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
   ID=RDC HEIGHT=1 WIDTH=1>
</OBJECT>

<TABLE DATASRC=#RDC>
   <TR>
      <TD> <SPAN DATAFLD="FirstName"></SPAN> </TD>
      <TD> <SPAN DATAFLD="LastName"></SPAN> </TD>
      <TD> <SPAN DATAFLD="Title"></SPAN> </TD>
      <TD> <SPAN DATAFLD="Type"></SPAN> </TD>
      <TD> <SPAN DATAFLD="Email"></SPAN> </TD>
   </TR>
</TABLE>

<HR>
<Input Size=70 Name="txtServer" Value="HTTP://<%= Request.ServerVariables("SERVER_NAME")%>">
<BR>
<Input Size=70 Name="txtConnect" Value="Provider=SQLOLEDB;User Id=rdsdemo;Password=rdsdemo;Initial Catalog=AddrBookDB">
<BR>
<Input Size=70 Name="txtSQL" Value="Select * from Employee">
<HR>
<INPUT TYPE=BUTTON NAME="Run" VALUE="Run"><BR>

<Script Language="VBScript">
<!--
' Set parameters of RDS.DataControl at Run Time
Sub Run_OnClick
   RDC.Server = txtServer.Value
   RDC.SQL = txtSQL.Value
   RDC.Connect = txtConnect.Value
   RDC.Refresh
End Sub
-->
</Script>

</body>
</html>
<!-- EndServerRuntimeVBS -->

See Also

DataControl Object (RDS) | Server Property (RDS)


SQL Property Example (VBScript)

The following code shows how to set the RDS.DataControl SQL parameter at design time and bind it to a data-aware control using the database called Pubs, which ships with Microsoft® SQL Server™. To test the example, copy the following code into a normal ASP document named SQLDesignVBS.asp on your Web server.

<!-- BeginSQLDesignVBS -->
<%@ Language=VBScript %>
<html>
<head>
    <meta name="VI60_DefaultClientScript"  content=VBScript>
    <meta name="GENERATOR" content="Microsoft Visual Studio 6.0">
    <title>SQL Property Example (VBScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<h1>SQL Property Example (VBScript)</h1>

<!-- RDS.DataControl -->
<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33" ID=RDC HEIGHT=1 WIDTH=1>
   <PARAM NAME="SQL" VALUE="Select * from Authors">
   <PARAM NAME="SERVER" VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>">
   <PARAM NAME="CONNECT" VALUE="dsn=Pubs;UID=sa;PWD=;">
</OBJECT>

<!-- Data Table -->

<TABLE DATASRC=#RDC BORDER=1>
   <TR>
      <TD> <SPAN DATAFLD="au_id"></SPAN> </TD>
      <TD> <SPAN DATAFLD="au_lname"></SPAN> </TD>
      <TD> <SPAN DATAFLD="au_fname"></SPAN> </TD>
      <TD> <SPAN DATAFLD="city"></SPAN> </TD>
      <TD> <SPAN DATAFLD="state"></SPAN> </TD>
   </TR>
</TABLE>

</body>
</html>
<!-- EndSQLDesignVBS -->

The following example shows how to set the necessary parameters of RDS.DataControl at run time. To test this example, cut and paste the following code into a normal ASP document and name it SQLRuntimeVBS.asp. ASP script will identify your server.

<!-- BeginSQLRuntimeVBS -->
<%@ Language=VBScript %>
<html>
<head>
    <meta name="VI60_DefaultClientScript"  content=VBScript>
    <meta name="GENERATOR" content="Microsoft Visual Studio 6.0">
    <title>SQL Property Example (VBScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<h1>SQL Property Example (VBScript)</h1>

<H2>RDS API Code Examples </H2>
<H3>Remote Data Service SQL Property Set at Run Time</H3>

<!-- RDS.DataControl with no parameters set at design time -->
<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
   ID=RDC HEIGHT=1 WIDTH=1>
</OBJECT>

<TABLE DATASRC=#RDC>
   <TR>
      <TD> <SPAN DATAFLD="FirstName"></SPAN> </TD>
      <TD> <SPAN DATAFLD="LastName"></SPAN> </TD>
      <TD> <SPAN DATAFLD="Title"></SPAN> </TD>
      <TD> <SPAN DATAFLD="Type"></SPAN> </TD>
      <TD> <SPAN DATAFLD="Email"></SPAN> </TD>
   </TR>
</TABLE>

<HR>
<Input Size=70 Name="txtServer" Value= "http://<%=Request.ServerVariables("SERVER_NAME")%>">
<BR>
<Input Size=70 Name="txtConnect" Value="Provider=SQLOLEDB;User Id=rdsdemo;Password=rdsdemo;Initial Catalog=AddrBookDB;">
<BR>
<Input Size=70 Name="txtSQL" VALUE="Select * from Employee">
<HR>
<INPUT TYPE=BUTTON NAME="Run" VALUE="Run"><BR>

<Script Language="VBScript">
<!--
' Set parameters of RDS.DataControl at Run Time.
Sub Run_OnClick
   RDC.Server = txtServer.Value
   RDC.SQL = txtSQL.Value
   RDC.Connect = txtConnect.Value
   RDC.Refresh
End Sub
-->
</Script>

</body>
</html>
<!-- EndSQLRuntimeVBS -->

See Also

DataControl Object (RDS) | SQL Property (RDS)


SubmitChanges Method Example (VBScript)

The following code fragment shows how to use the SubmitChanges method with an RDS.DataControl object.

To test this example, cut and paste this code into a normal ASP document and name it SubmitChangesCtrlVBS.asp. ASP script will identify your server.

<!-- BeginSubmitChangesCtrlVBS -->
<%@ Language=VBScript %>
<html>
<head>
    <meta name="VI60_DefaultClientScript"  content=VBScript>
    <meta name="GENERATOR" content="Microsoft Visual Studio 6.0">
    <title>SubmitChanges Method Example (VBScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<h1>SubmitChanges Method Example (VBScript)</h1>

<%@ Language=VBScript %>
<HTML>
<BODY>
<H2>RDS API Code Examples</H2>
<H3>Remote Data Service SubmitChanges and CancelUpdate Methods</H3>

<!-- RDS.DataControl with parameters set at design time -->
<OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33"
   ID=RDC HEIGHT=1 WIDTH=1>
   <PARAM NAME="SQL" VALUE="Select * from Employee for browse">
   <PARAM NAME="SERVER" VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>">
   <PARAM NAME="CONNECT" VALUE="Provider=SQLOLEDB;User Id=rdsdemo;Password=rdsdemo;Initial Catalog=AddrBookDB;">
</OBJECT>

<TABLE DATASRC="#RDC">
<THEAD>
<TR ID="ColHeaders">
   <TH>ID</TH>
   <TH>FName</TH>
   <TH>LName</TH>
   <TH>Name</TH>
   <TH>Title</TH>
   <TH>Type</TH>
   <TH>Email</TH>
   <TH>MgrEmail</TH>
   <TH>Bldg</TH>
   <TH>Rm</TH>
   <TH>Phone</TH>
</TR>
</THEAD>

<TBODY>
<TR>
   <TD> <INPUT DATAFLD="ID" size=4> </TD>
   <TD> <INPUT DATAFLD="FirstName" size=10> </TD>
   <TD> <INPUT DATAFLD="LastName" size=10> </TD>
   <TD> <INPUT DATAFLD="Name" size=15> </TD>
   <TD> <INPUT DATAFLD="Title" size=20> </TD>
   <TD> <INPUT DATAFLD="Type" size=10> </TD>
   <TD> <INPUT DATAFLD="Email" size=10> </TD>
   <TD> <INPUT DATAFLD="ManagerEmail" size=10> </TD>
   <TD> <INPUT DATAFLD="Building" size=3> </TD>
   <TD> <INPUT DATAFLD="Room" size=5> </TD>
   <TD> <INPUT DATAFLD="Phone" size=8> </TD>
</TR>
</TBODY>
</TABLE>

<HR>
<INPUT TYPE=BUTTON NAME="SubmitChange" VALUE="Submit-Changes">
<INPUT TYPE=BUTTON NAME="CancelChange" VALUE="Cancel-Update"><BR>
<H4>Alter a current entry in the table. Move off that Row. <BR>
Submit the Changes to your DBMS or cancel the updates. </H4>
</Center>
<Script Language="VBScript">
<!--
' Set parameters of RDS.DataControl at Run Time
Sub SubmitChange_OnClick
   RDC.SubmitChanges
   RDC.Refresh
End Sub

Sub CancelUpdate_OnClick
   RDC.CancelUpdate
   RDC.Refresh
End Sub
-->
</Script>
</BODY>
</HTML>
</body>
</html>
<!-- EndSubmitChangesCtrlVBS -->

See Also

DataControl Object (RDS) | SubmitChanges Method (RDS)


URL Property Example (VBScript)

The following code demonstrates how to set the URL property on the client side to specify an .asp file that in turn handles the submission of changes to the data source.

<!-- BeginURLClientVBS -->
<%@ Language=VBScript %>
<html>
<head>
    <meta name="VI60_DefaultClientScript"  content=VBScript>
    <meta name="GENERATOR" content="Microsoft Visual Studio 6.0">
    <title>URL Property Example (VBScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<h1>URL Property Example (VBScript)</h1>
<OBJECT classid=clsid:BD96C556-65A3-11D0-983A-00C04FC29E33 height=1 id=ADC width=1>
<PARAM NAME="SQL" VALUE="Select * from Employees for browse">
<PARAM NAME="Connect" VALUE="Provider=SQLOLEDB;User Id=sa;Password=;Initial Catalog=Northwind">
<PARAM NAME="Server" VALUE="http://<%=Request.ServerVariables("SERVER_NAME")%>">
</OBJECT>

<table datasrc="#ADC" align="center">
<thead>
<tr id="ColHeaders" class="thead2">
   <th>FirstName</th>
   <th>LastName</th>
   <th>Extension</th>
</tr>
</thead>
<tbody class="tbody">
<tr>
   <td><input datafld="FirstName" size=15> </td>
   <td><input datafld="LastName" size=25> </td>
   <td><input datafld="Extension" size=15> </td>
</tr>
</tbody>
</table>
<p align="center">
<INPUT type="submit" value="Update" id=Update name=Update>
</p>

<script Language="VBScript">
Sub Update_OnClick
   If ADC.ReadyState <> adcReadyStateLoaded then
      ADC.URL = "URLServerVBS.asp"
      ADC.SubmitChanges
      ADC.Refresh
   Else
      MsgBox "Query results still arriving, Please wait"
   End if
End Sub
</script>

</body>
</html>
<!-- EndURLClientVBS -->

The server-side code that exists in RDSSubmit.asp submits the updated Recordset to the data source.

!<-- BeginURLServerVBS -->
<%@ Language=VBScript %>
<html>
<head>
    <meta name="VI60_DefaultClientScript"  content=VBScript>
    <meta name="GENERATOR" content="Microsoft Visual Studio 6.0">
    <title>URL Property Example (VBScript)</title>
<style>
<!--
body {
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   BACKGROUND-COLOR:white;
   COLOR:black;
    }
.thead {
   background-color: #008080;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.thead2 {
   background-color: #800000;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
   color: white;
   }
.tbody {
   text-align: center;
   background-color: #f7efde;
   font-family: 'Verdana','Arial','Helvetica',sans-serif;
   font-size: x-small;
    }
-->
</style>
</head>

<body>
<h1>URL Property Example (VBScript)</h1>

<%

Option Explicit

Dim strSQL, strConnection, rstEmployees

strConnection = _
   "Provider=SQLOLEDB;Server=a-iresmi2000;Database=Northwind;User Id=sa;Password=;"
Set rstCustomers = Server.CreateObject("ADODB.Recordset")

On Error Resume Next
rstEmployees.Open Request
rstEmployees.ActiveConnection = strConnection
rstEmployees.UpdateBatch
If Err.Number <> 0 Then
   Request.Status = "500 " + Err.Source + ": " + Err.Description
End If
%>

</body>
</html>
<!-- EndURLServerVBS -->

See Also

URL Property (RDS)


RDS Code Examples in Microsoft Visual C++

Use the following code examples to learn how to use RDS properties when writing in Microsoft Visual C++.

Note Paste the entire code example, from beginning to end, into your code editor. The example may not run correctly if partial examples are used or if paragraph formatting is lost.

  • Handler Property Example
  • InternetTimeout Property Example


Handler Property Example (VC++)

This example demonstrates the RDS DataControl object Handler property. (See DataFactory Customization for more details.)

Assume the following sections in the parameter file, Msdfmap.ini, located on the server:

[connect AuthorDataBase]
Access=ReadWrite
Connect="DSN=Pubs"
[sql AuthorById]
SQL="SELECT * FROM Authors WHERE au_id = ?"

Your code looks like the following. The command assigned to the SQL property will match the AuthorById identifier and will retrieve a row for author Michael O'Leary. Although the Connect property in your code specifies the Northwind data source, that data source will be overwritten by the Msdfmap.ini connect section. The DataControl object Recordset property is assigned to a disconnected Recordset object purely as a coding convenience.

// BeginHandlerCpp
TESTHR( "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")
TESTHR( "C:\Program Files\Common Files\System\MSADC\msadco.dll"

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void HandlerX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//     Main Function                                    //
//                                                      //
//////////////////////////////////////////////////////////

void main()
{
    HRESULT  hr = S_OK;
    hr = ::CoInitialize(NULL);
    if (SUCCEEDED(hr))
    {
        HandlerX();
        printf("Press any key to continue...");
        getch();
        ::CoUninitialize();
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//          HandlerX Function                           //
//                                                      //
//////////////////////////////////////////////////////////

void HandlerX(void)
{
   HRESULT  hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
   _RecordsetPtr  pRst  = NULL;

   //Define RDS object pointers.
   RDS::IBindMgrPtr dc;

     try
    {
        TESTHR(hr = dc.CreateInstance(__uuidof(RDS::DataControl)));
        dc->Handler = "MSDFMAP.Handler";
        dc->Server = "http://srv";
        dc->Connect = "Data Source=AuthorDatabase";
        dc->SQL = "AuthorById(267-41-2394)";

        // Retrieve the record.
        dc->Refresh();

        // Use another Recordset as a convenience.
        pRst = dc->GetRecordset();
        printf("Author is %s %s",(LPSTR) (_bstr_t) pRst->Fields->GetItem("au_fname")->Value,\
            (LPSTR) (_bstr_t) pRst->Fields->GetItem("au_lname")->Value);
        pRst->Close();

    }  // End Try statement.
    catch (_com_error &e)
    {
        PrintProviderError(pRst->GetActiveConnection());
        PrintComError(e);
    }

}

//////////////////////////////////////////////////////////
//                                                      //
//      PrintProviderError Function                     //
//                                                      //
//////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr    pErr  = NULL;
    long      nCount  = 0;
    long      i  = 0;

    if( (pConnection->Errors->Count) > 0)
    {
        nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number, pErr->Description);
        }
    }
}

//////////////////////////////////////////////////////////
//
//      PrintComError Function                          //
//                                                      //
//////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
   _bstr_t bstrSource(e.Source());
   _bstr_t bstrDescription(e.Description());

    // Print Com errors.
   printf("Error\n");
   printf("\tCode = %08lx\n", e.Error());
   printf("\tCode meaning = %s\n", e.ErrorMessage());
   printf("\tSource = %s\n", (LPCSTR) bstrSource);
   printf("\tDescription = %s\n", (LPCSTR) bstrDescription);

}

// EndHandlerCpp

See Also

DataControl Object (RDS) | Handler Property (RDS)


InternetTimeout Property Example (VC++)

This example demonstrates the InternetTimeout property, which exists on the DataControl and DataSpace objects. In this case, the InternetTimeout property is demonstrated on the DataControl object and the timeout is set to 20 seconds.

// BeginInternetTimeoutCpp
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
    no_namespace rename("EOF", "EndOfFile")
#import "C:\Program Files\Common Files\System\MSADC\msadco.dll"

#include <ole2.h>
#include <stdio.h>
#include <conio.h>

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void InternetTimeOutX(void);
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);

//////////////////////////////////////////////////////////
//                                                      //
//    Main Function                                     //
//                                                      //
//////////////////////////////////////////////////////////

void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    InternetTimeOutX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//         InternetTimeOutX Function                    //
//                                                      //
//////////////////////////////////////////////////////////

void InternetTimeOutX(void)
{
    HRESULT hr = S_OK;

    // Define ADO object pointers.
    // Initialize pointers on define.
    // These are in the ADODB::  namespace.
    _RecordsetPtr  pRst = NULL;

    //Define RDS object pointers
    RDS::IBindMgrPtr dc ;

    try
    {
        TESTHR(dc.CreateInstance(__uuidof(RDS::DataControl)));
        dc->Server = "http://srv";
        dc->Connect = "Data Source=AuthorDatabase";
        dc->SQL = "SELECT * FROM Authors";

        // Wait at least 20 seconds.
        dc->InternetTimeout = 20000;
        dc->Refresh();

        // Use another Recordset as a convenience
        pRst = dc->GetRecordset();
        while(!(pRst->EndOfFile))
        {
            printf("%s %s",(LPSTR) (_bstr_t) pRst->Fields->
                GetItem("au_fname")->Value,
               (LPSTR) (_bstr_t) pRst->Fields->
                GetItem("au_lname")->Value);

            pRst->MoveNext();
        }
        pRst->Close();
    }

    catch (_com_error &e)
    {
        PrintProviderError(pRst->GetActiveConnection());
        PrintComError(e);
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//     PrintProviderError Function                      //
//                                                      //
//////////////////////////////////////////////////////////

void PrintProviderError(_ConnectionPtr pConnection)
{
    // Print Provider Errors from Connection object.
    // pErr is a record object in the Connection's Error collection.
    ErrorPtr  pErr  = NULL;

    if( (pConnection->Errors->Count) > 0)
    {
        long nCount = pConnection->Errors->Count;
        // Collection ranges from 0 to nCount -1.
        for(long i = 0; i < nCount; i++)
        {
            pErr = pConnection->Errors->GetItem(i);
            printf("\t Error number: %x\t%s", pErr->Number,
                pErr->Description);
        }
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//     PrintComError Function                           //
//                                                      //
//////////////////////////////////////////////////////////

void PrintComError(_com_error &e)
{
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());

    // Print Com errors.
    printf("Error\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    printf("\tSource = %s\n", (LPCSTR) bstrSource);
    printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
// EndInternetTimeoutCpp

See Also

InternetTimeoutProperty (RDS)


RDS Code Examples in Microsoft Visual J++

Use the following code examples to learn how to use RDS properties when writing in Microsoft Visual J++.

Note Paste the entire code example, from beginning to end, into your code editor. The example may not run correctly if partial examples are used or if paragraph formatting is lost.

  • Handler Property Example
  • InternetTimeout Property Example


Handler Property Example (VJ++)

This example demonstrates the RDS DataControl object Handler property. (See DataFactory Customization for more details.)

Assume the following sections in the parameter file, Msdfmap.ini, located on the server:

[connect AuthorDataBase]
Access=ReadWrite
Connect="DSN=Pubs"
[sql AuthorById]
SQL="SELECT * FROM Authors WHERE au_id = ?"

Your code looks like the following. The command assigned to the SQL property will match the AuthorById identifier and will retrieve a row for author Michael O'Leary. Although the Connect property in your code specifies the Northwind data source, that data source will be overwritten by the Msdfmap.ini connect section. The DataControl object's Recordset property is assigned to a disconnected Recordset object purely as a coding convenience.

// BeginHandlerJ
import com.ms.wfc.data.*;
import com.ms.wfc.data.rds.*;
import java.io.* ;

public class HandlerX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      HandlerX();
      System.exit(0);
   }

   // HandlerX function

   static void HandlerX()
   {

      // Define ADO Objects.
      Recordset rstAuthors = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      int intCount = 0;
      int intDisplaysize = 15;

      try
      {
         IBindMgr dc = (IBindMgr) new DataControl();
         dc.setServer("srv");
         dc.setConnect("Data Source=Northwind");
         dc.setSQL("AuthorById(267-41-2394)");
         dc.Refresh();                  // Retrieve the record.
         // Use another recordset as a convenience.
         rstAuthors = (Recordset)dc.getRecordset();
         System.out.println("Author is '" +
                        rstAuthors.getField("au_fname").getString() +
                        " " +
                        rstAuthors.getField("au_lname").getString() +
                        "'");

         // Cleanup objects before exit.
         rstAuthors.close();
         System.out.println("\nPress <Enter> to continue..");
         in.readLine();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstAuthors != null)
         {
            PrintProviderError(rstAuthors.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
      catch(java.lang.UnsatisfiedLinkError e)
      {
         System.out.println("Exception: " + e.getMessage());
      }
      catch(java.lang.NullPointerException ne)
      {
         System.out.println(
         "Exception: Attempt to use null where an object is required.");
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndHandlerJ

See Also

DataControl Object (RDS) | Handler Property (RDS)


InternetTimeout Property Example (VJ++)

This example demonstrates the InternetTimeout property, which exists on the DataControl and DataSpace objects. In this case, the InternetTimout property is demonstrated on the DataControl object and the timeout is set to 20 seconds.

// BeginInternetTimeoutJ
// The WFC class includes the ADO objects.
import com.ms.wfc.data.*;
import com.ms.wfc.data.rds.*;
import java.io.* ;

public class InternetTimeoutX
{
   // The main entry point for the application.

   public static void main (String[] args)
   {
      InternetTimeoutX();
      System.exit(0);
   }

   // InternetTimeoutX function

   static void InternetTimeoutX()
   {

      // Define ADO Objects.
      Recordset rstAuthors = null;

      // Declarations.
      BufferedReader in =
         new BufferedReader (new InputStreamReader(System.in));
      int intCount = 0;
      int intDisplaysize = 15;

      try
      {
         IBindMgr dc = (IBindMgr) new DataControl();
       dc.setServer("http://a-iresmi2000");
         dc.setConnect("DSN=pubs");
         dc.setSQL("SELECT * FROM Authors");
         dc.setInternetTimeout(20000);   // Wait at least 20 seconds.
         dc.Refresh();
         rstAuthors = (Recordset)dc.getRecordset();
         while(!rstAuthors.getEOF())
         {
            System.out.println(rstAuthors.getField
               ("au_fname").getString() + " " +
               rstAuthors.getField("au_lname").getString());
            intCount++;
            if(intCount % intDisplaysize == 0)
            {
               System.out.println("\nPress <Enter> to continue..");
               in.readLine();
               intCount = 0;
            }
            rstAuthors.moveNext();
         }
         // Cleanup objects before exit.
         rstAuthors.close();
         System.out.println("\nPress <Enter> to continue..");
         in.readLine();
      }
      catch( AdoException ae )
      {
         // Notify user of any errors that result from ADO.

         // As passing a Recordset, check for null pointer first.
         if (rstAuthors != null)
         {
            PrintProviderError(rstAuthors.getActiveConnection());
         }
         else
         {
            System.out.println("Exception: " + ae.getMessage());
         }
      }

      // System read requires this catch.
      catch( java.io.IOException je)
      {
         PrintIOError(je);
      }
      catch(java.lang.UnsatisfiedLinkError e)
      {
         System.out.println("Exception: " + e.getMessage());
      }
      catch(java.lang.NullPointerException ne)
      {
         System.out.println(
         "Exception: Attempt to use null where an object is required.");
      }
   }

   // PrintProviderError Function

   static void PrintProviderError( Connection Cnn1 )
   {
      // Print Provider errors from Connection object.
      // ErrItem is an item object in the Connection’s Errors collection.
      com.ms.wfc.data.Error  ErrItem = null;
      long nCount = 0;
      int  i      = 0;

      nCount = Cnn1.getErrors().getCount();

      // If there are any errors in the collection, print them.
      if( nCount > 0);
      {
         // Collection ranges from 0 to nCount - 1
         for (i = 0; i< nCount; i++)
         {
            ErrItem = Cnn1.getErrors().getItem(i);
            System.out.println("\t Error number: " + ErrItem.getNumber()
               + "\t" + ErrItem.getDescription() );
         }
      }

   }

   // PrintIOError Function

   static void PrintIOError( java.io.IOException je)
   {
      System.out.println("Error \n");
      System.out.println("\tSource = " + je.getClass() + "\n");
      System.out.println("\tDescription = " + je.getMessage() + "\n");
   }
}
// EndInternetTimeoutJ

See Also

InternetTimeout Property (RDS)


ADO MD API Reference

ADO MD API Reference

This section of the ADO MD documentation contains topics for each ADO MD object, collection, method, and property, as well as example code when appropriate. For more information, search for a specific topic in the index or refer to the following topics:

  • ADO MD Objects
  • ADO MD Collections
  • ADO MD Properties
  • ADO MD Methods
  • ADO MD Enumerated Constants
  • ADO MD Examples

See Also

ADO MD Code Examples | ADO MD Collections | ADO MD Enumerated Constants | ADO MD Methods | ADO MD Object Model | ADO MD Objects | ADO MD Properties


ADO MD Object Model

The following diagram shows how these objects are represented and related in ADO MD.

The Axis and Cell objects each have a Positions collection.

The Level and Position objects each have a Members collection.

The Axis, Cell, Cellset, CubeDef, Dimension, Hierarchy, Level, and Member objects each have a standard ADO Properties collection.

See Also

ADO MD API Reference | ADO MD Code Examples | ADO MD Collections | ADO MD Enumerated Constants | ADO MD Methods | ADO MD Objects | ADO MD Properties | Microsoft ADO MD Programmer's Reference | Overview of Multidimensional Schemas and Data | Working with Multidimensional Data


ADO MD Objects

Axis | Represents a positional or filter axis of a cellset, containing selected members of one or more dimensions.
Catalog | Contains multidimensional schema information (that is, cubes and underlying dimensions, hierarchies, levels, and members) specific to a multidimensional data provider (MDP).
Cell | Represents the data at the intersection of axis coordinates, contained in a cellset.
Cellset | Represents the results of a multidimensional query. It is a collection of cells selected from cubes or other cellsets.
CubeDef | Represents a cube from a multidimensional schema, containing a set of related dimensions.
Dimension | Represents one of the dimensions of a multidimensional cube, containing one or more hierarchies of members.
Hierarchy | Represents one way in which the members of a dimension can be aggregated or "rolled up." A dimension can be aggregated along one or more hierarchies.
Level | Contains a set of members, each of which has the same rank within a hierarchy.
Member | Represents a member of a level in a cube, the children of a member of a level, or a member of a position along an axis of a cellset.
Position | Represents a set of one or more members of different dimensions that defines a point along an axis.

Also, the Catalog object is connected to an ADO Connection object, which is included with the standard ADO library:

Object | Description
Connection | Represents an open connection to a data source.

The relationships between these objects are illustrated in the ADO MD Object Model.

Many ADO MD objects can be contained in a corresponding collection. For example, a CubeDef object can be contained in a CubeDefs collection of a Catalog. For more information, see ADO MD Collections.

See Also

ADO MD API Reference | ADO MD Code Examples | ADO MD Collections | ADO MD Enumerated Constants | ADO MD Methods | ADO MD Object Model | ADO MD Properties


Axis Object (ADO MD)

ADO MD API Reference

Axis Object

Represents a positional or filter axis of a cellset, containing selected members of one or more dimensions.

Remarks

An Axis object can be contained by an Axes collection, or returned by the FilterAxis property of a Cellset.

With the collections and properties of an Axis object, you can do the following:

  • Identify the Axis with the Name property.
  • Iterate through each position along an Axis using the Positions collection.
  • Obtain the number of dimensions on the Axis with the DimensionCount property.
  • Obtain provider-specific attributes of the Axis with the standard ADO Properties collection.

See Also

VBScript Example


Properties, Methods, and Events

ADO MD API Reference

Axis Object Properties, Methods, and Events

Properties/Collections

DimensionCount Property

Name Property

Positions Collection

Properties Collection

Methods

None.

Events

None.

See Also

Applies To: Axis Object


Catalog Object (ADO MD)

ADO MD API Reference

Catalog Object

Contains multidimensional schema information (that is, cubes and underlying dimensions, hierarchies, levels, and members) specific to a multidimensional data provider (MDP).

Remarks

With the collections and properties of a Catalog object, you can do the following:

  • Open the catalog by setting the ActiveConnection property to a standard ADO Connection object or to a valid connection string.
  • Identify the Catalog with the Name property.
  • Iterate through the cubes in a catalog using the CubeDefs collection.

See Also

Visual Basic Example


Properties, Methods, and Events

ADO MD API Reference

Catalog Object Properties, Methods, and Events

Properties/Collections

ActiveConnection Property

CubeDefs Collection

Name Property

Methods

None.

Events

None.

See Also

Applies To: Catalog Object


Cell Object (ADO MD)

ADO MD API Reference

Cell Object

Represents the data at the intersection of axis coordinates contained in a cellset.

Remarks

A Cell object is returned by the Item property of a Cellset object.

With the collections and properties of a Cell object, you can do the following:

  • Return the data in the Cell with the Value property.
  • Return the string representing the formatted display of the Value property with the FormattedValue property.
  • Return the ordinal value of the Cell within the Cellset with the Ordinal property.
  • Determine the position of the Cell within the CubeDef with the Positions collection.
  • Retrieve other information about the Cell with the standard ADO Properties collection.

The Properties collection contains provider-supplied properties. The following table lists properties that might be available. The actual property list may differ depending upon the implementation of the provider. See the documentation for your provider for a more complete list of available properties.

Name | Description
BackColor | Background color used when displaying the cell.
FontFlags | Bitmask detailing effects on the font.
FontName | Font used to display the cell value.
FontSize | Font size used to display the cell value.
ForeColor | Foreground color used when displaying the cell.
FormatString | Value in a formatted string.

See Also

VBScript Example


Properties, Methods, and Events

ADO MD API Reference

Cell Object Properties, Methods, and Events

Properties/Collections

FormattedValue Property

Ordinal Property (Cell)

Positions Collection

Properties Collection

Value Property

Methods

None.

Events

None.

See Also

Applies To: Cell Object


Cellset Object (ADO MD)

ADO MD API Reference

Cellset Object

Represents the results of a multidimensional query. It is a collection of cells selected from cubes or other cellsets.

Remarks

Data within a Cellset is retrieved using direct, array-like access. You can "drill down" to a specific member to obtain data about that member. For example, the following code returns the caption of the first member in the first position on the first axis of a cellset named cst:

cst.Axes(0).Positions(0).Members(0).Caption

There is no notion of a current cell within a cellset. Instead, the Item property retrieves a specific Cell object from the cellset. The arguments of the Item property determine which cell is retrieved. You can specify the unique ordinal value of a cell. You can also retrieve cells by using their position numbers along each axis of the cellset. For more information about retrieving cells, see the Item property.

With the collections, methods, and properties of a Cellset object, you can do the following:

  • Associate an open connection with a Cellset object by setting its ActiveConnection property.
  • Execute and retrieve the results of a multidimensional query with the Open method.
  • Retrieve a Cell from the Cellset with the Item property.
  • Return the Axis objects that define the Cellset with the Axes collection.
  • Retrieve information about the dimensions used to filter the data in the Cellset with the FilterAxis property.
  • Return or specify the query used to define the Cellset with the Source property.
  • Return the current state of the Cellset (open, closed, executing, or connecting) with the State property.
  • Close an open Cellset with the Close method.
  • Retrieve provider-specific information about the Cellset with the standard ADO Properties collection.

See Also

Visual Basic Example


Properties, Methods, and Events

ADO MD API Reference

Cellset Object Properties, Methods, and Events

Properties/Collections

ActiveConnection Property

Axes Collection

FilterAxis Property

Item Property (Cellset)

Properties Collection

Source Property

State Property

Methods

Close Method

Open Method

Events

None.

See Also

Applies To: Cellset Object


CubeDef Object (ADO MD)

ADO MD API Reference

CubeDef Object

Represents a cube from a multidimensional schema, containing a set of related dimensions.

Remarks

With the collections and properties of a CubeDef object, you can do the following:

  • Identify a CubeDef with the Name property.
  • Return a string that describes the cube with the Description property.
  • Return the dimensions that make up the cube with the Dimensions collection.
  • Obtain additional information about the CubeDef with the standard ADO Properties collection.

The Properties collection contains provider-supplied properties. The following table lists properties that might be available. The actual property list may differ depending upon the implementation of the provider. See the documentation for your provider for a more complete list of available properties.

Name | Description
CatalogName | The name of the catalog to which this cube belongs.
CreatedOn | Date and time of cube creation.
CubeGUID | Cube GUID.
CubeName | The name of the cube.
CubeType | The type of the cube.
DataUpdatedBy | User ID of the person doing the last data update.
Description | A meaningful description of the cube.
LastSchemaUpdate | Date and time of last schema update.
SchemaName | The name of the schema to which this cube belongs.
SchemaUpdatedBy | User ID of the person doing the last schema update.

See Also

VBScript Example


Properties, Methods, and Events

ADO MD API Reference

CubeDef Object Properties, Methods, and Events

Properties/Collections

Description Property

Dimensions Collection

Name Property

Properties Collection

Methods

GetSchemaObject Method

Events

None.

See Also

Applies To: CubeDef Object


Dimension Object (ADO MD)

ADO MD API Reference

Dimension Object

Represents one of the dimensions of a multidimensional cube, containing one or more hierarchies of members.

Remarks

With the collections and properties of a Dimension object, you can do the following:

  • Identify the Dimension with the Name and UniqueName properties.
  • Return a meaningful string that describes the Dimension with the Description property.
  • Return the Hierarchy objects that make up the Dimension with the Hierarchies collection.
  • Use the standard ADO Properties collection to obtain additional information about the Dimension object.

The Properties collection contains provider-supplied properties. The following table lists properties that might be available. The actual property list may differ depending upon the implementation of the provider. See the documentation for your provider for a more complete list of available properties.

Name | Description
CatalogName | The name of the catalog to which this cube belongs.
CubeName | The name of the cube.
DefaultHierarchy | The unique name of the default hierarchy.
Description | A meaningful description of the cube.
DimensionCaption | A label or caption associated with the dimension.
DimensionCardinality | The number of members in the dimension.
DimensionGUID | The GUID of the dimension.
DimensionName | The name of the dimension.
DimensionOrdinal | The ordinal number of the dimension among the group of dimensions that form the cube.
DimensionType | The dimension type.
DimensionUniqueName | The unambiguous name of the dimension.
SchemaName | The name of the schema to which this cube belongs.

See Also

VBScript Example


Properties, Methods, and Events

ADO MD API Reference

Dimension Object Properties, Methods, and Events

Properties/Collections

Description Property

Hierarchies Collection

Name Property

Properties Collection

UniqueName Property

Methods

None.

Events

None.

See Also

Applies To: Dimension Object


Hierarchy Object (ADO MD)

ADO MD API Reference

Hierarchy Object

Represents one way in which the members of a dimension can be aggregated or "rolled up." A dimension can be aggregated along one or more hierarchhies.

Remarks

With the collections and properties of a Hierarchy object, you can do the following:

  • Identify the Hierarchy with the Name and UniqueName properties.
  • Return a meaningful string that describes the Hierarchy with the Description property.
  • Return the Level objects that make up the Hierarchy with the Levels collection.
  • Use the standard ADO Properties collection to obtain additional information about the Hierarchy object.

The Properties collection contains provider-supplied properties. The following table lists properties that might be available. The actual property list may differ depending upon the implementation of the provider. See the documentation for your provider for a more complete list of available properties.

Name | Description
AllMember | The member at the highest level of rollup in the hierarchy.
CatalogName | The name of the catalog to which this cube belongs.
CubeName | The name of the cube.
DefaultMember | The unique name of the default member for this hierarchy.
Description | A meaningful description of the hierarchy.
DimensionType | The type of dimension to which this hierarchy belongs.
DimensionUniqueName | The unambiguous name of the dimension.
HierarchyCaption | A label or caption associated with the hierarchy.
HierarchyCardinality | The number of members in the hierarchy.
HierarchyGUID | The GUID of the hierarchy.
HierarchyName | The name of the hierarchy.
HierarchyUniqueName | The unambiguous name of the hierarchy.
SchemaName | The name of the schema to which this cube belongs.

See Also

VBScript Example


Properties, Methods, and Events

ADO MD API Reference

Hierarchy Object Properties, Methods, and Events

Properties/Collections

Description Property

Levels Collection

Name Property

Properties Collection

UniqueName Property

Methods

None.

Events

None.

See Also

Applies To: Hierarchy Object


Level Object (ADO MD)

ADO MD API Reference

Level Object

Contains a set of members, each of which has the same rank within a hierarchy.

Remarks

With the collections and properties of a Level object, you can do the following:

  • Identify the Level with the Name and UniqueName properties.
  • Return a string to use when displaying the Level with the Caption property.
  • Return a meaningful string that describes the Level with the Description property.
  • Return the Member objects that make up the Level with the Members collection.
  • Return the number of levels from the root of the Level with the Depth property.
  • Use the standard ADO Properties collection to obtain additional information about the Level object.

The Properties collection contains provider-supplied properties. The following table lists properties that might be available. The actual property list may differ depending upon the implementation of the provider. See the documentation for your provider for a more complete list of available properties.

Name | Description
CatalogName | The name of the catalog to which this cube belongs.
CubeName | The name of the cube.
Description | A meaningful description of the level.
DimensionUniqueName | The unambiguous name of the dimension.
HierarchyUniqueName | The unambiguous name of the hierarchy.
LevelCaption | A label or caption associated with the level.
LevelCardinality | The number of members in the level.
LevelGUID | The GUID of the level.
LevelName | Name of the level.
LevelNumber | The distance between the level and the root of the hierarchy.
LevelType | The type of level.
LevelUniqueName | The unambiguous name of the level.
SchemaName | The name of the schema to which this cube belongs.

See Also

VBScript Example


Properties, Methods, and Events

ADO MD API Reference

Level Object Properties, Methods, and Events

Properties/Collections

Caption Property

Depth Property

Description Property

Members Collection

Name Property

Properties Collection

UniqueName Property

Methods

None.

Events

None.

See Also

Applies To: Level Object


Member Object (ADO MD)

ADO MD API Reference

Member Object

Represents a member of a level in a cube, the children of a member of a level, or a member of a position along an axis of a cellset.

Remarks

The properties of a Member differ depending on the context in which it is used. A Member of a Level in a CubeDef has a Children property that returns the Members on the next lower level in the hierarchy from the current Member. For a Member of a Position, the Children collection is always empty. Also, the Type property applies only to Members of a Level.

A Member of Position has two properties—DrilledDown and ParentSameAsPrev—that are useful when displaying the Cellset. An error will occur if these properties are accessed on a Member of a Level.

With the collections and properties of a Member object of a Level, you can do the following:

  • Identify the Member with the Name and UniqueName properties.
  • Return a string to use when displaying the Member with the Caption property.
  • Return a meaningful string that describes a measure or formula Member with the Description property.
  • Determine the nature of the Member with the Type property.
  • Obtain information about the Level of the Member with the LevelDepth and LevelName properties.
  • Obtain related Members in a Hierarchy with the Parent and Children properties.
  • Count the children of a Member with the ChildCount property.
  • Use the standard ADO Properties collection to obtain additional information about the Level object.

With the collections and properties of a Member of a Position along an Axis, you can do the following:

  • Identify the Member with the Name and UniqueName properties.
  • Return a string to use when displaying the Member with the Caption property.
  • Return a meaningful string that describes a measure or formula Member with the Description property.
  • Obtain information about the Level of the Member with the LevelDepth and LevelName properties.
  • Count the children of a Member with the ChildCount property.
  • Use the DrilledDown property to determine whether there is at least one child on the Axis immediately following this Member.
  • Use the ParentSameAsPrev property to determine whether the parent of this Member is the same as the parent of the immediately preceding Member.
  • Use the standard ADO Properties collection to obtain additional information about the Level object.

The Properties collection contains provider-supplied properties. The following table lists properties that might be available. The actual property list may differ depending upon the implementation of the provider. See the documentation for your provider for a more complete list of available properties.

Name | Description
CatalogName | The name of the catalog to which this cube belongs.
ChildrenCardinality | The number of children that the member has.
CubeName | The name of the cube.
Description | A meaningful description of the member.
DimensionUniqueName | The unambiguous name of the dimension.
HierarchyUniqueName | The unambiguous name of the hierarchy.
LevelNumber | The distance between the level and the root of the hierarchy.
LevelUniqueName | The unambiguous name of the level.
MemberCaption | A label or caption associated with the member.
MemberGUID | The GUID of the member.
MemberName | The name of the member.
MemberOrdinal | The ordinal number of the member.
MemberType | The type of the member.
MemberUniqueName | The unambiguous name of the member.
ParentCount | The count of the number of parents that this member has.
ParentLevel | The level number of the member's parent.
ParentUniqueName | The unambiguous name of the member's parent.
SchemaName | The name of the schema to which this cube belongs.

See Also

Visual Basic Example


Properties, Methods, and Events

ADO MD API Reference

Member Object Properties, Methods, and Events

Properties/Collections

Caption Property

ChildCount Property

Children Property

Description Property

DrilledDown Property

LevelDepth Property

LevelName Property

Name Property

Parent Property

ParentSameAsPrev Property

Properties Collection

Type Property

UniqueName Property

Methods

None.

Events

None.

See Also

Applies To: Member Object


Position Object (ADO MD)

ADO MD API Reference

Position Object

Represents a set of one or more members of different dimensions that defines a point along an axis.

Remarks

With the properties and collections of a Position object you can do the following:

  • Use the Ordinal property to return the ordinal position of the Position along the Axis.
  • Use the Members collection to return the members that make up the position along the Axis.

See Also

VBScript Example


Properties, Methods, and Events

ADO MD API Reference

Position Object Properties, Methods, and Events

Properties/Collections

Members Collection

Ordinal Property (Position)

Methods

None.

Events

None.

See Also

Applies To: Position Object


ADO MD Collections

Axes | Contains the Axis objects that define a cellset.
CubeDefs | Contains the CubeDef objects that represent a cube from a multidimensional catalog.
Dimensions | Contains the Dimension objects that make up a cube.
Hierarchies | Contains the set Hierarchy objects from a dimension.
Levels | Contains the Level objects that make up a hierarchy.
Members | Contains the Member objects from a level or a position along an axis.
Positions | Contains the Position objects that define a point on an axis.

See Also

ADO MD API Reference | ADO MD Code Examples | ADO MD Enumerated Constants | ADO MD Methods | ADO MD Object Model | ADO MD Objects | ADO MD Properties


Axes Collection (ADO MD)

ADO MD API Reference

Axes Collection

Contains the Axis objects that define a cellset.

Remarks

A Cellset object contains an Axes collection. Once the Cellset is opened, this collection will contain at least one Axis. See the Axis object for a more detailed explanation of how to use Axis objects.

Note The filter axis of a Cellset is not contained in the Axes collection. See the FilterAxis property for more information.

Axes is a standard ADO collection. With the properties and methods of a collection, you can do the following:

  • Obtain the number of objects in the collection with the Count property.
  • Return an object from the collection with the default Item property.
  • Update the objects in the collection from the provider with the Refresh method.

See Also

Visual Basic Example


Axes Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Refresh Method

Events

None.

See Also

Applies To: Axes Collection


CubeDefs Collection (ADO MD)

ADO MD API Reference

CubeDefs Collection

Contains the CubeDef objects that represent a cube from a multidimensional catalog.

Remarks

CubeDefs is a standard ADO collection. With the properties and methods of a collection, you can do the following:

  • Obtain the number of objects in the collection with the Count property.
  • Return an object from the collection with the default Item property.
  • Update the objects in the collection from the provider with the Refresh method.

See Also

Visual Basic Example


CubeDefs Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Refresh Method

Events

None.

See Also

Applies To: CubeDefs Collection


Dimensions Collection (ADO MD)

ADO MD API Reference

Dimensions Collection

Contains the Dimension objects that make up a cube.

Remarks

Dimensions is a standard ADO collection. With the properties and methods of a collection, you can do the following:

  • Obtain the number of objects in the collection with the Count property.
  • Return an object from the collection with the default Item property.
  • Update the objects in the collection from the provider with the Refresh method.

See Also

Visual Basic Example


Dimensions Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Refresh Method

Events

None.

See Also

Applies To: Dimensions Collection


Hierarchies Collection (ADO MD)

ADO MD API Reference

Hierarchies Collection

Contains the set Hierarchy objects from a dimension.

Remarks

Hierarchies is a standard ADO collection. With the properties and methods of a collection, you can do the following:

  • Obtain the number of objects in the collection with the Count property.
  • Return an object from the collection with the default Item property.
  • Update the objects in the collection from the provider with the Refresh method.

See Also

Visual Basic Example


Hierarchies Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Refresh Method

Events

None.

See Also

Applies To: Hierarchies Collection


Levels Collection (ADO MD)

ADO MD API Reference

Levels Collection

Contains the Level objects that make up a hierarchy.

Remarks

Levels is a standard ADO collection. With the properties and methods of a collection, you can do the following:

  • Obtain the number of objects in the collection with the Count property.
  • Return an object from the collection with the default Item property.
  • Update the objects in the collection from the provider with the Refresh method.

See Also

Visual Basic Example


Levels Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Refresh Method

Events

None.

See Also

Applies To: Levels Collection


Members Collection (ADO MD)

ADO MD API Reference

Members Collection

Contains the Member objects from a level or a position along an axis.

Remarks

A Members collection is used to contain the following types of members:

  • The members that make up a level in a cube. These are contained in the Members collection of a Level object. For example, using the sample from Overview of Multidimensional Schemas and Data, the four members of the Countries level are Canada, USA, UK, and Germany.
  • The members that are the children of a specific member within a hierarchy. These members are returned by the Children property of the parent Member object. For example, again using the same sample, the two children of the Canada member are Canada-East and Canada-West.
  • The members that define a specific position along an axis of a cellset. Using the cellset from Working with Multidimensional Data as an example, the two members of the first position on the x-axis are Valentine and Seattle. These members are contained by the Members collection of a Position object.

Members is a standard ADO collection. With the properties and methods of a collection, you can do the following:

  • Obtain the number of objects in the collection with the Count property.
  • Return an object from the collection with the default Item property.
  • Update the objects in the collection from the provider with the Refresh method.

See Also

VBScript Example


Members Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Refresh Method

Events

None.

See Also

Applies To: Members Collection


Positions Collection (ADO MD)

ADO MD API Reference

Positions Collection

Contains the Position objects that define a point on an axis.

Remarks

Positions is a standard ADO collection. With the properties and methods of a collection, you can do the following:

  • Obtain the number of objects in the collection with the Count property.
  • Return an object from the collection with the default Item property.
  • Update the objects in the collection from the provider with the Refresh method.

See Also

Visual Basic Example


Positions Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Refresh Method

Events

None.

See Also

Applies To: Positions Collection


ADO MD Properties

ActiveConnection | Indicates to which ADO Connection object the current cellset or catalog currently belongs.
Caption | Indicates the text caption to use when displaying a Level or Member object.
ChildCount | Indicates the number of members for which the current Member object is the parent in a hierarchy.
Children | Returns a collection of Members for which the current Member is the parent in the hierarchy.
Count | Indicates the number of objects in a collection.
Depth | Indicates the number of levels between the Level and the root of the hierarchy level.
Description | Returns a text explanation of the current object.
DimensionCount | Indicates the number of dimensions on an axis.
DrilledDown | Indicates whether no children immediately follow the member on the axis.
FilterAxis | Indicates filter information for the current cellset.
Item | Retrieves a cell from a cellset using its coordinates.
Item | Returns a specific member of a collection by name or ordinal number.
FormattedValue | Indicates the formatted display of a cell value.
LevelDepth | Indicates the number of levels between the root of the hierarchy and a member.
LevelName | Indicates the name of the level of a member.
Name | Indicates the name of an object.
Ordinal (Cell) | Uniquely identifies a cell by its position within a cellset.
Ordinal (Position) | Uniquely identifies a position along an axis.
Parent | Indicates the member that is the parent of the current member in a hierarchy.
ParentSameAsPrev | Indicates whether the parent of this position member is the same as the parent of the immediately preceding member.
Source | Indicates the source for the data in the cellset.
State | Indicates the current state of the cellset.
Type | Indicates the type of the current member.
UniqueName | Indicates an unambiguous name for the current object.
Value | Indicates the value of the current cell.

See Also

ADO MD API Reference | ADO MD Code Examples | ADO MD Collections | ADO MD Enumerated Constants | ADO MD Methods | ADO MD Object Model | ADO MD Objects


ActiveConnection Property (ADO MD)

ADO MD API Reference

ActiveConnection Property

Indicates to which ADO Connection object the current cellset or catalog currently belongs.

Settings and Return Values

Sets or returns a Variant that contains a string defining a connection or Connection object. The default is empty.

Remarks

You can set this property to a valid ADO Connection object or to a valid connection string. When this property is set to a connection string, the provider creates a new Connection object using this definition and opens the connection.

If you use the ActiveConnection argument of the Open method to open a Cellset object, the ActiveConnection property will inherit the value of the argument.

Setting the ActiveConnection property of a Catalog object to Nothing releases the associated data, including data in the CubeDefs collection and any related Dimension, Hierarchy, Level, and Member objects. Closing a Connection object that was used to open a Catalog has the same effect as setting the ActiveConnection property to Nothing.

An error will occur if you attempt to change the ActiveConnection property for an open Cellset object.

Note In Visual Basic, remember to use the Set keyword when setting the ActiveConnection property to a Connection object. If you omit the Set keyword, you will actually be setting the ActiveConnection property equal to the Connection object's default property, ConnectionString. The code will work; however, you will create an additional connection to the data source, which may have negative performance implications.

When using the MSOLAP data provider, set the data source in a connection string to a server name and set the initial catalog to the name of a catalog from the data source. To connect to a cube file that is disconnected from a server, set the location to the full path to the .CUB file. In either case, set the provider to the provider name. For example, the following string connects to a catalog named Bobs Video Store on a server named Servername with the MSOLAP Provider:

"Data Source=Servername;Initial Catalog=Bobs Video Store;Provider=msolap"

The following string connects to a local cube file at the location C:\MSDASDK\samples\oledb\olap\data\bobsvid.cub:

"Location=C:\MSDASDK\samples\oledb\olap\data\bobsvid.cub;Provider=msolap"

See Also

Visual Basic Example

Connection Object | Open Method

Applies To: Catalog Object | Cellset Object


Caption Property (ADO MD)

ADO MD API Reference

Caption Property

Indicates the text caption to use when displaying a Level or Member object.

Return Values

Returns a String and is read-only.

See Also

Visual Basic Example

Description Property

Applies To: Level Object | Member Object


ChildCount Property (ADO MD)

ADO MD API Reference

ChildCount Property

Indicates the number of members for which the current Member object is the parent in a hierarchy.

Return Values

Returns a Long integer and is read-only.

Remarks

Use the ChildCount property to return an estimate of how many children a Member has. The actual children of a Member can be returned by the Children property.

For Member objects from a Position object, the maximum number returned is 65536. If the actual number of children exceeds 65536, the value returned will still be 65536. Therefore, the application should interpret a ChildCount of 65536 as equal to or greater than 65536 children.

For Member objects from a Level object, use the ADO collection Count property on the Children collection to determine the exact number of children. Determining the exact number of children may be slow if the number of children in the collection is large.

See Also

Children Property | Count Property | Members Collection

Applies To: Member Object


Children Property (ADO MD)

ADO MD API Reference

Children Property

Returns a Members collection for which the current Member is the parent in the hierarchy.

Return Values

Returns a Members collection and is read-only.

Remarks

The Children property contains a Members collection for which the current Member is the hierarchical parent. Leaf level Member objects have no child members in the Members collection. This property is only supported on Member objects belonging to a Level object. An error occurs when this property is referenced from Member objects belonging to a Position object.

See Also

ChildCount Property

Applies To: Member Object


Depth Property (ADO MD)

ADO MD API Reference

Depth Property

Indicates the number of levels between the Level and the root of the hierarchy.

Return Values

Returns an integer, and is read-only.

Remarks

A Level at the root of a hierarchy has a Depth value of zero (0).

See Also

LevelDepth Property

Applies To: Level Object


Description Property (ADO MD)

ADO MD API Reference

Description Property

Returns a text explanation of the current object.

Return Values

Returns a String and is read-only.

Remarks

For Member objects, Description applies only to measure and formula members. Description returns an empty string ("") for all other types of members. For more information about the various types of members, see the Type property.

This property is only supported on Member objects belonging to a Level object. An error occurs when this property is referenced from Member objects belonging to a Position object.

See Also

Applies To: CubeDef Object | Dimension Object | Hierarchy Object | Level Object | Member Object


DimensionCount Property (ADO MD)

ADO MD API Reference

DimensionCount Property

Indicates the number of dimensions on an axis.

Return Values

Returns a Long integer, and is read-only.

See Also

VBScript Example

Dimension Object

Applies To: Axis Object


DrilledDown Property (ADO MD)

ADO MD API Reference

DrilledDown Property

Indicates whether no children immediately follow the member on the axis.

Return Values

Returns a Boolean value and is read-only. DrilledDown returns True if there are no child members of the current member on the axis. DrilledDown returns False if there is one or more child members of the current member on the axis.

Remarks

Use the DrilledDown property to determine whether there is at least one child of this member on the axis immediately following this member. This information is useful when displaying the member.

This property is only supported on Member objects belonging to a Position object. An error occurs when this property is referenced from Member objects belonging to a Level object.

See Also

ParentSameAsPrev Property

Applies To: Member Object


FilterAxis Property (ADO MD)

ADO MD API Reference

FilterAxis Property

Indicates filter information about the current cellset.

Return Values

Returns an Axis object, and is read-only.

Remarks

Use the FilterAxis property to return information about the dimensions that were used to slice the data. The DimensionCount property of the Axis returns the number of slicer dimensions. This axis usually has just one row.

The Axis returned by FilterAxis is not contained in the Axes collection for a Cellset object.

See Also

Axis Object | Dimension Object | DimensionCount Property

Applies To: Cellset Object


FormattedValue Property (ADO MD)

ADO MD API Reference

FormattedValue Property

Indicates the formatted display of a cell value.

Return Values

Returns a String and is read-only.

Remarks

Use the FormattedValue property to obtain the formatted display value of the Value property of a Cell object. For example, if the value of a cell was 1056.87, and this value represented a dollar amount, FormattedValue would be $1,056.87.

See Also

Visual Basic Example

Value Property

Applies To: Cell Object


Item Property (ADO MD Cellset)

ADO MD API Reference

Item Property (Cellset)

Retrieves a cell from a cellset using its coordinates.

Syntax

Set Cell = Cellset.Item ( Positions )

Parameters

Positions
A Variant Array of values that uniquely specify a cell. Positions can be one of the following:
  • An array of position numbers
  • An array of member names
  • The ordinal position

Remarks

Use the Item property to return a Cell object within a Cellset object. If the Item property cannot find the cell corresponding to the Positions argument, an error occurs.

The Item property is the default property for the Cellset object. The following syntax forms are interchangeable:

Cellset.Item ( Positions )
Cellset ( Positions )

The Positions argument specifies which cell to return. You can specify the cell by ordinal position or by the position along each axis. When specifying the cell by position along each axis, you can specify the numeric value of the position or the names of the members for each position.

The ordinal position is a number that uniquely identifies one cell within the Cellset. Conceptually, cells are numbered in a Cellset as if the Cellset 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:

If member names are passed as strings to Item, the members must be listed in increasing order of the numeric axis identifiers. Within an axis, the members must be listed in increasing order of dimension nesting — that is, the outermost dimension's member comes first, followed by members of inner dimensions. Each dimension should be represented by a separate string, and the list of member strings should be separated by commas.

Note Retrieving cells by member name may not be supported by your data provider. See the documentation for your provider for more information.

See Also

Cell Object

Applies To: Cellset Object


LevelDepth Property (ADO MD)

ADO MD API Reference

LevelDepth Property

Indicates the number of levels between the root of the hierarchy and a member.

Return Values

Returns a Long integer, and is read-only.

Remarks

Use the LevelDepth property to determine the distance of the Member object from the root level of the hierarchy. The LevelDepth of a member at the root level is 0. This corresponds to the Depth property of a Level object.

See Also

Depth Property | Level Object

Applies To: Member Object


LevelName Property (ADO MD)

ADO MD API Reference

LevelName Property

Indicates the name of the level of a member.

Return Values

Returns a String and is read-only.

Remarks

Use the LevelName property to retrieve the name of the level to which a member belongs. This corresponds to the Name property of a Level object.

See Also

Level Object | Name Property

Applies To: Member Object


Name Property (ADO MD)

ADO MD API Reference

Name Property

Indicates the name of an object.

Return Values

Returns a String and is read-only.

Remarks

You can retrieve the Name property of an object by an ordinal reference, after which you can refer to the object directly by name. For example, if cdf.CubeDefs(0).Name yields "Bobs Video Store", you can refer to this CubeDef as cdf.CubeDefs("Bobs Video Store").

See Also

Visual Basic Example

Caption Property | Description Property | UniqueName Property

Applies To: Axis Object | Catalog Object | CubeDef Object | Dimension Object | Hierarchy Object | Level Object | Member Object


Ordinal Property (ADO MD Cell)

ADO MD API Reference

Ordinal Property (Cell)

Uniquely identifies a cell by its position within a cellset.

Return Values

Returns a Long integer and is read-only.

Remarks

The cell's ordinal value uniquely identifies the cell within a cellset. Conceptually, cells are numbered in a cellset as if the cellset were a p-dimensional array, where p is the number of axes. Cells are numbered starting from zero in row-major order. Here is the formula for calculating the ordinal number of a cell:

The cell's ordinal value can be used with the Item property of the Cellset object to quickly retrieve the Cell.

See Also

VBScript Example

Cellset Object | Item Property (Cellset) | Ordinal Property (Position)

Applies To: Cell Object


Ordinal Property (ADO MD Position)

ADO MD API Reference

Ordinal Property (Position)

Uniquely identifies a position along an axis.

Return Values

Returns a Long integer and is read-only.

Remarks

The Ordinal of a Position object corresponds to the index of the Position within the Positions collection.

A cell can quickly be retrieved using the Ordinal of the Position along each axis with the Item property of the Cellset object.

See Also

Cellset Object | Item Property (Cellset) | Ordinal Property (Cell)

Applies To: Position Object


Parent Property (ADO MD)

ADO MD API Reference

Parent Property

Indicates the member that is the parent of the current member in a hierarchy.

Return Values

Returns a Member object and is read-only.

Remarks

A member that is at the top level of a hierarchy (the root) has no parent. This property is supported only on Member objects belonging to a Level object. An error occurs when this property is referenced from Member objects belonging to a Position object.

See Also

Children Property

Applies To: Member Object


ParentSameAsPrev Property (ADO MD)

ADO MD API Reference

ParentSameAsPrev Property

Indicates whether the parent of this position member is the same as the parent of the immediately preceding member.

Return Values

Returns a Boolean value and is read-only.

Remarks

This property is supported only on Member objects belonging to a Position object. An error occurs when this property is referenced from Member objects belonging to a Level object.

See Also

DrilledDown Property

Applies To: Member Object


Source Property (ADO MD)

ADO MD API Reference

Source Property

Indicates the source for the data in the cellset.

Settings and Return Values

Sets or returns a Variant, and is read/write for closed Cellset objects and read-only for open Cellset objects. The Variant should contain a valid String, for example, an MDX query.

See Also

Visual Basic Example

ActiveConnection Property | Open Method

Applies To: Cellset Object


State Property (ADO MD)

ADO MD API Reference

State Property

Indicates the current state of the cellset.

Return Values

Returns a Long integer indicating the current condition of the Cellset object and is read-only. The following values are valid: adStateClosed (0) and adStateOpen (1).

Remarks

To use the ObjectStateEnum constant names, you must have the ADO type library referenced in your project. See Using ADO with ADO MD for more information.

See Also

Close Method | Open Method

Applies To: Cellset Object


Type Property (ADO MD)

ADO MD API Reference

Type Property

Indicates the type of the current member.

Return Values

Returns a MemberTypeEnum value and is read-only.

Remarks

This property is supported only on Member objects belonging to a Level object. An error occurs when this property is referenced from Member objects belonging to a Position object.

See Also

Applies To: Member Object


UniqueName Property (ADO MD)

ADO MD API Reference

UniqueName Property

Indicates an unambiguous name for the current object.

Return Values

Returns a String and is read-only.

See Also

Name Property

Applies To: Dimension Object | Hierarchy Object | Level Object | Member Object


Value Property (ADO MD)

ADO MD API Reference

Value Property

Indicates the value of the current cell.

Return Values

Returns a Variant and is read-only.

See Also

FormattedValue Property

Applies To: Cell Object


ADO MD Methods

Close | Closes an open cellset.
GetSchema | Retrieves an ADO MD schema object (Dimension, Hierarchy, Level, or Member) by its UniqueName.
Open | Retrieves the results of a multidimensional query and returns the results to a cellset.

See Also

ADO MD API Reference | ADO MD Code Examples | ADO MD Collections | ADO MD Enumerated Constants | ADO MD Object Model | ADO MD Objects | ADO MD Properties


Close Method (ADO MD)

ADO MD API Reference

Close Method

Closes an open cellset.

Syntax

Cellset.Close

Remarks

Using the Close method to close a Cellset object will release the associated data, including data in any related Cell, Axis, Position, or Member objects. Closing a Cellset does not remove it from memory; you can change its property settings and open it again later. To completely eliminate an object from memory, set the object variable to Nothing.

You can later call the Open method to reopen the Cellset using the same or another source string. While the Cellset object is closed, retrieving any properties or calling any methods that reference the underlying data or metadata generates an error.

See Also

Axis Object | Cell Object | Member Object | Open Method | Position Object | State Property

Applies To: Cellset Object


GetSchemaObject Method

Retrieves an ADO MD schema object (Dimension, Hierarchy, Level, or Member) by its UniqueName.

Syntax

Set object = CubeDef.GetSchemaObject (ObjType, UniqueName)

Parameters

ObjType
An ObjectTypeEnum value specifying what type of schema object (Dimension, Hierarchy, Level, or Member) to retrieve.
UniqueName
A string specifying the UniqueName property value of the object to retrieve.

Remarks

GetSchemaObject retireves objects using their unique names, as specified by the UniqueName property. The names of parent objects do not need to be known, and parent collections do not need to be populated to retrieve a schema object.

See Also

Applies To: CubeDef Object


Open Method (ADO MD)

ADO MD API Reference

Open Method

Retrieves the results of a multidimensional query and returns the results to a cellset.

Syntax

Cellset.Open Source, ActiveConnection

Parameters

Source
Optional. A Variant that evaluates to a valid multidimensional query, such as a Multidimensional Expression (MDX) query. The Source argument corresponds to the Source property. For more information about MDX, see the OLE DB for OLAP documentation in the Microsoft Data Access Components SDK.
ActiveConnection
Optional. A Variant that evaluates to a string specifying either a valid ADO Connection object variable name or a definition for a connection. The ActiveConnection argument specifies the connection in which to open the Cellset object. If you pass a connection definition for this argument, ADO opens a new connection using the specified parameters. The ActiveConnection argument corresponds to the ActiveConnection property.

Remarks

The Open method generates an error if either of its parameters is omitted and its corresponding property value has not been set prior to attempting to open the Cellset.

See Also

Visual Basic Example

ActiveConnection Property | Close Method | Connection Object | Source Property | State Property

Applies To: Cellset Object


ADO MD Enumerated Constants

To assist debugging, the ADO MD enumerated constants list a value for each constant. However, this value is purely advisory, and may change from one release of ADO MD to another. Your code should only depend on the name, not the actual value, of enumerated constants.

The following enumerations are defined.

  • SchemaObjectTypeEnum
  • MemberTypeEnum

See Also

ADO MD API Reference | ADO MD Code Examples | ADO MD Collections | ADO MD Methods | ADO MD Object Model | ADO MD Objects | ADO MD Properties


SchemaObjectTypeEnum

Specifies the type of object to be retrieved by the GetSchemaObject method.

Constant | Value | Description
adObjectTypeDimension | 1 | A Dimension object.
adObjectTypeHierarchy | 2 | A Hierarchy object.
adObjectTypeLevel | 3 | A Level object.
adObjectTypeMember | 4 | A Member object.


MemberTypeEnum

Specifies the setting for the Type property of a Member object.

Constant | Value | Description
adMemberAll | 4 | Indicates that the Member object represents all members of the level.
adMemberFormula | 3 | Indicates that the Member object is calculated using a formula expression.
adMemberMeasure | 2 | Indicates that the Member object belongs to the Measures dimension and represents a quantitative attribute.
adMemberRegular | 1 | Default. Indicates that the Member object represents an instance of a business entity.
adMemberUnknown | 0 | Cannot determine the type of the member.


ADO MD Code Examples

Use the following code examples to learn how to use the ADO MD objects, methods, and properties. These examples are a subset of the sample applications installed with Microsoft SQL Server OLAP Services for SQL Server 7.0.

These examples use the MSOLAP OLE DB provider, and run against a Microsoft SQL Server OLAP Services local host. However, these examples are intended to show fundamental ADO MD programming techniques, and should be easily adapted to other data sources or providers.

  • ADO MD Code Examples in Visual Basic
  • ADO MD Code Examples in Visual Basic, Scripting Edition

See Also

ADO MD API Reference | ADO MD Collections | ADO MD Enumerated Constants | ADO MD Methods | ADO MD Object Model | ADO MD Objects | ADO MD Properties


ADO MD Code Examples in Visual Basic

The following examples require Microsoft Visual Basic version 5.0 with Service Pack 3 or Visual Basic version 6.0.

Note Paste the entire code example, from beginning to end, into your code editor. The example may not run correctly if partial examples are used or if paragraph formatting is lost.

  • Cellset Example
  • Catalog Example


Cellset Example (VB)

This Visual Basic project demonstrates the basics of using ADO MD to access cube data. It displays member captions for column and row headers, then displays formatted values of specific cells within the cellset.

Private Sub cmdCellSettoDebugWindow_Click()
Dim cat As New ADOMD.Catalog
Dim cst As New ADOMD.Cellset
Dim i As Integer
Dim j As Integer
Dim strServer As String
Dim strSource As String
Dim strColumnHeader As String
Dim strRowText As String

On Error GoTo Error_cmdCellSettoDebugWindow_Click
Screen.MousePointer = vbHourglass
'*-----------------------------------------------------------------------
'* Set Server to Local Host
'*-----------------------------------------------------------------------
    strServer = "LOCALHOST"

'*-----------------------------------------------------------------------
'* Set MDX query string Source
'*-----------------------------------------------------------------------
    strSource = strSource & "SELECT "
    strSource = strSource & "{[Measures].members} ON COLUMNS,"
    strSource = strSource & _
        "NON EMPTY [Store].[Store City].members ON ROWS"
    strSource = strSource & " FROM Sales"

'*-----------------------------------------------------------------------
'* Set Active Connection
'*-----------------------------------------------------------------------
        cat.ActiveConnection = "Data Source=" & strServer & _
            ";Provider=msolap;"

'*-----------------------------------------------------------------------
'* Set Cell Set source to MDX query string
'*-----------------------------------------------------------------------
        cst.Source = strSource

'*-----------------------------------------------------------------------
'* Set Cell Sets active connection to current connection
'*-----------------------------------------------------------------------
    Set cst.ActiveConnection = cat.ActiveConnection

'*-----------------------------------------------------------------------
'* Open Cell Set
'*-----------------------------------------------------------------------
    cst.Open

'*-----------------------------------------------------------------------
'* Allow space for Row Header Text
'*-----------------------------------------------------------------------
strColumnHeader = vbTab & vbTab & vbTab & vbTab & vbTab & vbTab

'*-----------------------------------------------------------------------
'* Loop through Column Headers
'*-----------------------------------------------------------------------
       For i = 0 To cst.Axes(0).Positions.Count - 1
            strColumnHeader = strColumnHeader & _
                cst.Axes(0).Positions(i).Members(0).Caption & vbTab & _
                    vbTab & vbTab & vbTab
       Next
       Debug.Print vbTab & strColumnHeader & vbCrLf

'*-----------------------------------------------------------------------
'* Loop through Row Headers and Provide data for each row
'*-----------------------------------------------------------------------
        strRowText = ""
        For j = 0 To cst.Axes(1).Positions.Count - 1
            strRowText = strRowText & _
                cst.Axes(1).Positions(j).Members(0).Caption & vbTab & _
                    vbTab & vbTab & vbTab
            For k = 0 To cst.Axes(0).Positions.Count - 1
                strRowText = strRowText & cst(k, j).FormattedValue & _
                    vbTab & vbTab & vbTab & vbTab
            Next
            Debug.Print strRowText & vbCrLf
            strRowText = ""
        Next

    Screen.MousePointer = vbDefault
Exit Sub

Error_cmdCellSettoDebugWindow_Click:
   Beep
   Screen.MousePointer = vbDefault
   MsgBox "The Following Error has occurred:" & vbCrLf & _
      Err.Description, vbCritical, " Error!"
   Exit Sub
End Sub


Catalog Example (VB)

This Visual Basic project creates a new cube using MDX. Then, it documents the structure of a cube in a Microsoft Word document.

Private Sub cmdCreateDocForCube_Click()
Dim cn As ADODB.Connection
Dim s As String
Dim strProvider As String
Dim strDataSource As String
Dim strSourceDSN As String
Dim strSourceDSNSuffix As String
Dim strCreateCube As String
Dim strInsertInto As String

On Error GoTo Error_cmdCreateDocForCube_Click

'*-----------------------------------------------------------------------
'* The following code builds a cube file then documents the properties
'* with an OLE Connection to Word 8.0
'*-----------------------------------------------------------------------

'*-----------------------------------------------------------------------
'* Add Provider to the connection string.
'*-----------------------------------------------------------------------
strProvider = "PROVIDER=MSOLAP"

'*-----------------------------------------------------------------------
'* Add DataSource, the name of the file we will create.
'*-----------------------------------------------------------------------

strDataSource = "DATA SOURCE=c:\DocumentCube.cub"

'*-----------------------------------------------------------------------
'* Add Source DSN, the connection string for where the data comes from.
'* We need to quote the value so it is parsed as one value.
'* This can either be an ODBC connection string or an OLE DB connection
'* string. (As returned by the Data Source Locator component.)
'*-----------------------------------------------------------------------

strSourceDSN = "SOURCE_DSN=FoodMart"

'*-----------------------------------------------------------------------
'* We may have some other parameters that we want applied at run time,
'* but not stored in the cube file, or returned in the output string.
'*-----------------------------------------------------------------------

' strSourceDSNSuffix = "UID=;PWD="

'*-----------------------------------------------------------------------
'* Add CREATE CUBE.  This defines the structure of the cube, but not the
'* data in it. The BNF for is documented in the OLE DB for OLAP
'* Programmer's Reference. Note that we can quote names with either
'* double quotes or square brackets.
'*-----------------------------------------------------------------------

strCreateCube = "CREATECUBE=CREATE CUBE Sample( "
strCreateCube = strCreateCube & "DIMENSION [Product],"
        strCreateCube = strCreateCube & "LEVEL [All Products]  TYPE ALL,"
        strCreateCube = strCreateCube & "LEVEL [Product Family] ,"
        strCreateCube = strCreateCube & "LEVEL [Product Department] ,"
        strCreateCube = strCreateCube & "LEVEL [Product Category] ,"
        strCreateCube = strCreateCube & "LEVEL [Product Subcategory] ,"
        strCreateCube = strCreateCube & "LEVEL [Brand Name] ,"
        strCreateCube = strCreateCube & "LEVEL [Product Name] ,"
strCreateCube = strCreateCube & "DIMENSION [Store],"
        strCreateCube = strCreateCube & "LEVEL [All Stores]  TYPE ALL,"
        strCreateCube = strCreateCube & "LEVEL [Store Country] ,"
        strCreateCube = strCreateCube & "LEVEL [Store State] ,"
        strCreateCube = strCreateCube & "LEVEL [Store City] ,"
        strCreateCube = strCreateCube & "LEVEL [Store Name] ,"
strCreateCube = strCreateCube & "DIMENSION [Store Type],"
        strCreateCube = strCreateCube & _
            "LEVEL [All Store Type]  TYPE ALL,"
        strCreateCube = strCreateCube & "LEVEL [Store Type] ,"
strCreateCube = strCreateCube & "DIMENSION [Time] TYPE TIME,"
    strCreateCube = strCreateCube & "HIERARCHY [Column],"
        strCreateCube = strCreateCube & "LEVEL [All Time]  TYPE ALL,"
        strCreateCube = strCreateCube & "LEVEL [Year]  TYPE YEAR,"
        strCreateCube = strCreateCube & "LEVEL [Quarter]  TYPE QUARTER,"
        strCreateCube = strCreateCube & "LEVEL [Month]  TYPE MONTH,"
        strCreateCube = strCreateCube & "LEVEL [Week]  TYPE WEEK,"
        strCreateCube = strCreateCube & "LEVEL [Day]  TYPE DAY,"
    strCreateCube = strCreateCube & "HIERARCHY [Formula],"
        strCreateCube = strCreateCube & _
            "LEVEL [All Formula Time]  TYPE ALL,"
        strCreateCube = strCreateCube & "LEVEL [Year]  TYPE YEAR,"
        strCreateCube = strCreateCube & "LEVEL [Quarter]  TYPE QUARTER,"
        strCreateCube = strCreateCube & _
            "LEVEL [Month]  TYPE MONTH OPTIONS (SORTBYKEY) ,"
strCreateCube = strCreateCube & "DIMENSION [Warehouse],"
        strCreateCube = strCreateCube & _
            "LEVEL [All Warehouses]  TYPE ALL,"
        strCreateCube = strCreateCube & "LEVEL [Country] ,"
        strCreateCube = strCreateCube & "LEVEL [State Province] ,"
        strCreateCube = strCreateCube & "LEVEL [City] ,"
        strCreateCube = strCreateCube & "LEVEL [Warehouse Name] ,"
strCreateCube = strCreateCube & "MEASURE [Store Invoice] "
    strCreateCube = strCreateCube & "Function Sum "
    strCreateCube = strCreateCube & "Format '#.#',"
strCreateCube = strCreateCube & "MEASURE [Supply Time] "
    strCreateCube = strCreateCube & "Function Sum "
    strCreateCube = strCreateCube & "Format '#.#',"
strCreateCube = strCreateCube & "MEASURE [Warehouse Cost] "
    strCreateCube = strCreateCube & "Function Sum "
    strCreateCube = strCreateCube & "Format '#.#',"
strCreateCube = strCreateCube & "MEASURE [Warehouse Sales] "
    strCreateCube = strCreateCube & "Function Sum "
    strCreateCube = strCreateCube & "Format '#.#',"
strCreateCube = strCreateCube & "MEASURE [Units Shipped] "
    strCreateCube = strCreateCube & "Function Sum "
    strCreateCube = strCreateCube & "Format '#.#',"
strCreateCube = strCreateCube & "MEASURE [Units Ordered] "
    strCreateCube = strCreateCube & "Function Sum "
    strCreateCube = strCreateCube & "Format '#.#')"
'*-----------------------------------------------------------------------
'* Add INSERT INTO.  This defines where the data comes from, and how it
'* maps into the already-defined cube structure.  Note that the SELECT
'* clause might just be passed through to the relational database.
'* So I could pass in a stored procedure, for example.  If we needed to,
'* we could quote this whole thing.  Note that the columns in the SELECT
'* can be in any order.  One merely has to adjust the ordering of the
'* list of level/measure names to match the SELECT ordering.
'*-----------------------------------------------------------------------
strInsertInto = strInsertInto &
    "INSERTINTO=INSERT INTO Sample( " & _
        "Product.[Product Family], Product.[Product Department],"
strInsertInto = strInsertInto &
    "Product.[Product Category], Product.[Product Subcategory],"
strInsertInto = strInsertInto &
    "Product.[Brand Name], Product.[Product Name],"
strInsertInto = strInsertInto &
    "Store.[Store Country], Store.[Store State], Store.[Store City],"
strInsertInto = strInsertInto &
    "Store.[Store Name], [Store Type].[Store Type], [Time].[Column],"
strInsertInto = strInsertInto &
    "[Time].Formula.Year, [Time].Formula.Quarter, " & _
        "[Time].Formula.Month.[Key],"
strInsertInto = strInsertInto &
    "[Time].Formula.Month.Name, Warehouse.Country, " & _
        "Warehouse.[State Province],"
strInsertInto = strInsertInto &
    "Warehouse.City, Warehouse.[Warehouse Name], " * _
        "Measures.[Store Invoice],"
strInsertInto = strInsertInto &
    "Measures.[Supply Time], Measures.[Warehouse Cost], " & _
        "Measures.[Warehouse Sales],"
strInsertInto = strInsertInto &
    "Measures.[Units Shipped], Measures.[Units Ordered] )"
'*-----------------------------------------------------------------------
'* Add some options to the INSERT INTO if we need to.  These can control
'* if the SELECT clause is analyzed or just passed through, and if the
'* storage mode is MOLAP or ROLAP (DEFER_DATA).
'* strInsertInto = strInsertInto & " OPTIONS ATTEMPT_ANALYSIS"
'*-----------------------------------------------------------------------

'*-----------------------------------------------------------------------
'* Add the SELECT clause of the INSERT INTO statement.  Note that it is
'* merely concatenated onto the end of the INSERT INTO statement.  OLAP
'* Services will pass this through to the source database if unable to
'* parse it.  Note that for OLAP Services to analyze the SELECT clause,
'* each column must be qualified with the table name.
'*-----------------------------------------------------------------------

strInsertInto = strInsertInto &
    "SELECT product_class.product_family AS Col1,"
strInsertInto = strInsertInto &
    "product_class.product_department AS Col2,"
strInsertInto = strInsertInto & "product_class.product_category AS Col3,"
strInsertInto = strInsertInto &
    "product_class.product_subcategory AS Col4,"
strInsertInto = strInsertInto & "product.brand_name AS Col5,"
strInsertInto = strInsertInto & "product.product_name AS Col6,"
strInsertInto = strInsertInto & "store.store_country AS Col7,"
strInsertInto = strInsertInto & "store.store_state AS Col8,"
strInsertInto = strInsertInto & "store.store_city AS Col9,"
strInsertInto = strInsertInto & "store.store_name AS Col10,"
strInsertInto = strInsertInto & "store.store_type AS Col11,"
strInsertInto = strInsertInto & "time_by_day.the_date AS Col12,"
strInsertInto = strInsertInto & "time_by_day.the_year AS Col13,"
strInsertInto = strInsertInto & "time_by_day.quarter AS Col14,"
strInsertInto = strInsertInto & "time_by_day.month_of_year AS Col15,"
strInsertInto = strInsertInto & "time_by_day.the_month AS Col16,"
strInsertInto = strInsertInto & "warehouse.warehouse_country AS Col17,"
strInsertInto = strInsertInto &
    "warehouse.warehouse_state_province AS Col18,"
strInsertInto = strInsertInto & "warehouse.warehouse_city AS Col19,"
strInsertInto = strInsertInto & "warehouse.warehouse_name AS Col20,"
strInsertInto = strInsertInto &
    "inventory_fact_1997.store_invoice AS Col21,"
strInsertInto = strInsertInto &
    "inventory_fact_1997.supply_time AS Col22,"
strInsertInto = strInsertInto &
    "inventory_fact_1997.warehouse_cost AS Col23,"
strInsertInto = strInsertInto &
    "inventory_fact_1997.warehouse_sales AS Col24,"
strInsertInto = strInsertInto &
    "inventory_fact_1997.units_shipped AS Col25,"
strInsertInto = strInsertInto &
    "inventory_fact_1997.units_ordered AS Col26 "
strInsertInto = strInsertInto &
    "From [inventory_fact_1997], [product], [product_class], " &_
        "[time_by_day], [store], [warehouse] "
strInsertInto = strInsertInto &
    "Where [inventory_fact_1997].[product_id] = [product]." &_
        "[product_id] And "
strInsertInto = strInsertInto &
    "[product].[product_class_id] = [product_class]." &_
        "[product_class_id] And "
strInsertInto = strInsertInto &
    "[inventory_fact_1997].[time_id] = [time_by_day].[time_id] And "
strInsertInto = strInsertInto &
    "[inventory_fact_1997].[store_id] = [store].[store_id] And "
strInsertInto = strInsertInto &
    "[inventory_fact_1997].[warehouse_id] = [warehouse].[warehouse_id]"

'*-----------------------------------------------------------------------
'* Create the cube by passing connection string to Open.
'*-----------------------------------------------------------------------

Set cn = New ADODB.Connection
s = strProvider & ";" & strDataSource & ";" & strSourceDSN & ";" & _
    strCreateCube & ";" & strInsertInto & ";"
Screen.MousePointer = vbHourglass
cn.Open s

'*-----------------------------------------------------------------------
'* Cube file is written to hard drive a Word Document can be produced by
'* automating Word with VB
'*-----------------------------------------------------------------------

Dim cat As New ADOMD.Catalog
Dim cdf As ADOMD.CubeDef
Dim di As Integer
Dim hi As Integer
Dim le As Integer
Dim mem As Integer
Dim docWord As Word.Document
Dim rngCurrent As Word.Range
Dim SenCount As Integer
Dim strServer As String
Dim strSource As String
Dim strCubeName As String

'*-----------------------------------------------------------------------
'* Connection is made to cube file
'*-----------------------------------------------------------------------
cat.ActiveConnection = "DATA SOURCE=c:\DocumentCube.cub;Provider=msolap;"

'*-----------------------------------------------------------------------
'* Cube Definition is set to Name of Cube in cube file
'*-----------------------------------------------------------------------
Set cdf = cat.CubeDefs("Sample")

'*-----------------------------------------------------------------------
'* Object is created to hold Word 8.0
'*-----------------------------------------------------------------------
Set appword = CreateObject("Word.Application.8")

'*-----------------------------------------------------------------------
'* Create the document variable
'*-----------------------------------------------------------------------
   Set docWord = appword.Documents.Add()

   Set rngCurrent = docWord.Content

   SenCount = 0

'*-----------------------------------------------------------------------
'* Cube Title and Header written to Document
'*-----------------------------------------------------------------------
   With rngCurrent
       .InsertAfter "Report for Sample Cube"
       .InsertAfter vbCrLf
       SenCount = SenCount + 1
       docWord.Paragraphs(SenCount).Range.Bold = True
       docWord.Paragraphs(SenCount).Range.Underline = wdUnderlineSingle
       docWord.Paragraphs(SenCount).Range.Italic = False
       docWord.Paragraphs(SenCount).Range.Font.Size = 18

'*-----------------------------------------------------------------------
'* Properties of Cube are written to Document
'*-----------------------------------------------------------------------
For i = 0 To cdf.Properties.Count - 1
    .InsertAfter "(" & i & ") " & cdf.Properties(i).Name & ": " & _
        cdf.Properties(i).Value
    .InsertAfter vbCrLf
    SenCount = SenCount + 1
    docWord.Paragraphs(SenCount).Range.Bold = False
    docWord.Paragraphs(SenCount).Range.Italic = True
    docWord.Paragraphs(SenCount).Range.Font.Size = 8
Next i

'*-----------------------------------------------------------------------
'* Dimension Name(s) written to Document
'*-----------------------------------------------------------------------
    For di = 0 To cdf.Dimensions.Count - 1
    .InsertAfter "Dimension: " & cdf.Dimensions(di).Name
    .InsertAfter vbCrLf
    SenCount = SenCount + 1
    docWord.Paragraphs(SenCount).Range.Bold = True
    docWord.Paragraphs(SenCount).Range.Italic = False
    docWord.Paragraphs(SenCount).Range.Font.Size = 14

'*-----------------------------------------------------------------------
'* Properties of Dimension are written to Document
'*-----------------------------------------------------------------------
    For i = 0 To cdf.Dimensions(di).Properties.Count - 1
        .InsertAfter "(" & i & ") " & _
            cdf.Dimensions(di).Properties(i).Name & ": " & _
            cdf.Dimensions(di).Properties(i).Value
        .InsertAfter vbCrLf
        SenCount = SenCount + 1
        docWord.Paragraphs(SenCount).Range.Bold = False
        docWord.Paragraphs(SenCount).Range.Italic = True
        docWord.Paragraphs(SenCount).Range.Font.Size = 8
    Next i

'*-----------------------------------------------------------------------
'* Hierarchy Name(s) written to Document
'*-----------------------------------------------------------------------
    For hi = 0 To cdf.Dimensions(di).Hierarchies.Count - 1
        .InsertAfter vbTab & "Hierarchy: " & _
            cdf.Dimensions(di).Hierarchies(hi).Name
        .InsertAfter vbCrLf
        SenCount = SenCount + 1
        docWord.Paragraphs(SenCount).Range.Bold = True
        docWord.Paragraphs(SenCount).Range.Italic = False
        docWord.Paragraphs(SenCount).Range.Font.Size = 12

'*-----------------------------------------------------------------------
'* Properties of Hierarchy are written to Document
'*-----------------------------------------------------------------------
    For i = 0 To cdf.Dimensions(di).Hierarchies(hi).Properties.Count - 1
        .InsertAfter vbTab & "(" & i & ") " & _
            cdf.Dimensions(di).Hierarchies(hi).Properties(i).Name & _
            ": " & cdf.Dimensions(di).Hierarchies(hi).Properties(i).Value
        .InsertAfter vbCrLf
        SenCount = SenCount + 1
        docWord.Paragraphs(SenCount).Range.Bold = False
        docWord.Paragraphs(SenCount).Range.Italic = True
        docWord.Paragraphs(SenCount).Range.Font.Size = 8
    Next i

'*-----------------------------------------------------------------------
'* Level Name(s) written to Document
'*-----------------------------------------------------------------------
    For le = 0 To cdf.Dimensions(di).Hierarchies(hi).Levels.Count - 1
        .InsertAfter vbTab & vbTab & "Level: " & _
            cdf.Dimensions(di).Hierarchies(hi).Levels(le).Name & _
            " with a Member Count of: " & _
            cdf.Dimensions(di).Hierarchies(hi).Levels(le).Members.Count
        .InsertAfter vbCrLf
        SenCount = SenCount + 1
        docWord.Paragraphs(SenCount).Range.Bold = True
        docWord.Paragraphs(SenCount).Range.Italic = False
        docWord.Paragraphs(SenCount).Range.Font.Size = 10

'*-----------------------------------------------------------------------
'* Properties of Level are written to Document
'*-----------------------------------------------------------------------
    For i = 0 To
       cdf.Dimensions(di).Hierarchies(hi).Levels(le).Properties.Count - 1
        .InsertAfter vbTab & vbTab & "(" & i & ") " & _
             cdf.Dimensions(di).Hierarchies(hi).Levels(le)._
                 Properties(i).Name & ": " & _
             cdf.Dimensions(di).Hierarchies(hi).Levels(le)._
                 Properties(i).Value
        .InsertAfter vbCrLf
        SenCount = SenCount + 1
        docWord.Paragraphs(SenCount).Range.Bold = False
        docWord.Paragraphs(SenCount).Range.Italic = True
        docWord.Paragraphs(SenCount).Range.Font.Size = 8
    Next i

    Next le
    Next hi
    Next di

'*-----------------------------------------------------------------------
'* Set Word Document to visible
'*-----------------------------------------------------------------------
        appword.Visible = True
    End With
   Screen.MousePointer = vbDefault

'*-----------------------------------------------------------------------
'* Set Word Object to nothing to drop OLE connection
'*-----------------------------------------------------------------------
   Set appword = Nothing
Exit Sub

Error_cmdCreateDocForCube_Click:
    Screen.MousePointer = vbDefault
    MsgBox Err.Description
End Sub


ADO MD Code Examples in Visual Basic, Scripting Edition

The following examples require Microsoft Active Server Pages (ASP) for Microsoft Internet Information Server 4.0.

Note Paste the entire code example, from beginning to end, into your code editor. The example may not run correctly if partial examples are used or if paragraph formatting is lost.

  • Axis Example
  • Members Example
  • CubeDef Example


Axis Example (VBScript)

This Active Server Page displays OLAP data from an MDX Query string and writes the resulting cellset to an HTML table structure.

<%@ Language=VBScript %>
<%
'************************************************************************
'*** Active Server Page displays OLAP data from default
'*** MDX Query string and writes resulting cell set to HTML table
'*** structure.
'************************************************************************
Response.Buffer=True
Response.Expires=0
%>
<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY bgcolor=Ivory>
<FONT FACE=Verdana>

<%

Dim cat,cst,i,j,strSource,csw,intDC0,intDC1,intPC0,intPC1

'************************************************************************
'*** Set Connection Objects for Multidimensional Catalog and Cellset
'************************************************************************
Set cat = Server.CreateObject("ADOMD.Catalog")
Set cst = Server.CreateObject("ADOMD.CellSet")

'************************************************************************
'*** Use default settings of a known OLAP Server
'*** for Server Name for Connection Set Server Name Session Object
'*** to default value
'************************************************************************
'*** Must set OLAPServerName to OLAP Server that is
'*** present on network
'************************************************************************
   OLAPServerName = "Please set to present OLAP Server"
   cat.ActiveConnection = "Data Source=" & OLAPServerName & _
      ";Initial Catalog=FoodMart;Provider=msolap;"

'************************************************************************
'*** Use default MDX Query string of a known query that works
'*** with default server Set MDXQuery Session Object to default value
'************************************************************************
   strSource = strSource & "SELECT "
   strSource = strSource & "{[Measures].members} ON COLUMNS,"
   strSource = strSource & _
      "NON EMPTY [Store].[Store City].members ON ROWS"
   strSource = strSource & " FROM Sales"

'************************************************************************
'*** Set Cell Set Source property to strSource to be passed on cell set '*** open method
'************************************************************************
    cst.Source = strSource

'************************************************************************
'*** Set Cell Sets Active connection to use the current Catalogs Active
'*** connection
'************************************************************************
Set cst.ActiveConnection = cat.ActiveConnection

'************************************************************************
'*** Using Open method, Open cell set
'************************************************************************
cst.Open

'************************************************************************
'*** Set Dimension Counts minus 1 for Both Axes to intDC0, intDC1
'*** Set Position Counts minus 1 for Both Axes to intPC0, intPC1
'************************************************************************
intDC0 = cst.Axes(0).DimensionCount-1
intDC1 = cst.Axes(1).DimensionCount-1

intPC0 = cst.Axes(0).Positions.Count - 1
intPC1 = cst.Axes(1).Positions.Count - 1

'************************************************************************
'*** Create HTML Table structure to hold MDX Query return Record set
'************************************************************************
      Response.Write "<Table width=100% border=1>"

'************************************************************************
'*** Loop to create Column header
'************************************************************************
      For h=0 to intDC0
         Response.Write "<TR>"

'************************************************************************
'*** Loop to create spaces in front of Column headers
'*** to align with Row header
'************************************************************************
         For c=0 to intDC1
            Response.Write "<TD></TD>"
         Next

'************************************************************************
'*** Iterate through Axes(0) Positions writing member captions to table
'*** header
'************************************************************************
         For i = 0 To intPC0
            Response.Write "<TH>"
            Response.Write "<FONT size=-2>"
            Response.Write cst.Axes(0).Positions(i).Members(h).Caption
            Response.Write "</FONT>"
            Response.Write "</TH>"
         Next
         Response.Write "</TR>"
      Next
'************************************************************************
'*** Use Array values for row header formatting to provide
'*** spaces under beginning row header titles
'************************************************************************
      For j = 0 To intPC1
         Response.Write "<TR>"
         For h=0 to intDC1
            Response.Write "<TD><B>"
            Response.Write "<FONT size=-2>"
            Response.Write cst.Axes(1).Positions(j).Members(h).Caption
            Response.Write "</FONT>"
            Response.Write "</B></TD>"
         Next
         For k = 0 To intPC0
            Response.Write "<TD align=right bgcolor="
            Response.Write csw
            Response.Write ">"
            Response.Write "<FONT size=-2>"
            Response.Write cst(k, j).FormattedValue
            Response.Write "</FONT>"
            Response.Write "</TD>"
         Next
         Response.Write "</TR>"
      Next
      Response.Write "</Table>"

%>
</FONT>
</BODY>
</HTML>


Members Example (VBScript)

This sample uses an MDX query string to retrieve OLAP data and writes the resulting cellset to an HTML table structure using column spanning features for multiple-dimension cellsets.

<%@ Language=VBScript %>
<%
'************************************************************************
'*** Active Server Page displays OLAP data from default or provided
'*** MDX Query string and writes resulting cell set to HTML table
'*** structure. This ASP provides colspan features for multiple
'*** dimension cell sets.
'************************************************************************
Response.Buffer=True
Response.Expires=0
%>
<html>
<head>
<meta NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</head>
<body bgcolor="Ivory">
<font FACE="Verdana">

<%

Dim cat,cst,i,j,strSource,csw,LevelValue,intDC0,intDC1,intPC0, intPC1
'************************************************************************
'*** Gather Server Name and MDX Query Strings from text box and
'*** text area and assign them to Session Objects of same name
'************************************************************************
Session("ServerName")=Request.Form("strServerName")
Session("InitialCatalog")=Request.Form("strInitialCatalog")
Session("MDXQuery")=Request.Form("MDXQuery")

'************************************************************************
'*** Set Connection Objects for Multi dimensional Catalog and Cell Set
'************************************************************************
Set cat = Server.CreateObject("ADOMD.Catalog")
Set cst = Server.CreateObject("ADOMD.CellSet")

'************************************************************************
'*** Check to see if the Session Object Server Name is present
'*** If present then: Create Active Connection using Server Name
'*** and MSOLAP as connection Provider
'*** If not present then: Use default settings of a known OLAP Server
'*** for Server Name for Connection Set Server Name Session Object
'*** to default value
'************************************************************************
If Len(Session("ServerName")) > 0 Then
   cat.ActiveConnection = "Data Source=" & Session("ServerName") & _
      ";Initial Catalog=" & Session("InitialCatalog") & _
      ";Provider=msolap;"
Else

'************************************************************************
'*** Must set OLAPServerName to OLAP Server that is
'*** present on network
'************************************************************************
   OLAPServerName = "Please set to present OLAP Server"
   cat.ActiveConnection = "Data Source=" & OLAPServerName & _
      ";Initial Catalog=FoodMart;Provider=msolap;"
   Session("ServerName") = OLAPServerName
   Session("InitialCatalog") = "FoodMart"
End if
'************************************************************************
'*** Check to see if the Session Object MDXQuery is present
'*** If present then: Set strSource using MDXQuery Session Object
'*** If not present then: Use default MDX Query string of a known query
'*** that works with default server Set MDXQuery Session Object to
'*** default value
'************************************************************************
If Len(Session("MDXQuery")) < 5 Then
   strSource = strSource & "SELECT "
   strSource = strSource & "CROSSJOIN({[Store].[Store Country].MEMBERS},"
   strSource = strSource & "{[Measures].[Store " & _
      "Invoice],[Measures].[Supply Time]}) ON COLUMNS,"
   strSource = strSource & "CROSSJOIN({[Time].[Year].MEMBERS},"
   strSource = strSource & "CROSSJOIN({[Store Type].[Store " & _
      "Type].Members},{[Product].[Product Family].members})) ON ROWS"
   strSource = strSource & " FROM Warehouse"
Else
   strSource = Session("MDXQuery")
End if

'************************************************************************
'*** Set Cell Set Source property to strSource to be passed on cell set
'*** open method
'************************************************************************
   cst.Source = strSource

'************************************************************************
'*** Set Cell Sets Active connection to use the current Catalogs Active
'*** connection
'************************************************************************
Set cst.ActiveConnection = cat.ActiveConnection

'************************************************************************
'*** Using Open method, Open cell set
'************************************************************************
cst.Open

'************************************************************************
'*** Standard HTML to collect Sever Name and MDX Query Information
'*** Note that post action posts back to same page to process
'*** thus using state of Session Variables to change look of page
'************************************************************************
%>
<form action="ASPADOComplex.asp" method="POST" id="form1" name="form1">
<table>
<tr><td align="left">
<b>Olap Server name:</b><br><input type="text" id="strServerName" name="strServerName" value="<%=Session("ServerName")%>" size="20">
<br>
<b>Catalog name:</b><br><input type="text" id="strInitialCatalog" name="strInitialCatalog" value="<%=Session("InitialCatalog")%>" size="20">
</td><td align="center">
<b>MDX Query:</b><br>
<textarea rows="7" cols="70" id="textareaMDX" name="MDXQuery" wrap="soft">
<%=Session("MDXQuery")%>
</textarea>
</td></tr>
</table>
<table>
<tr><td>
<input type="submit" value="Submit MDX Query" id="submit1" name="submit1">
</td><td>
<input type="reset" value="Reset" id="reset1" name="reset1">
</td></tr>
</table>
</form>
<p align="left">
<font color="Black" size="-3">
<%=strSource%>
</font>
</p>
<%
'************************************************************************
'*** Set Dimension Counts minus 1 for Both Axes to intDC0, intDC1
'*** Set Position Counts minus 1 for Both Axes to intPC0, intPC1
'************************************************************************
intDC0 = cst.Axes(0).DimensionCount-1
intDC1 = cst.Axes(1).DimensionCount-1

intPC0 = cst.Axes(0).Positions.Count - 1
intPC1 = cst.Axes(1).Positions.Count - 1

'************************************************************************
'*** Create HTML Table structure to hold MDX Query return Record set
'************************************************************************
Response.Write "<Table width=100% border=1>"

'************************************************************************
'*** Loop to create Column header for all Dimensions based
'*** on Count of Dimensions for Axes(0)
'************************************************************************
For h=0 to intDC0
   Response.Write "<TR>"

'************************************************************************
'*** Loop to create spaces in front of Column headers
'*** to align with Row headers
'************************************************************************
   For c=0 to intDC1
      Response.Write "<TD></TD>"
   Next

'************************************************************************
'*** Check current dimension to see if equal to Last Dimension
'*** If True: Write Table header titles normally to HTML output with out
'*** ColSpan value
'*** If False: Write Table header titles with ColSpan values to HTML
'*** output
'************************************************************************
   If h = intDC0 then

'************************************************************************
'*** Iterate through Axes(0) Positions writing member captions to table
'*** header
'************************************************************************
      For i = 0 To intPC0
         Response.Write "<TH>"
         Response.Write "<FONT size=-2>"
         Response.Write cst.Axes(0).Positions(i).Members(h).Caption
         Response.Write "</FONT>"
         Response.Write "</TH>"
      Next
   Else

'************************************************************************
'*** Iterate through Axes(0) Positions writing member captions to table
'*** header taking into account for the span of columns for duplicate
'*** member captions
'************************************************************************
      CaptionCount = 1
      LastCaption = cst.Axes(0).Positions(0).Members(h).Caption
      Response.Write "<TH"
      For t=1 to intPC0

'************************************************************************
'*** Check to see if LastCaption is equal to current members caption
'*** If True: Add one to CaptionCount to increase Colspan value
'*** If False: Write Table header titles with ColSpan values to HTML
'*** output using current CaptionCount for Colspan and LastCaption for
'*** header string
'************************************************************************
         If LastCaption = _
            cst.Axes(0).Positions(t).Members(h).Caption then
            CaptionCount = CaptionCount+1

'************************************************************************
'*** Check if at last position
'*** If True: Write HTML to finish table row using current
'*** CaptionCount and LastCaption
'************************************************************************
            If t = intPC0 then
               Response.Write " colspan=" & CaptionCount & _
                  "><FONT size=-2>" & LastCaption & "</FONT></TH>"
            End if

         Else
            Response.Write " colspan=" & CaptionCount & _
               "><FONT size=-2>" & LastCaption & "</FONT></TH><TH"
            CaptionCount = 1
            LastCaption=cst.Axes(0).Positions(t).Members(h).Caption
         End if
      Next
         End if
         Response.Write "</TR>"
      Next

'************************************************************************
'*** Iterate through Axes(1) Positions first writing member captions
'*** to table row headers then writing cell set data to table structure
'************************************************************************
      Dim aryRows()
      Dim intArray,Marker
      intArray=0

'************************************************************************
'*** Set value of Array for row header formatting
'************************************************************************
      For a=1 To intDC1
         intArray = intArray+(intPC1+1)
      Next
      intArray = intArray-1
      ReDim aryRows(intArray)
      Marker=0

'************************************************************************
'*** Use Array values for row header formatting to provide
'*** spaces under beginning row header titles
'************************************************************************
      For j = 0 To intPC1
         Response.Write "<TR>"
         For h=0 to intDC1
            If h=intDC1 then
               Response.Write "<TD><B>"
               Response.Write "<FONT size=-2>"
               Response.Write cst.Axes(1).Positions(j).Members(h).Caption
               Response.Write "</FONT>"
               Response.Write "</B></TD>"
            Else
               aryRows(Marker) = _
                  cst.Axes(1).Positions(j).Members(h).Caption
               If Marker < intDC1 then
                  Response.Write "<TD><B>"
                  Response.Write "<FONT size=-2>"
                  Response.Write _
                     cst.Axes(1).Positions(j).Members(h).Caption
                  Response.Write "</FONT>"
                  Response.Write "</B></TD>"
                  Marker = Marker + 1
               Else
                  If aryRows(Marker) = aryRows(Marker - intDC1) then
                     Response.Write "<TD>&nbsp;</TD>"
                     Marker = Marker + 1
                  Else
                     Response.Write "<TD><B>"
                     Response.Write "<FONT size=-2>"
                     Response.Write _
                        cst.Axes(1).Positions(j).Members(h).Caption
                     Response.Write "</FONT>"
                     Response.Write "</B></TD>"
                     Marker = Marker + 1
                  End if
               End if
            End if
         Next

'************************************************************************
'*** Alternates Cell background color
'************************************************************************
         If (j+1) Mod 2 = 0 Then
            csw = "#cccccc"
         Else
            csw = "#ccffff"
         End If
         For k = 0 To intPC0
            Response.Write "<TD align=right bgcolor="
            Response.Write csw
            Response.Write ">"
            Response.Write "<FONT size=-2>"

'************************************************************************
'*** FormattedValue property pulls data
'************************************************************************
            Response.Write cst(k, j).FormattedValue
            Response.Write "</FONT>"
            Response.Write "</TD>"
         Next
         Response.Write "</TR>"
      Next
      Response.Write "</Table>"

%>
</font>
</body>
</html>


CubeDef Example (VBScript)

This example displays cube metadata on a web page.

<%@ Language=VBScript %>
<%
Response.Buffer=True
'Response.Expires=0
%>
<html>
<head>
<meta NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</head>
<body>

<%
Server.ScriptTimeout=360
Dim cat,cdf,di,hi,le,mem,strServer,strSource,strCubeName

'************************************************************************
'*** Set Session Variables
'************************************************************************
Session("CubeName") = Request.Form("strCubeName")
Session("CatalogName") = Request.Form("strCatalogName")
Session("ServerName") = Request.Form("strServerName")
Session("chkDim") = Request.Form("chkDimension")
Session("chkHier") =  Request.Form("chkHierarchy")
Session("chkLev") =  Request.Form("chkLevel")

'************************************************************************
'*** Create Catalog Object
'************************************************************************************
Set cat = Server.CreateObject("ADOMD.Catalog")

If Len(Session("ServerName")) > 0 Then
   cat.ActiveConnection = "Data Source=" & Session("ServerName") & ";Initial Catalog=" & Session("CatalogName") & ";Provider=msolap;"
Else
'************************************************************************************
'*** Must set OLAPServerName to OLAP Server that is
'*** present on network
'************************************************************************
OLAPServerName = "Please set to present OLAP Server"
   cat.ActiveConnection = "Data Source=" & OLAPServerName & _
      ";Initial Catalog=FoodMart;Provider=msolap;"
   Session("ServerName") = OLAPServerName
   Session("InitialCatalog") = "FoodMart"
End if

If Len(Session("CubeName")) > 0 Then
   Set cdf = cat.CubeDefs(Session("CubeName"))
Else
   Set cdf = cat.CubeDefs("Sales")
   Session("CubeName")="Sales"
End if

'************************************************************************
'*** Collect Information in HTML Form
'************************************************************************
%>
<form action="ASPADOCubeDoc.asp" method="post" id="form1" name="form1">
<table>
   <tr>
      <td>
      <b>Olap Server name:  </b><br><input type="text" id="strServerName" name="strServerName" value="<%=Session("ServerName")%>" size="20"><br>

      <b>Catalog Name:  </b><br><input type="text" id="strCatalogName" name="strCatalogName" value="<%=Session("CatalogName")%>" size="20"><br>

      <b>Cube Name:  </b><br><input type="text" id="strCubeName" name="strCubeName" value="<%=Session("CubeName")%>" size="20">
      </td>
      <td <TD>
         <b>Add Property Detail:  </b><br>
         Dimension Detail: <input type="checkbox" id="chkDimension" name="chkDimension"><br>

         Hierarchy Detail: <input type="checkbox" id="chkHierarchy" name="chkHierarchy"><br>

         Level Detail: <input type="checkbox" id="chkLevel" name="chkLevel">
      </td>
   </tr>
</table>
<input type="submit" value="Cube Information" id="submit1" name="submit1"><input type="reset" value="Reset" id="reset1" name="reset1">
</form>
<%

'************************************************************************
'*** Start of Report
'************************************************************************
Response.Write "<H3>Report for " & Session("CubeName") & " Cube</H3>"
Response.Write "<OL TYPE='i'>"

'************************************************************************
'*** Show properties of Cube
'************************************************************************
            For i = 0 To cdf.Properties.Count - 1
               Response.Write "<LI>"
               Response.Write "<FONT size=-2>" & cdf.Properties(i).Name & ": " & cdf.Properties(i).Value & "</FONT>"
            Next
            Response.Write "</OL>"
            Response.Write "<UL TYPE='SQUARE'>"
 '************************************************************************
'*** Loop to display Dimension Name and Properties if Check box is
'*** Checked
'************************************************************************
      For di = 0 To cdf.Dimensions.Count - 1
         Response.Write "<LI>"
         Response.Write "<FONT size=4><B>Dimension: " & _
            cdf.Dimensions(di).Name & "</B></FONT>"
         If Request.Form("chkDimension") = "on" Then
            Response.Write "<OL TYPE='1'>"
            For i = 0 To cdf.Dimensions(di).Properties.Count - 1
               Response.Write "<LI>"
               Response.Write "<FONT size=-2>" & _
                  cdf.Dimensions(di).Properties(i).Name & ": " & _
                  cdf.Dimensions(di).Properties(i).Value & "</FONT>"
            Next
            Response.Write "</OL>"
         End If
         Response.Write "<UL TYPE= 'Circle'>"
'************************************************************************
'*** Loop to display Hierarchy Name and Properties if Check box is
'*** Checked
'************************************************************************
         For hi = 0 To cdf.Dimensions(di).Hierarchies.Count - 1
            Response.Write "<LI>"
            Response.Write "<FONT size=3><B>Hierarchy: " & _
               cdf.Dimensions(di).Hierarchies(hi).Name & "</B></FONT>"
            If Request.Form("chkHierarchy") = "on" Then
               Response.Write "<OL TYPE='1'>"
               For i = 0 To _
                  cdf.Dimensions(di).Hierarchies(hi).Properties.Count - 1
                  Response.Write "<LI>"
                  Response.Write "<FONT size=-2>" & _
                     cdf.Dimensions(di).Hierarchies(hi).Properties(i)._
                     Name & ": " & _
                     cdf.Dimensions(di).Hierarchies(hi).Properties(i)._
                     Value & "</FONT>"
               Next
               Response.Write "</OL>"
            End If
            Response.Write "<UL TYPE='Disc'>"
'************************************************************************
'*** Loop to display Level Name and Properties if Check box is Checked
'************************************************************************
      For le = 0 To cdf.Dimensions(di).Hierarchies(hi).Levels.Count - 1
               Response.Write "<LI>"
               Response.Write "<FONT size=2><B>Level: " & _
                  cdf.Dimensions(di).Hierarchies(hi).Levels(le).Name & _
                  " with a Member Count of: " & _
                  cdf.Dimensions(di).Hierarchies(hi).Levels(le)._
                  Properties("LEVEL_CARDINALITY") & "</B></FONT>"
               If Request.Form("chkLevel") = "on" Then
                  Response.Write "<OL TYPE='1'>"
                  For i = 0 To
                     cdf.Dimensions(di).Hierarchies(hi).Levels(le)._
                     Properties.Count - 1
                     Response.Write "<LI>"
                     Response.Write "<FONT size=-2>" & _
                        cdf.Dimensions(di).Hierarchies(hi).Levels(le)._
                        Properties(i).Name & ": " & _
                        cdf.Dimensions(di).Hierarchies(hi).Levels(le)._
                        Properties(i).Value & "</FONT>"
                  Next
                  Response.Write "</OL>"
               End If
            Next
            Response.Write "</UL>"
         Next
         Response.Write "</UL>"
      Next
      Response.Write "</UL>"
%>
</body>
</html>


ADOX API Reference

ADOX API Reference

This section of the ADOX documentation contains topics for each ADOX object, collection, method, and property, as well as example code when appropriate. For more information, search for a specific topic in the index or refer to the following topics:

  • ADOX Objects
  • ADOX Collections
  • ADOX Properties
  • ADOX Methods
  • ADOX Enumerated Constants
  • ADOX Code Examples

See Also

ADO API Reference | ADO MD API Reference | ADOX Code Examples | ADOX Collections | ADOX Enumerated Constants | ADOX Methods | ADOX Object Model | ADOX Objects | ADOX Properties


ADOX Object Model

The following diagram illustrates how objects are represented and related in ADOX. For more information about a specific object or collection, see the specific reference topic, or ADOX Objects and ADOX Collections.

Each of the Table, Index, and Column objects also has a standard ADO Properties collection.

See Also

ADOX API Reference | ADOX Code Examples | ADOX Collections | ADOX Enumerated Constants | ADOX Methods | ADOX Objects | ADOX Properties | Microsoft ADOX Programmer's Reference


ADOX Objects

ADOX Object Summary

Object | Description
Catalog | Contains collections that describe the schema catalog of a data source.
Column | Represents a column from a table, index, or key.
Group | Represents a group account that has access permissions within a secured database.
Index | Represents an index from a database table.
Key | Represents a primary, foreign, or unique key field from a database table.
Procedure | Represents a stored procedure.
Table | Represents a database table, including columns, indexes, and keys.
User | Represents a user account that has access permissions within a secured database.
View | Represents a filtered set of records or a virtual table.

The relationships between these objects are illustrated in the ADOX Object Model.

Each object can be contained in its corresponding collection. For example, a Table object can be contained in a Tables collection. For more information, see ADOX Collections or a specific collection topic.

See Also

ADOX API Reference | ADOX Collections | ADOX Object Model | Catalog Object | Column Object | Group Object | Index Object | Key Object | Microsoft ADOX Programmer's Reference | Procedure Object | Table Object | User Object | View Object


Catalog Object (ADOX)

ADOX API Reference

Catalog Object

Contains collections (Tables, Views, Users, Groups, and Procedures) that describe the schema catalog of a data source.

Remarks

You can modify the Catalog object by adding or removing objects or by modifying existing objects. Some providers may not support all of the Catalog objects or may support only viewing schema information.

With the properties and methods of a Catalog object, you can:

  • Open the catalog by setting the ActiveConnection property to an ADO Connection object or a valid connection string.
  • Create a new catalog with the Create method.
  • Determine the owners of the objects in a Catalog with the GetObjectOwner and SetObjectOwner methods.

See Also

Catalog ActiveConnection Property Example (VB) | Command and CommandText Properties Example (VB) | Connection Close Method, Table Type Property Example (VB) | Create Method Example (VB) | Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB) | Parameters Collection, Command Property Example (VB) | ParentCatalog Property Example (VB) | Procedures Append Method Example (VB) | Procedures Delete Method Example (VB) | Procedures Refresh Method Example (VB) | Views and Fields Collections Example (VB) | Views Append Method Example (VB) | Views Collection, CommandText Property Example (VB) | Views Delete Method Example (VB) | Views Refresh Method Example (VB)


Properties, Methods, and Events

ADOX API Reference

Catalog Object Properties, Methods, and Events

Properties/Collections

ActiveConnection Property

Groups Collection

Procedures Collection

Tables Collection

Users Collection

Views Collection

Methods

Create Method

GetObjectOwner Method

SetObjectOwner Method

Events

None.

See Also

Applies To: Catalog Object


Column Object (ADOX)

ADOX API Reference

Column Object

Represents a column from a table, index, or key.

Remarks

The following code creates a new Column:

Dim obj As New Column

With the properties and collections of a Column object, you can:

  • Identify the column with the Name property.
  • Specify the data type of the column with the Type property.
  • Determine if the column is fixed-length, or if it can contain null values with the Attributes property.
  • Specify the maximum size of the column with the DefinedSize property.
  • For numeric data values, specify the scale with the NumericScale property.
  • For numeric data value, specify the maximum precision with the Precision property.
  • Specify the Catalog that owns the column with the ParentCatalog property.
  • For key columns, specify the name of the related column in the related table with the RelatedColumn property.
  • For index columns, specify whether the sort order is ascending or descending with the SortOrder property.
  • Access provider-specific properties with the Properties collection.

Note Not all properties of Column objects may be supported by your data provider. An error will occur if you have set a value for a property that the provider does not support. For new Column objects, the error will occur when the object is appended to the collection. For existing objects, the error will occur when setting the property.

When creating Column objects, the existence of an appropriate default value for an optional property does not guarantee that your provider supports the property. For more information about which properties your provider supports, see your provider documentation.

See Also

Columns and Tables Append Methods, Name Property Example (VB) | Connection Close Method, Table Type Property Example (VB) | Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB) | NumericScale and Precision Properties Example (VB) | ParentCatalog Property Example (VB) | SortOrder Property Example (VB)


Properties, Methods, and Events

ADOX API Reference

Column Object Properties, Methods, and Events

Properties/Collections

Attributes Property

DefinedSize Property

Name Property

NumericScale Property

ParentCatalog Property

Precision Property

Properties Collection

RelatedColumn Property

SortOrder Property

Type Property (Column)

Methods

None.

Events

None.

See Also

Applies To: Column Object


Group Object (ADOX)

ADOX API Reference

Group Object

Represents a group account that has access permissions within a secured database.

Remarks

The Groups collection of a Catalog represents all the catalog's group accounts. The Groups collection for a User represents only the group to which the user belongs.

With the properties, collections, and methods of a Group object, you can:

  • Identify the group with the Name property.
  • Determine whether a group has read, write, or delete permissions with the GetPermissions and SetPermissions methods.
  • Access the user accounts that have memberships in the group with the Users collection.
  • Access provider-specific properties with the Properties collection.

See Also

Properties, Methods, and Events | Groups Collection | Users Collection


Properties, Methods, and Events

ADOX API Reference

Group Object Properties, Methods, and Events

Properties/Collections

Name Property

Properties Collection

Users Collection

Methods

GetPermissions Method

SetPermissions Method

Events

None.

See Also

Applies To: Group Object


Index Object (ADOX)

ADOX API Reference

Index Object

Represents an index from a database table.

Remarks

The following code creates a new Index:

Dim obj As New Index

With the properties and collections of an Index object, you can:

  • Identify the index with the Name property.
  • Access the database columns of the index with the Columns collection.
  • Specify whether the index keys must be unique with the Unique property.
  • Specify whether the index is the primary key for a table with the PrimaryKey property.
  • Specify whether records that have null values in their index fields have index entries with the IndexNulls property.
  • Specify whether the index is clustered with the Clustered property.
  • Access provider-specific index properties with the Properties collection.

Notes An error will occur when appending a Column to the Columns collection of an Index if the Column does not exist in a Table object already appended to the Tables collection.

Your data provider may not support all properties of Index objects. An error will occur if you have set a value for a property that is not supported by the provider. For new Index objects, the error will occur when the object is appended to the collection. For existing objects, the error will occur when setting the property.

When creating Index objects, the existence of an appropriate default value for an optional property does not guarantee that your provider supports the property. For more information about which properties your provider supports, see your provider documentation.

See Also

Indexes Append Method Example (VB) | IndexNulls Property Example (VB) | PrimaryKey and Unique Properties Example (VB) | SortOrder Property Example (VB)


Properties, Methods, and Events

ADOX API Reference

Index Object Properties, Methods, and Events

Properties/Collections

Clustered Property

Columns Collection

IndexNulls Property

Name Property

PrimaryKey Property

Properties Collection

Unique Property

Methods

None.

Events

None.

See Also

Applies To: Index Object


Key Object (ADOX)

ADOX API Reference

Key Object

Represents a primary, foreign, or unique key field from a database table.

Remarks

The following code creates a new Key:

Dim obj As New Key

With the properties and collections of a Key object, you can:

  • Identify the key with the Name property.
  • Determine whether the key is primary, foreign, or unique with the Type property.
  • Access the database columns of the key with the Columns collection.
  • Specify the name of the related table with the RelatedTable property.
  • Determine the action performed on deletion or update of a primary key with the DeleteRule and UpdateRule properties.

See Also

Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB)


Properties, Methods, and Events

ADOX API Reference

Key Object Properties, Methods, and Events

Properties/Collections

Columns Collection

DeleteRule Property

Name Property

RelatedTable Property

Type Property (Key)

UpdateRule Property

Methods

None.

Events

None.

See Also

Applies To: Key Object


Procedure Object (ADOX)

ADOX API Reference

Procedure Object

Represents a stored procedure. When used in conjunction with the ADO Command object, the Procedure object can be used for adding, deleting, or modifying stored procedures.

Remarks

The Procedure object allows you to create a stored procedure without having to know or use the provider's "CREATE PROCEDURE" syntax.

With the properties of a Procedure object, you can:

  • Identify the procedure with the Name property.
  • Specify the ADO Command object that can be used to create or execute the procedure with the Command property.
  • Return date information with the DateCreated and DateModified properties.

See Also

Command and CommandText Properties Example (VB) | Parameters Collection, Command Property Example (VB) | Procedures Append Method Example (VB) | Procedures Delete Method Example (VB)


Properties, Methods, and Events

ADOX API Reference

Procedure Object Properties, Methods, and Events

Properties

Command Property

DateCreated Property

DateModified Property

Name Property

Methods

None.

Events

None.

See Also

Applies To: Procedure Object


Table Object (ADOX)

ADOX API Reference

Table Object

Represents a database table including columns, indexes, and keys.

Remarks

The following code creates a new Table:

Dim obj As New Table

With the properties and collections of a Table object, you can:

  • Identify the table with the Name property.
  • Determine the type of table with the Type property.
  • Access the database columns of the table with the Columns collection.
  • Access the indexes of the table with the Indexes collection.
  • Access the keys of the table with the Keys collection.
  • Specify the Catalog that owns the table with the ParentCatalog property.
  • Return date information with the DateCreated and DateModified properties.
  • Access provider-specific table properties with the Properties collection.

Note Your data provider may not support all properties of Table objects. An error will occur if you have set a value for a property that the provider does not support. For new Table objects, the error will occur when the object is appended to the collection. For existing objects, the error will occur when setting the property.

When creating Table objects, the existence of an appropriate default value for an optional property does not guarantee that your provider supports the property. For more information about which properties your provider supports, see your provider documentation.

See Also

Catalog ActiveConnection Property Example (VB) | Columns and Tables Append Methods, Name Property Example (VB) | Connection Close Method, Table Type Property Example (VB) | Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB) | ParentCatalog Property Example (VB)


Properties, Methods, and Events

ADOX API Reference

Table Object Properties, Methods, and Events

Properties/Collections

DateCreated Property

DateModified Property

Indexes Collection

Keys Collection

Name Property

ParentCatalog Property

Type Property (Table)

Methods

None.

Events

None.

See Also

Applies To: Table Object


User Object (ADOX)

ADOX API Reference

User Object

Represents a user account that has access permissions within a secured database.

Remarks

The Users collection of a Catalog represents all the catalog's users. The Users collection for a Group represents only the users of the specific group.

With the properties, collections, and methods of a User object, you can:

  • Identify the user with the Name property.
  • Change the password for a user with the ChangePassword method.
  • Determine whether a user has read, write, or delete permissions with the GetPermissions and SetPermissions methods.
  • Access the groups to which a user belongs with the Groups collection.
  • Access provider-specific properties with the Properties collection.

See Also

GetPermissions and SetPermissions Methods Example (VB)


Properties, Methods, and Events

ADOX API Reference

User Object Properties, Methods, and Events

Properties/Collections

Groups Collection

Name Property

Properties Collection

Methods

ChangePassword Method

GetPermissions Method

SetPermissions Method

Events

None.

See Also

Applies To: User Object


View Object (ADOX)

ADOX API Reference

View Object

Represents a filtered set of records or a virtual table. When used in conjunction with the ADO Command object, the View object can be used for adding, deleting, or modifying views.

Remarks

A view is a virtual table, created from other database tables or views. The View object allows you to create a view without having to know or use the provider's "CREATE VIEW" syntax.

With the properties of a View object, you can:

  • Identify the view with the Name property.
  • Specify the ADO Command object that can be used to add, delete, or modify views with the Command property.
  • Return date information with the DateCreated and DateModified properties.

See Also

Views and Fields Collections Example (VB) | Views Append Method Example (VB) | Views Collection, CommandText Property Example (VB) | Views Delete Method Example (VB)


Properties, Methods, and Events

ADOX API Reference

View Object Properties, Methods, and Events

Properties

Command Property

DateCreated Property

DateModified Property

Name Property

Methods

None.

Events

None.

See Also

Applies To: View Object


ADOX Collections

Columns | Contains all Column objects of a table, index, or key.
Groups | Contains all stored Group objects of a catalog or user.
Indexes | Contains all Index objects of a table.
Keys | Contains all Key objects of a table.
Procedures | Contains all Procedure objects of a catalog.
Tables | Contains all Table objects of a catalog.
Users | Contains all stored User objects of a catalog or group.
Views | Contains all View objects of a catalog.

See Also

ADOX API Reference | ADOX Object Model | ADOX Objects | Columns Collection | Groups Collection | Indexes Collection | Keys Collection | Microsoft ADOX Programmer's Reference | Procedures Collection | Tables Collection | Users Collection | Views Collection


Columns Collection (ADOX)

ADOX API Reference

Columns Collection

Contains all Column objects of a table, index, or key.

Remarks

The Append method for a Columns collection is unique for ADOX. You can:

  • Add a new column to the collection with the Append method.

The remaining properties and methods are standard to ADO collections. You can:

  • Access a column in the collection with the Item property.
  • Return the number of columns contained in the collection with the Count property.
  • Remove a column from the collection with the Delete method.
  • Update the objects in the collection to reflect the current database's schema with the Refresh method.

Note An error will occur when appending a Column to the Columns collection of an Index if the Column does not exist in a Table that is already appended to the Tables collection.

See Also

Columns and Tables Append Methods, Name Property Example (VB) | Connection Close Method, Table Type Property Example (VB) | Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB) | ParentCatalog Property Example (VB) | SortOrder Property Example (VB)


Columns Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Append Method (Columns)

Delete Method (Collections)

Refresh Method

Events

None.

See Also

Applies To: Columns Collection


Groups Collection (ADOX)

ADOX API Reference

Groups Collection

Contains all stored Group objects of a catalog or user.

Remarks

The Groups collection of a Catalog represents all of the catalog's group accounts. The Groups collection for a User represents only the group to which the user belongs.

The Append method for a Groups collection is unique for ADOX. You can:

  • Add a new security group to the collection with the Append method.

The remaining properties and methods are standard to ADO collections. You can:

  • Access a group in the collection with the Item property.
  • Return the number of groups contained in the collection with the Count property.
  • Remove a group from the collection with the Delete method.
  • Update the objects in the collection to reflect the current database's schema with the Refresh method.

Note Before appending a Group object to the Groups collection of a User object, a Group object with the same Name as the one to be appended must already exist in the Groups collection of the Catalog.

See Also

Properties, Methods, and Events | Catalog Object | Group Object


Groups Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Append Method (Groups)

Delete Method (Collections)

Refresh Method

Events

None.

See Also

Applies To: Groups Collection


Indexes Collection (ADOX)

ADOX API Reference

Indexes Collection

Contains all Index objects of a table.

Remarks

The Append method for an Indexes collection is unique for ADOX. You can:

  • Add a new index to the collection with the Append method.

The remaining properties and methods are standard to ADO collections. You can:

  • Access an index in the collection with the Item property.
  • Return the number of indexes contained in the collection with the Count property.
  • Remove an index from the collection with the Delete method.
  • Update the objects in the collection to reflect the current database's schema with the Refresh method.

See Also

Indexes Append Method Example (VB)


Indexes Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Append Method (Indexes)

Delete Method (Collections)

Refresh Method

Events

None.

See Also

Applies To: Indexes Collection


Keys Collection (ADOX)

ADOX API Reference

Keys Collection

Contains all Key objects of a table.

Remarks

The Append method for a Keys collection is unique for ADOX. You can:

  • Add a new key to the collection with the Append method.

The remaining properties and methods are standard to ADO collections. You can:

  • Access a key in the collection with the Item property.
  • Return the number of keys contained in the collection with the Count property.
  • Remove a key from the collection with the Delete method.
  • Update the objects in the collection to reflect the current database's schema with the Refresh method.

See Also

Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB)


Keys Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Append Method (Keys)

Delete Method (Collections)

Refresh Method

Events

None.

See Also

Applies To: Keys Collection


Procedures Collection (ADOX)

ADOX API Reference

Procedures Collection

Contains all Procedure objects of a catalog.

Remarks

The Append method for a Procedures collection is unique for ADOX. You can:

  • Add a new procedure to the collection with the Append method.

The remaining properties and methods are standard to ADO collections. You can:

  • Access a procedure in the collection with the Item property.
  • Return the number of procedures contained in the collection with the Count property.
  • Remove a procedure from the collection with the Delete method.
  • Update the objects in the collection to reflect the current database's schema with the Refresh method.

See Also

Command and CommandText Properties Example (VB) | Parameters Collection, Command Property Example (VB) | Procedures Append Method Example (VB) | Procedures Delete Method Example (VB) | Procedures Refresh Method Example (VB)


Procedures Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Append Method (Procedures)

Delete Method (Collections)

Refresh Method

Events

None.

See Also

Applies To: Procedures Collection


Tables Collection (ADOX)

ADOX API Reference

Tables Collection

Contains all Table objects of a catalog.

Remarks

The Append method for a Tables collection is unique for ADOX. You can:

  • Add a new table to the collection with the Append method.

The remaining properties and methods are standard to ADO collections. You can:

  • Access a table in the collection with the Item property.
  • Return the number of tables contained in the collection with the Count property.
  • Remove a table from the collection with the Delete method.
  • Update the objects in the collection to reflect the current database's schema with the Refresh method.

Some providers may return other schema objects, such as a View, in the Tables collection. Therefore, some ADOX collections may contain references to the same object. Should you delete the object from one collection, the change will not be visible in another collection that references the deleted object until the Refresh method is called on the collection. For example, with the OLE DB Provider for Microsoft Jet, Views are returned with the Tables collection. If you drop a View, you must Refresh the Tables collection before the collection will reflect the change.

See Also

Catalog ActiveConnection Property Example (VB) | Columns and Tables Append Methods, Name Property Example (VB) | Connection Close Method, Table Type Property Example (VB) | Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB)


Tables Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Append Method (Tables)

Delete Method (Collections)

Refresh Method

Events

None.

See Also

Applies To: Tables Collection


Users Collection (ADOX)

ADOX API Reference

Users Collection

Contains all stored User objects of a catalog or group.

Remarks

The Users collection of a Catalog represents all the catalog's users. The Users collection for a Group represents only the users that have a membership in the specific group.

The Append method for a Users collection is unique for ADOX. You can:

  • Add a new user to the collection with the Append method.

The remaining properties and methods are standard to ADO collections. You can:

  • Access a user in the collection with the Item property.
  • Return the number of users contained in the collection with the Count property.
  • Remove a user from the collection with the Delete method.
  • Update the objects in the collection to reflect the current database's schema with the Refresh method.

Note Before appending a User object to the Users collection of a Group object, a User object with the same Name as the one to be appended must already exist in the Users collection of the Catalog.

See Also

GetPermissions and SetPermissions Methods Example (VB)


Users Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Append Method (Users)

Delete Method (Collections)

Refresh Method

Events

None.

See Also

Applies To: Users Collection


Views Collection (ADOX)

ADOX API Reference

Views Collection

Contains all View objects of a catalog.

Remarks

The Append method for a Views collection is unique for ADOX. You can:

  • Add a new view to the collection with the Append method.

The remaining properties and methods are standard to ADO collections. You can:

  • Access a view in the collection with the Item property.
  • Return the number of views contained in the collection with the Count property.
  • Remove a view from the collection with the Delete method.
  • Update the objects in the collection to reflect the current database's schema with the Refresh method.

See Also

Views and Fields Collections Example (VB) | Views Append Method Example (VB) | Views Collection, CommandText Property Example (VB) | Views Delete Method Example (VB) | Views Refresh Method Example (VB)


Views Collection Properties, Methods, and Events

Properties

Count Property

Item Property

Methods

Append Method (Views)

Delete Method (Collections)

Refresh Method

Events

None.

See Also

Applies To: Views Collection


ADOX Properties

ActiveConnection | Indicates the ADO Connection object to which the catalog belongs.
Attributes | Describes column characteristics.
Clustered | Indicates whether the index is clustered.
Command | Specifies an ADO Command object that can be used to create or execute the procedure.
Count | Indicates the number of objects in a collection.
DateCreated | Indicates the date the object was created.
DateModified | Indicates the date the object was last modified.
DefinedSize | Indicates the stated maximum size of the column.
DeleteRule | Indicates the action performed when a primary key is deleted.
IndexNulls | Indicates whether records that have null values in their index fields have index entries.
Name | Indicates the name of the object.
NumericScale | Indicates the scale of a numeric value in the column.
ParentCatalog | Specifies the parent catalog of a table or column to provide access to provider-specific properties.
Precision | Indicates the maximum precision of data values in the column.
PrimaryKey | Indicates whether the index represents the primary key on the table.
RelatedColumn | Indicates the name of the related column in the related table (key columns only).
RelatedTable | Indicates the name of the related table.
SortOrder | Indicates the sort sequence for the column (index columns only).
Type (Column) | Indicates the data type of a column.
Type (Key) | Indicates the data type of the Key.
Type (Table) | Indicates the type of a table.
Unique | Indicates whether the index keys must be unique.
UpdateRule | Indicates the action performed when a primary key is updated.

See Also

ADOX API Reference | Chapter 15: ADOX Fundamentals


ActiveConnection Property (ADOX)

ADOX API Reference

ActiveConnection Property

Indicates the ADO Connection object to which the Catalog belongs.

Settings and Return Values

Sets a Connection object or a String containing the definition for a connection. Returns the active Connection object.

Remarks

The default value is a null object reference.

See Also

Catalog ActiveConnection Property Example (VB) | Command and CommandText Properties Example (VB) | Connection Close Method, Table Type Property Example (VB) | Parameters Collection, Command Property Example (VB) | Procedures Append Method Example (VB) | Procedures Delete Method Example (VB) | Procedures Refresh Method Example (VB) | Views and Fields Collections Example (VB) | Views Append Method Example (VB) | Views Collection, CommandText Property Example (VB) | Views Refresh Method Example (VB)

Create Method

Applies To: Catalog Object


Attributes Property (ADOX)

ADOX API Reference

Attributes Property

Describes column characteristics.

Settings and Return Values

Sets or returns a Long value. The value specifies characteristics of the table represented by the Column object and can be a combination of ColumnAttributesEnum constants. The default value is zero (0), which is neither adColFixed nor adColNullable.

See Also

Visual Basic Example

Applies To: Column Object | Property Object


Clustered Property (ADOX)

ADOX API Reference

Clustered Property

Indicates whether the index is clustered.

Settings and Return Values

Sets and returns a Boolean value.

Remarks

The default value is False.

This property is read-only on Index objects already appended to a collection.

See Also

Visual Basic Example

Applies To: Index Object


Command Property (ADOX)

ADOX API Reference

Command Property

Specifies an ADO Command object that can be used to create or execute the procedure.

Settings and Return Values

Sets or returns a valid ADO Command object.

Remarks

An error will occur when getting and setting this property if the provider does not support persisting commands.

See Also

Command and CommandText Properties Example (VB) | Parameters Collection, Command Property Example (VB) | Views and Fields Collections Example (VB) | Views Collection, CommandText Property Example (VB)

Applies To: Procedure Object | View Object


DateCreated Property (ADOX)

ADOX API Reference

DateCreated Property

Indicates the date the object was created.

Return Values

Returns a Variant value specifying the date created. The value is null if DateCreated is not supported by the provider.

Remarks

The DateCreated property is null for newly appended objects. After appending a new View or Procedure, you must call the Refresh method of the Views or Procedures collection to obtain values for the DateCreated property.

See Also

DateCreated and DateModified Properties Example (VB)

DateModified Property

Applies To: Procedure Object | Table Object | View Object


DateModified Property (ADOX)

ADOX API Reference

DateModified Property

Indicates the date the object was last modified.

Return Values

Returns a Variant value specifying the date modified. The value is null if DateModified is not supported by the provider.

Remarks

The DateModified property is null for newly appended objects. After appending a new View or Procedure, you must call the Refresh method of the Views or Procedures collection to obtain values for the DateModified property.

See Also

DateCreated and DateModified Properties Example (VB)

DateCreated Property

Applies To: Procedure Object | Table Object | View Object


DefinedSize Property (ADOX)

ADOX API Reference

DefinedSize Property

Indicates the stated maximum size of the column.

Settings and Return Values

Sets and returns a Long value that is the maximum length in characters of data values.

Remarks

The default value is zero (0).

This property is read-only for Column objects already appended to a collection.

See Also

Visual Basic Example

Applies To: Column Object


DeleteRule Property (ADOX)

ADOX API Reference

DeleteRule Property

Indicates the action performed when a primary key is deleted.

Settings and Return Values

Sets and returns a Long value that can be one of the RuleEnum constants. The default value is adRINone.

Remarks

This property is read-only on Key objects already appended to a collection.

See Also

Visual Basic Example

Applies To: Key Object


IndexNulls Property (ADOX)

ADOX API Reference

IndexNulls Property

Indicates whether records that have null values in their index fields have index entries.

Settings and Return Values

Sets and returns an AllowNullsEnum value. The default value is adIndexNullsDisallow.

Remarks

This property is read-only on Index objects already appended to a collection.

See Also

Visual Basic Example

Applies To: Index Object


Name Property (ADOX)

ADOX API Reference

Name Property

Indicates the name of the object.

Settings and Return Values

Sets or returns a String value.

Remarks

Names do not have to be unique within a collection.

The Name property is read/write on Column, Group, Key, Index, Table, and User objects. The Name property is read-only on Catalog, Procedure, and View objects.

For read/write objects (Column, Group, Key, Index, Table and User objects), the default value is an empty string ("").

Notes For keys, this property is read-only on Key objects already appended to a collection.

For tables, this property is read-only for Table objects already appended to a collection.

See Also

Columns and Tables Append Methods, Name Property Example (VB) | Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB) | ParentCatalog Property Example (VB)

Applies To: Column Object | Group Object | Index Object | Key Object | Procedure Object | Property Object | Table Object | User Object | View Object


NumericScale Property (ADOX)

ADOX API Reference

NumericScale Property

Indicates the scale of a numeric value in the column.

Settings and Return Values

Sets and returns a Byte value that is the scale of data values in the column when the Type property is adNumeric or adDecimal. NumericScale is ignored for all other data types.

Remarks

The default value is zero (0).

NumericScale is read-only for Column objects already appended to a collection.

See Also

NumericScale and Precision Properties Example (VB)

Type Property (Column)

Applies To: Column Object


ParentCatalog Property (ADOX)

ADOX API Reference

ParentCatalog Property

Specifies the parent catalog of a table or column to provide access to provider-specific properties.

Settings and Return Values

Sets and returns a Catalog object. Setting ParentCatalog to an open Catalog allows access to provider-specific properties prior to appending a table or column to a Catalog collection.

Remarks

Some data providers allow provider-specific property values to be written only at creation (when a table or column is appended to its Catalog collection). To access these properties before appending these objects to a Catalog, specify the Catalog in the ParentCatalog property first.

An error occurs when the table or column is appended to a different Catalog than the ParentCatalog.

See Also

Visual Basic Example

Applies To: Column Object | Table Object


Precision Property (ADOX)

ADOX API Reference

Precision Property

Indicates the maximum precision of data values in the column.

Settings and Return Values

Sets and returns a Long value that is the maximum precision of data values in the column when the Type property is a numeric type. Precision is ignored for all other data types.

Remarks

The default value is zero (0).

This property is read-only for Column objects already appended to a collection.

See Also

NumericScale and Precision Properties Example (VB)

Type Property (Column)

Applies To: Column Object


PrimaryKey Property (ADOX)

ADOX API Reference

PrimaryKey Property

Indicates whether the index represents the primary key on the table.

Settings and Return Values

Sets and returns a Boolean value.

Remarks

The default value is False.

This property is read-only on Index objects already appended to a collection.

See Also

PrimaryKey and Unique Properties Example (VB)

Applies To: Index Object


RelatedColumn Property (ADOX)

ADOX API Reference

RelatedColumn Property

Indicates the name of the related column in the related table (key columns only).

Settings and Return Values

Sets and returns a String value that is the name of the related column in the related table.

Remarks

The default value is an empty string ("").

This property is read-only for Column objects already appended to a collection.

See Also

Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB)

Key Object

Applies To: Column Object


RelatedTable Property (ADOX)

ADOX API Reference

RelatedTable Property

Indicates the name of the related table.

Settings and Return Values

Sets and returns a String value.

Remarks

The default value is an empty string ("").

If the key is a foreign key, then RelatedTable is the name of the table that contains the key.

See Also

Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB)

Applies To: Key Object


SortOrder Property (ADOX)

ADOX API Reference

SortOrder Property

Indicates the sort sequence for the column (index columns only).

Settings and Return Values

Sets and returns a Long value that can be one of the SortOrderEnum constants. The default value is adSortAscending.

Remarks

This property only applies to Column objects in the Columns collection of an Index.

See Also

Visual Basic Example

Columns Collection | Index Object

Applies To: Column Object


Type Property (Column) (ADOX)

ADOX API Reference

Type Property (Column)

Indicates the data type of a column.

Settings and Return Values

Sets or returns a Long value that can be one of the DataTypeEnum constants. The default value is adVarWChar.

Remarks

This property is read/write until the Column object is appended to a collection or to another object, after which it is read-only.

See Also

ParentCatalog Property Example (VB)

Type Property (Key) | Type Property (Table)

Applies To: Column Object


Type Property (Key) (ADOX)

ADOX API Reference

Type Property (Key)

Indicates the type of the key.

Settings and Return Values

Sets or returns a Long value that can be one of the KeyTypeEnum constants. The default value is adKeyPrimary.

Remarks

This property is read-only on Key objects already appended to a collection.

See Also

Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB)

Type Property (Column) | Type Property (Table)

Applies To: Key Object


Type Property (Table) (ADOX)

ADOX API Reference

Type Property (Table)

Indicates the type of a table.

Return Values

Returns a String value that specifies the type of table; for example, "TABLE", "SYSTEM TABLE", or "GLOBAL TEMPORARY".

Remarks

This property is read-only.

See Also

Catalog ActiveConnection Property Example (VB) | Connection Close Method, Table Type Property Example (VB)

Type Property (Column) | Type Property (Key)

Applies To: Table Object


Unique Property (ADOX)

ADOX API Reference

Unique Property

Indicates whether the index keys must be unique.

Settings and Return Values

Sets and returns a Boolean value.

Remarks

The default value is False.

This property is read-only on Index objects already appended to a collection.

See Also

PrimaryKey and Unique Properties Example (VB)

Key Object

Applies To: Index Object


UpdateRule Property (ADOX)

ADOX API Reference

UpdateRule Property

Indicates the action performed when a primary key is updated.

Settings and Return Values

Sets and returns a Long value that can be one of the RuleEnum constants. The default value is adRINone.

Remarks

This property is read-only on Key objects already appended to the collection.

See Also

Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB)

Applies To: Key Object


ADOX Methods

Append (Columns) | Adds a new Column object to the Columns collection.
Append (Groups) | Adds a new Group object to the Groups collection.
Append (Indexes) | Adds a new Index object to the Indexes collection.
Append (Keys) | Adds a new Key object to the Keys collection.
Append (Procedures) | Adds a new Procedure object to the Procedures collection.
Append (Tables) | Adds a new Table object to the Tables collection.
Append (Users) | Adds a new User object to the Users collection.
Append (Views) | Adds a new View object to the Views collection.
ChangePassword | Changes the password for a user account.
Create | Creates a new catalog.
Delete | Removes an object from a collection.
GetObjectOwner | Returns the owner of an object in a catalog.
GetPermissions | Returns the permissions for a group or user on an object.
SetObjectOwner | Specifies the owner of an object in a catalog.
SetPermissions | Specifies the permissions for group or user on an object.

See Also

ADOX API Reference | Append Method (Columns) | Append Method (Groups) | Append Method (Indexes) | Append Method (Keys) | Append Method (Procedures) | Append Method (Tables) | Append Method (Users) | Append Method (Views) | Create Method | Delete Method (Collections) | GetPermissions Method | Microsoft ADOX Programmer's Reference | SetPermissions Method


Append Method (ADOX Columns)

ADOX API Reference

Append Method (Columns)

Adds a new Column object to the Columns collection.

Syntax

Columns.Append Column [, Type] [, DefinedSize]

Parameters

Column
The Column object to append or the name of the column to create and append.
Type
Optional. A Long value that specifies the data type of the column. The Type parameter corresponds to the Type property of a Column object.
DefinedSize
Optional. A Long value that specifies the size of the column. The DefinedSize parameter corresponds to the DefinedSize property of a Column object.

Note An error will occur when appending a Column to the Columns collection of an Index if the Column does not exist in a Table that is already appended to the Tables collection.

See Also

Columns and Tables Append Methods, Name Property Example (VB) | Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB) | ParentCatalog Property Example (VB)

Append Method (Groups) | Append Method (Indexes) | Append Method (Keys) | Append Method (Procedures) | Append Method (Tables) | Append Method (Users) | Append Method (Views)

Applies To: Columns Collection


Append Method (ADOX Groups)

ADOX API Reference

Append Method (Groups)

Adds a new Group object to the Groups collection.

Syntax

Groups.Append Group

Parameters

Group
The Group object to append or the name of the group to create and append.

Remarks

The Groups collection of a Catalog represents all of the catalog's group accounts. The Groups collection for a User represents only the group to which the user belongs.

An error will occur if the provider does not support creating groups.

Note Before appending a Group object to the Groups collection of a User object, a Group object with the same Name as the one to be appended must already exist in the Groups collection of the Catalog.

See Also

Groups and Users Append, ChangePassword Methods Example (VB)

Append Method (Columns) | Append Method (Indexes) | Append Method (Keys) | Append Method (Procedures) | Append Method (Tables) | Append Method (Users) | Append Method (Views)

Applies To: Groups Collection


Append Method (ADOX Indexes)

ADOX API Reference

Append Method (Indexes)

Adds a new Index object to the Indexes collection.

Syntax

Indexes.Append Index [, Columns]

Parameters

Index
The Index object to append or the name of the index to create and append.
Columns
Optional. A Variant value that specifies the name(s) of the column(s) to be indexed. The Columns parameter corresponds to the value(s) of the Name property of a Column object or objects.

Remarks

The Columns parameter can take either the name of a column or an array of column names.

An error will occur if the provider does not support creating indexes.

See Also

Indexes Append Method Example (VB)

Append Method (Columns) | Append Method (Groups) | Append Method (Keys) | Append Method (Procedures) | Append Method (Tables) | Append Method (Users) | Append Method (Views)

Applies To: Indexes Collection


Append Method (ADOX Keys)

ADOX API Reference

Append Method (Keys)

Adds a new Key object to the Keys collection.

Syntax

Keys.Append Key [, KeyType] [, Column] [, RelatedTable] [, RelatedColumn]

Parameters

Key
The Key object to append or the name of the key to create and append.
KeyType
Optional. A Long value that specifies the type of key. The Key parameter corresponds to the Type property of a Key object.
Column
Optional. A String value that specifies the name of the column to be indexed. The Columns parameter corresponds to the value of the Name property of a Column object.
RelatedTable
Optional. A String value that specifies the name of the related table. The RelatedTable parameter corresponds to the value of the Name property of a Table object.
RelatedColumn
Optional. A String value that specifies the name of the related column for a foreign key. The RelatedColumn parameter corresponds to the value of the Name property of a Column object.

Remarks

The Columns parameter can take either the name of a column or an array of column names.

See Also

Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB)

Append Method (Columns) | Append Method (Groups) | Append Method (Indexes) | Append Method (Procedures) | Append Method (Tables) | Append Method (Users) | Append Method (Views)

Applies To: Keys Collection


Append Method (ADOX Procedures)

ADOX API Reference

Append Method (Procedures)

Adds a new Procedure object to the Procedures collection.

Syntax

Procedures.Append Name, Command

Parameters

Name
A String value that specifies the name of the procedure to create and append.
Command
An ADO Command object that represents the procedure to create and append.

Remarks

Creates a new procedure in the data source with the name and attributes specified in the Command object.

If the command text that the user specifies represents a view rather than a procedure, the behavior is dependent upon the provider being used. Append will fail if the provider does not support persisting commands.

Note When using the OLE DB Provider for Microsoft Jet, the Procedures collection Append method will allow you to specify a View rather than a Procedure in the Command parameter. The View will be added to the data source and will be added to the Procedures collection. After the Append, if the Procedures and Views collections are refreshed, the View will no longer be in the Procedures collection and will appear in the Views collection.

See Also

Procedures Append Method Example (VB)

Append Method (Columns) | Append Method (Groups) | Append Method (Indexes) | Append Method (Keys) | Append Method (Tables) | Append Method (Users) | Append Method (Views)

Applies To: Procedures Collection


Append Method (ADOX Tables)

ADOX API Reference

Append Method (Tables)

Adds a new Table object to the Tables collection.

Syntax

Tables.Append Table

Parameters

Table
A Variant value that contains a reference to the Table to append or the name of the table to create and append.

Remarks

An error will occur if the provider does not support creating tables.

See Also

Columns and Tables Append Methods, Name Property Example (VB) | ParentCatalog Property Example (VB)

Append Method (Columns) | Append Method (Groups) | Append Method (Indexes) | Append Method (Keys) | Append Method (Procedures) | Append Method (Users) | Append Method (Views)

Applies To: Tables Collection


Append Method (ADOX Users)

ADOX API Reference

Append Method (Users)

Adds a new User object to the Users collection.

Syntax

Users.Append User[, Password]

Parameters

User
A Variant value that contains the User object to append or the name of the user to create and append.
Password
Optional. A String value that contains the password for the user. The Password parameter corresponds to the value specified by the ChangePassword method of a User object.

Remarks

The Users collection of a Catalog represents all the catalog's users. The Users collection for a Group represents only the users that have a membership in the specific group.

An error will occur if the provider does not support creating users.

Note Before appending a User object to the Users collection of a Group object, a User object with the same Name as the one to be appended must already exist in the Users collection of the Catalog.

See Also

Groups and Users Append, ChangePassword Methods Example (VB)

Append Method (Columns) | Append Method (Groups) | Append Method (Indexes) | Append Method (Keys) | Append Method (Procedures) | Append Method (Tables) | Append Method (Views)

Applies To: Users Collection


Append Method (ADOX Views)

ADOX API Reference

Append Method (Views)

Creates a new View object and appends it to the Views collection.

Syntax

Views.Append Name, Command

Parameters

Name
A String value that specifies the name of the view to create.
Command
An ADO Command object that represents the view to create.

Remarks

Creates a new view in the data source with the name and attributes specified in the Command object.

If the command text that the user specifies represents a procedure rather than a view, the behavior is dependent upon the provider. Append will fail if the provider does not support persisting commands.

Note When using the OLE DB Provider for Microsoft Jet, the Views collection Append method will allow you to specify a Procedure rather than a View in the Command parameter. The Procedure will be added to the data source and will be added to the Views collection. After the Append, if the Procedures and Views collections are refreshed, the Procedure will no longer be in the Views collection and will appear in the Procedures collection.

See Also

Views Append Method Example (VB)

Append Method (Columns) | Append Method (Groups) | Append Method (Indexes) | Append Method (Keys) | Append Method (Procedures) | Append Method (Tables) | Append Method (Users)

Applies To: Views Collection


ChangePassword Method (ADOX)

ADOX API Reference

ChangePassword Method

Changes the password for a user account.

Syntax

User.ChangePassword OldPassword, NewPassword

Parameters

OldPassword
A String value that specifies the user's existing password. If the user doesn't currently have a password, use an empty string ("") for OldPassword.
NewPassword
A String value that specifies the new password.

Remarks

For security reasons, the old password must be specified in addition to the new password.

An error will occur if the provider does not support the administration of trustee properties.

See Also

Groups and Users Append, ChangePassword Methods Example (VB)

Applies To: User Object


Create Method (ADOX)

ADOX API Reference

Create Method

Creates a new catalog.

Syntax

Catalog.Create ConnectString

Parameters

ConnectString
A String value used to connect to the data source.

Remarks

The Create method creates and opens a new ADO Connection to the data source specified in ConnectString. If successful, the new Connection object is assigned to the ActiveConnection property.

An error will occur if the provider does not support creating new catalogs.

See Also

Create Method Example (VB)

ActiveConnection Property

Applies To: Catalog Object


Delete Method (ADOX Collections)

ADOX API Reference

Delete Method (Collections)

Removes an object from a collection.

Syntax

Collection.Delete Name

Parameters

Name
A Variant that specifies the name or ordinal position (index) of the object to delete.

Remarks

An error will occur if the Name does not exist in the collection.

For Tables and Users collections, an error will occur if the provider does not support deleting tables or users, respectively. For Procedures and Views collections, Delete will fail if the provider does not support persisting commands.

See Also

Procedures Delete Method Example (VB) | Views Delete Method Example (VB)

Applies To: Columns Collection | Groups Collection | Indexes Collection | Keys Collection | Procedures Collection | Tables Collection | Users Collection | Views Collection


GetObjectOwner Method (ADOX)

ADOX API Reference

GetObjectOwner Method

Returns the owner of an object in a Catalog.

Syntax

Owner = Catalog.GetObjectOwner(ObjectName, ObjectType [, ObjectTypeId])

Return Value

Returns a String value that specifies the Name of the User or Group that owns the object.

Parameters

ObjectName
A String value that specifies the name of the object for which to return the owner.
ObjectType
A Long value which can be one of the ObjectTypeEnum constants, that specifies the type of the object for which to get the owner.
ObjectTypeId
Optional. A Variant value that specifies the GUID for a provider object type not defined by the OLE DB specification. This parameter is required if ObjectType is set to adPermObjProviderSpecific; otherwise, it is not used.

Remarks

An error will occur if the provider does not support returning object owners.

See Also

GetObjectOwner and SetObjectOwner Methods Example (VB)

SetObjectOwner Method

Applies To: Catalog Object


GetPermissions Method (ADOX)

ADOX API Reference

GetPermissions Method

Returns the permissions for a group or user on an object or object container.

Syntax

ReturnValue = GroupOrUser.GetPermissions(Name, ObjectType
   [, ObjectTypeId])

Return Value

Returns a Long value that specifies a bitmask containing the permissions that the group or user has on the object. This value can be one or more of the RightsEnum constants.

Parameters

Name
A Variant value that specifies the name of the object for which to set permissions. Set Name to a null value if you want to get the permissions for the object container.
ObjectType
A Long value which can be one of the ObjectTypeEnum constants, that specifies the type of the object for which to get permissions.
ObjectTypeId
Optional. A Variant value that specifies the GUID for a provider object type not defined by the OLE DB specification. This parameter is required if ObjectType is set to adPermObjProviderSpecific; otherwise, it is not used.

See Also

GetPermissions and SetPermissions Methods Example (VB)

Name Property | SetPermissions Method

Applies To: Group Object | User Object


SetObjectOwner Method (ADOX)

ADOX API Reference

SetObjectOwner Method

Specifies the owner of an object in a Catalog.

Syntax

Catalog.SetObjectOwner ObjectName, ObjectType , OwnerName [, ObjectTypeId]

Parameters

ObjectName
A String value that specifies the name of the object for which to specify the owner.
ObjectType
A Long value which can be one of the ObjectTypeEnum constants that specifies the owner type.
OwnerName
A String value that specifies the Name of the User or Group to own the object.
ObjectTypeId
Optional. A Variant value that specifies the GUID for a provider object type not defined by the OLE DB specification. This parameter is required if ObjectType is set to adPermObjProviderSpecific; otherwise, it is not used.

Remarks

An error will occur if the provider does not support specifying object owners.

See Also

GetObjectOwner and SetObjectOwner Methods Example (VB)

GetObjectOwner Method

Applies To: Catalog Object


SetPermissions Method (ADOX)

ADOX API Reference

SetPermissions Method

Specifies the permissions for a group or user on an object.

Syntax

GroupOrUser.SetPermissions Name, ObjectType, Action, Rights [, Inherit]
   [, ObjectTypeId]

Parameters

Name
A String value that specifies the name of the object for which to set permissions.
ObjectType
A Long value which can be one of the ObjectTypeEnum constants, that specifies the type of the object for which to get permissions.
Action
A Long value which can be one of the ActionEnum constants that specifies the type of action to perform when setting permissions.
Rights
A Long value which can be a bitmask of one or more of the RightsEnum constants, that indicates the rights to set.
Inherit
Optional. A Long value which can be one of the InheritTypeEnum constants, that specifies how objects will inherit these permissions. The default value is adInheritNone.
ObjectTypeId
Optional. A Variant value that specifies the GUID for a provider object type not defined by the OLE DB specification. This parameter is required if ObjectType is set to adPermObjProviderSpecific; otherwise, it is not used.

Remarks

An error will occur if the provider does not support setting access rights for groups or users.

Note When calling SetPermissions, setting Actions to adAccessRevoke overrides any settings of the Rights parameter. Do not set Actions to adAccessRevoke if you want the rights specified in the Rights parameter to take effect.

See Also

GetPermissions and SetPermissions Methods Example (VB)

GetPermissions Method | Name Property

Applies To: Group Object | User Object


ADOX Enumerated Constants

To assist debugging, the ADOX enumerated constants list a value for each constant. However, this value is purely advisory, and may change from one release of ADOX to another. Your code should only depend on the name, not the actual value, of enumerated constants.

The following enumerated constants are defined.

Enumeration | Description
ActionEnum | Specifies the type of action to be performed when SetPermissions is called.
AllowNullsEnum | Specifies whether records with null values are indexed.
ColumnAttributesEnum | Specifies characteristics of a Column.
InheritTypeEnum | Specifies how objects will inherit permissions set with SetPermissions.
KeyTypeEnum | Specifies the type of Key: primary, foreign, or unique.
ObjectTypeEnum | Specifies the type of database object for which to set permissions or ownership.
RightsEnum | Specifies the rights or permissions for a group or user on an object.
RuleEnum | Specifies the rule to follow when a Key is deleted.
SortOrderEnum | Specifies the sort sequence for an indexed column.

See Also

ADOX API Reference | Microsoft ADOX Programmer's Reference


ActionEnum

Specifies the type of action to be performed when SetPermissions is called.

Constant | Value | Description
adAccessDeny | 3 | The group or user will be denied the specified permissions.
adAccessGrant | 1 | The group or user will have at least the requested permissions.
adAccessRevoke | 4 | Any explicit access rights the group or user has will be revoked.
adAccessSet | 2 | The group or user will have exactly the requested permissions.


AllowNullsEnum

Specifies whether records with null values are indexed.

Constant | Value | Description
adIndexNullsAllow | 0 | The index does allow entries in which the key columns are null. If a null value is entered in a key column, the entry is inserted into the index.
adIndexNullsDisallow | 1 | Default. The index does not allow entries in which the key columns are null. If a null value is entered in a key column, an error will occur.
adIndexNullsIgnore | 2 | The index does not insert entries containing null keys. If a null value is entered in a key column, the entry is ignored and no error occurs.
adIndexNullsIgnoreAny | 4 | The index does not insert entries where some key column has a null value. For an index having a multi-column key, if a null value is entered in some column, the entry is ignored and no error occurs.

See Also

Applies To: IndexNulls Property


ColumnAttributesEnum

Specifies characteristics of a Column.

Constant | Value | Description
adColFixed | 1 | The column is a fixed length.
adColNullable | 2 | The column may contain null values.

See Also

Applies To: Attributes Property


InheritTypeEnum

Specifies how objects will inherit permissions set with SetPermissions.

Constant | Value | Description
adInheritBoth | 3 | Both objects and other containers contained by the primary object inherit the entry.
adInheritContainers | 2 | Other containers that are contained by the primary object inherit the entry.
adInheritNone | 0 | Default. No inheritance occurs.
adInheritNoPropagate | 4 | The adInheritObjects and adInheritContainers flags are not propagated to an inherited entry.
adInheritObjects | 1 | Non-container objects in the container inherit the permissions.

See Also

Applies To: SetPermissions Method


KeyTypeEnum

Specifies the type of Key: primary, foreign, or unique.

Constant | Value | Description
adKeyPrimary | 1 | Default. The key is a primary key.
adKeyForeign | 2 | The key is a foreign key.
adKeyUnique | 3 | The key is unique.

See Also

Applies To: Type Property (Key)


ObjectTypeEnum

Specifies the type of database object for which to set permissions or ownership.

Constant | Value | Description
adPermObjColumn | 2 | The object is a column.
adPermObjDatabase | 3 | The object is a database.
adPermObjProcedure | 4 | The object is a procedure.
adPermObjProviderSpecific | -1 | The object is a type defined by the provider. An error will occur if the ObjectType parameter is adPermObjProviderSpecific and an ObjectTypeId is not supplied.
adPermObjTable | 1 | The object is a table.
adPermObjView | 5 | The object is a view.

See Also

Applies To: GetObjectOwner Method | GetPermissions Method | SetObjectOwner Method | SetPermissions Method


RightsEnum

Specifies the rights or permissions for a group or user on an object.

Constant | Value | Description
adRightCreate | 16384 (&H4000) | The user or group has permission to create new objects of this type.
adRightDelete | 65536 (&H10000) | The user or group has permission to delete data from an object. For objects such as Tables, the user has permission to delete data values from records.
adRightDrop | 256 (&H100) | The user or group has permission to remove objects from the catalog. For example, Tables can be deleted by a DROP TABLE SQL command.
adRightExclusive | 512 (&H200) | The user or group has permission to access the object exclusively.
adRightExecute | 536870912 (&H20000000) | The user or group has permission to execute the object.
adRightFull | 268435456 (&H10000000) | The user or group has all permissions on the object.
adRightInsert | 32768 (&H8000) | The user or group has permission to insert the object. For objects such as Tables, the user has permission to insert data into the table.
adRightMaximumAllowed | 33554432 (&H2000000) | The user or group has the maximum number of permissions allowed by the provider. Specific permissions are provider-dependent.
adRightNone | 0 | The user or group has no permissions for the object.
adRightRead | -2147483648 (&H80000000) | The user or group has permission to read the object. For objects such as Tables, the user has permission to read the data in the table.
adRightReadDesign | 1024 (&H400) | The user or group has permission to read the design for the object.
adRightReadPermissions | 131072 (&H20000) | The user or group can view, but not change, the specific permissions for an object in the catalog.
adRightReference | 8192 (&H2000) | The user or group has permission to reference the object.
adRightUpdate | 1073741824 (&H40000000) | The user or group has permission to update the object. For objects such as Tables, the user has permission to update the data in the table.
adRightWithGrant | 4096 (&H1000) | The user or group has permission to grant permissions on the object.
adRightWriteDesign | 2048 (&H800) | The user or group has permission to modify the design for the object.
adRightWriteOwner | 524288 (&H80000) | The user or group has permission to modify the owner of the object.
adRightWritePermissions | 262144 (&H40000) | The user or group can modify the specific permissions for an object in the catalog.

See Also

Applies To: GetPermissions Method | SetPermissions Method


RuleEnum

Specifies the rule to follow when a Key is deleted.

Constant | Value | Description
adRICascade | 1 | Cascade changes.
adRINone | 0 | Default. No action is taken.
adRISetDefault | 3 | Foreign key value is set to the default.
adRISetNull | 2 | Foreign key value is set to null.

See Also

Applies To: DeleteRule Property


SortOrderEnum

Specifies the sort sequence for an indexed column.

Constant | Value | Description
adSortAscending | 1 | Default. The sort sequence for the column is ascending.
adSortDescending | 2 | The sort sequence for the column is descending.

See Also

Applies To: SortOrder Property


ADOX Code Examples

Use the following code examples to learn how to use the ADOX objects, methods, properties, and events.

Note Paste the entire code example into your code editor. The example may not run correctly if partial examples are used or if paragraph formatting is lost.

  • ADOX Code Examples in Microsoft Visual Basic
  • ADOX Code Examples in Microsoft Visual C++

See Also

Catalog ActiveConnection Property Example (VB) | Columns and Tables Append Methods, Name Property Example (VB) | Command and CommandText Properties Example (VB) | Connection Close Method, Table Type Property Example (VB) | GetPermissions and SetPermissions Methods Example (VB) | Indexes Append Method Example (VB) | Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB) | Microsoft ADOX Programmer's Reference | Parameters Collection, Command Property Example (VB) | ParentCatalog Property Example (VB) | Procedures Append Method Example (VB) | Procedures Delete Method Example (VB) | Procedures Refresh Method Example (VB) | Views and Fields Collections Example (VB) | Views Append Method Example (VB) | Views Collection, CommandText Property Example (VB) | Views Delete Method Example (VB) | Views Refresh Method Example (VB)


ADOX Code Examples in Microsoft Visual Basic

These topics provide sample code to help you understand how to use ADOX. All code examples are written using Microsoft Visual Basic.

Note Paste the entire code example, from Sub to End Sub, into your code editor. The example may not run correctly if you use partial examples or if paragraph formatting is lost.

Methods

  • Columns and Tables Append Methods, Name Property Example (VB)
  • Connection Close Method, Table Type Property Example (VB)
  • Create Method Example (VB)
  • GetObjectOwner and SetObjectOwner Methods Example (VB)
  • Get Permissions and Set Permissions Methods Example (VB)
  • Groups and Users Append, ChangePassword Methods Example (VB)
  • Indexes Append Method Example (VB)
  • Keys Append Method, Key Type, RelatedColumn, RelatedTable, and UpdateRule Properties Example (VB)
  • Procedures Append Method Example (VB)
  • Procedures Delete Method Example (VB)
  • Procedures Refresh Method Example (VB)
  • Views Append Method Example (VB)
  • Views Delete Method Example (VB)
  • Views Refresh Method Example (VB)

Properties

  • Attributes Property Example (VB)
  • Catalog ActiveConnection Property Example (VB)
  • Clustered Property Example (VB)
  • Command and CommandText Properties Example (VB)
  • Command Property, Parameters Collection Example (VB)
  • CommandText Property, Views Collection Example (VB)
  • DateCreated and DateModified Properties Example (VB)
  • DefinedSize Property Example (VB)
  • DeleteRule Property Example (VB)
  • IndexNulls Property Example (VB)
  • Key Type, RelatedColumn, RelatedTable, and UpdateRule Properties, Keys Append Method Example (VB)
  • Name Property, Columns and Tables Append Methods Example (VB)
  • NumericScale and Precision Properties Example (VB)
  • ParentCatalog Property Example (VB)
  • PrimaryKey and Unique Properties Example (VB)
  • SortOrder Property Example (VB)
  • Table Type Property, Connection Close Method, Example (VB)

Collections

  • Parameters Collection, Command Property Example (VB)
  • Views Collection, CommandText Property Example (VB)
  • Views and Fields Collections Example (VB)


Attributes Property Example (VB)

This example demonstrates the Attributes property of a Column. Setting it to adColNullable allows the user to set the value of a Recordset Field to an empty string. In this situation, the user can distinguish between a record where data is not known and a record where the data does not apply.

' BeginAttributesVB
Sub AttributesX()

    Dim cnn As New ADODB.Connection
    Dim cat As New ADOX.Catalog
    Dim colTemp As New ADOX.Column
    Dim rstEmployees As New Recordset
    Dim strMessage As String
    Dim strInput As String
    Dim tblEmp As ADOX.Table

    ' Connect the catalog.
    cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;data source=c:\" & _
        "Program Files\Microsoft Office\Office\Samples\Northwind.mdb;"
    Set cat.ActiveConnection = cnn

    Set tblEmp = cat.Tables("Employees")

    ' Create a new Field object and append it to the Fields
    ' collection of the Employees table.
    colTemp.Name = "FaxPhone"
    colTemp.Type = adVarWChar
    colTemp.DefinedSize = 24
    colTemp.Attributes = adColNullable
    cat.Tables("Employees").Columns.Append colTemp

    ' Open the Employees table for updating as a Recordset
    rstEmployees.Open "Employees", cnn, adOpenKeyset, adLockOptimistic

    With rstEmployees
        ' Get user input.
        strMessage = "Enter fax number for " & _
            !FirstName & " " & !LastName & "." & vbCr & _
            "[? - unknown, X - has no fax]"
        strInput = UCase(InputBox(strMessage))
        If strInput <> "" Then
            Select Case strInput
                Case "?"
                    !FaxPhone = Null
                Case "X"
                    !FaxPhone = ""
                Case Else
                    !FaxPhone = strInput
            End Select
            .Update

            ' Print report.
            Debug.Print "Name - Fax number"
            Debug.Print !FirstName & " " & !LastName & " - ";

            If IsNull(!FaxPhone) Then
                Debug.Print "[Unknown]"
            Else
                If !FaxPhone = "" Then
                    Debug.Print "[Has no fax]"
                Else
                    Debug.Print !FaxPhone
                End If
            End If

        End If

        .Close
    End With

    ' Delete new field because this is a demonstration.
    tblEmp.Columns.Delete colTemp.Name
    cnn.Close

End Sub
' EndAttributesVB

See Also

Attributes Property | Catalog Object | Column Object


Catalog ActiveConnection Property Example (VB)

Setting the ActiveConnection property to a valid, open connection "opens" the catalog. From an open catalog, you can access the schema objects contained within that catalog.

<"C:\MDAC SDK 2.6\ADO\Source\ADOCore\Code\VB\Catalog.bas" BeginOpenConnectionVB,EndOpenConnectionVB>

Setting the ActiveConnection property to a valid connection string also "opens" the catalog.

' BeginOpenConnection2VB
Sub OpenConnectionWithString()

   Dim cat As New ADOX.Catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=c:\Program Files\Microsoft Office\" & _
      "Office\Samples\Northwind.mdb;"
   Debug.Print cat.Tables(0).Type

End Sub
' EndOpenConnection2VB

See Also

ActiveConnection Property | Catalog Object | Table Object | Tables Collection | Type Property (Table)


Clustered Property Example (VB)

This example demonstrates the Clustered property of an Index. Note that Microsoft Jet databases do not support clustered indexes, so this example will return False for the Clustered property of all indexes in the Northwind database.

' BeginClusteredVB
Sub ClusteredX()
    Dim cnn As New ADODB.Connection
    Dim cat As New ADOX.Catalog
    Dim tblLoop As ADOX.Table
    Dim idxLoop As ADOX.Index
    Dim strCnn As String

   strCnn = "Provider=SQLOLEDB;Data Source=srv;Initial Catalog=pubs;" & _
            "User Id=sa;Password=;"
    ' Connect the catalog.
   cnn.Open strCnn
   cat.ActiveConnection = cnn

    ' Enumerate Tables
    For Each tblLoop In cat.Tables
        'Enumerate Indexes
        For Each idxLoop In tblLoop.Indexes
            Debug.Print tblLoop.Name & " " & _
                idxLoop.Name & " " & idxLoop.Clustered
        Next idxLoop
    Next tblLoop

End Sub
' EndClusteredVB

See Also

Catalog Object | Clustered Property | Index Object | Table Object


Columns and Tables Append Methods, Name Property Example (VB)

The following code demonstrates how to create a new table.

' BeginCreateTableVB
Sub CreateTable()

   Dim tbl As New Table
   Dim cat As New ADOX.Catalog

'Open the catalog.
   ' Open the Catalog.
   cat.ActiveConnection = _
      "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=c:\Program Files\Microsoft Office\" & _
      "Office\Samples\Northwind.mdb;"

   tbl.Name = "MyTable"
   tbl.Columns.Append "Column1", adInteger
   tbl.Columns.Append "Column2", adInteger
   tbl.Columns.Append "Column3", adVarWChar, 50
   cat.Tables.Append tbl

End Sub
' EndCreateTableVB

See Also

Append Method (Columns) | Append Method (Tables) | Column Object | Columns Collection | Name Property | Table Object | Tables Collection


Command and CommandText Properties Example (VB)

The following code demonstrates how to use the Command property to update the text of a procedure.

' BeginProcedureTextVB
Sub ProcedureText()

   Dim cnn As New ADODB.Connection
   Dim cat As New ADOX.Catalog
   Dim cmd As New ADODB.Command

   ' Open the Connection
   cnn.Open _
      "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=c:\Program Files\Microsoft Office\" & _
      "Office\Samples\Northwind.mdb;"

   ' Open the catalog
   Set cat.ActiveConnection = cnn

   ' Get the Command
   Set cmd = cat.Procedures("CustomerById").Command

   ' Update the CommandText
   cmd.CommandText = "Select CustomerId, CompanyName, ContactName " & _
      "From Customers " & _
      "Where CustomerId = [CustId]"

   ' Update the Procedure
   Set cat.Procedures("CustomerById").Command = cmd

End Sub
' EndProcedureTextVB

See Also

ActiveConnection Property | Catalog Object | Command Property | Procedure Object | Procedures Collection


Connection Close Method, Table Type Property Example (VB)

Setting the ActiveConnection property to Nothing should "close" the catalog. Associated collections will be empty. Any objects that were created from schema objects in the catalog will be orphaned. Any properties on those objects that have been cached will still be available, but attempting to read properties that require a call to the provider will fail.

' BeginCloseConnectionVB
Sub CloseConnectionByNothing()

   Dim cnn As New ADODB.Connection
   Dim cat As New ADOX.Catalog
   Dim tbl As ADOX.Table

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source= c:\Program Files\Microsoft Office\" & _
      "Office\Samples\Northwind.mdb;"
   Set cat.ActiveConnection = cnn
   Set tbl = cat.Tables(0)
   Debug.Print tbl.Type    ' Cache tbl.Type info
   Set cat.ActiveConnection = Nothing
   Debug.Print tbl.Type    ' tbl is orphaned
   ' Previous line will succeed if this was cached
   Debug.Print tbl.Columns(0).DefinedSize
   ' Previous line will fail if this info has not been cached

End Sub
' EndCloseConnectionVB

Closing a Connection object that was used to "open" the catalog should have the same effect as setting the ActiveConnection property to Nothing.

<"C:\MDAC SDK 2.6\ADO\Source\ADOCore\Code\VB\Connection.bas" BeginCloseConnection2VB,EndCloseConnection2VB

See Also

ActiveConnection Property | Catalog Object | Column Object | Columns Collection | Table Object | Tables Collection | Type Property (Table)


Create Method Example (VB)

The following code shows how to create a new Microsoft Jet database with the Create method.

Attribute VB_Name = "Create"

See Also

Catalog Object | Create Method


DateCreated and DateModified Properties Example (VB)

This example demonstrates the DateCreated and DateModified properties by adding a new Column to an existing Table and by creating a new Table. The DateOutput procedure is required for this example to run.

' BeginDateCreatedVB
Sub DateCreatedX()
    Dim cat As New ADOX.Catalog
    Dim tblEmployees As ADOX.Table
    Dim tblNewTable As ADOX.Table

    ' Connect the catalog.
    cat.ActiveConnection = _
        "Provider=Microsoft.Jet.OLEDB.4.0;" & _
        "Data Source=c:\Program Files\" & _
        "Microsoft Office\Office\Samples\Northwind.mdb;"

    With cat
        Set tblEmployees = .Tables("Employees")

        ' Print current information about the Employees table.
        DateOutput "Current properties", tblEmployees

        ' Create and append column to the Employees table.
        tblEmployees.Columns.Append "NewColumn", adInteger
        .Tables.Refresh

        ' Print new information about the Employees table.
        DateOutput "After creating a new column", tblEmployees

        ' Delete new column because this is a demonstration.
        tblEmployees.Columns.Delete "NewColumn"

        ' Create and append new Table object to the Northwind database.
        Set tblNewTable = New ADOX.Table
        tblNewTable.Name = "NewTable"
        tblNewTable.Columns.Append "NewColumn", adInteger
        .Tables.Append tblNewTable
        .Tables.Refresh

        ' Print information about the new Table object.
        DateOutput "After creating a new table", .Tables("NewTable")

        ' Delete new Table object because this is a demonstration.
        .Tables.Delete tblNewTable.Name

    End With

End Sub
' EndDateCreatedVB

See Also

DateCreated Property | DateModified Property | Procedure Object | Procedures Collection | View Object | Views Collection


DefinedSize Property Example (VB)

This example demonstrates the DefinedSize property of a Column. The code will redefine the size of the FirstName column of the Employees table of the Northwind database. Then, the change in the values of the FirstName Field of a Recordset based on the Employees table is displayed. Note that by default, the FirstName field becomes padded with spaces after you redefine the DefinedSize property.

' BeginDefinedSizeVB
Public Sub DefinedSizeX()

    Dim rstEmployees As ADODB.Recordset
    Dim catNorthwind As New ADOX.Catalog
    Dim colFirstName As ADOX.Column
    Dim colNewFirstName As New ADOX.Column
    Dim aryFirstName() As String
    Dim i As Integer
    Dim strCnn As String

    strCnn = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
             "Data Source=c:\Program Files\" & _
             "Microsoft Office\Office\Samples\Northwind.mdb;"

    ' Open a Recordset for the Employees table.
    Set rstEmployees = New ADODB.Recordset
    rstEmployees.Open "Employees", strCnn, adOpenKeyset, , adCmdTable
    ReDim aryFirstName(rstEmployees.RecordCount)

    ' Open a Catalog for the Northwind database,
    ' using same connection as rstEmployees
    Set catNorthwind.ActiveConnection = rstEmployees.ActiveConnection

    ' Loop through the recordset displaying the contents
    ' of the FirstName field, the field's defined size,
    ' and its actual size.
    ' Also store FirstName values in aryFirstName array.
    rstEmployees.MoveFirst
    Debug.Print " "
    Debug.Print "Original Defined Size and Actual Size"
    i = 0
    Do Until rstEmployees.EOF
        Debug.Print "Employee name: " & rstEmployees!FirstName & _
            " " & rstEmployees!LastName
        Debug.Print "    FirstName Defined size: " _
            & rstEmployees!FirstName.DefinedSize
        Debug.Print "    FirstName Actual size: " & _
            rstEmployees!FirstName.ActualSize
            aryFirstName(i) = rstEmployees!FirstName
        rstEmployees.MoveNext
        i = i + 1
    Loop
    rstEmployees.Close

    ' Redefine the DefinedSize of FirstName in the catalog
    Set colFirstName = catNorthwind.Tables("Employees").Columns("FirstName")
    colNewFirstName.Name = colFirstName.Name
    colNewFirstName.Type = colFirstName.Type
    colNewFirstName.DefinedSize = colFirstName.DefinedSize + 1

    ' Append new FirstName column to catalog
    catNorthwind.Tables("Employees").Columns.Delete colFirstName.Name
    catNorthwind.Tables("Employees").Columns.Append colNewFirstName

    ' Open Employee table in Recordset for updating
    rstEmployees.Open "Employees", catNorthwind.ActiveConnection, _
        adOpenKeyset, adLockOptimistic, adCmdTable

    ' Loop through the recordset displaying the contents
    ' of the FirstName field, the field's defined size,
    ' and its actual size.
    ' Also restore FirstName values from aryFirstName.
    rstEmployees.MoveFirst
    Debug.Print " "
    Debug.Print "New Defined Size and Actual Size"
    i = 0
    Do Until rstEmployees.EOF
        rstEmployees!FirstName = aryFirstName(i)
        Debug.Print "Employee name: " & rstEmployees!FirstName & _
            " " & rstEmployees!LastName
        Debug.Print "    FirstName Defined size: " _
            & rstEmployees!FirstName.DefinedSize
        Debug.Print "    FirstName Actual size: " & _
            rstEmployees!FirstName.ActualSize
        rstEmployees.MoveNext
        i = i + 1
    Loop
    rstEmployees.Close

    ' Restore original FirstName column to catalog
    catNorthwind.Tables("Employees").Columns.Delete colNewFirstName.Name
    catNorthwind.Tables("Employees").Columns.Append colFirstName

    ' Restore original FirstName values to Employees table
    rstEmployees.Open "Employees", catNorthwind.ActiveConnection, _
        adOpenKeyset, adLockOptimistic, adCmdTable

    rstEmployees.MoveFirst
    i = 0
    Do Until rstEmployees.EOF
        rstEmployees!FirstName = aryFirstName(i)
        rstEmployees.MoveNext
        i = i + 1
    Loop
    rstEmployees.Close

    Set catNorthwind = Nothing

End Sub
' EndDefinedSizeVB

See Also

Column Object | DefinedSize Property


DeleteRule Property Example (VB)

This example demonstrates the DeleteRule property of a Key object. The code appends a new Table and then defines a new primary key, setting DeleteRule to adRICascade.

' BeginDeleteRuleVB
Sub DeleteRuleX()

    Dim kyPrimary As New ADOX.Key
    Dim cat As New ADOX.Catalog
    Dim tblNew As New ADOX.Table

    ' Connect the catalog
    cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
                     "data source=c:\Program Files\" & _
                     "Microsoft Office\Office\Samples\Northwind.mdb;"

    ' Name new table
    tblNew.Name = "NewTable"

    ' Append a numeric and a text field to new table.
    tblNew.Columns.Append "NumField", adInteger, 20
    tblNew.Columns.Append "TextField", adVarWChar, 20

    ' Append the new table
    cat.Tables.Append tblNew

    ' Define the Primary key
    kyPrimary.Name = "NumField"
    kyPrimary.Type = adKeyPrimary
    kyPrimary.RelatedTable = "Customers"
    kyPrimary.Columns.Append "NumField"
    kyPrimary.Columns("NumField").RelatedColumn = "CustomerId"
    kyPrimary.DeleteRule = adRICascade

    ' Append the primary key
    cat.Tables("NewTable").Keys.Append kyPrimary

    'Delete the table as this is a demonstration
    cat.Tables.Delete tblNew.Name

End Sub
' EndDeleteRuleVB

See Also

DeleteRule Property | Key Object


GetObjectOwner and SetObjectOwner Methods Example (VB)

This example demonstrates the GetObjectOwner and SetObjectOwner methods. This code assumes the existence of the group Accounting (see the Groups and Users Append, ChangePassword Methods Example to see how to add this group to the system). The owner of the Categories table is set to Accounting.

' BeginOwnersVB
Sub OwnersX()

    Dim tblLoop As New ADOX.Table
    Dim cat As New ADOX.Catalog
    Dim strOwner As String

    ' Open the Catalog.
    cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
        "Data Source=c:\Program Files\" & _
        "Microsoft Office\Office\Samples\Northwind.mdb;" & _
        "jet oledb:system database=" & _
        "c:\Program Files\Microsoft Office\Office\system.mdw"

    ' Print the original owner of Categories
    strOwner = cat.GetObjectOwner("Categories", adPermObjTable)
    Debug.Print "Owner of Categories: " & strOwner

    ' Set the owner of Categories to Accounting
    cat.SetObjectOwner "Categories", adPermObjTable, "Accounting"

    ' List the owners of all tables and columns in the catalog.
    For Each tblLoop In cat.Tables
        Debug.Print "Table: " & tblLoop.Name
        Debug.Print "   Owner: " & _
            cat.GetObjectOwner(tblLoop.Name, adPermObjTable)
    Next tblLoop

    ' Restore the original owner of Categories
    cat.SetObjectOwner "Categories", adPermObjTable, strOwner

End Sub
' EndOwnersVB

See Also

Catalog Object | GetObjectOwner Method | SetObjectOwner Method


GetPermissions and SetPermissions Methods Example (VB)

This example demonstrates the GetPermissions and SetPermissions methods. The following code gives full access for the Orders table to the Admin user.

' BeginGrantPermissionsVB
Sub GrantPermissions()

    Dim cnn As New ADODB.Connection
    Dim cat As New ADOX.Catalog
    Dim lngPerm As Long

    ' Opens a connection to the northwind database
    ' using the Microsoft Jet 4.0 provider
    cnn.Provider = "Microsoft.Jet.OLEDB.4.0"
    cnn.Open "data source=c:\Program Files\" & _
        "Microsoft Office\Office\Samples\Northwind.mdb;" & _
        "jet oledb:system database=" & _
        "c:\Program Files\Microsoft Office\Office\system.mdw"

    Set cat.ActiveConnection = cnn

    ' Retrieve original permissions
    lngPerm = cat.Users("admin").GetPermissions("Orders", adPermObjTable)
    Debug.Print "Original permissions: " & Str(lngPerm)

    ' Revoke all permissions
    cat.Users("admin").SetPermissions "Orders", adPermObjTable, _
        adAccessRevoke, adRightFull

    ' Display permissions
    Debug.Print "Revoked permissions: " & _
        Str(cat.Users("admin").GetPermissions("Orders", adPermObjTable))

    ' Give the Admin user full rights on the orders object
    cat.Users("admin").SetPermissions "Orders", adPermObjTable, _
        adAccessSet, adRightFull

    ' Display permissions
    Debug.Print "Full permissions: " & _
        Str(cat.Users("admin").GetPermissions("Orders", adPermObjTable))

    ' Restore original permissions
    cat.Users("admin").SetPermissions "Orders", adPermObjTable, _
        adAccessSet, lngPerm

    ' Display permissions
    Debug.Print "Final permissions: " & _
        Str(cat.Users("admin").GetPermissions("Orders", adPermObjTable))

End Sub
' EndGrantPermissionsVB

See Also

Catalog Object | GetPermissions Method | SetPermissions Method | User Object | Users Collection


Groups and Users Append, ChangePassword Methods Example (VB)

This example demonstrates the Append method of Groups, as well as the Append method of Users by adding a new Group and a new User to the system. The new Group is appended to the Groups collection of the new User. Consequently, the new User is added to the Group. Also, the ChangePassword method is used to specify the User password.

' BeginGroupVB
Sub GroupX()

    Dim cat As ADOX.Catalog
    Dim usrNew As ADOX.User
    Dim usrLoop As ADOX.User
    Dim grpLoop As ADOX.Group

    Set cat = New ADOX.Catalog
    cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
        "Data Source=c:\Program Files\" & _
        "Microsoft Office\Office\Samples\Northwind.mdb;" & _
        "jet oledb:system database=c:\samples\system.mdb"

    With cat
        'Create and append new group with a string.
        .Groups.Append "Accounting"

        ' Create and append new user with an object.
        Set usrNew = New ADOX.User
        usrNew.Name = "Pat Smith"
        usrNew.ChangePassword "", "Password1"
        .Users.Append usrNew

        ' Make the user Pat Smith a member of the
        ' Accounting group by creating and adding the
        ' appropriate Group object to the user's Groups
        ' collection. The same is accomplished if a User
        ' object representing Pat Smith is created and
        ' appended to the Accounting group Users collection
        usrNew.Groups.Append "Accounting"

        ' Enumerate all User objects in the
        ' catalog's Users collection.
        For Each usrLoop In .Users
            Debug.Print "  " & usrLoop.Name
            Debug.Print "    Belongs to these groups:"
            ' Enumerate all Group objects in each User
            ' object's Groups collection.
            If usrLoop.Groups.Count <> 0 Then
                For Each grpLoop In usrLoop.Groups
                    Debug.Print "    " & grpLoop.Name
                Next grpLoop
            Else
                Debug.Print "    [None]"
            End If
        Next usrLoop

        ' Enumerate all Group objects in the default
        ' workspace's Groups collection.
        For Each grpLoop In .Groups
            Debug.Print "  " & grpLoop.Name
            Debug.Print "    Has as its members:"
            ' Enumerate all User objects in each Group
            ' object's Users collection.
            If grpLoop.Users.Count <> 0 Then
                For Each usrLoop In grpLoop.Users
                    Debug.Print "    " & usrLoop.Name
                Next usrLoop
            Else
                Debug.Print "    [None]"
            End If
        Next grpLoop

        ' Delete new User and Group objects because this
        ' is only a demonstration.
        .Users.Delete "Pat Smith"
        .Groups.Delete "Accounting"

    End With

End Sub
' EndGroupVB

See Also

Append Method (Groups) | Append Method (Users) | Catalog Object | ChangePassword Method | Group Object | Groups Collection | User Object | Users Collection


Indexes Append Method Example (VB)

The following code demonstrates how to create a new index. The index is on two columns in the table.

Attribute VB_Name = "IndexesAppend"
Option Explicit

' BeignCreateIndexVB
Sub CreateIndex()

   Dim tbl As New Table
   Dim idx As New ADOX.Index
   Dim cat As New ADOX.Catalog

'Open the catalog.
   ' Open the Catalog.
   cat.ActiveConnection = _
      "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=c:\Program Files\Microsoft Office\" & _
      "Office\Samples\Northwind.mdb;"

   ' Define the table and append it to the catalog
   tbl.Name = "MyTable"
   tbl.Columns.Append "Column1", adInteger
   tbl.Columns.Append "Column2", adInteger
   tbl.Columns.Append "Column3", adVarWChar, 50
   cat.Tables.Append tbl

   ' Define a multi-column index
   idx.Name = "multicolidx"
   idx.Columns.Append "Column1"
   idx.Columns.Append "Column2"

   ' Append the index to the table
   tbl.Indexes.Append idx

End Sub
' EndCreateIndexVB

See Also

Append Method (Indexes) | Index Object | Indexes Collection


IndexNulls Property Example (VB)

This example demonstrates the IndexNulls property of an Index. The code creates a new index and sets the value of IndexNulls based on user input (from a list box named List1). Then, the Index is appended to the Employees Table in the Northwind Catalog. The new Index is applied to a Recordset based on the Employees table, and the Recordset is opened. A new record is added to the Employees table, with a Null value in the indexed field. Whether this new record is displayed depends on the setting of the IndexNulls property.

<"C:\MDAC SDK 2.6\ADO\Source\ADOX\Code\VB\IndexNulls.frm" BeginIndexNullsVB,EndIndexNullsVB>

See Also

Index Object | IndexNulls Property


Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VB)

The following code demonstrates how to create a new foreign key. It assumes two tables (Customers and Orders) exist.

' BeginCreateKeyVB
Sub CreateKey()

    Dim kyForeign As New ADOX.Key
    Dim cat As New ADOX.Catalog

    ' Connect the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=c:\Program Files\Microsoft Office\" & _
      "Office\Samples\Northwind.mdb;"

    ' Define the foreign key
    kyForeign.Name = "CustOrder"
    kyForeign.Type = adKeyForeign
    kyForeign.RelatedTable = "Customers"
    kyForeign.Columns.Append "CustomerId"
    kyForeign.Columns("CustomerId").RelatedColumn = "CustomerId"
    kyForeign.UpdateRule = adRICascade

    ' Append the foreign key
    cat.Tables("Orders").Keys.Append kyForeign

    'Delete the Key as this is a demonstration
    cat.Tables("Orders").Keys.Delete kyForeign.Name
End Sub
' EndCreateKeyVB

See Also

Append Method (Columns) | Append Method (Keys) | Catalog Object | Column Object | Columns Collection | Key Object | Keys Collection | Name Property | RelatedColumn Property | RelatedTable Property | Table Object | Tables Collection | Type Property (Key) | UpdateRule Property


NumericScale and Precision Properties Example (VB)

This example demonstrates the NumericScale and Precision properties of the Column object. This code displays their value for the Order Details table of the Northwind database.

' BeginNumericScalePrecVB
Sub NumericScalePrecX()

    Dim cnn As New ADODB.Connection
    Dim cat As New ADOX.Catalog
    Dim tblOD As ADOX.Table
    Dim colLoop As ADOX.Column

    ' Connect the catalog.
    cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
        "data source=c:\Program Files\" & _
        "Microsoft Office\Office\Samples\Northwind.mdb;"
    Set cat.ActiveConnection = cnn

    ' Retrieve the Order Details table
    Set tblOD = cat.Tables("Order Details")

    ' Display numeric scale and precision of
    ' small integer fields.
    For Each colLoop In tblOD.Columns
        If colLoop.Type = adSmallInt Then
            MsgBox "Column: " & colLoop.Name & vbCr & _
                "Numeric scale: " & _
                    colLoop.NumericScale & vbCr & _
                "Precision: " & colLoop.Precision
        End If
    Next colLoop

End Sub
' EndNumericScalePrecVB

See Also

Column Object | NumericScale Property | Precision Property


Parameters Collection, Command Property Example (VB)

The following code demonstrates how to use the Command property with the Command object to retrieve parameter information for the procedure.

' BeginParametersVB
Sub ProcedureParameters()

   Dim cnn As New ADODB.Connection
   Dim cmd As ADODB.Command
   Dim prm As ADODB.Parameter
   Dim cat As New ADOX.Catalog

   ' Open the Connection
   cnn.Open _
      "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=c:\Program Files\Microsoft Office\" & _
      "Office\Samples\Northwind.mdb;"

   ' Open the catalog
   Set cat.ActiveConnection = cnn

   ' Get the command object
   Set cmd = cat.Procedures("CustomerById").Command

   ' Retrieve Parameter information
   cmd.Parameters.Refresh
   For Each prm In cmd.Parameters
      Debug.Print prm.Name & ":" & prm.Type
   Next

End Sub
' EndParametersVB

See Also

ActiveConnection Property | Catalog Object | Command Property | Procedure Object | Procedures Collection


ParentCatalog Property Example (VB)

The following code demonstrates how to use the ParentCatalog property to access a provider-specific property prior to appending a table to a catalog. The property is AutoIncrement, which creates an AutoIncrement field in a Microsoft Jet database.

' BeginCreateAutoIncrColumnVB
Sub CreateAutoIncrColumn()

   Dim cnn As New ADODB.Connection
   Dim cat As New ADOX.Catalog
   Dim tbl As New ADOX.Table

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source= c:\Program Files\" & _
      "Microsoft Office\Office\Samples\Northwind.mdb;"
   Set cat.ActiveConnection = cnn

   With tbl
      .Name = "MyContacts"
      Set .ParentCatalog = cat
      ' Create fields and append them to the new Table object.
      .Columns.Append "ContactId", adInteger
      ' Make the ContactId column and auto incrementing column
      .Columns("ContactId").Properties("AutoIncrement") = True
      .Columns.Append "CustomerID", adVarWChar
      .Columns.Append "FirstName", adVarWChar
      .Columns.Append "LastName", adVarWChar
      .Columns.Append "Phone", adVarWChar, 20
      .Columns.Append "Notes", adLongVarWChar
   End With

   cat.Tables.Append tbl

   Set cat = Nothing

End Sub
' EndCreateAutoIncrColumnVB

See Also

Append Method (Columns) | Append Method (Tables) | Catalog Object | Column Object | Columns Collection | Name Property | ParentCatalog Property | Table Object | Type Property (Column)


PrimaryKey and Unique Properties Example (VB)

This example demonstrates the PrimaryKey and Unique properties of an Index. The code creates a new table with two columns. The PrimaryKey and Unique properties are used to make one column the primary key for which duplicate values are not allowed.

' BeginPrimaryKeyVB
Sub PrimaryKeyX()

    Dim catNorthwind As New ADOX.Catalog
    Dim tblNew As New ADOX.Table
    Dim idxNew As New ADOX.Index
    Dim idxLoop As New ADOX.Index
    Dim colLoop As New ADOX.Column

    ' Connect the catalog
    catNorthwind.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
        "data source=c:\Program Files\" & _
        "Microsoft Office\Office\Samples\Northwind.mdb;"

    ' Name new table
    tblNew.Name = "NewTable"

    ' Append a numeric and a text field to new table.
    tblNew.Columns.Append "NumField", adInteger, 20
    tblNew.Columns.Append "TextField", adVarWChar, 20

    ' Append new Primary Key index on NumField column
    ' to new table
    idxNew.Name = "NumIndex"
    idxNew.Columns.Append "NumField"
    idxNew.PrimaryKey = True
    idxNew.Unique = True
    tblNew.Indexes.Append idxNew

    ' Append an index on Textfield to new table.
    ' Note the different technique: Specifying index and
    ' column name as parameters of the Append method
    tblNew.Indexes.Append "TextIndex", "TextField"

    ' Append the new table
    catNorthwind.Tables.Append tblNew

    With tblNew

        Debug.Print tblNew.Indexes.Count & " Indexes in " & _
            tblNew.Name & " Table"

        ' Enumerate Indexes collection.
        For Each idxLoop In .Indexes

            With idxLoop
                Debug.Print "Index " & .Name
                Debug.Print "   Primary key = " & .PrimaryKey
                Debug.Print "   Unique = " & .Unique

                ' Enumerate Columns collection of each Index
                ' object.
                Debug.Print "    Columns"
                For Each colLoop In .Columns
                    Debug.Print "       " & colLoop.Name
                Next colLoop

            End With

        Next idxLoop

    End With

    ' Delete new table as this is a demonstration
    catNorthwind.Tables.Delete tblNew.Name
    Set catNorthwind = Nothing

End Sub
' EndPrimaryKeyVB

See Also

Index Object | PrimaryKey Property | Unique Property


Procedures Append Method Example (VB)

The following code demonstrates how to use a Command object and the Procedures collection Append method to create a new procedure in the underlying data source.

' BeginCreateProcedureVB
Sub CreateProcedure()

   Dim cnn As New ADODB.Connection
   Dim cmd As New ADODB.Command
   Dim prm As ADODB.Parameter
   Dim cat As New ADOX.Catalog

   ' Open the Connection
   cnn.Open _
      "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=c:\Program Files\Microsoft Office\" & _
      "Office\Samples\Northwind.mdb;"

   ' Create the parameterized command (Microsoft Jet specific)
   Set cmd.ActiveConnection = cnn
   cmd.CommandText = "PARAMETERS [CustId] Text;" & _
      "Select * From Customers Where CustomerId = [CustId]"

   ' Open the Catalog
   Set cat.ActiveConnection = cnn

   ' Create the new Procedure
   cat.Procedures.Append "CustomerById", cmd

End Sub
' EndCreateProcedureVB

See Also

ActiveConnection Property | Append Method (Procedures) | Catalog Object | Procedure Object | Procedures Collection


Procedures Delete Method Example (VB)

The following code demonstrates how to delete a procedure using the Procedures collection Delete method.

' BeginDeleteProcedureVB
Sub DeleteProcedure()

Dim cat As New ADOX.Catalog

   ' Open the Catalog.
   cat.ActiveConnection = _
      "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=c:\Program Files\Microsoft Office\" & _
      "Office\Samples\Northwind.mdb;"

   ' Delete the Procedure.
   cat.Procedures.Delete "CustomerById"

End Sub
' EndDeleteProcedureVB

See Also

ActiveConnection Property | Catalog Object | Delete Method (Collections) | Procedure Object | Procedures Collection


Procedures Refresh Method Example (VB)

The following code shows how to refresh the Procedures collection of a Catalog. This is required before Procedure objects from the Catalog can be accessed.

Attribute VB_Name = "ProceduresRefresh"

See Also

Catalog Object | Procedures Collection | Refresh Method


SortOrder Property Example (VB)

This example demonstrates the SortOrder property of a Column that has been appended to the Columns collection of an Index. The code appends an ascending index to the Country column in the Employees table, then displays the records. Then the code appends a descending index to the Country column in the Employees table and displays the records again. The difference between ascending and descending indexes is shown.

' BeginSortOrderVB
Sub SortOrderX()

    Dim cnn As New ADODB.Connection
    Dim catNorthwind As New ADOX.Catalog
    Dim idxAscending As New ADOX.Index
    Dim idxDescending As New ADOX.Index
    Dim rstEmployees As New ADODB.Recordset

    ' Connect the catalog.
    cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
        "data source=c:\Program Files\" & _
        "Microsoft Office\Office\Samples\Northwind.mdb;"
    Set catNorthwind.ActiveConnection = cnn

    ' Append Country column to new index
    idxAscending.Columns.Append "Country"
    idxAscending.Columns("Country").SortOrder = adSortAscending
    idxAscending.Name = "Ascending"

    'Append new index to Employees table
    catNorthwind.Tables("Employees").Indexes.Append idxAscending

    rstEmployees.Index = idxAscending.Name
    rstEmployees.Open "Employees", cnn, adOpenKeyset, _
        adLockOptimistic, adCmdTableDirect

    With rstEmployees
        .MoveFirst
        Debug.Print "Index = " & .Index
        Debug.Print "  Country - Name"

        ' Enumerate the Recordset. The value of the
        ' IndexNulls property will determine if the newly
        ' added record appears in the output.
        Do While Not .EOF
            Debug.Print "    " & !Country & " - " & _
                !FirstName & " " & !LastName
            .MoveNext
        Loop

        .Close
    End With

    ' Append Country column to new index
    idxDescending.Columns.Append "Country"
    idxDescending.Columns("Country").SortOrder = adSortDescending
    idxDescending.Name = "Descending"

    'Append descending index to Employees table
    catNorthwind.Tables("Employees").Indexes.Append idxDescending

    rstEmployees.Index = idxDescending.Name
    rstEmployees.Open "Employees", cnn, adOpenKeyset, _
        adLockOptimistic, adCmdTableDirect

'    DisplaySortedRecords (rstEmployees)

     With rstEmployees
        .MoveFirst
        Debug.Print "Index = " & .Index
        Debug.Print "  Country - Name"

        ' Enumerate the Recordset. The value of the
        ' IndexNulls property will determine if the newly
        ' added record appears in the output.
        Do While Not .EOF
            Debug.Print "    " & !Country & " - " & _
                !FirstName & " " & !LastName
            .MoveNext
        Loop

        .Close
    End With

    ' Delete new Indexes because this is a demonstration.
    catNorthwind.Tables("Employees").Indexes.Delete idxAscending.Name
    catNorthwind.Tables("Employees").Indexes.Delete idxDescending.Name
    Set catNorthwind = Nothing

End Sub
' EndSortOrderVB

See Also

Column Object | Columns Collection | Index Object | SortOrder Property


Views and Fields Collections Example (VB)

The following code demonstrates how to use the Command property and the Recordset object to retrieve field information for the view.

' BeginViewFieldsVB
Sub ViewFields()

   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset
   Dim fld As ADODB.Field
   Dim cat As New ADOX.Catalog

   ' Open the Connection
   cnn.Open _
      "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=c:\Program Files\Microsoft Office\" & _
      "Office\Samples\Northwind.mdb;"

   ' Open the catalog
   Set cat.ActiveConnection = cnn

   ' Set the Source for the Recordset
   Set rst.Source = cat.Views("AllCustomers").Command

   ' Retrieve Field information
   rst.Fields.Refresh
   For Each fld In rst.Fields
      Debug.Print fld.Name & ":" & fld.Type
   Next

End Sub
' EndViewFieldsVB

See Also

ActiveConnection Property | Catalog Object | Command Property | View Object | Views Collection


Views Append Method Example (VB)

The following code demonstrates how to use a Command object and the Views collection Append method to create a new view in the underlying data source.

' BeginCreateViewVB
Sub CreateView()

   Dim cmd As New ADODB.Command
   Dim cat As New ADOX.Catalog

   ' Open the Catalog
   cat.ActiveConnection = _
      "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=c:\Program Files\Microsoft Office\" & _
      "Office\Samples\Northwind.mdb;"

   ' Create the command representing the view.
      cmd.CommandText = "Select * From Customers"

   ' Create the new View
   cat.Views.Append "AllCustomers", cmd

End Sub
' EndCreateViewVB

See Also

ActiveConnection Property | Append Method (Views) | Catalog Object | View Object | Views Collection


Views Collection, CommandText Property Example (VB)

The following code demonstrates how to use the Command property to update the text of a view.

' BeginViewsCollectionVB

See Also

ActiveConnection Property | Catalog Object | Command Property | View Object | Views Collection


Views Delete Method Example (VB)

The following code shows how to use the Delete method to delete a view from the catalog.

' BeginDeleteViewVB
Sub DeleteView()

   Dim cat As New ADOX.Catalog

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=c:\Program Files\Microsoft Office\" & _
      "Office\Samples\Northwind.mdb;"

   'Delete the View
   cat.Views.Delete "AllCustomers"

End Sub
' EndDeleteViewVB

See Also

Delete Method (Collections) | Views Collection


Views Refresh Method Example (VB)

The following code shows how to refresh the Views collection of a Catalog. This is required before View objects from the Catalog can be accessed.

Attribute VB_Name = "ViewsRefresh"

See Also

Refresh Method | Views Collection


ADOX Code Examples in Microsoft Visual C++

These topics provide sample code to help you understand how to use ADOX. All code examples are written using Microsoft Visual C++.

Note Paste the entire code example, from beginning to end, into your code editor. The example may not run correctly if you use partial examples or if paragraph formatting is lost.

Methods

  • Columns and Tables Append Methods, Name Property Example (VC++)
  • Connection Close Method, Table Type Property Example (VC++)
  • Create Method Example (VC++)
  • GetObjectOwner and SetObjectOwner Methods Example (VC++)
  • GetPermissions and SetPermissions Methods Example (VC++)
  • Groups and Users Append, ChangePassword Methods Example (VC++)
  • Indexes Append Method Example (VC++)
  • Keys Append Method, Key Type, RelatedColumn, RelatedTable, and UpdateRule Properties Example (VC++)

Properties

  • Attributes Property Example (VC++)
  • Catalog ActiveConnection Property Example (VC++)
  • Clustered Property Example (VC++)
  • Command and CommandText Properties Example (VC++)
  • Command Property, Parameters Collection Example (VC++)
  • DateCreated and DateModified Properties Example (VC++)
  • DefinedSize Property Example (VC++)
  • DeleteRule Property Example (VC++)
  • IndexNulls Property Example (VC++)
  • Key Type, RelatedColumn, RelatedTable, UpdateRule Properties, Keys Append Method Example (VC++)
  • Name Property, Columns and Tables Append Methods Example (VC++)
  • NumericScale and Precision Properties Example (VC++)
  • ParentCatalog Property Example (VC++)
  • PrimaryKey and Unique Properties Example (VC++)
  • Table Type Property, Connection Close Method Example (VC++)

Collections

  • Parameters Collection, Command Property Example (VC++)


Attributes Property Example (VC++)

This example demonstrates the Attributes property of a Column. Setting it to adColNullable allows the user to set the value of a Recordset Field to an empty string. In this situation, the user can distinguish between a record where data is not known and a record where the data does not apply.

// BeginAttributesCpp
#import "c:\Program Files\Common Files\system\ado\msado15.dll"
#import "c:\Program Files\Common Files\system\ado\msadox.dll" \
    no_namespace

#include "iostream.h"
#include "stdio.h"
#include "conio.h"
#include "ADOXAttributesX.h"

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void AttributesX(void);

//////////////////////////////////////////////////////////
//                                                      //
//         Main Function                                //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    AttributesX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//        AttributesX Function                          //
//                                                      //
//////////////////////////////////////////////////////////
void AttributesX(void)
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _CatalogPtr m_pCatalog  = NULL;
    _ColumnPtr m_pColumn  = NULL;
    _TablePtr m_pTable  = NULL;

    // Define ADODB object pointers
    ADODB::_ConnectionPtr m_pCnn         = NULL;
    ADODB::_RecordsetPtr m_pRstEmployees  = NULL;

    IADORecordBinding *picRs  = NULL;   // Interface Pointer Declared
    CEmployeeRs emprs;            // C++ Class Object

    // Define string variables.
    _bstr_t strcnn("Provider=Microsoft.Jet.OLEDB.4.0;"
        "Data Source= c:\\Program Files\\Microsoft Office\\"
        "Office\\Samples\\Northwind.mdb;");

    try
    {
        // Connect the catalog.
        TESTHR(hr = m_pCnn.CreateInstance(__uuidof (ADODB::Connection)));
        TESTHR(hr = m_pCatalog.CreateInstance(__uuidof (Catalog)));
        TESTHR(hr = m_pColumn.CreateInstance(__uuidof(Column)));
        TESTHR(hr = m_pRstEmployees.CreateInstance(
            __uuidof(ADODB::Recordset)));

        m_pCnn->Open(strcnn,"","",NULL);

        m_pCatalog->PutActiveConnection(
            _variant_t((IDispatch *) m_pCnn));
        m_pTable= m_pCatalog->Tables->GetItem("Employees");

        // Create a new Field object and append it to the Fields
        // collection of the Employees table.
        m_pColumn->Name = "FaxPhone";
        m_pColumn->Type = adVarWChar;
        m_pColumn->DefinedSize = 24;
        m_pColumn->Attributes = adColNullable;

        m_pCatalog->Tables->GetItem("Employees")->Columns->
            Append("FaxPhone",adVarWChar,24);

        // Open the Employees table for updating as a Recordset.
        m_pRstEmployees->Open("Employees",
            _variant_t((IDispatch *) m_pCnn),
            ADODB::adOpenKeyset,ADODB::adLockOptimistic,
            ADODB::adCmdTable);

        // Get user input.
        printf("Enter fax number for : %s %s\n",(LPSTR) (_bstr_t)
            m_pRstEmployees->Fields->GetItem("LastName")->Value,
           (LPSTR) (_bstr_t) m_pRstEmployees->Fields->
            GetItem("FirstName")->Value);
        printf("[? - unknown, X - has no fax] : \n");
        char strInput[10];
        gets(strInput);
        char* strTemp = strtok(strInput," \t");
        _variant_t vNull;
        vNull.vt = VT_BSTR;
        vNull.bstrVal = NULL;
        if(strTemp!=NULL)
        {
            if(strcmp(strTemp,"?") == 0)
            {
                m_pRstEmployees->Fields->GetItem("FaxPhone")->
                    PutValue(vNull);
            }
            else if( (strcmp(strTemp,"X") == 0) | (strcmp(strTemp,"x") == 0) )
            {
                m_pRstEmployees->Fields->GetItem("FaxPhone")->
                    PutValue("");
            }
            else
            {
                m_pRstEmployees->Fields->GetItem("FaxPhone")->
                    PutValue(strTemp);
            }
            m_pRstEmployees->Update();

            // Open an IADORecordBinding interface pointer which
            // we will use for binding Recordset to a class
            TESTHR(hr = m_pRstEmployees->QueryInterface(
                __uuidof(IADORecordBinding),(LPVOID*)&picRs));

            // Bind the Recordset to a C++ class here
            TESTHR(hr = picRs->BindToRecordset(&emprs));

            // Print report.
            printf("\nName - Fax number\n");
            printf("%s %s ",emprs.lemp_LastNameStatus == adFldOK ?
                emprs.m_szemp_LastName : "<NULL>",
                emprs.lemp_FirstNameStatus == adFldOK ?
                emprs.m_szemp_FirstName : "<NULL>");

            if (emprs.lemp_FaxphoneStatus == adFldNull)
                printf("- [Unknown]\n");
            else if (strcmp((LPSTR)emprs.m_szemp_Faxphone,"") == 0)
                printf("- [Has no fax]\n");
            else
                printf("- %s\n",emprs.m_szemp_Faxphone);
        }
        m_pRstEmployees->Close();

        // Delete new field because this is a demonstration.
        m_pTable->Columns->Delete(m_pColumn->Name);

        // Release the IADORecordset Interface here
        if(picRs)
            picRs->Release();
        m_pCnn->Close();
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
            (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndAttributesCpp

AttributesX.h

<"C:\MDAC SDK 2.6\ADO\Source\ADOCore\Code\VC\ADOXAttributesX.h" BeginAttributesH,EndAttributesH>

See Also

Attributes Property | Column Object


Catalog ActiveConnection Property Example (VC++)

Setting the ActiveConnection property to a valid, open connection "opens" the catalog. From an open catalog, you can access the schema objects contained within that catalog.

// BeginActiveConnectionCpp

See Also

ActiveConnection Property


Clustered Property Example (VC++)

This example demonstrates the Clustered property of an Index. Note that Microsoft Jet databases do not support clustered indexes, so this example will return False for the Clustered property of all indexes in the Northwind database.

// BeginClusteredCpp
#import "c:\Program Files\Common Files\system\ado\msadox.dll" \
    no_namespace

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void ADOXClusteredX(void);

//////////////////////////////////////////////////////////
//                                                      //
//     Main Function                                    //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    ClusteredX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//       ClusteredX Function                            //
//                                                      //
//////////////////////////////////////////////////////////
void ClusteredX()
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _CatalogPtr m_pCatalog = NULL;
    _TablePtr m_pTable = NULL;
    _IndexPtr m_pIndex = NULL;

    //Define other variables here
    _variant_t vIndex;
    try
    {
        TESTHR(hr = m_pCatalog.CreateInstance(__uuidof(Catalog)));

        // Connect to the catalog.
        m_pCatalog->PutActiveConnection(
            "Provider=Microsoft.Jet.OLEDB.4.0;data source="
            "c:\\Program Files\\Microsoft Office\\Office\\Samples"
            "\\Northwind.mdb;");

        int iLineCnt = 1;
        //Enumerate Tables.
        for(short iTable = 0;iTable < m_pCatalog->Tables->Count;iTable++)
        {
            vIndex = iTable;
            m_pTable = m_pCatalog->Tables->GetItem(vIndex);

            //Enumerate Indexes.
            for(short iIndex = 0;iIndex < m_pTable->Indexes->Count;
                iIndex++)
            {
                vIndex = iIndex;
                m_pIndex = m_pTable->Indexes->GetItem(vIndex);
                cout << m_pTable->Name << "   " ;
                cout << m_pIndex->Name << "   " << (m_pIndex->
                    GetClustered() ? "True" : "False") << endl;

                 if (iLineCnt%15 == 0)
                {
                    printf("\nPress any key to continue...\n");
                    getch();
                }
                iLineCnt++;
            }
        }
    }
    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
            (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndClusteredCpp

See Also

Clustered Property | Index Object


Columns and Tables Append Methods, Name Property Example (VC++)

The following code demonstrates how to create a new table.

// BeginCreateTableCpp
#import "c:\Program Files\Common Files\system\ado\msadox.dll" \
    no_namespace

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void CreateTableX(void);

//////////////////////////////////////////////////////////
//                                                      //
//       Main Function                                  //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    CreateTableX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//       CreateTableX Function                          //
//                                                      //
//////////////////////////////////////////////////////////
void CreateTableX()
{
        HRESULT hr = S_OK;

        // Define ADOX object pointers.
        // Initialize pointers on define.
        // These are in the ADOX::  namespace.
        _CatalogPtr m_pCatalog = NULL;
        _TablePtr m_pTable = NULL;

        try
        {
            TESTHR(hr = m_pCatalog.CreateInstance(__uuidof(Catalog)));

            //Open the catalog
            m_pCatalog->PutActiveConnection(
                "Provider=Microsoft.Jet.OLEDB.4.0;" \
                "data source=c:\\Program Files\\Microsoft Office"
                "\\Office\\Samples\\Northwind.mdb;");

            TESTHR(hr = m_pTable.CreateInstance(__uuidof(Table)));
            m_pTable->PutName("MyTable");
            m_pTable->Columns->Append("Column1",adInteger,0);
            m_pTable->Columns->Append("Column2",adInteger,0);
            m_pTable->Columns->Append("Column3",adVarWChar,50);
            m_pCatalog->Tables->Append(
                _variant_t((IDispatch *)m_pTable));

            //Delete the table
            m_pCatalog->Tables->Delete("MyTable");
        }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
            (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndCreateTableCpp

See Also

Append Method (Columns) | Append Method (Tables) | Name Property


Command and CommandText Properties Example (VC++)

The following code demonstrates how to use the Command property to update the text of a procedure.

// BeginCommandTextCpp
#import "c:\Program Files\Common Files\system\ado\msadox.dll" \
    no_namespace
#import "c:\Program Files\Common Files\system\ado\msado15.dll"

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void ProcedureTextX(void);

//////////////////////////////////////////////////////////
//                                                      //
//      Main Function                                   //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    ProcedureTextX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//       ProcedureTextX Function                        //
//                                                      //
//////////////////////////////////////////////////////////
void ProcedureTextX()
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _CatalogPtr m_pCatalog = NULL;

    // Define ADODB object pointers.
    ADODB::_ConnectionPtr m_pCnn = NULL;
    ADODB::_CommandPtr m_pCommand = NULL;

    try
    {
        //Open the Connection
        TESTHR(hr = m_pCnn.CreateInstance(__uuidof(ADODB::Connection)));
        TESTHR(hr = m_pCatalog.CreateInstance(__uuidof(Catalog)));
        TESTHR(hr = m_pCommand.CreateInstance(__uuidof(ADODB::Command)));
        m_pCnn->Open("Provider=Microsoft.Jet.OLEDB.4.0;"
            "data source=c:\\Program Files\\Microsoft Office"
            "\\Office\\Samples\\Northwind.mdb;","","",NULL);

        //Open the catalog
        m_pCatalog->PutActiveConnection(_variant_t((IDispatch *)m_pCnn));

        //Get the Command
        m_pCommand = m_pCatalog->Procedures->GetItem("CustomerById")->GetCommand();

        //Update the CommandText
        m_pCommand->PutCommandText("PARAMETERS [CustId] Text;select "
            "CustomerId, CompanyName, ContactName "
            "from Customers where CustomerId = [CustId]");

        //Update the Procedure
        m_pCatalog->Procedures->GetItem("CustomerById")->PutCommand(
            _variant_t((IDispatch *)m_pCommand));
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",(LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndCommandTextCpp

See Also

Command Property


Connection Close Method, Table Type Property Example (VC++)

Setting the ActiveConnection property to Nothing should "close" the catalog. Associated collections will be empty. Any objects that were created from schema objects in the catalog will be orphaned. Any properties on those objects that have been cached will still be available, but attempting to read properties that require a call to the provider will fail.

<"C:\MDAC SDK 2.6\ADO\Source\ADOX\Code\VC\ADOXCloseConnectX.cpp" BeginCloseConnectionCpp,EndCloseConnectionCpp>

See Also

ActiveConnection Property


Create Method Example (VC++)

The following code shows how to create a new Microsoft Jet database with the Create method.

// BeginCreateDatabaseCpp
#import "c:\Program Files\Common Files\system\ado\msadox.dll" no_namespace
#import "c:\Program Files\Common Files\system\ado\msado15.dll"

#define TESTHR(x) if FAILED(x) _com_issue_error(hr);

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

//Function declarations
void CreateDatabaseX(void);

//------------------------------------------------------------                                       //
//Main Function
//Purpose:  Test Driver
//------------------------------------------------------------                                       //
void main()
{
   HRESULT hr = S_OK;

   hr = ::CoInitialize(NULL);
   if(SUCCEEDED(hr))
   {
      CreateDatabaseX();

      //Wait here for the user to see the output
      printf("Press any key to continue...");
      getch();

      ::CoUninitialize();
   }
}

//------------------------------------------------------------                                       //
//CreateDatabaseX
//Purpose:  create a new Jet database with the Create method
//------------------------------------------------------------                                       //
void CreateDatabaseX()
{
   HRESULT hr = S_OK;

   // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.

   _CatalogPtr m_pCatalog = NULL;

   //Set ActiveConnection of Catalog to this string
   _bstr_t strcnn("Provider=Microsoft.JET.OLEDB.4.0;"
            "Data source = c:\\new.mdb");
   try
   {
      TESTHR(hr = m_pCatalog.CreateInstance(__uuidof (Catalog)));
      m_pCatalog->Create(strcnn);

   }

   catch(_com_error &e)
   {
      // Notify the user of errors if any.
      _bstr_t bstrSource(e.Source());
      _bstr_t bstrDescription(e.Description());

      printf("\n\tSource :  %s \n\tdescription : %s \n ",(LPCSTR)bstrSource,(LPCSTR)bstrDescription);

   }

   catch(...)
   {
      cout << "Error occured in include files...."<< endl;
   }

}
// EndCreateDatabaseCpp

See Also

Create Method


DateCreated and DateModified Properties Example (VC++)

This example demonstrates the DateCreated and DateModified properties by adding a new Column to an existing Table and by creating a new Table. The DateOutput procedure is required for this example to run.

// BeginDateCreatedCpp
#import "c:\Program Files\Common Files\system\ado\msadox.dll" \
    no_namespace
#import "c:\Program Files\Common Files\system\ado\msado15.dll"

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void DateCreatedX(void);
void DateOutPut(_bstr_t strTemp , _TablePtr tblTemp);

//////////////////////////////////////////////////////////
//                                                      //
//        Main Function                                 //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    DateCreatedX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//       DateCreatedX Function                          //
//                                                      //
//////////////////////////////////////////////////////////
void DateCreatedX()
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _CatalogPtr m_pCatalog = NULL;
    _TablePtr m_pTblEmployees = NULL;
    _TablePtr m_pTblNew = NULL;

    //Set ActiveConnection of Catalog to this string
    _bstr_t strCnn("Provider=Microsoft.Jet.OLEDB.4.0;"
        "Data Source= c:\\Program Files\\Microsoft Office\\"
        "Office\\Samples\\Northwind.mdb;");

    try
    {
        TESTHR(hr = m_pCatalog.CreateInstance(__uuidof (Catalog)));

        // Connect the catalog.
        m_pCatalog->PutActiveConnection(strCnn);

        m_pTblEmployees = m_pCatalog->Tables->GetItem("Employees");

        // Print current information about the Employees table.
        DateOutPut((_bstr_t)"Current properties", m_pTblEmployees);

        // Create and append column to the Employees table.
        m_pTblEmployees->Columns->Append("NewColumn", adInteger,0);

        m_pCatalog->Tables->Refresh();

        // Print new information about the Employees table.
        DateOutPut((_bstr_t)"After creating a new column",
            m_pTblEmployees);

        // Delete new column because this is a demonstration.
        m_pTblEmployees->Columns->Delete("NewColumn");

        // Create and append new Table object to the Northwind database.
        TESTHR(hr = m_pTblNew.CreateInstance(__uuidof(Table)));

        m_pTblNew->Name = "NewTable";
        m_pTblNew->Columns->Append("NewColumn", adInteger,0);
        m_pCatalog->Tables->Append(_variant_t((IDispatch*)m_pTblNew));
        m_pCatalog->Tables->Refresh();

        //Print information about the new Table object.
        DateOutPut((_bstr_t)"After creating a new table", m_pCatalog->
            Tables->GetItem("NewTable"));

        // Delete new Table object because this is a demonstration.
        m_pCatalog->Tables->Delete(m_pTblNew->Name);
}

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
            (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}

//////////////////////////////////////////////////////////
//                                                      //
//        DateOutPut Function                           //
//                                                      //
//////////////////////////////////////////////////////////
void DateOutPut(_bstr_t strTemp , _TablePtr tblTemp)
{
    // Print DateCreated and DateModified information about
    // specified Table object.
    cout << strTemp << endl;
    cout << "    Table: " << tblTemp->GetName() << endl;
    cout << "        DateCreated = " << (_bstr_t)tblTemp->
        GetDateCreated() << endl;
    cout << "        DateModified = " << (_bstr_t)tblTemp->
        GetDateModified() << endl;
}
// EndDateCreatedCpp

See Also

Column Object | DateCreated Property | DateModified Property | Table Object


DefinedSize Property Example (VC++)

This example demonstrates the DefinedSize property of a Column. The code will redefine the size of the FirstName column of the Employees table of the Northwind database. Then, the change in the values of the FirstName Field of a Recordset based on the Employees table is displayed. Note that by default, the FirstName field becomes padded with spaces after you redefine the DefinedSize property.

// BeginDefinedSizeCpp
#import "c:\Program Files\Common Files\system\ado\msadox.dll" \
    no_namespace
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
     rename("EOF", "EndOfFile")

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void DefinedSizeX(void);

//////////////////////////////////////////////////////////
//                                                      //
//      Main Function                                   //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    DefinedSizeX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//      DefinedSizeX Function                           //
//                                                      //
//////////////////////////////////////////////////////////
void DefinedSizeX(void)
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _CatalogPtr m_pCatNorthwind   = NULL;
    _ColumnPtr m_pColFirstName    = NULL;
    _ColumnPtr m_pColNewFirstName = NULL;

    // Define ADODB object pointers
    ADODB::_RecordsetPtr m_pRstEmployees  = NULL;

    // Define string variables.
    _bstr_t strCnn("Provider=Microsoft.Jet.OLEDB.4.0;data source="
        "c:\\Program Files\\Microsoft Office\\Office\\Samples\\"
        "Northwind.mdb;");
    _bstr_t aryFirstName[10];

    try
    {
        // Open a Recordset for the Employees table.
        TESTHR(hr = m_pRstEmployees.CreateInstance(
            __uuidof(ADODB::Recordset)));
        TESTHR(hr = m_pCatNorthwind.CreateInstance(__uuidof (Catalog)));
        TESTHR(hr = m_pColNewFirstName.CreateInstance(__uuidof(Column)));

        m_pRstEmployees->Open("Employees",strCnn,ADODB::adOpenKeyset,
            ADODB::adLockReadOnly,ADODB::adCmdTable);

        long lngSize = m_pRstEmployees->RecordCount;
        aryFirstName[lngSize];

        // Open a catalog for the Northwind database,
        // using same connection as rstEmployees.
        m_pCatNorthwind->PutActiveConnection(m_pRstEmployees->
            GetActiveConnection());

        // Loop through the recordset displaying the contents,
        // of the FirstName field, the field's defined size,
        // and its actual size.
        // Also store FirstName values in aryFirstName array.
        m_pRstEmployees->MoveFirst();
        printf("\nOriginal Defined Size and Actual Size");
        int iCount=0;
        while (!(m_pRstEmployees->EndOfFile))
        {
            printf("\nEmployee Name:");
            printf("%s ",(LPSTR)(_bstr_t)m_pRstEmployees->Fields->
                GetItem("FirstName")->Value);
            printf("%s\n",(LPSTR)(_bstr_t)m_pRstEmployees->Fields->
                GetItem("LastName")->Value);
            printf("  FirstName Defined size: %d\n",m_pRstEmployees->
                Fields->GetItem("FirstName")->DefinedSize) ;
            printf("  FirstName Actual size: %d\n",m_pRstEmployees->
                Fields->GetItem("FirstName")->ActualSize);
            aryFirstName[iCount] = (_bstr_t) m_pRstEmployees->Fields->
                GetItem("FirstName")->Value;
            m_pRstEmployees->MoveNext();
            iCount++;
            if(iCount%5==0)
            {
                printf("Press any key to continue...");
                getch();
                system("cls");
            }
        }
        m_pRstEmployees->Close();

        // Redefine the DefinedSize of FirstName in the catalog.
        m_pColFirstName = m_pCatNorthwind->Tables->GetItem("Employees")->
            Columns->GetItem("FirstName");
        m_pColNewFirstName->Name = m_pColFirstName->Name;
        m_pColNewFirstName->Type = m_pColFirstName->Type;
        m_pColNewFirstName->DefinedSize =
            (m_pColFirstName->DefinedSize) + 1;

        // Append new FirstName column to catalog.
        m_pCatNorthwind->Tables->GetItem("Employees")->Columns->
            Delete(m_pColFirstName->Name);
        m_pCatNorthwind->Tables->GetItem("Employees")->Columns->
            Append(_variant_t((IDispatch*)m_pColNewFirstName,true),
            adVarWChar,m_pColNewFirstName->DefinedSize);

        // Open Employee table in Recordset for updating.
        m_pRstEmployees->Open("Employees",m_pCatNorthwind->
            GetActiveConnection(),ADODB::adOpenKeyset,
            ADODB::adLockOptimistic,ADODB::adCmdTable);

        // Loop through the recordset displaying the contents
        // of the FirstName field,the field's defined size,
        // and its actual size.
        // Also restore FirstName values from aryFirstName.
        printf("Press any key to continue...");
        getch();
        system("cls");
        m_pRstEmployees->MoveFirst();
        printf("\n\nNew Defined Size and Actual Size");
        iCount=0;
        while (!(m_pRstEmployees->EndOfFile))
        {
            m_pRstEmployees->Fields->GetItem("FirstName")->Value =
                aryFirstName[iCount];
            printf("\nEmployee Name: ");
            printf("%s ",(LPSTR) (_bstr_t)m_pRstEmployees->Fields->
                GetItem("FirstName")->Value);
            printf("%s\n",(LPSTR)(_bstr_t)m_pRstEmployees->Fields->
                GetItem("LastName")->Value);
            printf("  FirstName Defined size: %d\n",m_pRstEmployees->
                Fields->GetItem("FirstName")->DefinedSize );
            printf("  FirstName Actual size: %d\n",m_pRstEmployees->
                Fields->GetItem("FirstName")->ActualSize );
            m_pRstEmployees->MoveNext();
            iCount++;
            if(iCount%5==0)
            {
                printf("Press any key to continue...");
                getch();
                system("cls");
            }
        }
        m_pRstEmployees->Close();

        // Restore original FirstName column to catalog
        m_pCatNorthwind->Tables->GetItem("Employees")->Columns->
            Delete(m_pColNewFirstName->Name);

        m_pCatNorthwind->Tables->GetItem("Employees")->Columns->
            Append(_variant_t((IDispatch*)m_pColFirstName,true),
            adVarWChar,m_pColFirstName->DefinedSize);

        // Restore original FirstName values to Employees table.
        m_pRstEmployees->Open("Employees",m_pCatNorthwind->
            GetActiveConnection(),ADODB::adOpenKeyset,
            ADODB::adLockOptimistic,ADODB::adCmdTable);

        m_pRstEmployees->MoveFirst();
        iCount = 0;
        while(!(m_pRstEmployees->EndOfFile))
        {
            m_pRstEmployees->Fields->GetItem("FirstName")->Value =
                aryFirstName[iCount];
            m_pRstEmployees->MoveNext();
            iCount++;
        }
        m_pRstEmployees->Close();
        m_pCatNorthwind = NULL;
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
            (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndDefinedSizeCpp

See Also

Column Object | DefinedSize Property


DeleteRule Property Example (VC++)

This example demonstrates the DeleteRule property of a Key object. The code appends a new Table and then defines a new primary key, setting DeleteRule to adRICascade.

// BeginDeleteRuleCpp
#import "c:\Program Files\Common Files\system\ado\msadox.dll" \
    no_namespace
#import "c:\Program Files\Common Files\system\ado\msado15.dll"

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void DeleteRuleX(void);

//////////////////////////////////////////////////////////
//                                                      //
//      Main Function                                   //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    DeleteRuleX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//       DeleteRuleX Function                           //
//                                                      //
//////////////////////////////////////////////////////////
void DeleteRuleX(void)
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _KeyPtr m_pKeyPrimary = NULL;
    _CatalogPtr m_pCatalog   = NULL;
    _TablePtr m_pTblNew  = NULL;

    // Define string variables.
    _bstr_t strcnn("Provider=Microsoft.JET.OLEDB.4.0;"
                "Data source = c:\\Program Files\\Microsoft Office"
                "\\Office\\Samples\\Northwind.mdb;");
    try
    {
        TESTHR(hr = m_pKeyPrimary.CreateInstance(__uuidof(Key)));
        TESTHR(hr = m_pCatalog.CreateInstance(__uuidof (Catalog)));
        TESTHR(hr = m_pTblNew.CreateInstance(__uuidof(Table)));

        // Connect the catalog.
        m_pCatalog->PutActiveConnection(strcnn);

        // Name new table.
        m_pTblNew->Name = "NewTable";

        // Append a numeric and a text field to new table.
        m_pTblNew->Columns->Append("NumField",adInteger,20);
        m_pTblNew->Columns->Append("TextField",adVarWChar,20);

        // Append the new table.
        m_pCatalog->Tables->Append(_variant_t((IDispatch*)m_pTblNew));

        // Define the Primary key.
        m_pKeyPrimary->Name = "NumField";
        m_pKeyPrimary->Type = adKeyPrimary;
        m_pKeyPrimary->RelatedTable = "Customers";
        m_pKeyPrimary->Columns->Append("NumField",adInteger,20);
        m_pKeyPrimary->Columns->GetItem("NumField")->RelatedColumn =
            "CustomerId";
        m_pKeyPrimary->DeleteRule = adRICascade;

        //to pass an optional column parameter to Key's Apppend method
        _variant_t vOptional;
        vOptional.vt = VT_ERROR;
        vOptional.scode = DISP_E_PARAMNOTFOUND;

        // Append the primary key.
        m_pCatalog->Tables->GetItem("NewTable")->Keys->Append(
            _variant_t((IDispatch*)m_pKeyPrimary),
            adKeyPrimary,vOptional,L"",L"");

        // Delete the table as this is a demonstration.
        m_pCatalog->Tables->Delete(m_pTblNew->Name);
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
            (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndDeleteRuleCpp

See Also

DeleteRule Property | Key Object


GetObjectOwner and SetObjectOwner Methods Example (VC++)

This example demonstrates the GetObjectOwner and SetObjectOwner methods. This code assumes the existence of the group Accounting (see the Groups and Users Append, ChangePassword Methods Example to see how to add this group to the system). The owner of the Categories table is set to Accounting.

// BeginOwnersCpp
#import "c:\Program Files\Common Files\system\ado\msadox.dll" no_namespace

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void OwnersX(void);

//////////////////////////////////////////////////////////
//                                                      //
//        Main Function                                 //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    OwnersX();

        ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//      OwnersX Function                                //
//                                                      //
//////////////////////////////////////////////////////////
void OwnersX()
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _TablePtr m_pTable = NULL;
    _CatalogPtr m_pCatalog = NULL;

    try
    {
        TESTHR(hr = m_pCatalog.CreateInstance(__uuidof(Catalog)));
        TESTHR(hr = m_pTable.CreateInstance(__uuidof(Table)));

        //Open the Catalog.
        m_pCatalog->PutActiveConnection(
            "Provider=Microsoft.Jet.OLEDB.4.0;" \
            "data source=c:\\Program Files\\Microsoft Office\\"
                "Office\\Samples\\Northwind.mdb;"
                "jet oledb:system database=c:\\WINNT\\system32\\"
                "system.mdw");

        //Print the original owner of Categories
        _bstr_t strOwner = m_pCatalog->GetObjectOwner("Categories",
            adPermObjTable);
        cout << "Owner of Categories: " << strOwner << "\n" << endl;
        int iLineCnt = 2;

        //Create and append new group with a string.
        m_pCatalog->Groups->Append("Accounting");

        //Set the owner of Categories to Accounting.
        m_pCatalog->SetObjectOwner("Categories",
            adPermObjTable,"Accounting");

        _variant_t vIndex;
        //List the owners of all tables and columns in the catalog.
        for (long iIndex = 0;iIndex < m_pCatalog->Tables->Count;iIndex++)
        {
            vIndex = iIndex;
            m_pTable = m_pCatalog->Tables->GetItem(vIndex);
            cout << "Table: " << m_pTable->Name << endl;
            cout << "   Owner: " << m_pCatalog->
                GetObjectOwner(m_pTable->Name,adPermObjTable) << endl;
            if (iLineCnt%16 == 0)
                {
                    printf("\nPress any key to continue...\n");
                    getch();
                }
                iLineCnt = iLineCnt + 2;
        }

        //Restore the original owner of Categories
        m_pCatalog->SetObjectOwner("Categories",adPermObjTable,strOwner);

        //Delete Accounting
        m_pCatalog->Groups->Delete("Accounting");
    }
    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
             (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndOwnersCpp


GetPermissions and SetPermissions Methods Example (VC++)

This example demonstrates the GetPermissions and SetPermissions methods. The following code gives full access to the Orders table to the Admin user.

// BeginGrantPermissionCpp
#import "c:\Program Files\Common Files\system\ado\msado15.dll"
#import "c:\Program Files\Common Files\system\ado\msadox.dll" \
    no_namespace

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void GrantPermissionsX(void);

//////////////////////////////////////////////////////////
//                                                      //
//     Main Function                                    //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    GrantPermissionsX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//    GrantPermissionsX Function                        //
//                                                      //
//////////////////////////////////////////////////////////
void GrantPermissionsX()
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _CatalogPtr m_pCatalog = NULL;

    //Define ADODB object pointers;
    ADODB::_ConnectionPtr m_pCnn = NULL;

    //Define other variables here.
    try
    {
        TESTHR(hr = m_pCnn.CreateInstance(__uuidof(ADODB::Connection)));

        //Opens a connection to the northwind database
        //using the Microsoft Jet 4.0 provider
        m_pCnn->PutProvider("Microsoft.Jet.OLEDB.4.0");
        m_pCnn->Open("data source=c:\\Program Files\\"  \
        "Microsoft Office\\Office\\Samples\\Northwind.mdb;"  \
        "jet oledb:system database=c:\\WINNT\\system32\\system.mdw",
            "","",NULL);

         TESTHR(hr = m_pCatalog.CreateInstance(__uuidof(Catalog)));

        m_pCatalog->PutActiveConnection(_variant_t((IDispatch *)m_pCnn));

        //Retrieve original permissions
        long lngPerm = m_pCatalog->Users->GetItem("admin")->
            GetPermissions("Orders",adPermObjTable);
        long lngOrgPerm = lngPerm;
        cout << "Original Permissions: " << lngPerm << "\n" << endl;

        //Revoke all permissions
        m_pCatalog->Users->GetItem("admin")->SetPermissions("Orders",
            adPermObjTable,adAccessRevoke,adRightFull,adInheritNone);

        //Display permissions
        lngPerm = m_pCatalog->Users->GetItem("admin")->
            GetPermissions("Orders",adPermObjTable);
        cout << "Revoked permissions: " << lngPerm << "\n" << endl;

        //Give the Admin user full rights on the orders object
        m_pCatalog->Users->GetItem("admin")->SetPermissions("Orders",
            adPermObjTable,adAccessSet,adRightFull,adInheritNone);

        //Display permissions
        lngPerm = m_pCatalog->Users->GetItem("admin")->
            GetPermissions("Orders",adPermObjTable);
        cout << "Full permissions: " << lngPerm << "\n" << endl;

        //Restore original permissions
        m_pCatalog->Users->GetItem("admin")->SetPermissions("Orders",
            adPermObjTable,adAccessSet,(RightsEnum) lngOrgPerm,
            adInheritNone);

        //Display permissions
        lngPerm = m_pCatalog->Users->GetItem("admin")->
            GetPermissions("Orders",adPermObjTable);
        cout << "Final permissions: " << lngPerm << "\n" << endl;
    }
    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
              (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndGrantPermissionCpp


Groups and Users Append, ChangePassword Methods Example (VC++)

This example demonstrates the Append method of Groups, as well as the Append method of Users by adding a new Group and a new User to the system. The new Group is appended to the Groups collection of the new User. Consequently, the new User is added to the Group. Also, the ChangePassword method is used to specify the User password.

// BeginGroupCpp
#import "c:\Program Files\Common Files\system\ado\msadox.dll" no_namespace

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

//Function declarations
void GroupX(void);
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};

//////////////////////////////////////////////////////////
//                                                      //
//      Main Function                                   //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    GroupX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//         GroupX Function                              //
//                                                      //
//////////////////////////////////////////////////////////
void GroupX(void)
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _CatalogPtr m_pCatalog   = NULL;
    _UserPtr m_pUserNew   = NULL;
    _UserPtr m_pUser  = NULL;
    _GroupPtr m_pGroup = NULL;

    // Define String Variables.
    _bstr_t strCnn("Provider=Microsoft.JET.OLEDB.4.0;"
        "Data source = c:\\Program Files\\Microsoft Office\\"
        "Office\\Samples\\Northwind.mdb;"
        "jet oledb:system database=c:\\WINNT\\system32\\system.mdw");

    try
    {
        TESTHR(hr = m_pCatalog.CreateInstance(__uuidof (Catalog)));
        m_pCatalog->PutActiveConnection(strCnn);

        // Create and append new group with a string.
        m_pCatalog->Groups->Append("Accounting");

        // Create and append new user with an object.
        TESTHR(hr = m_pUserNew.CreateInstance(__uuidof(User)));
        m_pUserNew->PutName("Pat Smith");
        m_pUserNew->ChangePassword("","Password1");
        m_pCatalog->Users->Append(
            _variant_t((IDispatch *)m_pUserNew),"");

        // Make the user Pat Smith a member of the
        // Accounting group by creating and adding the
        // appropriate Group object to the user's Groups
        // collection.The same is accomplished if a User
        // object representing Pat Smith is created and
        // appended to the Accounting group Users collection
        m_pUserNew->Groups->Append("Accounting");

        // Enumerate all User objects in the
        // catalog's Users collection.
        long lUsrIndex;
        long lGrpIndex;
        _variant_t vIndex;
        for(lUsrIndex=0;lUsrIndex<=m_pCatalog->Users->Count;lUsrIndex++)
        {
            vIndex = lUsrIndex;
            m_pUser = m_pCatalog->Users->GetItem(vIndex);
            cout<<"  "<<m_pUser->Name <<endl;
            cout<<"   Belongs to these groups:"<<endl;

            // Enumerate all Group objects in each User
            // object's Groups collection.
            if(m_pUser->Groups->Count != 0)
            {
                for(lGrpIndex=0;lGrpIndex<=m_pUser->Groups->
                    Count;lGrpIndex++)
                {
                    vIndex = lGrpIndex;
                    m_pGroup = m_pUser->Groups->GetItem(vIndex);
                    cout<<"     "<< m_pGroup->Name<<endl;
                }
            }
            else
            {
                cout<<"       [None]"<<endl;
            }
        }

        // Enumerate all Group objects in the default
        // workspace's Groups collection.
        for(lGrpIndex=0;lGrpIndex<=m_pCatalog->Groups->Count;lGrpIndex++)
        {
            vIndex = lGrpIndex;
            m_pGroup = m_pCatalog->Groups->GetItem(vIndex);
            cout<<"   "<< m_pGroup->Name <<endl;
            cout<<"    Has as its members:"<<endl;

            // Enumerate all User objects in each Group
            // object's Users Collection.
            if(m_pGroup->Users->Count != 0)
            {
                for(lUsrIndex=0;lUsrIndex<=m_pGroup->Users->Count;
                    lUsrIndex++)
                {
                    vIndex = lUsrIndex;
                    m_pUser = m_pGroup->Users->GetItem(vIndex);
                    cout<<"    "<<m_pUser->Name<<endl;
                }
            }
            else
            {
                cout<<"      [None]"<<endl;
            }
        }

        // Delete new User and Group object because this
        // is only a demonstration.
        m_pCatalog->Users->Delete("Pat Smith");
        m_pCatalog->Groups->Delete("Accounting");
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
             (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndGroupCpp


Indexes Append Method Example (VC++)

The following code demonstrates how to create a new index. The index is on two columns in the table.

// BeginCreateIndexCpp
#import "c:\Program Files\Common Files\system\ado\msadox.dll" no_namespace

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void CreateIndexX(void);

//////////////////////////////////////////////////////////
//                                                      //
//      Main Function                                   //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    CreateIndexX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//        CreateIndexX Function                         //
//                                                      //
//////////////////////////////////////////////////////////
void CreateIndexX(void)
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _TablePtr m_pTable   = NULL;
    _IndexPtr m_pIndex    = NULL;
    _CatalogPtr m_pCatalog = NULL;

    //Define other variables
    _bstr_t strcnn("Provider=Microsoft.JET.OLEDB.4.0;"
        "Data source = c:\\Program Files\\Microsoft Office\\"
        "Office\\Samples\\Northwind.mdb;");
    try
    {
        TESTHR(hr = m_pTable.CreateInstance(__uuidof(Table)));
        TESTHR(hr = m_pIndex.CreateInstance(__uuidof(Index)));
        TESTHR(hr = m_pCatalog.CreateInstance(__uuidof (Catalog)));

        // Open the catalog.
        m_pCatalog->PutActiveConnection(strcnn);

        // Define the table and append it to the catalog.
        m_pTable->Name = "MyTable";
        m_pTable->Columns->Append("Column1",adInteger,0);
        m_pTable->Columns->Append("Column2",adInteger,0);
        m_pTable->Columns->Append("Column3",adVarWChar,50);
        m_pCatalog->Tables->Append(_variant_t((IDispatch *)m_pTable));

        // Define a multi-column index.
        m_pIndex->Name = "multicolidx";
        m_pIndex->Columns->Append("Column1",adInteger,0);
        m_pIndex->Columns->Append("Column2",adInteger,0);

        // Append the index to the table.
        m_pTable->Indexes->Append(_variant_t((IDispatch *)m_pIndex));

        // Delete the table.
        m_pCatalog->Tables->Delete("MyTable");
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
             (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndCreateIndexCpp


IndexNulls Property Example (VC++)

This example demonstrates the IndexNulls property of an Index. The code creates a new index and sets the value of IndexNulls based on user input. Then, the Index is appended to the Employees Table in the Northwind Catalog. The new Index is applied to a Recordset based on the Employees table, and the Recordset is opened. A new record is added to the Employees table, with a Null value in the indexed field. Whether this new record is displayed depends on the setting of the IndexNulls property.

// BeignIndexNullCpp
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
    rename("EOF", "EndOfFile")
#import "c:\Program Files\Common Files\system\ado\msadox.dll" \
    no_namespace

#include "iostream.h"
#include "stdio.h"
#include "conio.h"
#include "IndexNullsX.h"

// Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void IndexNullsX(_bstr_t);

//////////////////////////////////////////////////////////
//                                                      //
//      Main Function                                   //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    printf("\nShow records having indexed field value = NULL? (Y/N):");
    char input = getche();

    if(toupper(input)=='Y')
    {
        IndexNullsX("Allow");
    }
    else if(toupper(input)=='N')
    {
        IndexNullsX("Ignore");
    }
    else
    {
       exit(0);
    }

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//      IndexNullsX Function                            //
//                                                      //
//////////////////////////////////////////////////////////
void IndexNullsX(_bstr_t strSel)
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _CatalogPtr m_pCatalog     = NULL;
    _IndexPtr m_pIndexNew      = NULL;

    // Define ADODB object pointers
    ADODB::_ConnectionPtr m_pCnn           = NULL;
    ADODB::_RecordsetPtr  m_pRstEmployees  = NULL;

    // Define other variables
    IADORecordBinding *picRs  = NULL;  // Interface Pointer Declared
    CEmployeeRs emprs;          // C++ Class Object

    // Define string variable.
    _bstr_t strCnn("Provider=Microsoft.Jet.OLEDB.4.0;"
        "data source=c:\\Program Files\\Microsoft Office\\Office\\"
        "Samples\\Northwind.mdb;");

    try
    {
        TESTHR(hr = m_pCnn.CreateInstance(__uuidof(ADODB::Connection)));
        TESTHR(hr = m_pCatalog.CreateInstance(__uuidof (Catalog)));
        TESTHR(hr = m_pIndexNew.CreateInstance(__uuidof(Index)));
        TESTHR(hr = m_pRstEmployees.CreateInstance(
            __uuidof(ADODB::Recordset)));

        // Connect the catalog.
        m_pCnn->Open(strCnn,"","",NULL);
        m_pCatalog->PutActiveConnection(_variant_t((IDispatch *)m_pCnn));

        // Append Country column to new index.
        m_pIndexNew->Columns->Append("Country",adVarWChar,0);
        m_pIndexNew->Name = "NewIndex";

        // Set IndexNulls based on user input
        if(strcmp((LPSTR)strSel,"Allow")==0)
        {
            m_pIndexNew->IndexNulls = adIndexNullsAllow;
        }
        else if(strcmp((LPSTR)strSel,"Ignore")==0)
        {
            m_pIndexNew->IndexNulls = adIndexNullsIgnore;
        }

        // Append new index to Employees table
        m_pCatalog->Tables->GetItem("Employees")->Indexes->Append(
            _variant_t((IDispatch *)m_pIndexNew));
        m_pRstEmployees->Index = m_pIndexNew->Name;
        m_pRstEmployees->Open("Employees",
            _variant_t((IDispatch *)m_pCnn),
            ADODB::adOpenKeyset,ADODB::adLockOptimistic,
            ADODB::adCmdTableDirect);

        // Add a new record to the Employees table.
        m_pRstEmployees->AddNew();
        m_pRstEmployees->Fields->GetItem("FirstName")->Value =
             (_bstr_t) "Gary";
        m_pRstEmployees->Fields->GetItem("LastName")->Value =
             (_bstr_t) "Haarsager";
        m_pRstEmployees->Update();

        // Bookmark the newly added record.
        _variant_t varBookmark = m_pRstEmployees->Bookmark;

        // Use the new index to set the order of the records.
        m_pRstEmployees->MoveFirst();
        printf("\n\nIndex = %s,",(LPSTR) m_pRstEmployees->Index);
        printf("IndexNulls = %d\n\n",m_pIndexNew->IndexNulls);
        cout<<"Country            -        Name"<<endl;

        // Open an IADORecordBinding interface pointer which
        // we will use for binding Recordset to a class
        TESTHR(hr = m_pRstEmployees->QueryInterface(
            __uuidof(IADORecordBinding),(LPVOID*)&picRs));

        // Bind the Recordset to a C++ class here
        TESTHR(hr = picRs->BindToRecordset(&emprs));

        // Enumerate the Recordset.The value of the
        // IndexNulls property will determine if the newly
        // added record appears in the output.
        while(!(m_pRstEmployees->EndOfFile))
        {
            printf("%s    -    %s %s\n",
                emprs.lemp_CountryStatus == adFldOK ?
                emprs.m_szemp_Country :"[Null]",
                emprs.lemp_FirstNameStatus == adFldOK ?
                emprs.m_szemp_FirstName :"<NULL>",
                emprs.lemp_LastNameStatus == adFldOK ?
                emprs.m_szemp_LastName :"<NULL>");
            m_pRstEmployees->MoveNext();
        }

        // Delete new record because this is a demonstration.
        m_pRstEmployees->Bookmark = varBookmark;
        m_pRstEmployees->Delete(ADODB::adAffectCurrent);
        m_pRstEmployees->Close();

        // Delete new Index because this is a demonstration.
        m_pCatalog->Tables->GetItem("Employees")->Indexes->
            Delete(m_pIndexNew->Name);
        m_pCatalog = NULL;
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
             (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndIndexNullCpp

IndexNullX.h

// BeginIndexNullsH
// IndexNullsX.h

#include "icrsint.h"

//This class extracts LastName,Country,FirstName from Employees table

class CEmployeeRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CEmployeeRs)
        // Column LastName is the 2nd field in the table
        ADO_VARIABLE_LENGTH_ENTRY2(2,adVarChar,m_szemp_LastName,
            sizeof(m_szemp_LastName),lemp_LastNameStatus,TRUE)
        // Column Country is the 11th field in the table
        ADO_VARIABLE_LENGTH_ENTRY2(11,adVarChar,m_szemp_Country,
            sizeof(m_szemp_Country),lemp_CountryStatus,TRUE)
            // Column Country is the 17th field in the table
        ADO_VARIABLE_LENGTH_ENTRY2(17,adVarChar,m_szemp_FirstName,
            sizeof(m_szemp_FirstName),lemp_FirstNameStatus,TRUE)
END_ADO_BINDING()

public:
    CHAR m_szemp_LastName[21];
    ULONG lemp_LastNameStatus;
    CHAR m_szemp_Country[16];
    ULONG lemp_CountryStatus;
    CHAR m_szemp_FirstName[11];
    ULONG lemp_FirstNameStatus;
};
// EndIndexNullsH


Keys Append Method, Key Type, RelatedColumn, RelatedTable and UpdateRule Properties Example (VC++)

The following code demonstrates how to create a new foreign key. It assumes two tables (Customers and Orders) exist.

// BeginCreateKeyCpp
#import "c:\Program Files\Common Files\system\ado\msadox.dll" \
    no_namespace
#import "c:\Program Files\Common Files\system\ado\msado15.dll"

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void CreateKeyX(void);

//////////////////////////////////////////////////////////
//                                                      //
//      Main Function                                   //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    CreateKeyX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//        CreateKeyX Function                           //
//                                                      //
//////////////////////////////////////////////////////////
void CreateKeyX(void)
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _KeyPtr m_pKeyForeign = NULL;
    _CatalogPtr m_pCatalog   = NULL;

    //Define other variables
    _bstr_t strcnn("Provider=Microsoft.JET.OLEDB.4.0;"
                "Data source = c:\\Program Files\\Microsoft Office\\"
                "Office\\Samples\\Northwind.mdb;");
    try
    {
        TESTHR(hr = m_pKeyForeign.CreateInstance(__uuidof(Key)));
        TESTHR(hr = m_pCatalog.CreateInstance(__uuidof (Catalog)));
        m_pCatalog->PutActiveConnection(strcnn);

        // Define the foreign key.
        m_pKeyForeign->Name = "CustOrder";
        m_pKeyForeign->Type = adKeyForeign;
        m_pKeyForeign->RelatedTable = "Customers";
        m_pKeyForeign->Columns->Append("CustomerId",adVarWChar,0);
        m_pKeyForeign->Columns->GetItem("CustomerId")->RelatedColumn =
            "CustomerId";
        m_pKeyForeign->UpdateRule = adRICascade;

        // To pass as column parameter to Key's Apppend method
        _variant_t vOptional;
        vOptional.vt = VT_ERROR;
        vOptional.scode = DISP_E_PARAMNOTFOUND;

        // Append the foreign key.
        m_pCatalog->Tables->GetItem("Orders")->Keys->
            Append(_variant_t((IDispatch *)m_pKeyForeign),
            adKeyPrimary,vOptional,L"",L"");

        // Delete the key as this is a demonstration.
        m_pCatalog->Tables->GetItem("Orders")->Keys->
            Delete(m_pKeyForeign->Name);
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
             (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndCreateKeyCpp


NumericScale and Precision Properties Example (VC++)

This example demonstrates the NumericScale and Precision properties of the Column object. This code displays their value for the Order Details table of the Northwind database.

// BeginNumericScalePrecCpp
#import "c:\Program Files\Common Files\system\ado\msadox.dll" \
    no_namespace
#import "c:\Program Files\Common Files\system\ado\msado15.dll"

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void NumericScalePrecX(void);

//////////////////////////////////////////////////////////
//                                                      //
//       Main Function                                  //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    NumericScalePrecX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//          NumericScalePrecX Function                  //
//                                                      //
//////////////////////////////////////////////////////////
void NumericScalePrecX(void)
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _CatalogPtr m_pCatalog = NULL;
    _TablePtr m_pTable = NULL;
    _ColumnPtr m_pColumn = NULL;

    //Define ADODB object pointers
    ADODB::_ConnectionPtr m_pCnn = NULL;

    //Declare string variables
    _bstr_t strCnn("Provider=Microsoft.Jet.OLEDB.4.0;"
        "Data Source=c:\\Program Files\\Microsoft Office\\"
        "Office\\Samples\\Northwind.mdb;");
    try
    {
        TESTHR(hr = m_pCnn.CreateInstance(__uuidof(ADODB::Connection)));

        TESTHR(hr = m_pCatalog.CreateInstance(__uuidof (Catalog)));

        // Connect the catalog.
        m_pCnn->Open (strCnn, "", "", NULL);

        m_pCatalog->PutActiveConnection(variant_t((IDispatch *)m_pCnn));

        // Retrieve the Order Details table
        m_pTable = m_pCatalog->Tables->GetItem("Order Details");

        // Display numeric scale and precision of
        // small integer fields.
        _variant_t vIndex;
        for (long lIndex=0; lIndex < m_pTable->Columns->Count; lIndex++)
        {
            vIndex = lIndex ;
            m_pColumn = m_pTable->Columns->GetItem(vIndex);
                if(m_pColumn->Type == adSmallInt)
                {
                    cout << "Column: " << m_pColumn->GetName() << endl;
                    cout << "Numeric scale: " << (_bstr_t) m_pColumn->
                        GetNumericScale() << endl;
                    cout << "Precision: " << m_pColumn->GetPrecision() <<
                        endl;
                }
        }
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
             (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndNumericScalePrecCpp


Parameters Collection, Command Property Example (VC++)

The following code demonstrates how to use the Command property with the Command object to retrieve parameter information for the procedure.

// BeginProcedureParametersCpp
#import "c:\Program Files\Common Files\system\ado\msadox.dll" \
    no_namespace
#import "c:\Program Files\Common Files\system\ado\msado15.dll"

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void ProcedureParametersX(void);

//////////////////////////////////////////////////////////
//                                                      //
//    Main Function                                     //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    ProcedureParametersX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//      ProcedureParametersX Function                   //
//                                                      //
//////////////////////////////////////////////////////////
void ProcedureParametersX()
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _CatalogPtr m_pCatalog = NULL;

    //Define ADODB object pointers.
    ADODB::_ConnectionPtr m_pCnn = NULL;
    ADODB::_CommandPtr m_pCommand = NULL;
    ADODB::_ParameterPtr m_pParameter = NULL;

    try
    {
        TESTHR(hr = m_pCnn.CreateInstance(__uuidof(ADODB::Connection)));

        //Open the Connection
        m_pCnn->Open("Provider=Microsoft.Jet.OLEDB.4.0;"
                "Data Source=c:\\Program Files\\Microsoft Office\\"
                "Office\\Samples\\Northwind.mdb;","","",NULL);

        TESTHR(hr = m_pCatalog.CreateInstance(__uuidof(Catalog)));

        //Open the catalog
        m_pCatalog->PutActiveConnection(_variant_t((IDispatch *)m_pCnn));

        //Get the command object
        m_pCommand = m_pCatalog->Procedures->GetItem("CustomerById")->
            GetCommand();

        _variant_t vIndex;

        //Retrieve Parameter information
        m_pCommand->Parameters->Refresh();
        for (long lIndex = 0; lIndex < m_pCommand->Parameters->Count;
            lIndex ++)
        {
            vIndex = lIndex;
            m_pParameter = m_pCommand->Parameters->GetItem(vIndex);
            cout << m_pParameter->Name << ":" << m_pParameter->Type <<
                "\n" << endl;
        }
    }
    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
             (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndProcedureParametersCpp


ParentCatalog Property Example (VC++)

The following code demonstrates how to use the ParentCatalog property to access a provider-specific property prior to appending a table to a catalog. The property is AutoIncrement, which creates an AutoIncrement field in a Microsoft Jet database.

// BeginCreateAutoIncrColumnCpp
#import "c:\Program Files\Common Files\system\ado\msadox.dll" \
    no_namespace
#import "c:\Program Files\Common Files\system\ado\msado15.dll"

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

//Function declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void CreateAutoIncrColumnX(void);

//////////////////////////////////////////////////////////
//                                                      //
//      Main Function                                   //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    CreateAutoIncrColumnX();

     ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//       CreateAutoIncrColumnX Function                 //
//                                                      //
//////////////////////////////////////////////////////////
void CreateAutoIncrColumnX(void)
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _CatalogPtr m_pCatalog = NULL;
    _TablePtr m_pTable = NULL;

    // Define ADODB object pointers.
    ADODB::_ConnectionPtr m_pCnn = NULL;

    //Define string variables
    _bstr_t strCnn("Provider=Microsoft.Jet.OLEDB.4.0;"
        "Data Source=c:\\Program Files\\Microsoft Office\\"
        "Office\\Samples\\Northwind.mdb;");
    try
    {
        TESTHR(hr = m_pCnn.CreateInstance(__uuidof(ADODB::Connection)));

        TESTHR(hr = m_pCatalog.CreateInstance(__uuidof (Catalog)));

        TESTHR(hr = m_pTable.CreateInstance(__uuidof (Table)));

        // Connect the catalog.
        m_pCnn->Open (strCnn, "", "", NULL);

        m_pCatalog->PutActiveConnection(variant_t((IDispatch *)m_pCnn));

        m_pTable->Name="MyContacts";
        m_pTable->ParentCatalog = m_pCatalog;

        // Create fields and append them to the new Table object.
        m_pTable->Columns->Append("ContactId", adInteger,0);

        // Make the ContactId column and auto incrementing column
        m_pTable->Columns->GetItem("ContactId")->Properties->
            GetItem("AutoIncrement")->Value = true;
        m_pTable->Columns->Append("CustomerID", adVarWChar,0);
        m_pTable->Columns->Append("FirstName", adVarWChar,0);
        m_pTable->Columns->Append("LastName", adVarWChar,0);
        m_pTable->Columns->Append("Phone", adVarWChar, 20);
        m_pTable->Columns->Append("Notes", adLongVarWChar,0);
        m_pCatalog->Tables->Append(_variant_t((IDispatch*)m_pTable));

        // Refresh the database.
        m_pCatalog->Tables->Refresh();

        // Delete new table, since this is only an example
        m_pCatalog->Tables->Delete("MyContacts");

        m_pCatalog = NULL;
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
             (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndCreateAutoIncrColumnCpp


PrimaryKey and Unique Properties Example (VC++)

This example demonstrates the PrimaryKey and Unique properties of an Index. The code creates a new table with two columns. The PrimaryKey and Unique properties are used to make one column the primary key for which duplicate values are not allowed.

// BeginPrimaryKeyCpp
#import "c:\program files\common files\system\ado\msadox.dll" \
    no_namespace
#import "c:\program files\common files\system\ado\msado15.dll"

#include "iostream.h"
#include "stdio.h"
#include "conio.h"

//Function Declarations
inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
void PrimaryKeyX(void);

//////////////////////////////////////////////////////////
//                                                      //
//    Main Function                                     //
//                                                      //
//////////////////////////////////////////////////////////
void main()
{
    if(FAILED(::CoInitialize(NULL)))
        return;

    PrimaryKeyX();

    ::CoUninitialize();
}

//////////////////////////////////////////////////////////
//                                                      //
//        PrimaryKeyX Function                          //
//                                                      //
//////////////////////////////////////////////////////////
void PrimaryKeyX()
{
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    _CatalogPtr m_pCatalog = NULL;
    _TablePtr m_pTableNew = NULL;
    _IndexPtr m_pIndexNew  = NULL;
    _IndexPtr m_pIndex  = NULL;
    _ColumnPtr m_pColumn = NULL;

    //Define string variable
    _bstr_t strcnn("Provider=Microsoft.JET.OLEDB.4.0;"
        "Data Source = c:\\Program Files\\"
        "Microsoft Office\\Office\\Samples\\Northwind.mdb;");

    try
    {
        TESTHR(hr = m_pCatalog.CreateInstance(__uuidof(Catalog)));
        TESTHR(hr = m_pTableNew.CreateInstance(__uuidof(Table)));
        TESTHR(hr = m_pIndexNew.CreateInstance(__uuidof(Index)));
        TESTHR(hr = m_pIndex.CreateInstance(__uuidof(Index)));
        TESTHR(hr = m_pColumn.CreateInstance(__uuidof(Column)));

        // Connect the catalog
        m_pCatalog->PutActiveConnection(strcnn);

        // Name new table
        m_pTableNew->Name = "NewTable";

        // Append a numeric and a text field to new table.
        m_pTableNew->Columns->Append("NumField", adInteger, 20);
        m_pTableNew->Columns->Append("TextField", adVarWChar, 20);

        // Append new Primary Key index on NumField column
        // to new table
        m_pIndexNew->Name = "NumIndex";
        m_pIndexNew->Columns->Append("NumField",adInteger,0);
        // here "-1" is required instead of "true".
        m_pIndexNew->PutPrimaryKey(-1);
        m_pIndexNew->PutUnique(-1);
        m_pTableNew->Indexes->Append(
            _variant_t ((IDispatch*)m_pIndexNew));

        // Append an index on Textfield to new table.
        // Note the different technique: Specifying index and
        // column name as parameters of the Append method
        m_pTableNew->Indexes->Append("TextIndex", "TextField");

        // Append the new table
        m_pCatalog->Tables->Append(_variant_t ((IDispatch*)m_pTableNew));

        cout << m_pTableNew->Indexes->Count << " Indexes in "
            << m_pTableNew->Name << " Table" << endl;
        m_pCatalog->Tables->Refresh();

        _variant_t vIndex;
        // Enumerate Indexes collection.
        for (long lIndex = 0;lIndex < m_pTableNew->Indexes->Count;
             lIndex++)
        {
            vIndex = lIndex;
            m_pIndex = m_pTableNew->Indexes->GetItem(vIndex);
            cout << "Index " << m_pIndex->Name << endl;
            cout << "   Primary key = " << (m_pIndex->GetPrimaryKey() ?
                "True" : "False") << endl;
            cout << "   Unique = "  << (m_pIndex->GetUnique() ? "True" :
                "False") << endl;

            // Enumerate Columns collection of each Index
            // object.
            cout << "    Columns" << endl;

            for (long lIndex = 0;lIndex < m_pIndex->Columns->Count;
                 lIndex++)
            {
                vIndex = lIndex  ;
                m_pColumn = m_pIndex->Columns->GetItem(vIndex);
                cout << "       " << m_pColumn->Name << endl;
            }
        }

        // Delete new table as this is a demonstration
        m_pCatalog->Tables->Delete(m_pTableNew->Name);
        m_pCatalog = NULL;
    }

    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());

        printf("\n\tSource :  %s \n\tdescription : %s \n ",
              (LPCSTR)bstrSource,(LPCSTR)bstrDescription);
    }

    catch(...)
    {
        cout << "Error occured in include files...."<< endl;
    }
}
// EndPrimaryKeyCpp


Microsoft OLE DB Start Page

OLE DB Start Page

OLE DB
Version 2.6

Purpose

OLE DB is a set of COM-based interfaces that expose data from a variety of sources. OLE DB interfaces provide applications with uniform access to data stored in diverse information sources, or data stores. These interfaces support the amount of DBMS functionality appropriate to the data store, enabling the data store to share its data.

For more information about changes and enhancements for MDAC 2.6, see What's New in the MDAC SDK.

For information about support for Microsoft Data Access Components, see Getting Help from Microsoft Product Support Services.

Main Components of OLE DB 2.6

OLE DB Programmer's Reference

The Programmer's Reference contains an introduction to OLE DB, a discussion of OLE DB core components, the OLE DB for OLAP documentation, and a reference section for all OLE DB interfaces and methods.

OLE DB Providers

The OLE DB Providers section contains documentation for eight data or service providers as well as the OLE DB Simple Provider (OSP) Toolkit.

OLE DB Test Tools

The OLE DB testing components in this section are provided to help you develop OLE DB consumers and providers.

OLE DB Samples

This section includes three OLE DB sample programs written in Visual C++.

Feedback

Send feedback about the OLE DB documentation or samples directly to the OLE DB documentation team.

See Also

  • OLE DB Technical Articles
  • ADO
  • ODBC


OLE DB Programmer's Reference

What's New in OLE DB

The OLE DB documentation released with Microsoft® Data Access Components (MDAC) 2.6 includes a number of topics that have been changed, added, or deleted.

OLE DB Programmer's Reference

A new interface, IRowsetBookmark, has been introduced to set the next fetch position based on a bookmark.

Another new interface, ICommandStream, has been introduced to support issuing commands in a stream instead of as a text string. This interface is used in conjunction with the new properties, DBPROP_OUTPUTENCODING and DBPROP_OUTPUTSTREAM, to return query results in a stream. Providers, such as the OLE DB Provider for SQL Server, can use streams to return potentially sizable query results, such as rowsets encoded as XML data.

The OLE DB header, oledb.h, now defines certain data types as 64-bits long when the "/D_WIN64" compiler option is used. These 64-bit data types are used in conjunction with 64-bit processors and corresponding builds of OLE DB and the operating system. OLE DB samples should work properly for either 32-bit or 64-bit systems; however, samples are certified only for 32-bit systems.

The conceptual and reference material for change notifications has been clarified. Modifications have been made to Chapter 12: OLE DB Object Notifications and to the IRowsetNotify interface and its methods to clarify the interaction between phases, reasons for those phases, and changes to a rowset.

The OLE DB Glossary has been updated and edited. Terms not specific or central to an understanding of OLE DB have been removed. Terminology introduced in the conceptual matter now has a clearer definition in the glossary. Terms from OLE DB for OLAP have also been added.

To efficiently obtain information, or table statistics, that can be used to estimate the number and distribution of values in a query result, OLE DB introduces the TABLE_STATISTICS schema rowset, the DBPROP_TABLESTATISTICS property, a new bit in the DBPROP_OPENROWSETSUPPORT property, and a new histogram rowset returned by IOpenRowset::OpenRowset. A conceptual overview of these features and how to use them effectively is provided in the section "Special-Purpose Rowsets," in Chapter 1: Overview of OLE DB.

Other new OLE DB properties that have been defined and are described in Appendix C: OLE DB Properties. The following table provides a brief description of each.

Property | Description
DBPROP_OUTPUTENCODING | Specifies the encoding to be used in a returned stream
DBPROP_OUTPUTSTREAM | Specifies the stream for which results should be returned
DBPROP_SKIPROWCOUNTRESULTS | Requires a provider that supports multiple results to either return or skip control row count results
DBPROP_TABLESTATISTICS | Indicates support for table statistics
DBPROP_OPENROWSETSUPPORT | A new bit indicates whether the provider supports histogram rowsets.

OLE DB for OLAP

Added features expand the capabilities of the consumer (or client application) to view and analyze multidimensional data obtained through a multidimensional or tabular data provider.

Expanded Hierarchy Support

OLE DB for OLAP now supports unbalanced and network hierarchy types, along with balanced and ragged types. For more information, see "Supported Hierarchy Types" in Chapter 22: OLE DB for OLAP Concepts.

Dimension Write-Back

Dimension write-back provides a means for your application to add or remove members from a dimension to create "what-if" scenarios.

Annotations

The ANNOTATIONS column, a new optional column in the CUBES, DIMENSIONS, HIERARCHIES, and MEMBERS schema rowsets, provides a means to store and share comments on hierarchy members and cell values.

New Schema Rowsets

The following schema rowsets have been added to allow client applications to execute processes stored in the data cub, and to identify information pertinent to sets:

  • ACTIONS rowset The ACTIONS rowset allows data residing in the cube to be "linked" to actions appropriate to the data. This feature is explained in detail in "ACTIONS Rowset," in Chapter 23: OLE DB for OLAP Objects and Schema Rowsets.
  • FUNCTIONS rowset The FUNCTIONS rowset exposes all functions supported by a multidimensional provider and user-defined functions on the client. See "FUNCTIONS Rowset," in Chapter 23: OLE DB for OLAP Objects and Schema Rowsets.
  • SETS rowset A schema rowset for named sets has been added. See "SETS Rowset," in Chapter 23: OLE DB for OLAP Objects and Schema Rowsets.

Additional and Modified Functions

Following is a summary of functions that have been added or changed in the MDAC 2.6 release for OLE DB for OLAP:

  • In the ANCESTORS function, member and set value expressions have been added and/or modified.
  • The COUSIN function has been deprecated.
  • The DESCENDANTS function has a new form.
  • The LINKMEMBER function, that links equivalent members in different hierarchies, has been added.
  • String manipulation functions, allowing conversions to and from strings, sets, tuples, and members, have been added.
  • Drilling operations have been enhanced, and new ones have been added.

Function descriptions and examples can be found in the "Advanced MDX" section of Chapter 25: Multidimensional Expressions.

New User-Defined Functions

User-defined functions can now be used within MDX to operate on multidimensional data. These custom functions can be registered in your library and used by your application like any other function.

Properties

New OLE DB for OLAP properties have been defined and are described in Appendix C: Properties. The following table provides a brief description of these new (or modified) properties.

Property | Description
MDPROP_VISUALMODE | Defines visibility of precalculated totals
MDPROP_NAMED_LEVELS | (Modified) Support for named levels

OLE DB Samples

The Rowset Viewer sample has been updated for the row object and direct binding interfaces. It also now handles multidimensional data functions for developers using OLE DB for OLAP.

The OLE DB Sample Provider sample has been moved here from Technical Articles.

The OLE DB Query Demo sample has been deleted from this release of MDAC.

OLE DB Test Tools

The Trace Provider tool has been removed from MDAC pending further development. It had been offered as a beta preview for MDAC 2.5.


OLE DB Programmer's Reference

OLE DB Programmer's Reference

The OLE DB Programmer's Reference is divided into the following parts:

  • Part 1: Introduction to OLE DB, contains 18 chapters of conceptual material.
  • Part 2: OLE DB Core, contains a discussion of OLE DB services and OLE DB core components. Chapter 19 covers common services that enhance the native functionality and performance of OLE DB providers; Chapter 20 discusses the essential elements of OLE DB.
  • Part 3: OLE DB for OLAP, contains seven chapters of conceptual material relating to OLE DB for OLAP.
  • Part 4: OLE DB Reference, describes all of the OLE DB interfaces, including Core, OLE DB, and OLAP. The reference contains enhanced explanations and clarifications to the conceptual material.
  • Part 5: Appendixes, gives detailed information about topics such as data types and properties. Appendix F is a listing of the code for the sample OLE DB consumer application that is used for examples throughout the programmer's reference.

The OLE DB Glossary defines terms you will need to understand to work with OLE DB.

Links to related information are included for easier navigation, as well as new interfaces, data types, schema rowsets, and properties.

Working with 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 illustration 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 table explains the typographic conventions used in this documentation.

Format | Used for
DBPROPFLAGS | Uppercase letters indicate certain values, property names, SQL statements, and macro names.
#include <oledb.h> | Monospace font is used for sample command lines and program code.
argument | Italicized words indicate programmatic arguments, 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 interface and method names.


Part 1 Introduction to OLE DB

OLE DB Programmer's Reference

Part 1: Introduction to OLE DB

Part 1 of the OLE DB Programmer's Reference describes the functional advantages of using OLE DB for consistent data access performance across diverse data stores and also provides an in-depth discussion of the primary OLE DB objects, data types, properties, and important programming considerations. This section contains the following chapters:

  • Chapter 1: Overview of OLE DB
  • Chapter 2: Data Source and Session Objects
  • Chapter 3: Commands
  • Chapter 4: Rowsets
  • Chapter 5: Updating Data in Rowsets
  • Chapter 6: Getting and Setting Data
  • Chapter 7: BLOBs and COM Objects
  • Chapter 8: Binder Objects and Direct Binding
  • Chapter 9: Row and Stream Objects
  • Chapter 10: Index Rowsets
  • Chapter 11: Integrated Indexes
  • Chapter 12: OLE DB Object Notifications
  • Chapter 13: Data Types in OLE DB
  • Chapter 14: Properties
  • Chapter 15: Transactions
  • Chapter 16: Errors
  • Chapter 17: Asynchronous Processing
  • Chapter 18: Programming Considerations


Chapter 1: Overview of OLE DB

Today, a vast amount of the critical information necessary for conducting day-to-day business is found outside of traditional, corporate production databases. Instead, this information is found in file systems, in indexed-sequential files such as Btrieve, and in personal databases such as Microsoft® Access and Microsoft Visual FoxPro®; it's found in productivity tools such as spreadsheets, project management planners, and electronic mail; and more and more frequently, it's found on the World Wide Web.

To take advantage of the benefits of database technology, such as declarative queries, transactions, and security, businesses have traditionally had to move the data from its original containing system into some type of database management system (DBMS). This process is expensive and redundant. Furthermore, businesses need to be able to exploit the advantages of database technology not only when accessing data within a DBMS but also when accessing data from any other type of information container. To address this need, Microsoft created OLE DB.

OLE DB is a set of Component Object Model (COM) interfaces that provide applications with uniform access to data stored in diverse information sources and that also provide the ability to implement additional database services. These interfaces support the amount of DBMS functionality appropriate to the data store, enabling it to share its data.


Conceptual Programming Models in OLE DB

OLE DB is consistent with a general model of how applications access data. The model assumes the data is not directly accessible by the application but resides in some separate data store, such as a file or e-mail system, a database, or a resource on the Internet.

The application requests information from an intermediary that returns a copy of the desired data to the application. Although different data stores have different native means to access their information, the intermediary presents a single, consistent means of access to the application.

Data delivered to the application is held in a cache, which is used to locate, examine, and manipulate items of data. Furthermore, the cache facilitates adding new data items and modifying or deleting existing ones. If appropriate, any changes made in the cache are written back to the data store.

Basically, in this model, an application retrieves and optionally updates data in a data store with the help of the intermediary; that is, a consumer (the data access application) accesses data from a data store with the help of a provider (the intermediary). The concept of consumer and provider is fundamental to OLE DB and is the context in which the reader should view all descriptions of OLE DB technology.

Note Described in COM terms, an OLE DB consumer is any piece of system or application code that consumes an OLE DB interface. This includes OLE DB components themselves. An OLE DB provider is any software component that exposes an OLE DB interface.

OLE DB formalizes the general model with a programming model—the series of tasks that must be performed by a consumer to access, or a provider to deliver, data. OLE DB expresses the programming model by using an object model—the set of COM objects that accomplish the tasks in the programming model.

The COM objects in the object model are manipulated through their interfaces, each of which describes a portion of the functionality necessary to access data. The interfaces are like the two faces of a coin: Developers writing providers map the native functionality of specific data stores to the functionality defined by the interfaces; developers writing consumers invoke the functionality exposed by the interfaces to access the data.

The availability of interface functionality is said to be provider-specific because the availability depends on whether a given provider is able to implement all the defined functionality. Consumers can request a list of the provider's capabilities at run time.

Providers contain programming logic that may augment the native functionality of a data store. Not only can providers supply data, but they can supply services—such as resource pooling and automatic transaction enlistment—through OLE DB interfaces.

Finally, service providers—such as the Remoting Provider, Data Shaping Service, and Persistence Provider—supply services to data providers, such as accessing data on remote machines, restructuring cached data, and locally saving and restoring cached data.

Cotypes as Object Definitions

A cotype is 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. The following example, which defines the cotype for the data source object, indicates whether the implementation of a specific interface is mandatory or optional.

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;
}

Object Hierarchy

The objects in OLE DB are created in a hierarchy. That is, higher-level objects explicitly or implicitly create lower-level objects. The highest-level object in the hierarchy is created with the COM API routine, CoCreateInstance. Each successive object uses its own interface methods to create subsidiary objects.

Properties

OLE DB properties qualify general functionality, making the functionality specific. The value of a property either represents the state of an attribute of a data source object or service or controls the behavior of a method or service.

OLE DB supports getting or setting arrays of properties in a single operation and also supports getting a list of properties supported by a provider, which enables a consumer to determine the provider's capabilities at run time. Options indicate whether setting each property is required or optional, and status flags indicate whether each property was set successfully. 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.

Event Notifications

OLE DB uses an ActiveX® controls notification model to implement notifications among OLE DB providers and consumers. Notifications are used by groups of cooperating consumers sharing a rowset. All consumers and 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.

For more information about notifications, see Chapter 12: OLE DB Object Notifications.

The Basic OLE DB Programming Models

For a simplified conceptual understanding, consider OLE DB as having two programming models: the "Rowset" and "Binder" models. The Rowset Programming and Object Model mirrors the general data access model described in the preceding section, where OLE DB objects are created in a hierarchical fashion. The Binder Programming and Object Model is a type of shorthand, where OLE DB objects are created directly from Uniform Resource Locators (URLs).


Rowset Programming and Object Model

The tasks in the Rowset programming model are as follows:

  1. Identify the data source object.
  2. Establish a context for transactions, subsequent tasks, and a means of retrieving data if the provider doesn't support commands.
  3. Create and execute data access commands (if commands are supported).
  4. Receive, hold, and manipulate the data in a cache (if a command requests data).
  5. If the data represents a container such as a file system directory, hold and manipulate the container and, optionally, its contents.
  6. Handle any errors that occur.

The Rowset programming model is represented by the hierarchy of objects shown in the following illustration:

In the preceding illustration, the object creation hierarchy is as follows:

  1. The enumerator object creates the data source object. (The enumerator object is optional because there are several ways to create a data source object.)
  2. The data source object creates a session object.
  3. The session object optionally creates a command object.
  4. The session or command object creates a rowset object.
  5. The rowset object creates a row object.
  6. The row object creates a stream object. (If the row object represents a collection, such as a file system directory, it can create other rowset, row, or stream objects that represent the contents of the collection.)

Note Because any object can create an error object, the error object is shown as an autonomous entity in the preceding illustration.

The following topics take a closer look at the OLE DB objects and their role in the Rowset programming model:

  • Enumerator Objects
  • Data Source Objects
  • Session Objects
  • Command Objects
  • Rowset Objects
  • Row and Stream Objects
  • Error Objects


Enumerator Objects

In the Rowset Programming and Object Model, the enumerator programming task is represented by the enumerator object. The enumerator is an OLE DB object that searches for available data source objects and other enumerators. Consumers that are not customized for a particular provider can use enumerators to search for an appropriate data source object to use.

Note Although the enumerator object comes first in this model, other means of obtaining a data source object are used more often.

An enumerator object exposes the ISourcesRowset interface, which returns a rowset describing all data source objects and enumerators that are known to the enumerator. Another type of enumerator object, the root enumerator, traverses the registry looking for data source objects and other enumerators. For more information about the root enumerator object, see "Root Enumerator Object" in Chapter 20: OLE DB Core Components. 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

In the Rowset Programming and Object Model, the data source programming task is represented by the data source object. The data source object represents a provider's underlying data store, such as a file or DBMS. Each OLE DB provider defines its own data source object class.

In this programming model, a consumer must first identify a data source object. The data source object specifies a provider and any authorizations and capabilities required to access the underlying data store. These specifications are unique to each data source object and are defined by its properties. After properties are set, the consumer initializes the data source object, which establishes the environment necessary to exchange data between the consumer and provider. After the data source object is initialized, the consumer can get self-descriptive information about the data store but cannot yet access data.

A data source object can be created in the following ways:

  • From a registry entry that identifies the provider
  • From an interface that uses a connection string to specify the provider and property values
  • From an interface that presents a dialog to prompt a user for the provider and property values

For more information about creating instances of data source objects, see Chapter 2: Data Source and Session Objects.


Session Objects

In the Rowset programming model, the session programming task is represented by the session object. The session object is a factory for command and rowset objects. Session objects can also act as factories for transaction objects, which are used to control nested transactions.

In this programming model, a session provides the following:

  • A context for transactions
  • A default means of obtaining data from a data store if the provider does not support commands
  • A means to modify the properties of tables and indexes on the data store

Note It is provider-specific whether a single data source object is able to create multiple sessions. Simple providers, such as those built over a base table, index, file, or in-memory structure instead of a relational DBMS data store, usually do not support obtaining data with commands.

In OLE DB, a transaction is a set of operations on a data store that must be completed as a whole; if any part of the transaction fails, the entire transaction fails. Transactions can be created implicitly or explicitly, and may be nested. OLE DB factors support for transactions into a set of transaction interfaces that are created by the session object. For more information, see the section Transaction Management, in this chapter.

For more information about session objects, see Chapter 2: Data Source and Session Objects.


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. Transactions 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 ITransactionLocal::StartTransaction 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 ITransactionLocal::StartTransaction and ITransaction::Commit or ITransaction::Abort method calls and including other objects created underneath it (commands or rowsets)—is part of the transaction.

ITransactionLocal::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 ITransactionLocal::StartTransaction within an existing transaction begins a new nested transaction below the current transaction. Calling ITransaction::Commit or ITransaction::Abort on the session commits or aborts, respectively, the transaction at the lowest level.

Distributed Transactions

OLE DB defines an interface, ITransactionJoin, for consumers to request that providers enlist in a coordinated transaction among multiple (possibly distributed) data providers and other types of resource managers. The details of how providers enlist themselves as resource managers with the transaction coordinator are described in the Component Services (or Microsoft Transaction Server if you are using Microsoft® Windows NT®) documentation.

For more information about transactions, see Chapter 15: Transactions.


Command Objects

In the Rowset programming model, the command programming task is represented by the command object. A command object is a container for a text or stream command.

In OLE DB, a text command is a string to be passed from a consumer to a provider's data source object for execution by the provider's underlying data store. Most commonly, the text command is an SQL SELECT statement. If the text command is an SQL SELECT statement, the command object becomes a factory for a rowset. (A single session object may be able to create multiple commands.) A command object holds a text command and any command properties to be requested from the provider. Command properties are properties that either control execution of the command or, if executing the command causes a rowset to be created, affect the behavior of the resulting rowset.

Commands are requests to the underlying data store to insert, retrieve, modify, delete, or otherwise manipulate data or structure in the data store. For example, a command to retrieve all the data in a table of a relational database might be an SQL statement such as the following:

"SELECT * FROM table_name".

The results of a command that retrieves data are stored in a cache. A command object may contain multiple commands that will return multiple results. Commands may be expressed as text or as a stream, and results may be expressed as a rowset or as a stream.

Command objects expose various interfaces representing different areas of functionality of a query processor, including query formulation, preparation, and execution. For example, if a command object exposes the ICommandWithParameters interface, the command object can support dynamic parameters in addition to a text command and command properties.

For more information, see Chapter 3: Commands.


Rowset Objects

In the Rowset programming model, the rowset is the cache that holds data returned from a data store. A rowset can be envisioned as a rectangular array of zero or more rows and one or more columns, where each column has a specific data type. In OLE DB, there are several special-purpose rowsets that provide a number of features and benefits to the data access programmer.

The rowset programming task is represented by the rowset object, which is the central object that enables all OLE DB data providers to expose data in tabular form. The rowset object contains data from the data store retrieved in response to a command or a session object method and provides means to navigate from row to row within the rowset, to examine and modify data, and to propagate any modifications back to the data store.

Values in columns, and parameters in commands, are set and retrieved using bindings and accessors. Bindings map the location of data in system and application memory and determine any data type conversions to be performed when data is moved from one to the other. Accessors are arrays of bindings that may be used by the interface methods that set or get values or parameters. For more information about accessors and bindings, see Chapter 6: Getting and Setting Data.

The following illustration depicts a basic rowset object and its mandatory interfaces. The consumer can request other, optional interfaces to obtain more functionality. In the illustration, the row cache is the system buffer that holds information from the data store. Consumer applications use an array of row handles, a fundamental data structure in OLE DB programming, to indirectly reference row cache data.

Rowset object interfaces define groups of column bindings (IAccessor); methods to fetch and traverse rows sequentially (IRowset); and information about data type conversions (IConvertType), rowset columns (IColumnsInfo), and the rowset itself (IRowsetInfo).

Data structures supported by a generic rowset object

For more information about rowsets and rowset objects, see Chapter 4: Rowsets.


Special-Purpose Rowsets

OLE DB uses rowset objects not only as a means to represent consumer data, but as a tool to represent information such as the following:

  • Metadata—Schema rowsets contain self-descriptive information (metadata) about the data store or about a rowset derived from the data store. Schema rowsets may contain logical descriptions of the data, such as the columns in a table, or physical descriptions of the data store, such as the names and structures of indexes in a database. Some OLE DB providers support schema rowsets. For more information, see Appendix B: Schema Rowsets.
  • Indexes—An index provides access to data in the rows of a rowset based on key values and references to those rows. Index rowsets may be separate from the rowset they reference (index rowsets) or part of the referenced rowset (integrated index rowsets). Providers over ISAM data typically support index rowsets. For more information, see Chapter 10: Index Rowsets, and Chapter 11: Integrated Indexes.
  • Views—A view rowset defines a subset of the rows and columns from a rowset as specified by some criteria. Views contain no data of their own, and they cannot be used to combine data from multiple rowsets. Views are useful for sorting and filtering.
  • Chapters—Chapters are rowsets whose rows are grouped together as specified by some common criteria, and each group of rows can be treated as if it were a separate rowset. Additional columns, such as those created from calculations on the same column in several rows (such as sums and averages) or different columns in a single row, may be appended to one or more rows in a chapter.

    References to a chapter may be set as the value of columns in another rowset. The effect is that the chapter appears to be contained in a column of the other rowset. That is, the rowsets appear to be hierarchical (hierarchical rowsets). For more information, see Chapter 4: Hierarchical Rowsets.

  • Table statistics—A new feature as of OLE DB 2.6, histogram and metadata rowsets contain statistical information about the rowset that would result from a query request. These rowsets are of value mostly to developers writing query processors. For more detailed information, see the topics "Table Statistics" and "Histogram Rowset" in this section.

Table Statistics

The OLE DB TABLE_STATISTICS schema rowset and histogram rowset, along with associated properties, enables a specialized audience of consumers to write efficient distributed query processors by giving providers the means to expose useful statistics on base tables. All statistics are returned in the form of rowsets.

Using table statistics, the consumer can estimate the number of rows that will be returned by a query. Three kinds of statistics are provided: column cardinalities, tuple cardinalities, and histograms.

Column and Tuple Cardinality

In general, cardinality is the number of elements in a given mathematical set, and a tuple is a set with a specified number of ordered elements.

Column cardinality is the number of unique values of a column in a table. Tuple cardinality is the number of unique values of a tuple in a table. Column and tuple cardinality is used with other information to estimate the number of rows that would match a given predicate (for example, the WHERE clause of a SELECT statement).

Histogram

In general, a histogram consists of a sorted set of ranges versus the number of elements within each range.

Histogram rowsets contain information regarding the distribution of values in a column. In the absence of histograms, query processors have to rely on a uniform distribution of values. With histograms, a query processor can make more accurate cardinality estimates.

Specific Features

The following features support the ability to obtain statistical information:

  • DBPROP_TABLESTATISTICS—A data source information property that indicates whether the provider supports one or more of column cardinality, tuple cardinality, or histogram statistics on a specific base table.
  • DBPROP_OPENROWSETSUPPORT property—A new bit is added which indicates whether the provider supports histogram rowsets. Any provider that supports histograms should also set DBPROP_TABLESTATISTICS.
  • TABLE_STATISTICS—A schema rowset obtained with the IDBSchemaRowset interface. This schema rowset lists column and tuple cardinality information as well as other information for a given base table.
  • IOpenRowset::OpenRowset—This method accepts a TableID and special DBID structure arguments and returns a histogram rowset corresponding to the specified table.

A provider may choose to support both cardinalities and histograms, only cardinalities, or only histograms.

The provider may choose to optimize its implementation of histogram and cardinality information by sampling only a subset of the rows in the base table. In this case, the information reported in the histogram and TABLE_STATISTICS rowsets should be scaled up to the total number of rows in the base table and not merely the sample size.

Whether information returned in the TABLE_STATISTICS schema and histogram rowsets is up-to-date with the latest table contents is provider-specific. Consumers should not assume the statistics are current. The provider may choose its own strategy in how often statistics are updated.

Example Table

The following table will be used to illustrate concepts in subsequent discussions. The columns are arbitrarily named k1, k2, and k3, and their values have no special meaning other than being easy to work with.

k1 | k2 | k3
10 | 10 | 10
10 | 20 | 40
10 | 20 | 40
20 | 30 | 40
30 | 40 | 50

TABLE_STATISTICS Schema Rowset

The TABLE_STATISTICS schema rowset describes the available set of statistics on tables in the provider. Each statistic is identified by a fully qualified name (catalog/schema/name), like other database objects, and corresponds to statistics data on one or more columns of a table.

Consider a statistic defined over n ordered set of columns, c1, c2, ... cn. A provider can support one or more of the following statistics:

Column cardinalities

Column cardinality values are defined as follows:

di = (number of distinct values of the column ci in the table)

The column cardinality for our example table is as follows:

d1= 3 (for values 10, 20, and 30 in column k1)
d2 = 4 (for values 10, 20, 30, and 40 in column k2)
d3 = 3 (for values 10, 40, and 50 for column k3)

Tuple cardinalities

Tuple cardinality values are defined as follows:

di = (number of distinct values of tuples <c1, c2, ..., ci> where ci is a column in the table).

In this definition, tuples all start from the first column (c1). The tuple cardinality of the example table is as follows:

<k1> = 3 (for values 10, 20, and 30 in k1)
<k1, k2> = 4 (for values 10, 20, 30, and 40 in k1 and k2)
<k1, k2, k3> = 5 (for values 10, 20, 30, 40, and 50 in k1, k2 and k3)

The provider can choose not to support some of the i cardinality values. If a particular cardinality value is not available, the provider should return NULL as the value.

Histogram Rowset

The IOpenRowset::OpenRowset method can return a histogram rowset for a specified table. A histogram rowset consists of a row for each range of values of the first column in the table. The histogram rowset is sorted on the RANGE_HI_KEY column in ascending order.

Histogram rowsets have the columns described in the following table.

Column name | Type | Description
RANGE_HI_KEY | (depends on column type) | The highest key value corresponding to this histogram range.
RANGE_ROWS | DBTYPE_R8 | (Optional) The fraction of the number of rows in the base table that have a value that falls in this histogram range.
EQ_ROWS | DBTYPE_R8 | (Optional) The fraction of the number of rows in the base table that have a value equal to RANGE_HI_KEY.
DISTINCT_RANGE_ROWS | DBTYPE_I8 | (Optional) Number of distinct values in the base table that fall in this histogram range.

The provider must return at least one of RANGE_ROWS or EQ_ROWS for each histogram row. The provider should return NULL for unsupported optional columns.

Each range is represented by RANGE_HI_KEY, the highest value in the first column of the table in that range. The main piece of information associated with each range is RANGE_ROWS, the fraction of rows in the base table that have a value that falls in the histogram range.

RANGE_ROWS = (number of rows in the base table that have a value x that falls in the range defined by Ki-1 < x <= Ki, where Ki is the RANGE_HI_KEY for the i-th histogram range) / (number of rows in the table).

The provider can also return EQ_ROWS, which is the number of rows in the table that exactly match RANGE_HI_KEY.

EQ_ROWS = (number of rows in the first column of the base table that contain a value equal to RANGE_HI_KEY) / (number of rows in the table).

A possible histogram rowset for the example table is illustrated by the following. (The real numbers, 0.6 and 0.2, are the equivalents of the fractions 3/5 and 1/5.)

range_hi_key | range_rows | eq_rows | distinct_range_ rows
10 | 0.6 | 0.6 | 1
20 | 0.2 | 0.2 | 1
30 | 0.2 | 0.2 | 1

Providers can also provide information about frequent values in the table. This can be done by adding a histogram row corresponding to the frequent value and setting EQ_ROWS to the fraction of the rows in the table that match this frequent value.


Row and Stream Objects

The row and stream programming tasks are represented by the row and stream objects. A row object is an OLE DB object that contains a set of columns of data. A stream object is an OLE DB object that encapsulates the contents of files, e-mail messages, or other stream-based objects.

Rows and streams fit more naturally in the Binder programming model, which is described in the Binder Programming and Object Model topic in this chapter.


Error Objects

The error programming task is represented by the Automation error object. An error object is an object that contains detailed information about an error.

Each interface method reports a return code indicating whether the method succeeded or failed. In some cases, if a method operates on an array of data structures, an array of status information is returned, reporting the state of each element of the array of data structures.

In addition, providers can expose OLE DB error objects that extend the Automation error object and that contain extended error information such as a description of the error or the appropriate SQLSTATE. Error objects can be created by any interface on any OLE DB object. Providers can also choose to define custom error objects.

For more information, see Chapter 16: Errors.


Binder Programming and Object Model

Some data is not well-represented by a rectangular table. For example, consider an e-mail system where messages have fields such as "To:," "From:," "Received:," "Subject:," "Cc:," and so on. Each message could be represented as a row in a table, and each field as a column. However, not every message has the same fields, so not every row would have the same columns. This means the table would not be rectangular.

The e-mail system may contain folders and messages; that is, containers and contents. The containers may occur recursively, and the contents may be of arbitrary size and data type (such as text, embedded objects, and so on). Again, this type of information is difficult to express in a table.

In addition, it is sometimes useful to represent an OLE DB object with a name, such as a Uniform Resource Locator (URL).

For situations like these, OLE DB offers the Binder programming model. The Binder model associates (that is, binds or direct binds) a URL with an OLE DB object, automatically creating a hierarchy of objects if necessary.

The tasks in the Binder model are, given a URL, to find a provider binder that can process the URL and to bind the OLE DB object. The Binder programming model is represented by the hierarchy of objects shown in the following illustration:

The root binder object creates a provider binder object, and the provider binder can create any other OLE DB object. If the row object represents a container or contents, it can recursively create a rowset, stream, or another row object.

Because any object can create an error object, the error object is shown as an autonomous entity in the preceding illustration.

The following topics take a closer look at the OLE DB binder, row, and stream objects and their role in the Binder programming model:

  • Root Binder Objects
  • Provider Binder Objects
  • Row Objects
  • Stream Objects


Root Binder Objects

In the Binder Programming and Object Model, the root binder task is represented by the root binder object. The root binder object is an OLE DB object that oversees the direct binding process and manages the mapping of URL namespaces to OLE DB providers.

The root binder object searches the system registry for the provider binder best able to process the given URL, instantiates it, and then delegates the remaining processing to it. The function of the root binder is analogous to the enumerator in the Rowset Programming and Object Model.

For more information, see Chapter 8: Binder Objects and Direct Binding.


Provider Binder Objects

In the Binder Programming and Object Model, the provider binder programming task is represented by the provider binder object. The provider binder object is an OLE DB object that performs direct binding operations on the URL namespace for which it is registered.

The provider binder object represents a provider that knows how to process a URL. A URL can be bound to a data source, session, command, rowset, row, or stream object. In addition, a row object can bind a rowset, stream, or another row object.

When a solitary object is bound, OLE DB implicitly creates a hierarchy of superior objects if necessary. For example, when a session object is bound, a data source object is created automatically and a method on the bound object enables the consumer to discover the next higher-level object.

For more information, see Chapter 8: Binder Objects and Direct Binding.


Row Objects

In the Binder Programming and Object Model, the row programming task is represented by the row object. A row object is an OLE DB object that contains a set of columns of data.

A row can be created directly from the URL or from a specific row in a rowset. A row object created from a row in a rowset object contains all the columns of the row as well any additional columns appended by the provider binder.

If the row represents a container, such as a directory in a file system, the container's contents, such as files and subdirectories, can be retrieved as a row or rowset object. If the row represents contents, such as a file, the contents can be retrieved as a stream object.

A row object is different from a rowset object consisting of a single row because the objects have different interface methods.

For more information, see Chapter 9: Row and Stream Objects.


Stream Objects

In the Binder Programming and Object Model, the stream programming task is represented by the stream object. A stream object is an OLE DB object that encapsulates the contents of files, e-mail messages, or other stream-based objects.

A stream is data is treated purely as a series of bytes, without any interpretation of their meaning. A stream could contain the text in an e-mail message, data in a Microsoft Excel spreadsheet file, or a command and its parameters.

Two interfaces enable streams to be read or written either in a forward-only fashion or at any point in the stream.

For more information, see Chapter 9: Row and Stream Objects.


Component Database Management Systems

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. OLE DB defines an open, extensible collection of COM interfaces that encapsulate reusable portions of DBMS functionality. These interfaces define the boundaries of DBMS components, such as row containers, query processors, and transaction coordinators, that provide uniform transactional access to diverse information sources. Such components factor a DBMS into a set 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 not only contribute to uniform data access among diverse information sources but also help reduce the application's complexity by enabling developers to use only the DBMS functionality they need. The following illustration shows how an application can employ OLE DB interfaces to initiate actions by a query processor to access different data structures.

A component DBMS architecture using OLE DB interfaces

The chapters that follow go into these concepts in detail.


Chapter 2: Data Source and Session Objects

Two types of COM objects are necessary for consumers to connect to providers: data source objects and session objects.

To connect to an OLE DB provider, a consumer must first create and initialize an instance of the provider's data source object. This data source object can then be used to create a session object, which in turn 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 a provider and to define how the different units relate to one another in a system with parallel activities.

For more information on | Go to
Data providers, consumers, and service components | "Conceptual Programming Models in OLE DB" in Chapter 1: Overview of OLE DB
Transactions | Chapter 15: Transactions
OLE DB connection models | "Rowset Programming and Object Model" and "Binder Programming and Object Model" in Chapter 1: Overview of OLE DB
Data source object | "Data Source Objects" in this chapter
Session | "Session Objects" in this chapter
Rowsets | Chapter 4: Rowsets


Data Source Objects

In OLE DB, a data source object is a COM object through which a consumer connects to a provider's underlying data store.

It is important to distinguish a provider's data source object, which provides data from the data store to a consumer, from the data store itself, which contains the data but interacts with the consumer only through the data source object. For example, a data store might be a comma-separated-value (CSV) text file, an SQL DBMS, or an in-memory array in an application.

The data source object cotype is defined as follows. For more information about cotypes, see "Conceptual Programming Models in OLE DB" in Chapter 1: Overview of OLE DB.

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 to register classes of data source objects, see "Provider Registry Entries" in Chapter 18: Programming Considerations.


Creating Data Source Objects

The OLE DB connection model defines how OLE DB consumers locate and activate OLE DB data providers. Two objects are the basis for the OLE DB connection model: the data source object and the session object. To access an OLE DB provider, a consumer must first create an instance of the provider's data source object. Each provider is identified by a unique class identifier (CLSID) in the registry.

Prior to OLE DB 2.0, consumers created a provider's data source object through the root enumerator or by passing the provider's CLSID to CoCreateInstance, as in the following code example:

// Create an instance of CLSID_MSDASQL.
CoCreateInstance(CLSID_MSDASQL, NULL, CLSCTX_INPROC_SERVER,
                 IID_IDBInitialize,(void**)&pIDBInitialize);

This model requires applications to hard-code provider CLSIDs or to obtain the CLSID at run time through application-specific mechanisms. OLE DB 2.0 provides common services that consumers can leverage by calling IDataInitialize or IDBPromptInitialize methods. The IDataInitialize interface is exposed by the Service Component Manager, which can be instantiated by using CLSID_MSDAINITIALIZE. The IDBPromptInitialize interface is exposed by the Data Links component, which can be instantiated by using CLSID_DataLinks. For more information about these interfaces, see "IDataInitialize" and "IDBPromptInitialize" in the OLE DB Core Components documentation, which is included in the Microsoft® Data Access Components (MDAC) SDK.

For example, to create an instance of a provider that can take advantage of OLE DB 2.0 services, based on the provider's CLSID, the preceding code would look like the following:

// Create an instance of the OLE DB Initialization Component.
CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER,
   IID_IDataInitialize,(void**)&pIDataInitialize);

// Create an instance of CLSID_MSDASQL with supported Services.
pIDataInitialize->CreateDBInstance(CLSID_MSDASQL, NULL,
   CLSCTX_INPROC_SERVER, NULL, IID_IDBInitialize,
   (IUnknown**)&pIDBInitialize);

Alternatively, the consumer could use the Data Links component of OLE DB to prompt the user to indicate the provider to use, as well as other properties to be used to initialize the data source object:

// Create an instance of the Data Links component.
CoCreateInstance(CLSID_DataLinks, NULL, CLSCTX_INPROC_SERVER,
   IID_IDBPromptInitialize, (void **)&pIDBPromptInitialize);

// Initialize output pointer.
*pIDBInitialize = NULL;

// Prompt the user for the provider and initialization properties.
pIDBPromptInitialize->PromptDataSource(NULL, hWndParent,
   DBPROMPTOPTIONS_PROPERTYSHEET, 0, NULL, NULL, IID_IDBInitialize,
   (IUnknown **)&pIDBInitialize);

//Initialize the data source object.
pIDBInitialize->Initialize();

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 object does not exist in an authenticated environment) as well as the name of the data store (file or database) containing the data to be accessed. It also exposes IDBInitialize, which the consumer uses to connect to the provider by initializing the data source object.

After a data source object has been successfully initialized, the consumer can call IDBProperties::GetPropertyInfo on the data source object to get information about the capabilities of the provider. These capabilities include the interfaces, rowset properties such as approximate positioning, transaction properties such as supported isolation levels, and the SQL dialects (if any) a provider supports. The consumer can then call IDBCreateSession::CreateSession to create a session object.

The session acts as a rowset, command, and transaction object factory. Not all OLE DB providers support command objects. However, all data providers, whether or not they support commands, must expose the IOpenRowset interface, which enables providers to expose their data directly as rowsets without the use of commands.


Data Source Object States

When a data source object is created, it is in an uninitialized state. For example, if the underlying data store is a file, the data source object has not yet opened the file; if the data store 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 object:

  • Call QueryInterface for IUnknown, IConnectionPointContainer, IDBAsynchStatus, IDBDataSourceAdmin, IDBInitialize, IDBProperties, IPersist, IPersistFile, or ISupportErrorInfo.
  • Call AddRef or Release on any interface obtained from QueryInterface.
  • Call methods on IConnectionPointContainer, IDBDataSourceAdmin, IDBInitialize, IDBAsynchStatus, IPersist, IPersistFile, or ISupportErrorInfo.
  • Call methods on 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_NOINTERFACE if the consumer calls QueryInterface for an interface other than those listed.

To initialize the data source object, usually the consumer first calls IDBProperties::SetProperties to set the value of properties in the Initialization property group. For example, if the data store is a text file, the consumer might specify the name and location of the file. If the data store is a database in a DBMS, 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 set properties to 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 create and initialize a new data source object by calling IDBDataSourceAdmin::CreateDataSource.


Data Source Object Example

The code in this example shows how to create and initialize a data source object.

/////////////////////////////////////////////////////////////////
// myCreateDataSource
//
// This function creates an OLE DB data source object for a
// provider selected by the user, sets initialization properties
// for the data source, and initializes the data source. The
// function returns a pointer to the data source object's
// IUnknown in *ppUnkDataSource.
//
/////////////////////////////////////////////////////////////////
HRESULT myCreateDataSource
   (
   IUnknown **            ppUnkDataSource
   )
{
   HRESULT                hr;
   IDataInitialize *      pIDataInitialize         = NULL;
   IDBPromptInitialize *  pIDBPromptInitialize     = NULL;
   IDBInitialize *        pIDBInitialize           = NULL;
   CLSID                  clsid                    = CLSID_MSDASQL;

   // Use the Microsoft Data Links UI to create the data source
   // object; this will allow the user to select the provider
   // to connect to and to set the initialization properties
   // for the data source object, which will be created by the
   // Data Links UI.
   if( g_dwFlags & USE_PROMPTDATASOURCE )
   {
      // Create the Data Links UI object, and obtain the
      // IDBPromptInitialize interface from it.
      XCHECK_HR(hr = CoCreateInstance(
                CLSID_DataLinks,                   // clsid--Data Links UI
                NULL,                              // pUnkOuter
                CLSCTX_INPROC_SERVER,              // dwClsContext
                IID_IDBPromptInitialize,           // riid
                (void**)&pIDBPromptInitialize));   // ppvObj

      // Invoke the Data Links UI to allow the user to select
      // the provider and set initialization properties for
      // the data source object that this will create.
      XCHECK_HR(hr = pIDBPromptInitialize->PromptDataSource(
                NULL,                             // pUnkOuter
                GetDesktopWindow(),               // hWndParent
                DBPROMPTOPTIONS_PROPERTYSHEET,    // dwPromptOptions
                0,                                // cSourceTypeFilter
                NULL,                             // rgSourceTypeFilter
                NULL,                             // pwszszzProviderFilter
                IID_IDBInitialize,                // riid
                (IUnknown**)&pIDBInitialize));    // ppDataSource

      // We've obtained a data source object from the Data Links UI. This
      // object has had its initialization properties set, so all we
      // need to do is initialize it.
      XCHECK_HR(hr = pIDBInitialize->Initialize());
   }
   // We are not using the Data Links UI to create the data source
   // object. Instead, we will enumerate the providers installed on this
   // system through the OLE DB enumerator and will allow the user to
   // select the ProgID of the provider for which we will create a
   // data source object.
   else
   {
      // Use the OLE DB enumerator to obtain a rowset of installed
      // providers, and then allow the user to select a provider from
      // this rowset.
      CHECK_HR(hr = myCreateEnumerator(CLSID_OLEDB_ENUMERATOR, &clsid));

      // We will create the data source object through the OLE DB service
      // component IDataInitialize interface, so we need to create an
      // instance of the data initialization object.
      XCHECK_HR(hr = CoCreateInstance(
                CLSID_MSDAINITIALIZE,         // clsid--data initialize
                NULL,                         // pUnkOuter
                CLSCTX_INPROC_SERVER,         // dwClsContext
                IID_IDataInitialize,          // riid
                (void**)&pIDataInitialize));  // ppvObj

      // Use IDataInitialize::CreateDBInstance to create an uninitialized
      // data source object for the chosen provider. By using this
      // service component method, the service component manager can
      // provide additional functionality beyond what is natively
      // supported by the provider if the consumer requests that
      // functionality.
      XCHECK_HR(hr = pIDataInitialize->CreateDBInstance(
                clsid,                           // clsid--provider
                NULL,                            // pUnkOuter
                CLSCTX_INPROC_SERVER,            // dwClsContext
                NULL,                            // pwszReserved
                IID_IDBInitialize,               // riid
                (IUnknown**)&pIDBInitialize));   // ppDataSource

      // Initialize the data source object by setting any required
      // initialization properties and calling IDBInitialize::Initialize.
      CHECK_HR(hr = myDoInitialization(pIDBInitialize));
   }

CLEANUP:
   *ppUnkDataSource = pIDBInitialize;
   if( pIDataInitialize )
      pIDataInitialize->Release();
   if( pIDBPromptInitialize )
      pIDBPromptInitialize->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////
// myDoInitialization
//
// This function sets initialization properties that tell the
// provider to prompt the user for any information required to
// initialize the provider, and then calls the provider's
// initialization function.
//
/////////////////////////////////////////////////////////////////
HRESULT myDoInitialization
   (
   IUnknown *           pIUnknown
   )
{
   HRESULT              hr;
   IDBInitialize *      pIDBInitialize      = NULL;
   IDBProperties *      pIDBProperties      = NULL;
   HWND                 hWnd                = GetDesktopWindow();

   const ULONG          cProperties         = 2;
   DBPROP               rgProperties[cProperties];
   DBPROPSET            rgPropSets[1];

   // To initialize the data source object, most providers require
   // some initialization properties to be set by the consumer. For
   // instance, these might include the data source to connect to and the
   // user ID and password to use to establish identity. We will ask the
   // provider to prompt the user for this required information by
   // setting the following properties:
   myAddProperty(&rgProperties[0],DBPROP_INIT_PROMPT,VT_I2,
                 DBPROMPT_COMPLETE);
   myAddProperty(&rgProperties[1],DBPROP_INIT_HWND,  VT_I4,(LONG)hWnd);

   rgPropSets[0].rgProperties      = rgProperties;
   rgPropSets[0].cProperties      = cProperties;
   rgPropSets[0].guidPropertySet   = DBPROPSET_DBINIT;

   // Obtain the needed interfaces.
   XCHECK_HR(hr = pIUnknown->QueryInterface(IID_IDBProperties,
             (void**)&pIDBProperties));
   XCHECK_HR(hr = pIUnknown->QueryInterface(IID_IDBInitialize,
             (void**)&pIDBInitialize));

   // If a provider requires initialization properties, it must support
   // the properties that we are setting (_PROMPT and _HWND). However,
   // some providers do not need initialization properties and may
   // therefore not support the _PROMPT and _HWND properties. Because of
   // this, we will not check the return value from SetProperties.
   hr = pIDBProperties->SetProperties(1, rgPropSets);

   // Now that we've set our properties, initialize the provider.
   XCHECK_HR(hr = pIDBInitialize->Initialize());

CLEANUP:
   if( pIDBProperties )
      pIDBProperties->Release();
   if( pIDBInitialize )
      pIDBInitialize->Release();
   return hr;
}


Enumerators

An enumerator is an object that searches for available data source objects and other enumerators.

The enumerator object cotype is defined as follows. For more information about cotypes, see "Conceptual Programming Models in OLE DB" in Chapter 1: Overview of OLE DB.

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


Enumerating Data Source Objects and Enumerators

Generic consumers that do not want to use the OLE DB Data Links component to select a data source and set initialization properties can use enumerators to search for data source objects and other enumerators. By using enumerators, rather than searching the registry directly, consumers will continue to work if the registry information changes or if new root enumerators with additional functionality, such as the ability to enumerate data source objects on other machines, become available later. To create an instance of an enumerator, the consumer calls CoCreateInstance with the class ID of the enumerator.

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

Each row in the rowset returned by ISourcesRowset contains a SOURCES_ISPARENT column. If the row describes an enumerator, this column contains the value VARIANT_TRUE if the enumerator 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. If the row describes a data source object, the SOURCES_ISPARENT column should be ignored by the consumer.

A root enumerator is shipped in the MDAC SDK. This enumerator traverses the registry searching for providers and enumerators. For more information about it, see "Root Enumerator Object" in the OLE DB Core Components documentation.

Providers can choose to support their own enumerators, in addition to the root enumerator, although they are not required to do so. Provider-specific enumerators search for data source objects 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 store. The enumerator for this provider might search the DBMS for databases, which the provider could then expose as separate data source objects, one for each database. The rowset returned by the enumerator would include one row for each database it finds, marking that database as a data source object.

Or suppose a provider exposes data that is stored in text files and treats each directory as a separate data store and each text file in that directory as a table. The enumerator for this provider might traverse the file system for text files, which the provider could then expose as separate data source objects, one for each text file. For each directory it finds, the rowset returned by the enumerator would include one row as an enumerator and one or more rows as data source objects. 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 object. 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 18: Programming Considerations.


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 it enumerates directories, the enumerator does not yet know the starting directory. If it 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 for 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 IUnknown::QueryInterface for IUnknown, IDBInitialize, IDBProperties, or ISupportErrorInfo.
  • Call AddRef or Release on any interface obtained from QueryInterface.
  • Call methods on IDBProperties for properties in the Initialization property group.
  • Call methods on IDBInitialize or ISupportErrorInfo.

If the consumer calls any other OLE DB method, the provider returns E_UNEXPECTED. The provider may also return 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 the enumerator should 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 set properties to 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.


Enumerator Example

The code in this example shows how to create and use an enumerator object.

/////////////////////////////////////////////////////////////////
// myCreateEnumerator
//
// This function creates an enumerator, obtains a sources rowset
// from it, displays the rowset to the user, and allows the user
// to specify the ProgID of a provider. The CLSID that matches
// this ProgID is retuned to the caller in *pCLSID.
//
/////////////////////////////////////////////////////////////////
HRESULT myCreateEnumerator
   (
   REFCLSID             clsidEnumerator,
   CLSID *              pCLSID
   )
{
   HRESULT              hr;
   IUnknown *           pIUnkEnumerator               = NULL;
   ISourcesRowset *     pISourcesRowset               = NULL;
   IRowset *            pIRowset                      = NULL;
   IDBInitialize *      pIDBInitialize                = NULL;
   WCHAR                wszProgID[MAX_NAME_LEN + 1]   = {0};

   const ULONG          cProperties                   = 2;
   DBPROP               rgProperties[cProperties];
   DBPROPSET            rgPropSets[1];

   // Create the enumerator object. We ask for IUnknown when creating
   // the enumerator because some enumerators may require initialization
   // before we can obtain a sources rowset from the enumerator. This is
   // indicated by whether or not the enumerator object exposes
   // IDBInitialize. (We don't want to ask for IDBInitialize, since
   // enumerators that don't require initialization will cause the
   // CoCreateInstance to fail.)
   XCHECK_HR(hr = CoCreateInstance(
            clsidEnumerator,            // clsid--enumerator
            NULL,                       // pUnkOuter
            CLSCTX_INPROC_SERVER,       // dwClsContext
            IID_IUnknown,               // riid
            (void**)&pIUnkEnumerator));  // ppvObj

   // If the enumerator exposes IDBInitialize, we need to initialize it.
   if( SUCCEEDED(hr = pIUnkEnumerator->QueryInterface(IID_IDBInitialize,
      (void**)&pIDBInitialize)) )
   {
      CHECK_HR(hr = myDoInitialization(pIUnkEnumerator));
   }

   // Set properties on the rowset, to request additional functionality.
   myAddRowsetProperties(rgPropSets, cProperties, rgProperties);

   // Obtain a sources rowset from the enumerator. This rowset contains
   // all of the OLE DB providers that this enumerator is able to list.
   XCHECK_HR(hr = pIUnkEnumerator->QueryInterface(IID_ISourcesRowset,
             (void**)&pISourcesRowset));
   XCHECK_HR(hr = pISourcesRowset->GetSourcesRowset(
             NULL,                    // pUnkOuter
             IID_IRowset,             // riid
             1,                       // cPropSets
             rgPropSets,              // rgPropSets
             (IUnknown**)&pIRowset));  // ppRowset

   // Display the rowset to the user. This will allow the user to
   // perform basic navigation of the rowset and will allow the user
   // to select a row containing a desired provider.
   CHECK_HR(hr = myDisplayRowset(pIRowset,
            L"SOURCES_NAME", MAX_NAME_LEN, wszProgID));

   // Obtain the ProgID for the provider to use from the user.
   // The default value for this is the value of the SOURCES_NAME
   // column in the row selected by the user previously.
   myGetInputFromUser(wszProgID, L"\nType the ProgID of a provider"
                      L" to use [Enter = `%s`]: ", wszProgID);
   XCHECK_HR(hr = CLSIDFromProgID(wszProgID, pCLSID));

CLEANUP:
   if( pIUnkEnumerator )
      pIUnkEnumerator->Release();
   if( pISourcesRowset )
      pISourcesRowset->Release();
   if( pIRowset )
      pIRowset->Release();
   if( pIDBInitialize )
      pIDBInitialize->Release();
   return hr;
}


Session Objects

Session objects provide methods for creating commands and rowsets and for creating and modifying tables and indexes. They also define transaction scope and can be used to create transaction objects, which are used to control nested transactions.

The session object cotype is defined as follows. For more information about cotypes, see "Conceptual Programming Models in OLE DB" in Chapter 1: Overview of OLE DB.

CoType TSession {
   [mandatory]   interface IGetDataSource;
   [mandatory]   interface IOpenRowset;
   [mandatory]   interface ISessionProperties;
   [optional]    interface IAlterIndex;
   [optional]    interface IAlterTable;
   [optional]    interface IBindResource;
   [optional]    interface ICreateRow;
   [optional]    interface IDBCreateCommand;
   [optional]    interface IDBSchemaRowset;
   [optional]    interface IIndexDefinition;
   [optional]    interface ISupportErrorInfo;
   [optional]    interface ITableCreation;
   [optional]    interface ITableDefinition;
   [optional]    interface ITableDefinitionWithConstraints;
   [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 auto-commit mode, and any work done in the session is automatically committed. It cannot be aborted. For more information about transactions, see Chapter 15: Transactions.

To create a session, a consumer calls IDBCreateSession::CreateSession on 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 object 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 store 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 table definitions.
  • Create a schema rowset by calling IDBSchemaRowset::GetRowset, if the provider supports schema rowsets. For more information about schema rowsets, see Appendix B: Schema Rowsets.


Session Object Example

The code in this example shows how to create a session object.

/////////////////////////////////////////////////////////////////
// myCreateSession
//
// Create an OLE DB session object from the given data source
// object. The IDBCreateSession interface is mandatory, so this
// is a simple operation.
//
/////////////////////////////////////////////////////////////////
HRESULT   myCreateSession
   (
   IUnknown *           pUnkDataSource,
   IUnknown **          ppUnkSession
   )
{
   HRESULT              hr;
   IDBCreateSession *   pIDBCreateSession = NULL;

   //Create a session object from a data source object.
   XCHECK_HR(hr = pUnkDataSource->QueryInterface(
             IID_IDBCreateSession, (void**)&pIDBCreateSession));
   XCHECK_HR(hr = pIDBCreateSession->CreateSession(
             NULL,               // pUnkOuter
             IID_IOpenRowset,    // riid
             ppUnkSession ));    // ppSession

CLEANUP:
   if( pIDBCreateSession )
      pIDBCreateSession->Release();
   return hr;
}


Chapter 3: Commands

This chapter discusses the use and states of command objects, processing multiple result sets, binding input parameters and using output parameters, and the use of rowset properties in command objects. 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, a command returns either a single result, which can be either a rowset object or a row count, or multiple results, which must be returned in a multiple results object.

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 object is used to execute a provider-specific text command, such as an SQL statement. Text commands are expressed in a provider-specific language—usually SQL-92—and are generally used for creating a rowset—for example, executing 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.

The command object cotype is defined as follows. For more information about cotypes, see "Conceptual Programming Models in OLE DB" in Chapter 1: Overview of OLE DB.

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 ICommandPersist;
   [optional]    interface ICommandPrepare;
   [optional]    interface ICommandWithParameters;
   [optional]    interface ISupportErrorInfo;
   [optional]    interface ICommandStream;
}


Using Commands

Before a consumer can use a command, it must determine whether commands are supported by the session. To do this, the consumer calls QueryInterface for IDBCreateCommand on the session object. If this interface is exposed, the provider supports commands. To create a command, the consumer then calls IDBCreateCommand::CreateCommand on the session. A consumer 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 dialect values, or it can be 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 command text is an SQL statement and the provider supports SQL, the provider must recognize and process the command text as an SQL statement, even if the provider's default grammar is not SQL. This is required for maximum interoperability.
DBGUID_SQL | The command text is to be interpreted according to a superset of SQL syntax rules. To determine the level of 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 SQL, or any subset of SQL according to their reported level of support, but must comply with the rules defined in SQL for any supported syntax defined in that specification. Note DBGUID_SQL was added in OLE DB 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. (See above for DBGUID_DEFAULT.)

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.

By calling ICommandProperties::SetProperties before executing a command that creates a rowset, the consumer can request properties that control execution of the command and affect the behavior of the resulting rowset (such as the interfaces to expose). For more information, see "Creating a Rowset with a Command" in Chapter 4: Rowsets. The consumer can also get information about the columns of the rowset by calling IColumnsInfo::GetColumnInfo, which all providers must support, or IColumnsRowset::GetColumnsRowset, which providers may support, on the command.

Before calling GetColumnInfo or GetColumnsRowset, the consumer must set the command text and, if the provider supports command preparation, prepare the command object.

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.

Prepared commands can be 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 following parameterized SQL statement to update the salaries of a number of employees:

UPDATE Employees SET (Salary = ?) WHERE EmployeeID = ?

Support for parameterized commands is optional. Providers that support them must expose ICommandWithParameters on command objects. To use parameters, the consumer first describes the parameters to the provider by calling ICommandWithParameters::SetParameterInfo. Some providers themselves can describe parameters 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 ICommand::Execute. On later calls to Execute, the consumer simply places new parameter values in the buffer and calls ICommand::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 value 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 and the necessary information is not available to the command object) | OK | OK (ICommandPrepare is not supported) - or - NP (ICommandPrepare is supported and the necessary information is not available to the command object)
IColumnsInfo::MapColumnIDs | NC | OK (ICommandPrepare is not supported) - or - NP (ICommandPrepare is supported and the necessary information is not available to the command object) | OK | OK (ICommandPrepare is not supported) - or - NP (ICommandPrepare is supported and the necessary information is not available to the command object)
IColumnsRowset::GetAvailableColumns | NC | OK (ICommandPrepare is not supported) - or - NP (ICommandPrepare is supported and the necessary information is not available to the command object) | OK | OK (ICommandPrepare is not supported) - or - NP (ICommandPrepare is supported and the necessary information is not available to the command object)
IColumnsRowset::GetColumnsRowset | NC | OK (ICommandPrepare is not supported) - or - NP (ICommandPrepare is supported and the necessary information is not available to the command object) | OK | OK (ICommandPrepare is not supported) - or - NP (ICommandPrepare is supported and the necessary information is not available to the command object)
ICommand::Cancel | OK | OK | OK | OK
ICommand::Execute | NC | OK | OK | OK
ICommand::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)
ICommandPrepare::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
ICommandProperties::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
ICommandText::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
ICommandWithParameters::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
ICommandWithParameters::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 figure illustrates the state transition for a command where the command text is set to an empty string and a non-empty string. Any call to ICommandText::SetCommandText with an empty command text must terminate in the Initial state, which is defined as command text not set. The only way to transition from Initial to Unprepared is to call ICommandText::SetCommandText with a non-empty command text. ICommand::Execute or ICommand::Prepare is called to transition to the Execute or Prepared states, respectively. In the Execute or Prepared states, calling ICommandText::SetCommandText with a non-empty command text returns the command to the Unprepared state, where the same call with a non-empty command text returns the command to the Unprepared state by using ICommandPrepare::Unprepare.


Command Object Example

The code in this example shows how to create and execute a command object.

/////////////////////////////////////////////////////////////////
// myCreateCommand
//
// This function takes an IUnknown pointer on a session object
// and attempts to create a command object using the session's
// IDBCreateCommand interface. Since this interface is optional,
// this may fail.
//
/////////////////////////////////////////////////////////////////
HRESULT   myCreateCommand
   (
   IUnknown *           pUnkSession,
   IUnknown **          ppUnkCommand
   )
{
   HRESULT              hr;
   IDBCreateCommand *   pIDBCreateCommand = NULL;

   // Attempt to create a command object from the session object.
   XCHECK_HR(hr = pUnkSession->QueryInterface(
             IID_IDBCreateCommand, (void**)&pIDBCreateCommand));
   XCHECK_HR(hr = pIDBCreateCommand->CreateCommand(
             NULL,             // pUnkOuter
             IID_ICommand,     // riid
             ppUnkCommand));   // ppCommand

CLEANUP:
   if( pIDBCreateCommand )
      pIDBCreateCommand->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////
// myExecuteCommand
//
// This function takes an IUnknown pointer on a command
// object and performs the following steps to create a new
// rowset object:
//  - Sets the given properties on the command object; these
//     properties will be applied by the provider to any rowset
//     created by this command.
//  - Sets the given command text for the command.
//  - Executes the command to create a new rowset object.
//
/////////////////////////////////////////////////////////////////
HRESULT   myExecuteCommand
   (
   IUnknown *             pUnkCommand,
   WCHAR *                pwszCommandText,
   ULONG                  cPropSets,
   DBPROPSET*             rgPropSets,
   IUnknown **            ppUnkRowset
   )
{
   HRESULT                hr;
   ICommandText *         pICommandText          = NULL;
   ICommandProperties *   vpICommandProperties   = NULL;

   // Set the properties on the command object.
   XCHECK_HR(hr = pUnkCommand->QueryInterface(
             IID_ICommandProperties, (void**)&pICommandProperties));
   XCHECK_HR(hr = pICommandProperties->SetProperties(cPropSets,
             rgPropSets));

   // Set the text for this command, using the default command text
   // dialect. All providers that support commands must support this
   // dialect, and providers that support SQL must be able to recognize
   // an SQL command as SQL when this dialect is specified.
   XCHECK_HR(hr = pUnkCommand->QueryInterface(
             IID_ICommandText, (void**)&pICommandText));
   XCHECK_HR(hr = pICommandText->SetCommandText(
             DBGUID_DEFAULT,      // guidDialect
             pwszCommandText));   // pwszCommandText

   // And execute the command. Note that the user could have
   // entered a non-row returning command, so we will check for
   // that and return failure to prevent the display of the
   // nonexistent rowset by the caller.
   XCHECK_HR(hr = pICommandText->Execute(
             NULL,            // pUnkOuter
             IID_IRowset,     // riid
             NULL,            // pParams
             NULL,            // pcRowsAffected
             ppUnkRowset));   // ppRowset

   if( !*ppUnkRowset )
   {
      printf("\nThe command executed successfully, but did not " \
             "return a rowset.\nNo rowset will be displayed.\n");
      hr = E_FAIL;
   }

CLEANUP:
   if( pICommandText )
      pICommandText->Release();
   if( pICommandProperties )
      pICommandProperties->Release();
   return hr;
}


Multiple Results

When executed, commands return results. A result can be a rowset, a row, 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.

The multiple results object cotype is defined as follows. For more information about cotypes, see "Conceptual Programming Models in OLE DB" in Chapter 1: Overview of OLE DB.

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 sets the DBPROP_IMultipleResults property on the command and 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, row, 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, one result per command. If the command did not affect any rows, the returned row count for that result is DB_COUNTUNAVAILABLE. 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.

It is provider-specific whether or not to retrieve row counts. When set, DBPROP_SKIPROWCOUNTRESULTS requires a provider that supports multiple results to either return or skip control row count results on calls to IMultipleResults::GetResult.

Rowsets returned by IMultipleResults::GetResult have the properties set as defined by the command that created the multiple results object. These properties are identical for each result set. There is no way to set different properties for results of a multiple result.


Using Parameters

Parameters are placeholders that can be inserted into a command object. These placeholders are bound to consumer variables at execution time. By using input parameters, consumers can specify a command once and execute it multiple times, each with a different set of values. Parameters are also useful for inserting nonstring values into a command. By using parameters, consumers can execute a command, such as a stored procedure, that contains output arguments.

The provider determines how 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 can legally appear only in certain positions within an SQL statement. They cannot 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 SQL 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 store. This is particularly useful for providers that cannot describe the parameters embedded within a command. 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 creating a parameter accessor and passing a handle to the parameter accessor in its call to ICommand::Execute. For more information about parameter accessors, see "Using Accessors" in Chapter 6: Getting and Setting Data.

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.

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 ICommandWithParameters::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 then calls ICommandWithParameters::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 ICommandWithParameters::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. It must either obtain parameter information from the user or "guess" at the data types to bind each parameter. If the consumer does not need to convert between the bound type and the type of data on the server and does not need 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.


Rowset Properties in Command Objects

When creating a command that returns a rowset, the consumer can specify properties from the rowset property group that must be supported by the rowset. For example, all providers are required to expose the IRowset interface on rowset objects. IRowset enables consumers to, at minimum, navigate forward in the rowset. By requesting the DBPROP_CANSCROLLBACKWARDS rowset property, a consumer can specify that the rowset must enable the consumer to navigate through the rowset in both directions.

Other rowset properties specify interfaces that the rowset must support in addition to the mandatory ones. For example, a consumer might require interfaces to enable direct positioning within a rowset with bookmarks (DBPROP_IRowsetLocate), approximate positioning (DBPROP_IRowsetScroll), updatability of rows (DBPROP_IRowsetChange), and deferred updatability (DBPROP_IRowsetUpdate). Consumers can also specify additional properties that specialize the behavior of certain interfaces—for example, to make some columns of the rowset updatable and the rest read-only.

Note Rowset properties are generally ignored when executing a non–row returning command but may generate errors in some cases. For maximum interoperability, general consumers should make sure that no rowset properties are set when executing non–row returning commands.


Chapter 4: Rowsets

This chapter explains how to create and release rowsets and how to set their properties, such as supported interfaces. Rowsets are the central objects that enable OLE DB components to expose and manipulate data in tabular form. A rowset object is a set of rows in which each row has columns of data. For example, providers present data, as well as metadata, to consumers in the form of rowsets. Query processors present query results in the form of rowsets. The use of rowsets throughout OLE DB makes it possible to aggregate components that consume or produce data through the same object.

Also explained in this chapter are ways to manage the activity of individual rows or rowsets: fetching, reference counting of row handles, and column access and reference. Rowset reference and fetching are discussed in the use of bookmarks, views, and chapters. The consumer uses bookmarks to return quickly to a row. Views can expose simple operations such as sorting or filtering a rowset. Chapters define groups of rows within a rowset. This chapter also discusses hierarchical rowsets, which are rowsets that are linked together by using chapters.

For more information on | Go to
Creating rowsets | "Creating Rowsets" in this chapter
Column access | "Deferred Columns" and "Column IDs" in this chapter
Rows | "Rows and Row Handles" 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 of hierarchical rowsets | "Navigating Hierarchical Rowsets" in this chapter
Lifetime of row handles and chapters | "Lifetime of Row Handles Within Chapters" in this chapter

The rowset object cotype is defined as follows. For more information about cotypes, see "Conceptual Programming Models in OLE DB" in Chapter 1, "Overview of OLE DB."

CoType TRowset {
   [mandatory]   interface IAccessor;
   [mandatory]   interface IColumnsInfo;
   [mandatory]   interface IConvertType;
   [mandatory]   interface IRowset;
   [mandatory]   interface IRowsetInfo;
   [optional]    interface IChapteredRowset;
   [optional]    interface IColumnsInfo2;
   [optional]    interface IColumnsRowset;
   [optional]    interface IConnectionPointContainer;
   [optional]    interface IDBAsynchStatus;
   [optional]    interface IGetRow;
   [optional]    interface IRowsetChange;
   [optional]    interface IRowsetChapterMember;
   [optional]    interface IRowsetCurrentIndex;
   [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;
   [optional]    interface IRowsetBookmark;
}

All rowsets must expose the interfaces described in the following table.

Interface | Use
IRowset | Contains methods for fetching rows, getting row data, and maintaining reference counts on row handles.
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.


Creating Rowsets

A consumer obtains a rowset object in one of the following ways:

  • The consumer calls IOpenRowset::OpenRowset on the current session object to create a rowset directly. All providers must support IOpenRowset.
  • The consumer calls IDBCreateCommand::CreateCommand on the session object to obtain a command object. The consumer then calls ICommand::Execute on the command object. If the command is one that returns rows, such as an SQL SELECT statement, ICommand::Execute returns either a rowset or, if the consumer has requested IMultipleResults, a multiple results object.
  • The consumer invokes a method that returns a rowset or schema rowset, such as IColumnsRowset::GetColumnsRowset, IDBSchemaRowset::GetRowset, IViewRowset::OpenViewRowset, or ISourcesRowset::GetSourcesRowset.
  • The consumer calls some other method that returns tabular results as a rowset.


Creating a Rowset with IOpenRowset

Some providers need to expose very simple rowsets to their consumers. Conceptually, these rowsets represent the result of the simple query SELECT * FROM Tablename. 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 illustration.

Creating a rowset directly with IOpenRowset

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 if DBPROP_CATALOGLOCATION is DBPROPVAL_CL_START or the provider does not support the DBPROP_CATALOGLOCATION property, a consumer follows these steps:

  1. If DBLITERAL_CATALOG_NAME is not NULL and not an empty string (fSupported is TRUE in DBLITERALINFO), get the appropriate catalog name for the table (for example, 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 example, 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.

To construct a fully qualified name if DBPROP_CATALOGLOCATION is DBPROPVAL_CL_END, a consumer follows these steps:

  1. If DBLITERAL_SCHEMA_NAME is not NULL and not an empty string (fSupported is TRUE in DBLITERALINFO), get the appropriate schema name for the table (for example, 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 example, from the TABLES schema rowset).

Note Consumers should provide qualified table names only to reference tables in other schemas or catalogs. Further, consumers should not quote table names unless such quoting is required to make table names unambiguous (such as, to enforce case sensitivity). Providers for which quoting a simple table name has no meaning might not support quoting tables in IOpenRowset::OpenRowset while they do support quoting tables as part of a command text for parsing reasons. Providers should not require table names to be quoted and should guarantee that unquoted table names correctly open the specified table, even if the table name contains special characters, as long as the table can be unambiguously identified without quoting.


Creating a Rowset with a Command

Providers that implement their own query or data definition language (DDL) can offer their consumers additional functionality by using a command.

To implement a query or DDL by using a command, a consumer should follow these steps:

  1. Create a session. (For more information, see "Session Objects" in Chapter 2: Data Source and Session Objects.)
  2. Call IDBCreateCommand::CreateCommand on the session to obtain a command object. When the command is first created, it does not contain command text. Set the command text by calling ICommandText::SetCommandText.

    The text command can be expressed in SQL or some other provider-specific language. For more information about creating command objects, see "Using Commands" in Chapter 3: Commands.

  3. Call ICommandProperties::SetProperties to request properties that control execution of the command and affect the behavior of the resulting rowset (such as the interfaces to expose).
  4. Call ICommand::Execute on the command.

    If the command is one that returns rows, such as an SQL SELECT statement, ICommand::Execute returns either a rowset or, if the consumer has requested IMultipleResults, a multiple results object. See the following illustration.

Creating a rowset through a command

The consumer calls ICommandProperties::SetProperties on the command object to request properties that control execution of the command and affect the behavior of the rowsets returned by ICommand::Execute. The most commonly requested properties are the interfaces the rowset must support.

All rowsets must support IRowset, IAccessor, IColumnsInfo, IRowsetInfo, and IConvertType. Providers can choose to return rowsets exposing additional interfaces if doing so does not break consumer code that is not expecting them. For example, IRowsetLocate can be exposed on the rowset even if the consumer does not request it, because this interface does not change the rowset's behavior. In contrast, IRowsetUpdate cannot safely be exposed if the consumer does not request it, because exposing IRowsetUpdate on the rowset changes the behavior of the rowset's IRowsetChange interface. In most cases, the riid parameter passed to ICommand::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 14: Properties.


Rowset Creation Example

The code in this example shows how to create a rowset.

/////////////////////////////////////////////////////////////////
// myCreateRowset
//
// This function creates an OLE DB rowset object from the given
// provider's session object. It first obtains a default table
// name from the user through the tables schema rowset, if
// supported, and then creates a rowset object by one of two methods:
//
// - If the user requested that the rowset object be created
//    from a command object, it creates a command object and then
//    obtains command text from the user, sets properties and
//    the command text, and finally executes the command to
//    create the rowset object.
// - Otherwise, the function obtains a table name from the user
//    and calls IOpenRowset::OpenRowset to create a rowset object
//    over that table that supports the requested properties.
//
/////////////////////////////////////////////////////////////////
HRESULT   myCreateRowset
   (
   IUnknown *           pUnkSession,
   IUnknown **          ppUnkRowset
   )
{
   HRESULT              hr;
   IUnknown *           pUnkCommand                      = NULL;
   IOpenRowset *        pIOpenRowset                     = NULL;
   WCHAR                wszTableName[MAX_NAME_LEN + 1]   = {0};

   const ULONG          cProperties                      = 2;
   DBPROP               rgProperties[cProperties];
   DBPROPSET            rgPropSets[1];

   // Obtain a default table name from the user by displaying the
   // tables schema rowset if schema rowsets are supported.
   CHECK_HR(hr = myCreateSchemaRowset(DBSCHEMA_TABLES, pUnkSession,
            MAX_NAME_LEN, wszTableName));

   // Set properties on the rowset, to request additional functionality.
   myAddRowsetProperties(rgPropSets, cProperties, rgProperties);

   // If the user requested that the rowset be created from a
   // command object, create a command, set its properties and
   // text, and execute it to create the rowset object.
   if( g_dwFlags & USE_COMMAND )
   {
      WCHAR      wszCommandText[MAX_NAME_LEN + 1];

      // Attempt to create the command object from the provider's
      // session object. Note that commands are not supported by
      // all providers, and this will fail in that case.
      CHECK_HR(hr = myCreateCommand(pUnkSession, &pUnkCommand));

      // Get the command text that we will execute from the user.
      if( !myGetInputFromUser(wszCommandText, L"\nType the command "
         L"to execute [Enter = `select * from %s`]: ", wszTableName) )
      {
         swprintf(wszCommandText, L"select * from %s", wszTableName);
      }

      // And execute the command the user entered.
      CHECK_HR(hr = myExecuteCommand(pUnkCommand, wszCommandText,
               1, rgPropSets, ppUnkRowset));
   }
   // Otherwise, the user gets the default behavior, which is to use
   // IOpenRowset to create the rowset object from the session object.
   // IOpenRowset is supported by all providers; it takes a TableID
   // and creates a rowset containing all rows in that table. It is
   // similar to using SQL command text of "SELECT * FROM TableID".
   else
   {
      DBID         TableID;

      // Create the TableID.
      TableID.eKind            = DBKIND_NAME;
      TableID.uName.pwszName   = wszTableName;

      // Obtain the table name from the user.
      myGetInputFromUser(wszTableName, L"\nType the name of the table "
         L"to use [Enter = `%s`]: ", wszTableName);

      // Get the IOpenRowset interface, and create a rowset object
      // over the requested table through OpenRowset.
      XCHECK_HR(hr = pUnkSession->QueryInterface(
                IID_IOpenRowset, (void**)&pIOpenRowset));
      XCHECK_HR(hr = pIOpenRowset->OpenRowset(
                NULL,            // pUnkOuter
                &TableID,        // pTableID
                NULL,            // pIndexID
                IID_IRowset,     // riid
                1,               // cPropSets
                rgPropSets,      // rgPropSets
                ppUnkRowset));   // ppRowset
   }

CLEANUP:
   if( pIOpenRowset )
      pIOpenRowset->Release();
   if( pUnkCommand )
      pUnkCommand->Release();
   return hr;
}


Rows and Row Handles

Conceptually, a rowset is a set of rows. A row is a set of related data items, or columns, that are treated as a unit. In OLE DB, each row in a rowset object is stored in a separately allocated data buffer that the provider maintains on behalf of the consumer. Thus a rowset's row data, stored in these row buffers, is external to the rowset object, which contains a set of handles to the row buffers. These handles are called row handles.

Row handles are of type HROW, which is defined as follows:

typedef ULONG_PTR HROW;

The consumer can use row handles to manage the activity of individual rows or sets of rows in a rowset. A rowset may have a limit on the number of open row handles it can hold. This limit is imposed by the rowset's DBPROP_MAXOPENROWS property.

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 accesses the data in the row through the functions IRowset::GetData and IRowsetChange::SetData, which use accessor objects to define how the data is to be transferred. For more information, see "Accessors" in Chapter 6: Getting and Setting Data.

By having a local copy of the row, multiple consumers can get data from the row without making additional trips to the data store, which might reside on a separate server. Consumers can also coordinate changes to the row without transmitting them to the data store until all consumers are ready.

However, caching rows in the provider means that it is costly to keep materialized row handles—those defined by creating an instance. Although a particular provider might not enforce a preset limit on the number of active row handles, keeping even a few hundred row handles deteriorates provider performance. Some providers might be expected to handle rowsets with millions of rows, but no provider is expected to materialize many rows simultaneously. Consumers are therefore 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 IRowsetFind::FindNextRow, IRowset::GetNextRows, IRowsetLocate::GetRowsAt, IRowsetLocate::GetRowsByBookmark, and IRowsetScroll::GetRowsAtRatio are used to fetch rows from a rowset. The consumer can use these methods 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 the row data in a buffer local to the provider. The row is usually stored in a native format, although how this is done is provider-specific. The consumer does not have direct access to the provider's data. Instead, the consumer calls IRowset::GetData to fetch row data into its own buffer and IRowsetChange::SetData to make changes to it. For more information, see Chapter 6: Getting and Setting Data.

The rowset's DBPROP_CANHOLDROWS property specifies whether row handles previously obtained from earlier calls to methods such as GetNextRows must be released before the consumer can call IRowset::GetNextRows to retrieve new row handles.


Reference Counting of Row Handles

Row handles are of type HROW and are not COM objects. Nevertheless, each row handle has its own associated reference count. These reference counts are incremented by calling IRowset::AddRefRows and decremented by calling IRowset::ReleaseRows. All methods that fetch rows are required to increment the reference counts on the returned row handles. The provider holds the rows until the consumer releases them with IRowset::ReleaseRows. The consumer must call ReleaseRows on a row once for each time it was fetched and each time AddRefRows was called on it.

Rows held by the provider are freed after the rowset's final reference has been released or after all references on the row handle have been freed. Deleting a row does not release the row handle. It does not matter whether the row is deleted by calling IRowsetUpdate::Update on a row with a pending deletion, calling IRowsetUpdate::Undo on a row with a pending insert, or calling IRowsetChange::DeleteRows: The consumer must call IRowset::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 IRowset::GetNextRows (with no skips, changes of direction, or intervening calls to IRowsetFind::FindNextRow, IRowsetIndex::Seek, or IRowset::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 IRowsetFind::FindNextRow with a NULL pBookmark value. Calling IRowsetIndex::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 IRowset::GetNextRows (or IRowsetFind::FindNextRow with a NULL pBookmark value), 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 GetNextRows (or FindNextRow with a NULL pBookmark value).


Fetch Direction

Methods that return row handles can take a negative count of requested rows. This means that the absolute count of rows is returned, beginning with the first specified row and then moving backward (that is, in decreasing order of row number). For example, the next fetch row in IRowset::GetNextRows or the bookmarked row in IRowsetLocate::GetRowsAt is the specified row, so those preceding it up to the absolute count are 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, the DBPROP_LITERALIDENTITY property determines whether the rowset contains one or multiple copies of the row.

Providers that can detect whether a newly fetched row is already represented by a currently held row handle should 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 can represent the same row in the underlying data store. To the rowset, these generally appear as separate rows. Therefore, changes made through one row handle are not reflected when reading the row through a second row handle. Calling IRowsetRefresh::RefreshVisibleData, IRowsetUpdate::Undo, or IRowsetUpdate::Update affects only 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 store until IRowset::GetData is called for that column. If the column contains a COM object, the provider need not retrieve data 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 IRowset::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::GetColumnInfo is set for the column.

Whether the provider caches the data for a deferred column depends on the setting of the DBCOLUMNFLAGS_CACHEDEFERRED property returned by IColumnsInfo::GetColumnInfo. If this property is set to VARIANT_TRUE, 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. In that case, it is released only when the row handle is released. If the DBCOLUMNFLAGS_CACHEDEFERRED flag is not set, multiple calls to IRowset::GetData can return different values. Otherwise, such calls are not guaranteed to reflect a change to the underlying column unless IRowsetRefresh::RefreshVisibleData has been called.

Whether or not a column is deferred by default is provider-specific and may vary depending on the column. To retrieve a column as deferred or cache deferred, the consumer sets the DBPROP_DEFERRED or DBPROP_CACHEDEFERRED column property on the column, respectively. To determine whether or not a column is deferred, the consumer checks the DBCOLUMNFLAGS_CACHEDEFERRED and DBCOLUMNFLAGS_MAYDEFER bits in the dwFlags element of the DBCOLUMNINFO structure returned by IColumnsInfo::GetColumnInfo or the DBCOLUMN_FLAGS column returned in IColumnsRowset::GetColumnsRowset.

Note The value of DBPROP_DEFERRED is automatically set to VARIANT_TRUE if DBPROP_CACHEDEFERRED is set to VARIANT_TRUE.

Using deferred columns increases isolation risk for consumers because deferred data might be read at a different time than nondeferred data. If the consumer is not using repeatable-read transaction isolation or a higher isolation level, it might encounter discrepancies between the original and the deferred access to a row. In particular, the consumer might find that the column is missing because the row has been deleted, or the column might not match the rest of the row. However, under some circumstances, 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 by examining the DBCOLUMNFLAGS_ISROWVER flag in IColumnsInfo::GetColumnInfo.


Column IDs

Consumers use column IDs primarily when executing commands on data stores for which there are no stable column ordinals. Because column IDs are relatively slow to use on a repeated basis, consumers use column ordinals to identify columns in DBBINDING structures. (For more information about DBBINDING structures, see "DBBINDING Structures" in Chapter 6: Getting and Setting Data.) 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 | Meaning
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.

Consumers should treat table and column names within DBID structures as case-sensitive.

If a provider implements row objects, column IDs must be unique within a row object.

OLE DB providers never return pGuid-style bindings. They are provided as a convenient shortcut for consumers supplying bindings all covered by the same GUID (for example, when creating bindings to access data).

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 schema 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.

It should not be assumed that DBIDs used for row and rowset columns are the same as DBIDs constructed from the COLUMNS schema rowset for base columns. Nor is there currently a property that reports whether a provider's row or rowset DBIDs match the column IDs constructed from base schema information. However, many OLE DB relational providers are implemented so that they are the same. Following are the important scenarios:

  • Consumers should be able to construct DBIDs from the appropriate COLUMN_NAME, COLUMN_GUID, and COLUMN_PROPID columns of the COLUMNS schema rowset. These DBIDs should be usable in schema-modifying methods, such as IAlterTable::AlterColumn and ITableDefinition::DropColumn, that require column IDs.

    For providers that use DBIDs of type DBKIND_NAME, consumers should be able to construct column DBIDs from the DBCOLUMN_BASECOLUMNNAME column of the column metadata rowset returned by IColumnsRowset::GetColumnsRowset.

  • If providers implement row or rowset DBIDs that are the same as the DBIDs constructed in the first scenario, the DBIDs in the DBCOLUMNINFO's columnid element could also be used in schema-modifying methods.
  • If a row or rowset DBID is different from a base column ID as constructed in the first scenario, consumers should not expect DBIDs obtained from IColumnsInfo::GetColumnInfo to work in schema-modifying methods. Providers should match DBIDs based on the DBID's eKind; for example, DBIDs of type DBKIND_GUID_NAME should match both the GUID and name parts.


Bookmarks

Bookmarks are placeholders that enable 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 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.

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.

Bookmark columns have DBCOLUMNFLAGS_ISBOOKMARK set in their column information. A bookmark can be of a fixed scalar type such as DBTYPE_I4, or it can 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 more compact or swifter bookmark schemes 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, column 0 is the bookmark for the rows. Column 0 is present in a rowset only if the rowset has the property DBPROP_BOOKMARKS set to VARIANT_TRUE. Reading this column yields 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. The row also has a nonoverlapping candidate key; the primary key may change entirely, but the same value remains 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 might 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 point to a new row that usurps the old values.


Comparing Bookmarks

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.

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, the provider must expose IRowsetLocate, which has methods to compare two bookmarks and to return a stable hash value for a bookmark.

If the DBPROP_LITERALBOOKMARKS property is VARIANT_TRUE, 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 values, the comparison is of unsigned bytes, with the most significant byte in position 0.

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), the consumer can 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, it calls IRowsetLocate::GetRowsAt for bookmark i and keeps scanning. It is guaranteed to find j unless it is deleted. If it is deleted, the consumer can see the gap in order as it passes 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 standard bookmarks listed in the following table are guaranteed to be the same for all providers.

Value | Meaning
DBBMK_INVALID | DBBMK_INVALID can be used by applications to initialize and subsequently check bookmark validity.
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 -1 and lRowsOffset is zero, IRowsetLocate::GetRowsAt returns this row. If cRows is less than –1, it 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, IRowsetLocate::GetRowsAt returns the last cRows rows of the rowset.

A consumer cannot fetch forward past this point. For example, if cRows is one and lRowsOffset is zero, IRowsetLocate::GetRowsAt returns this row. If cRows is greater than one, it 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 usually returns an error. The provider must never terminate abnormally for a bookmark that it has returned to the user.

However, it is a programming error to pass a random bookmark value that has not been obtained from the provider. The effect of specifying an unknown, random bookmark value is undefined and might include abnormal termination.


Views

By creating a view object, providers that do not support full command syntax can still expose simple operations such as sorting or filtering a rowset. Views can be applied when opening a rowset, or they can be applied to an existing rowset to return either a new chapter on that rowset or a completely new rowset with the additional functionality. OLE DB view objects are not equivalent to SQL-92 views and cannot be used to combine multiple results.

Views are similar to commands, with the following restrictions:

  • The source of the view is a single rowset, 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.

  • The view object cotype is defined as follows. For more information about cotypes, see "Conceptual Programming Models in OLE DB" in Chapter 1: Overview of OLE DB.
   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 either by applying a view when opening a rowset or by applying a view to an existing rowset. A view object may be returned in place of a rowset when executing any rowset-returning method.

  • To apply a view when opening a rowset, the following steps occur:
    1. The consumer requests one of the following properties from the View property group (either directly or by requesting the associated IID) when opening the rowset:

      DBPROP_IViewChapter
      DBPROP_IViewFilter
      DBPROP_IViewRowset
      DBPROP_IViewSort

      When one or more of these properties are set, the consumer must request an interface supported by a view object or the provider will return E_NOINTERFACE. Requesting any other interface when opening the rowset, and not explicitly requesting an interface from the View property group, returns the rowset object, regardless of other properties specified on the rowset.

    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 to return the actual rowset with the view applied.
  • 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 to return a new rowset with the view operations applied.

Providers that expose IViewFilter on the view object must also expose IAccessor and IColumnsInfo on the view object. Consumers should use only accessors created on the view object to get or set filters.

Note To work with ADO 2.1 or later, for views created from existing rowsets (by calling IRowsetView::CreateView), the view object must allow the use of accessors created on the rowset from which the view was created.


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_PTR HCHAPTER;

Because chapters have significant overhead, 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 might 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, the resources needed to manage the chapter can be discarded or marked as eligible for discard. However, 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 through a call to either IRowsetUpdate::Update or IRowsetUpdate::Undo.


Hierarchical Rowsets

A hierarchical rowset is a collection of rowsets linked together by using chapters. Hierarchies model 1:n relationships between tables, such as a master-detail relationship between Customers, Orders, and Items. A hierarchical rowset can be stored persistently in a data source object or computed by a command. This section discusses only computed hierarchies.

Chapters are identifiers of groups of details within a rowset. 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. 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 preceding command contains three rowsets. Each rowset represents a complete hierarchy level in the result. The root rowset—the rowset returned from ICommand::Execute, which we call the Customer rowset—contains columns cust_no, cust_name, and city, as well as a chaptered column named order_rows. The second rowset, which we call the Orders rowset, contains columns ord_no, ord_date, and total, as well as a chaptered column named 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 use GetChildRowset to obtain an aggregated instance of the child rowset. They use 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 IRowsetInfo::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.

IParentRowset::GetChildRowset can be called only once for each child rowset and, if called, must be called before any calls to IRowsetInfo::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 call 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 information, such as accessors and row position, is held when all references to the rowset are released. Furthermore, providers must ensure that the next fetch position within the child rowset is reset after all references to that child rowset have been released. Calling IParentRowset::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 IRowsetInfo::GetReferencedRowset on the Customer rowset with the ordinal of the order_rows column.

The consumer navigates rows inside a chapter by 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 IRowset::GetNextRows on the Customer rowset 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 example, 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 has DBCOLUMNFLAGS_ISCHAPTER set in its column information. Chapter handles are DWORD 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 might lead to a set rather than a single row. The functions that take a chapter as a parameter, such as IRowsetLocate::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 overhead dedicated to chapters. A reference count is taken on the chapter handle each time it is retrieved through IRowsetLocate::GetRowsAt on the parent rowset or by calling IChapteredRowset::AddRefChapter. The reference counts must be explicitly released by 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 a column of a table, that value is not the same thing as the physical instantiation of the chapter. The chapter may be instantiated when a command is executed or on demand when the chapter value is used. In any case, the actual chapter belongs not to the rowset that had the value column but to the rowset within which the chapter is defined. In the hierarchy example shown in the section "Hierarchical Rowsets" earlier in this chapter, 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 on 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 Within Chapters

The lifetime of row handles and the lifetime of 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.

However, some providers keep an implicit reference on the parent row handle until the chapter handle obtained from that row is released. Therefore, if the DBPROP_CANHOLDROWS property is VARIANT_FALSE for the rowset, the consumer cannot obtain additional rows from the parent rowset until all chapters are released for the current set of held row handles. As a standard practice, 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 IChapteredRowset::ReleaseChapter for any retrieved chapters before calling IRowset::GetData in order to retrieve (and thus implicitly take a reference on) another chapter handle. In this case, attempting to call IRowset::GetData on a column containing a chapter handle returns DB_E_CHAPTERNOTRELEASED.


Releasing Rowsets

When the consumer is done with the rowset, it must release it as follows:

  • If the consumer has created any accessors on the rowset, it must release them by calling IAccessor::ReleaseAccessor on each accessor individually.
  • To release the rowset object itself, the consumer must call IUnknown::Release on each interface pointer retrieved on the rowset. When the rowset is released, it forces the release of any remaining row handles the consumer holds. Such handle objects are subordinate to the rowset and cannot cause it to linger beyond the point where all the interfaces for the rowset have been released. The rowset is responsible for cleaning up all such subordinate objects.

Note If the consumer obtained the rowset by executing a command containing output parameters and the provider populates output parameters when the rowset is released (that is, the value of DBPROP_OUTPUTPARAMETERAVAILABILITY is DBPROP_OA_ATROWRELEASE), the consumer must ensure that memory allocated for the output parameters that were bound at ICommand::Execute time must still be valid when the rowset is released. Not doing so is a serious programming error that can cause abnormal termination.


Chapter 5: Updating Data in Rowsets

Consumers can change row data either through commands, such as the UPDATE, DELETE, and INSERT commands in SQL, or through rowsets. This chapter discusses how consumers, by calling methods in IRowsetChange, change row data through rowsets. Two update modes are presented: immediate and deferred. The visibility of changes is discussed, both as it relates to the state of the row and as it relates to 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 | "Conceptual Programming Models in OLE DB" 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. deferred update mode | "Immediate vs. Deferred Update Mode" in this chapter
The state of the row in deferred update mode | "Row States in Deferred Update Mode" 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
Refreshing row values | "Refreshing Row Data" in this chapter


Changing Data

Providers that allow consumers to change rowset data do so by exposing IRowsetChange on the rowset object. Consumers use the methods in IRowsetChange to update the values of columns in existing rows, delete existing rows, or insert new rows. These methods operate on the rows in the rowset. Whether these changes are immediately transmitted to the underlying data store 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 from the rowset to the underlying data store, where they become visible to other transactions running at the Read Uncommitted transaction isolation level. Such changes are called transmitted changes.

If the rowset does expose IRowsetUpdate, it is said to be in deferred update mode. In deferred update mode, the methods in IRowsetChange do not transmit changes to the data store. 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 underlying data store, 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. 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, what happens to those pending changes depends on the DBPROP_COMMITPRESERVE and DBPROP_ABORTPRESERVE properties. For more information, see Committing and Aborting Simple Transactions in Chapter 15: Transactions.

The following illustration shows how the methods in IRowsetChange, IRowsetUpdate, and ITransaction update data.

IRowsetChange, IRowsetUpdate, and ITransaction methods updating data

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


Immediate vs. Deferred Update Mode

Whether the consumer uses immediate or deferred 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 store. Consumers use deferred 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 deferred update mode, consumers can coordinate their changes locally in the rowset before transmitting them to the data store.
  • 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 deferred update mode, the consumer can buffer these changes in the rowset before transmitting them to the data store.
  • Network traffic—If a rowset resides on one node in a network and the data store resides on another node, transmitting changes from the rowset to the data store requires a network call. By using deferred update mode, the consumer can batch 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 deferred update mode, consumers can expose an undo capability to users without having to implement it.


Row States in Deferred Update Mode

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 and their meanings are listed in the following table.

Status value | Row state | Description
DBPENDINGSTATUS_NEW | Pending Insert | The row has a pending insert.
DBPENDINGSTATUS_CHANGED | Pending Update | The row has a deferred change.
DBPENDINGSTATUS_DELETED | Pending Delete | The row has a pending delete. This state is sometimes called soft delete.
DBPENDINGSTATUS_UNCHANGED | Unchanged | No changes have been made to the row, or changes that were made to the row have been transmitted or undone.
DBPENDINGSTATUS_INVALIDROW | Invalid | The row has been deleted and that deletion has been transmitted to the data store. This status is also used when a row handle passed to IRowsetUpdate::GetRowStatus was invalid. This state is sometimes called hard delete.

The Unchanged and Invalid states are the nonpending change states. The Pending Insert, Pending Update, and Pending Delete states are the pending change states. The following illustration 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), and 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 when, for example, 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 rely on this.


Visibility of Changes

If a consumer makes a change to a row in a rowset, releases the row handle for that row, refetches the same row from the data store into the rowset, and calls IRowset::GetData on the row, does the consumer see the change it made? If so, the change is said to be visible to the rowset. If other rowsets also make changes to the same row, those changes may or may not be visible to the first rowset.

Whether a change to a row is visible, and to whom, depends on many factors, such as whether the change is pending or transmitted, whether it affects the row's position in the rowset, and whether it was made inside a transaction. It also depends on the values of various rowset properties.

The following sections discuss these properties and their effects on the visibility of changes of different types:

  • Visibility of Pending Changes
  • Visibility of Transmitted Changes
  • Position of Inserted Rows
  • Visibility of Other Changes
  • Refreshing Row Data


Visibility of Pending Changes

If the consumer completely releases and then refetches a row that has a pending update or delete, the visibility of that pending update or delete depends on the values of the DBPROP_OWNUPDATEDELETE, DBPROP_LITERALIDENTITY, and DBPROP_REMOVEDELETE properties.

If DBPROP_OWNUPDATEDELETE and DBPROP_LITERALIDENTITY are both set to VARIANT_TRUE, pending and transmitted updates and deletes are visible when rows are fetched. If DBPROP_REMOVEDELETE is also set to VARIANT_TRUE, rows with pending or transmitted deletes are not returned when rows are fetched.

If DBPROP_OWNUPDATEDELETE is set to VARIANT_TRUE and DBPROP_LITERALIDENTITY is set to VARIANT_FALSE, only transmitted changes and deletes are visible when rows are fetched. If DBPROP_REMOVEDELETE is set to VARIANT_TRUE, rows with transmitted deletes are not returned when rows are fetched, but rows with pending deletes usually are returned.

If supported, the property DBPROP_CACHEDEFERRED controls when column values are fetched. If DBPROP_CACHEDEFERRED is set to VARIANT_TRUE, changes made via a different row handle are not seen until IRowsetRefresh::RefreshVisibleData is called.

For more information, see "Uniqueness of Rows in the Rowset" in Chapter 4: Rowsets.

Whether methods that fetch rows can retrieve pending insert rows (that is, rows that have been inserted in deferred update mode but for which IRowsetUpdate::Update has not been called) depends on the DBPROP_RETURNPENDINGINSERTS property.

Note Many providers cannot return pending inserts because they use the row-returning mechanisms of the data store. Because pending inserts have not yet been transmitted to the data store, the mechanisms there cannot return them.


Visibility of Transmitted Changes

If a consumer completely releases and then refetches a row for which changes have been transmitted to the data store, 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: OLE DB Properties.


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, 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 a consumer completely releases and then refetches a row handle on a row that has been changed by another rowset or consumer, whether the consumer can see 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 the values of these properties as well as on the transaction isolation level, as shown in the following table.

Property value | Read uncommitted | Read committed | Repeatable read | Serializable
DBPROP_OTHERUPDATEDELETE: 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


Refreshing Row Data

A consumer can refresh the row data in its data buffer by completely releasing its row handles, refetching the rows into the rowset, and retrieving the data from the rowset's copies of the refetched rows. However, this process is somewhat cumbersome, particularly for applications in which multiple consumers are making changes to the same rows in the data store. To address the needs of such consumers, providers can support the IRowsetRefresh interface.

The methods in IRowsetRefresh enable the consumer to refetch rows from the data store into the rowset and to refresh row data in its own data buffer. To update rows in the rowset with data that is currently visible to the rowset in the data store, the consumer calls IRowsetRefresh::RefreshVisibleData. If fOverwrite is set to TRUE in the call to RefreshVisibleData, the provider discards any pending changes to a given row and accepts the visible values from the data store as the current values. If fOverwrite is set to FALSE, pending changes are not discarded. After calling IRowsetRefresh::RefreshVisibleData, the consumer calls IRowsetRefresh::GetLastVisibleData to retrieve the new row data into its data buffer.

Note For deferred columns and for providers that do not cache visible data in the rowset, calling IRowsetRefresh::RefreshVisibleData with fOverwrite set to FALSE has no effect. However, consumers that need to work with general providers should always call IRowsetRefresh::RefreshVisibleData before calling IRowsetRefresh::GetLastVisibleData for any nondeferred columns; thus, they can be sure they get the latest data values from the data store.

IRowsetRefresh::GetLastVisibleData also allows the consumer to fetch row data directly from the data store, bypassing the rowset, when necessary to diagnose and repair collisions that occur under optimistic concurrency control. For more information, see "IRowsetRefresh::GetLastVisibleData" in the reference section.

Although the ability to obtain visible data is influenced by 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 this is often difficult for providers to implement, many rowsets do not expose IRowsetRefresh unless the values of the DBPROP_OWNUPDATEDELETE and DBPROP_OTHERUPDATEDELETE properties are VARIANT_TRUE.


Chapter 6: Getting and Setting Data

This chapter discusses how data is transferred between OLE DB consumers and providers and introduces the three parts of data—value, length of the value, and status of the value. An accessor is a collection of information that describes how data is stored in the consumer's buffer. A special type, the reference accessor, is also discussed in this chapter. Defined another way, an accessor is a collection of bindings, and each binding associates a single column or parameter to the consumer's buffer. DBBINDING structures, memory management, and data transfer complete this chapter's discussion of getting and setting data.

For more information on | Go to
Consumers and providers | "Conceptual Programming Models in OLE DB" in Chapter 1: Overview of OLE DB
OLE DB objects | "Rowset Programming and Object Model" and "Binder Programming and Object Model" in Chapter 1: Overview of OLE DB
Length of a data value | "Length" in this chapter
Status of a data value | "Status" in this chapter
Accessors | "Accessors" in this chapter
Optimized accessors | "Optimized Accessors" in this chapter
Reference accessors | "Reference Accessors" in this chapter
DBBINDING structures | "Bindings" in this chapter
Responsibility for freeing memory | "Responsibility for Freeing Memory" 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 the consumer executes the command. Rowset data is the data returned in a rowset—for example, the rowset generated by executing the command (such as the SELECT * statement in this paragraph) and the key column values passed to an index rowset when setting the index range or searching for a key value. (For more information on index rowsets, see Chapter 10, "Index Rowsets.")

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 in the data store to a column in the consumer's buffer. A group of bindings is gathered together in an accessor. When transferring data, the consumer passes the provider a handle to the accessor and a pointer to the consumer's data buffer. In the provider, how a rowset's row data is handled is provider-specific. For example, row data can be cached in a set of row buffers belonging to the rowset, or it may be passed directly to and from the data store.

The provider 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 ICommand::Execute.


Binding Data Values

In OLE DB, a data value has three parts: value, length (for variable-length data types), and status. The consumer can bind one, two, or all three of these parts. Usually the consumer binds all three. However, there are positive and negative consequences if certain parts are bound.

If the consumer is getting data and does not bind a particular part, the provider does not return that part. Although this is not an error, the result often has a specific use, as illustrated in the table below. For example, if the consumer binds the status and length but not the value, and calls IRowset::GetData, the provider returns the length and status of the data. The consumer could use the length to allocate memory for variable-length data.

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, and calls IRowsetChange::SetData with the status set to DBSTATUS_S_ISNULL, the provider sets the data value to NULL and returns a status of DBSTATUS_S_ISNULL. But, using the same binding, if the consumer calls IRowsetChange::SetData with the status set to DBSTATUS_S_OK, the provider returns an error and sets the status to DBSTATUS_E_UNAVAILABLE, since no data value was bound.

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

Value | Length | Status | Use when getting data | Use when setting data
Y Y Y Most commonly used combination when getting variable-length data.

If the column contains a string value, the consumer should bind DBPART_LENGTH only if it intends to retrieve the string value. If the consumer does bind DBPART_LENGTH to retrieve string data, it should allocate a buffer of at least the size of a null-termination character. Providers generally return DB_E_DATAOVERFLOW when binding DBPART_VALUE for string data with a cbMaxLen value smaller than the size of a null-termination character.

 Most commonly used combination when setting variable-length data.
Y Y N Not generally used.

If the method returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer must assume that the method did not 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 whether the column value is NULL.

 Not generally used.

If the method returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer must assume that the method did not successfully get or set the value for the column.

The provider assumes the status is DBSTATUS_S_OK.
Y N Y Most commonly used combination when getting fixed-length data.[1] Most commonly used combination when setting fixed-length data.

The provider assumes that string data is null-terminated.[2]
Y N N Not generally used.

If the method returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer must assume that it did not 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 whether 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 did not 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.
N Y Y Used to get the length and status of the value (for example, to allocate memory for variable-length data) without actually retrieving the value. Used to specify the length of a long variable-length column prior to passing any data. This is required by providers that must know the number of bytes of BLOB data that will be sent before any data is sent.

If IColumnsInfo::GetColumnInfo does not return DBCOLUMNFLAGS_ISLONG for the column, the provider returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED.
N Y N Not generally used.

If the method returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer must assume that the method did not successfully get or set the length 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 whether the column value is NULL.

 Not generally used.

If the method returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer must assume that the method did not successfully get or set the value for the column.

The provider assumes the status is DBSTATUS_S_OK.
N N Y 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.
N | N | N | IAccessor::CreateAccessor returns DBBINDSTATUS_BADBINDINFO for the binding. | IAccessor::CreateAccessor returns DBBINDSTATUS_BADBINDINFO for the binding.

[1] Consumers should always include the length data part when retrieving variable-length, non-null-terminated data. If the length is not bound, the provider populates the value and status with the correct values but the consumer must rely on some intimate knowledge of the data to derive its length.

[2] Consumers should always include the length data part when specifying non-null values of type DBTYPE_BYTES or DBTYPE_VARNUMERIC. If wType in the DBBINDING structure is DBTYPE_BYTES or DBTYPE_VARNUMERIC, and the status, if bound, is DBSTATUS_S_OK, providers should use the value of cbMaxLen in the DBBINDING structure as the number of bytes to read. However, consumers should not rely on this behavior, because many providers return an error for this case.


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 available for write 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 in the cbMaxLen element of the DBBINDING structure, the string is truncated.

If the type indicator is DBTYPE_BSTR or is combined with DBTYPE_BYREF, DBTYPE_ARRAY, or DBTYPE_VECTOR, 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_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: Data Types.

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 of a data value is its size in bytes as it is stored in the consumer's data buffer. When the consumer gets data, length is the size of the data after it is converted from the provider type to the consumer type. When the consumer sets data, length is the size of the data before it is converted from the consumer type to the provider type.

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," later in this chapter.

Length Values

The value stored in the cbMaxLen element 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, rather than the null-termination character, is used in determining the end of the string. Consumers are encouraged not to use embedded null characters. Nevertheless, providers must support them. If the length is not bound, the string is assumed to be null-terminated.
  • Fixed-length data types—When the consumer gets data, the returned length of a fixed-length data value is the size of its data type. When the consumer sets data, the provider ignores the length of fixed-length data types.
  • DBTYPE_IUNKNOWN, DBTYPE_IDISPATCH—When setting data through ISequentialStream, the length is used to specify the total number of bytes that will be sent through ISequentialStream (which does not include space for a null because data returned through ISequentialStream is not null-terminated). When getting data through ISequentialStream, the length is either sizeof(IUnknown *) or the total number of bytes that will be 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 as DBTYPE_IUNKNOWN or DBTYPE_IDISPATCH to an object other than ISequentialStream.
  • 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—These types are considered fixed-length types. If the type indicator is joined in a logical OR with DBTYPE_ARRAY or DBTYPE_VECTOR, the returned length when getting or setting data is sizeof(SAFEARRAY*) and sizeof(DBVECTOR), respectively.


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. The status is described in the DBSTATUS enumerated type defined in Appendix D: Cotypes, Structures, and Enumerated Types.

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 that the consumer can determine which data values it got successfully. It produces the return codes as follows:

  • Success return code (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 of variable length data 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. If status is bound, the consumer can check the status value to learn why the column failed.
  • 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.

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 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. The consumer should ignore the contents of the value and length parts of the buffer.
DBSTATUS_S_TRUNCATED | Warning | Y | Variable-length data was 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 might cause deferred accessor validation to fail, see the description of the DBBINDSTATUS values listed under the rgStatus argument in the reference entry for "IAccessor::CreateAccessor."
DBSTATUS_E_CANTCONVERTVALUE | Error | N | The data value could not be converted for reasons other than sign mismatch or data overflow. For example, the data was corrupted in the data store 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, 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 the 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 data or to set input parameter data, use ICommand::Execute, IRowsetChange::SetData, IRowsetChange::InsertRow, IRowsetIndex::Seek, and IRowsetIndex::SetRange. When calling these methods, the consumer must set the status to one of the values in the following table.

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 IRowsetChange::SetData or IRowsetChange::InsertRow or 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 sets them on output for input/output parameters. This status value is never returned when getting data. If the provider can determine the default value on output, that value is returned along with a status of DBSTATUS_S_OK if the default is not NULL, or DBSTATUS_S_ISNULL if the default is NULL. If the provider cannot determine the default value on output, it returns DBSTATUS_E_UNAVAILABLE when calling IRowset::GetData for columns set as DBSTATUS_S_DEFAULT. Consumers should be prepared for OLE DB 1.0 or 1.1 providers to return DBSTATUS_E_BADSTATUS when attempting to specify DBSTATUS_S_DEFAULT in IRowsetChange::SetData or in IRowsetChange::InsertRow or for providers that cannot set a column to its default value.
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 parameter values. Consumers should be prepared for OLE DB 1.x providers to return DBSTATUS_E_BADSTATUS when attempting to specify DBSTATUS_S_IGNORE in IRowsetChange::SetData or in IRowsetChange::InsertRow.

Note In IRowsetChange::InsertRow, specifying DBSTATUS_S_IGNORE for a column is equivalent to not including that column in the accessor. For most providers, this is equivalent to specifying DBSTATUS_S_DEFAULT.

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. The provider produces return codes as follows:

  • Success return code (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 of variable-length data is an error condition when setting data. The corresponding status is set to DBSTATUS_E_CANTCONVERTVALUE or DBSTATUS_E_OVERFLOW as appropriate.

  • 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.

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_S_IGNORE | Success | N | The consumer specified DBSTATUS_S_IGNORE when setting data, and the provider ignored the column.
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 DBBINDSTATUS values listed under the rgStatus argument of in the reference entry for "IAccessor::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, the binding was not passed as an input or input/output parameter in a procedure, and the provider was not able to determine the default value for the column.
  • The status value was DBSTATUS_S_DEFAULT, the binding was used in a method other than IRowsetChange::SetData or IRowsetChange::InsertData, and the binding was not passed as 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 1.1 provider.
  • The binding used in IRowsetChange::SetData did not contain the value part, and status on input is not DBSTATUS_S_ISNULL, DBSTATUS_S_IGNORE, DBSTATUS_S_DEFAULT nor DBSTATUS_S_OK.
  • 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_S_IGNORE, and the provider was a 1.x provider.
DBSTATUS_E_CANTCONVERTVALUE | Error | N | The data value could not 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 | 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's constraint 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 One of the following conditions:
  • The consumer did not bind the value part, IColumnsInfo::GetColumnInfo does not return DBCOLUMNFLAGS_ISLONG for the column, and the status was DBSTATUS_S_OK.
  • 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.
  • 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.


Accessors

An accessor is a data structure created by the consumer that describes how row or parameter data from the data store is to be laid out in the consumer's data buffer. For each column in a row (or parameter in a set of parameters), the accessor contains a binding. A binding is a data structure that holds information about a column or parameter value, such as its ordinal value, data type, and destination in the consumer's buffer. When calling a method that transfers data, such as IRowset::GetData, IRowsetChange::SetData, or ICommand::Execute, the consumer passes as parameters to the method an accessor handle (or, when transferring parameter data, a DBPARAMS structure containing an accessor handle) and a pointer into the consumer's data buffer. The provider uses the accessor to determine how to transfer the data to or from the consumer's buffer.

An accessor is identified by its handle, as follows:

typedef ULONG_PTR HACCESSOR;


Using Accessors

Accessors are created by calling IAccessor::CreateAccessor, reference-counted by calling IAccessor::AddRefAccessor, and released by calling IAccessor::ReleaseAccessor. They can be created or released at any time. An accessor can be a row accessor (used for accessing rowset data), a parameter accessor (used for accessing parameter data), or both.

Row accessors

Row accessors can be created on command and rowset objects, but they can be used only on rowset objects. These accessors describe the bindings to columns in the rowset and, if created on a command object, are inherited by any rowsets created by that command object. Row accessors created on a rowset after a row is fetched from that rowset can still be used against that row.

Consumers can create and use more than one accessor at a time on any row. Therefore, 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 IRowset::GetData to get the picture.

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 (except DBACCESSOR_PARAMETERDATA), and handle of each accessor are the same on both the rowset and the command. After the rowset is opened, it gets its own independent copy of the accessors. Calling IAccessor methods on the command object has no effect on the accessors available on any existing rowsets, and calling IAccessor methods 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. To create a row accessor on a command, the consumer must know the column ordinals.

Parameter accessors

Parameter accessors can be created and used only on command objects. They are used for accessing parameter data that describes bindings to parameters in the command text.

Combined row/parameter accessors

Combined row/parameter accessors can be created on command objects by setting both the DBACCESSOR_ROWDATA and the DBACCESSOR_PARAMETERDATA flags. Combined row/parameter accessors can also be inherited and used on a rowset. When used on a rowset, the DBACCESSOR_PARAMETERDATA flag is irrelevant. Therefore, it is provider-specific whether or not this flag is set.


Optimized Accessors

Row accessors can be optimized. How this is done is provider-specific. It is usually faster to use an optimized accessor than an unoptimized 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.

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, clients should use a data type coercible from the type specified in any optimized accessor for that column.


Reference Accessors

A reference accessor enables consumers to get rowset data directly from the provider's buffer and enables providers to get input parameter data directly from the consumer's buffer. Consumers 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 through OLE DB interfaces 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.

To use reference accessors, the layout of the consumer's bindings must exactly match the layout of the provider's buffer. If the provider's buffer does not contain length or status parts, the consumer cannot bind these parts and, in the same manner as the provider, must determine length and whether the data value is NULL.

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.

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 returned by the provider 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 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 "Binding Data Values," earlier in this chapter, and "DBBINDING Structures," later in this chapter.

Each binding associates, or binds, a single column or parameter to the consumer's buffer. The consumer can bind columns and output parameters more than once in a single accessor but can bind 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, because the result of doing so is unpredictable.

For example, the following illustration shows a consumer's buffer containing 10 bytes for a string, 2 unused bytes to properly align the string's status and length values, 4 bytes for the string's status, 4 bytes for the string's length, 2 unused bytes to properly align the integer's status value, 2 bytes for a 2-byte integer, and 4 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. The table following the illustration summarizes the bindings in this example.

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 the consumer gets data, the provider uses the information in each binding to determine where and how to return data in the consumer's buffer. When the consumer sets data, the provider uses the information in each binding to determine where and how to retrieve data from 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 {
   DBORDINAL      iOrdinal;
   DBBYTEOFFSET   obValue;
   DBBYTEOFFSET   obLength;
   DBBYTEOFFSET   obStatus;
   ITypeInfo     *pTypeInfo;
   DBOBJECT      *pObject;
   DBBINDEXT     *pBindExt;
   DBPART         dwPart;
   DBMEMOWNER     dwMemOwner;
   DBPARAMIO      eParamIO;
   DBLENGTH       cbMaxLen;
   DWORD          dwFlags;
   DBTYPE         wType;
   BYTE           bPrecision;
   BYTE           bScale;
} DBBINDING;

The elements of this structure are used as described in the following table.

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 have values greater than or equal to 1. Rowset column ordinals start at 1 and are numbered sequentially in increasing order (for example, 1, 2, 3, and so on). When a row object is derived from a rowset, the row-specific column ordinals must come after the rowset column ordinals.

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.

To retrieve parameter ordinals, consumers call ICommandWithParameters::GetParameterInfo on the command object.
obValue The offset in bytes in the consumer's buffer to the value part. The value of 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 LONG aligned.
obLength The offset in bytes in the consumer's buffer to the length part. The value of 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 DBLENGTH. This offset must be properly aligned for the processor architecture of the consumer's machine. That is, it should be a multiple of sizeof(DBLENGTH). 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. The value of 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. For columns or parameters that can hold BLOBs or COM objects, this structure gives information needed to access the object:
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 COM object.
  • iid—The interface to be exposed on the COM object.

The storage mode flags apply only to IRowset::GetData; IRowsetChange::SetData ignores dwFlags.

The storage mode flags are defined by the Structured Storage model in COM. They are as follows:

STGM_READ
Requests a storage object from the provider that can be used only to read the current contents of the column.
STGM_WRITE
Requests a writable storage object from the provider that contains the current contents of the column. The consumer can use this storage object to modify or append to the current contents of the column.
STGM_READWRITE
Combination of STGM_READ and STGM_WRITE.
STGM_SHARE_DENY_NONE
Requests others subsequently opening the object not be denied read or write access.
STGM_SHARE_DENY_READ
Requests that others subsequently opening the object be denied read access.
STGM_SHARE_DENY_WRITE
Requests that others subsequently opening the object be denied write access.
STGM_SHARE_EXCLUSIVE
Combination of STGM_SHARE_DENY_READ and STGM_SHARE_DENY_WRITE.
STGM_DIRECT
Requests a direct (nontransacted) storage object over the column.
STGM_TRANSACTED
Requests a transacted storage object over the column.
STGM_CREATE
Requests a new storage object with which to write data to the column. The current contents of the column are replaced by the new storage object.
STGM_CONVERT
(Generally not used in OLE DB.)
STGM_FAILIFTHERE
Requests for a storage object should fail if the column already contains data.
STGM_PRIORITY
Opens the storage object with exclusive access to the most recently committed version. Other users cannot commit changes to the object while it is open in this mode. STGM_DIRECT and STGM_READ must be specified with this mode.
STGM_DELETEONRELEASE
(Generally not used in OLE DB.)

To open a new, empty stream over the contents of the column, specify the STGM_CREATE flag. When the new stream is opened, the seek pointer is positioned at the beginning of the stream.

Note If STGM_CREATE or STGM_WRITE is specified when opening a stream over a null column, the provider always returns an empty stream, as opposed to returning a null pointer or setting the DBSTATUS_S_ISNULL flag.

The value of pObject is ignored unless the DBPART_VALUE bit is set in dwPart and wType is DBTYPE_IUNKNOWN. If neither of these conditions is true, pObject should be set to a null pointer.

A null value of pObject implies that both of the following statements are true:

  • pObject->iid is IID_IUnknown
  • pObject->dwFlags is at least STGM_READ

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. If wType is DBTYPE_IUNKNOWN, the DBPART_VALUE bit in dwPart is set, and pObject is NULL, providers may assume when setting data or inserting rows that the consumer buffer contains a pointer to an object that supports at least IUnknown. Providers can then call IUnknown::QueryInterface for any interfaces they are interested in. If the call to QueryInterface succeeds, the provider uses the returned interface pointers to set the data for this column. If the object does not support one of these interfaces, the provider should return DBSTATUS_E_CANTCREATE.
pBindExt The DBBINDEXT structure can be used for future extensions to the binding structure. The pBindExt value is reserved for future use, and consumers should set it to a null pointer:
typedef struct tagDBBINDEXT{
   BYTE         *pExtension;
   DBCOUNTITEM   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 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. The dwMemOwner value specifies whether the consumer or provider is responsible for freeing this memory. The provider ignores this when the consumer sets 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, the rowset is freed, or a method is called for the row that might invalidate the pointer. The methods that might invalidate the pointer depend on the method that returned it, as follows:

    Pointer returned by Might be invalidated by

    GetData DeleteRows[1]
    RefreshVisibleData
    SetData
    Update
    Undo

    GetLastVisibleData GetLastVisibleData

    GetOriginalData RefreshVisibleData
    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 IRowset::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 cases either consumer-owned or provider-owned memory can be used. However, the provider might not support using provider-owned memory to retrieve columns for which IColumnsInfo::GetColumnInfo returns DBCOLUMNFLAGS_ISLONG for the column. Consumers can mix bindings for provider-owned and consumer-owned memory in the same accessor.

For bindings in parameter accessors, consumer-owned memory must always be used.

The value of 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 store. The value of 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.
  • DBPARAMIO_INPUT | DBPARAMIO_OUTPUT—The parameter is both an input and an output parameter.

For row accessors, the value of 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. The value of cbMaxLen is ignored unless the DBPART_VALUE bit is set in dwPart and the data value stored at that location is variable-length. For information about variable-length data types, see "Fixed-Length and Variable-Length Data Types" in Chapter 13: Data Types in OLE DB.

The value of 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 string 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).

    Important 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 The consumer can set dwFlags to specify information about how returned data is to be formatted. 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.

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: Data Types.

This type forms an implied conversion between the buffer type and the type of the column or parameter. For information about conversions that providers are required to support, see "Data Type Conversion" in Chapter 13: Data Types in OLE DB.
bPrecision | The maximum precision to use when getting data and wType is DBTYPE_NUMERIC or DBTYPE_VARNUMERIC. This is ignored in three cases: when setting data; when wType is not DBTYPE_NUMERIC or DBTYPE_VARNUMERIC; and when the DBPART_VALUE bit is not set in dwPart. For more information, see "Conversions Involving DBTYPE_NUMERIC or DBTYPE_DECIMAL" in Appendix A: Data Types.
bScale | The scale to use when getting data and wType is DBTYPE_NUMERIC, DBTYPE_VARNUMERIC, or DBTYPE_DECIMAL. This is ignored in three cases: when setting data; when wType is not DBTYPE_NUMERIC, DBTYPE_VARNUMERIC, or DBTYPE_DECIMAL; and when the DBPART_VALUE bit is not set in dwPart. For more information, see "Conversions Involving DBTYPE_NUMERIC or DBTYPE_DECIMAL" in Appendix A: Data Types.

[1] Immediate update mode.

Note The offsets specified in obValue, obLength, and obStatus must not point to overlapping areas of memory. The provider is not required to check for the occurrence of this condition. Consumers must be careful to avoid this condition when constructing bindings.


Binding and Accessor Example

The code in this example shows how to set up bindings and use them to create an accessor.

/////////////////////////////////////////////////////////////////
// mySetupBindings
//
// This function takes an IUnknown pointer from a rowset object
// and creates a bindings array that describes how we want the
// data we fetch from the rowset to be laid out in memory. It
// also calculates the total size of a row so that we can use
// this to allocate memory for the rows that we will fetch
// later.
//
// For each column in the rowset, there will be a corresponding
// element in the bindings array that describes how the
// provider should transfer the data, including length and
// status, for that column. This element also specifies the data
// type that the provider should return the column as. We will
// bind all columns as DBTYPE_WSTR, with a few exceptions
// detailed below, as providers are required to support the
// conversion of their column data to this type in the vast
// majority of cases. The exception to our binding as
// DBTYPE_WSTR is if the native column data type is
// DBTYPE_IUNKNOWN or if the user has requested that BLOB
// columns be bound as ISequentialStream objects, in which case
// we will bind those columns as ISequentialStream objects.
//
/////////////////////////////////////////////////////////////////
HRESULT mySetupBindings
   (
   IUnknown *           pUnkRowset,
   ULONG *              pcBindings,
   DBBINDING **         prgBindings,
   ULONG *              pcbRowSize
   )
{
   HRESULT              hr;
   ULONG                cColumns;
   DBCOLUMNINFO *       rgColumnInfo      = NULL;
   LPWSTR               pStringBuffer     = NULL;
   IColumnsInfo *       pIColumnsInfo     = NULL;

   ULONG                iCol;
   ULONG                dwOffset          = 0;
   DBBINDING *          rgBindings        = NULL;

   ULONG                cStorageObjs      = 0;
   BOOL                 fMultipleObjs     = FALSE;

   // Obtain the column information for the rowset; from this, we can
   // find out the following information that we need to construct the
   // bindings array:
   //  - the number of columns
   //  - the ordinal of each column
   //  - the precision and scale of numeric columns
   //  - the OLE DB data type of the column
   XCHECK_HR(hr = pUnkRowset->QueryInterface(
             IID_IColumnsInfo, (void**)&pIColumnsInfo));
   XCHECK_HR(hr = pIColumnsInfo->GetColumnInfo(
             &cColumns,          // pcColumns
             &rgColumnInfo,      // prgColumnInfo
             &pStringBuffer));   // ppStringBuffer

   // Allocate memory for the bindings array; there is a one-to-one
   // mapping between the columns returned from GetColumnInfo and our
   // bindings.
   rgBindings = (DBBINDING*)CoTaskMemAlloc(cColumns * sizeof(DBBINDING));
   CHECK_MEMORY(hr, rgBindings);
   memset(rgBindings, 0, cColumns * sizeof(DBBINDING));

   // Determine whether the rowset supports multiple storage object
   // bindings. If it does not, we will bind only the first BLOB column
   // or IUnknown column as an ISequentialStream object, and will bind
   // the rest as DBTYPE_WSTR.
   myGetProperty(pUnkRowset, IID_IRowset, DBPROP_MULTIPLESTORAGEOBJECTS,
                 DBPROPSET_ROWSET, &fMultipleObjs);

   // Construct the binding array element for each column.
   for( iCol = 0; iCol < cColumns; iCol++ )
   {
      // This binding applies to the ordinal of this column.
      rgBindings[iCol].iOrdinal   = rgColumnInfo[iCol].iOrdinal;

      // We are asking the provider to give us the data for this column
      // (DBPART_VALUE), the length of that data (DBPART_LENGTH), and
      // the status of the column (DBPART_STATUS).
      rgBindings[iCol].dwPart = DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS;

      // The following values are the offsets to the status, length, and
      // data value that the provider will fill with the appropriate
      // values when we fetch data later. When we fetch data, we will
      // pass a pointer to a buffer that the provider will copy column
      // data to, in accordance with the binding we have provided for
      // that column;these are offsets into that future buffer.
      rgBindings[iCol].obStatus   = dwOffset;
      rgBindings[iCol].obLength   = dwOffset + sizeof(DBSTATUS);
      rgBindings[iCol].obValue    = dwOffset + sizeof(DBSTATUS) +
                                     sizeof(ULONG);

      // Any memory allocated for the data value will be owned by us, the
      // client. Note that no data will be allocated in this case, as the
      // DBTYPE_WSTR bindings we are using will tell the provider to
      // simply copy data directly into our provided buffer.
      rgBindings[iCol].dwMemOwner = DBMEMOWNER_CLIENTOWNED;

      // This is not a parameter binding.
      rgBindings[iCol].eParamIO = DBPARAMIO_NOTPARAM;

      // We want to use the precision and scale of the column.
      rgBindings[iCol].bPrecision = rgColumnInfo[iCol].bPrecision;
      rgBindings[iCol].bScale = rgColumnInfo[iCol].bScale;

      // Bind this column as DBTYPE_WSTR, which tells the provider to
      // copy a Unicode string representation of the data into our
      // buffer, converting from the native type if necessary.
      rgBindings[iCol].wType = DBTYPE_WSTR;

      // Initially, we set the length for this data in our buffer to 0;
      // the correct value for this will be calculated directly below.
      rgBindings[iCol].cbMaxLen   = 0;

      // Determine the maximum number of bytes required in our buffer to
      // contain the Unicode string representation of the provider's
      // native data type, including room for the NULL-termination.
      character switch( rgColumnInfo[iCol].wType )
      {
         case DBTYPE_NULL:
         case DBTYPE_EMPTY:
         case DBTYPE_I1:
         case DBTYPE_I2:
         case DBTYPE_I4:
         case DBTYPE_UI1:
         case DBTYPE_UI2:
         case DBTYPE_UI4:
         case DBTYPE_R4:
         case DBTYPE_BOOL:
         case DBTYPE_I8:
         case DBTYPE_UI8:
         case DBTYPE_R8:
         case DBTYPE_CY:
         case DBTYPE_ERROR:
            // When the above types are converted to a string, they
            // will all fit into 25 characters, so use that plus space
            // for the NULL-terminator.
            rgBindings[iCol].cbMaxLen = (25 + 1) * sizeof(WCHAR);
            break;

         case DBTYPE_DECIMAL:
         case DBTYPE_NUMERIC:
         case DBTYPE_DATE:
         case DBTYPE_DBDATE:
         case DBTYPE_DBTIMESTAMP:
         case DBTYPE_GUID:
            // Converted to a string, the above types will all fit into
            // 50 characters, so use that plus space for the terminator.
            rgBindings[iCol].cbMaxLen = (50 + 1) * sizeof(WCHAR);
            break;

         case DBTYPE_BYTES:
            // In converting DBTYPE_BYTES to a string, each byte
            // becomes two characters (e.g. 0xFF -> "FF"), so we
            // will use double the maximum size of the column plus
            // include space for the NULL-terminator.
            rgBindings[iCol].cbMaxLen =
               (rgColumnInfo[iCol].ulColumnSize * 2 + 1) * sizeof(WCHAR);
            break;

         case DBTYPE_STR:
         case DBTYPE_WSTR:
         case DBTYPE_BSTR:
            // Going from a string to our string representation,
            // we can just take the maximum size of the column,
            // a count of characters, and include space for the
            // terminator, which is not included in the column size.
            rgBindings[iCol].cbMaxLen =
               (rgColumnInfo[iCol].ulColumnSize + 1) * sizeof(WCHAR);
            break;

         default:
            // For any other type, we will simply use our maximum
            // column buffer size, since the display size of these
            // columns may be variable (e.g. DBTYPE_VARIANT) or
            // unknown (e.g. provider-specific types).
            rgBindings[iCol].cbMaxLen = MAX_COL_SIZE;
            break;
      };

      // If the provider's native data type for this column is
      // DBTYPE_IUNKNOWN or this is a BLOB column and the user
      // has requested that we bind BLOB columns as ISequentialStream
      // objects, bind this column as an ISequentialStream object if
      // the provider supports our creating another ISequentialStream
      // binding.
      if( (rgColumnInfo[iCol].wType == DBTYPE_IUNKNOWN ||
          ((rgColumnInfo[iCol].dwFlags & DBCOLUMNFLAGS_ISLONG) &&
          (g_dwFlags & USE_ISEQSTREAM))) &&
          (fMultipleObjs || !cStorageObjs) )
      {
         // To create an ISequentialStream object, we will
         // bind this column as DBTYPE_IUNKNOWN to indicate
         // that we are requesting this column as an object.
         rgBindings[iCol].wType = DBTYPE_IUNKNOWN;

         // We want to allocate enough space in our buffer for
         // the ISequentialStream pointer we will obtain from
         // the provider.
         rgBindings[iCol].cbMaxLen = sizeof(ISequentialStream *);

         // To specify the type of object that we want from the
         // provider, we need to create a DBOBJECT structure and
         // place it in our binding for this column.
         rgBindings[iCol].pObject =
                        (DBOBJECT *)CoTaskMemAlloc(sizeof(DBOBJECT));
         CHECK_MEMORY(hr, rgBindings[iCol].pObject);

         // Direct the provider to create an ISequentialStream
         // object over the data for this column.
         rgBindings[iCol].pObject->iid = IID_ISequentialStream;

         // We want read access on the ISequentialStream
         // object that the provider will create for us.
         rgBindings[iCol].pObject->dwFlags = STGM_READ;

         // Keep track of the number of storage objects
         // (ISequentialStream is a storage interface) that we have
         // requested, so that we can avoid requesting multiple storage
         // objects from a provider that supports only a single storage
         // object in our bindings.
         cStorageObjs++;
      }

      // Ensure that the bound maximum length is no more than the
      // maximum column size in bytes that we've defined.
      rgBindings[iCol].cbMaxLen
         = min(rgBindings[iCol].cbMaxLen, MAX_COL_SIZE);

      // Update the offset past the end of this column's data so
      // that the next column will begin in the correct place in
      // the buffer.
      dwOffset = rgBindings[iCol].cbMaxLen + rgBindings[iCol].obValue;

      // Ensure that the data for the next column will be correctly
      // aligned for all platforms or, if we're done with columns,
      // that if we allocate space for multiple rows that the data
      // for every row is correctly aligned.
      dwOffset = ROUNDUP(dwOffset);
   }

   // Return the row size (the current dwOffset is the size of the row),
   // the count of bindings, and the bindings array to the caller.
   *pcbRowSize    = dwOffset;
   *pcBindings    = cColumns;
   *prgBindings   = rgBindings;

CLEANUP:
   CoTaskMemFree(rgColumnInfo);
   CoTaskMemFree(pStringBuffer);
   if( pIColumnsInfo )
      pIColumnsInfo->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////
// myCreateAccessor
//
// This function takes an IUnknown pointer for a rowset object
// and creates an accessor that describes the layout of the
// buffer we will use when we fetch data. The provider will fill
// this buffer according to the description contained in the
// accessor that we will create here.
//
/////////////////////////////////////////////////////////////////
HRESULT myCreateAccessor
   (
   IUnknown *           pUnkRowset,
   HACCESSOR *          phAccessor,
   ULONG *              pcBindings,
   DBBINDING **         prgBindings,
   ULONG *              pcbRowSize
   )
{
   HRESULT              hr;
   IAccessor *          pIAccessor = NULL;

   // An accessor is basically a handle to a collection of bindings.
   // To create the accessor, we need to first create an array of
   // bindings for the columns in the rowset.
   CHECK_HR(hr = mySetupBindings(pUnkRowset, pcBindings, prgBindings,
            pcbRowSize));

   // Now that we have an array of bindings, tell the provider to
   // create the accessor for those bindings. We get back a handle
   // to this accessor, which we will use when fetching data.
   XCHECK_HR(hr = pUnkRowset->QueryInterface(
             IID_IAccessor, (void**)&pIAccessor));
   XCHECK_HR(hr = pIAccessor->CreateAccessor(
             DBACCESSOR_ROWDATA,   // dwAccessorFlags
             *pcBindings,          // cBindings
             *prgBindings,         // rgBindings
             0,                    // cbRowSize
             phAccessor,           // phAccessor
             NULL));               // rgStatus

CLEANUP:
   if( pIAccessor )
      pIAccessor->Release();
   return hr;
}


Responsibility for Freeing Memory

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 a consumer-owned data buffer. The consumer is responsible for allocating and freeing memory for this buffer. 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 a separately allocated data buffer. This separate data buffer 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 an ANSI string. If the type indicator is DBTYPE_BSTR, the data value is a BSTR, which is a pointer to a length-prefixed, null-terminated string of Unicode characters. The remainder of this section discusses how this separate data buffer is managed.

Note A consumer can 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. If the consumer does this, and when the provider returns the output value for the parameter, the provider 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 getting data, the provider is responsible for allocating the separate data buffer. 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 in the following list. 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 by calling COM string manipulation functions such as SysAllocString and SysFreeString. For more information, see "Overview of Data Types" in Appendix A: Data Types.
  • DBTYPE_BYREF—Memory is allocated and freed by calling the COM task allocator's IMalloc methods.
  • DBTYPE_ARRAY—SAFEARRAY structures are allocated and freed by calling COM array manipulation functions such as SafeArrayCreate and SafeArrayDestroy. For more information, see "Overview of Data Types" in Appendix A: Data Types.
  • DBTYPE_VECTOR—Memory is allocated and freed by calling the COM task allocator's IMalloc methods.

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 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 includes two columns that contain ANSI strings with a maximum length of 32,000 characters but only one column 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—for example, by using IRowset::GetData for rowset data or ICommand::Execute for output parameter data—the consumer must perform the following actions:

  1. Create an accessor to bind the columns or output parameters.
  2. Allocate a buffer to hold the data returned by the provider.
  3. Call 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. Validate 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. Check whether the data is NULL.

    If it is, either the provider sets the status to DBSTATUS_S_ISNULL and ignores the value and length or the provider sets the status to DBSTATUS_S_OK and sets the value to VT_EMPTY. The provider then proceeds to the next column or output parameter. The address where 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. Convert 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: Data Types.

  4. Set the length.

    The address where the length value is placed is calculated from the buffer address passed to the method and the obLength element of the binding as follows:

    • Variable-length data types—The length of the untruncated data in bytes, not counting the null-termination character for strings.
    • Fixed-length data types—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. If wType is DBTYPE_BSTR, the length is sizeof(BSTR), not the length of the string itself.
    • 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 sizeof(SAFEARRAY*) and sizeof(DBVECTOR), respectively.
  5. Set the data value to the converted data.

    The address where the data value is stored is calculated from the buffer address passed to the method and the obValue element of the binding as follows:

    • 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. For more information, see "String Data" in Chapter 13: Data Types in OLE DB.
    • 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 COM object, not the object itself. For more information, see Chapter 7: BLOBs and COM Objects.

      If the type indicator is DBTYPE_BSTR, the provider places the converted data in a BSTR and places 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 "Responsibility for Freeing Memory," 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 structure 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 "Responsibility for Freeing Memory," earlier in this chapter.
  6. Set 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, if one or more errors have occurred, the provider 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 all column or output parameter values were 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—for example, with IRowsetChange::SetData for rowset data or ICommand::Execute for input parameter data—the consumer must perform the following actions:

  1. Create an accessor to bind the columns or input parameters.
  2. Allocate a buffer to hold the data to pass to the provider.
  3. Place data in this buffer. (For more information, see the next procedure.)
  4. Call a method that sets data and passes the handle to the accessor and a pointer to the buffer.

For each column or input parameter specified in the accessor, the consumer performs the following actions. When passing key values to an index rowset, the consumer performs these actions only for the number of key columns specified in the cKeyValues argument in IRowsetIndex::Seek or in 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 actions to be performed are the same except that the consumer does not set the unbound part.

  1. If the data is NULL, the consumer 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, or if the provider updates the column, the consumer 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 an insert or update, the consumer sets the status to DBSTATUS_S_IGNORE.

    Otherwise, the consumer sets the status to DBSTATUS_S_OK.

    The address where the status value is stored is calculated from the buffer address passed to the method and the obStatus element of the binding.

  2. If wType is a variable-length data type, the consumer 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. The consumer sets the data value.

    The address where the data value is stored 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 stores the data in separately allocated memory and stores in its buffer a pointer to the data. For information about how this memory is allocated and freed, see "Responsibility for Freeing Memory," earlier in this chapter.

For each column or input parameter specified in the accessor, the provider performs the following actions. 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 IRowsetIndex::Seek or in the cStartKeyValues or cEndKeyValues arguments in IRowsetIndex::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 "Binding Data Values," earlier in this chapter.

  1. If it has not already done so, the provider validates the accessor against the metadata.

    The provider can validate the entire accessor before setting any data or on a binding-by-binding basis while setting data. If validation fails before the provider sets data, the provider produces the appropriate return code and does not set any data. If it fails when the provider is setting data, the provider sets the status to DBSTATUS_E_BADACCESSOR and proceeds to the next column or output parameter.
  2. The provider retrieves the status value from the consumer's buffer, depending on the status setting, as follows:
    • DBSTATUS_S_ISNULL—The provider uses a null value and proceeds to the next column or input parameter.
    • DBSTATUS_S_DEFAULT—The provider uses the default value and proceeds to the next column or input parameter.
    • DBSTATUS_S_IGNORE—The provider skips the column and proceeds to the next column to be inserted or updated.
    • 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 a variable-length data type, 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. The provider 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 usually 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.
    • 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, as well as the bound length pointed to at the offset indicated by obLength.

      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 COM Objects.

      If the type indicator is DBTYPE_BSTR, the provider retrieves the pointer to the BSTR from the consumer's buffer and the BSTR itself 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, DBTYPE_VARNUMERIC, or DBTYPE_BYTES.

      Note It is an error to combine DBTYPE_BYREF and DBTYPE_BYTES with no bound length.

    • 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 structure from the consumer's buffer. It retrieves the data from the SAFEARRAY to which this pointer points.
  5. The provider 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: Data Types.

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 also does one of the following:

  • Stops processing and backs out any changes already made
  • Stops processing and leaves any changes already made
  • Continues processing the remaining columns or input parameters

If the provider stops processing, it must set the status value to DBSTATUS_E_UNAVAILABLE for any column or input parameter that was not in error and was not successfully set. Thus, 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 searching for 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, searching for a key value, or setting the index range, the provider then returns DB_E_ERRORSOCCURRED.


Chapter 7: BLOBs and COM Objects

Some providers support the storage and retrieval of binary large objects (BLOBs) in rowsets. To the provider (and thus the rowset), a BLOB is a sequence of uninterpreted bytes. The consumer is responsible for interpreting the contents of the BLOB. The provider is responsible only for providing the mechanism or mechanisms with which the consumer stores and retrieves BLOBs and COM objects. Whether a provider supports BLOBs and COM objects is provider-specific.

This chapter discusses ways to access BLOB data as in-memory data, storage objects, and columns. In-memory data is treated as a sequence of bytes sent or retrieved in one piece. A storage object—that is, a COM object that exposes a storage interface such as ISequentialStream, IStorage, or ILockBytes—can be retrieved or sent in pieces. The rows in the rowset act as containers for the storage objects. A specialized type of storage object exposed by the COM interface IStream, called an OLE DB stream object, is described in Chapter 9: Row and Stream Objects.

This chapter also discusses limitations and lifetimes of storage objects. COM objects—for example, objects that support IPersist* interfaces—can also be stored in columns in a rowset. The manipulation, update semantics, and transaction considerations of IPersist* objects are explained in this chapter.

For more information on | Go to
Accessing BLOB data | "Accessing BLOB Data" 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

OLE DB defines handling of the following kinds of objects:

  • 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—A COM object that exposes IPersistStream, IPersistStreamInit, or IPersistStorage. The type indicator of an IPersist* column is DBTYPE_IUNKNOWN.

The consumer can determine what types of BLOBs and COM objects a provider supports by calling IDBProperties::GetProperties, passing the DBPROP_OLEOBJECTS property as a parameter.


Accessing BLOB Data

BLOBs can be accessed as in-memory data—a sequence of bytes sent or retrieved as a single unit—or as storage objects, through ISequentialStream, IStream, IStorage, or ILockBytes.

The provider can set the DBCOLUMNFLAGS_ISLONG flag to inform the consumer that the provider supports treating BLOBs as storage objects. Although the consumer can still choose to access the BLOB as in-memory data, there might be provider-specific problems in doing so. For example, the BLOB might be truncated due to machine limits on memory. Also, limitations on BLOB columns might apply to binding BLOBs as in-memory data. For example, IRowset::GetData might fail if called more than once for the BLOB. For more information on possible limitations when accessing BLOB columns, see "Limitations of Storage Objects" in this chapter.

If the DBCOLUMNFLAGS_ISLONG flag is not set, the consumer can access the BLOB as in-memory data. It is provider-specific whether the provider supports reading such data through a storage interface. Most consumers allocate sufficient memory to hold such columns in their entirety and treat them as in-memory data.


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 consumers get or set character data in this manner, the data is null-terminated.


BLOBs as Storage Objects

A COM object that exposes one or more storage interfaces such as ISequentialStream, IStream, IStorage, or ILockBytes is known as a storage object. In OLE DB, BLOBs can be bound as storage objects. When a BLOB is stored in a column as a storage object, the provider indicates this by setting the DBCOLUMNFLAGS_ISLONG bit in the dwFlags element of the column's DBCOLUMNINFO structure. The consumer can get or set the BLOB by calling methods on one of its storage interfaces.

The rows in the rowset act as containers for 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, COM requires that all child objects are invalidated when a parent IStorage object is released. If IRowset::ReleaseRows frees a row handle and no other references to the row remain, any storage objects open in that row enter a zombie state. In this case, releasing the object usually is the only valid consumer action.

The following table shows the rowset methods and interfaces that are analogous to IStorage methods and modes.

IStorage method or mode | Rowset method, interface, or mode
Transacted mode | Deferred 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

In most cases, BLOB data is not fetched and storage objects are not created until IRowset::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: Rowsets.
IStorage::DestroyElement IRowsetChange::SetData

For more information, see "Getting and Setting BLOB Data with Storage Objects," later in this chapter.


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, whether or not the objects are constructed over the same column, over different columns in the same row, or over 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. By calling IDBProperties::GetProperties in the DBPROP_BLOCKINGSTORAGEOBJECTS property, a consumer determines whether storage objects might prevent the use of other methods.
  • 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.
  • Type conversions and storage objects—A storage object is essentially a stream of uninterrupted bytes. When getting or setting rowset or parameter data of type DBTYPE_IUNKNOWN, the storage object reader cannot request data conversions. The reader must be able to handle the storage object writer's implementation of the storage object.


Lifetime of Storage Objects

Storage objects are freed in the same manner as other COM objects. Thus, when a consumer no longer needs a storage object created by a provider, it calls Release on that object. If the provider supports a VARIANT_FALSE value for DBPROP_MULTIPLESTORAGEOBJECTS, the consumer must always call Release on the storage object before it can open a new storage object on any row. Releasing a reference to a storage object does not free the storage object. Doing so only causes the reference count on the storage object to be decremented. When the object's reference count reaches zero, the object is freed automatically.

In addition, a storage object might 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 underlying data store.

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 BLOB data as a storage object, a consumer creates an accessor that includes a binding to the BLOB column. The consumer will perform the following actions:

  1. Set the wType element of the DBBINDING structure for the BLOB column to DBTYPE_IUNKNOWN.
  2. Set 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 in 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. Set 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 IAccessor::CreateAccessor or in a method that uses the accessor, such as IRowset::GetData.

Getting BLOB Data with Storage Objects

To get BLOB data by using a storage object, a consumer will perform the following actions:

  1. Create 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. Call 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. Call methods on the storage interface to get the BLOB's data, such as ISequentialStream::Read, IStream::Read, ILockBytes::ReadAt, or IStorage::OpenStream. If these methods are used to get character data, the data is not null-terminated.

If the consumer calls IRowset::GetData, IRowsetRefresh::GetLastVisibleData, or IRowsetUpdate::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 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
   } ;
   ISequentialStream *   pISequentialStream;

   // 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, and 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) {
      //Obtain pointer to ISequentialStream.
      pISequentialStream = *(ISequentialStream **)pData;
      do {
         pISequentialStream->Read(rgBuffer,   // Buffer to write the data
                           sizeof(rgBuffer),   // Number of bytes to get
                           &cb);   // Number of bytes written to buffer
         if (cb > 0)               // Check to see if we got data
         {
            // Process data here.
         }
      } while (cb >= sizeof(rgBuffer));
   };

   pIMalloc->Free(rghRows);
};

Setting BLOB Data with Storage Objects

There are two ways a consumer can set BLOB data by 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's 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.

It is permissible to open an ISequentialStream storage object with both read and write access (STGM_READWRITE). With such an object, there is a single "current position" that is incremented by both the ISequentialStream::Read and the ISequentialStream::Write methods. In this case, a write would overlay the contents of the stream.

Writing Data Directly to the Provider's Storage Object

To write data directly to the provider's storage object, the consumer performs the following actions:

  1. Creates an accessor that binds the value of the BLOB column.

    The consumer must specify that it needs to have write access to the storage object by at least requesting STGM_WRITE. 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. Because IRowsetChange::SetData is not called, this technique does not require IRowsetChange, so there is no requirement that DBPROP_IRowsetChange is VARIANT_TRUE. If the provider supports deferred validation of accessors and if the provider cannot deliver an updatable storage object, writing directly to the storage object would fail with a bad binding status (such as DBBINDSTATUS_E_BADSTORAGEFLAG) on either IAccessor::CreateAccessor or IRowset::GetData.

    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.

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's Storage Object

To pass a pointer to its own storage object, the consumer must perform the following actions:

  1. Create an accessor that binds the value of the BLOB column by at least requesting STGM_WRITE. 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. Call IRowsetChange::SetData, IRowsetChange::InsertRow, IRowChange::SetColumns, or IRowSchemaChange::AddColumns with the accessor that binds the BLOB column, passing a pointer to a storage interface on the consumer's storage object.

If the provider already has a storage object open over the BLOB's data, the method might return a status of DBSTATUS_E_CANTCREATE for the column. The provider reads the data from the consumer's stream in order to update the column. The provider may read the data at the time IRowsetChange::SetData or IRowsetChange::InsertRow is called, or (if the rowset is in deferred update mode) may defer reading the data until IRowsetUpdate::Update or IRowsetUpdate::Undo is called for the row, or until IRowsetChange::SetData is called again for the same column in the same row. Thus, the consumer's storage object must be valid upon IRowsetChange::SetData or IRowsetChange::InsertRow and must remain valid until the provider releases it. When the provider has finished using the consumer's storage object, it calls IUnknown::Release to release the pointer. For more information about releasing the reference to a storage object, see "Releasing a reference to a storage object" in this section.

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 uses the Stat method on the consumer's storage object if the consumer's storage object exposes IStream, IStorage, or ILockBytes, or retrieves this number 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 if the consumer's storage object exposes IStream, IStorage, or ILockBytes, the provider ignores the bound length value.

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 IRowset::GetData, the GetData method returns a status value of DBSTATUS_S_ISNULL. If the consumer sets the column to a zero-length value and then calls IRowset::GetData, the GetData method returns a pointer to a storage object that contains no data.

Note Not all providers support setting a BLOB column to a zero-length value.

Releasing a reference to a storage object

The provider must release any references on the storage object by the time the row is successfully updated or the change is undone or overwritten; if the rowset is in immediate update mode, the provider must not hold any pointers or reference counts on the consumer's storage object after the call to IRowsetChange::SetData or IRowsetChange::InsertRow in which the storage object is passed. If the rowset is in deferred update mode, the provider must not hold any pointers or reference counts on the consumer's storage object after IRowsetUpdate::Update or IRowsetUpdate::Undo has been called successfully for the row or after IRowsetChange::SetData has been successfully called to replace the contents of the column. If the consumer wants to ensure access to its storage object after SetData returns, it must call IUnknown::AddRef on the pointer before calling IRowsetChange::SetData.

In general, the provider is responsible for releasing storage objects, on either rowsets (IRowsetChange::SetData, IRowsetChange::InsertRow) or rows (IRowChange::SetColumns, IRowSchemaChange::AddColumns), both on success and on any errors. The provider is responsible for releasing storage objects of column types DBTYPE_IUNKNOWN and DBTYPE_IUNKNOWN | DBTYPE_BYREF. It is the consumer's responsibility to release storage objects of type DBTYPE_IDISPATCH or to release any storage object embedded in the following types: DBTYPE_VARIANT, DBTYPE_PROPVARIANT, DBTYPE_VECTOR, and DBTYPE_ARRAY. For more information, see the applicable interface and method descriptions in the "OLE DB Reference" section of this documentation.

Code example of passing a pointer to a storage object

The following code passes a pointer to a different ISequentialStream object to IRowsetChange::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's IRowsetChange interface
   // 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 will
   // 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();

   // Assume you already have a row handle (HROW). Call
   // SetData and pass it the address of the pointer to the
   // object ISequentialStream not in the rowset. The rowset
   // will copy the data from the ISequentialStream object to
   // the rowset. Set up pData row buffer.
   BYTE* pData=(BYTE*)CoTaskMemAlloc(sizeof(IUnknown*)+sizeof(ULONG));

   // Value - pass ISequentialStream pointer to the provider.
   *(ISequentialStream**)(pData+rgBinding[0].obValue)=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 an ISequentialStream::Read
   //  on the pISeqStr pointer passed in...
   pIRowsetChange->SetData(hrow, hAccessor, pData);
   CoTaskMemFree(pData);
   return 0;
} ;

Getting and Setting BLOB Data with Storage Objects with Parameters

Consumers get and set BLOBs and COM objects in command parameters in a manner similar to accessing BLOB columns as described earlier in "Getting and Setting BLOB Data with Storage Objects."

If the consumer binds a command parameter to a storage object, at command execution the consumer passes the storage object pointer, for example ISequentialStream, as the parameter value. At execution, the provider calls a method in the storage object—in this example, ISequentialStream::Read—to access the contents of the storage object. The provider does not reset position on the storage object (if the storage object supports setting position).

Providers should be able to handle the storage object, which can be large, in a way that does not adversely impact memory usage.


Storage Objects and Rowset Update Semantics

When does the data that is set in a storage object become visible to other transactions operating at the Read Uncommitted transaction isolation level? For providers built over database servers, this question can also be phrased as, "When does data that is set in a storage object become visible in the data store?"

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 deferred 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 ITransaction::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 deferred update mode—Storage objects can be large. Therefore, using them in deferred update mode can be expensive because the provider may locally cache any changes to them until IRowsetUpdate::Update is called. Consumers need to control this expense, so the DBPROP_DELAYSTORAGEOBJECTS property controls whether storage objects are always in immediate update mode, whether or not the rowset is in immediate or deferred update mode. For some providers, this is a read-only property. See the following illustration.

Storage object update visibility

The following table shows the sequence of calls needed to make data that is 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.

Storage mode | Update mode | DBPROP_DELAYSTORAGEOBJECTS | Calls needed to expose data to other transactions running at Read Uncommitted
Direct | Immediate | Not applicable | Write – or – SetData
Direct | Deferred | VARIANT_FALSE | Write – or – SetData
Direct | Deferred | VARIANT_TRUE | Write Update – or – SetData Update
Transacted | Immediate | Not applicable | Write Commit on storage interface – or – SetData
Transacted | Deferred | VARIANT_FALSE | Write Commit on storage interface – or – SetData
Transacted | Deferred | VARIANT_TRUE | Write Commit on storage interface Update – or – SetData Update

When the rowset is in deferred update mode, the consumer cannot undo any pending changes made through a storage object that 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 depends on the DBPROP_TRANSACTEDOBJECT property, which can be set on a per-column basis. If the value of this property is VARIANT_TRUE, 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 depend on the retaining flag (fRetaining) used in the call to ITransaction::Commit or ITransaction::Abort. The retaining flag set to TRUE implicitly begins a new unit of work. If a commit is retaining, the storage objects and any uncommitted data in them remain valid. If an abort is retaining, 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 store after the transaction was aborted there. If the rowset is not preserved after a commit or abort, 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 "ITransactionObject" in Chapter 15: Transactions.

If the value of DBPROP_TRANSACTEDOBJECT is VARIANT_FALSE, 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 in the data store. Calling ITransaction::Commit or ITransaction::Abort on a session containing a rowset with nontransacted storage objects should not affect the state of the storage object.


IPersist* Objects

COM objects can be stored in columns in a rowset. Such objects are required to expose 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.


Getting and Setting IPersist* Objects

The following sections explain how consumers get and set IPersist* objects and how these objects are stored by providers.

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 will perform the following actions:

  1. Create an instance of the IPersist* object and obtain an IPersistStorage, IPersistStreamInit, or IPersistStream pointer on it.

    For example, the provider 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.

  2. Create 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. Load the IPersist* object: that is, call Load on the IPersist* interface on the object and pass the interface pointer obtained in step 2.
  4. Perform 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. Return 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 will return 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 IRowset::GetData, IRowsetRefresh::GetLastVisibleData, or IRowsetUpdate::GetOriginalData returns.

Setting IPersist* Objects

To set an IPersist* object in a column, a consumer uses one of two procedures. The first 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 that the rowset will not be able to later create an instance of.

In this first procedure, the consumer will perform the following actions:

  1. Call 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 IRowset::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. Call Save on the IPersist* interface of the IPersist* object and pass it the storage interface pointer retrieved in step 1.
  3. Call 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.

The second 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. In this second procedure, the consumer will call IRowsetChange::SetData or IRowsetChange::InsertRow and pass a pointer to an interface on the IPersist* object.

In turn, the provider will perform the following actions:

  1. Create a new storage object for the column and retrieve a storage interface pointer on that object.

    If there is an existing storage object on the column that the provider cannot release, IRowsetChange::SetData or IRowsetChange::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 might return a status of DBSTATUS_E_CANTCREATE for the column.

  2. (Optional) Call 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. Call Save on the IPersist* interface of the IPersist* object and pass 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. Call IPersistStorage::SaveCompleted on the IPersist* object if IPersistStorage is used. The provider sets the pStgNew argument to a null pointer.
  5. Perform the operations required to enable future calls to IRowset::GetData, IRowsetRefresh::GetLastVisibleData, or IRowsetUpdate::GetOriginalData to detect whether the object is loaded and to return a pointer on the loaded object.

    After IRowsetChange::SetData or IRowsetChange::InsertRow returns, the rowset does not hold any reference counts on the IPersist* object.


Lifetime of IPersist* Objects

An IPersist* object is a COM object that supports an IPersist interface. 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 in the data store. Data stored in an IPersist* object is not exposed to its underlying storage object—and hence, to other transactions—until it is persisted by using 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.


Chapter 8: Binder Objects and Direct Binding

OLE DB 2.5 introduces the use of Uniform Resource Locators (URLs) as an alternative to connection strings and command text for specifying data stores and the location of files, directories, and tables. URLs can be used to specify row, stream, rowset, data source, and session objects.

The process of associating an OLE DB object with a resource named by a URL is called direct binding. Direct binding is performed by the root binder and provider binder objects. With direct binding, OLE DB applications can bind URL names to OLE DB objects without having to know which provider services the object.

Direct binding makes it possible to create consumer applications that can access multiple providers based on the URLs of resources to be bound to row and rowset objects. Such consumers do not need to instantiate data source objects and session objects. They do not even have to know which provider "owns" the URL of the resource to be bound.

This chapter discusses binder objects and their use in direct binding.

For more information on | Go to
Row and stream objects | Chapter 9: Row and Stream Objects
Binder objects | "Binder Objects" in this chapter
Root binder object | "Root Binder Object" in this chapter
Provider binder objects | "Provider Binder Objects" in this chapter
Direct binding | "Direct Binding" in this chapter
How to support direct binding | "Provider and Consumer Support for Direct Binding" in this chapter
Inheritance of bind options | "Inheritance of Bind Options" in this chapter


Binder Objects

Binder objects are OLE DB objects that bind resources named by URLs to other OLE DB objects. There are two types of binder objects, and they are described as follows:

  • The root binder object oversees the direct binding process; it has a well-known CLSID and is part of the Microsoft® Windows® 2000 operating system. For consumers, the root binder object is the starting point of their interaction with OLE DB 2.5-compliant providers.
  • The provider binder object performs direct binding operations on the URL namespace for which it is registered. Each OLE DB 2.5-compliant provider implements its own provider binder object.

The binder object cotype is defined as follows. For more information about cotypes, see "Conceptual Programming Models in OLE DB" in Chapter 1: Overview of OLE DB.

CoType TBinder {
   [mandatory]   interface IBindResource;
   [mandatory]   interface ICreateRow;
   [mandatory]   interface IDBBinderProperties;
   [optional]    interface IRegisterProvider;        // Root binder only
   [optional]    interface ISupportErrorInfo;
}

All binder objects must expose the interfaces listed in the following table.

Interface | Use
IBindResource | Binds to an object named by a URL.
ICreateRow | Creates and binds to an object named by a URL.
IDBBinderProperties | Gets, sets, or resets direct binding properties.


Root Binder Object

The binding process is managed by the root binder object, which is an OLE DB object with a well-known CLSID. This CLSID, which is defined in Msdasc.h, is CLSID_RootBinder. The root binder object manages the mapping of URL namespaces to OLE DB providers.

The consumer instantiates the root binder object by calling CoCreateInstance on the CLSID of the root binder object, requesting an IBindResource interface pointer. When the root binder object has been instantiated, providers can register their provider binder objects with the root binder object through the IRegisterProvider interface. Each provider calls IRegisterProvider::SetURLMapping to tell the root binder about each URL namespace it can bind.

Consumers request bindings by calling IBindResource::Bind. When the root binder receives such a URL binding request, it decodes the URL to the extent needed to determine which provider most closely matches the URL and then looks up the CLSID of that provider's binder object. The root binder then instantiates the provider binder object and delegates the bind request to it.

Before calling IBindResource::Bind, the consumer can call IDBBinderProperties::SetProperties on the root binder object to set any OLE DB properties, including provider-specific properties, that are needed for the bind operation. These properties are applied to all subsequent binds until the consumer calls IDBBinderProperties::Reset or IDBBinderProperties::SetProperties to reset or change the properties for subsequent IBindResource::Bind calls.

If IBindResource::Bind returns DB_E_ERRORSOCCURRED or DB_S_ERRORSOCCURRED, the consumer can immediately call IDBProperties::GetProperties with the DBPROPSET_PROPERTIESINERROR property set to return all the properties that were in error. For more information, see "Property Sets" in Appendix C: OLE DB Properties.


Provider Binder Objects

Every OLE DB 2.5-compliant provider that implements direct URL binding is required to implement a provider binder. If the provider sets the DBPROPVAL_OO_DIRECTBIND value of DBPROP_OLEOBJECTS, the consumer can assume that direct binding is supported.

As in the case of the root binder object, IDBBinderProperties::GetProperties is used to determine the current values of OLE DB properties on the provider binder object. This interface also has a SetProperties method. However, rather than setting properties on the provider binder object directly, the consumer normally calls SetProperties to set these properties on the root binder, which in turn sets the properties of the provider binder object.

For the root binder to know when to route URLs to a given provider, each provider must register its provider binder object to handle a portion of the URL namespace. The root binder will invoke the provider binder whose registered namespace most closely matches the URL to be bound. For more information about the format and syntax of URLs, see RFC 1738, Uniform Resource Locators (URL). You can find this document at http://ds.internic.net/rfc/rfc1738.txt.

Only one OLE DB provider can be registered for the same portion of the URL namespace. If the consumer needs to map a set of URLs to more than one provider (for example, depending on online or offline status) or if a provider needs to service a set of URLs differently under different circumstances, this must be accomplished through application-specific or provider-specific behavior.

Note The URL scheme and prefix combination ("myscheme://myserver/myprefix") is not case-sensitive. This means you cannot have the same mappings to different providers; "myscheme://myserver/myprefix" is the same as "MyScheme://MyServer/MyPrefix". For IBindResource::Bind and ICreateRow::CreateRow, the URL-to-provider mapping will handle any URL in any case that begins with this scheme and prefix combination.


Direct Binding

Through direct binding, a consumer can specify an OLE DB object in such a way that it can be obtained in a single method call, without the use of connection strings, accessors, or provider-specific mechanisms. This is done by associating OLE DB objects with resources named by URLs. Row, rowset, stream, data source, and session objects can all be obtained through direct binding.

The consumer calls IBindResource::Bind on the root binder object, passing as an argument the URL of the resource that is to be bound to the requested OLE DB object.

IBindResource::Bind has flags that encapsulate the most common properties to be set on the returned OLE DB object. The consumer can set additional properties by calling IDBBinderProperties methods.

When IBindResource::Bind is called for a URL, the following procedure occurs:

  1. The root binder obtains the CLSID of the provider binder that is registered to the nearest matching URL prefix.
  2. The root binder cocreates the provider binder object of the provider that is registered for that prefix, if it is not already instantiated.
  3. The root binder delegates the bind request to the appropriate provider. Any properties that were set on the root binder are also passed to the provider binder object. These properties are passed when IBindResource::Bind is called because the root binder does not know which provider binder to use at any other time.
  4. The provider binder object opens the resource named by the URL.
  5. If the requested OLE DB object type is a data source or session object and this object does not currently exist, the provider instantiates the object. If the object type requested is a row, stream, or rowset object, the provider creates a new row, stream, or rowset object over the resource named by the URL.
  6. The provider binder object returns the requested interface pointer on the OLE DB object to the caller. The OLE DB object remains bound as long as this interface pointer is held by the calling application.

A URL designates a resource in a hierarchical data store owned by an OLE DB provider. By virtue of this association, the URL that is requested in a direct binding call (to IBindResource::Bind or ICreateRow::CreateRow) defines a scope for the session object associated with the object that is obtained from the bind. The scope is that of the resource named by the URL. This scope is the same, no matter how the session object is obtained. The session object can be obtained in one of the following ways:

  • The consumer calls IBindResource::Bind or ICreateRow::CreateRow to bind to a URL, requesting a rowset, row, or stream object. The consumer can then obtain the session object by calling IRowsetInfo::GetSpecification, IGetSession::GetSession, or IGetSourceRow::GetSourceRow.
  • The consumer calls IBindResource::Bind to bind to a URL, requesting a session object in the bind call. Bind then returns the implicit session object.
  • The consumer calls IBindResource::Bind to bind to a URL, requesting a data source object in the bind call. The consumer then calls IDBCreateSession::CreateSession on the returned data source object to obtain the implicit session object.

Note It is provider-specific if binds requested outside the scope of the current session or row object succeed.

If the resource named by a URL does not exist, an application can create the resource itself, as well as the row or rowset object, through the ICreateRow interface. The newly created row or rowset object inherits the default columns of the object named by the parent URL. When a consumer instantiates a row or rowset directly in this way, the provider implicitly creates an OLE DB session object and then creates the desired object within the scope of that session. To obtain an interface pointer on this implicit session, the consumer can use the DBIMPLICITSESSION parameter of IBindResource::Bind (or ICreateRow::CreateRow) or can call IGetSession::GetSession on the row object (or IRowsetInfo::GetSpecification on the rowset object). This interface pointer can be used to commit or abort a transaction that the row or rowset is part of.

Note For a given instance of a provider binder object, each time IBindResource::Bind is called, the provider creates a new row, rowset, or stream object in the context of the same implicit session.

When a new row or rowset object is created, the provider might need to generate the suffix of the new URL. The DBPROP_GENERATEURL data source object property indicates whether the provider generates URL suffixes or the consumer must supply the complete URL.

When binding to a URL, with either IBindResource or ICreateRow, the consumer must specify the type of object to be returned: data source, session, rowset, row, or stream. If the caller binds to a URL of a row or rowset but requests a data source or session object, no row or rowset is instantiated. Instead, the appropriate data source object or session is instantiated if it does not already exist. To obtain a row or rowset object, the caller must bind again and request a row or rowset object.


Provider and Consumer Support for Direct Binding

IBindResource::Bind accepts a set of bind options defining the properties of the requested OLE DB object.

Note These options must be specified separately for each new call to Bind. For more information, see the reference entry for IBindResource::Bind.

OLE DB 2.5-compliant providers that support direct binding are required to implement IBindResource and ICreateRow on provider binder objects. If the provider does not allow the creation of resources via ICreateRow, it returns E_NOINTERFACE when the consumer calls ICreateRow::CreateRow. IBindResource is used when the URL represents an existing object. When the application needs to create a new row object, ICreateRow is used.

Note Because all data access in OLE DB is done in the context of one or more sessions, any provider that supports direct binding must also implement IBindResource on the session objects that it provides. This is intended to allow direct navigation to some resource of the provider by using an absolute or relative URL. For more information about navigation in hierarchical data, see "Navigating Tree-Structured Namespaces" in Chapter 9: Row and Stream Objects.

If a provider supports direct binding, it sets the bit DBPROPVAL_OO_DIRECTBIND in the value of the DBPROP_OLEOBJECTS data source object property. If this bit is set, the IBindResource and ICreateRow interfaces are supported on the session object and the provider implements a provider binder object.

Note IBindResource::Bind can run synchronously or asynchronously. In either case, the consumer can use an initialization step to first register a callback to receive progress notifications or can obtain an IDBAsynchStatus interface on the requested object to poll for status or to cancel the operation.


Inheritance of Bind Options

The bind options that specify the properties of the bind operation to be performed can be inherited from the provider's data source, session, and command objects.

Inheritance of Bind Options from the Data Source Object

When a client creates a session from a data source object by using IDBCreateSession, any binding properties set on the data source object are also set on the session object.

Inheritance of Bind Options from the Session Object

Any binding properties specified on the bind to the session object by the data source object or root binder are inherited when binding from the session object. If the client needs to use different binding properties, it must bind again from the object on which the binding properties were originally specified. Because of this limitation, the binding properties cannot be changed when binding to objects inside the same session.

If the dwBindURLFlags parameter is nonzero, the bind flags specified there are used for the new bind when a client binds from the session object using IBindResource. If dwBindURLFlags is zero, whatever flags were in effect when the session object was created (by using either IDBCreateSession from the data source object or IBindResource) are used for the new bind.

Inheritance of Bind Options from the Command Object

When a command object is created from a row or session object, all binding options that were in effect when the row or session object was created are inherited by the command object. These binding options propagate from the command object to the row or rowset object created from it.


Chapter 9: Row and Stream Objects

OLE DB provides rowset objects for accessing information in data stores, such as relational databases, in which data is formatted into tables. However, not all structured data can easily be viewed as tables. This is especially true of heterogeneous hierarchical data such as directories and files in a file system or folders and messages in an e-mail system.

OLE DB 2.5 introduces the row object, which is an OLE DB object that contains a set of columns of data. A row object can represent a row in a rowset, the result of a singleton SQL SELECT statement, or a node in a tree-structured namespace, such as a file in a directory or a message in a mail folder.

OLE DB 2.5 also introduces the OLE DB stream object, which encapsulates the contents of files, e-mail messages, or other stream-based objects. A stream object is a specialized storage object that exposes the COM IStream interface. Stream objects are created from a row object and reference the parent object.

This chapter discusses the use of row and stream objects.

For more information on | Go to
Rowset objects | Chapter 4: Rowsets
Row objects | "Row Objects" in this chapter
Creating row objects | "Creating Row Objects" in this chapter
Row metadata | "Row Metadata" in this chapter
Special row column DBIDs | "Special Row Column DBIDs" in this chapter
Column values | "Column Values" in this chapter
Transactions on row objects | "Transactions on Row Objects" in this chapter
How to support row objects | "Provider and Consumer Support for Row Objects" in this chapter
Stream objects | "Stream Objects" in this chapter
Modeling tree-structured namespaces | "Modeling Tree-Structured Namespaces" in this chapter
Direct binding in tree-structured namespaces | "Direct Binding in Tree-Structured Namespaces" in this chapter
Navigating tree-structured namespaces | "Navigating Tree-Structured Namespaces" in this chapter
Scoped tree manipulation operations | "Scoped Tree Manipulation Operations" in this chapter
Resource rowsets and document source providers | "Resource Rowsets and Document Source Providers" in this chapter
Direct binding | Chapter 8: Binder Objects and Direct Binding

The row object cotype is defined as follows. (For more information about cotypes, see "Conceptual Programming Models in OLE DB" in Chapter 1: Overview of OLE DB.)

CoType TRow {
   [mandatory]   interface IColumnsInfo;
   [mandatory]   interface IConvertType;
   [mandatory]   interface IGetSession;
   [mandatory]   interface IRow;
   [optional]    interface IColumnsInfo2;
   [optional]    interface IConnectionPointContainer;
   [optional]    interface ICreateRow;
   [optional]    interface IDBAsynchStatus;
   [optional]    interface IDBCreateCommand;
   [optional]    interface IDBInitialize;
   [optional]    interface IRowChange;
   [optional]    interface IRowSchemaChange;
   [optional]    interface IScopedOperations;
   [optional]    interface ISupportErrorInfo;
}

All row objects must expose the interfaces listed in the following table.

Interface | Use
IColumnsInfo | Provides information about the columns of the row object.
IConvertType | Provides information about the data type conversions supported by the row object.
IGetSession | Returns an interface pointer on the session object within whose context the row object was created.
IRow | Contains methods for reading column data from a row object and for obtaining the source rowset of the row object, if one exists.


Row Objects

Row objects make rows available as first-class objects that can exist independently of rowsets. In addition, the row object provides an alternative binding mechanism to the accessor model. Using row object interfaces, the consumer can navigate to the desired row in a rowset and access its columns directly, without creating an accessor object.

The row object provides an efficient way to model a singleton SQL SELECT statement. Otherwise, the consumer would have to create a rowset to get the results of a singleton SELECT. Whether it is simpler or more efficient for a consumer to use row objects instead of rowsets depends on the application scenario.

Row and rowset objects can be used together. For example, a row can contain a rowset as one of its column values. Or a row object created from a row in a rowset can have its own schema that adds row-specific columns to the columns defined by the source rowset.

Rowset column ordinals start at 1 and are numbered sequentially in increasing order (for example, 1, 2, 3, and so on). When a row object is derived from a rowset, the row-specific column ordinals must come after the rowset column ordinals.

Hierarchies of row and rowset objects can also be used to model collections of related nodes in tree-structured namespaces, such as directories in a file system or messages in an e-mail system. These hierarchies can be used not only to access hierarchical data in read-only mode but also to manipulate the underlying resource tree and its contents. Just as an OLE DB consumer can change the rows in a rowset and transmit those changes to the underlying data store, it can also create new objects in a tree-structured namespace, such as directories and e-mail folders. Or it can move, copy, or delete entire subtrees by calling methods on the appropriate row objects.

If IBindResource::Bind is called, updatability of the row (whether or not the row exposes IRowChange) depends on the bits of dwBindURLFlags. Otherwise, if a row is obtained from a source rowset using IGetRow::GetRowFromHROW, or if a row is obtained from a parent row using IRow::Open, the updatability of the row is inherited from the source rowset or the parent row.


Creating Row Objects

A consumer obtains a row object in one of the following ways:

  • The consumer calls IOpenRowset::OpenRowset on the current session object and requests a row object instead of a rowset.
  • The consumer executes a command containing a singleton SQL SELECT statement and requests a row object instead of a rowset.
  • The consumer calls IGetRow::GetRowFromHROW on the row handle of a row in a rowset.
  • The consumer calls IBindResource::Bind or ICreateRow::CreateRow on the URL of a node in a tree-structured namespace, such as a message in a mail folder, passing DBGUID_ROW as the value of rguid.
  • The consumer calls IRow::Open on a row-object-valued column of an existing row object, passing DBGUID_ROW as the value of rguidColumnType.
  • The consumer calls IMultipleResults::GetResult and requests a row object.

Creating a Row Object with IOpenRowset

To create a row object directly with IOpenRowset, the consumer calls IOpenRowset::OpenRowset on the current session object. However, by default, IOpenRowset::OpenRowset returns a rowset object, so to obtain a row object instead of a rowset object, the consumer must do one of the following when calling IOpenRowset::OpenRowset:

  • Request one of the following interfaces that are unique to row objects: IRow, IRowChange, or IRowSchemaChange.
  • Set the rowset property DBPROP_IRow to VARIANT_TRUE by passing an appropriate DBPROPSET structure to IOpenRowset::OpenRowset through the rgPropertySets input parameter. The consumer must do this if requesting any other interface on the row object.

Creating a Row Object with a Singleton SELECT

To obtain a row object, the consumer calls IDBCreateCommand::CreateCommand on the session object to obtain a command object and then calls ICommand::Execute on the command object, requesting a row object instead of a rowset. If the command is one that returns a row, such as a singleton SQL SELECT statement, and if the provider supports creating row objects via commands, ICommand::Execute returns a row object. By default, ICommand::Execute returns a rowset object. To obtain a row object instead of a rowset object, the consumer must do one of the following when calling ICommand::Execute:

  • Request one of the following interfaces that are unique to row objects: IRow, IRowChange, or IRowSchemaChange.
  • Set the rowset property DBPROP_IRow to VARIANT_TRUE by calling ICommandProperties::SetProperties on the command object before executing the command. The consumer must do this if requesting any other interface on the row object.

OLE DB does not require providers to know the number of rows to be returned for a given SELECT statement. If the consumer has requested a row object and if the command would otherwise return a rowset of two or more rows, the provider should return only the first row (as a row object), along with a return code of DB_S_NOTSINGLETON.

Note The "first row" is defined to be the first row of the rowset that would be returned if a rowset was requested and IRowset::GetNextRows was called with lRowsOffset set to zero. Also, it can be difficult or expensive for some providers to determine that the command would return a rowset with more than one row, so providers are not required to return DB_S_NOTSINGLETON.

Creating a Row Object from a Row in a Rowset

The consumer can obtain a row object for a row in a rowset by calling IGetRow::GetRowFromHROW on the row handle (HROW) of the row. GetRowFromHROW creates a new row object each time it is called.

Conversely, the method IRow::GetSourceRowset allows the consumer to obtain the source rowset for a row object. Row objects are permitted, but not required, to keep references on source rowsets.

Using the two methods discussed above, the consumer can navigate between a rowset and a row object.

If a row object has a source rowset and changes to this rowset are deferred, changes to all columns of the row object are also deferred. Thus, IRow::GetColumns returns the current value of all row columns, and the original values of row columns that are not part of the source rowset are unavailable. Updates to row column values, via IRowChange::SetColumns or IRowSchemaChange::AddColumns, are not propagated to the underlying data store until IRowsetUpdate::Update is called on the source rowset.

Note IRowsetUpdate::Undo can also undo changes made to row-specific columns.

If a row object is created from a source rowset that implements IRowsetRefresh to synchronize the rowset with its data store, the row-specific column values of the row object are refreshed along with those defined by the source rowset.

Note If a row is derived from a source rowset, changes made to a column by using the row object are visible when the column is retrieved using the source rowset. The reverse is also true; that is, changes made to a column value by using the rowset are visible when that column is accessed using the row object. If multiple row objects are created from the same HROW via calls to GetRowFromHROW, visibility to the other row objects of changes made to a specific row object is governed by properties of the source rowset such as DBPROP_OWNUPDATEDELETE.

Obtaining the URL of a Row in a Rowset

IGetRow::GetURLFromHROW can be used to obtain the URL of a row in a rowset. The consumer can then use this URL in a call to IBindResource::Bind (or IScopedOperations::Bind, if the URL represents a row in the scope of the row object on which the IScopedOperations interface was obtained) to create a new row object for this row.

In addition, DBROWCOL_ROWURL is provided as a shortcut column ID (DBID) on a row object to identify the column, if any, that contains the URL of that row. For more information about the row URL column and DBROWCOL_ROWURL, see "Special Row Column DBIDs" later in this chapter.


Row Metadata

Like rowset columns, row object columns are identified by column IDs, which take the form of DBID structures. Column IDs must be unique within a row or rowset. This allows columns to be accessed by name, GUID, or PROPID.

If a row object is created from a row in a rowset, the columns of the row object must be a superset of the columns of the source rowset. That is, the row object can contain row-specific columns in addition to those that are already defined by the source rowset. The row schema of a row object is the set of all columns of the row object.

Getting Column Metadata

Applications cannot always detect what columns are available in a row object. This is particularly true of any number of general-purpose data binding tools. Therefore, it is useful to enumerate the names and types of the available columns in a row. To get column metadata for row object columns, consumers call IColumnsInfo2::GetColumnInfo (inherited from IColumnsInfo). This method returns metadata, including the types of the available columns in the row object.

For applications involving data stores that have large numbers of columns, IColumnsInfo2::GetColumnInfo may return a very large amount of metadata. In such cases, it is often useful to get column metadata for a restricted set of columns, based on a column name mask. OLE DB 2.5 introduces the IColumnsInfo2::GetRestrictedColumnInfo method, which can return column metadata based on an optional column mask.

Setting Column Metadata

To add or delete row-specific columns, the consumer calls IRowSchemaChange methods. IRowSchemaChange::DeleteColumns deletes columns from a row or, if the columns also belong to a source rowset, sets their values to NULL. IRowSchemaChange::AddColumns can be used to create the row schema or add columns to it.

Note These methods cannot add or remove columns that are inherited from the parent rowset of the row object.

The AddColumns method is similar to IRowChange::SetColumns; however, IRowSchemaChange::AddColumns creates a column if it does not already exist, while IRowChange::SetColumns returns an error if it is called for a nonexistent column.

If a consumer deletes a column that is part of both a row and its source rowset, subsequent calls to access that column must return a null value until both the row and rowset are released. If a consumer deletes a column from a row and that column is not part of the source rowset, providers are free to delete the column from the row. Subsequent calls to access the deleted column can return the DBSTATUS_E_DOESNOTEXIST status code, indicating that the column does not exist.


Special Row Column DBIDs

OLE DB defines special row column IDs (DBIDs) that can be used as shortcuts to access the row URL column and the row default stream column.

DBROWCOL_ROWURL

DBROWCOL_ROWURL is a predefined GUID that designates a column of a row object that contains the URL of the row. The value of DBROWCOL_ROWURL is defined to be {DBGUID_ROWURL, DBKIND_GUID, 0}. The actual DBID of this column is provider-assigned, but DBROWCOL_ROWURL can be used as a shortcut to obtain this column, if it exists. Row objects are not required to have a column that contains the URL of the row, but if a row object has such a column, this DBID can be used to access the column.

The row URL column is defined by the DBCOLUMNINFO structure described in the following table.

Element | Description
pwszName | Provider-specific.
pTypeInfo | Reserved for future use. Providers are required to set pTypeInfo to NULL.
iOrdinal | Provider-specific. OLE DB does not require the row URL column to have a specific iOrdinal value. Must be nonzero.
dwFlags | DBCOLUMNFLAGS_ISROWURL is set.
ulColumnSize | Size of the URL.
wType | DBTYPE_WCHAR.
bPrecision | ~0 (bitwise, the value is not 0; that is, all bits are set to 1).
bScale | ~0 (bitwise, the value is not 0; that is, all bits are set to 1).
columnid | Provider-specific.

DBROWCOL_DEFAULTSTREAM

DBROWCOL_ROWDEFAULTSTREAM is a predefined GUID that designates a column of a row object that contains the default stream of the row. The value of DBROWCOL_DEFAULTSTREAM is defined to be {DBGUID_ROWDEFAULTSTREAM, DBKIND_GUID, 0}. The actual DBID of this column is provider-assigned, but DBROWCOL_DEFAULTSTREAM can be used as a shortcut to obtain the column, if it exists. Row objects are not required to have a default stream column, but if a row has a default stream column, this DBID can be used to access the column. A row can have at most one default stream column.

The default stream column is defined by a DBCOLUMNINFO structure as described in the following table.

Element | Description
pwszName | Provider-specific.
pTypeInfo | Reserved for future use. Providers are required to set pTypeInfo to NULL.
iOrdinal | Provider-specific. OLE DB does not require the default stream column to have a specific iOrdinal value. Must be nonzero.
dwFlags | DBCOLUMNFLAGS_ISDEFAULTSTREAM is set.
ulColumnSize | Size of the stream.
wType | DBTYPE_IUnknown.
bPrecision | ~0 (bitwise, the value is not 0; that is, all bits are set to 1).
bScale | ~0 (bitwise, the value is not 0; that is, all bits are set to 1).
columnid | Provider-specific.


Column Values

The columns in a row can be of any OLE DB data type, including instances of other COM objects (whose data type is DBTYPE_IUnknown), such as row, stream, and rowset objects.

Row object column values can be retrieved using the IRow interface, which all providers are required to support on row objects, and they can be updated using the optional IRowChange interface.

Getting Column Values

To retrieve the values of one or more columns from a row object, the consumer calls IRow::GetColumns. This method does not require an accessor object. Instead, the consumer passes an array of DBCOLUMNACCESS structures. Similarly, status information is returned in the output argument of this method rather than via an accessor. For more information, see the reference entry for IRow::GetColumns.

To open an object-valued column, such as the default stream for a row object, the consumer calls IRow::Open, which opens the COM object contained in the column and returns an interface pointer on it. For more information about object-valued columns, see Chapter 7: BLOBs and COM Objects.

The following rules govern how the DBPROP_ACCESSORDER property interacts with various methods to determine the order in which columns are retrieved from a row object:

  • If the row is obtained from a rowset by IGetRow::GetRowFromHROW, the row inherits its access order from the rowset; that is, the row access order is the same as the rowset access order.
  • If the row is obtained by an IOpenRowset::OpenRowset singleton select operation, and this property is set, the row access order is as specified by this property.
  • If the row is obtained by an ICommand::Execute or IMultipleResults::GetResult singleton select operation or a direct binding operation, and this property is set, the row access order is as specified by this property. For maximum interoperability with various providers, consumers should set the default value for this property to DBPROPVAL_AO_SEQUENTIALSTORAGEOBJECTS.

Setting Column Values

To update the value of one or more columns of a row object, the consumer calls IRowChange::SetColumns. As with IRow::GetColumns, no accessor object is required. The consumer specifies the number of columns to update, information such as the name and type of the column, and a buffer to hold the update value in an array of DBCOLUMNACCESS structures passed as an argument to the method.

If two row objects are created from the same row handle in a source rowset, metadata (schema) and data changes made to one row object are not visible to the other row object. However, once the changes have been transmitted to the underlying data store and the source rowset is recreated or refreshed (for example, by calling IRowsetRefresh::RefreshVisibleData), these changes are visible to new row objects that are created from the row handle.

If two row objects are created from the same row in the underlying data store but do not share a common source rowset, metadata (schema) and data changes made to one row object are not visible to the other row object. However, they are visible to new row objects that are created from the underlying row.


Transactions on Row Objects

Every row object exists within the context of an OLE DB session. If the consumer creates a row object directly by using IBindResource::Bind or ICreateRow::CreateRow, an implicit session is created as part of creating the row object. If the row object is obtained from a rowset, it inherits the rowset's session. If the row object is created by executing a command, it inherits the session of the corresponding command object. Finally, if the row object is created from a session object by calling IBindResource::Bind or ICreateRow::CreateRow, it is created in the context of that session.

As a result, to transact row object operations—either column operations or scoped operations—an application creates a transaction as it would in earlier versions of OLE DB, using interfaces on the session, and the application uses the existing interfaces to commit or abort the transaction. For more information about transactions, see Chapter 15: Transactions.

As with rowset objects, transactions on row objects can also be initiated by calling ITransactionLocal::StartTransaction. The ITransactionLocal interface is available on the session object. Rows that have been instantiated directly by using IBindResource can use IGetSession::GetSession to get this interface. To commit or abort a transaction, call ITransaction::Commit or ITransaction::Abort as desired. After a row's transaction commits or aborts, the row either retains its full functionality or enters a zombie state. Zombie rows support only IUnknown operations. All other operations on a zombie row return E_UNEXPECTED. The DBPROP_COMMITPRESERVE and DBPROP_ABORTPRESERVE rowset properties also apply to any row objects created from a source rowset. These properties determine whether the row remains active or enters a zombie state, after a transaction is committed or aborted, respectively.

Multiple row objects can participate in a transaction. The following rules govern the semantics:

  • Associated with each row object is a session. Row objects instantiated directly by using IBindResource or ICreateRow all belong to the same implicit session.
  • Row objects instantiated after the call to ITransactionLocal::StartTransaction automatically participate in the transaction in progress.
  • Calling ITransaction::Commit (or ITransaction::Abort) commits (or aborts) all work done on all row objects within the scope of the session.

Note By default, a provider is in auto-commit mode; that is, any changes are committed automatically. The application can explicitly start a transaction (thus getting out of auto-commit mode) by calling ITransactionLocal::StartTransaction.

The impact of row operations on a transaction in progress is governed by the OLE DB property DBPROP_SUPPORTEDTXNDDL. These properties describe whether data manipulation language (DML) or data definition language (DDL) statements are supported or ignored within a transaction, or whether such statements cause the transaction to be committed. All data manipulation operations on a row object are transacted.

In OLE DB 2.5, the following DML methods are used for updating columns:

  • IRowChange::SetColumns
  • IRowSchemaChange::AddColumns (when columns already exist)
  • IRowSchemaChange::DeleteColumns (because the row-specific columns are not deleted until the row or rowset is released)

The following DDL methods are used for updating schemas:

  • ICreateRow::CreateRow
  • IRowSchemaChange::AddColumns (when columns do not already exist)

In OLE DB 2.5, scoped operations (such as IScopedOperations::Copy, Delete, and Move) are considered neither DML nor DDL.


Provider and Consumer Support for Row Objects

A provider that supports row objects indicates additional support by setting, in the DBPROP_OLEOBJECTS property, the flags described in the following table.

Flag Support
DBPROPVAL_OO_ROWOBJECT
  • Row objects support the mandatory IRow, IGetSession, IColumnsInfo, and IConvertType interfaces.
  • Rowset objects support the IGetRow interface.
  • Providers that support direct binding support binding to row objects with IBindResource::Bind and optionally ICreateRow::CreateRow.
DBPROPVAL_OO_SINGLETON
  • ICommand::Execute and IOpenRowset::OpenRowset can return row objects.
  • The provider can choose to support the return of row objects by methods in other interfaces that traditionally return rowsets, such as IColumnsRowset, IViewRowset, and IDBSchemaRowset.
DBPROPVAL_OO_SCOPED
  • Row objects support the IScopedOperations interface.


Stream Objects

It is a common scenario for a consumer to bind to a row object, access its columns, and open its default stream, if it has one. This can be done using an OLE DB stream object, which is an OLE DB object that exposes the COM IStream interface. A row object can contain more than one stream object, one of which can be designated as the default stream for the row.

The OLE DB stream object cotype is defined as follows. (For more information about cotypes, see "Conceptual Programming Models in OLE DB" in Chapter 1: Overview of OLE DB.)

Note ADO 2.5 requires support for the IStream interface.

CoType TStream {
   [mandatory]   interface ISequentialStream;
   [optional]    interface IConnectionPointContainer;
   [optional]    interface IDBAsynchStatus;
   [optional]    interface IDBInitialize;
   [optional]    interface IGetSourceRow;
   [optional]    interface ISupportErrorInfo;
   [optional]    interface IStream;
}

All stream objects must expose the ISequentialStream interface, which is a subset of the COM IStream interface that provides forward-only reading and writing of data.

The provider can designate one stream-valued column as the default stream of the row object. DBROWCOL_DEFAULTSTREAM is provided as a shortcut column ID (DBID) on a row object to identify the column, if any, that is the default stream of that row. For more information about the default stream column and DBROWCOL_DEFAULTSTREAM, see "Special Row Column DBIDs," earlier in this chapter.

To obtain the source row object for a default stream object, the consumer calls IGetSourceRow::GetSourceRow on the default stream object. To open the default stream object for a row object, the consumer calls IRow::Open on the row object, passing in either the column ID (DBID) of the default stream column or the shortcut value DBROWCOL_DEFAULTSTREAM.

When binding to a row, the default behavior of IBindResource::Bind, ICreateRow::CreateRow, and IScopedOperations::Bind is to fetch the default stream as well as the columns of the row object. To override this behavior, the consumer can specify bind flag DBBINDURLFLAG_DELAYFETCHSTREAM to delay access to the default stream until it is explicitly opened, or the consumer can specify DBBINDURLFLAG_DELAYFETCHCOLUMNS to delay access to row columns until they are explicitly accessed. The absence of either bind flag signals the consumer's intent to immediately open the default stream or access the columns of the row. To minimize network round-trips, the provider should immediately instantiate the default stream object and fetch its contents or download the columns of the row.


Modeling Tree-Structured Namespaces

The chapter model in OLE DB assumes that each level of a hierarchy can be modeled by a single rowset. While this is a natural and efficient way to model homogeneous hierarchies (such as a Customers->Orders->OrderDetails hierarchy), it becomes a limitation when dealing with more general hierarchies that arise in tree-structured namespaces. For example, an e-mail inbox is a collection of items. Each item can be of a different type, such as an e-mail message, a calendar entry, a journal entry, or a folder. Therefore, to model hierarchies of heterogeneous collections in OLE DB requires more flexibility than rowset objects alone can provide.

In OLE DB 2.5, this can be done using a combination of row and rowset objects. When row objects are used to model tree-structured namespaces, each node in the tree can be modeled by a row object, which belongs to the rowset of its siblings. One of the columns in this row object can be the rowset that contains all the children of this node. In this situation, the row object is also used as a scoping mechanism. Operations performed on the row object apply to the subtree rooted at the node represented by the row object.

In working with tree-structured namespaces, there is a frequent need to perform move, copy, or delete operations on the subtree rooted at the current node. For example, a copy operation might need to copy not only the given node but the entire subtree rooted at that node.

The row object has an optional interface, IScopedOperations, with methods that allow data modification and navigation operations to be performed within the scope of a given node (row object). IScopedOperations is derived from IBindResource.


Direct Binding in Tree-Structured Namespaces

Navigation in tree-structured namespaces is done through row and rowset objects named by URLs. In this context, a URL identifies a particular node in the tree. Given a URL identifying such a node, a consumer can bind directly to the following:

  • The row object representing this node.
  • The rowset containing the children of the node. (This provides a shortcut alternative to binding to the row object and then calling IScopedOperations::OpenRowset.)

When a consumer binds from the session object by using IBindResource, all specified bind options are propagated to any rowset, row, or stream object that is eventually created.

Inheritance of Bind Flags from the Row Object

If the consumer passes zero as the value for dwBindURLFlags when calling IBindResource::Bind on an existing row object, no flags are set, so the binding flags specified on the previous bind to the row object are reused. If any bind flags are set in the current bind request, none of the previous flag values are inherited. This is illustrated in the following example:

  1. Bind to http://example from the root binder with DBBINDURLFLAG_WRITE, obtaining row object A.

    Effective bind flag: DBBINDURLFLAG_WRITE

  2. Bind from row object A to http://example/subdirectory1 with no new bind flag (DBBINDFLAG) values specified.

    Effective bind flag: DBBINDURLFLAG_WRITE

  3. Bind from row object A to http://example/subdirectory2, specifying DBBINDURLFLAG_SHARE_DENY_WRITE.

    Effective bind flag: DBBINDURLFLAG_SHARE_DENY_WRITE

If the consumer specifies one or more bind flag values in dwBindURLFlags when calling IBindResource::Bind on the row object, the following rules govern the use of these flags on subsequent binds from the row object, based on the flags specified when binding to the row object:

  • If DBBINDURLFLAG_RECURSIVE is specified on the bind to the row object, it must also be specified on subsequent binds from the row object in which dwBindURLFlags is not zero.
  • If DBBINDURLFLAG_RECURSIVE and DBBINDURLFLAG_SHARE_DENY_READ and/or DBBINDURLFLAG_SHARE_DENY_WRITE are specified on the bind to the row object, these flags must also be specified on subsequent binds from the row object in which dwBindURLFlags is not zero. If only one of the SHARE_DENY flags was specified on the bind to the row, the client can use both in binds from the row.
  • If DBBINDURLFLAG_SHARE_DENY_NONE is specified on the bind to the row, the client may choose to specify either or both SHARE_DENY_READ and SHARE_DENY_WRITE on binds from the row object. DBBINDURLFLAG_SHARE_DENY_NONE cannot be legally combined with DBBINDURLFLAG_RECURSIVE unless other locking behaviors are requested. Therefore, if DBBINDURLFLAG_RECURSIVE was not specified on the bind to the row, the client is free to use it on binds from the row. If it was specified, the client is constrained by other rules in this list (because another locking behavior must have been requested as well).

Binding to Default Streams

Two important data stores have tree-structured namespaces: e-mail systems and file systems. For these data stores, the nodes in the tree are usually associated with a default stream that contains the contents of the file or the e-mail message as a binary stream. Consumers frequently find it useful to take a URL that represents the file or the e-mail and then bind to the default stream. In fact, in these scenarios, a given URL string can be modeled by both a row object (which models the properties of the name element) and a stream object (which models the default contents of the element). Thus, the URL of the default stream is the same as the URL of the row.

OLE DB 2.5 provides two ways to handle this scenario. First, the consumer can bind directly to the default stream represented by a URL, using IBindResource::Bind or ICreateRow::CreateRow. In this scenario, no row object is created.

Second, the consumer can create a row object using IBindResource::Bind, ICreateRow::CreateRow, or IScopedOperations::Bind. On the row object, the default stream column is identified by a special constant DBID. The consumer passes this DBID as an argument to the IRow::Open method to get the default stream. The stream object has an interface called IGetSourceRow, which has a method called GetSourceRow. This method can be used to get an interface pointer on the containing row object of the stream.


Navigating Tree-Structured Namespaces

It is often useful for consumers to navigate the subtree rooted at a given node. This can be done through the row object for the node if one column of the row object is a rowset with rows that represent the children of the node. (Whether this is the case is provider-specific.) To open this rowset, the consumer calls IScopedOperations::OpenRowset on the row object. To navigate to a child node, the consumer calls IBindResource::Bind from the row object to the resource represented by the appropriate row in the rowset of child nodes.


Scoped Tree Manipulation Operations

Consumers can perform scoped operations by calling IScopedOperations methods.

IScopedOperations contains the methods Move, Copy, and Delete, which perform move, copy, and delete operations on the tree (or subtrees, using absolute or relative URLs) rooted at a given node. Each method can operate on multiple nodes in a single invocation. Providers that implement IScopedOperations on row objects must at a minimum support Copy, Delete, and Move for source and destination URLs within the scope of the subtree whose root is the node that is bound to the current row object. Whether these operations can be performed on source and destination URLs outside the scope of the current row object is provider-specific.

When a tree or subtree is moved or copied to a destination tree or subtree, the provider might need to generate the suffix of the destination URL. The data source property DBPROP_GENERATEURL indicates whether the provider generates URL suffixes or whether the consumer must supply the complete destination URL.


Resource Rowsets and Document Source Providers

OLE DB defines a special class of providers, called document source providers, that manage access to folders and documents. A provider indicates that it is a document source provider by setting the value of the DBPROP_DATASOURCE_TYPE property to DBPROPVAL_DST_DOCSOURCE.

A consumer can expect a document source provider to exhibit the following behavior:

  • When binding to a collection and requesting a rowset, the resource rowset defines the shape of that rowset.
  • When creating a new row via ICreateRow::CreateRow, the container class is defined to be a folder and the default columns of the new row are the columns of the resource rowset.

For more information about the column attributes associated with DBPROP_DATASOURCE_TYPE, see DBPROP_DATASOURCE_TYPE in Appendix C: OLE DB Properties.


Chapter 10: Index Rowsets

This chapter discusses the interfaces, use, and structure of an index rowset. An OLE DB index, also known as an index rowset, is a rowset built over an index in a data store. It is generally used in conjunction with a rowset built over a base table in the same data store. 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. Index structures can be either of two types: one or more columns that form a key value, or a column containing a bookmark. This chapter also includes an index example.

For more information on | Go to
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
Index example | "Index Example" in this chapter

The index object cotype is defined as follows. For more information about cotypes, see "Conceptual Programming Models in OLE DB" in Chapter 1: Overview of OLE DB.

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 IRowsetCurrentIndex;
   [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 the following: 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, taken together, uniquely identify a row. The following are examples of SQL queries whose execution plans can exploit the capabilities of the IRowsetIndex interface:
    • Retrieving rows ordered by some column. For example where x is indexed:
      SELECT * FROM Table ORDER BY Table.x
    • 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, where R.x is indexed:
      SELECT DISTINCT R.x FROM R
    • 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 types of information: one or more columns that form a key value used to search in the index rowset, and a column containing a bookmark. 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 iOrdinal values returned by IColumnsInfo::GetColumnInfo. The consumer can determine which columns are key columns from the DBINDEXCOLUMNDESC structure returned by IRowsetIndex::GetIndexInfo.

Note The DBTYPE of the column exposed by the index rowset must be the same as the DBTYPE of the corresponding column in the base table rowset.

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 must perform the following actions:

  1. Create an instance of a rowset over a base table and create an index rowset over an index on the same base table.
  2. Call 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. IColumnsInfo::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 IRowsetIndex::GetIndexInfo.

  3. (Optional) Call 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. Call 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. If the consumer did not perform step 3, Seek will search the entire index.
  5. Call 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. If the consumer did not perform step 3, GetNextRows can traverse the entire index.
  6. 6Call IRowset::GetData on the index rowset for each fetched row to get the bookmarks pointing to the base table rowset.
  7. Call IRowsetLocate::GetRowsByBookmark on the base table rowset to retrieve the rows in that rowset that correspond to the index rows fetched in step 5.

Because an index is a rowset, the consumer can treat it like any other rowset. Therefore, the consumer can update the index by calling IRowsetChange methods. However, the provider is not required to validate the consumer's updates to the index.


Index Example

The code in this example 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 bytes 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 will perform the following actions:

  1. Initialize the database and create a session (not shown).
  2. Obtain interfaces for the table and index. (Only the latter is shown.)
  3. Get information about the column types of the index rowset (not shown).
  4. Establish bindings for the table and index columns. (Only the latter is shown.)
  5. Create accessors for the table and index rowsets. (Only the latter is shown.)
  6. Read the index rowset and retrieve 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;

   // Code not shown. Initialize the database, create a session, and
   // obtain, from the session object, a pointer pIOpenRowset to an
   // IOpenRowset interface.

   // 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);

   // Code not shown: 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 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, &Bookmark, 0, NULL, DBRANGE_PREFIX);
   pIndex->Seek(hIndexAccBmk, 1, &Bookmark, 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, NULL);

   //
};


Chapter 11: 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 the following:

  • 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. A useful element of integrated indexes, 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 code example is provided.

For more information on | Go to
Nonintegrated indexes | Chapter 10: Index Rowsets
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 18: 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 Appendix B: Schema Rowsets.)

You can obtain integrated index behavior by opening a rowset on an integrated index—that is, passing valid table and index IDs to IOpenRowset::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 should do the following:

  1. Call IRowsetIndex::SetRange on the index rowset to set the range.
  2. (Optional) Call 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. Call IRowset::GetNextRows on the index rowset to fetch the index entry.
  4. Extract a bookmark from the index row that points to the corresponding row in the base table rowset.
  5. Call 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 should do the following:

  1. Call IRowsetIndex::SetRange on the base table rowset to set the range.
  2. (Optional) Call 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. Call 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 in the preceding section, "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.


Fetching Rows

Four areas of fetching rows are affected by integrating indexes and rowsets: rowset order, next fetch position, range, and holding rows.


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 IRowset::GetNextRows, this means that repeated calls will traverse the entire rowset in index order. For IRowsetLocate::GetRowsAt and IRowsetScroll::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 IRowset::GetNextRows and IRowset::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 IRowset::GetNextRows, IRowsetLocate::GetRowsAt, and IRowsetScroll::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. Therefore, 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. Pending updates to key columns might not appear in the correct position in the index until the update is transmitted to the data store.

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", if the change is transmitted to the data store, and if transmitted updates are visible in the index, 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 IRowsetIndex::SetRange and IRowsetIndex::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 can 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. Therefore, 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" in Chapter 10: Index Rowsets, 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. (Not shown.) Initializes the database and creates a session.
  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. 66Gets 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;

   // Code not shown. Initialize the database, create a session,
   // and obtain, from the session object, a pointer pIOpenRowset
   // to an IOpenRowset interface.

   // 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);

   // Code not shown. 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.

   // 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, NULL);

   // Release the rowset.
   pIRowsetIndex->Release();
   pIColumnsInfo->Release();
   pIAccessor->Release();
   pIRowset->Release();
};


Chapter 12: OLE DB Object Notifications

Notifications permit consumers to be informed of changes to objects, such as data source objects and rowsets, when these objects are shared with other consumers. When significant changes occur to the state of a shared object, the provider notifies all consumers that currently share access to the object. Support for notifications is provider-specific.

This chapter explains when notifications are sent and how to nest and group them. It also discusses ways for consumers to screen out unwanted notifications. To illustrate ways of issuing notifications, diagrams of states and state transitions in providers and consumers are provided.

For more information on | Go to
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 18: Programming Considerations


When Notifications Are Sent

Providers send notifications when changes occur to provider objects, such as rowset objects, that are shared by two or more consumers. For example, when several consumers share a rowset object and one of them changes the rowset by calling an IRowsetChange method, the provider can notify the other consumers in case they need to refresh their local copies of row data. To receive these notifications, consumers must implement IRowsetNotify and register it on a connection point with the provider, which calls IRowsetNotify methods to send notifications to consumers. The consumers then take on the roll of listeners, alert to notifications of changes to the object as sent out by the provider.

Note Providers do not send notifications when changes are attempted but fail.

Notifications also can be used to coordinate sets of changes performed by multiple consumers. Often such changes occur in phases, during which participating consumers make changes based on changes made by other consumers in previous phases.

Notifications can be nested. That is, sending one notification can cause a consumer to take actions that cause another notification to be sent.

Notifications can also be grouped. For example, providers that support notifications can be instructed to send only two notifications for an operation on multiple rowsets: one for all rows that succeeded and one for all rows that failed. If a provider supports grouping of notifications, it reports a value of DBPROPVAL_NT_MULTIPLEROWS for the property DBPROP_NOTIFICATIONGRANULARITY.

If an action by the consumer causes the provider to send multiple notifications, all of those notifications must be approved by the consumer that instigated the action 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 that made the call 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 single column, no columns are updated. For more information, see the entry for IRowsetChange::SetData in the reference section.

Consumers can screen out unwanted notifications of events by returning DB_S_UNWANTEDREASON or DB_S_UNWANTEDPHASE when the provider calls the consumer's IRowsetNotify methods to report the unwanted notifications. Other actions on a rowset will continue to send out notifications for all phases unless they are also specifically screened. Although providers are not required to stop notifying consumers of the unwanted notifications, they can choose, for performance reasons, not to send notifications of unwanted events.


States and State Transitions in Providers

The following diagram shows the events that take place within a rowset method, such as IRowsetChange::SetData, when the method is implemented in a provider that supports 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. These states are represented by the values of DBEVENTPHASE.
  • Preliminary Work, Permanent Work, and Undo Work are internal states representing work done by the provider between notifications.

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 shows the actions that can take place within a consumer that supports notifications through IRowsetNotify.

Consumer state/state transition diagram

States

  • Initial is a conceptual state denoting the initial state of any IRowsetNotify method before any phase starts. At this point, full-phased events such as DBREASON_COLUMN_SET can receive an OKTODO or FAILEDTODO notification. 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 method-transition states that occur when the method receives the phase having the same name as the respective state. All these states can involve some internal work by the consumer. All states, except DIDEVENT, can 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) on reception of a FAILEDTODO notification. 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.


Chapter 13: Data Types in OLE DB

This chapter introduces OLE DB data type indicators. Providers use these type indicators to tell consumers what data types they support. Consumers use them when creating tables and when getting and setting data. Example mappings from native data types to OLE DB data type indicators are given for an ANSI SQL provider, a Microsoft® Access provider, and a Microsoft SQL Server provider. The treatment of string values and provider support for data type conversions are also discussed.

For more information on | Go to
Consumers and providers | "Conceptual Programming Models in OLE DB," 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 Server provider | "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-length and variable-length data | "Fixed-Length 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 Microsoft 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. For more information on how data types map to type indicators as well as a complete list of type indicators, see "Type Indicators" in Appendix A: Data Types.

Places where type indicators are used include 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.


Using Type Indicators in Providers

OLE DB defines a standard set of data type indicators for the most commonly used data types. Providers use these data type indicators to tell consumers what data types they support. Each provider maps its own underlying data types to these data type indicators and advertises its support for them through the PROVIDER_TYPES schema rowset. The underlying data types used by the provider 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 of the OLE DB data types listed in "Type Indicators" in Appendix A: Data Types, nor are they required to support all possible conversions among them. For information about what conversions all providers are required to support, see "Data Type Conversion," later in this chapter.

Although OLE DB attempts to provide a unified type system, it is subject to the following limitations:

  • 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 store–specific type names for parameters in text commands. For more information, see ICommandWithParameters::SetParameterInfo in the reference section.


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) | DBTYPE_STR
VARCHAR (n) | DBTYPE_STR
CLOB | DBTYPE_STR
DECIMAL (p, s) | DBTYPE_NUMERIC
NUMERIC (p, s) | DBTYPE_NUMERIC
BIT | DBTYPE_BOOL
TINYINT | DBTYPE_I1
SMALLINT | DBTYPE_I2
INTEGER | DBTYPE_I4
BIGINT | DBTYPE_I8
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 | DBTYPE_STR
image | DBTYPE_BYTES
smallmoney | DBTYPE_CY
money | DBTYPE_CY
user-defined-type | DBTYPE_UDT


Using Type Indicators in Consumers

Consumers use type indicators when creating and altering tables and when getting and setting data. To determine which data types are supported by the provider, the consumer calls IDBSchemaRowset::GetRowset, passing DBSCHEMA_PROVIDER_TYPES as a parameter. This method returns the provider's predefined PROVIDER_TYPES schema rowset, which contains detailed information about the data types that are supported by the provider. For more information about the PROVIDER_TYPES schema rowset, see "PROVIDER_TYPES Rowset" in Appendix B: Schema Rowsets.


Using Type Indicators When Creating Tables

Consumers can create tables in two ways. One is to call ICommand::Execute on a command object containing an SQL CREATE TABLE statement as its command text. (For more information, see Chapter 3: Commands.) The other is to call either ITableDefinition::CreateTable or ITableDefinitionWithConstraints::CreateTableWithConstraints. When creating a table by calling ITableDefinition or ITableDefinitionWithConstraints methods, the consumer creates a DBCOLUMNDESC structure whose elements specify the columns of the table. The wType and pwszTypeName elements of this structure correspond to the DATA_TYPE and TYPE_NAME column values from the PROVIDER_TYPES schema rowset. A consumer is guaranteed to always get a safe conversion when getting or setting values of the column by using the type indicator specified in wType.

For example, a provider built on top of an SQL-92–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.

For more information about the DBCOLUMNDESC structure, see "ITableDefinition::CreateTable" in the reference section.


Using Type Indicators When Getting and Setting Data

Most consumers, when they allocate memory in which to get or set data, 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 by GetColumnInfo, 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 conversion between that type and the type returned by GetColumnInfo. For more information, see "Data Type Conversion," later in this chapter.


String Data

The term string is used collectively to refer to ANSI strings, Unicode strings, and BSTRs. When used as metadata—for example, 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 as an in-memory string rather than through a storage object, the provider always returns a null-terminated string to the consumer. If the consumer's buffer is smaller than the sum of the length of the character data plus the length of the null-termination character, the provider truncates the data and adds a null-termination character to the truncated data. String data retrieved through a storage object is not null-terminated.

Note Consumers cannot assume that multibyte character set (MBCS) string data was truncated at a character boundary.

To ensure that strings returned by the provider are not truncated, consumers should allocate space for a 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 5-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 6-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 space for the null-termination character, 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.

Note It is an error to set cbMaxLen to less than the size of the null-termination character.

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 can be obtained by calling SysStringLength. For more information, see "Type Indicators" in Appendix A: Data Types.


Fixed-Length and Variable-Length Data Types

The provider determines how much memory to allocate for each data value based on its data type indicator. For fixed-length data types, such as Booleans and integers, the provider simply allocates enough space to hold one item of that type. For variable-length data types, the provider must rely on the consumer to specify how much memory must be allocated for the data. The consumer does this by setting the value of cbMaxLen appropriately.

When the length part of a binding is applied to a column, the length includes all bytes of the value. It should be the same as the length passed to IMalloc to allocate a buffer big enough to hold the data value. For strings, this length must include space for a null-termination character. If the value is to be converted, the length should be the length in bytes that the value has after it is converted into the type specified by the consumer.

If a variable-length string value is copied into a buffer that is too small to hold it, the string is truncated abruptly to fit the buffer. 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 lost when the string was truncated.

Note Variable-length numeric data values are not truncated. Attempting to copy a variable-length numeric value into a buffer that is too small to hold the entire value is a programming error.

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 modifiers do affect how data is stored and how cbMaxLen is used 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 Types.


Data Type Conversion

Providers must be able to convert bound data values as follows:

  • To the native 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: Data Types. 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: Data Types.


Chapter 14: Properties

This chapter defines and discusses properties, property sets, and property groups. 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 description of these structures and types.

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 and whether or not the rowset exposes bookmarks.

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 object 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.

For more information on | Go to
Rowsets | Chapter 4: Rowsets
Data source object | "Data Source Objects" in Chapter 2: Data Source and Session Objects
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 can be associated with an OLE DB object, such as a data source object, or with an entity that is not directly represented as an OLE DB object, such as a table or column. Every OLE DB property belongs to a property set. Each property is identified by its property set's GUID and an integer property ID that is unique within the property set. Taken together, the property set's GUID and the property ID uniquely identify 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.

For a complete list of the OLE DB property sets, see Property Sets in Appendix C: OLE DB Properties.

Each property belongs to a property group. A property group is a set of logically related properties that could apply to an OLE DB object, such as a rowset or a data source object, to an operation on an OLE DB object, or to an object that is not directly represented as an OLE DB object. Property groups are identified by a DBPROPFLAGS value that is returned in the DBPROPINFO structure. OLE DB defines property groups. Providers cannot define their own property groups.

For a complete listing and description of the OLE DB property groups, see Properties by Property Groups in Appendix C: OLE DB Properties. For information about how a consumer determines which group a property is in, see "Getting Information about Properties," later in this chapter.

Note Property sets and property groups overlap. That is, a property group can contain properties from more than one property set.


Setting Property Values

To set property values, a consumer must perform the following actions:

  1. Determine the properties for which to set values. To set the value of a property to a different value, the property must be settable. However, consumers can set the value of a read-only property to its default value. For information about how to determine if a property is settable, see "Getting Information About Properties," later in this chapter.
  2. Determine the property sets that contain the identified properties.
  3. Allocate an array of DBPROPSET structures, one for each identified property set.
  4. Allocate 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. Store the property's value in that property's DBPROP structure, along with a flag showing whether it is required or optional.

    If the provider cannot satisfy a required property, it fails to create the object to which the property applies and the object-creation method returns DB_E_ERRORSOCCURRED. If the provider cannot set the value of an optional property, it creates the object to which the property applies and the method returns 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. Otherwise, the consumer sets the value of colid to DB_NULLID.

    Note In cases where the colid element in the DBPROP structure for specified properties is ignored, the consumer must nevertheless set the value of colid to DB_NULLID. Not to do so is a programming error.

  6. For each property set, place the property set GUID in the DBPROPSET structure. It then places in the DBPROPSET structure the count of elements in, as well as the pointer to, the corresponding DBPROP array.
  7. Call a method that sets properties and pass 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 structures shown in the following illustration.

An example of setting property values

Note 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.

The following table lists methods consumers can call 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,
IDBDataSourceAdmin::CreateDataSource
Index | IIndexDefinition::CreateIndex
Rowset IColumnsRowset::GetColumnsRowset,
ICommandProperties::SetProperties,
IDBSchemaRowset::GetRowset,
IOpenRowset::OpenRowset,
ISourcesRowset::GetSourcesRowset,
ITableDefinition::CreateTable
Session | ISessionProperties::SetProperties
Table ITableDefinition::CreateTable,
IAlterTable::AlterTable

The return code generated by a property-setting method indicates how far the method has succeeded. Whether a success (S_) or an error (E_) return code is generated depends on factors such as whether the properties that failed to be set were required and on whether the method is SetProperties or a method that performs other actions in addition to setting properties. These factors are summarized in the following table.

Note Generally, consumers should not set the same property twice, because if they do, the value of the property is provider-specific, meaning it is unknown to the consumer. The status for the successfully set value should be DBPROPSTATUS_OK, but it is provider-specific what the status value is for the other property values.

Success or failure case | Return code: SetProperties | Return code: other methods
All properties are optional, and all are set successfully. | S_OK | S_OK
All properties are optional, and one or more (but not all) fail to be set. | DB_S_ERRORSOCCURRED | DB_S_ERRORSOCCURRED
All properties are optional, and all fail to be set. | DB_E_ERRORSOCCURRED | DB_S_ERRORSOCCURRED
All properties are required, and all are set successfully. | S_OK | S_OK
All properties are required, and one or more (but not all) fail to be set. | DB_S_ERRORSOCCURRED | DB_E_ERRORSOCCURRED
All properties are required, and all fail to be set. | DB_E_ERRORSOCCURRED | DB_E_ERRORSOCCURRED
Not all properties are required, and all are set successfully. | S_OK | S_OK
Not all properties are required, all required properties are set successfully, and one or more optional properties fail to be set. | DB_S_ERRORSOCCURRED | DB_S_ERRORSOCCURRED
Not all properties are required, and one or more (but not all) required properties fail to be set. | DB_S_ERRORSOCCURRED | DB_E_ERRORSOCCURRED
Not all properties are required, and all fail to be set. | DB_E_ERRORSOCCURRED | DB_E_ERRORSOCCURRED


Getting Property Values

To get property values, a consumer must perform the following actions:

  1. Determine the properties for which to get values.
  2. Determine the property sets that contain the identified properties.
  3. Allocate an array of DBPROPIDSET structures, one for each identified property set.
  4. Allocate an array of DBPROPIDs for each property set. The number of elements in the array is determined by the number of properties identified in step 1 that belong to that property set.
  5. Store the ID of each property in the appropriate DBPROPID array.
  6. Store the GUID for each property set in the appropriate DBPROPIDSET structure. The consumer then stores a pointer to the corresponding DBPROPID array, along with the number of elements in the array, in the same DBPROPIDSET structure.
  7. Call a method that gets properties and pass it the count and array of DBPROPIDSET structures.

To return property values, the provider must perform the following actions:

  1. Allocate 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, allocate an array of DBPROP structures. The elements of the DBPROP array correspond to the elements of the DBPROPID array.
  3. Store 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 that property.
  4. Store the GUID for each property set in the appropriate DBPROPSET structure. The provider then stores a pointer to the corresponding DBPROP array, along with the number of elements in the array, in the same DBPROPSET structure.
  5. Return the number of DBPROPSET structures, along with the array of DBPROPSET structures itself, 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 structures shown in the following figure.

An example of how a consumer gets property values

The provider returns the structures shown in the following illustration.

An example of what property values a provider returns

The following table lists methods consumers can call to get property values.

Property group | Methods used to get property values
Data Source | IDBProperties::GetProperties
Data Source Information | IDBProperties::GetProperties
Index | IRowsetIndex::GetIndexInfo
Initialization | IDBProperties::GetProperties
Rowset ICommandProperties::GetProperties,
IRowsetInfo::GetProperties
Session | ISessionProperties::GetProperties


Getting Information About Properties

To get information about properties, a consumer can perform the following actions:

  1. Determine the properties for which to get information.
  2. Determine the property sets that contain the identified properties.
  3. Allocate an array of DBPROPIDSET structures, one for each identified property set.
  4. Allocate an array of DBPROPIDs for each property set. The number of elements in the array is determined by the number of properties identified in step 1 that belong to that property set.
  5. Store the ID of each property in the appropriate DBPROPID array.
  6. Store the GUID for each property set in the appropriate DBPROPIDSET structure. The consumer then stores a pointer to the corresponding DBPROPID array, along with the number of elements in the array, in the same DBPROPIDSET structure.
  7. Call a method that gets properties and pass it the count and array of DBPROPIDSET structures.

To return property values, the provider can perform the following actions:

  1. Allocate 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, allocate an array of DBPROPINFO structures. The elements of the DBPROPINFO array correspond to the elements of the DBPROPID array.
  3. Store 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 correspond to the DBPROPID value for that property. For properties that do not apply to columns, the provider sets the column ID to NULL_ID.
  4. Store the GUID for each property set in the appropriate DBPROPINFOSET structure. The provider then stores a pointer to the corresponding DBPROPINFO array, along with the number of elements in the array, in the same DBPROPINFOSET structure.
  5. Return the number of DBPROPINFOSET structures, along with the array of DBPROPINFOSET structures itself, 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 structures shown in the following illustration.

An example of how a consumer gets information about properties

The provider returns the structures shown in the following illustration.

An example of what property information a provider returns

The following table lists methods consumers can call to get information about properties.

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.

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, one for each property in the property set, is passed in a DBPROPSET structure. Each DBPROPSET structure represents one property set.

The DBPROP structure is defined as follows:

typedef struct tagDBPROP {
   DBPROPID        dwPropertyID;
   DBPROPOPTIONS   dwOptions;
   DBPROPSTATUS    dwStatus;
   DBID            colid;
   VARIANT         vValue;
} DBPROP;

The elements of this structure are described in the following table.

Element | Description
dwPropertyID | The ID of the property.
dwOptions | The options for this property. For example, when setting a property, the consumer can specify that the property is optional; therefore, the provider should continue even if it cannot satisfy the property. Alternatively, the consumer can specify that a property is required; therefore, the provider should fail if it cannot satisfy the property.
dwStatus The status returned by the provider to indicate success or failure in setting or getting the property value. This value is ignored on input and need not be set by the consumer.

For properties the provider does not support, the provider returns DBPROPSTATUS_NOTSUPPORTED in this element and 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, unless the DBPROPFLAGS_COLUMNOK flag is returned for the property in the dwFlags element of the DBPROPINFO structure.

Note The consumer is required to set colid to a valid DBID value or to DB_NULLID, even if the actual value of colid is ignored by the provider. Not setting colid to a valid value or to DB_NULLID is a programming error.
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.

Note The consumer is required to set vValue to a valid VARIANT value or to VT_EMPTY, even if the actual value of vValue is ignored by the provider. Not setting vValue to a valid value or to VT_EMPTY is a programming error.


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 described in the following table. These flags can be combined except as noted.

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 following flags are mutually exclusive:

DBPROPFLAGS_COLUMN
DBPROPFLAGS_DATASOURCE
DBPROPFLAGS_DATASOURCECREATE
DBPROPFLAGS_DATASOURCEINFO
DBPROPFLAGS_DBINIT, DBPROPFLAGS_INDEX
DBPROPFLAGS_ROWSET
DBPROPFLAGS_SESSION
DBPROPFLAGS_TABLE

For a complete listing and description of the OLE DB property groups, see Properties by Property Groups in Appendix C: OLE DB Properties.
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 to 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.
DBPROPFLAGS_READ | A method can get the property's value. For a list of methods that get properties, see "Getting Property Values," earlier 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," earlier in this chapter.
DBPROPFLAGS_REQUIRED The property must be set.

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 this flag is set, the consumer must specify a value for the property before calling IDBInitialize::Initialize to initialize the data source or enumerator.

Whether or not a property is required for a particular provider can vary depending on other properties specified (for example, DBPROP_DATASOURCE) or on other provider-specific factors. Providers should mark as required only those properties that are always required for that instance of the data source object. 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 object 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 object can be created even if the consumer does not specify a value for the property.
DBPROPFLAGS_VIEW | The property belongs to the View property group.
DBPROPFLAGS_STREAM | The property belongs to the Stream property group.


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. All property ID values must be greater than 1. The values 0 and 1 are reserved for compatibility with COM property IDs.

The DBPROPID type is defined as follows:

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 described in the following table.

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 described in the following table.

Element | Description
pwszDescription A text description of the property. For properties defined by OLE DB, this must be the description listed for the property in Appendix C: OLE DB Properties. This property description is used to construct connection strings and must meet all of the following requirements in order to work correctly with the Data Source Locator:
  • All property descriptions must be unique.
  • All property descriptions are NOT to be localized.
  • Property descriptions must not contain the equal sign or single or double quotation marks. However, they may contain semicolons.

All OLE DB properties, including provider-specific properties, must have non-NULL descriptions.
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," earlier 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. Normally this list is returned only if the property can be set and if one of the following conditions apply:
  • The property type is an integer, and the possible values of the property are an enumerated type.
  • The property type is an integer whose bits are to be read as a bitmask, and all supported bits are set.
  • 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 the COM function 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 described in the following table.

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.

Providers should return the same DBPROPOPTIONS value for GetProperties that the consumer specified in SetProperties. In particular, providers must not return failure because a property, set as DBPROPOPTIONS_OPTIONAL, could not be satisfied; and providers must not return success without satisfying all properties set as DBPROPOPTIONS_REQUIRED. Properties that have not been set by the consumer are usually returned as DBPROPOPTIONS_OPTIONAL.

The values of DBPROPOPTIONS have the following meanings.

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 object or creating a rowset, fails.

If the provider supports the specified property, all methods except ICommandProperties::SetProperties or IDBProperties::SetProperties can determine whether the provider supports setting the property to the requested value. For these methods, if the provider does not support setting the property to the requested 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.

For ICommandProperties::SetProperties or IDBProperties::SetProperties, if the provider supports the specified property and SetProperties determines that the provider does not support setting the property to the requested value, it sets dwStatus to DBPROPSTATUS_NOTSUPPORTED and returns DB_S_ERRORSOCCURRED if one or more properties passed to SetProperties were successfully set, or DB_E_ERRORSOCCURRED if none of the properties could be set. If the provider supports the specified property but SetProperties cannot determine whether the provider supports the requested value, it sets dwStatus to DBPROPSTATUS_OK. When ICommand::Execute or IDBInitialize::Initialize is called, the provider determines whether it can support the requested value. If the provider cannot support the requested value, ICommand::Execute or IDBInitialize::Initialize returns DB_E_ERRORSOCCURRED.
DBPROPOPTIONS_OPTIONAL The specified property value is optional. 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 object or creating a rowset, succeeds.

If the provider supports the specified property, all methods except ICommandProperties::SetProperties or IDBProperties::SetProperties can determine whether the provider supports setting the property to the requested value. For these methods, 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 object or creating a rowset, succeeds.

For ICommandProperties::SetProperties or IDBProperties::SetProperties, if the provider supports the specified property and SetProperties cannot determine whether the provider supports setting the property to the requested value, it sets dwStatus to DBPROPSTATUS_OK. ICommandProperties::GetProperties or IDBProperties::GetProperties returns the specified value in vValue and DBPROPOPTIONS_OPTIONAL in dwOptions. When ICommand::Execute or IDataInitialize::Initialize is called, the provider determines whether it can support the specified value. If the provider does not support setting the property to the requested value, ICommand::Execute or IDataInitialize::Initialize returns DB_S_ERRORSOCCURRED but successfully executes the command or initializes the data source object.


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 described in the following table.

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.

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 settable property, but the provider does not support the value specified by vValue. This includes the case where the value was added to the property in OLE DB after the provider was written. (See DBPROPSTATUS_NOTSUPPORTED in this table.)
DBPROPSTATUS_CONFLICTING The property's value was not set because doing so would have conflicted with an existing property.

This value is returned when a property is set that conflicts with the read-only state of a rowset—for example, a schema rowset.
DBPROPSTATUS_NOTALLSETTABLE | A property was specified to be applied to all columns but could not be applied to one or more of them.
DBPROPSTATUS_NOTAVAILABLE | (Reserved)
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 was not 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. This value is also returned if a settable column property could not be set for the particular column.
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 can return DBPROPSTATUS_NOTSUPPORTED for properties that do not apply to the provider. For example, a read-only provider might always return DBPROPSTATUS_NOTSUPPORTED if the consumer called IDBProperties::GetPropertyInfo for DBPROP_UPDATABILITY.

Note Some providers return DBPROPSTATUS_NOTSUPPORTED in cases where the value in vValue was a valid value for the property and the provider supports the property as a settable property but the provider does not support the value specified by vValue. Although this is not a programming error, providers are encouraged to return DBPROPSTATUS_BADVALUE for all such cases. This is partly for forward compatibility. When new values are added to an existing property in OLE DB, existing providers should return DBPROPSTATUS_BADVALUE for those values (rather than DBPROPSTATUS_NOTSUPPORTED, which might give the false impression that the property is no longer supported).


Property Example

The code in this example shows how to get the value of a property and how to add properties to a rowset.

/////////////////////////////////////////////////////////////////
// myGetProperty
//
// This function gets the BOOL value for the specified property
// and returns the result in *pbValue.
//
/////////////////////////////////////////////////////////////////
HRESULT myGetProperty
   (
   IUnknown *             pIUnknown,
   REFIID                 riid,
   DBPROPID               dwPropertyID,
   REFGUID                guidPropertySet,
   BOOL *                 pbValue
   )
{
   HRESULT                hr;
   DBPROPID               rgPropertyIDs[1];
   DBPROPIDSET            rgPropertyIDSets[1];

   ULONG                  cPropSets        = 0;
   DBPROPSET *            rgPropSets       = NULL;

   IDBProperties *        pIDBProperties   = NULL;
   ISessionProperties *   pISesProps       = NULL;
   ICommandProperties *   pICmdProps       = NULL;
   IRowsetInfo *          pIRowsetInfo     = NULL;

   // Initialize the output value.
   *pbValue = FALSE;

   // Set up the property ID array.
   rgPropertyIDs[0] = dwPropertyID;

   // Set up the Property ID Set.
   rgPropertyIDSets[0].rgPropertyIDs       = rgPropertyIDs;
   rgPropertyIDSets[0].cPropertyIDs        = 1;
   rgPropertyIDSets[0].guidPropertySet     = guidPropertySet;

   // Get the property value for this property from the provider, but
   // don't try to display extended error information, since this may
   // not be a supported property. A failure is, in fact, expected if
   // the property is not supported.
   if( riid == IID_IDBProperties )
   {
      XCHECK_HR(hr = pIUnknown->QueryInterface(IID_IDBProperties,
                (void**)&pIDBProperties));
      CHECK_HR(hr = pIDBProperties->GetProperties(
               1,                     // cPropertyIDSets
               rgPropertyIDSets,      // rgPropertyIDSets
               &cPropSets,            // pcPropSets
               &rgPropSets));         // prgPropSets
   }
   else if( riid == IID_ISessionProperties )
   {
      XCHECK_HR(hr = pIUnknown->QueryInterface(IID_ISessionProperties,
                (void**)&pISesProps));
      CHECK_HR(hr = pISesProps->GetProperties(
               1,                     // cPropertyIDSets
               rgPropertyIDSets,      // rgPropertyIDSets
               &cPropSets,            // pcPropSets
               &rgPropSets));         // prgPropSets
   }
   else if( riid == IID_ICommandProperties )
   {
      XCHECK_HR(hr = pIUnknown->QueryInterface(IID_ICommandProperties,
                (void**)&pICmdProps));
      CHECK_HR(hr = pICmdProps->GetProperties(
               1,                     // cPropertyIDSets
               rgPropertyIDSets,      // rgPropertyIDSets
               &cPropSets,            // pcPropSets
               &rgPropSets));         // prgPropSets
   }
   else
   {
      XCHECK_HR(hr = pIUnknown->QueryInterface(IID_IRowsetInfo,
                (void**)&pIRowsetInfo));
      CHECK_HR(hr = pIRowsetInfo->GetProperties(
               1,                     // cPropertyIDSets
               rgPropertyIDSets,      // rgPropertyIDSets
               &cPropSets,            // pcPropSets
               &rgPropSets));         // prgPropSets
   }

   // Return the value for this property to the caller if
   // it's a VT_BOOL type value, as expected.
   if( V_VT(&rgPropSets[0].rgProperties[0].vValue) == VT_BOOL )
      *pbValue = V_BOOL(&rgPropSets[0].rgProperties[0].vValue);

CLEANUP:
   if( rgPropSets )
   {
      CoTaskMemFree(rgPropSets[0].rgProperties);
      CoTaskMemFree(rgPropSets);
   }
   if( pIDBProperties )
      pIDBProperties->Release();
   if( pISesProps )
      pISesProps->Release();
   if( pICmdProps )
      pICmdProps->Release();
   if( pIRowsetInfo )
      pIRowsetInfo->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////
// myAddProperty
//
// This function initializes the property structure pProp.
//
/////////////////////////////////////////////////////////////////
void myAddProperty
   (
   DBPROP *           pProp,
   DBPROPID           dwPropertyID,
   VARTYPE            vtType,
   LONG               lValue,
   DBPROPOPTIONS      dwOptions
   )
{
   // Set up the property structure.
   pProp->dwPropertyID    = dwPropertyID;
   pProp->dwOptions       = dwOptions;
   pProp->dwStatus        = DBPROPSTATUS_OK;
   pProp->colid           = DB_NULLID;
   V_VT(&pProp->vValue)   = vtType;

   // Since VARIANT data is a union, we can place the value in any
   // member (except for VT_DECIMAL, which is a union with the whole
   // VARIANT structure -- but we know we're not passing VT_DECIMAL).
   V_I4(&pProp->vValue) = lValue;
}
/////////////////////////////////////////////////////////////////
// myAddRowsetProperties
//
// This function sets up the given DBPROPSET and DBPROP
// structures, adding two optional properties that describe
// features that we would like to use on the Rowset created
// with these properties applied:
//  - DBPROP_CANFETCHBACKWARDS -- the rowset should support
//     fetching rows backwards from our current cursor position.
//  - DBPROP_IRowsetLocate -- the rowset should support
//     the IRowsetLocate interface and its semantics.
//
/////////////////////////////////////////////////////////////////
void myAddRowsetProperties(DBPROPSET* pPropSet, ULONG cProperties, DBPROP* rgProperties)
{
   // Initialize the property set array.
   pPropSet->rgProperties      = rgProperties;
   pPropSet->cProperties       = cProperties;
   pPropSet->guidPropertySet   = DBPROPSET_ROWSET;

   // Add the following two properties (as OPTIONAL) to the property
   // array contained in the property set array in order to request
   // that they be supported by the rowset we will create. Because
   // these are optional, the rowset we obtain may or may not support
   // this functionality. We will check for the functionality that
   // we need once the rowset is created and will modify our behavior
   // appropriately.
   myAddProperty(&rgProperties[0], DBPROP_CANFETCHBACKWARDS);
   myAddProperty(&rgProperties[1], DBPROP_IRowsetLocate);
}


Chapter 15: Transactions

In this chapter, simple transactions, nested transactions, distributed transactions, and transaction isolation are discussed.

A transaction enables a consumer to specify that a sequence of operations within a session must be performed as a single unit. Whether providers support transactions is provider-specific. If the provider supports transactions, a session object that supports ITransactionLocal can enter a simple (that is, non-nested) transaction.

A nested transaction occurs when a transaction is begun within the scope of another transaction. This inner transaction is represented by a transaction object. Providers that support nested transactions can choose to commit or abort a transaction at the outermost level or to commit or abort one of the inner transactions. ITransactionOutcomeEvents is implemented by the consumer to discover the outcome of transaction events.

For more information on | Go to
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
Discovering the outcome of transaction events | "Event Notifications" in this chapter
Methods to perform distributed transactions | "Distributed Transactions" in this chapter
Transaction isolation levels | "Isolation Levels in OLE DB" in this chapter
Locking resources to regulate sharing and isolation | "Transaction Locks" in this chapter

The transaction object and transaction options object cotypes are defined as follows. For more information about cotypes, see "Conceptual Programming Models in OLE DB" in Chapter 1: Overview of OLE DB.

CoType TTransaction {
   [mandatory]   interface IConnectionPointContainer;
   [mandatory]   interface ITransaction;
   [optional]    interface ISupportErrorInfo;
};
CoType TTransactionOptions {
   [mandatory]   interface ITransactionOptions;
   [optional]    interface ISupportErrorInfo;
};


Simple Transactions

At any point in its lifetime, a session can be inside or outside a transaction. When created, a session is outside a transaction and all work done within the scope of that session and that is visible to the data store is immediately committed on each method call. (This is sometimes referred to as auto-commit mode or implicit mode.) When a session enters a transaction, all work done within the scope of that session and that is visible to the data store is part of that transaction and is committed or aborted as a single unit. (This is sometimes referred to as manual-commit mode.)

When the consumer calls ITransactionLocal::StartTransaction, a new local transaction is started. If there is already an active transaction on the session, the new transaction is started as a nested transaction below the current transaction. If the provider does not support nested transactions, calling StartTransaction when there is already an active transaction on the session returns XACT_E_XTIONEXISTS.

The transaction ends when the consumer calls ITransaction::Commit or ITransaction::Abort, at which time all changes to the rowset that have been transmitted to the data store are committed or aborted.

A single data source object can create multiple session objects, each of which can be inside or outside the scope of a transaction at any time.


Committing and Aborting Simple Transactions

When a transaction is committed or aborted, two behaviors are of interest:

  • Retention—whether the session object enters a new transaction.
  • Preservation—whether the rowset can still be used after the transaction has ended.


Transaction Retention

Committing or aborting the transaction with the fRetaining flag in ITransaction::Commit or ITransaction::Abort set to TRUE (also called a retaining commit or abort) causes a new transaction to be started immediately after the current one ends. If the fRetaining flag is set to FALSE, the transaction is committed or aborted and no new transaction is created. Any new work done on the session is outside the scope of a transaction. Attempting to explicitly commit or abort a transaction when there is no outstanding transaction returns an error.

A command object created within the scope of a transaction remains fully functional after the transaction is ended by a retaining commit or abort. The command also remains active in the case of a nonretaining commit or abort, but its prepared state may be lost.

Note A retaining commit or abort should not change the characteristics (isolation level, isolation flags, transaction options) of the transaction. The new unit of work retains the same characteristics as the committed work.


Rowset Preservation

After a transaction ends, one of the following states is possible: The full functionality of the relevant rowset is preserved, or the rowset enters a zombie state. A preserved rowset retains all of its capabilities. A rowset that enters a zombie state is an object whose functionality is virtually lost. Specifically, zombie rowsets support only IUnknown operations and incrementing and decrementing reference counts on outstanding row and accessor handles. In general, all other operations on the zombie rowset produce the error E_UNEXPECTED.

The behavior of a rowset after a commit is determined by the property DBPROP_COMMITPRESERVE. If the value of this property is VARIANT_FALSE, the rowset enters a zombie state. However, if the value of DBPROP_COMMITPRESERVE is VARIANT_TRUE, the rowset remains fully functional after the commit, with all of its rows, bookmarks, pending changes, and so on still valid. Providers usually preserve pending changes on a preserved commit, although consumers should not rely on this behavior.

Similarly, the behavior of a rowset on an abort is determined by the property DBPROP_ABORTPRESERVE. If the value of DBPROP_ABORTPRESERVE is VARIANT_FALSE, rowsets created within the scope of the aborted transaction enter a zombie state after the abort. If the value of DBPROP_ABORTPRESERVE is VARIANT_TRUE, the rowsets remain fully functional, with all of their rows, bookmarks, pending changes, and so on still valid. Whether pending changes are retained or lost is provider-specific. In general, consumers should not expect pending changes to survive an abort. All changes made visible to the data source within the aborted transaction are undone.

Preserved rowsets are not automatically refreshed from the underlying data store. For example, if the transaction is aborted and the value of DBPROP_ABORTPRESERVE is VARIANT_TRUE, any pending changes are not undone in the rowset. The consumer must call IRowsetRefresh::RefreshVisibleData to synchronize the rowset with the data store.

Even if a rowset is created with preserving properties, it can enter a zombie state if other objects on which it depends are destroyed as a side effect of an abort. For example, a rowset can enter a zombie state if a table on which it was built was created as part of the same transaction that was aborted.

Following are some important notes about rowset preservation:

  • Providers are allowed, but not required, to support starting or enlisting in transactions while command, rowset, or row objects are open on a session. If a provider does not or cannot supply this support, it should return DB_E_OBJECTOPEN when ITransactionLocal::StartTransaction attempts to start, or when ITransactionJoin::JoinTransaction attempts to enlist, the transaction. (See "Distributed Transactions," in this chapter, for more information about enlisting in a transaction.)
  • Whether a provider returns DB_E_OBJECTOPEN may be related to whether the open object has been completely transmitted from the underlying data source or whether the provider is allowed to open additional connections to satisfy the request.
  • Rowset preservation is governed at the rowset level, as opposed to the session level, by the DBPROP_COMMITPRESERVE and DBPROP_ABORTPRESERVE properties.
  • If a provider allows objects to be open outside or prior to a transaction, those objects may be affected by a transaction commit or abort. However, a provider should zombie a rowset (that is, not preserve the rowset object) only if DBPROP_COMMITPRESERVE is FALSE and ITransaction::Commit is called or if DBPROP_ABORTPRESERVE is FALSE and ITransaction::Abort is called.
  • Because consumers might not always be aware that called code starts or enlists in transactions for them, consumers should explicitly set the preservation properties to TRUE to guarantee rowset preservation.


Nested Transactions

A nested transaction occurs when a new transaction is started on a session that is already inside the scope of an existing transaction. The new, nested transaction is said to be nested within (or below the level of) the existing transaction. Changes made within the nested transaction are invisible to the top-level transaction until the nested transaction is committed. Even then, the changes are not visible outside the top-level transaction until that transaction is committed. Providers that expose ITransactionLocal and support nested transactions can choose to support only a limited number of nested transactions, including zero.

For providers that support nested transactions, calling ITransactionLocal::StartTransaction on a session with an existing transaction begins a new transaction nested below the current transaction. The return level indicates the level of nesting: a return value of 1 indicates a top-level transaction (that is, a transaction that is not nested within another transaction); 2 indicates a second-level transaction (a transaction nested within a top-level transaction); and so on.

Each nested transaction is represented by a transaction object.

Calling ITransaction::Commit or ITransaction::Abort on the session object commits or aborts the transaction at the current (lowest) nesting level. Calling Commit or Abort on a transaction object nested at a given level commits or aborts the transaction at that nesting level. Committing or aborting a transaction commits or aborts all transactions nested below it as well.

Calling ITransaction::Commit or ITransaction::Abort on a 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.

Before the resources used by a transaction object can be freed by the provider, the transaction object and all transaction objects nested below it must be released. Releasing a transaction object aborts any uncommitted work performed in that transaction.


ITransactionObject

Providers can support extended transaction functionality by exposing ITransactionObject on session objects. The consumer can call ITransactionObject::GetTransactionObject to get an ITransaction interface pointer on the transaction object associated with a particular transaction nesting level. This transaction object can be used to commit or abort a nested transaction at a particular level or to enlist in transaction outcome events for any level.


Distributed Transactions

A distributed transaction is a transaction that updates data on two or more networked computer systems. If the provider supports distributed transactions, it exposes ITransactionJoin on the session. To determine whether the provider supports distributed transactions, the consumer calls QueryInterface to request an ITransactionJoin interface pointer on the session object. The consumer calls JoinTransaction on this interface pointer to enlist the session in a distributed transaction. After the consumer has joined a distributed transaction, it uses the ITransaction interface of the transaction coordinator to commit or abort the transaction. Consumers must set fRetaining to FALSE when committing or aborting a coordinated transaction. Following the commit or abort, 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 enlisted in a local transaction. In addition, the provider might require that any existing distributed transactions be committed or aborted before enlisting in a new distributed transaction. To unenlist from the transaction, the consumer calls ITransactionJoin::JoinTransaction on the session with pUnkTransactionCoord set to NULL.

If the consumer calls ITransactionLocal::StartTransaction to begin a local transaction while in a distributed transaction, that new transaction acts as a nested transaction within the distributed transaction. This is not supported by most providers.


Isolation Levels in OLE DB

Transaction isolation levels are a measure of the extent to which changes made outside a transaction are visible to that transaction. In particular, transaction isolation levels are defined by the presence or absence of the following phenomena:

  • Dirty read—A dirty read occurs when a transaction reads data that has not yet been committed. For example, suppose transaction 1 changes a row. Transaction 2 reads the changed row before transaction 1 commits the change. If transaction 1 aborts the change, transaction 2 will have read data that is considered to have never existed.
  • Nonrepeatable read—A nonrepeatable read occurs when a transaction reads the same row twice but gets different data each time. For example, suppose transaction 1 reads a row. Transaction 2 changes or deletes that row and commits this change or deletion. If transaction 1 attempts to reread the row, it retrieves different row values or discovers that the row has been deleted.
  • Phantom—A phantom is a row that matches the search criteria but is not initially seen. For example, suppose transaction 1 reads a set of rows that satisfy some search criteria. Transaction 2 inserts a new row that matches the search criteria for transaction 1. If transaction 1 reexecutes the statement that read the rows, it gets a different set of rows.

According to these phenomena, the isolation levels defined by OLE DB are as follows:

  • Read Uncommitted (also called 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 (also called 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 (also called Isolated)—A transaction operating at the Serializable level guarantees that all concurrent transactions 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.


Transaction Locks

Locking is the process by which a DBMS restricts access to a row in a multiuser environment. Transactions lock rows to regulate their sharing and isolation.

Locking can 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 can 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 operation until the other transaction has finished and released 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 time-out failure, and the time-out can be set to zero for infinite time-out. Various time-out settings can be chosen by setting transaction properties.

The data source object implements transaction locking by using a strategy that corresponds to the transaction isolation level. These strategies are specific to the data source object.


Event Notifications

The provider can support transaction outcome event notification by exposing IConnectionPointContainer on the session object. The consumer can expose the ITransactionOutcomeEvents interface to receive notification from the provider when a transaction is committed or aborted.

To receive event notifications, the consumer must implement the methods in ITransactionOutcomeEvents and register the interface with the provider's connection point mechanism. To do this, the consumer calls QueryInterface to request an IConnectionPointContainer interface pointer on the session, obtains the IConnectionPoint interface for IID_ITRANSACTIONOUTCOMEEVENT, and passes its ITransactionOutcomeEvents interface to that connection point. When a transaction is committed or aborted, the provider notifies each registered ITransactionOutcomeEvents interface of the outcome of the transaction.

For more information about ITransactionOutcomeEvents interface and its methods, see the Microsoft Distributed Transaction Coordinator Programmer's Reference. For more information about IConnectionPoint and IConnectionPointContainer, see the COM reference documentation.


Chapter 16: Errors

This chapter addresses the ability of OLE DB to process errors by using methods that either return a code or create an error object. Error objects can return detailed error information. Handling of multiple error items is discussed in the section "Arrays of Errors," later in this chapter.

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
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 object example | "Error Object Example" in this chapter
Error records | "Error Records" in this chapter
Error record example | "Error Record Example" in this chapter
Interfaces used by OLE DB error objects | "Interfaces Used by OLE DB Error Objects" in this chapter
How a provider returns an error object | "How a Provider Returns an OLE DB Error Object" in this chapter
How a consumer 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 18: Programming Considerations
Provider-specific error lookup services | "Error Lookup Services" in this chapter "Error Lookup Service Registry Entries" in Chapter 18: Programming Considerations

Methods return error information in two ways. The value returned by a method indicates the overall success or failure of the method. 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.

The error object, custom error object, and error record object cotypes are defined as follows. For more information about cotypes, see "Conceptual Programming Models in OLE DB" in Chapter 1: Overview of OLE DB.

CoType TErrorObject {
   [mandatory]   interface IErrorRecords;
}

CoType TCustomErrorObject {
   [optional]   interface ISQLErrorInfo
}

CoType TErrorRecord {
   [mandatory]   interface IErrorInfo;
}


Return Codes

Each method returns a return code, which indicates the overall success or failure of the method. These return codes are of type HRESULT. There are two classes of return codes: success and warning codes, and error codes.

Success and warning codes begin with S_ or DB_S_ (for example, S_OK) 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 documentation for 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_ (for example, DB_E_BADACCESSORHANDLE) and indicate that the method failed and was unable to do any useful work. For example, IRowset::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. 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 that allocate memory that is returned to the consumer. The reason for listing E_OUTOFMEMORY in these sections is to remind consumers that they might be able to call the method successfully by requesting fewer returned values, such as fewer rows from IRowset::GetNextRows.

In addition to the return codes for errors listed in the OLE DB Programmer's Reference, providers can return provider-specific error codes. However, providers must not return success codes other than those listed in the Programmer's Reference.

Note Methods that can return DB_S_ERRORSOCCURRED do so only when one or more errors occur. Methods that fail entirely (that is, encounter nothing but errors) can return DB_E_ERRORSOCCURRED. Some providers incorrectly return DB_S_ERRORSOCCURRED (instead of S_OK) 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.

If the consumer requests it, these methods also return an array of status 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.


Automation Error Objects

The following sections provide a brief description of Automation error objects as background information. They are followed by a discussion of OLE DB error objects, which use many of the same mechanisms.

  • How an Automation Component Returns an Error Object
  • How an Automation Consumer Retrieves an Error Object


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 must do the following:

  1. Call CreateErrorInfo in the Automation DLL. This function creates an Automation error object that exposes ICreateErrorInfo and IErrorInfo.
  2. Call 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. Call QueryInterface to retrieve the IErrorInfo interface pointer on the error object. This interface pointer identifies the error object to all Automation components.
  4. Call SetErrorInfo in the Automation DLL and pass it the IErrorInfo interface pointer. This function replaces its current error object, if any, with the new error object and adds a reference count to the new error object.
  5. Call Release to release the ICreateErrorInfo and IErrorInfo interface pointers. This effectively transfers ownership of the error object from the component that caused the error to the Automation DLL.


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 performs the following actions:

  1. If it does not already have an ISupportErrorInfo interface pointer on the component, the consumer can request one by calling QueryInterface on the component. ISupportErrorInfo must be supported by all components that create error objects.
  2. If it has not already determined whether the interface that returned the code supports error objects, the consumer calls ISupportErrorInfo::InterfaceSupportsErrorInfo and passes it the IID of the interface containing the method that returned the code. ISupportErrorInfo::InterfaceSupportsErrorInfo returns S_OK if the interface supports error objects (meaning the consumer should retrieve the current error object, if one exists) and returns 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. Call GetErrorInfo 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, transferring ownership of the error object to the consumer.
  4. Call the methods in the IErrorInfo interface to retrieve information from the error object. For example, IErrorInfo::GetDescription can be called to retrieve a text description of the error.
  5. Call Release on the error object to release it.


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 in the stack of service components might want to add its own error information to the existing 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, while an OLE DB error object contains multiple error records.

Support for OLE DB error objects is provider-specific. Providers can choose to generate them from all, none, or a subset of their interfaces. Consumers can call ISupportErrorInfo::InterfaceSupportsErrorInfo for each interface to determine whether the interface supports error objects.

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 and returns S_OK.

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 or not that method returns an error. Therefore, the consumer can be assured that if an error object does exist after calling a method on an interface that supports error objects, the error object describes the outcome of that method.


Error Object Example

The code in this example illustrates the use of error objects in OLE DB error handling.

////////////////////////////////////////////////////////////////////////
// myHandleResult
//
// This function is called as part of the XCHECK_HR macro; it takes an
// HRESULT, which is returned by the method called in the XCHECK_HR
// macro, and the file and line number where the method call was made.
// If the method call failed, this function attempts to get and display
// the extended error information for the call from the IErrorInfo,
// IErrorRecords, and ISQLErrorInfo interfaces.
//
////////////////////////////////////////////////////////////////////////
HRESULT myHandleResult
   (
   HRESULT              hrReturned,
   LPCWSTR              pwszFile,
   ULONG                ulLine)
{
   HRESULT              hr;
   IErrorInfo *         pIErrorInfo               = NULL;
   IErrorRecords *      pIErrorRecords            = NULL;
   ULONG                cRecords;
   ULONG                iErr;

   // If the method called as part of the XCHECK_HR macro failed,
   // we will attempt to get extended error information for the call.
   if( FAILED(hrReturned) )
   {
      // Obtain the current error object, if any, by using the
      // Automation GetErrorInfo function, which will give
      // us back an IErrorInfo interface pointer if successful.
      hr = GetErrorInfo(0, &pIErrorInfo);

      // We've got the IErrorInfo interface pointer on the error object.
      if( SUCCEEDED(hr) && pIErrorInfo )
      {
         // OLE DB extends the Automation error model by allowing
         // error objects to support the IErrorRecords interface. This
         // interface can expose information on multiple errors.
         hr = pIErrorInfo->QueryInterface(IID_IErrorRecords,
                                          (void**)&pIErrorRecords);
         if( SUCCEEDED(hr) )
         {
            // Get the count of error records from the object.
            CHECK_HR(hr = pIErrorRecords->GetRecordCount(&cRecords));

            // Loop through the set of error records, and
            // display the error information for each one.
            for( iErr = 0; iErr < cRecords; iErr++ )
            {
               myDisplayErrorRecord(hrReturned, iErr, pIErrorRecords,
                                    pwszFile, ulLine);
            }
         }
         // The object didn't support IErrorRecords. Display
         // the error information for this single error.
         else
         {
            myDisplayErrorInfo(hrReturned, pIErrorInfo, pwszFile,
                               ulLine);
         }
      }
      // There was no error object, so just display the HRESULT to the
      // user.
      else
      {
         wprintf(L"\nNo Error Info posted; HResult: 0x%08x\n"
            L"File: %s, Line: %d\n", hrReturned, pwszFile, ulLine);
      }
   }

CLEANUP:
   if( pIErrorInfo )
      pIErrorInfo->Release();
   if( pIErrorRecords )
      pIErrorRecords->Release();
   return hrReturned;
}


Error Records

OLE DB error objects store error information in error records. Each error record is composed of the following parts: an ERRORINFO structure, error parameters, a pointer to a custom error object, a dynamic error ID, and a lookup ID. Each of these parts is described more fully in this section.

Records in OLE DB error objects are numbered starting from zero. By using the methods in IErrorRecords, consumers can 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.) Thus, consumers can start with the highest level of error information and then retrieve increasingly more detailed information.


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 described in the following table.

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. The value of iid can be different from the ID of the interface that defines the method the consumer called. For example, if a consumer calls If ICommand::Execute in a query processor and if the query processor then calls IOpenRowset::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 because of inheritance, this value 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 | Optional and provider-specific. Could be used, for example, to 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 (<param1>) that could not be opened. Error parameters are inserted into error messages by a provider-specific error lookup service. Therefore, 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

A custom error object can be associated with each error record. 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. In this way, 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 Microsoft® Data Access Components (MDAC) SDK to ignore the provider's lookup service and use the description supplied in the error resource DLL shipped with the MDAC SDK. For more information, see "Error Lookup Services," later in this chapter.


Error Record Example

The code in this example shows how to process the contents of an error record.

////////////////////////////////////////////////////////////////////////
// myDisplayErrorRecord
//
// This function displays the error information for a single error
// record, including information from ISQLErrorInfo, if supported.
//
////////////////////////////////////////////////////////////////////////
HRESULT myDisplayErrorRecord
   (
   HRESULT              hrReturned,
   ULONG                iRecord,
   IErrorRecords *      pIErrorRecords,
   LPCWSTR              pwszFile,
   ULONG                ulLine)
{
   HRESULT              hr;
   IErrorInfo *         pIErrorInfo       = NULL;
   BSTR                 bstrDescription   = NULL;
   BSTR                 bstrSource        = NULL;
   BSTR                 bstrSQLInfo       = NULL;

   static LCID          lcid              = GetUserDefaultLCID();

   LONG                 lNativeError      = 0;
   ERRORINFO            ErrorInfo;

   // Get the IErrorInfo interface pointer for this error record.
   CHECK_HR(hr = pIErrorRecords->GetErrorInfo(iRecord, lcid,
            &pIErrorInfo));

   // Get the description of this error.
   CHECK_HR(hr = pIErrorInfo->GetDescription(&bstrDescription));

   // Get the source of this error.
   CHECK_HR(hr = pIErrorInfo->GetSource(&bstrSource));

   // Get the basic error information for this record.
   CHECK_HR(hr = pIErrorRecords->GetBasicErrorInfo(iRecord, &ErrorInfo));

   // If the error object supports ISQLErrorInfo, get this information.
   myGetSqlErrorInfo(iRecord, pIErrorRecords, &bstrSQLInfo,
                     &lNativeError);

   // Display the error information to the user.
   if( bstrSQLInfo )
   {
      wprintf(L"\nErrorRecord:  HResult: 0x%08x\nDescription: %s\n"
         L"SQLErrorInfo: %s\nSource: %s\nFile: %s, Line: %d\n",
         ErrorInfo.hrError,
         bstrDescription,
         bstrSQLInfo,
         bstrSource,
         pwszFile,
         ulLine);
   }
   else
   {
      wprintf(L"\nErrorRecord:  HResult: 0x%08x\nDescription: %s\n"
         L"Source: %s\nFile: %s, Line: %d\n",
         ErrorInfo.hrError,
         bstrDescription,
         bstrSource,
         pwszFile,
         ulLine);
   }

CLEANUP:
   if( pIErrorInfo )
      pIErrorInfo->Release();
   SysFreeString(bstrDescription);
   SysFreeString(bstrSource);
   SysFreeString(bstrSQLInfo);
   return hr;
}

////////////////////////////////////////////////////////////////////////
// myDisplayErrorInfo
//
// This function displays basic error information for an error object
// that doesn't support the IErrorRecords interface.
//
////////////////////////////////////////////////////////////////////////
HRESULT myDisplayErrorInfo
   (
   HRESULT           hrReturned,
   IErrorInfo *      pIErrorInfo,
   LPCWSTR           pwszFile,
   ULONG             ulLine)
{
   HRESULT           hr;
   BSTR              bstrDescription   = NULL;
   BSTR              bstrSource        = NULL;

   // Get the description of the error.
   CHECK_HR(hr = pIErrorInfo->GetDescription(&bstrDescription));

   // Get the source of the error--this will be the window title.
   CHECK_HR(hr = pIErrorInfo->GetSource(&bstrSource));

   // Display this error information.
   wprintf(L"\nErrorInfo: HResult: 0x%08x, Description: %s\nSource: %s\n"
            L"File: %s, Line: %d\n",
            hrReturned,
            bstrDescription,
            bstrSource,
            pwszFile,
            ulLine);

CLEANUP:
   SysFreeString(bstrDescription);
   SysFreeString(bstrSource);
   return hr;
}

////////////////////////////////////////////////////////////////////////
// myGetSqlErrorInfo
//
// If the error object supports ISQLErrorInfo, get the SQL error
// string and native error code for this error.
//
////////////////////////////////////////////////////////////////////////
HRESULT myGetSqlErrorInfo
   (
   ULONG                iRecord,
   IErrorRecords *      pIErrorRecords,
   BSTR *               pBstr,
   LONG *               plNativeError
   )
{
   HRESULT              hr;
   ISQLErrorInfo *      pISQLErrorInfo   = NULL;
   LONG                 lNativeError     = 0;

   // Attempt to get the ISQLErrorInfo interface for this error
   // record through GetCustomErrorObject. Note that ISQLErrorInfo
   // is not mandatory, so failure is acceptable here.
   CHECK_HR(hr = pIErrorRecords->GetCustomErrorObject(
            iRecord,                         // iRecord
            IID_ISQLErrorInfo,               // riid
            (IUnknown**)&pISQLErrorInfo));   // ppISQLErrorInfo

   // If we obtained the ISQLErrorInfo interface, get the SQL
   // error string and native error code for this error.
   if( pISQLErrorInfo )
      hr = pISQLErrorInfo->GetSQLInfo(pBstr, &lNativeError);

CLEANUP:
   if( plNativeError )
      *plNativeError = lNativeError;
   if( pISQLErrorInfo )
      pISQLErrorInfo->Release();
   return hr;
}


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 in the MDAC SDK, and which functions are implemented by the OLE DB error object.

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 OLE DB error object and is used by that code to implement IErrorRecords and IErrorInfo. For more information, see "Error Lookup Services," later in this chapter.
IErrorInfo Defined by: Automation
Implemented by: MDAC SDK
Implemented on: OLE DB error object

OLE DB error objects expose IErrorInfo so that 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 a record, the consumer can call IErrorRecords::GetErrorInfo and pass the 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 by using the specified locale ID.
IErrorRecords Defined by: OLE DB
Implemented by: MDAC SDK
Implemented on: OLE DB error object

This is the main interface through which OLE DB error objects are accessed.
IClassFactory Defined by: Automation
Implemented by: MDAC 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 MDAC 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 by using 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 should perform the following actions:

  1. Call GetErrorInfo in the Automation DLL to obtain ownership of the error object on the current thread and request an IErrorInfo interface pointer on it. (The error object is not required to support IErrorInfo.) Such an error object will exist only if added by a lower-level provider. Therefore, a provider at the bottom of a chain of providers will not find an error object. 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. Create 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. Call IErrorRecords::AddErrorRecord to add one or more error records to the error object. To add by using AddErrorRecord, the provider 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. Call QueryInterface to retrieve an IErrorInfo interface pointer on the error object. This interface pointer identifies the error object to all Automation components.
  5. Call the function SetErrorInfo in the Automation DLL and pass it the IErrorInfo interface pointer. SetErrorInfo replaces the current error object (if any) on the thread with the new error object and adds a reference count to hold the new error object.
  6. Call Release to release all of its references to 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 that is unable to open a table might respond by creating an OLE DB error object and transferring ownership of the object to the Automation DLL:

#include <oledb.h>

DWORD                 errCantOpenTable;
IOpenRowset *         pSimpleProvider;
IRowset *             pRowset;
DBID                  TableID;
extern GUID           CLSID_THISISAM;
extern DISPID         DISPID_OpenRowset;
BSTR                  bstrTableName;

int main() {
   IClassFactory *    pErrorObjectFactory;
   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, 0,
                                       NULL, (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)
   {
      CoGetClassObject(CLSID_EXTENDEDERRORINFO,
         CLSCTX_INPROC_SERVER,
         NULL,                              // Reserved, must be NULL
         IID_IClassFactory,
         (LPVOID *)&pErrorObjectFactory));

      hr = pErrorObjectFactory->CreateInstance(NULL, IID_IErrorInfo,
                                               (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 ofthe object to the Automation DLL.

   pErrorRecords->Release();
   pErrorInfo->Release();
   if(pErrorObjectFactory)
      pErrorObjectFactory->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);
};

extern DISPID           DISPID_GetData;
extern GUID             CLSID_THISISAM;
DWORD                   errGeneralError;

int main() {
   IClassFactory *      pErrorObjectFactory;
   ERRORINFO            ErrorInfo;
   HRESULT              hr, hrErr;
   IErrorInfo *         pErrorInfo;
   IErrorRecords *      pErrorRecords;

   // Clear the current error object.
   SetErrorInfo(0, NULL);

   // (Not shown.) Do something that causes an error to occur.

   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);

   CoGetClassObject(CLSID_EXTENDEDERRORINFO,
      CLSCTX_INPROC_SERVER,
      NULL,                              // Reserved, must be NULL
      IID_IClassFactory,
      (LPVOID *)&pErrorObjectFactory));

   if (!pErrorInfo)
   hr = pErrorObjectFactory->CreateInstance(NULL, IID_IErrorInfo,
                                            (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();
   pErrorObjectFactory->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 must perform the following actions:

  1. If it does not already have an ISupportErrorInfo interface pointer on the component, the consumer can request one by calling QueryInterface on the component. ISupportErrorInfo must be supported by all components that create error objects.
  2. If it has not already determined whether the interface that returned the code supports error objects, the consumer can call ISupportErrorInfo::InterfaceSupportsErrorInfo and pass it the IID of the interface containing the method that returned the code. ISupportErrorInfo::InterfaceSupportsErrorInfo returns S_OK if the interface supports error objects (meaning the consumer should retrieve the current error object, if one exists) and returns 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. Call the GetErrorInfo function in the Automation DLL. This function returns an IErrorInfo interface pointer on the current error object (or NULL, if there is no current error object) and releases its reference count on the error object, transferring ownership of the error object to the consumer.
  4. Call QueryInterface to return an IErrorRecords interface pointer on the error object.
  5. Call 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, the consumer 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, because it applies to a specific record.) The consumer then calls methods using this pointer.
  6. Call Release on the error object to release all references to it.

The following code shows how a consumer might retrieve and use an OLE DB error object. (In the interest of clarity, some error checks and interface releases are not shown.)

#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;

   // (Not shown.) Error or warning occurs when calling method.
   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. Therefore, 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. To prevent this problem, the provider should first call local methods and then post its error object.

The system error information set and retrieved by calling the Automation SetErrorInfo and GetErrorInfo functions is defined to be thread-specific. During an asynchronous operation, in which the error condition occurs on a background thread, the consumer must call GetErrorInfo inside its IDBAsynchNotify::OnProgress(COMPLETE) implementation in order to be assured that it receives the correct error object.


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 three important reasons for this: First, it allows the information to be easily localized. Second, it means that memory need not be allocated to store the information until the information 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, which creates what are called 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 run-time case, which creates 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 18: Programming Considerations.

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. The error object then calls IErrorLookup::GetErrorDescription and passes the hrError element of the record's ERRORINFO structure, 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 MDAC SDK. Finally, the error object returns the completed, localized error message and source to the error object, which returns error messages 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, the error object uses the error lookup service to get the Help file and Help context ID.


Chapter 17: Asynchronous Processing

Asynchronous processing enables methods to return immediately without blocking on the calling thread. Consumers request asynchronous processing when initializing a data source object 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.

OLE DB 2.5 providers that support direct URL binding or scoped operations on row objects may also support asynchronous operations. Consumers may request asynchronous processing when binding from a URL to an OLE DB object or when performing scoped operations—such as copy, delete or move—on a row object.

Data Source Initialization properties are used when consumers initialize a data source object 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 18: Programming Considerations
Consumers | "Conceptual Programming Models in OLE DB" in Chapter 1: Overview of OLE DB
Data source objects | "Data Source Objects" in Chapter 2: Data Source and Session Objects
Rowset | Chapter 4: Rowsets
Asynchronously initializing a data source | "Asynchronous Data Source Initialization" in this chapter
Asynchronously opening a rowset | "Asynchronous Command Execution and Rowset Generation" in this chapter
Asynchronously populating a rowset | "Asynchronous Rowset Population" in this chapter
Asynchronously binding from a URL to an OLE DB object | "Asynchronous URL Binding" in this chapter
Performing asynchronous scoped operations | "Asynchronous Scoped Operations" 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 or IDBAsynchStatus 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 ppwszStatusText set to a provider-specific value indicating completion or NULL.

Note If a consumer enlists in an asynchronous phase that has already completed, the provider should immediately call IDBAsynchNotify::OnProgress with ulAsynchPhase set to DBASYNCHPHASE_COMPLETE for that operation, followed by IDBAsynchNotify::OnStop, to notify the consumer that asynchronous processing has completed.

If a consumer enlists in an asynchronous phase that was aborted or vetoed, the provider should immediately call IDBAsynchNotify::OnProgress with ulAsynchPhase set to DBASYNCHPHASE_CANCELED for that operation, followed by IDBAsynchNotify::OnStop, to notify the consumer that asynchronous processing has finished but was canceled.


Asynchronous Data Source Initialization

Consumers wishing to initialize a data source object 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 object's connection point. The consumer must explicitly request Asynchronous Data Source Initialization. Otherwise, IDBInitialize::Initialize does not return until the data source object 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 prior to calling ICommand::Execute, IOpenRowset::OpenRowset, IDBSchemaRowset::GetRowset, IRowPosition::GetRowset, IColumnsRowset::GetColumnsRowset, IMultipleResults::GetResult, ISourcesRowset::GetSourcesRowset, or any other method that returns a rowset, riid must be set to IID_IDBAsynchStatus, IID_IConnectionPointContainer, or IID_IUnknown.

The method returns immediately with S_OK if the rowset initialization completes immediately, or with 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 usually are not available until the rowset is completely initialized and might 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 and then calls ICommand::Execute with DBPROP_IMultipleResults set to VARIANT_TRUE and riid set to IID_IMultipleResults; and finally, the consumer calls IMultipleResults::GetResult with riid set to IID_IDBAsynchStatus or IID_IConnectionPointContainer to retrieve individual results asynchronously. When consumers request a multiple-results object from ICommand::Execute, providers should return immediately with the multiple-results object and save processing until the consumer calls IMultipleResults::GetResult.

To cancel creation of the rowset, the consumer can call IDBAsynchStatus::Abort or can 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 methods that return rows wait, if necessary, in order to return the requested rows. Therefore, 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 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. The method used to request rows waits to return 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 can 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.


Asynchronous URL Binding

Consumers wishing to poll for status, cancel an operation, or receive notifications of progress when requesting a bind by calling IBindResource::Bind or ICreateRow::CreateRow should set the DBBINDURLFLAG_WAITFORINIT flag. When this flag is set, Bind or CreateRow immediately returns an uninitialized object. The consumer may call IUnknown::QueryInterface on the uninitialized object for IDBAsynchStatus to poll for status or call QueryInterface for IConnectionPointContainer to register the consumer's IDBAsynchNotify interface as a notification sink. The consumer then calls IUnknown::QueryInterface for the IDBInitialize interface and calls IDBInitialize::Initialize to initiate the bind or create operation and initialize the object. All objects that can be bound using the DBBINDURLFLAG_WAITFORINIT flag should support the optional interfaces IDBInitialize, IConnectionPointContainer, and IDBAsynchStatus.

The returned object must be fully defined by the call to IBindResource::Bind or ICreateRow::CreateRow. All bind flags and properties required to define the object being returned must be specified at the time the call is made. Before calling IDBInitialize::Initialize, it is not possible to set properties or call other methods that affect initialization. The DBBINDURLFLAG_WAITFORINIT flag can be used only to set up notification and polling channels. The consumer is limited to registering an IDBAsynchNotify notification sink for initialization of notifications and obtaining an IDBAsynchStatus interface pointer to poll for status or cancel the operation.

Providers may not be able to uninitialize an object bound using DBBINDURLFLAG_WAITFORINIT and on IDBInitialize::Uninitialize may return E_UNEXPECTED.

Consumers wishing to bind asynchronously to a data source, rowset, row, or stream object must set the DBBINDURLFLAG_ASYNCHRONOUS bit of dwBindURLFlags prior to calling IBindResource::Bind or ICreateRow::CreateRow. (Note: Only Bind can bind to data source objects.) If this flag is set, the provider returns immediately from the call to Bind or CreateRow, returning S_OK if the operation has completed immediately, or DB_S_ASYNCHRONOUS if the operation will continue asynchronously. Consumers can call IDBAsynchStatus::GetStatus to get the status of the operation or can register for progress notifications by setting the DBBINDURLFLAG_WAITFORINIT flag and passing the consumer's IDBAsynchNotify interface to the requested object's connection point. The consumer must explicitly request an asynchronous operation. Otherwise, Bind or CreateRow does not return until the requested object is completely bound. The DBBINDURLFLAG_WAITFORINIT flag transfers the latency of the bind to the IDBInitialize::Initialize call.


Asynchronous Scoped Operations

When calling IScopedOperations::Copy, Delete, or Move, a consumer can request asynchronous processing by setting the DBCOPY_ASYNC bit of dwCopyFlags. (Note: For IScopedOperations::Delete, the DELETE_ASYNC bit of dwDeleteFlags is set instead. For IScopedOperations::Move, the DBMOVE_ASYNC bit of dwMoveFlags is set.) When this flag is set, the provider returns immediately from the call to Copy, Delete, or Move, returning either S_OK, if the operation has completed immediately, or DB_S_ASYNCHRONOUS, if the operation will continue asynchronously. Consumers can call IDBAsynchStatus::GetStatus to get the status of the operation or can register for progress notifications by passing the consumer's IDBAsynchNotify interface to the requested object's connection point. The consumer must explicitly request an asynchronous operation. Otherwise, the call to Copy, Delete, or Move does not return until all requested trees or subtrees have been copied, deleted, or moved as appropriate. If multiple IScopedOperations operations are invoked asynchronously on the same row instance, the behavior of IDBAsynchStatus and IDBAsynchNotify is undefined.


Chapter 18: 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
Provider threading responsibilities | "Provider Threading Responsibilities" 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 COM Objects" in this chapter
Freeing memory by the consumer | "Argument Memory" in this chapter


Aggregation

OLE DB objects, like other COM objects, 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.

OLE DB providers are required to support aggregation of all objects.


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. Following are several reasons for registering these OLE DB components:

  • Registration is required as part of being a COM component and provides a way to create an instance of a COM object.
  • Registration provides the mechanism for the Data Links user-interface component and the root enumerator shipped with the OLE DB component of the Microsoft® Data Access Components (MDAC) 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 a 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 can be provider-specific. A provider might consider itself capable of supporting many data formats. A usage example would be to group providers together and help consumers interactively decide which provider might work for them.


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 | Name | Values
HKEY_CLASSES_ROOT |  |
CLSID |  |
EnumeratorCLSID | (Default) | FriendlyDisplayName
InprocServer32 | (Default) | EnumeratorDLLFilename
 | ThreadingModel | Apartment|Free|Both
OLE DB Enumerator | (Default) | Description
ProgID | (Default) | EnumeratorProgID
VersionIndependentProgID | (Default) | VersionIndependentProgID

For more information about enumerators, see "Enumerators" in Chapter 2: Data Source and Session Objects.


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 | Name | Value
HKEY_CLASSES_ROOT |  |
CLSID |  |
ProviderCLSID | (Default) | VersionIndependentProgID
 | OLEDB_SERVICES | SupportedServices[1]
ExtendedErrors[2] | (Default) | Extended Error Service
ErrorLookupCLSID[2] | (Default) | FriendlyDisplayName
InprocServer32 | (Default) | ProviderDLLFilename
 | ThreadingModel | Apartment|Free|Both
OLE DB Provider | (Default) | Description
ProgID | (Default) | ProviderProgID
VersionIndependentProgID | (Default) | VersionIndependentProgID
ProviderProgID | (Default) | Description
Clsid | (Default) | ProviderCLSID

[1] Generally 0xffffffff.

[2] If Error Lookup Service is supported.

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: Data Source and Session Objects.


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 the programmatic identifier (ProgID).

Keys and subkeys | Name | Value
HKEY_CLASSES_ROOT |  |
CLSID |  |
ErrorLookupCLSID | (Default) | FriendlyDisplayName
InprocServer32 | (Default) | ErrorLookupDLLFilename
 | ThreadingModel | Apartment|Free|Both
ProgID | (Default) | ErrorLookupProgID
VersionIndependentProgID | (Default) | VersionIndependentErrorLookupProgID

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 16: Errors.


Binder Registry Entries

Binders are identified in the registry by the OLE DB Binder subkey under the class ID of the binder. Under HKEY_CLASSES_ROOT, binders must have the following subkeys and values for the programmatic identifier (ProgID).

Keys and subkeys | Name | Values
HKEY_CLASSES_ROOT |  |
CLSID |  |
BinderCLSID | (Default) | FriendlyDisplayName
InprocServer32 | (Default) | BinderDLLFilename
 | ThreadingModel | Apartment|Free|Both
OLE DB Binder | (Default) | Description (for example, "Microsoft OLE DB Binder for Internet Publishing")
ProgID | (Default) | BinderProgID
VersionIndependentProgID | (Default) | VersionIndependentProgID

For more information about binders, see "Direct Binding" in Chapter 8: Binder Objects and Direct Binding.


Provider Threading Models

Providers can support single, apartment, or free-threaded OLE DB objects as long as they advertise which models are supported. It is recommended 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 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 might 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: OLE DB Properties.


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::RefreshVisibleData 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 storage objects) are also free-threaded.
  • To guarantee a minimum of marshaling support for their use by apartment-model consumers; for example, by aggregating to them an IMarshal object obtained from CoCreateFreeThreadedMarshaler. 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 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 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.
  • 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 when the pointers correspond to free-threaded objects.

    The set of threads initialized as free-threaded acts similarly to a 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 (but not required) that the pointer be marshaled at the source and unmarshaled 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 objects (and other COM 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 that 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 a rowset that is written internally to use multiple threads, not just to run on multiple threads. Consequently, 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 COM Objects

OLE DB supports two kinds of COM 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 IRowset::GetData returns the object.

For more information about storage objects and IPersist* objects, see Chapter 7: BLOBs and COM Objects.


Argument Memory

The consumer can safely free memory at any time after a method return. 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.


Argument Validation

With few exceptions as noted, OLE DB defines specific error conditions for invalid arguments passed to various methods. Compliant providers are required to provide this argument validation. Providers are allowed, however, to expose provider-specific mechanisms, such as provider-specific properties or registry entries, for disabling such argument validation.


Part 2 OLE DB Core

OLE DB Programmer's Reference

Part 2: OLE DB Core

Part 2 of the OLE DB Programmer's Reference reviews essential elements of OLE DB, such as resource pooling, core components, and the Data Link API. This section contains the following sections:

  • Chapter 19: OLE DB Services
  • Chapter 20: OLE DB Core Components


Chapter 19: OLE DB Services

OLE DB provides common services to enhance the native functionality and performance of OLE DB providers. These services include the Client Cursor Engine, which enables OLE DB consumers to scroll or find over the rowsets of providers that do not natively support such functionality, as well as performance and scaling features such as resource pooling and automatic transaction enlistment within a Microsoft® Windows NT®/Windows® 2000 Component Services (or Microsoft Transaction Server, if you are using Windows NT) environment.

OLE DB services are automatically enabled when a consumer invokes a provider by calling an IDataInitialize or IDBPromptInitialize method. Consumers can also enable OLE DB services by calling CoCreateInstance, passing the CLSID of the OLE DB Initialization Component as an argument. (OLE DB services are not enabled by calling CoCreateInstance to invoke the provider based on the provider's CLSID.) For more information, see "Creating Data Source Objects" in Chapter 2: Data Source and Session Objects.

The following sections describe OLE DB resource pooling and explain how to use it and other services in OLE DB providers and consumers:

  • Resource Pooling
  • Writing Scalable OLE DB Providers
  • How Providers Work Effectively with Resource Pooling
  • How Consumers Use Resource Pooling
  • Automatic Transaction Enlistment
  • Enabling and Disabling Individual OLE DB Services
  • Setting Provider Service Defaults
  • Overriding Provider Service Defaults


Resource Pooling

Whenever the consumer requests a data source object from the provider, OLE DB services intercept the request, query the provider for supported information, and return a proxy data source object to the consumer. This proxy object appears to the consumer as if it were the provider's data source object. However, when the consumer calls methods to set data source properties, the property settings are cached in the proxy object and not used to set properties on the provider's actual data source object.

When the consumer calls IDBInitialize::Initialize, the proxy object looks for any initialized and connected data source object that matches the specified connection information and is not already in use. If such a data source object is found, rather than create a new data source object, set its properties, and establish a new connection to the database, the proxy object uses the existing data source object.

If a matching data source object does not already exist, the resource pool creates one. Immediately after initializing the new data source object, the resource pool creates a session object and caches it internally. The first time the consumer requests a session, the proxy object returns this cached session object.

When the consumer releases the proxy object, the data source object is returned to the resource pool. Any data source object that is released by the consumer and not reused after a 60-second time-out is automatically released by the resource pool.

When the consumer releases the session object, the resource pool keeps it until the data source object has timed out. This is because transaction enlistment occurs at the session level and can be very expensive. By caching the session object, the resource pool ensures that multiple connections enlisted in the same transaction are not required to reenlist each time.

To maximize scalability and performance, OLE DB resource pooling implements multiple, homogeneous pools. That is, there is a separate pool of connected data source objects for each combination of connection information used. Because it is not necessary to compare multiple different types of connections within one pool, this makes it very fast to identify a matching data source object. Because the pool is locked until a match is found, implementing these separate pools also reduces contention, which is extremely important in a scalable environment. (For more information about contention, see Writing Scalable OLE DB Providers.)

To further reduce contention, especially on a multiprocessor machine, OLE DB resource pooling maintains up to n+1 pools for each combination of connection information used, where n is the number of processors.

For the same reason, OLE DB services cache provider information, such as initialization properties, default registry information, and the provider's class factory. Registry lookups can be expensive, and retrieving property information requires task memory allocations. Caching provider information reduces the need for registry lookups.

Within a Component Services (or MTS, if you are using Windows NT) environment, OLE DB services automatically determine whether or not the calling thread is in a distributed transaction and if so, enlist the connected data source object in the transaction by calling methods on the provider's ITransactionJoin interface.

Note In ADO, OLE DB services are enabled by default.

Controlling Timeout Behaviors

Registry entries can be used to control session pooling timeout behavior. When set under a provider's CLSID entry in the registry, the SPTimeout value controls the length of time, in seconds, that unused sessions are held in the pool. SPTimeout should be entered as a DWORD value under HKEY_CLASSES_ROOT\CLSID\{class id} where {class id} is the CLSID of the OLE DB provider. The default value is 60 seconds.

The Retry Wait value controls the length of time, in seconds, to wait between connection attempts. It is entered as a DWORD value under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\DataAccess\Session Pooling. The default value is 60 seconds.


Writing Scalable OLE DB Providers

There are a few simple rules for writing scalable OLE DB providers in a multithreaded environment. Most have to do with making sure that one thread isn't blocking other threads from doing work.

  • Minimize the use of global critical sections. Global critical sections prevent other threads from doing work until the thread that holds a lock completes its work. When providers use global critical sections, adding more threads actually hurts performance because the threads continually block each other. This blocking of threads is called contention.
  • Minimize the number and cost of memory allocations. In Microsoft® Windows NT® 4.0, memory allocation and management can be improved by using Multithreaded Heap Manager (MpHeap), which is shipped as part of MSDN. MpHeap acquires large blocks of memory from the operating system and uses them to allocate application memory simultaneously on different threads. MpHeap is also less aggressive about freeing or compacting memory. Note that Windows NT 4.0, Service Pack 4 and Windows 2000 or later, contains an improved task memory allocator.

    When memory is allocated by one component and freed by another, this memory must be allocated through the operating system's task memory allocator. Because using the task memory allocator is expensive, providers should use it only to obtain memory that must be passed to the consumer. A provider should never use the task memory allocator for allocating memory that will be freed by that provider.

  • Allow consumers to unenlist from transactions that have been completed. Within a Component Services (or MTS, if you are using Windows NT) environment, providers must support distributed transactions if packages are marked as anything other than nontransactional. To do so, providers must support ITransactionJoin to allow consumers to enlist in distributed transactions. Therefore, providers should support calling ITransactionJoin::JoinTransaction with a null value to allow consumers to unenlist from transactions that have been completed.
  • Work effectively with OLE DB resource pooling. To benefit from the scaling support built into OLE DB services, providers must work well with OLE DB resource pooling. Strategies for doing this are described in How Providers Work Effectively with Resource Pooling.


How Providers Work Effectively with Resource Pooling

To use OLE DB services, providers must support aggregation of all objects. This is a requirement of all OLE DB 1.5 and later providers. In addition, to work well with OLE DB resource pooling, providers must allow data source objects to be called on any thread, not just the one on which they were created. Specifically, providers must support the free thread model or, at minimum, the rental thread model. They must not be single or apartment threaded. The resource pool determines the thread model of the data source object by examining the value of the data source object's DBPROP_DSOTHREADMODEL property.

If the provider has a global connection state that might change while the data source object is in an initialized state, the provider should also support the DBPROP_RESETDATASOURCE property. This property is called before a connected data source object is reused, and gives the provider the opportunity to clean up the data source object's state. If the provider cannot clean up the data source object's state, it can return DBPROPSTATUS_NOTSETTABLE for the DBPROP_RESETDATASOURCE property and the data source object will not be reused.

Providers that connect to a remote database and can detect whether or not that connection may be lost should support the DBPROP_CONNECTIONSTATUS property. This property gives the OLE DB services the ability to detect dead connections and ensure that they are not returned to the pool.

Finally, automatic transaction enlistment must occur at the same level as resource pooling. Therefore, if a provider supports automatic transaction enlistment, it should support disabling it in favor of OLE DB automatic transaction enlistment, which occurs at the pooling level. To do this, the provider must support the DBPROP_INIT_OLEDBSERVICES property. OLE DB services will clear the DBPROPVAL_OS_TXNENLISTMENT flag when OLE DB automatic transaction enlistment is enabled. The provider should then disable its own support for automatic transaction enlistment.


How Consumers Use Resource Pooling

To use OLE DB resource pooling, the consumer must obtain its data source object by calling IDataInitialize or IDBPromptInitialize to ensure that OLE DB services are enabled.

OLE DB services maintain resource pools of connected data source objects as long as a reference to IDataInitialize or IDBPromptInitialize is held or as long as there is a connection in use. Pools are also maintained automatically within a Component Services (MTS, if you are using Windows NT) or Internet Information Services (IIS) environment. To use OLE DB resource pooling outside of a Component Services or IIS environment, the consumer must keep a reference to IDataInitialize or IDBPromptInitialize or must hold on to at least one connected data source object. To ensure that the pool is not destroyed when the last proxy object is released by the consumer, keep the reference or hold on to the connected data source object for the lifetime of your application.

OLE DB services identify the resource pool from which the connected data source object will be drawn when IDBInitialize::Initialize is called. After the data source object is drawn from a pool, it cannot be moved to a different pool. Therefore, consumers should avoid changing initialization information. (Initialization information is changed by calling IDBInitialize::Uninitialize.) Pooling is also disabled if the consumer calls QueryInterface for a provider-specific interface prior to calling IDBInitialize::Initialize. Also, connections established with a prompt value other than DBPROMPT_NOPROMPT will not be pooled. However, the initialization string retrieved from a connection established through prompting can be used to establish additional pooled data source objects.

Some providers must create a separate connection for each session. These additional connections must be enlisted separately in the distributed transaction, if one exists. OLE DB services cache and reuse one session object per data source object, but if the consumer requests more than one session at a time from a single data source object, the provider may create additional connections and transaction enlistments that are not pooled. It is actually more efficient to create a separate data source object for each session in a pooled environment than to create multiple sessions from a single data source object.


Automatic Transaction Enlistment

It is not always apparent to the consumer when a provider is not automatically enlisted in transactions. Specifically, in the following cases enlistment does not occur:

  • The provider does not support aggregation. To use OLE DB services, providers must support aggregation of all objects.
  • The OLEDB_SERVICES registry entry does not exist. For information about how to add an OLEDB_SERVICES registry entry, see "Setting Provider Service Defaults."
  • The provider has not enabled automatic transaction enlistment by assigning an appropriate value to the OLEDB_SERVICES registry entry. For more information, see "Overriding Provider Service Defaults."
  • The provider does not support the ITransactionJoin interface.


Enabling and Disabling Individual OLE DB Services

The OLE DB Service Component Manager compares the properties specified by the consumer to those supported by the provider. By doing so, it determines whether individual service components can be enabled to satisfy extended functionality requested by the consumer. For example, if an application requests a scrollable cursor and the provider supports only a forward-only cursor, the Service Component Manager provides scrolling functionality by enabling the Client Cursor Engine service component. If the consumer relies on extended functionality supported by default on the provider's rowset and the consumer does not explicitly set the properties to request that functionality, the functionality may not appear on the rowset returned by the Client Cursor Engine. To be interoperable, consumers should always set properties to explicitly request extended functionality where needed.

In some cases, providers might need to disable individual OLE DB services to work well with consumers that make assumptions about the characteristics of a provider. OLE DB services can be used to disable individual services or all services, either on a connection-by-connection basis or for all consumers that use a single provider.


Setting Provider Service Defaults

Individual OLE DB services can be enabled or disabled by default for all consumers that access a single provider. This is done by adding an OLEDB_SERVICES registry entry under the provider's CLSID, with a DWORD value specifying the services to enable or disable as shown in the following table.

Default services enabled | DWORD value
All services (default) | 0xffffffff
All except pooling and automatic transaction enlistment | 0xfffffffc
All except Client Cursor Engine | 0xfffffffb
All except pooling, automatic transaction enlistment, and Client Cursor Engine | 0xfffffff8
Pooling and automatic transaction enlistment, session level aggregation only | 0x00000003
No services | 0x00000000
No aggregation, all services disabled | (No registry entry)


Overriding Provider Service Defaults

The provider's registry value for OLEDB_SERVICES is returned as the default value for the DBPROP_INIT_OLEDBSERVICES initialization property on the data source object.

As long as the registry entry exists, the provider's objects will be aggregated and the consumer can override the provider's default setting for enabled services by setting the DBPROP_INIT_OLEDBSERVICES property prior to initialization. To enable or disable a particular service, the consumer will usually get the current value of the DBPROP_INIT_OLEDBSERVICES property, set or clear the bit for the particular property to be enabled or disabled, and reset the property. DBPROP_INIT_OLEDBSERVICES can be set directly in OLE DB or in the connection string passed to ADO or IDataInitialize::GetDatasource.

Performance can be tuned using DBPROPVAL_OS_AGR_AFTERSESSION. When off, aggregation beyond the session level is disabled, resulting in improved performance. The setting is ignored when non–session level services, such as the Client Cursor Engine, are used.

The corresponding values to enable or disable individual services are listed in the following tables. Note that the Client Cursor Engine is excluded; ADO invokes the Client Cursor Engine independent of these OLE DB Services.

Default services enabled | DBPROP_INIT_OLEDBSERVICES property value
All services (default) | DBPROPVAL_OS_ENABLEALL
All except pooling and automatic transaction enlistment | (DBPROPVAL_OS_ENABLEALL & ~DBPROPVAL_OS_TXNENLISTMENT & ~DBPROPVAL_OS_RESOURCEPOOLING)
All except Client Cursor Engine | (DBPROPVAL_OS_ENABLEALL & ~DBPROPVAL_OS_CLIENTCURSOR)
All except pooling, automatic transaction enlistment, and Client Cursor Engine | (DBPROPVAL_OS_ENABLEALL & ~DBPROPVAL_OS_RESOURCEPOOLING & ~DBPROPVAL_OS_TXNENLISTMENT & ~DBPROPVAL_OS_CLIENTCURSOR)
Pooling and automatic transaction enlistment only, session level aggregation only | DBPROPVAL_OS_TXNENLISTMENT & DBPROPVAL_OS_RESOURCEPOOLING
No services | DBPROPVAL_OS_DISABLEALL
Default services enabled | Value in connection string
All services (default) | "OLE DB Services = -1;"
All except pooling and automatic transaction enlistment | "OLE DB Services = -4;"
All except Client Cursor Engine | "OLE DB Services = -5;"
All except pooling, automatic transaction enlistment, and Client Cursor Engine | "OLE DB Services = -8;"
Pooling and automatic transaction enlistment only, session level aggregation only | "OLE DB Services = 3;"
No services | "OLE DB Services = 0;"

Note If the registry entry does not exist for the provider, the Component Managers will not aggregate the provider's objects and no services will be enabled, even if explicitly requested by the consumer.


Chapter 20: OLE DB Core Components

The Core Components are the essential elements of OLE DB. The following topics in this section describe each component as well as various interfaces that can be used in the Data Link API:

  • Root Enumerator Object
  • Row Position Object
  • Connection Strings and Data Links
    This section also includes a discussion of the Connection String Syntax, which sets the rules for constructing connection strings.
  • Provider Extensible Data Link User Interface API

The Root Binder Object, also an OLE DB core component, is discussed in Chapter 8.


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 provider data source objects and enumerators listed in the registry.

In the rowset returned by ISourcesRowset::GetSourcesRowset, the root enumerator returns values from the registry as indicated in the following table. Each row in the rowset describes a single enumerator or data source object.

Note The root enumerator has the class ID CLSID_OLEDB_ENUMERATOR.

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

For more information, see "Enumerating Data Source Objects and Enumerators" in Chapter 2: Data Source and Session Objects, and "Registry Entries" in Chapter 18: Programming Considerations.


Row Position Object

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

For more information, see IRowPosition in Appendix I.


Connection Strings and Data Links

A data link enables you to easily store connection information within an application or to 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 store. Without a connection string, it would be necessary 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.


Connection String Syntax

A connection string is a string version of the initialization properties needed to connect to a data store and enables you to easily store connection information within your application or to pass it between applications. Without a connection string, you would be required to store or pass a complex array of structures to access data. When accessing OLE DB directly, using IDataInitialize, the connection string is passed as a parameter to create an OLE DB data source object.

Note In ADO, connection strings are retrieved and set using the ConnectionString property to create a Connection object.

In some cases, rather than using the standard data link user interface, it might be desirable to build a connection string within an application—for example, if you want to provide a custom user interface. In these cases, it is important that the connection string syntax adhere to the formatting rules described in the following sections.

Basic Connection String Format

The basic format of a connection string is based on the ODBC connection string. The string consists of a series of keyword/value pairs separated by semicolons. The equal sign (=) connects 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 value of the Provider keyword to the VersionIndependentProgID value 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 value 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 ODBC connection string in the following example 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 names a provider that does not exist or is invalid, as in the following example, an error is returned.

Example Provider=;Database=MyDatabase

Specifying a Keyword

To identify a keyword used after the Provider keyword, use the property description of the OLE DB initialization property that you want to set. For example, the property description of the standard OLE DB initialization property DBPROP_INIT_LOCATION is Location. Therefore, to include this property in a connection string, use the keyword Location.

Example Provider=MSDASQL;Location=Pubs

Note Standard OLE DB properties are documented under the OLE DB Initialization property group (DBPROPSET_DBINIT) in Appendix C: OLE DB Properties. For information about provider-specific initialization properties, see the documentation that came with your provider.

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

Example Jet OLE DB:System Database=c:\system.mda

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

Example Authentication;Info=Column 5

The keyword is "Authentication;Info".

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

Example Verification==Security=True

The keyword is "Verification=Security".

If multiple equal signs appear, each one must be preceded by an additional equal sign.

Example Many====One=Valid

The keyword is "Many==One".

Example TooMany===False

The keyword is "TooMany=".

Setting Keyword Values

The value of a keyword must be specified as a string. 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.

In connection strings, the short property values are case-insensitive, so the values can be made more readable by using True and False.

Example 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 can be used for the short values, as follows:

  • Prompt
  • Complete
  • CompleteRequired
  • NoPrompt

Example Prompt=Complete

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

  • DB_MODE_SHARE_DENY_READ
  • DB_MODE_SHARE_DENY_WRITE
  • DB_MODE_SHARE_EXCLUSIVE

The underscores after the prefix "DB_MODE_SHARE_" are replaced with spaces in the short values, as follows:

  • Deny Read
  • Deny Write
  • Share Exclusive

These shortened property values apply only to those properties that are documented in the OLE DB specification. Different OLE DB data providers may use customized properties and 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 a decimal, hex, or octal value.

Examples:

Window Handle=123

Window Handle=0x123

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 enclosed in double quotes.

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

If the value contains both a semicolon and a double-quote character, the value can be enclosed in single quotes.

Example 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 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 DataSchema="'MyOtherCustTable'"

The value is 'MyOtherCustTable'.

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

Example NewRecordsCaption='"Company''s "new" customer"'

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

Example NewRecordsCaption="""Company's ""new"" customer"""

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

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 MyKeyword=My Value

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

Example 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 enclosed in either single quotes or double quotes.

Example MyKeyword=' My Value '

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

Example MyKeyword=" My Value "

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

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 even though quote marks are not used. This is to support backward compatibility for ODBC connection strings. Trailing spaces after keywords might also be preserved.

Example 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.

Returning Multiple Values

For standard OLE DB initialization properties 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 Mode=Deny Write|Deny Read

Listing Keywords Multiple Times

If a specific keyword in a keyword=value pair occurs multiple times in a connection string, the last occurrence listed is used in the value set.

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

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

One exception to the preceding rule 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 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 by using the pipe (|) character is not allowed.

Example DBType=3|5

Example Connect Timeout=(34*23)/8

Each of the preceding examples is illegal, assuming the property data types are not string data types. If they are string data types, 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. The OLE DB Data Link core component implements and validates these rules for you.

See Also

Data Link API Overview | Referencing a Connection String in Your Application | OLE DB Initialization Properties: Quick Reference


OLE DB Initialization Properties: Quick Reference

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

The following table lists the property description for each standard OLE DB initialization property and, where applicable, the OLE DB documented value with its corresponding short value. For information about provider-specific properties, see the documentation that came with your provider.

Initialization property | OLE DB documented value | Short value
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 | DBPROPVAL_ASYNCH_INITIALIZE | Initialize
DBPROP_INIT_BINDFLAGS Description: Bind Flags | DB_BINDFLAGS_COLLECTION DB_BINDFLAGS_DELAYFETCHCOLUMNS DB_BINDFLAGS_DELAYFETCHSTREAM DB_BINDFLAGS_ISSTRUCTUREDDOCUMENT DB_BINDFLAGS_OPENIFEXISTS DB_BINDFLAGS_OUTPUT DB_BINDFLAGS_OVERWRITE DB_BINDFLAGS_RECURSIVE | Collection DelayFetchColumns DelayFetchStream StructuredDocument OpenIfExists Output Overwrite Recursive
DBPROP_INIT_CATALOG Description: Initial Catalog |  |
DBPROP_INIT_DATASOURCE Description: Data Source |  |
DBPROP_INIT_GENERALTIMEOUT |  |
DBPROP_INIT_HWND Description: Window Handle |  |
DBPROP_INIT_IMPERSONATION_LEVEL Description: Impersonation Level | DB_IMP_LEVEL_ANONYMOUS DB_IMP_LEVEL_IDENTIFY DB_IMP_LEVEL_IMPERSONATE DB_IMP_LEVEL_DELEGATE | Anonymous Identify Impersonate Delegate
DBPROP_INIT_LCID Description: Locale Identifier |  |
DBPROP_INIT_LOCATION Description: Location |  |
DBPROP_INIT_LOCKOWNER Description: Lock Owner |  |
DBPROP_INIT_MODE Description: Mode | DB_MODE_READ DB_MODE_WRITE DB_MODE_READWRITE DB_MODE_SHARE_DENY_READ DB_MODE_SHARE_DENY_WRITE DB_MODE_SHARE_EXCLUSIVE DB_MODE_SHARE_DENY_NONE | Read Write ReadWrite Share Deny Read Share Deny Write Share Exclusive Share Deny None
DBPROP_INIT_OLEDBSERVICES Description: OLE DB Services | DBPROPVAL_OS_RESOURCEPOOLING DBPROPVAL_OS_TXNENLISTMENT DBPROPVAL_OS_ENABLEALL DBPROPVAL_OS_DISABLEALL DBPROPVAL_OS_CLIENTCURSOR DBPROPVAL_OS_AGR_AFTERSESSION | ResourcePooling TxnEnlistment EnableAll DisableAll ClientCursor AgrAfterSession
DBPROP_INIT_PROMPT Description: Prompt | DBPROMPT_PROMPT DBPROMPT_COMPLETE DBPROMPT_COMPLETEREQUIRED DBPROMPT_NOPROMPT | Prompt Complete CompleteRequired NoPrompt
DBPROP_INIT_PROTECTION_LEVEL Description: Protection Level | DB_PROT_LEVEL_NONE DB_PROT_LEVEL_CONNECT DB_PROT_LEVEL_CALL DB_PROT_LEVEL_PKT DB_PROT_LEVEL_PKT_INTEGRITY DB_PROT_LEVEL_PKT_PRIVACY | None Connect Call Pkt Pkt Integrity 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: OLE DB Properties


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 stores.

Because the Data Link API is included with OLE DB as a core component, the user interface is always available to your application users if ADO or OLE DB is properly installed on their system.

Using the Data Link API

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

  • 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 must 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, which you can save and reuse in your applications. This string version is referred to as a connection string. The user interface object that prompts users to build the connection string is referred to as the Data Link Properties dialog box. 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 Properties dialog box, your end users can build connection strings directly within your application. Once created, a connection string can be saved by using either of the following methods:

  • It can be saved within the application itself.
  • It can be saved as a separate file with the .udl extension. This file is referred to as a Microsoft Data Link file.

These two methods of saving a connection string are compared in the following table.

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 a .udl file, enabling users to save and modify it from their system.
Connection string is part of the application and is therefore 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 any time if the connection information changes.

Users can also build connection strings using the Data Link Properties dialog box. On Microsoft Windows® 95 systems, a Data Link icon was created in the Windows Control Panel when Data Access components were installed. On Windows 98 and Windows NT systems where Data Access components were installed, right-clicking on the Desktop allowed the user to select New and then Microsoft Data Link to create a .udl file.

On computers running the Windows 2000 operating system, you can access the Data Link Properties dialog box by following these steps:

  1. Open Windows Explorer.
  2. Select the folder in which you want to save the .udl file. Right-click in the right, or results pane, point to New, and then click Text Document.
  3. On the Tools menu, click Folder Options. On the View tab, clear the Hide file extensions for known file types check box and then click OK.
  4. Right-click on the text file you created in step 2, and then click Rename. Type the new file name using a .udl file extension. Press Enter.
  5. A warning may appear, explaining that changing file extensions may cause files to become unusable. Disregard this warning.

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
  • Select Data Link File

Note To get help information about the Data Link interface, press F1 while viewing an active dialog box.

Data Link Properties Dialog Box

The Data Link Properties dialog box is typically used either to edit an existing connection string or to build a new connection string. This tabbed dialog box 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.

Select Data Link File Dialog Box

If, instead of storing a connection string directly within your application, you want your application to reference a .udl file, 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 a .udl file that points to the user's Customer database. Then, by using the Select Data Link File dialog box, the user could specify this .udl file to access the Customer database.

Referencing a .udl file is beneficial to users if a database is upgraded to a different server or is modified. In either 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 returns the selected .udl file to the application.

Properties Set by the Connecting Application

The properties in the following list will not be persisted to the .udl file. They are generally set by the connecting application at run time. Although these can be set manually, they could be easily overwritten by the connecting application.

  • DBPROP_INIT_OLEDBSERVICES
  • DBPROP_INIT_HWND
  • DBPROP_INIT_PROMPT
  • DBPROP_INIT_ASYNCH
  • DBPROP_INIT_TIMEOUT
  • DBPROP_INIT_LCID

See Also

Referencing a Connection String in Your Application | Connection String Syntax


Referencing a Connection String in Your Application

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. For more information about creating data source objects, see the OLE DB IDataInitialize interface.

For greater performance, you can enable .udl pooling by adding "Udl Pooling" to the registry key "SOFTWARE\Microsoft\DataAccess" under HKEY_LOCAL_MACHINE. A Cache Size value greater than 0 (Cache Size=1) will enable the feature. A value of 100 is recommended. The .udl pooling feature allows connection information to be cached using IDataInitialize::LoadStringFromStorage and then passed to IDataInitialize::GetDataSource.

Programmatically Accessing the Data Link User Interface

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

To access the Data Link API from applications that can directly access a COM interface, use the OLE DB IDBPromptInitialize interface.

See Also

Data Link API Overview | Connection String Syntax


Provider Extensible Data Link User Interface API

The Data Link user interface implemented in MDAC 2.0 enabled users of OLE DB data stores to customize connections visually, as they had done in the past with the ODBC Data Source Administrator.

While all of the potential initialization properties for an OLE DB data source object can be set through the default Data Link user interface, there are no provisions for guiding the user through setting the right properties or for enforcing dependencies among multiple properties.

This section describes the mechanism for implementing provider extensions to the Data Link user interface. The interfaces used are all standard COM interfaces for handling property sheets, and detailed reference information for them can be found in the MSDN Library documentation.

The extensible Data Link user interface provides the following features:

  • A mechanism in which the Data Link core component can determine whether the provider has customized tab pages for the property sheet.
  • A callback on the Data Link core component that the provider can use to describe the pages to be used.
  • 6A handshaking mechanism by which provider pages can pass page changes to the Data Link core component.

The provider implements property sheet tabs that replace the Connection and Advanced tabs within the Data Link user interface.


Implementing Customized User Interfaces in OLE DB Providers

To implement customized Data Link property sheets, the provider must do the following:

  • Implement pages for both the Connection and Advanced tabs of the user interface. This requirement is for consistency between providers and to simplify the implementation within the Data Link user interface. The provider can choose to leave the Advanced property page blank if it is unused. Microsoft® Windows® 2000 requires that these pages be implemented for Unicode support as well.
  • Register CLSIDs for the provider pages. The extensibility mechanism requires that the extension pages have their own CLSIDs. The provider will be responsible for registering these CLSIDs and handing them to the Data Link core component when requested.
  • Implement code to load and save changes to the user interface by using the interfaces described in the section Interfaces Implemented by the Provider. The provider relays property information to the Data Link core component via the IPropertyBag and IPropertyPage interfaces. When a new page is selected, the Data Link core component will call into the provider to determine whether there were changes and, if so, it then calls into the provider to have the changes committed.

    Note While the provider will have access to the data source object for getting information from the provider, the provider should not do anything to change the state of the IDBProperties associated with that data source object.

  • Localize all strings and error messages in the property sheets. The provider is responsible for localizing all information on the property pages, including the tab headings. See Localization for more information.
  • Implement the interfaces specified in the section Interfaces Implemented by the Provider. These interfaces allow the provider to communicate with the Data Link user interface.


Interfaces Implemented by the Provider

The interfaces on the provider should be implemented within the provider's data source object. This object will need to be created in-process because the property sheet interfaces expect in-process operation.

Only the methods used by the Data Link core component are listed below. It is left to the provider whether to implement any of the other methods of the interface, but the Data Link core component will not use them.

IServiceProvider

IServiceProvider::QueryService

QueryService is used by the Data Link core component to determine whether the provider supports extensible user interface. Since it is possible that a provider has already implemented property pages for another container, the Data Link core component also uses QueryService to determine whether the provider's pages were implemented for the Data Link core component in particular.

HRESULT QueryService(
   _Guid      guidService,
   _Guid      riid,
   Object[]   ppvObject);

Parameters

guidService
[in]
The constant OLEDB_SVC_DSLPropertyPages will be passed to the QueryService method, guidService parameter. This is the GUID for the Data Source Locator property pages.
riid
[in]
The unique identifier of the interface the caller wishes to receive for the service.
ppvObject
[out]
Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObject contains a pointer to the requested interface.

ISpecifyPropertyPages

ISpecifyPropertyPages::GetPages

Fills a counted array of GUID values, where each GUID specifies the CLSID of each property page that can be displayed in the property sheet for this object.

GetPages is called by the Data Link core component to get an array of exactly two CLSIDs that represent the extensible property pages: the first to replace the Connection tab, and the second to replace the Advanced tab. These pages must be registered properly so that they can be cocreated by the Data Link core component in-process. If an entry in the array is set to GUID_NULL, that page is not overloaded and the corresponding default Data Link page is used.

HRESULT GetPages(
   CAUUID *   pPages);

Parameters

pPages
[out]
Pointer to a caller-allocated CAUUID structure that must be initialized and filled before returning. The pElems field in the CAUUID structure is allocated by the callee with CoTaskMemAlloc and freed by the caller with CoTaskMemFree.

IPropertyPage

IPropertyPage::SetPageSite

Initializes a property page and provides the page with a pointer to the IPropertyPageSite interface through which the property page communicates with the property frame.

The SetPageSite method is used by the Data Link core component to give the property page a reference to the Data Link core component IPropertyPageSite implementation.

To unload the provider's user interface, the Data Link core component calls SetPageSite with a null pointer.

HRESULT SetPageSite(
   IPropertyPageSite *   pPageSite);

Parameters

pPageSite
[in]
Pointer to the IPropertyPageSite interface of the page site that manages and provides services to this property page within the entire property sheet.

IPropertyPage::Activate

Creates the dialog box window for the property page.

Activate is used by the Data Link core component to request that the provider page is displayed (including window creation, and so forth). All provider pages will be requested as modal dialogs.

HRESULT Activate(
   HWND      hWndParent,
   LPCRECT   prc,
   BOOL      bModal);

Parameters

hWndParent
[in]
Window handle of the parent of the dialog box that is being created.
prc
[in]
Pointer to the RECT structure containing the positioning information for the dialog box. This method must create its dialog box with the placement and dimensions described by this rectangle—that is, origin point at (prc->left, prc->top) and dimensions of (prc->right-prc->Left, prc->bottom-prc->top).
bModal
[in]
Indicates whether the dialog box frame is modal (TRUE) or modeless (FALSE).

IPropertyPage::Deactivate

Destroys the window created with Activate.

Deactivate is used by the Data Link core component to request that the provider page be destroyed. Property sheets are supposed to destroy property pages that are not in use, so whenever the user changes providers via the Providers tab, the Data Link core component will request that the provider destroy its page.

HRESULT Deactivate(void);

IPropertyPage::IsPageDirty

Indicates whether the property page has changed since activated or since the most recent call to Apply.

IsPageDirty is used by the Data Link core component to determine whether the user has made any changes to the provider property sheet. If so, the Data Link core component will make a second request to the provider to have the changes applied to the sheet.

HRESULT IsPageDirty(void);

IPropertyPage::Apply

Applies current property page values to underlying objects.

When the Data Link user interface has received an Apply button hit message or when the user changes pages in the property sheet, the Data Link core component will ask the active page whether there are pending changes. If so, it will call the Apply method. The provider should then write only the changes using the Data Link core component IPropertyBag implementation.

HRESULT Apply(void);

IPropertyPage::TranslateAccelerator

Provides a pointer to an MSG structure that specifies a keystroke to process.

This method is consumed by the Data Link user interface so that the Data Link core component, upon receiving a keystroke it doesn't handle, can pass the keystroke on to the active property page for processing.

HRESULT TranslateAccelerator(
   LPMSG   pMsg);

Parameters

pMsg
[in]
Pointer to the MSG structure describing the keystroke to process.

IPropertyPage::Help

Invokes help in response to end-user request.

This is a notification to the provider that a user has requested help for the current tab page. The provider is responsible for invoking context-sensitive help for its own pages.

HRESULT Help(
   LPCOLESTR *   pszHelpDir);

Parameters

pszHelpDir
[in]
Pointer to the string under the HelpDir key in the property page's CLSID information in the registry. If HelpDir does not exist, this will be the path found in the InProcServer32 entry minus the server file name. (LocalServer32 is not checked in the current implementation because local property pages are not currently supported.)

IPersistPropertyBag

IPersistPropertyBag::Load

Called by the container to load the control's properties.

After activating a provider page to be displayed, the Data Link core component will subsequently call the provider's Load method, passing in the Data Link core component implementation of IPropertyBag. This is to be used by the provider to populate the controls on the page being activated.

HRESULT Load(
   IPropertyBag *   pPropBag,
   IErrorLog *      pErrorLog);

Parameters

pPropBag
[in]
Pointer to the caller's IPropertyBag interface bag that the control uses to read its properties. Cannot be NULL.
pErrorLog
[in]
Currently unused. Will always be set to NULL.

IPersistPropertyBag::Save

Called by the container to save the object's properties.

If the Data Link core component determines that the page is dirty from the IsPageDirty call, it will subsequently call the provider's Save method. The provider is then to use the IPropertyBag passed in to save changes. The Data Link core component will always pass in True for the fClearDirty flag and False for the fSaveAllProperties flag.

HRESULT Save(
   IPropertyBag *   pPropBag,
   BOOL            fClearDirty,
   BOOL            fSaveAllProperties);
pPropBag
[in]
Pointer to the caller's IPropertyBag interface through which the object can write properties. Cannot be NULL.
fClearDirty
[in]
A flag indicating whether the object should clear its dirty flag when saving is complete. TRUE means clear the flag, and FALSE means leave the flag unaffected. FALSE is used when the caller wishes to do a Save Copy As type of operation.
fSaveAllProperties
[in] A flag indicating whether the object should save all its properties (TRUE) or only those that have changed since the last save or initialization (FALSE).


Interfaces Implemented by the Data Link Core Component

The interfaces in this section are implemented by the Data Link core component. The full API reference entries for these interfaces can be found in the COM documentation in the Platform SDK, which is included in the MSDN Library. Below are implementation notes specific to the Data Link core component.

Methods not implemented on these interfaces will return E_NOTIMPL from a call to the method. If the method is a part of the interface but is not supported, it is not listed below.

IPropertyPageSite

IPropertyPageSite::OnStatusChange

This method is used by the provider's property page to tell the Data Link core component that a value on the page has been updated. This should be called whenever the user changes the value of one of the controls on the property page.

HRESULT OnStatusChange(
   DWORD   dwFlags);

Parameters

dwFlags
[in]
Flags to indicate what changes have occurred to the property page.
Flag | Meaning
PROPPAGESTATUS_DIRTY | Values in the page have changed.
PROPPAGESTATUS_CLEAN | Values in the page have not changed.
PROPPAGESTATUS_VALIDATE | Now is an appropriate time to apply changes.

IPropertyPageSite::GetLocaleID

Returns the locale identifier so the property page can adjust itself to country-specific settings.

This method is provided so that the provider can determine what language settings are being used. The provider should then use that locale when setting up the pages for display.

HRESULT GetLocaleID(
   LCID *   pLocaleID);

Parameters

pLocaleID
[out]
Pointer to locale identifier.

IPropertyPageSite::TranslateAccelerator

Passes a keystroke to the property frame for processing.

This method is implemented by the Data Link user interface so that the provider page, upon receiving a keystroke it doesn't handle, can pass the keystroke on to the property sheet for processing.

HRESULT TranslateAccelerator(
   LPMSG   pMsg);

Parameters

pMsg
[in]
Pointer to the MSG structure to be processed.

IPropertyBag

IPropertyBag::Read

Within the provider's IPersistPropertyBag::Load implementation, the provider page is to use IPropertyBag::Read to extract the properties used to load the controls on the page being displayed.

HRESULT Read(
   LPCOLESTR     pszPropName,
   VARIANT *     pVar,
   IErrorLog *   pErrorLog);

Parameters

pszPropName
[in]
Pointer to the name of the property to read. Cannot be NULL.
pVar
[in]
Pointer to the caller-initialized VARIANT that is to receive the property value on output.
pErrorLog
[in]
Currently unused. Should be set to NULL.

IPropertyBag::Write

When the provider receives a request to IPersistPropertyBag::Save, it calls the Write method on the Data Link core component IPropertyBag implementation to save the individual properties that were changed while the page was active.

HRESULT Write(
   LPCOLESTR   pszPropName,
   VARIANT *   pVar);

Parameters

pszPropName
[in]
Pointer to the name of the property to write. Cannot be NULL.
pVar
[in]
Pointer to the caller-initialized VARIANT that holds the property value to save.

IPropertyPage2

The IPropertyPage2 interface is an extension to IPropertyPage to support initial selection of a property on a page.

IPropertyPage2::EditProperty

Specifies in dispID the property's control located on the property page that will receive the focus when the page is activated.


Flow of Operations

Determining Provider Support for a Customized User Interface

  1. When a provider is chosen via the Providers tab, the Data Link core component cocreates that provider's data source object.
  2. The Data Link core component calls IUnknown::QueryInterface for IServiceProvider. If this fails, the core component assumes that the provider does not support extensions and will use the default property pages.
  3. If the call to QueryInterface succeeds, the Data Link core component calls IServiceProvider::QueryService with OLEDB_SVC_DSLPropertyPages and IID_ISpecifyPropertyPages to retrieve the object that provides property pages for the Data Link core component. If this fails, the Data Link core component will assume that the provider does not support extensions and will use the default property pages.

Loading Pages

  • The Data Link core component calls ISpecifyPropertyPages::GetPages, receiving an array of page CLSIDs. The provider can specify only two pages here: one that replaces Connection, and one that replaces Advanced. The tabs will be replaced in the order that the elements exist within the CLSID array.
  • The Data Link core component walks the array, cocreating each of the pages. On each page, it calls IUnknown::QueryInterface for IPropertyPage and calls IPropertyPage::SetPageSite with the Data Link core component IPropertyPageSite implementation.

Data Link Core Component Actions on Tab Change

There are two possible code paths within the current the Data Link core component implementation: one that is used for the Data Link core component internal pages, and one that is used for the providers pages.

  • If the current page is a Data Link core component internal page, it will commit changes to the per-provider property store.
  • If the current page is a non-Data Link core component owned page, it will do the following:
    1. Call the provider's IPropertyPage::IsPageDirty method to determine whether there have been changes.
    2. If dirty, call IPersistPropertyBag::Save to have those changes committed to the temporary store.
    3. Call IPropertyPage::Deactivate on the current page.
    4. Call IPropertyPage::Activate on the new page, followed by IPersistPropertyBag::Load.

Provider Actions on Page Change

  • The provider page losing focus will receive an IPropertyPage::IsPageDirty call; if there are deltas on the page, it will return True.
  • If dirty, the provider will receive an IPersistPropertyBag::Save. In that code, it will write the changes from its controls to the specified IPropertyBag interface. It should write only those values that are different from the defaults. This enables the Data Source Locator to build a connection string that contains only the nondefault properties.
  • The property page will receive an IPropertyPage::Deactivate call, during which it should deactivate the window.
  • The provider page getting focus will receive an IPropertyPage::Activate method call as well as a call to IPersistPropertyBag::Load, from which it can populate its controls.

Unloading Pages

  • The Data Link core component calls SetPageSite with a null pointer when unloading the provider's user interface.


Localization

Localization of the replaced tabs will be up to the provider implementing the pages that replace the default pages.

All providers must implement property pages for at least the English locale. If the provider calls IPropertyPageSite::GetLocaleID and the value is unrecognized, the property page should display its English version.

When the Data Source Locator is running in a particular non-English locale and the provider does not implement that locale, two particular behaviors will be observed:

  1. The contents of the property pages for the Connection and Advanced tabs will be in English, although the other pages are in the current locale.
  2. The names of the tabs for Connection and Advanced will change at run time when the provider is selected. Before the provider is selected, they will be in the current locale; when the provider is selected, they will change to English. If another provider is then selected, they may change again.


Help and Documentation

The provider will be responsible for responding to requests for help, which come through the IPropertyPage::Help method. The Data Link core component will invoke help only for its own internal pages; it will not do so for pages that are implemented by the provider.


Part 3 OLE DB for OLAP

OLE DB Programmer's Reference

Part 3: OLE DB for OLAP

Part 3 of the OLE DB Programmer's Reference describes the functional advantages of using OLE DB for OLAP to access multidimensional and relational databases. This section contains the following chapters:

  • Chapter 21: OLE DB for OLAP Overview
  • Chapter 22: OLE DB for OLAP Concepts
  • Chapter 23: OLE DB for OLAP Objects and Schema Rowsets
  • Chapter 24: Rowsets in OLE DB for OLAP
  • Chapter 25: Multidimensional Expressions
  • Chapter 26: MDX Grammar
  • Chapter 27: Mapping MDX to SQL Statements
  • Chapter 28: OLE DB for OLAP Code Samples


Chapter 21: OLE DB for OLAP Overview

Microsoft® OLE DB for Online Analytical Processing (OLAP) is a set of objects and interfaces that extends the ability of OLE DB to provide access to multidimensional data stores. OLAP applications typically handle huge volumes of complexly interrelated data, and OLE DB for OLAP enables users to perform sophisticated data analysis through fast, consistent, interactive access to a variety of possible views of the underlying information.

As a freely published specification and a developing industry standard for multidimensional data access, OLE DB for OLAP allows independent software vendors and corporate application developers to depend on a single API for consuming multidimensional data, regardless of the vendor or source. With this technology, OLAP applications can uniformly access both relational and nonrelational data stored in diverse information sources, regardless of location or type.

Providers of multidimensional data can implement a single set of interfaces that allow all OLAP clients to access their data. (These interfaces are described in the section OLE DB for OLAP Interfaces, in Part 4 of this guide.) Consumers can use the COM query interface method and data source properties to dynamically adapt to provider capabilities.

This documentation assumes the audience has the following technical qualifications:

  • Working knowledge of the C or Microsoft Visual C++® programming language
  • Knowledge of COM
  • Knowledge of OLE DB
  • General knowledge of multidimensional and relational database concepts and query languages

The following topics in this chapter provide more information about general and basic OLE DB for OLAP concepts and define the data model example that will be referenced throughout the OLE DB for OLAP documentation:

  • Introduction to OLE DB for OLAP
  • SalesData Example


Introduction to OLE DB for OLAP

OLE DB for OLAP is designed as an extension of OLE DB that allows you to access both relational and multidimensional data stores. A relational data store stores data that is logically related and exposes it as a set of two-dimensional tables; a multidimensional data store stores the data in inherently multidimensional structures.

The following illustration shows this extension of the OLE DB architecture. The consumer can use the OLE DB resources in the COM or COM+ environment to connect to an OLAP-specific provider, following the typical OLE DB architectural model.

OLE DB for OLAP leverages the OLE DB object model and defines specific interfaces that enable representation, expression, transportation, and efficient navigation of multidimensional data within COM. These interfaces, IMDDataset, IMDFind, and IMDRangeRowset, can interconnect so that the same interfaces are applicable among data providers and various levels of service component implementation. As a result, the number of OLE DB for OLAP–specific objects and interfaces is minimized.

The OLE DB for OLAP interfaces cover common consumer (or client application) requirements for processing multidimensional data, but these interfaces are implemented so that existing multidimensional data providers (MDPs) can expose OLE DB for OLAP without making changes at the server level.

By supporting existing mechanisms for updating OLE DB rowsets, OLE DB for OLAP providers can define new interfaces, as well as language clauses for updating results. Vendors can define proprietary language extensions for data definition language (DDL) functionality.

As a means to access and manipulate data, OLE DB for OLAP uses MDX. MDX is a customized query syntax for multidimensional expressions that is explained in detail in Chapter 25: Multidimensional Expressions.

OLE DB for OLAP is designed to enforce symmetry across all dimensions. For example, interfaces expose measurements, such as percent growth over time, as a dimension with functionality equivalent to all other dimensions. Therefore, it is possible to use any of the OLE DB for OLAP–defined functions on any type of dimension to obtain desired results.

OLE DB for OLAP is optimized for both of the following configurations:

  • The MDP and the consumer run in the same process in one process space, such as an OLE DB for OLAP MDP and the query engine of an RDBMS running in the same process.
  • Service components that expose OLE DB for OLAP interfaces and also consume other OLE DB interfaces.

Conceptually, multidimensional data is represented as a cube. The cube—or more appropriately, the hypercube—is also the central metadata object recognized by OLE DB for OLAP and is the representation of multidimensional data in rowset form. It is expressed through the dataset object, which is specific to OLE DB for OLAP. Consumers can expose the data as rowsets by connecting to a tabular data provider (TDP) or a multidimensional data provider (MDP), as shown in the following illustration.

The Programming and Object Models

OLE DB for OLAP follows the basic programming model set forth in OLE DB:

  • Locate a data source. The enumerator object is used to locate the provider (TDP or MDP) associated with the targeted data store and to create the OLE DB data source object, which is then used to create the session object. However, in OLE DB for OLAP, the consumer must be able to easily determine the type of a given data source object—whether TDP, MDP, or both.
  • Specify a session, which is the context on a connection to a data source in which transactions or operations are created.
  • Request a rowset object, typical of OLE DB, or, specific to OLE DB for OLAP, request data through a command by using multidimensional expressions (MDX), which comprise an OLE DB for OLAP–provided language used to manipulate the results in a dataset object.
  • Present data through either the rowset object (using a TDP) or the dataset object (using an MDP and MDX). The rowset object is used to present multidimensional data in a flattened form.
  • Pass a return code that reports whether the method succeeded or failed, optionally generating an error object containing details about a failed operation.

OLE DB Extensions for OLAP

OLE DB for OLAP defines expressions, interfaces and methods that enable providers to expose the multidimensional schemas of a multidimensional data store. It expresses those schemas as a set of cubes, which in turn enables the consumer to manipulate these cubes as schema rowsets.

Using the following extensions, you can build OLAP applications that leverage the OLE DB architecture:

  • The dataset object . Execution of a user's query results in a dataset object, which consists of a set of value points, or cells, taken from the data store cube (or cubes) and built into the dataset cube. 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 object exposes standard OLE DB interfaces and three OLE DB for OLAP–specific ones: IMDDataset, IMDFind, and IMDRangeRowset. OLE DB for OLAP also allows you to view a dataset, represented by a cube, in a flattened form. The process of flattening a dataset to produce a rowset can create a variety of views in tabular, rowset form.
  • The range rowset object . When cell data requires updating, it is actually the range rowset object that is updated by using methods similar to those used on any other OLE DB rowset object. In a range rowset, each row corresponds to a cell in the dataset and each selected cell property appears as a column in the rowset. For more information, see "Working with Cell Data" in Chapter 24: Rowsets in OLE DB for OLAP.
  • Multidimensional schema rowsets for OLE DB for OLAP . Consumers can get data store information about the cubes, dimensions, hierarchies, levels, and members by browsing multidimensional schema rowsets using the IDBSchemaRowset interface. For information about how OLE DB for OLAP exposes and uses these multidimensional schemas as schema rowsets, see "Multidimensional Schemas" in Chapter 22, "OLE DB for OLAP Concepts."
  • Multidimensional expressions (MDX) . For specifying a dataset object, OLE DB for OLAP defines a rich syntax of multidimensional expressions, which can be understood as a set of macros defined on top of the SQL language. Although these macros are generally useful for querying a multidimensional data store, their precise semantics are defined by means of their SQL language mappings.
  • Executing client-side processes. OLE DB for OLAP also extends the capability of existing OLAP tools and applications by implementing the ACTIONS schema rowset, which allows a standard mechanism of integrating client actions or processes. It is also possible to annotate values in a schema rowset and store those values with the data, providing more benefits to the user.


SalesData Example

The SalesData example model described in this section will be referenced throughout the OLE DB for OLAP documentation. In its entirety, the SalesData example represents a complex multidimensional data store containing several dimensions. Because OLE DB relies on the dataset object to present various views of the data in this multidimensional model, on any combination of its dimensions, the following illustration is intended only to provide a conceptual representation of a simple three-dimensional dataset, or cube, that might be derived from the SalesData model.

As defined in OLE DB for OLAP, each dimension in a data cube is either an axis dimension or a slicer dimension. For example, in the preceding illustration, Sales (a member of the Measures dimension) and Products are on the axis dimensions x and y (also called the ROW and COLUMN axes), respectively, and Years represents the slicer dimension, with 1991 representing a possible two-dimensional "slice" as a table showing all product sales for 1991.

SalesData Dataset

The SalesData dataset includes the following dimensions:

  • SalesRep—Names of all sales representatives.
  • Geography—A hierarchy that includes the levels Continents, Countries, Regions, and Cities.
  • Months
  • Quarters
  • Year
  • Measures, of which the three are Sales, PercentChange, and BudgetedSales
  • Products

The inherent beauty of an OLAP application is its ability to calculate and view data in a number of ways through different dimensions. For example, it is possible to use PercentChange of the Measures dimension to calculate the percentage variance in sales of a particular product over a selection of years. Or, from another perspective, to calculate the percent of change in sales of a particular product as compared against all other products over a selection of years.

Using five of the SalesData dimensions listed above, the following dataset illustration shows how PercentChange would be reported across SalesRep of a Geography region by Years, for all Products.

Another user might want to see how products fare in certain geographical areas, not concerned about time or growth as much as simple sales totals for each product through the quarters of the past year. Accessing data through the same dimensions as in the preceding example, the application can report the data for the selected products across the SalesRep and Geography dimensions for each quarter in a given year, as shown in the following dataset illustration.

Note The axis on which a dimension is located can vary, depending on how the user or programmer wants to view or configure the information.

The preceding two dataset examples have the following characteristics:

  • Two axes dimensions encompassing Measures, SalesRep, and Geography in one, and Year in the other, as follows—
    • The Measures (represented by PercentChange or Sales) dimension, the SalesRep dimension, and the Geography dimension compose the ROW (or x) axis.
    • The Year or Quarters dimension composes the COLUMN (or y) axis.
  • One slicer dimension: Products.

The following sections provide sample tables and illustrations of the SalesData dimensions, corresponding members, and hierarchies.

SalesData Dimensions/Members

The following table displays seven dimensions of the SalesData dataset and a sampling of members in each dimension. This table is used as the basis of all hierarchical structures listed below the table.

Note Due to space considerations, an elipsis (…) is sometimes used to indicate those portions of information not included for a specific example.

SalesRep | Geography | Quarters | Months | Year | Measures | Products
Director NA | All | QtrYear | January | 1989 | Sales | Computers
Netz | North America | Qtr1 | February | 1990 | PercentChange | Expansion Cards
Venkatrao | Canada | Qtr2 | March | 1991 | BudgetedSales | Floppy Drives
Director Europe | USA | Qtr3 | April | 1992 |  | Ceiling Fan
Ng | Canada_East | Qtr4 | May | 1993 |  | 75W Light Bulb
King | Canada_West |  | June | 1994 |  | AC Adapter, 12V
 | USA_North |  | July | 1995 |  | AC Adapter, 6V
 | USA_South |  | August | 1996 |  | Printers
 | USA_West |  | September | 1997 |  | Scanners
 | USA_East |  | October | 1998 |  |
 | Boise |  | November | 1999 |  |
 | Boston |  | December | 2000 |  |
 | Calgary |  |  | 2001 |  |
 | Cookstown |  |  |  |  |
 | Houston |  |  |  |  |
 | Los Angeles |  |  |  |  |
 | Miami |  |  |  |  |
 | New York |  |  |  |  |
 | Ottawa |  |  |  |  |
 | Pembroke |  |  |  |  |
 | Seattle |  |  |  |  |
 | Shreveport |  |  |  |  |
 | Toronto |  |  |  |  |
 | Vancouver |  |  |  |  |
 | Japan |  |  |  |  |

SalesData Geography Hierarchies

The SalesData Geography hierarchy, detailed in the following table, is constructed from members of the Geography dimension as listed in the SalesData Dimensions/Members table above.

Geography.All | Geography.NorthAmerica
Geography.Europe | USA
Geography.NorthAmerica | USA_East
 | Boston
 | New York
 | Cookstown
 | Pembroke
 | USA_South
 | Houston
 | Miami
 | USA_North
 | Seattle
 | Boise
 | USA_West
 | Los Angeles
 | Canada
 | Canada_East
 | Ottawa
 | Toronto
 | Canada_West
 | Vancouver
 | Calgary
Geography.Japan | (N/A)

The following illustration provides a conceptual view of the preceding table.

Note The root level member (All) of this hierarchy has no parents, and the leaf level members (Cities) have no children.

SalesData Time Hierarchies

The SalesData Time hierarchy listed in the following table are constructed from members of the Quarters and Months dimensions as listed in the SalesData Dimensions/Members table.

QtrYear
Qtr1
January
February
March
Qtr2
April
May
June
…

The following illustration provides a conceptual view of the preceding table:

For comparative purposes, the following table shows how the Time hierarchies are built for both calendar and fiscal year reporting.

Year-Calendar | Year-Fiscal
Qtr1 | Qtr1
January | July
February | August
March | September
Qtr2 | Qtr2
… | …

The following illustration provides a conceptual view of the preceding table:


Chapter 22: OLE DB for OLAP Concepts

In OLE DB for OLAP, the implementation of specialized schema rowsets is a powerful way of exposing schema information and navigating through multidimensional data stores. You can use these special schema rowsets to browse metadata objects, cache the schema information, and manage the hierarchical structures in your data.

The cube is a central concept in OLE DB for OLAP, and three types of cubes are significant when discussing schema information: virtual cubes, hypercubes, and multicubes. A cube is always represented by a dataset object. The dataset object is created from the OLE DB command object by using multidimensional expressions (MDX) in the context of a multidimensional provider (MDP). The dataset object encapsulates the multidimensional result set returned by the MDP; this result set consists of a set of axes with defined data points, or coordinates, along each axis.

Note Defined in this specification, MDX is a language, more precisely a set of macros, built on top of SQL that provides the means to retrieve and manipulate the data. Although introduced in this chapter because it of its relationship with the dataset object, MDX is fully described in Chapter 25: Multidimensional Expressions.) and its syntax is presented in Chapter 26: MDX Grammer.


Multidimensional Schemas

OLE DB for OLAP has interfaces and methods that allow providers to expose schemas. The OLE DB for OLAP specification does not mandate that MDPs expose its defined schema rowsets; it simply enables those schemas to be exposed if the MDP chooses to support them.

OLE DB for OLAP also provides interfaces that enable consumers to browse a set of metadata objects. An application can use these objects to access the data. Most providers expose a subset of the OLE DB for OLAP metadata objects.

The central metadata object recognized by OLE DB for OLAP is the hypercube, often referred to in this documentation simply as the cube. The hypercube consists of a set of related dimensions that defines an n-dimensional manifold. The data points of interest are the points in this manifold: Each point is uniquely identified by a set of coordinates, and each coordinate is a member of one component dimension. For example, SalesData is a cube with seven component dimensions: SalesRep, Geography, Months, Quarters, Years, Measures, and Products.

Note For complete symmetry between dimensions, OLE DB for OLAP handles the computational dimensions (in this case, the Measures dimension) the same way it handles all other dimensions.

A multidimensional schema is composed of a set of cubes. The main purpose of a schema is to impose high-level authorization policies. A set of schemas is grouped together into a catalog. A provider does not have to support this two-level grouping system just because catalogs and schemas exist. The provider might support catalogs only, schemas only, both, or neither.

Dimensions

A hypercube 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 that is a subset of the members of a dimension.

For example, consider the SalesData cube Dimensions/Members list. The Geography dimension contains many members grouped under one root-level member: The World. Viewing the dataset object resulting from an unorganized query would be of minimal value. Organizing members into hierarchies increases the power and flexibility of dimensions.

Hierarchies

A natural hierarchy exists in the Geography dimension, as shown in the following illustration:

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_North, USA-_South, USA_East, USA_West, Wales}
  • Cities = {Belfast, Berlin, Boise, Boston, Calgary, Cardiff, Cookstown, Dover, Edinburgh, Glasgow, Hamburg, Houston, Los Angeles, London, Miami, Munich, New York, Ottawa, Pembroke, Seattle, Shreveport, Stuttgart, Toronto, Vancouver}

Members at the leaf level, presented here as cities, have no children, and members 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:

Multiple hierarchies built from the same dimension can be very useful, both in data storage and retrieval. The Quarters dimension in the SalesData Dimensions/Members list contains members that provide many views of the data. The following illustration taken from the SalesData cube shows two ways to roll up to the Year level from the Quarters level:

The preceding example 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. That is, 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.

Level Properties

Each piece of data in any given level can have properties assigned to it. In the SalesRep dimension, there is usually a need to store additional data about each salesperson—for example, data such as Address or Phone Number. Therefore, to store this data, each level of a hierarchy tends to have a different set of properties, while 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 might not apply at the Region level. Instead, a region might have properties such as REGIONAL_MANAGER and IMPORT_POINT.


Browsing Metadata

Consumers can get information about the available cubes, dimensions, hierarchies, levels (including level properties), members, and member properties by using the IDBSchemaRowset interface to browse. Because IDBSchemaRowset is an interface on the session object, the consumer must have a session object before it can browse for metadata.

OLE DB for OLAP defines schema rowsets for the following metadata objects:

  • Actions
  • Catalogs
  • Cubes
  • Dimensions
  • Hierarchies
  • Levels
  • Measures
  • Members
  • Properties (for each dimension level)
  • Schemas
  • Sets

Note OLE DB for OLAP rowsets for catalogs and schemas 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, the CUBES rowset has five rows. Each column of a schema rowset provides one piece of information about that object. As shown in the following table, the CUBES rowset has 11 columns, each of which provides 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.

It is not always 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 dimensions available in the schema. Therefore, OLE DB defines a generic method to restrict a schema rowset based on certain conditions, called restrictions. Each schema rowset has a defined set of restrictions, which are typically conditions on one column of a schema rowset. For example, the DIMENSIONS rowset can be restricted by the following properties: CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, DIMENSION_NAME, and DIMENSION_UNIQUE_NAME.

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 contain only dimensions belonging to a particular cube, the application puts the cube name into the third element of the restrictions array.

For detailed information about restrictions in OLE DB for OLAP schema rowsets, see "Additions to IDBSchemaRowset" in Chapter 23: OLE DB for OLAP Objects and Schema Rowsets.


Caching Schema Rowset Information

Many clients need to retrieve 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 except MEMBERS. Although caching schema rowset information results in a slightly larger working set for the application, the gain in performance far outweighs the increase in the working set.

For more information about MEMBERS rowsets, see "MEMBERS Rowset." For more information about working with schema information, see "Using Schema Rowsets in OLE DB for OLAP" in Chapter 23: OLE DB for OLAP Objects and Schema Rowsets.


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. That is, 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, as shown in the following illustration, 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, as shown in the following illustration:

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. Also, 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, 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, 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.

There is an important distinction between these two systems for OLE DB for OLAP schema rowsets. 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), 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. Because a dimension can have many members, this rowset is too big to be considered metadata. On the other hand, the list of members is reasonably static, which is a characteristic usually associated with metadata.

Note Due to the potentially large number of members in a dimension, materializing a MEMBERS rowset without any restrictions (or restricted only up to the dimension name) might take a while to be returned. Therefore, consumers should be judicious in asking for a MEMBERS rowset without any restrictions.

Another distinctive characteristic of the MEMBERS rowset is that 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 occupying a column. Because the list of properties varies based on the level that the member belongs to, the structure of the rowset also varies. All of this assumes that the provider supports named levels. For more information, 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 the section "Restrictions in the MEMBERS RowsetolapRestrictions," in Chapter 23: OLE DB for OLAP Objects and Schema Rowsets.


Named and Unnamed Levels

A data source object 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 stores support named levels. Many data stores 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 store, you cannot give the name "Continents" to a set of members but must instead determine the distance from the root of the World-Continent hierarchy.

OLE DB for OLAP does not require a data store 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 providers not supporting named levels to create a dummy level name containing all members of a hierarchy (or a dimension). The dummy name must be the name of the dimension.

The MEMBERS rowset contains additional columns for each property at the end of the rowset. 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.

Returning "Dummy Names" for Numbered (Unnamed) Levels

For providers that do not support named levels, it is recommended that they return "dummy" level names for each numbered level in the schema rowsets. A simple algorithm, such as naming level 2 as "Level_2," can generate these dummy names; or they can be something more meaningful, such as naming level 2 of the SalesRep dimension as "General Manager."

Generic OLAP clients use the columns of the schema rowset to populate the display; for example, a drop-down list of levels might be populated by the LEVEL_NAME field of the LEVELS schema rowset. By generating dummy names, providers enable such applications to write consistent display code whether or not a provider supports named levels.

Consumers should not use these dummy names in an MDX statement. (A provider that does not support named levels will return an error in this case.) Level names should be used in an MDX statement only if the provider has full support for named levels.

Whether a provider supports named levels, numbered levels with dummy level names, or just numbered levels can be determined by looking at the value of the data source property MDPROP_NAMED_LEVELS.


Supported Hierarchy Types

OLE DB for OLAP supports four hierarchy types: balanced, ragged, unbalanced, and network.

One of the most common types is the balanced hierarchy. In a balanced hierarchy, the parent of each member comes from the level immediately above the member. Most hierarchies on the Time dimension are balanced.

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. This type of hierarchy can also be referred to as ragged-balanced, because levels still exist. A Geography dimension is a typical example of such a hierarchy.

In this example, there is still the concept of a named level—and Country applies equally well to both branches of the hierarchy. The Continent level also applies, but only on one branch of the hierarchy.

In an unbalanced hierarchy, the concept of levels does not apply. Consider a dimension containing components for various products. Because different products contain totally different parts, no notion of a level exists.

The distinguishing feature of a network hierarchy is that nodes can contain more than one parent. A family tree is a common example.

OLE DB for OLAP supports all four types of 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.


Datasets and Multidimensional Expressions

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 store 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.


Multidimensional Expressions Overview

Multidimensional OLE DB for OLAP provides a syntax called multidimensional expressions (MDX) to specify a dataset. MDX is a set of macros defined on top of SQL. These macros are generally useful for querying a multidimensional data source, and their precise semantics are defined by means of their structured query language mappings.

Multidimensional expressions are discussed in greater detail in Chapter 25: Multidimensional Expressions. Relevant topics include the following:

  • Parts of an MDX Statement
  • Axis and Slicer Dimensions
  • Establishing Cube Context
  • Namespace and Unique Names
  • Using the OLE DB Command Object


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 multidimensional data as a set of tables (for example, a central fact table with associated dimension tables), 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 27: Mapping MDX to SQL Statements.

This approach is suited ideally for relational OLAP servers. Rather than implementing support for a new language, they can leverage their existing SQL language implementation.

Note Relational OLAP is a term used to describe OLAP systems that are based on a relational DBMS. A relational OLAP server is usually a full-fledged relational DBMS that stores multidimensional data as a set of tables. This is in contrast to multidimensional OLAP, which refers to OLAP systems that store data in inherently multidimensional structures.

Providers with Nontabular Storage

Providers with nontabular storage are usually multidimensional OLAP systems. Multidimensional OLAP servers typically have storage structures that are inherently multidimensional. MDX statements provide a direct expression of the query capabilities of these servers. If a multidimensional OLAP 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.


Delimited Identifiers

When used in the context of a DBMS, 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 follows:

SELECT [Microsoft Office].Children ON COLUMNS, Quarters.MEMBERS ON ROWS
FROM SalesCube

Now the preceding 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 stores, are strongly encouraged to delimit all identifiers used in an MDX statement or in a schema rowset restriction.


Delimited Identifiers Returned from the Provider

When the consumer inputs a query string or schema rowset restriction, identifiers need to be quoted. However, there are occasions when a provider returns delimited identifiers, which could be the same quotation marks.

Consumers frequently use the information returned from schema rowsets to formulate a query. For example, the consumer user interface might 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 discussed in the section "Namespace and Unique Names," one possible way a provider can generate a unique name is by constructing it out of various component names, using a period (.) as the separator between these component names. Such providers should make sure that each component is delimited; otherwise, the query might have trouble parsing.

For example, two cities share the same name: New Albany in New Jersey, USA, and New Albany in Nova Scotia, Canada. If the provider returned the MEMBER_UNIQUE_NAME column of the MEMBERS rowset for these two members without using delimit characters, the returned values would be USA.New Jersey.New Albany and Canada.Nova Scotia.New Albany, respectively. The consumer could put delimit characters around each name: [USA.New Jersey.New Albany] and [Canada.Nova Scotia.New Albany]. However, the provider would be unable to determine 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. Therefore, 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 should be 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.

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 "Namespace and Unique Names" in Chapter 25: Multidimensional Expressions.


Differences in Delimit Characters

The examples in the preceding sections (see "Delimited Identifiers" and "Delimited Identifiers Returned from the Provider") used square brackets ([ ]) as delimit characters, which is a common implementation. However, it is possible to use other delimit characters as well. A consumer cannot rely on any particular delimit character when working with multiple data stores but should check for a provider's delimit characters. This ensures interoperability.

The OLE DB method IDBInfo::GetLiteralInfo allows consumers to interrogate providers to determine their delimit characters. The consumer should use DBLITERAL = DBLITERAL_QUOTE. In the returned DBLITERALINFO structure, the pwszLiteralValue column contains the delimit characters.

If the pwszLiteralValue column contains just one character, the same character is used at both ends of the delimited identifier. A common example is the double quotation mark ("), which is used as follows: "USA"."New Jersey"."New Albany". If the pwszLiteralValue column contains two characters, 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. 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, 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 Products dimensions are oriented along the x-axis of a dataset, a coordinate along this axis might 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. In this example, determining a coordinate along the x-axis 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 include the following:

  • A formatted string, which is the form of cell data that can be displayed
  • The cell ordinal number
  • Display information such as foreground color or background color

These "information pieces" are collectively called cell properties.

Postprocessing and Specifying New Datasets

After obtaining a new dataset, the consumer typically will perform several operations on it, such as pivoting, changing dimension orientation, sorting, or rotating.

OLE DB for OLAP solves this problem by employing 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.

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 identifying the postprocessing operators as a subset of the original command operators, defining semantics for each of these operators, and so on.


Describing the Axes

A dataset is a discrete collection consisting of data points dimensioned by the axes. To manipulate the dataset, the application must get 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, 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. IMDDataset::GetAxisInfo also returns the number of axes in the dataset. For more information about the structure of MDAXISINFO, see "MDAXISINFO Structure" in Chapter 23: OLE DB for OLAP Objects and Schema Rowsets.

Axis Rowset

After the consumer obtains basic information about the axes, the next step is to get detailed information about the structure of each tuple on the axis. This means that the consumer must get the properties of each member of each tuple. (Recall that a tuple is made of one or more components and that each component is a member.) All this information is available as a rowset, called the axis rowset. There is one axis rowset per axis. You can retrieve an axis rowset by using 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 "Navigating Cells in a Dataset" for more information), which contains the ordinal number of the tuple that a given row corresponds to. The remaining columns contain member properties.

Whether or not you choose any member properties by using the DIMENSION PROPERTIES clause (see "Retrieving Member Properties" in Chapter 25: Multidimensional Expressions), the axis rowset always contains several properties for each dimension member, such as its unique name, the level to which it belongs, its display caption, and so on. For a list of these properties, see "Axis Rowsets" in Chapter 23: OLE DB for OLAP Objects and Schema Rowsets.

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 the names and data types of the columns of the axis rowset.


Finding and Returning Slicer Information

Every dimension in the cube is either an axis dimension or a slicer dimension. For the purposes of returning slicer dimension information, the set of slicer dimensions is treated as if it were on a special axis, called the slicer axis. Slicer axis information is returned by the MDAXISINFO structure. In the following illustration, the slicer dimension is Time with "1991" as the selected member.

Finding Slicer Information

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. 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 because most providers support only one tuple for a slicer.

The slicer axis rowset for the SalesData Example is shown in the following table. Because the rowset has 16 columns and all will not fit on the same page, only the MEMBER_UNIQUE_NAME and LEVEL_UNIQUE_NAME columns are shown for each dimension.

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. On the other hand, the name of the TIME_LEVEL is "Years" because it reflects a specific Time dimension hierarchy. 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 if rgpwszDimensionNames is [Years, Measures, Products], then the columns for the 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.

Returning Slicer Information

After finding how the axes are laid out, the next step is to find out the structure of the slicer dimensions and which members are chosen from the slicer dimensions. 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. The 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 support only one tuple for a slicer.


Navigating Cells in a Dataset

After the structure of the axes is known, the application can navigate the cells in the dataset. Navigation is the process of traversing through the various cells in the dataset and zeroing in on the desired set of cells. To do this, there must be a way to refer to cells that determines their relative position in the dataset. In OLE DB for OLAP, there are three modes of navigation.

Navigating by Cell Ordinal

Navigating by cell ordinal, the application simply refers to the ordinal number of the desired cell (or the starting cell of the desired set of cells). OLE DB for OLAP assigns each coordinate (tuple) on an axis a number, called a tuple ordinal, starting from zero and increasing rightward along the axis. 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.

Navigating by Axes Coordinates

Navigating by axes coordinates, the application identifies a cell by identifying the tuple on each axis at the intersection of which the cell lies. These are the set of coordinates that identify the desired cells. For example, the application specifies the cell at the intersection of the coordinates (5, 3, 2) (5th coordinate on the x-axis, 3rd coordinate on the y-axis, and 2nd coordinate on the z-axis).

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.

Navigating by Member Names

Navigating by member names is a variation on the previous method. The application identifies a cell by a set of members in a tuple. Each tuple's constituent parts are used to identify the desired cells. For example, the application might request the data value for Sales in USA for 1992, Qtr1, for all SalesReps. In a sense, this is the natural way to think about the cells in a dataset.

Two OLE DB for OLAP methods are used to navigate by member names: IMDFind::FindCell takes an array of members and a starting cell ordinal and returns the corresponding cell ordinal; then the IMDDataset::GetCellData method gets the cell data.

For more information about the formula used to derive a cell ordinal given its tuple ordinals, see the description of IMDDataset::GetCellData in the interface reference section.


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, the data type of each cell, and cell properties.

A Conceptual Model for Cell Data

Each cell in the dataset has the following pieces of data associated with it: the data value and two required properties. OLE DB for OLAP requires all providers to support the FORMATTED_VALUE and CELL_ORDINAL properties. Optional cell properties can be selected and retrieved using the CELL PROPERTIES clause.

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.

The following SalesData dataset example illustrates this model:

Describing Cell Properties

Because properties of a cell are modeled as columns in a row, you can use the interface IColumnsInfo to determine the name, data type, number of bytes required to transfer the data, and other related information about each property.

Data Type of VALUE

The VALUE property of a cell can contain data of different types. The VALUE property is cell-specific. For example, consider the following 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 appears as follows:

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.

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 and 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. IColumnsInfo is not designed to describe rowsets for which the data type of a column varies based on the row. (This 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 About 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 required dataset object interface 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.

Fetching Cell Data

OLE DB for OLAP uses the same model that OLE DB uses for retrieving data. In this model, the consumer follows these steps:

  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, and length of the column data. This information is available through the methods in the IColumnsInfo interface.
  2. Chooses the columns (cell properties) in the dataset's rowset that it wants to retrieve.
  3. Allocates an array of DBBINDING structures, one structure for each 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, 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. 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. Calls IMDDataset::GetCellData. This method takes the following arguments:
    • The accessor created in step 4.
    • The 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.

See Part 1 of the OLE DB Programmer's Reference for more information about accessors, the DBBINDING structure, and data types.


Stateless Navigation

The methods that navigate the dataset do not depend on the concept of a "current position." In other words, none of the navigation/fetch interfaces in OLE DB for OLAP assume that there is a current cell. To fetch a cell, the application specifies the ordinal position of the cell 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 about the cell on which the consumer is positioned.


Creating Multiple Datasets

Multiple datasets cannot be created on the same command object by using the same command unless the associated rowsets are closed. This means it is not possible to modify the command specification to create another dataset using the same command object if axes rowsets, range rowsets, or flattened rowsets remain open.

If the application wants to operate on the dataset to create a new dataset, it must either close the original dataset before reusing the command or create a new command object.


Getting the Command Object from a Dataset

In OLE DB, a method on an object returns an interface on the object that created it. For example, IGetDataSource::GetDataSource is a method on the session object that returns an interface pointer on the data source object that created it.

In OLE DB for OLAP, the dataset object needs a method that returns an interface pointer on the data source object that created it. IMDDataset::GetSpecification fills that role: The GetSpecification method retrieves the object that contains the dataset specification—that is, the command object.


Chapter 23: 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, some of 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 this chapter:

  • Enumerator object. OLE DB for OLAP introduces one change to the sources rowset returned by the enumerator.
  • Data source 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.
  • Session object. OLE DB for OLAP uses a number of OLE DB schema rowsets and introduces the following OLAP-specific rowsets:
    • ACTIONS rowset
    • CUBES rowset
    • DIMENSIONS rowset
    • FUNCTIONS rowset
    • HIERARCHIES rowset
    • LEVELS rowset
    • MEASURES rowset
    • MEMBERS rowset
    • PROPERTIES rowset
    • SETS rowset
  • Rowset object. As in the rest of OLE DB, the rowset object plays an essential role in OLE DB for OLAP.
  • Command object. The command object supports text commands in the MDX syntax by using the OLE DB ICommandText interface.
  • Dataset object. The description of a dataset's axes also requires descriptions of the MDAXISINFO structure and the layout of the axes rowsets.


Enumerator Object

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 must be able to easily determine the type of a given data source object. For example, a data source object "locator" may implement option buttons that allow the user to browse for TDPs, MDPs, or both TDPs and MDPs. It is useful for the provider to expose data source object types at enumeration time.

OLE DB for OLAP adds another source type to the sources rowset returned by the enumerator.

The value DBSOURCETYPE_DATASOURCE indicates that a data store can be accessed by a particular type of provider: TDP or MDP. The following new type has been added to the source type column of the sources rowset:

#define DBSOURCETYPE_DATASOURCE_MDP 3
#define DBSOURCETYPE_DATASOURCE_TDP DBSOURCETYPE_DATASOURCE

This value enables an application to find out whether a row in the sources rowset corresponds to an enumerator, a tabular data provider, or a multidimensional data provider. If a data store can be accessed by both a TDP and an MDP, 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.


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 section Provider Registry Entries in Chapter 18: Programming Considerations, 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


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 | Short description
MDPROP_FLATTENING_SUPPORT | Flattening support.
MDPROP_NAMED_LEVELS | Support for named levels. A bitmask specifying whether the provider supports named levels and/or numbered levels.
DBPROP_DATASOURCE_TYPE | Data source type.
MDPROP_MDX_JOINCUBES | Support for query-joining multiple cubes.
MDPROP_AXES | 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 | Support for updating aggregated cells.
MDPROP_RANGEROWSET | Support for cell updates.
MDPROP_MDX_SLICER | The capabilities in the WHERE clause of an MDX statement.
MDPROP_MDX_OBJQUALIFICATION | Provider's ability to qualify a cube name. A bitmask specifying how multidimensional schema object names can be qualified in an MDX expression.
MDPROP_MDX_OUTERREFERENCE | Support for an outer reference in an MDX statement.
MDPROP_MDX_QUERYBYPROPERTY | Support for querying by property values in an MDX statement.
MDPROP_MDX_CASESUPPORT | Support for MDX case statements. A bitmask specifying the support in a provider for case statements.
MDPROP_MDX_STRING_COMPOP | Support for string comparison operators other than equals and not-equals operators. A bitmask specifying the support in a provider for string comparison operators.
MDPROP_MDX_DESCFLAGS | Support for various <desc flag> values in the DESCENDANTS function.
MDPROP_MDX_SET_FUNCTIONS | Support for various set functions.
MDPROP_MDX_MEMBER_FUNCTIONS | Support for various member functions.
MDPROP_MDX_NUMERIC_FUNCTIONS | Support for various numeric functions.
MDPROP_MDX_FORMULAS | Support for creation of named sets and calculated members.
MDPROP_MDX_NONMEASURE_EXPRESSIONS | The capabilities in the <numeric_value_expression> argument of set functions.


Keywords

When a keyword is used in a query, it must be enclosed in double quotation marks (""). The OLE DB data source object interface IDBInfo::GetKeywords returns all keywords specific to the provider in use, including keywords not listed here.

A consumer can decide which identifier must be quoted by using IDBInfo::GetKeywords.

Note It is a good practice to quote all identifiers in an MDX expression, whether or not they are keywords. However, it is not necessary to quote a quoted name, such as a unique name returned from a schema rowset.

If the provider also declares itself to be a TDP, it must return (in addition to the following list) keywords found in the reference section under IDBInfo::GetKeywords.

Following is an alphabetical list of the new keywords introduced by MDX.

A

AFTER | AGGREGATE | ANCESTOR
AND | AS | AXIS

B

BACK_COLOR | BASC | BDESC
BEFORE | BEFORE_AND_AFTER | BOTTOMCOUNT
BOTTOMPERCENT | BOTTOMSUM |

C

CHAPTERS | CHILDREN | CHILDREN_CARDINALITY
CLOSINGPERIOD | COALESCEEMPTY | COLUMNS
CORRELATION | COUSIN | COVARIANCE
CROSSJOIN | CUBE | CUBE_NAME
CURRENTMEMBER | CASE | CATALOG_NAME
CELL | CELL_ORDINAL |

D

DEFAULTMEMBER | DESCENDANTS | DESCRIPTION
DIMENSION | DIMENSION_UNIQUE_NAME | DRILLDOWNLEVEL
DRILLDOWNLEVELBOTTOM | DRILLDOWNLEVELTOP | DRILLDOWNMEMBER
DRILLDOWNMEMBERBOTTOM | DRILLDOWNMEMBERTOP | DRILLUPLEVEL
DRILLUPMEMBER |  |

E

ELSE | EMPTY | END
EXCEPT | EXTRACT |

F

FILTER | FIRSTCHILD | FIRSTSIBLING
FONT_FLAGS | FONT_NAME | FONT_SIZE
FORE_COLOR | FORMAT_STRING | FORMATTED_VALUE
FROM |  |

G

GENERATE

H

HIERARCHIZE HIERARCHY_UNIQUE_NAME

I

IIF | INTERSECT | ISEMPTY
ITEM |  |

L

LAG | LASTCHILD | LASTPERIODS
LASTSIBLING | LEAD | LEVEL_UNIQUE_NAME
LEVELS | LINREGINTERCEPT | LINREGR2
LINREGSLOPE | LINREGVARIANCE |

M

MEDIAN | MEMBER | MEMBER_CAPTION
MEMBER_GUID | MEMBER_NAME | MEMBER_ORDINAL
MEMBER_TYPE | MEMBER_UNIQUE_NAME | MEMBERS
MTD |  |

N

NEXTMEMBER NON NOT

O

ON | OPENINGPERIOD | OR
ORDER |  |

P

PAGES | PARALLELPERIOD | PARENT
PARENT_LEVEL | PARENT_UNIQUE_NAME | PERIODSTODATE
PREVMEMBER | PROPERTIES |

Q

QTD

R

RANK REFRESH ROWS

S

SCHEMA_NAME | SECTIONS | SELECT
SELF | SELF_AND_AFTER | SELF_AND_BEFORE
SELF_BEFORE_AFTER | SET | SOLVE_ORDER
STDEV |  |

T

TOGGLEDRILLSTATE | TOPCOUNT | TOPPERCENT
TOPSUM |  |

U

UNION

V

VALUE VAR

W

WHEN | WHERE | WITH
WTD |  |

X

XOR

Y

YTD


Session Object

Exactly as in OLE DB, an OLE DB for OLAP consumer creates a session object by using a data source object (by calling IDBCreateSession::CreateSession). A session establishes the current catalog or schema of the consumer. Within this context are zero or more cubes.

For providers categorized as TDP or TDP/MDP, the cotype definition of the OLE DB for OLAP session object is as follows:

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;
}

For providers categorized exclusively as MDP, the cotype definition of the OLE DB for OLAP session object is as follows:

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


Multidimensional Schema Object Names

Rules for naming objects in a multidimensional schema are provider-specific. For example, a provider might have a restriction that all cube names should be fewer than 15 characters long and that the character "&" may not occur in a cube name.

IDBInfo::GetLiteralInfo, using the DBLITERALINFO structure, provides the means for retrieving information about these types of literals.

OLE DB for OLAP adds the constants described in the following table to the DBLITERAL enumeration.

Note The pwszLiteralValue of the DBLITERALINFO structure is not used for any of these values of DBLITERAL.

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.


Additions to IDBSchemaRowset

Consumers can get information about the cubes, dimensions, hierarchies, levels, and members available from the data source object by browsing multidimensional schema rowsets using the IDBSchemaRowset interface.

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). OLE DB for OLAP assumes that a multidimensional data store accessed by an MDP has catalogs, which contain schemas, which contain cubes. Of course, some providers might have only catalogs, 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 for each schema object on the schema rowset. The number of restriction columns for each schema rowset is defined as a constant 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_ACTIONS | 7 | CATALOG_NAME SCHEMA_NAME CUBE_NAME ACTION_NAME ACTION_TYPE COORDINATE COORDINATE_TYPE
MDSCHEMA_CUBES | 3 | CATALOG_NAME SCHEMA_NAME CUBE_NAME
MDSCHEMA_DIMENSIONS | 5 | CATALOG_NAME SCHEMA_NAME CUBE_NAME DIMENSION_NAME DIMENSION_UNIQUE_NAME
MDSCHEMA_FUNCTIONS | 4 | LIBRARY_NAME INTERFACE_NAME FUNCTION_NAME RESTRICTIONS
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 Tree operator
MDSCHEMA_SETS | 5 | CATALOG_NAME SCHEMA_NAME CUBE_NAME SET_NAME SCOPE

MDP-Supported GUIDs

The provider must support the following GUIDs if it lists itself as an MDP in the sources rowset returned by ISourcesRowset::GetSourcesRowset:

  • MDSCHEMA_CUBES
  • MDSCHEMA_DIMENSIONS
  • MDSCHEMA_HIERARCHIES
  • MDSCHEMA_LEVELS
  • MDSCHEMA_MEASURES
  • MDSCHEMA_MEMBERS
  • MDSCHEMA_PROPERTIES
  • MDSCHEMA_SETS

Support for MDSCHEMA_LEVELS is not required if the provider has set the MDPROP_NAMED_LEVELS property to MDPROP_NL_UNSUPPORTED. The provider can also ignore level-based restrictions in schema rowsets such as PROPERTIES or 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. Furthermore, 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 appropriate interface description.

  • All rowsets have mandatory columns, and some have optional ones.

Note The CUBE_NAME column in all schema rowsets except the ACTIONS and CUBES rowset can be NULL. This does not mean that the provider does not support cubes; it means that the dimension, hierarchy, level, member, or property does not belong and is not owned by any cube. For more information, see "Hypercubes and Multicubes" in Chapter 22: OLE DB for OLAP Concepts.

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—information 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 or DBSCHEMA_SCHEMATA rowset. A consumer can call IDBSchemaRowset::GetSchemas to find out which schema rowsets are supported. If the provider does not support catalogs or schemas, specifying a restriction on either of these returns DB_E_NOTSUPPORTED.


Object Names in Schema Rowsets

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, levels, members, or properties. Many schema rowsets contain columns of the following types: _NAME, _UNIQUE_NAME, _CAPTION, and DESCRIPTION. These four concepts are related but not identical.

Consider the entry in the MEMBERS rowset for the city of Portland, 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. However, 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 to be a displayable name: For instance, it could be "Portland" or "Portland, the city of roses." The DESCRIPTION column is a string that literally can be anything. 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 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 22: OLE DB for OLAP Concepts.

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 the cube, schema, or catalog). It has a column for its name (NAME) and for the display caption (CAPTION).


Sizes of UNIQUE_NAME Columns

Many providers generate the values of _UNIQUE_NAME columns by concatenating several names. For example, the unique name of a level might be <dim_name>.<hier_name>.<level_name>. In such a case, the provider might not be able to report the maximum sizes of such columns (when methods on the IColumnsInfo interface are called to determine a size). If the provider is unable to determine the maximum column size, it returns a size of ~0 (all the bits are set to 1) to indicate that it is unable to accurately determine the size.


Using Schema Rowsets in OLE DB for OLAP

The schema rowsets in OLE DB for OLAP are used to define or interpret the basic relationships between views of the data. Each schema rowset has a special purpose and associated benefits. For a detailed description of each rowset, see the following topics:

  • ACTIONS Rowset
  • CUBES Rowset
  • DIMENSIONS Rowset
  • FUNCTIONS rowset
  • HIERARCHIES Rowset
  • LEVELS Rowset
  • MEASURES Rowset
  • MEMBERS Rowset
  • PROPERTIES Rowset
  • SETS Rowset

Note With the release of MDAC 2.6, the ANNOTATIONS column was added for the CUBES, DIMENSIONS, HIERARCHIES, and MEMBERS schema rowsets. The ANNOTATIONS column allows the user to store notes, in XML format, with the data in the schema rowset. For more information about this feature, see "The ANNOTATIONS Column," later in this chapter.


ACTIONS Rowset

The ACTIONS schema rowset allows OLAP tools to be easily integrated with other applications by providing descriptive and content information to perform the action. The following table describes the available columns in the ACTIONS schema rowset. (The default sort order is CATALOG_NAME, SCHEMA_NAME, and CUBE_NAME.)

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | Optional. The catalog name.
SCHEMA_NAME | DBTYPE_WSTR | Optional. The name of the schema to which this cube belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Required. Name of the cube.
ACTION_NAME | DBTYPE_WSTR | Optional. The action name. This column value should not be changed and usually is not presented to users.
ACTION_TYPE | DBTYPE_WSTR | Optional. One of the enumerated action types.
COORDINATE | DBTYPE_WSTR | Required. A string indicating the "coordinate" of the action. Its format is dependent on COORDINATE_TYPE. Generally used as a restriction when querying for the actions available on a particular object.
COORDINATE_TYPE DBTYPE_UI4 Required. One of the following:
  • MDACTION_COORDINATE_CUBE
  • MDACTION_COORDINATE_DIMENSION
  • MDACTION_COORDINATE_LEVEL
  • MDACTION_COORDINATE_MEMBER
  • MDACTION_COORDINATE_SET
  • MDACTION_COORDINATE_CELL

The format of COORDINATE is determined by this value.

CAPTION | DBTYPE_WSTR | Optional. The friendly name for the action. Will be presented to users and can be localized.
DESCRIPTION | DBTYPE_WSTR | Optional. A description of the action. Will be presented to users and can be localized.
CONTENT | DBTYPE_WSTR | Optional. The string containing the action. Its meaning is dependent on the action type.
APPLICATION DBTYPE_WSTR Optional. The application "name." This is a loosely defined field that might be useful in the following circumstances:
  • Distinguishing proprietary actions
  • Logically grouping actions for presentation to the user

The APPLICATION column should not be interpreted to contain a path name.


Using the ACTIONS Rowset

The ACTIONS rowset provides a standard mechanism for integrating OLAP tools with other applications. OLAP client applications typically provide rich interfaces for browsing data, but these are often closed systems, making it difficult to provide any level of integration between an OLAP client and theiroperational systems.

This ACTIONS rowset allows data residing in a multidimensional data store to be "linked" to processes appropriate to the data. For example, a grocery store might keep inventory information in a data store and might use any number of OLE DB for OLAP client applications from various vendors to analyze the data. Using the ACTIONS rowset as a vehicle for retrieving a predefined set of client-side actions, the database administrator can provide a link between inventory items and the store's purchasing application. When the client application presents a list of items in the inventory, the user can execute the predefined action to remedy a shortage. The process invokes the purchasing application and presents the user with a purchase order form. In another scenario, the predefined action might simply be a link to a URL, allowing the user, after viewing the inventory, to view purchase orders posted on the company's intranet.

Note OLE DB for OLAP does not specify how client actions are created, stored, or processed in the OLE DB for OLAP provider. Instead, it specifies how client applications determine what client actions are possible on a given object and how the process is invoked. It's the provider's responsibility to associate the action with the data, and it's the database administrator's responsibility to determine how to store the action.

To execute client-side actions, the following steps are required:

  1. The client application calls IDBSchemaRowset to query the ACTIONS schema rowset to determine what actions are allowed on a given object and makes the choice of actions available to the user. An action can be associated with cubes, dimensions, levels, members, cells, or sets.
  2. When an action is selected, the client application executes a simple series of predefined steps to perform the action. For example, a client application might query the ACTIONS schema rowset to determine all of the actions that can be performed on a particular dimension.
  3. The application determines the appropriate action, as defined in the ACTIONS schema rowset, to execute and then attempts to execute that action.

For more information about using the ACTIONS schema rowset, see the following topics:

  • Querying for Actions
  • Types of Predefined Actions
  • COORDINATE Column Format in the ACTIONS Rowset


Querying for Actions

The actions to be performed are stored with the data but exposed in the ACTIONS rowset. The rowset holds four significant pieces of information, as defined by the following columns: CUBE_NAME, ACTION_NAME, ACTION_TYPE, COORDINATE, and COORDINATE_TYPE.

Client applications should exercise caution when querying the ACTIONS schema rowset. Restricting the query as much as possible produces the most efficient results. The provider might return an error if a query is made without setting the required restriction on columns. For restriction requirements on the ACTIONS rowset, as well as for the restriction requirements on the other OLE DB for OLAP schema rowsets, see the description of columns in "Additions to IDBSchemaRowset," earlier in this chapter.

When to Query for Actions

Client applications should query for actions in response to user interface events, such as when displaying a context-sensitive menu. For example, if the user right-clicks the Sales dimension member, the client might query with the restrictions listed in the following table.

Restricted column | Contained value
CUBE_NAME | Sales
COORDINATE | [Products].[Beverages].[Soda]
COORDINATE_TYPE | MDACTION_COORDINATE_MEMBER
CATEGORIES | NULL

This would return the set of actions available on the [Products].[Beverages].[Soda] member, which the client application could then display in a context-sensitive menu.

Note Each time the client application requests an action on an object, more computing resources are used, increasing the operation's overhead. Therefore, client applications should avoid querying for actions on a large number of objects, such as for every cell in a dataset. Restricting the query as much as possible produces the most efficient results.


Types of Predefined Actions

OLE DB for OLAP provides for standard actions that have predefined triggering methods. This enables client applications to execute the standard action types without needing to understand the nature of the action. The following table lists the defined action types.

Predefined action types | Interpretation
URL | The returned string in the ACTIONS rowset CONTENT column is a URL that should be launched through an Internet browser.
HTML | The returned string in the CONTENT column is an HTML script. The script should be saved to a file and rendered through an Internet browser. The script can be of any format that can be interpreted by the browser—for example, an .asp file containing scripting language commands.
STATEMENT | The string in the CONTENT column contains a statement that is executed by calling ICommandText::SetCommandText and then calling ICommand::Execute.
DATASET | The string in the CONTENT column is an MDX statement. As with STATEMENT action types, the statement should be executed on an OLE DB command object by calling ICommandText::SetCommandText and ICommand::Execute. The client application should set the riid parameter in Execute to IID_IMDDataset.
ROWSET | Like DATASET, but the client application should call ICommand to return IID_IRowset.
COMMAND | The string in the CONTENT column contains an executable file name and, possibly, additional parameters. The client application should execute the command without waiting for its completion.
PROPRIETARY | The string in the CONTENT column is in a proprietary format. The client application should not execute the action unless it has custom knowledge of the proprietary format.


COORDINATE Column Format in the ACTIONS Rowset

The format of the COORDINATE column is dependent on the value of COORDINATE_TYPE described in the following table.

Value | Description
MDACTION_COORDINATE_CUBE | The COORDINATE column contains the cube name. If CUBE_NAME is also specified in the query, it must match the value in COORDINATE. If not, the provider should return an error.
MDACTION_COORDINATE_DIMENSION | The column contains a dimension specification.
MDACTION_COORDINATE_LEVEL | The column contains a level specification.
MDACTION_COORDINATE_MEMBER | The column specifies a member.
MDACTION_COORDINATE_SET | The column specifies the name of a set.
MDACTION_COORDINATE_CELL | The column contains a tuple that identifies a single cell.


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 structure described in the following table.

The default sort order is CATALOG_NAME, SCHEMA_NAME, and CUBE_NAME.

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | Optional. The name of the catalog to which this cube belongs. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | Optional. The name of the schema to which this cube belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Required. Name of the cube.
CUBE_TYPE DBTYPE_WSTR Required. Cube type. A provider-specific value or one of the following:
  • "CUBE"
  • "VIRTUAL CUBE"
CUBE_GUID | DBTYPE_GUID | Optional. Cube GUID. NULL if no GUID exists.
CREATED_ON | DBTYPE_DBTIMESTAMP | Optional. Date and time of cube creation. NULL if unknown.
LAST_SCHEMA_UPDATE | DBTYPE_DBTIMESTAMP | Optional. Date and time of last schema update. NULL if unknown.
SCHEMA_UPDATED_BY | DBTYPE_WSTR | Optional. User ID of the person who last updated the schema. NULL if unknown.
LAST_DATA_UPDATE | DBTYPE_DBTIMESTAMP | Optional. Date and time of last data update. NULL if unknown.
DATA_UPDATED_BY | DBTYPE_WSTR | Optional. User ID of the person who last updated the data. NULL if unknown.
DESCRIPTION | DBTYPE_WSTR | Optional. A human-readable description of the cube. If no description exists, this column is NULL.
ANNOTATIONS | DBTYPE_WSTR | Optional. A string containing annotations added by analysts through their client applications. For more information about this feature, see "The ANNOTATIONS Column," later in this chapter.


DIMENSIONS Rowset

The DIMENSIONS rowset contains information about the dimensions in a given cube. It has one row for each dimension.

Note Because OLE DB for OLAP treats measures as just another dimension, a row is returned in the DIMENSIONS rowset for the Measures dimension.

The default sort order is CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, and DIMENSION_NAME.

The rowset has the structure described in the following table.

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | Optional. The name of the catalog to which this dimension belongs. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | Optional. The name of the schema to which this dimension belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Required. 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 22: OLE DB for OLAP Concepts.
DIMENSION_NAME | DBTYPE_WSTR | Required. Name of the dimension. If a dimension is part of more than one cube, there is one row for each cube/dimension combination. The field rgpwszDimensionNames, in the MDAXISINFO structure, corresponds to this element. For more information, see "Hypercubes and Multicubes" in Chapter 22: OLE DB for OLAP Concepts.
DIMENSION_UNIQUE_NAME | DBTYPE_WSTR | Required. Unique name of the dimension. For providers that generate unique names by qualification, each component of this name is delimited.
DIMENSION_GUID | DBTYPE_GUID | Optional. The GUID of the dimension. NULL if no GUID exists.
DIMENSION_CAPTION | DBTYPE_WSTR | Required. 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 | Required. Ordinal number of the dimension, among the group of dimensions that form the cube.
DIMENSION_TYPE DBTYPE_I2 Required. The dimension type. Can be one of the following values:
  • MD_DIMTYPE_TIME, which indicates a Time dimension.
  • MD_DIMTYPE_MEASURE, which indicates a Measures dimension.
  • MD_DIMTYPE_OTHER, which indicates the dimension is neither a Time dimension nor a Measures dimension.
  • MD_DIMTYPE_UNKNOWN, which indicates that the provider is unable to classify the dimension.
DIMENSION_CARDINALITY | DBTYPE_UI4 | Required. The number of members in the dimension. This value can be an approximation of the real cardinality. Consumers should not rely on this to be accurate.
DEFAULT_HIERARCHY | DBTYPE_WSTR | Required. Indicates the default hierarchy for this dimension. This is a unique name. Each dimension must have a default hierarchy.
DESCRIPTION | DBTYPE_WSTR | Optional. A human-readable description of the dimension. NULL if no description exists.
ANNOTATIONS | DBTYPE_WSTR | Optional. A string containing annotations added by analysts through their client applications. For more information about this feature, see "The ANNOTATIONS Column," later in this chapter.


FUNCTIONS Rowset

The FUNCTIONS rowset exposes all functions supported by the MDP, including user-defined functions (UDF) registered on the client.

The default sort order is ORIGIN, INTERFACE_NAME, and FUNCTION_NAME.

The rowset has the structure described in the following table.

Column name | Type indicator | Description
FUNCTION_NAME | DBTYPE_WSTR | Required. The name of the function.
DESCRIPTION | DBTYPE_WSTR | Optional. A human-readable description of the function.
PARAM_LIST | DBTYPE_WSTR | Optional. A comma delimited list of parameters in Visual Basic style, for example: Name as String.
RETURN_TYPE | DBTYPE_I4 | Required. VARTYPE of the return data type of the function.
ORIGIN | DBTYPE_I4 | Required. Indigenous or user-defined function, such as MSMD_SCHEMA_FUNCTIONS_ORIGIN_MSOLAP, or a UDF such as MSMD_FUNCTIONS_ORIGIN_UDF, which are both supported by MSOLAP.
INTERFACE_NAME | DBTYPE_WSTR | Required. Name of the interface for UDF and Group name for the MDX functions.
LIBRARY_NAME | DBTYPE_WSTR | Optional. Name of the type library for UDFs. NULL for MDX functions.
DLL_NAME | DBTYPE_WSTR | Optional. Name of the .dll or .exe in which this function is implemented. NULL for MDX functions
HELP_FILE | DBTYPE_WSTR | Optional. Name of the help file in which this function is documented. NULL for MDX functions.
HELP_CONTEXT | DBTYPE_I4 | Optional. Help context ID for this function in the help file.
OBJECT | DBTYPE_ WSTR | Optional. Object, such as dimension or level, to which function applies. For example, Products.count uses the dimension Products, with "count" as the function.


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 default sort order is CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, DIMENSION_UNIQUE_NAME, and HIERARCHY_NAME.

The rowset structure is described in the following table.

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | Optional. The name of the catalog to which this hierarchy belongs. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | Optional. The name of the schema to which this hierarchy belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Required. Name of the cube to which this hierarchy belongs.
DIMENSION_UNIQUE_NAME | DBTYPE_WSTR | Required. 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 | Required. 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 | Required. 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 | Optional. Hierarchy GUID. NULL if there is no GUID.
HIERARCHY_CAPTION | DBTYPE_WSTR | Required. 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 Required. 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 Measures dimension.
  • MD_DIMTYPE_OTHER, which indicates that the dimension is neither a Time dimension nor a Measures dimension.
  • MD_DIMTYPE_UNKNOWN, which indicates that the provider is unable to classify the dimension.
HIERARCHY_CARDINALITY | DBTYPE_UI4 | Required. The number of members in the hierarchy. Because of the presence of multiple hierarchies, this number might not be the same as DIMENSION_CARDINALITY. This value can be an approximation of the real cardinality. Consumers should not assume that this value is accurate.
DEFAULT_MEMBER | DBTYPE_WSTR | Optional. The default member for this hierarchy. This is a unique name. Every hierarchy must have a default member.
[ALL]_MEMBER | DBTYPE_WSTR | Optional. The member at the highest level of rollup in the hierarchy. This can be different from DEFAULT_MEMBER.
DESCRIPTION | DBTYPE_WSTR | Optional. A human-readable description of the hierarchy. NULL if no description exists.
ANNOTATIONS | DBTYPE_WSTR | Optional. A string containing annotations added by analysts through their client applications. For more information about this feature, see "The ANNOTATIONS Column," later in this chapter.
STRUCTURE DBTYPE_I2 The type of hierarchy. Can be one of the following values:
  • MD_STRUCTURE_FULLYBALANCED
  • MD_STRUCTURE_RAGGEDBALANCED
  • MD_STRUCTURE_UNBALANCED
  • MD_STRUCTURE_NETWORK


LEVELS Rowset

The LEVELS rowset contains information about the levels available in a dimension.

The default sort order is CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, DIMENSION_UNIQUE_NAME, HIERARCHY_UNIQUE_NAME, and LEVEL_NUMBER.

The rowset structure is described in the following table.

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | Optional. The name of the catalog to which this level belongs. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | Optional. The name of the schema to which this level belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Required. Name of the cube to which this level belongs.
DIMENSION_UNIQUE_NAME | DBTYPE_WSTR | Required. 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 | Required. Unique name of the hierarchy. If the level belongs to more than one hierarchy, 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 | Required. Name of the level.
LEVEL_UNIQUE_NAME | DBTYPE_WSTR | Required. Unique name of the level. For providers that generate unique names by qualification, each component of this name is delimited.
LEVEL_GUID | DBTYPE_GUID | Optional. Level GUID. NULL if the level does not have a GUID.
LEVEL_CAPTION | DBTYPE_WSTR | Required. A label or caption associated with the hierarchy. Used primarily for display purposes. If a caption does not exist, LEVEL_NAME is returned.
LEVEL_NUMBER | DBTYPE_UI4 | Required. The distance of the level from the root of the hierarchy. The root level is zero.
LEVEL_CARDINALITY | DBTYPE_UI4 | Required. The number of members in a level. This value can be an approximation of the real cardinality. Consumers should not assume that this value is accurate.
LEVEL_TYPE DBTYPE_I4 Required. The following bitmasks 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

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 might indicate to an application that the members of the level should be expressed in text format (that is, "Sunday", "Monday", "Tuesday", and so on). LEVEL_TYPE_REGULAR can be used to signify a level that does not require any special formatting.
DESCRIPTION | DBTYPE_WSTR | Optional. A human-readable description of the level. NULL if no description exists.


MEASURES Rowset

The MEASURES rowset contains information about the available measures.

The default sort order is CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, and MEASURE_NAME.

The rowset has the structure described in the following table.

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | Optional. The name of the catalog to which this measure belongs. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | Optional. The name of the schema to which this measure belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Required. Name of the cube to which this measure belongs.
MEASURE_NAME | DBTYPE_WSTR | Required. Name of the measure.
MEASURE_UNIQUE_NAME | DBTYPE_WSTR | Required. Unique name of the measure. For providers that generate unique names by qualification, each component of this name is delimited.
MEASURE_CAPTION | DBTYPE_WSTR | Required. A label or caption associated with the measure. Used primarily for display purposes. If a caption does not exist, MEASURE_NAME is returned.
MEASURE_GUID | DBTYPE_GUID | Optional. Measure GUID. NULL if the measure does not have a GUID.
MEASURE_AGGREGATOR DBTYPE_I4 Required. 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 | Required. Data type of the measure. Can be any of the types listed in Appendix A: Data Types.
NUMERIC_PRECISION | DBTYPE_UI2 | Required. 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 | Required. 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 value is NULL.
MEASURE_UNITS | DBTYPE_WSTR | Required. The unit of measurement. 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 | Optional. A human-readable description of the measure. NULL if no description exists.


PROPERTIES Rowset

The PROPERTIES rowset contains information about the available properties for each level of the dimension, assuming that each level defines a class of members. The properties of all members in this class are the same. For a data store that does not support named levels, a dummy level includes all 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. Providers are required to have at least three properties (the VALUE, FORMATTED_VALUE, and CELL_ORDINAL properties of a cell); therefore, 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.

A property can be both a member property and a cell property. By restricting the PROPERTY_TYPE field of this rowset, consumers can retrieve only the member property, only the cell property, or both.

The default sort order is PROPERTY_TYPE, CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, DIMENSION_UNIQUE_NAME, HIERARCHY_UNIQUE_NAME, and LEVEL_UNIQUE_NAME.

Column name | Type indicator | Description
PROPERTY_TYPE DBTYPE_I2 Required. The following bitmasks are used to describe the property type:
  • MDPROP_MEMBER. Property of a member. Can be used in the DIMENSION PROPERTIES clause of the SELECT statement.
  • MDPROP_CELL. Property of a cell. Can be used in the CELL PROPERTIES clause that occurs at the end of the SELECT statement.
CATALOG_NAME | DBTYPE_WSTR | Optional. The name of the catalog to which this property belongs. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | Optional. The name of the schema to which this property belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Required. Name of the cube to which this property belongs.
DIMENSION_UNIQUE_NAME | DBTYPE_WSTR | Required. 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 | Required. 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 | Required. Unique name of the level to which this property belongs. If the provider does not support named levels, 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 | Required. Used for data stores that do not support named levels or have properties on a member-by-member basis. Contains the unique name of the member to which the property belongs. If the property applies to all members in a level, this column is NULL. For providers that generate unique names by qualification, each component of this name is delimited.
PROPERTY_NAME | DBTYPE_WSTR | Required. Name of the property.
PROPERTY_CAPTION | DBTYPE_WSTR | Required. A label or caption associated with the property. Used primarily for display purposes. If a caption does not exist, PROPERTY_NAME is returned.
DATA_TYPE | DBTYPE_UI2 | Required. Data type of the property. Can be any of the types listed in Appendix A: Data Types .
CHARACTER_MAXIMUM_LENGTH DBTYPE_UI4 Required. The maximum possible length of a value in this property. For character, binary, and bit properties, it 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 | Required. 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 | Required. 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 | Required. 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 value is NULL.
DESCRIPTION | DBTYPE_WSTR | Optional. A human-readable description of the property. NULL if no description exists.


MEMBERS Rowset

The MEMBERS rowset contains information about the available members.

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.

The rowset has the structure described in the following table.

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | Optional. The name of the catalog to which this member belongs. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | Optional. The name of the schema to which this member belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Required. Name of the cube to which this member belongs.
DIMENSION_UNIQUE_NAME | DBTYPE_WSTR | Required. 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 | Required. Unique name of the hierarchy. If the member belongs to more than one hierarchy, 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 | Required. 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 | Required. The distance of the member from the root of the hierarchy. The root level is zero.
MEMBER_ORDINAL | DBTYPE_UI4 | Required. Ordinal number of the member. 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, this should be the rank when sorted by MEMBER_NAME.
MEMBER_NAME | DBTYPE_WSTR | Required. Name of the member.
MEMBER_UNIQUE_NAME | DBTYPE_WSTR | Required. Unique name of the member. For providers that generate unique names by qualification, each component of this name is delimited.
MEMBER_TYPE DBTYPE_I4 Required. Type of the member. Can be one of the following values:
  • MDMEMBER_TYPE_REGULAR
  • MDMEMBER_TYPE_ALL
  • MDMEMBER_TYPE_FORMULA
  • MDMEMBER_TYPE_MEASURE
  • MDMEMBER_TYPE_UNKNOWN
  • MDMEMBER_TYPE_FORMULA takes precedence over MDMEMBER_TYPE_MEASURE. Therefore, if there is a formula (calculated) member on the Measures dimension, it is listed as MDMEMBER_TYPE_FORMULA.
MEMBER_GUID | DBTYPE_GUID | Optional. Member GUID. NULL if no GUID exists.
MEMBER_CAPTION | DBTYPE_WSTR | Required. A label or caption associated with the member. Used primarily for display purposes. If a caption does not exist, MEMBER_NAME is returned.
CHILDREN_CARDINALITY | DBTYPE_UI4 | Required. Number of children that the member has. This can be an estimate, so consumers should not rely on this to be the exact count. Providers should return the best estimate possible.
PARENT_LEVEL | DBTYPE_UI4 | Required. The distance of the member's parent from the root level of the hierarchy. The root level is zero.
PARENT_UNIQUE_NAME | DBTYPE_WSTR | Required. 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 | Required. Number of parents that this member has. For more information, see the section "Complex Member Relationships" in this chapter.
DESCRIPTION | DBTYPE_WSTR | Optional. A human-readable description of the member.

For the MEMBERS rowset columns above, a column exists for each property of the member. The type indicator depends on the PROPERTIES rowset from which the structure of these columns can be evaluated.

All other schema rowsets are usually orders of magnitude smaller than the MEMBERS rowset. The MEMBERS rowset has hundreds of thousands of rows, and there is seldom any need for all the members; therefore, applications should not attempt to cache the MEMBERS schema rowset. The common need is for a 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 the DIMENSION PROPERTIES clause in the MDX statement that created the dataset. This puts these additional columns on the axis rowset.

For more information about axis rowsets, see "Axis Rowsets" in this chapter. For more information about the DIMENSION PROPERTIES clause, see "Retrieving Member Properties" in Chapter 25: Multidimensional Expressions.


Restrictions in the MEMBERS Rowset

The MEMBERS rowset can have up to 12 restrictions. The first 11 are the 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
  • MEMBER_TYPE

The 12th restriction, called the tree operation, is not on any particular column of the MEMBERS rowset; rather, it specifies a tree operator. This allows the consumer to specify a MEMBER_UNIQUE_NAME restriction and the tree operator (ANCESTORS, CHILDREN, SIBLINGS, PARENT, DESCENDANTS, SELF) to obtain the desired set of members. The constants for tree operations, described in the following table, comprise a bitmask and can be combined. They are defined in the header file.

Constant | Description
MDTREEOP_ANCESTORS | Returns all of the ancestors.
MDTREEOP_CHILDREN | Returns only the immediate children.
MDTREEOP_SIBLINGS | Returns members on the same level.
MDTREEOP_PARENT | Returns only the immediate parent.
MDTREEOP_SELF | Returns itself in the list of returned rows.
MDTREEOP_DESCENDANTS | Returns all of the descendants.

Effect of Multiple Restrictions

With one exception, 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", the effect is to find "Bill" among "General Managers".

Tree operator restrictions are an exception to this rule. Tree operators operate on a particular member. This member is 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:

  1. All restrictions other than the tree operator are joined by an AND operation and applied to the MEMBERS rowset to get the members.
  2. 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 and Microsoft® Visual 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 nontree 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 two columns that indicate the member's position in the hierarchy: LEVEL_UNIQUE_NAME and LEVEL_NUMBER. However, it is possible for the same member to belong to more than one hierarchy. OLE DB for OLAP requires the MEMBERS rowset to contain a row for each occurrence of a member in a different hierarchy. For example, if the member "January" belongs to two different hierarchies, Quarters and Months, the MEMBERS rowset has two rows, for which MEMBER_UNIQUE_NAME is "January.Quarters" and "January.Months." Because the member name "January" has been qualified here, to make it unique compared to the hierarchy name, the MEMBER_CAPTION for both rows will be the same.


Complex Member Relationships

Providers can 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, only one 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.


SETS Rowset

The SETS rowset contains information about the sets in a schema (or the catalog, if the provider does not support schemas). It has the structure described in the following table.

The default sort order is CATALOG_NAME, SCHEMA_NAME, and CUBE_NAME.

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | Name of the catalog to which the set belongs. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | The name of the schema to which the dimension belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Name of the cube to which the set belongs. Cannot be NULL.
SET_NAME | DBTYPE_WSTR | The name of the set, as specified in the CREATE SET statement.
SCOPE DBTYPE_I4 The scope of the set. One of the following:
  • MDSET_SCOPE_GLOBAL
  • MDSET_SCOPE_SESSION
DESCRIPTION | DBTYPE_WSTR | Optional. A human-readable description of the set.


The ANNOTATIONS Column

ANNOTATIONS is an optional column in the CUBES, DIMENSIONS, HIERARCHIES, and MEMBERS schema rowsets. OLE DB for OLAP supports the ability to annotate values in these schema rowsets, which allows for reminders, notes, and the like to be stored with the data. Groups of cells or individual cells can be annotated. The annotation string is stored as a cell property and must be in XML format. The content of the ANNOTATIONS column is updated through IRowsetChange

Contents of the Annotations String

The annotation string is in XML format, as shown in the following example:

<?xml version="1.0"?>
<annotations>
   <annotation>
   <text>The numbers for this quarter look low.</text>
   <author>J. Smith</author>
   </annotation>
   <annotation>
   <text>Soda sales are slipping.</text>
   </annotation>
</annotations>

The XML schema described below is an example using XML-Data syntax, which can be used to enhance the annotation capability. (The preceding example string is pure XML.)

Note XML-Data is a convenient syntax for describing the data, but the client is not required to understand XML-Data or to have access to the XML schema in order to use annotations.

The following table describes each XML element tag available for annotating data.

Element | Description
anntext | The complete text of the annotation. Can be localized.
anndate | The date with optional time and optional time zone, expressed using ISO 8601 format. Example: "1988-04-07T18:39:09-08:00".
author | The author of the annotation. Can be localized.
initials | The author's initials. Can be localized.

The following XML schema defines the element types listed in the preceding table:

<?xml version="1.0"?>
<Schema name="annotation"
 xmlns="urn:schemas-microsoft-com:xml-data"
 xmlns:dt="urn:schema-microsoft-com:datatypes">
   <ElementType name="anntext"/>
   <datatype dt:type="string"/>
   </ElementType>

   <ElementType name="anndate">
   <datatype dt:type="dateTime.tz"/>
   </ElementType>

   <ElementType name="author"/>
   <datatype dt:type="string"/>
   </ElementType>

   <ElementType name="initials"/>
   <datatype dt:type="string"/>
   </ElementType>

   <ElementType name="singleAnnotation">
   <element type="#anntext" occurs=REQUIRED/>
   <element type="#anndate" occurs=OPTIONAL/>
   <element type="#author" occurs=OPTIONAL/>
   <element type="#initials"occurs=OPTIONAL/>
   </ElementType>

   <ElementType name="annotations">
   <element type="#singleAnnotation" occurs=ONEORMORE/>
   </ElementType>
</Schema>


Command Object

The command object supports text commands in the MDX syntax by using the OLE DB ICommandText interface. Consumers use ICommandText::SetCommandText to specify the command and ICommand::Execute to process the command. For commands that may be used several times, consumers can prepare the command with ICommandPrepare::Prepare. For a complete discussion of the OLE DB command object, see "Command Objects" in Chapter 1: Overview of OLE DB. For additional information about the MDX syntax defined by OLE DB for OLAP and its use, see "Datasets and Multidimensional Expressions" in Chapter 22: OLE DB for OLAP Concepts. Also see Chapter 25: Multidimensional Expressions.

OLE DB for OLAP defines a GUID that denotes the MDX syntax: MDGUID_MDX. This GUID is used in ICommandText::SetCommandText to notify the provider that the query language being used is the MDX syntax defined in Chapter 26: MDX Grammar.


Semantics of DBGUID_DEFAULT

OLE DB defines a special language dialect identifier, DBGUID_DEFAULT, that 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, it must interpret the command string as SQL when DBGUID_DEFAULT is passed as the dialect identifier.
  • If the provider is an MDP, it must interpret the command string as MDX when DBGUID_DEFAULT is passed as the dialect identifier.

Note Multidimensional consumers are strongly discouraged from using DBGUID_DEFAULT for creating datasets. They should use DBGUID_MDX instead.


Controlling the Creation of Instances of a Rowset or a Dataset

Executing an MDX query can result either in a dataset or in a flattened rowset. The application can control which object (rowset or dataset) can result from a query. The following rules determine the object resulting from ICommand::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 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, a rowset object is created.
  2. If the consumer asks for any interface on the dataset object, a dataset object is created.
  3. If the consumer asks for any interface on the rowset object that does not exist on the dataset, 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 the DBPROP_IRowset property to VARIANT_FALSE (or leave it untouched) and ask for an interface on the dataset object.


Dataset Object

OLE DB for OLAP defines an additional OLE DB object. The dataset object provides an abstraction for a multidimensional result set.

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.

OLE DB for OLAP dataset objects are defined as follows:

CoType TDataset {
[mandatory]   interface IMDDataset;
[mandatory]   interface IAccessor;
[mandatory]   interface IConvertType;
[mandatory]   interface IColumnsInfo;
[optional]    interface IMDFind;
[optional]    interface IMDRangeRowset;
[optional]    interface ISupportErrorInfo;
}

The interfaces and methods perform the following actions:

  • 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 values such as the number and data type of cell properties.
  • Retrieve cell property values.
  • Search the dataset for a given cell and search the axes for a given tuple.
  • Provide information about supported data type conversions.
  • Retrieve an interface on the command object that created the dataset object.

Where possible, the existing OLE DB infrastructure used for navigation, description, and other functions has been used for OLE DB for OLAP.


IMDDataset Interface

This is the core interface on the dataset object and is required. It has methods to perform the following actions:

  • Get axis information (GetAxisInfo).
  • Get axis rowsets (GetAxisRowset).
  • Get 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).

For more information about this interface, see IMDDataset in the OLE DB for OLAP Interfaces section under the OLE DB Reference topic.


MDAXISINFO Structure

This structure contains information about an axis in the dataset. The method IMDDataset::GetAxisRowset 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. For any given array of MDAXISINFO structures, cbSize will remain constant.

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 (which corresponds to the DIMENSION_NAME element of the DIMENSIONS rowset) is a pointer to an array of strings. The provider allocates this array. It contains the names of each dimension. The cardinality of both arrays is cDimensions.

Provider and Consumer Requirements

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 IMDDataset::GetAxisInfo. 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 memory will be freed.

For providers, the presence of the IMDDataset::FreeAxisInfo method means that the memory for the MDAXISINFO array and the fields of each of its elements need not 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 about how consumers should move from one element to the next in the MDAXISINFO array, see the description of IMDDataset::GetAxisInfo in the reference section.


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 value). In addition, there might be provider-specific default properties, or the application might have selected additional properties by using the DIMENSION PROPERTIES clause. Therefore, it is possible for a dimension to have more than five columns. The rgcColumns field in the MDAXISINFO structure for this axis points to an array containing the number of columns in the axis rowset for this dimension.

The range rowset is a rowset in which each row corresponds to a cell in the dataset. Each selected cell property appears as a column in this rowset. Range rowsets provide the means for updating the axis rowset. For more information about these rowsets, see "Range Rowsets" in Chapter 24: Rowsets in OLE DB for OLAP. Also in Chapter 24, see "Creating an Accessor to Fetch Data" for an explanation of how the application, before fetching cell data, can create an accessor that describes the structure of the data buffer.

The axis rowset has columns in the following order:

  1. TUPLE_ORDINAL column.
  2. Columns for the outermost dimension.
  3. 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 as follows:

  1. MEMBER_UNIQUE_NAME. The unique name of the member. This is the same as the MEMBER_UNIQUE_NAME column of the MEMBERS rowset.
  2. MEMBER_CAPTION. The display name of the member. This is the same as the MEMBER_CAPTION column of the MEMBERS rowset.
  3. 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.
  4. LEVEL_NUMBER. The distance of the member from the root, which is zero. This is the same as the LEVEL_NUMBER column of the MEMBERS rowset.
  5. 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 shown in the following illustration.

    • 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 the SalesData example in Chapter 21: OLE DB for OLAP Overview. 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, 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 might 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}, 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 all the children of a member to 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 them to all 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 type for the TUPLE_ORDINAL value is DBTYPE_UI4. The data types for the five mandatory properties are listed in the following table.

Default column properties | Data type
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.

Axis Rowset Example

Consider the following dataset:

This dataset has two axes, x and y (0 and 1).

Note In this example, it is assumed that the members of the SalesRep and Geography dimensions need to be qualified and that members from all other dimensions need not be qualified (to ensure uniqueness). It is also assumed that all level names are unique without qualification.

The x-axis has the following elements:

  • Two dimensions: SalesRep and Geography.
  • SalesRep 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] (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] (no provider-specific mandatory properties)
  • rgpwszDimensionNames = pointer to array [Quarters]

The axis rowset for the x-axis is shown in the following table. (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 follows. (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 | January | January | Month | 2 | 0, 0, 31
1 | February | February | Month | 2 | 1, 0, 29
2 | March | 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 | October | October | Month | 2 | 0, 0, 31
6 | November | November | Month | 2 | 1, 0, 30
7 | December | December | Month | 2 | 1, 0, 31


Effect of DIMENSION PROPERTIES Clause on Axis Rowset

OLE DB Programmer's Reference

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 belongs. 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 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.


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 values of other mandatory properties (such as LEVEL_*, MEMBER_ORDINAL, PARENT_*, and DESCRIPTION) are determined when the CREATE clause is invoked.

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 optional member properties, if any, are provider-defined.


Mandatory Member Properties

All providers are required to support the following 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. For more information, see "MEMBERS Rowset" in this chapter.

The consumer can include any of these properties in the axis rowset by specifying them in the DIMENSION PROPERTIES clause.


Chapter 24: Rowsets in OLE DB for OLAP

The OLE DB rowset object is the central object that enables OLE DB components to expose and manipulate data. The rowset presents data as a set of rows in tabular form. Rowsets are used extensively by OLE DB for OLAP. The three following types of rowsets are returned:

  • Schema rowsets—These are used to return schema information.
  • Axis rowsets—These are used to return information about the axes of a dataset.
  • Range rowsets—Each cell in a dataset is modeled as a row, and the collection of cells that can be updated by the application is presented as a range rowset.

This means that an OLE DB for OLAP provider must implement the rowset object and all mandatory interfaces on that object. However, OLE DB for OLAP rowsets are usually much simpler than the typical OLE DB rowset. For example, they do not need to implement bookmarks or scrolling, and the axis rowsets and the schema rowsets need not implement updatability. Nothing prevents a provider from implementing a rowset with these optional additional features, but full implementation of all these features is not a requirement for compliance with OLE DB for OLAP.

For more information about rowsets in OLE DB, see Chapter 4: Rowsets.


Range Rowsets

In a range rowset, 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 calling IMDRangeRowset::GetRangeRowset. 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 that used in IMDDataset::GetCellData. IMDRangeRowset is an optional interface on the dataset object. For information about how to determine whether or not the range rowset is supported by the provider, see "Data Source Property MDPROP_RANGEROWSET."

Consider the dataset in the SalesData example. The call

IMDRangeRowset::GetRangeRowset(NULL, 1, 19, cProperties, rgPropertySets,
                               &pIRowsetChange);

returns the rowset shown in the following table, assuming that the default properties—VALUE, FORMATTED_VALUE, and CELL_ORDINAL—were selected.

Note The quotation marks around values in the FORMATTED_VALUE column are there as a visual aid to indicate that they are strings. The provider does not return the quotes.

VALUE | FORMATTED_VALUE | CELL_ORDINAL
10 | "10" | 1
20 | "20" | 2
30 | "30" | 3
11 | "11" | 9
21 | "21" | 10
31 | "31" | 11
12 | "12" | 17
22 | "22" | 18
32 | "32" | 19

The primary uses of the range rowset are as follows:

  • Remoting—Working with a dataset remotely amounts to remoting three elements of the dataset: the axis rowsets, the MDAXISINFO structures associated with each axis, and the range rowset. The advantage for the provider of remoting a rowset is that it follows the typical OLE DB conventions of rowset manipulation.
  • Updating—Since the range rowset specifies the location and data of the selected cells of a cube, updating the cells of a cube is the same as updating the range rowset. "Working with Cell Data" discusses this process in detail.

The following rules govern the structure of the range rowset:

  1. The columns in the range rowset are ordered according to the following conditions:
    • If the MDX statement that created the dataset had a CELL PROPERTIES clause, the columns appear in the order specified in that clause. Otherwise, the columns are VALUE, FORMATTED_VALUE, and CELL_PROPERTIES, in that order. In the case where the MDX statement has a property specified multiple times in the CELL PROPERTIES clause (for example, CELL PROPERTIES CELL_ORDINAL, VALUE, FORMATTED_VALUE, and VALUE), the results are provider-specific.
    • What happens in either of the following 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), usually at least one column, frequently the VALUE column, can be updated. Providers usually support the updating of other columns as well. To determine whether 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.


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 through the following dataset. The axis dimensions are Products, Geography, and Quarters. Slicer dimensions are Measures, Years, and SalesRep.

Flattening this dataset yields the following rowset:

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]". However, it is also 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 required to return only unique names. The extent to which the names are qualified to make them unique is up to the provider. A provider need not use qualification as a way of ensuring uniqueness; it can use any method.


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 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:
    • Let k be the lowermost level on the axis from this dimension.
    • Let Li be the set of unique names of levels above (and including) k up to (but not including) the ALL level. If this hierarchy does not have an ALL level, include the unique name of the root level in Li. Li must contain unique names.

      This rule is meaningful only when the MDX statement for Di results in members from a single hierarchy. If members from multiple hierarchies are included, the set of levels in Li is provider-dependent. For example, suppose that the Geography dimension has two hierarchies—Political and Physical. If the members Geography.Political.China and Geography.Physical.USA are both included as a result of the MDX statement for Di, the set of levels in Li is provider-dependent.

      Case:

      If there is a DIMENSION PROPERTIES clause in the MDX statement that created the dataset, let Pi be the list of properties specified in this clause, enclosed in [ ]. (Therefore, if DIMENSION PROPERTIES MEMBER_UNIQUE_NAME is specified, the Pi value contains the string "[MEMBER_UNIQUE_NAME]".)

      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:
    • Each name in C is a column.
    • The levels from the highest numbered axis come first, followed by lower numbered axes.
    • Within the levels of each axis, the levels of the outermost dimension come first.
    • 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, 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.
    • These columns appear after the columns from L.
    • Within X, the columns are in tuple order.
    • The names in X are always the unique names, whether or not the DIMENSION PROPERTIES clause exists. Consequently, any properties specified in this clause are ignored.
  8. If a hierarchy has an ALL member and that member appears on an axis, 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 multidimensional 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 following MDX statement. (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 following dataset:

In turn, this dataset yields the following 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 in which such data is usually displayed in a tabular view.

This example also illustrates step 2 of the algorithm in the section "Flattening Algorithm": If the DIMENSION PROPERTIES clause is not specified, it is equivalent to specifying DIMENSION PROPERTIES MEMBER_CAPTION.


Complex Flattened Rowset Example

The flattened dataset from the SalesData example looks like this:

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.


DIMENSION PROPERTIES Example

Consider the following 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 statement results in the following flattened rowset:

It is important to note the following:

  • 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 "Appearance of NULLs in a Flattened Rowset."


Order of Members in a Flattened Rowset

The order of members in a 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 about 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 23: OLE DB for OLAP Objects and Schema Rowsets.


Appearance of NULLs in a Flattened Rowset

NULLs appear in the flattened rowset in two different situations:

  • To indicate that a member is not present on the axis—for example, in column 2 of rows 4 and 5 of the flattened dataset diagrammed in the complex flattened rowset example. In the dataset, "Months" for Qtr3 and Qtr4 are not on the axis.
  • To indicate that a cell is empty. For example, the flattened dataset diagrammed in the DIMENSION PROPERTIES example has NULLs in the last column of the first row. This is because Venkatrao did not have any sales of Microsoft® Excel in the 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 requests, it has space to hold the following information:

  • 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.


Support Level 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 stores do not support named levels. For such data stores, providers can use the following methods:

  • They can generate dummy names for the tree levels they possess. This means that the column names in the set L described in "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 not to support flattening.

Of these three options, it is strongly recommended that the provider support the dummy name method. This makes the provider accessible to a larger class of applications.

Which of these options a provider chooses can be determined by the value of the MDPROP_FLATTENING_SUPPORT property in the DBPROPSET_DATASOURCEINFO property set.


Working with 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 equivalent to updating cube data. For updating cell data in a cube, OLE DB for OLAP provides a means for updating the cells of a dataset.

The consumer can update the range rowset by using the IRowsetChange interface—just as with any other OLE DB rowset.

The following rules apply:

  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 whether 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 "Update Dependencies" for more information. Whether or not a row can be deleted is provider-specific.


Creating an Accessor to Fetch Data

Before fetching cell data, the application has to create an accessor that describes the structure of the data buffer. This is done by calling IAccessor::CreateAccessor. The accessor created using this method is given as an input parameter to IMDDataset::GetCellData. Because GetCellData can fetch multiple cells in a single call, the provider must know the size of each cell in the data buffer. Applications must provide this size in the cbRowSize argument of IAccessor::CreateAccessor. Failure to do so will result in an error from this method, and an accessor will not be created.


Data Source Property MDPROP_RANGEROWSET

Based on the following conditions, the data source property MDPROP_RANGEROWSET returns information about whether or not the provider supports updating of cells through the IMDRangeRowset interface:

  • If the value of this property is MDPROPVAL_RR_NORANGEROWSET, the provider does not support getting the range rowset—that is, IMDRangeRowset is not supported.
  • If the value is MDPROPVAL_RR_READONLY, the provider supports IMDRangeRowset but the returned rowset is read-only.
  • If the value is MDPROPVAL_RR_UPDATE, the provider supports IMDRangeRowset with at least one column (typically the VALUE column) that can be updated—that is, the provider supports IRowsetChange on the range rowset.

Consumers should not use the range rowset purely to fetch cell data (even though it is possible to do it). IMDDataset::GetCellData is better optimized for the typical navigation requirements of an OLAP application. Moreover, because the range rowset might have to support the update interfaces, it might not be as efficient as fetching cell data by calling IMDDataset::GetCellData.


Deleting Cells

Deleting a cell means there will be no value for that point in the n-dimensional space. If that cell is included in a dataset and fetched by calling IMDDataset::GetCellData, the STATUS part of the consumer's buffer has the value MDSTATUS_S_CELLEMPTY.

Deletion also can be accomplished by setting the STATUS part of the consumer's buffer that corresponds to the VALUE column to MDSTATUS_S_CELLEMPTY and then calling IRowsetChange::SetData.


Cell Insertion Prohibited

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 create 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.

Whether or not dependent properties can be updated is provider specific. If they can be updated, it is provider-specific what happens to the determining property (such as VALUE) when a dependent property (such as FORMATTED_VALUE) is updated. For clearer semantics, providers are encouraged to make dependent properties read-only. The OLE DB for OLAP 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.

Updating Aggregate Cells

When updating a cell in an aggregated dataset, behavior varies among providers. That is, what happens to the cells that combine to create the aggregate is provider-specific. 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 (January, February, and so forth).

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 the nonatomic cell.

The property MDPROP_AGGREGATECELL_UPDATE can be used by the consumer to determine whether or not a provider supports updating nonatomic cells. If it does, the property also can yield more information about a provider's behavior when such a cell is updated.


Visibility of Changes

When a consumer updates or deletes a row, changes might or might not be visible when the consumer releases the row handle and refetches the row. The same uncertainty exists about updates to dependent properties. The solution depends on the rowset property DBPROP_OWNUPDATEDELETE. If the value of this property is VARIANT_TRUE, the changes are visible on refetching. If the value is VARIANT_FALSE, the changes are not visible unless the command is reexecuted.

Note Explicit consumer updates are known to the consumer. Therefore, there is no need to refetch the updated rows. However, explicit updates might result in implicit updates of the dependent properties. The consumer needs to refetch or reexecute 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 might not be known when changes are visible in that dataset. It is the provider's responsibility to implement a solution in this case.


Chapter 25: Multidimensional Expressions

OLE DB for OLAP defines multidimensional expressions (MDX), which provide a rich syntax for specifying a dataset. MDX can be viewed as a set of macros defined on top of the SQL language. Although these macros are generally useful for querying a multidimensional data source, their precise semantics are defined by means of their SQL language mappings.

For more information about SQL language mappings, see Chapter 27: Mapping MDX to SQL Statements.

The following sections discuss concepts of MDX and provide examples of its use.


MDX Concepts

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 store 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," in Chapter 22: OLE DB for OLAP Concepts.


Parts of an MDX Statement

To specify a dataset, the MDX statement must contain information about the following:

  • The cube or cubes that set the scope of the query.
  • The number of axes.
  • The dimensions projected on each axis and the level of nesting at which each dimension appears on the axis.
  • The members or member tuples to include from each dimension, 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>


Leveling Rules for MDX Statements

  1. Support for the WITH clause 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> and <drop_formula_statement> is optional. Consumers can check the value of the property MDPROP_MDX_FORMULAS to see whether a provider supports this feature.
  7. If a provider supports <create_formula_statement>, support for a <scope> of GLOBAL is optional. Consumers can check the value of the property MDPROP_MDX_FORMULAS to see whether a provider supports this feature.


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 and 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.


Establishing Cube Context

In the expression template shown in "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, 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. That is, to be compliant with OLE DB for OLAP, a provider need not support cross-cube queries. Applications can check the property MDPROP_MDX_JOINCUBES in the DBPROPSET_DATASOURCEINFO property group to determine whether 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, it is necessary to understand the nature of an axis.

Contents of the x-Axis

In the dataset of the SalesData example, the x-axis has two nested dimensions: Geography and SalesRep. From the Geography dimension, four members are selected: Seattle, Boston, USA_South, and Japan. Two members are selected from the SalesRep 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 SalesRep 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)

Consider an axis as comprising a set of tuples. If there is only one dimension on an axis, each tuple is just a member. If there are two dimensions, 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, 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:

Jan, Feb, Mar, Qtr2, Qtr3, Oct, Nov, Dec

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.


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, follow these steps:

  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 Chapter 26: MDX Grammar. Following are some examples of the many ways to choose 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 SalesRep 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 as follows:

CROSSJOIN({Venkatrao, Netz},
{USA_North.CHILDREN, USA_South, Japan})

The expression for the y-axis in the example dataset is the following:

{Qtr1.CHILDREN, Qtr2, Qtr3, Qtr4.CHILDREN}

Note The MDX syntax is much more extensive than is shown in these few examples. Some functionality not covered here includes the following:

  • 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

    See "Advanced MDX" for more details about MDX. Chapter 26: MDX Grammar, contains the full MDX syntax.


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 "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

Noncontiguous Axes in the SELECT List

A SELECT list should not be specified where the axes are not contiguous. For example, it is an error to have ROWS and PAGES, but not COLUMNS; or to have COLUMNS and PAGES, but not ROWS.

However, a SELECT list can be specified 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, the slicing condition can be expressed 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 "Namespace 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_MDX_SLICER returns information on the provider capabilities in this area. If a slicer is a set of tuples, 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 about the AGGREGATE function, see "Numeric Functions," in this chapter.


SalesData Example Dataset Expression

Combining the SELECT list with the FROM and WHERE clauses, the query for the example dataset, SalesData, 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])


Namespace and Unique Names

The query expression examples given in previous sections use member names. However, MDX syntax allows expressions containing dimension, hierarchy, and level names as well.

If all data source objects require that all object names used within a cube be unique, you can use unqualified names (as in the examples). The domain within which an object name must be unique is called the namespace of that object. In other words, if the namespace of all objects were the cube, there would be no problems. However, this is not always the case.

Data source objects that allow for more granular namespaces require that hierarchy and level names be unique within a cube.

However, members can have very granular namespaces, 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 namespace 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 namespaces, 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.

This situation 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 might not always work. For example, if the name of an ancestor member changes, 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 about the contents of the unique name given by a provider. Specifically, it should not assume that the unique name is a qualified name. Providers can generate a unique name in any way. The provider guarantees only that the unique name unambiguously references an object.

Note If you are dealing with namespaces of objects across all cubes in a schema or across all schemas in a catalog (queries involving multiple cubes), 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.

Like other aspects of the schema, unique names can usually be treated as persistent between sessions. However, the OLE DB for OLAP provider may allow administrative procedures that result in changes to the unique names of various objects, such as changing the name of a cube or dimension.


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 could 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, 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.

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.

In addition, depending on your provider, the shortest unambiguous name may be the most optimal for object lookup. For example, the member name "Europe" may produce faster results than "Geography.Europe."

Note OLE DB for OLAP does not impose any structure on the provider for uniqueness.

Provider writers should keep these considerations in mind when deciding on what to expose as a unique name.

Note When an MDX statement contains ambiguous names, it is provider dependent whether it raises an error or uses default qualifiers to make the names unambiguous. For example, the name "October" in the expression "October.CHILDREN" is ambiguous because the year is not specified. A provider may either raise an error or return the children of "October" for the default year. If 1998 is the default year, the expression is equivalent to "1998.October.CHILDREN".


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 exposes an interface called ICommandText, which has the following methods that, when used with the ICommand methods, provide the ability to execute queries:

  • ICommandText::GetCommandText gets the query string associated with the command object.
  • ICommandText::SetCommandText associates the query string with the 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."


Advanced MDX

The sections described in the following table explain MDX statements in detail.

Topic | Description
The MDX Statement | Contains a complete description of the construct.
Retrieving Member Properties and Retrieving Cell Properties | Dimension members and cells both have associated properties. These sections explain how to retrieve those properties.
Join Compatibility | Explains compatibility issues when joining cubes and presents JOIN examples.
Empty FROM Clause | Explains provider support of implicitly deriving the cube from the specified axis and slicer dimensions.
Default Hierarchy and Member | Describes the default hierarchy and default member for dimensions.
Types of Expressions in MDX | Set, Member, Tuple, Number, and Time expressions are discussed in detail.
Comparing CROSSJOIN and ORDER: A Provider Implementation Note | Explains the differences both in use and in results between the CROSSJOIN and ORDER functions.
WHERE Clause Overrides | Explains special cases when using the WHERE clause.
Creating a Cell Within the Context of a Cube | Describes returning data for a single cell.
Querying on Property Values | Explains how to retrieve property values.
Using Outer References in a Query | Describes outer references.
Named Sets and Calculated Members | Describes how to specify a name for set expressions and formulas.
Set Functions for Expressing User-Interface Operations | Describes drilling operations on schema rowsets that are commonly exposed in a user interface.
Preserving State During User-Interface Operations | Describes state preservation during drilling operations on schema rowsets that are commonly exposed in a user interface.
Conditional Expressions Explains the following conditional expressions:
  • IF Clause
  • Simple Case Expression
  • Searched Case Expression
Empty Cells | Explains working with empty cells.


The MDX Statement

The complete construct of the MDX statement follows:

<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>, 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 Because 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>, 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


Retrieving Member Properties

In the axis specification for a given axis, the set expression 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. Members often have additional properties associated with them, and properties usually are 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 might want to extend member information by adding member properties on the axis. Therefore, 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, SalesRep.[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 23: OLE DB for OLAP Objects and Schema Rowsets.

The BNF form 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 for this to work. If the data store 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, the consumer cannot choose MEMBER_CAPTION for the Geography dimension nor MEMBER_UNIQUE_NAME for the SalesRep dimension. The consumer must choose the same property (or properties) for all dimensions on an axis.


Retrieving Cell Properties

A cell can 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 can vary from provider to provider. OLE DB for OLAP recognizes the properties listed in the following table as typical properties that a provider might support for a cell.

Property name 		Mandatory/
optional
Data type
Description
VALUE | Mandatory | DBTYPE_VARIANT | Actual data value.
FORMATTED_VALUE | Mandatory | DBTYPE_WSTR | Character string that represents a formatted display of VALUE.
CELL_ORDINAL | Mandatory | DBTYPE_UI4 | Ordinal number of the cell in the dataset. For more information, see "Navigating Cells in a Dataset" in Chapter 22: OLE DB for OLAP Concepts.
FORMAT_STRING | Optional | DBTYPE_WSTR | Format string used to create FORMATTED_VALUE. For more information, see "Contents of FORMAT_STRING" in this chapter.
FORE_COLOR | Optional | DBTYPE_UI4 | Foreground color for displaying VALUE. For more information, see "Contents of FORE_COLOR and BACK_COLOR" in this chapter.
BACK_COLOR | Optional | DBTYPE_UI4 | Background color for displaying VALUE. For more information, see "Contents of FORE_COLOR and BACK_COLOR" in this chapter.
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, SalesRep.[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 VALUE, FORMATTED_VALUE, and CELL_ORDINAL, in that order. This property set 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, only those properties selected in this clause are returned. (If the clause does not include any of the default properties mentioned above, 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 object 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 characters described in the following table 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. Displays 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), means "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 might 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 might 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, use Chr(34) to enclose the text. (The character code for a double quotation mark is 34.)

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 might 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 might 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 earlier than 10:00 (for example 09:59), in either the A.M. or the P.M. cycle. 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 from midnight until noon; displays an uppercase PM with any hour from noon until midnight.
am/pm | Uses the 12-hour clock. Displays a lowercase am with any hour from midnight until noon; displays a lowercase pm with any hour from noon until midnight.
A/P | Uses the 12-hour clock. Displays an uppercase A with any hour from midnight until noon; displays an uppercase P with any hour from noon until midnight.
a/p | Uses the 12-hour clock. Displays a lowercase a with any hour from midnight until noon; displays a lowercase p with any hour from noon until midnight.
AMPM | Uses the 12-hour clock. Displays the AM string literal as defined by your system with any hour from midnight until noon; displays the PM string literal as defined by your system with any hour from noon until midnight. 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-significant 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 discussed in "Establishing Cube Context," 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 To perform a join, it is not necessary to specify the relationship between cubes, because joining is always by common dimensions (dimensions with the same name). 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 the section "Hypercubes and Multicubes" in Chapter 22: OLE DB for OLAP Concepts.

The semantics of joining by common dimensions, called a natural join, can be expressed as follows:

  1. Let the notation X(A, B, C) indicate a cube X that is composed of dimensions A, B, and C.
  2. Let X(A, B, C) and Y(C, D, E) be two cubes with the common dimension C.
  3. If X.C and Y.C do not have an identical set of members, then X and Y cannot be joined.
  4. 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). And 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).

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 determine whether or not a data source object 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 must 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.
  • IMDFind::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 named DEFAULT_HIERARCHY that indicates the default hierarchy for a dimension. The HIERARCHIES rowset has a column named 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 whether 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 the OLE DB for OLAP ability to provide a rich and powerful syntax for specifying datasets.

MDX statements operate on the following basic entities:

  • 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.
  • Member. This represents a member of a dimension. A member value expression can operate on 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.
  • 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.
  • Time dimension. The time series functions operate on the dimension that is specified implicitly as part of their <set>, <member>, or <level> arguments. (The time series functions described in this chapter are, in fact, member, set, or numeric functions.)


Set Value Expressions

For any expression examples in the following sections, SampleSet is defined as

{USA, Buffalo, France, NYC, London, California, LA, Nice, UK, Paris}

with sales data as indicated in the following 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>.

ANCESTOR(<member>, <distance>)

Returns the ancestor of <member> that is <distance> steps away in the hierarchy. If <member> has multiple parents, the provider elects which parent member to return.

ANCESTOR(<member>, 0) returns <member>.

ANCESTORS(<member>, <level>)

Returns all ancestors of <member> at the specified level. Unlike the ANCESTOR function, ANCESTORS is a set value expression. Useful in network hierarchies where a member may have more than one parent.

The set of returned members must all be from the same hierarchy, but <level> need not be a level of the same hierarchy as <member>.

ANCESTORS(<member>, <distance>)

Returns all members of the hierarchy that are exactly <distance> steps above <member> in the hierarchy. This form of ANCESTORS is intended for cases in which the level of the parent is unknown or cannot be named.

Unlike the ANCESTOR function, ANCESTORS is a set value expression. It is useful in network hierarchies where a member may have more than one parent.

The set of returned members must all be from the same hierarchy.

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)}

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.

The statement

DESCENDANTS(USA, Counties, BEFORE_AND_AFTER

yields all states and cities in USA.

DESCENDANTS(<member>, <distance> [, <desc_flags>])

All descendants of <member> that are <distance> steps away from <member>. Otherwise identical to the original form of DESCENDANTS, it is useful when dealing with ragged hierarchies.

For example,

DESCENDANTS(USA, 1)

yields all states in the USA, while the statement

DESCENDANTS(ISRAEL, 1)

might return all cities in Israel.

The use of <desc_flags> is the same as for the original form of DESCENDANTS. For example, the statement

DESCENDANTS(USA, 2, BEFORE)

would also return all states in the USA.

The LEAVES flag can be used to return all the leaf-level descendants of the given member, up to <distance> steps away. In a Geography dimension, the statement

DESCENDANTS(Geography.[All], 4, LEAVES)

might return all cities in the hierarchy, or possibly counties or states if those members contain no children.

DESCENDANTS(<member>, 0, LEAVES) would return <member>.

DISTINCT(<set>)

Deletes duplicate tuples from a set. Duplicates are deleted from the tail.

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 nonmatching duplicates are retained.

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.

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}

GENERATE(<set1>, <set2> [,[ALL]])

This is best explained with an example, as follows:

GENERATE({USA, France}, DESCENDANTS(Geography.CURRENTMEMBER, Cities))

The GENERATE function 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, 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, all duplicates are retained in the result. If ALL is not specified, duplicates are removed. For example, the statement

GENERATE({USA, FRANCE}, {SEATTLE, BOSTON}, [ALL])

will result in the following:

{SEATTLE, BOSTON, SEATTLE, BOSTON}

However, if ALL was not specified, the result is as follows:

{SEATTLE, BOSTON}

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. The statement

HIERARCHIZE(SampleSet)

returns the set in natural order. The hierarchized dataset follows (assuming that the natural order for the data store is alphabetical).

Country | State | City
France |  |
 |  | Nice
 |  | Paris
UK |  |
 |  | London
USA |  |
 | California |
 |  | LA
 |  | Buffalo
 |  | NYC

Note Even when none of the ancestors of a member are present in the input set, the sorting is done as if the appropriate ancestor is present. For example, Buffalo and NYC appear after LA because California (the parent of LA) sorts before New York (the parent of Buffalo and NYC).

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.

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, and 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. The statement

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 shown in the following table.

Location |  |  | 1995 sales
USA |  |  | 5000
 | California |  | 2000
 |  | LA | 500
 |  | Buffalo | 300
 |  | NYC | 900
France |  |  | 2500
 |  | Paris | 365
 |  | Nice | 27
UK |  |  | 1900
 |  | London | 250

The statement

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 shown in the following table.

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, the input order is preserved. For example, if the sales for USA and Europe is 300 each and the sales for Asia is 100, the call

   ORDER({USA, Europe, Asia}, Sales, BASC)

returns the set {Asia, USA, Europe} and not the set {Asia, Europe, USA}.

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.

For both these functions, the number of members returned is always equal to the specified <index>. Specifically, if there is a tie during the calculation of these members, the provider resolves the tie in a provider-dependent fashion and returns only the required number of members. For example, the statement

TOPCOUNT(Geography.Cities.MEMBERS, 5, Sales)

yields the following results, in terms of sales:

  • The top four cities are New York, LA, Chicago, and Philadelphia.
  • The fifth city is a tie between Seattle and Boston.

The provider can do either of the following:

  1. Return all six members: {NYC, LA, Chicago, Philadelphia, Seattle, Boston}
  2. Arbitrarily break the tie between Seattle and Boston and return only five cities: {NYC, LA, Chicago, Philadelphia, <either Seattle or Boston>}

OLE DB for OLAP requires that the provider not choose option 1 but instead, to resolve a tie in a provider-dependent manner, choose option 2 and return only the specified number of members.

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.

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.


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 returns the first child of a member according to an implicit natural order. (The children of any member are arranged along this natural 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 of the <member>. For example,

May.FIRSTSIBLING

returns April, and 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. If <member> has multiple parents, the provider elects which parent member to return.

<level> need not be from the same hierarchy as <member>.

If the level of <member> is equal to <level>, the function returns <member>. For example,

ANCESTOR(USA, Country)

would return USA.

COUSIN(<member>, <ancestor_member>)

Note The COUSIN function has been deprecated, and providers are no longer required to support it. In most cases, PARALLELPERIOD can be used instead.

This function is best illustrated by an example: The expression

COUSIN([1996].March, [1994])

yields the member [1994].March.

The algorithm to evaluate the COUSIN function call is as follows:

COUSIN(<member1>, <member2>)

The three possible scenarios are as follows:

  1. If the level of <member2> is not at a higher level than <member1>, return immediately. Higher level means a lower level number, because level numbers start at the root (whose level is zero) and increase towards the leaf.
  2. If <member2> is a direct ancestor of <member1>, <member1> returns.
  3. Otherwise:
    • Each member in the hierarchy has a unique sibling-number associated with it. The sibling-number is the rank of a member among its siblings when the siblings are in natural order.
    • Let <ancestor> be a member so that it is the ancestor of <member1> and is a sibling of <member2>.
    • Consider the subtree rooted at <ancestor>. Let P denote the set of sibling-numbers of members traversed in the path from <ancestor> to <member1>. For example, if <ancestor> is North America and <member1> is Seattle, then P = {5, 41}, assuming that the rank of USA among all children of North America is 5 and that the rank of Washington state among all children of USA is 41.
    • Let <m> be the member obtained by starting at <member2> and traversing the path P. Return <m>.

LINKMEMBER(<member>, <hierarchy>)

Returns the equivalent to <member> in <hierarchy>. For example,

LINKMEMBER([Calendar].[1999].[Jan].[12], [Fiscal Year])

might return [Fiscal Year].[1999].[Qtr1].[Jan].[12].


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.

For example, if MySet was defined as

MySet = {(386DX, Venkatrao), (486SX, Netz)}

then the expression

MySet.ITEM("386" + "DX", "Venkat" + "rao")

returns the following tuple:

(386DX, Venkatrao)

The same tuple is also returned by the expression MySet.ITEM(0).


Numeric Functions

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], [SalesRep].[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.

AVG(<set>[, <numeric_value_expression>])

Computes the average of the tuples in <set> based on <numeric_value_expression>. Similar numeric functions are as follows:

  • MEDIAN(<set>[, <numeric_value_expression>])
  • MIN(<set>[, <numeric_value_expression>])
  • MAX(<set>[, <numeric_value_expression>])
  • VAR(<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. To force the inclusion of empty cells, the application must use the COALESCEEMPTY function. For more information, see "Empty Cells" in this chapter.

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."

SUM(<set>[, <numeric_value_expression>])

Sums <set> based on an optional numeric value expression.

For example, the expression

SUM({USA, FRANCE}, Sales.VALUE)

will return the sum of sales for USA and France. This can also be written in a more intuitive way as follows:

SUM({USA, FRANCE}, Sales)

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, 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], [SalesRep].[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, SalesRep, 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.


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 member, set, and 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 many cases, scenarios exist 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 xTD (YTD, MTD, QTD, WTD) functions, which are applicable to the Time dimension only.

Note As used here, Time dimension 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, whichever is chosen is provider-specific.

Time Series Set Value Expressions

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 <member>.LAG(<member>, <index> – 1):<member>.

For example, the statement

LASTPERIODS(5, [1991June])

returns the set

{[1991Feb], [1991Mar], [1991Apr], [1991May], [1991June]}

If <member> is not specified, its value is Time.CURRENTMEMBER.

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, 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>.

The statement

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].)

And the statement

PERIODSTODATE(Year)

returns the set of members through Time.CURRENTMEMBER, from the beginning of the year that is the ancestor of Time.CURRENTMEMBER.

The statement

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(<level>, <member>), <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, the default is Time.CURRENTMEMBER.

Time Series Member Value Expressions

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, the default is Time.CURRENTMEMBER. If no <level> is specified, 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 that it returns the last sibling instead of the first sibling.

PARALLELPERIOD([<level>[, <index>[, <member>]]])

This function is similar to the COUSIN function but is more closely related to the time series functions. It takes the ancestor of <member> at <level> (call it <ancestor>), and 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 the following:

COUSIN(<member>,LAG(ANCESTOR(<member>,<level>),<index>)

The expression

PARALLELPERIOD(Year,2, [96 Qtr 3])

returns the member [94 Qtr3].

The expression

PARALLELPERIOD(Year,2)

returns the parallel period of Time.CURRENTMEMBER of two years ago. That is, if Time.CURRENTMEMBER is [1993June], the returned member is [1991June].

The expression

PARALLELPERIOD(Year)

returns the parallel period of Time.CURRENTMEMBER from last year. That is, if Time.CURRENTMEMBER is [1993June], the returned member is [1992June].

The expression

PARALLELPERIOD()

returns the parallel period in the immediately prior sibling to the parent of Time.CURRENTMEMBER. For example, if Time.CURRENTMEMBER is [1993June], the returned member is [1993March]. The parent of ([1993June] is Qtr2, whose immediately prior sibling is Qtr1, in which the parallel period is [1993March].


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, 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, SalesRep). However, it does make sense for the Time dimension. Covariance and correlation are calculated for these x and y values.

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>])

These methods evaluate <set> against the first numeric value expression to get the set of values for the y-axis, and they evaluate <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, 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, SalesRep). However, it does make sense for the Time dimension.

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.

After obtaining the set of points, LINREGINTERCEPT returns the intercept of the regression line (a in the equation above). LINREGSLOPE returns the slope of the regression line (b 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—<numeric_value_expression>,<set>, and <numeric_value_expression>—like the other LINREGxxx functions to calculate the regression line. Next 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, use the regression line y = ax + b, where x is the cost and y is the revenue. The a and b values are calculated as follows:

a = LinRegCoef(Campaigns.Members, Revenues, Cost)
b = LinRegConst(Campaigns.Members, Revenues, Cost)

For another example, find the trend in Sales for the sales of the last nine periods and forecast the next period, as follows:

a = LinRegSlope(LastPeriods(9), Sales)
b = LinRegIntercept(LastPeriods(9), Sales)
NextPeriod = LinRegPoint(10, LastPeriods(9), Sales)


String Manipulation Functions

These are functions to convert to and from strings, sets, tuples, and members.

STRTOSET(<string>)

A set value expression that converts <string> into a set. The string must follow the same formatting rules as a set in MDX. Used to convert output from a user-defined function to a set usable in an MDX expression.

SETTOSTR(<set>)

Converts <set> to a string. The string is formatted as a set in MDX, using the unique names of the members or cells. Used to convert from a set to a string for input to a user-defined function.

STRTOTUPLE(<string>)

Converts <string> into a tuple. <string> must follow the same formatting rules as a tuple in MDX.

TUPLETOSTR(<tuple>)

Converts <tuple> into a string. The string is formatted as a tuple in MDX.

STRTOMEMBER(<string>)

Converts <string> into a member. <string> must be formatted as an MDX member.

MEMBERTOSTR(<member>)

Converts <member> to a string. The string is formatted as an MDX member.

SETTOARRAY(<set> [, <set>]… [, <numeric expression>])

Converts one or more sets to an array for use in a user-defined function. The number of dimensions in the resulting array is the same as the number of sets specified.

The optional numeric expression can be used to provide the values in the array cells. If omitted, the default value of the set member is used for the array cell value.

The cell coordinates in the resulting array correspond to the position of the sets in the list. For example, SETTOARRAY(SA, SB, SC), where each set has two elements, produces the following three-dimensional array:

(SA1, SB1, SC1) (SA2, SB1, SC1) (SA1, SB2, SC1) (SA2, SB2, SC1)
(SA1, SB1, SC2) (SA2, SB1, SC2) (SA1, SB2, SC2) (SA2, SB2, SC2)

The return type of SETTOARRAY is the VARIANT type VT_ARRAY. For that reason, the output of SETTOARRAY should be used only as input to a user-defined function.


Comparing CROSSJOIN and ORDER: A Provider Implementation Note

Assume set S is defined as CROSSJOIN(S1, S2), where S1 and S2 are sets consisting of members from a single dimension. (For example, S1 has members from Products and S2 has members from SalesRep.)

Now consider F(S), where F is any set function other than CROSSJOIN.

The following is never true: F(S) = CROSSJOIN(F(S1), F(S2))

The example below illustrates why this is not true in the case of the ORDER function. It is straightforward to come up with similar examples for the other set functions as well.

Suppose that for the Customers Army and Industry:

(Army, Sales) = 20000 (for [all] SalesRep)
(Industry, Sales) = 30000 (for [all] SalesRep)

Also, suppose that for the sales reps Venkatrao and Netz:

(Venkatrao, Sales) = 10000 (to [all] customers)
(Netz, Sales) = 20000 (to [all] customers)

With this data and assuming that ALL is the default member for both Customers and SalesRep dimensions:

ORDER({Army, Industry}, Sales, DESC) = {Industry, Army}
ORDER({Netz, Venkatrao}, Sales, DESC) = {Netz, Venkatrao}
CROSSJOIN(ORDER({Army…), ORDER({Netz,…)) = {(Industry, Netz), (Industry, Venkatrao), (Army, Netz), (Army, Venkatrao)}

Now suppose that Venkatrao sold 6000 to Army and 3000 to Industry (and the other 1000 to some other customer) and that Netz sold 1000 to Army and 500 to Industry (and the other 18500 to some other customer). Then:

ORDER(CROSSJOIN({Army, Industry}, {Netz, Venkatrao}), Sales, DESC) = {(Army, Venkatrao), (Industry, Venkatrao), (Army, Netz), (Industry, Netz)}

Therefore, CROSSJOIN(ORDER, ORDER) is not equal to ORDER(CROSSJOIN).

Note If F(S) = CROSSJOIN(F(S1), F(S2)) were true, implementation would be simplified—because there is no need to implement any set function (except CROSSJOIN) for sets formed out of multiple dimensions. For example, if the set S is defined as CROSSJOIN(S1, S2), the evaluation of HIERARCHIZE(S) could be done as follows:

CROSSJOIN(HIERARCHIZE(S1), HIERARCHIZE(S2))


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 the following expression:

SELECT FILTER(SalesRep.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. Six coordinates are needed—one from each of the six dimensions—to determine VALUE. The argument contains only one coordinate, from the Years dimension. In such a case, the other coordinates are obtained by looking at the following, in order:

  1. The rest of the axis specification; this yields (in the example above) the coordinate of the SalesRep dimension because the FILTER function iterates through each member of the SalesRep dimension.
  2. The slicer condition (WHERE clause) and picking up the coordinates for the slicer dimensions. This yields the coordinates for the Geography, Products, and Measures dimensions as (respectively) Geography.[All], Products.[All], and Measures.Sales.
  3. The default member for dimensions that appear neither on the axis nor on the slicer. Thus the default members are picked for the Quarters dimension.

A special case arises when a coordinate is specified both in 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 that had more than 500 units of ActualSales in 1995 and that, on the ROWS axis, contains 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], [SalesRep].[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 might be desirable to return data for a single cell within a cube. For example, executives might have a decision-support application written in Microsoft® Excel that uses data from a multidimensional data store. Suppose that when the application starts every day, the executives want to view, at the top of the application's main window, 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 takes 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.

In this case, where there are no axes and hence only one cell, the following conditions apply:

  • IMDDataset::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 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 ...

The syntax for referencing properties is similar to specifying a member of a level, for example:

   [<Level>].[<PropertyName>] or [<Member>].[<PropertyName>]

Occasionally, this syntax results in ambiguity or conflict with an existing member name or keyword (for example, "Children"). To resolve this, the following syntax is also supported:

   [<Level>].Properties("<PropertyName>")

The following example shows how the preceding syntax is used:

SELECT  {Filter( [Customers].[Name].MEMBERS,
                 [Customers].Properties("Children")="2")}
   ON ROWS
FROM Sales

In general, property[.VALUE] is a value expression primary and 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 the goal is to obtain the actual sales of all products that 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,
   Geography.MEMBERS ON ROWS
FROM [Actual Sales]
WHERE ([1997])

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, it is 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 each formula as a new member.

Named sets are useful because they can be used concisely in other MDX statements. Creating formulas and treating them as members of a dimension is a very powerful feature and has a variety of applications.

For an illustration of these concepts, see the following examples:

  • "Defining a New Member Example" shows how to define a new member on a dimension.
  • "Defining and Qualifying a New Member Example" shows how to define and qualify a new member for a specific dimension.
  • "Assigning a Solve Order Example" shows how to assign a solve order for calculated members.


Defining a New Member Example

On the ROWS axis, show the Actual Sales and percent change from last year's sales for the product lines for which (Sales,Actual) is less than (Sales,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 dataset defined above is retrieved by the following MDX statement:

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 named "% 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). To yield the required result, this new member is evaluated, within the context of the rest of the expression, for each value in the n-dimensional manifold selected by the dataset.


Defining and Qualifying a New Member Example

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 dataset defined above is retrieved by the following MDX statement:

WITH MEMBER
   USA.[North West Ratio] AS SUM({WA, [OR], CA}, Sales) /
   SUM(EXCEPT(USA.CHILDREN, {Hawaii, 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 object recognizes that this should be on the Geography dimension.


Assigning a Solve Order Example

When a dataset has multiple formulas, there can be a problem with the order of formula evaluation. To understand the problem, consider the following MDX statement:

WITH
   MEMBER [Year].[Annual Growth] AS ([1994] - [1995])
   MEMBER [Status].[Performance] AS (([Actual] - [Planned]) /
                                     [Planned] * 100)
SELECT
   {[1994], [1995], [Annual Growth]} ON COLUMNS,
   {[Actual], [Planned], [Performance]} ON ROWS
FROM SalesBitmap

The preceding statement produces the following dataset:

The cells shaded in gray could be either Annual Growth of Performance or Performance of Annual Growth. If it is Annual Growth of Performance, 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 is as follows. (The text in bold indicates the changes.)

WITH
MEMBER [Year].[Annual Growth] AS
   ([1995] - [1994]), SOLVE_ORDER = 1
MEMBER [Status].[Performance] AS
   (([Actual] - [Planned]) / [Planned] * 100), SOLVE_ORDER = 2
SELECT
   {[1994], [1995], [Annual Growth]} ON COLUMNS,
   {[Actual], [Planned], [Performance]} ON ROWS
FROM SalesBitmap

And to get Performance of Annual Growth, the MDX statement is as follows. (The text in bold indicates the changes.)

WITH
MEMBER [Year].[Annual Growth] AS
   ([1995] - [1994]), SOLVE_ORDER = 2
MEMBER [Status].[Performance] AS
   (([Actual] - [Planned]) / [Planned] * 100), SOLVE_ORDER = 1
SELECT
   {[1994], [1995], [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 orders. 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 user-interface–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 user-interface operations. For more information about how to do this, see "Preserving State During User-Interface 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. 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 lasts until an explicit <drop_formula_statement> is issued.
  • SESSION. The created entity will be visible for all queries within the session. Its lifetime lasts as long as that of the session within which it is created. When the session dies, the created entity will be automatically dropped.

Note See also "Visibility in Schema Rowsets" in this chapter.

As an example, the following MDX statement creates the same dataset that is specified in the "SalesData example" in Chapter 21: OLE DB for OLAP Overview:

CREATE SET SalesCube.ColumnsAxisSet AS CROSSJOIN({Venkatrao, Netz},
   {USA_North.CHILDREN, USA_South, Japan})
CREATE SET 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, 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. 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

Whether they are SESSION scope or GLOBAL scope, calculated members and named sets are not visible in schema rowsets.

The MDX set operations, such as <dimension>.MEMBERS, <hierarchy>.MEMBERS, or <level>.MEMBERS, do not put any calculated members in the resulting set.


Set Functions for Expressing User-Interface 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 user interface. 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 three children of USA based on Sales."

MDX has an assortment of set functions that encapsulate these user-interface operations. See the following topics in this section:

  • Drilling by Member
  • Drilling by Level
  • Drilling Through

Note All of the functions discussed in this section require that the sets on which they operate be formed from a single dimension. That is, the set argument(s) to these functions cannot be the result of a CROSSJOIN operation. The list of functions with this restriction is DRILLDOWNMEMBER, DRILLUPMEMBER, DRILLDOWNMEMBERTOP, DRILLUPMEMBERTOP, DRILLDOWNMEMBERBOTTOM, DRILLDOWNLEVEL, DRILLDOWNLEVELTOP, DRILLDOWNLEVELBOTTOM, DRILLUPLEVEL, and TOGGLEDRILLSTATE.


Drilling by Member

All functions in this section require that the sets on which they operate be formed from a single dimension. The set arguments to these functions cannot be the result of a CROSSJOIN operation. This includes the following:

DRILLDOWNLEVEL | DRILLDOWNMEMBERTOP
DRILLDOWNLEVELBOTTOM | DRILLUPLEVEL
DRILLDOWNLEVELTOP | DRILLUPMEMBER
DRILLDOWNMEMBER | DRILLUPMEMBERTOP
DRILLDOWNMEMBERBOTTOM | TOGGLEDRILLSTATE

To drill down by a member, use the DRILLDOWNMEMBER function.

DRILLDOWNMEMBER(<set1>, <set2>[, RECURSIVE])

This expression drills down members in <set1> that are present in <set2>. If RECURSIVE is not specified, each member of <set2> is checked against the original <set1> and if a member is present, it is drilled down. If RECURSIVE is specified, 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 following set:

{USA, <[all] states in USA>, France, Canada, <[all] provinces in
Canada>}

The member Washington from <set2> has no effect because it does not occur in <set1>. 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 in the statement

DRILLDOWNMEMBER({USA, France, Canada}, {USA, Washington, Canada},
RECURSIVE)

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 whether it is 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 whether it is 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 whether it is 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.

This process is sensitive to the order of the members in <set2>. If <set2> were {Washington, USA, Canada}, the result would not contain all the cities in Washington. This is because <set1> is checked to see whether it contains "Washington" first. At this stage, it does not—because USA has not been drilled down yet.

A typical usage scenario for an application is the following: <set1> is the set of members from a given dimension on a given axis. (<set1> may itself be the result of an expression.) The user clicks the plus (+) sign next to a member m of <set1> in the user interface. The application now calls DRILLDOWNMEMBER(<set1>, {m}) to drill down on m. More sophisticated user interfaces 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:

  1. Let <set3> equal <set1>, the empty set.
  2. In the FOR statement, let X = <set3> if RECURSIVE and let X = <set1> if not.
  3. For each member m of <set2> and for each occurrence of m in the set X:
    • Let p be the member immediately after m in set X.
    • If p is a descendant of m, then break.
    • Otherwise, let <set3> = HIERARCHIZE(Union(<set3>, m.CHILDREN)).
  4. Return <set3>.

In the above algorithm, the BREAK command causes another iteration to begin as it breaks out of the IF clause.

The DRILLDOWNMEMBERTOP function is a variation of DRILLDOWNMEMBER. Instead of including all children (or descendants) of a member, it includes only the top n children (or descendants). The syntax of this function is as follows:

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 following 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 following set (assuming the top five states in USA were California, Texas, Florida, Georgia, and Washington):

{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 as follows:

DRILLUPMEMBER(<set1>, <set2>)

DRILLUPMEMBER 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 following set:

{USA, France, Canada}

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. That is, drilling up is usually recursive. Therefore, the call

DRILLUPMEMBER({USA, California, Los Angeles, Washington, Seattle,
King County, France, Canada}, {USA})

returns the following set:

{USA, France, Canada}

As a result, all descendants of USA—California, LA, Washington, Seattle, and 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 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 as follows:

TOGGLEDRILLSTATE(<set1>, <set2>[, RECURSIVE])

TOGGLEDRILLSTATE 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), DRILLUPMEMBER(<set1>, {m}) is applied. If it is drilled up (that is, there is no descendant of m that immediately follows m), 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 following set:

{USA, California, <[all] cities in California>, Washington,
<[all] cities in Washington>, France, Canada, Ontario,
<[all] cities in Ontario>, Quebec, <[all] cities in Quebec>}

The members that result from the drill down are hierarchized to occur under their parents. Among the original members in <set1>, order is preserved. Therefore, in the above example, France still occurs after USA and Canada still occurs after France.

If <level> is not specified, 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 following:

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. Therefore, 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. (<set1> may itself be the result of an expression.) The user interface 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, let <level> be the level of the lowest level member in <set1>.
  2. Let <set2> = <set1>.
  3. For each member m in <set1>:
    • If m is at <level>, and
    • If there are no descendants of m immediately following m,
    • Then <set2> = UNION(<set2>, m.CHILDREN, [ALL]).
  4. Return <set2>.

DRILLDOWNLEVEL can be used in unbalanced hierarchies, where the concept of a level does not exist, by using DRILLDOWNLEVEL(<set>). The members selected for drill-down are those with the greatest distance from the root of the hierarchy.

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 includes only the top n children.

For example, the call

DRILLDOWNLEVELTOP({USA, California, Washington, France, Canada,
Ontario, Quebec}, 5, States, Sales)

returns the following 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 following set:

{USA, California, <top 5 cities in California>, Washington,
<top 5 cities in Washington>, France, Canada}

To use DRILLDOWNLEVELTOP with unbalanced hierarchies, use the form DRILLDOWNLEVELTOP(<set>, <index> [, <numeric_value_expression>]). The members selected for drill-down are those with the greatest distance from the root of the hierarchy.

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 following set:

{USA, California, Washington, France, Canada, Ontario, Quebec}

If the <level> in the above call were changed from States to Countries, the resulting set would be the following:

{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>, remove m from <set1>.
  2. Return <set1>.

Note the following about DRILLDOWNLEVEL and DRILLUPLEVEL:

  • Calling DRILLUPLEVEL without a level parameter, on a set that has only the root level members, has no effect.
  • Calling DRILLDOWNLEVEL without a level parameter has no effect when the input set has all nonleaf members drilled down to their leaf-level descendants.


Drilling Through

It is often useful to retrieve the actual database rows that underlie a particular tuple. OLE DB for OLAP supports this action through a capability known as DRILLTHROUGH. For example, in this statement:

DRILLTHROUGH SELECT {[Unit Sales]} ON COLUMNS,
{[Time].{[1997].[Q1]} ON ROWS
FROM Sales
WHERE [Seattle]

the SELECT statement might return the following cellset:

 | Unit Sales
Q1 | 25

The underlying rowset might have the following rows, which contain the data underlying the specified cell:

ID | Date | Product | Location | Unit sales | Price
25678 | 1/5/1997 | Tractor | Seattle | 3 | 5000
34566 | 2/3/1997 | Tractor | Seattle | 5 | 6000
34700 | 2/7/1997 | Truck | Seattle | 15 | 15000
35110 | 2/27/1997 | Bulldozer | Seattle | 2 | 30000

The syntax of DRILLTHROUGH is as follows:

<drillthrough> ::= DRILLTHROUGH [MAXROWS <numeric_value_expression>] <select_statement>

In the example above, <select_statement> specified a single cell. The provider can choose to support select statements that return more than one cell, but it is not required to. Except for that restriction, the <select_statement> can be any valid MDX query.

If the MAXROWS clause is specified, the number of rows returned is limited to a maximum value specified by <numeric_value_expression>.

The provider might not support drill-through on all cubes. The client application should query the CUBES schema rowset and examine the DRILLTHROUGH property to determine whether it is available.


Preserving State During User-Interface Operations

Many graphical user interface (GUI) OLAP tools can be used to do the following:

  • Create a dataset based on some initial criteria.
  • Enable the user to manipulate the dataset by implementing functions that perform drill-down and drill-up operations.

Instead of rewriting the entire query to perform drill operations, which could be cumbersome and inefficient, MDX provides a powerful and simple alternative using the DRILLDOWN and DRILLUP functions.

The idea is to represent each axis expression as a named set. When the user does rollups (aggregations) and drill-downs, a function such as DRILLDOWNMEMBER, DRILLDOWNLEVEL, or DRILLUPMEMBER can be used on the named set. That way, the application does not have to access the axis expression and modify it.

For example, consider the dataset described in the SalesData example in Chapter 21: OLE DB for OLAP Overview. By using named sets, the MDX statement for this dataset looks like the following:

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 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 as follows:

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. For more information about these conditional clauses, including examples of proper usage, see the following topics in this section:

  • IF Clause
  • Simple Case Expression
  • Searched Case Expression


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 BNF form is as follows:

<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], [SalesRep].[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 sales rep. 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 terms 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,
   SalesRep.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 the section "Empty Cells" in this chapter.


Searched Case Expression

In the simple case expression example, a measure called PerfRating exists, which contains a numerical rating from 1 to 5 for each sales rep. Assume that this rating is based on the percentage by which each sales rep surpassed 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, however, the WHEN operand is a search condition. Clearly, a CASE operand is no longer needed. The construct of the searched case clause is as follows:

<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. Based on the logic explained above, the following example uses searched case to generate a performance rating for each sales rep:

WITH MEMBER [Measures].[PerfTemp] AS
   ([Measures].[Sales] - [Measures].[BudgetedSales])/
   [Measures].[BudgetedSales]*100
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,
   SalesRep.MEMBERS ON ROWS
FROM SalesCube
WHERE ([1997], [Geography].[All], [Products].[All])

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 checks only to see whether PerfTemp > 50 rather than whether PerfTemp > 50 AND PerfTemp <= 100.

As with the simple case, if no ELSE is specified and the search conditions of all the WHEN clauses are FALSE, 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 SalesData example), the SalesRep 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. This is because 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>, while others are available implicitly from the context of the MDX statement). This cell can be an empty cell. Empty cells affect expression evaluation in the following three cases:

  • 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 following 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.

For further discussion relating to empty cells and value expressions, see the following topics in this section:

  • Semantics of Empty Cell Value
  • COALESCEEMPTY Function
  • Counting Empty Cells
  • Empty Cells in a Dataset


Semantics of Empty Cell Value

To precisely define the semantics of empty cells, 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. 5When 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. Unlike for the other types of case expressions—the simple case and the searched case—support for COALESCEEMPTY is mandatory.

The BNF construct of the COALESCEEMPTY function is as follows:

<coalesce_empty> ::= COALESCEEMPTY (<value_expression> ,
                                     <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 evaluate to the empty cell value, COALESCEEMPTY returns the empty cell value.

COALESCEEMPTY is simply a special case of a CASE expression. That is,

COALESCEEMPTY(V1, V2)

is the same as the following:

CASE WHEN NOT ISEMPTY(V1) THEN V1 ELSE V2 END

And the expression

COALESCEEMPTY(V1, V2, ..., Vn), n > 2

is the same as the following:

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, but whether or not to evaluate an empty cell by this type of function should be given some careful forethought. In practice, it sometimes makes sense to count the number of empty cells. For example, when counting the number of sales reps for purposes of performance evaluation, all sales reps should be included in the count whether or not they sold anything. In this case, each no-sale results in an empty cell. However, 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-sale 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 as follows:

<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 sales rep Venkatrao did not sell anything during the first half of 1996. Suppose further that during the second half of 1996, he sold Office products but no Operating System products. Finally, suppose that 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.


Dimension Write-Back

It is often useful for a client application to be able to add or remove members from a dimension. A common example occurs in "what-if" scenarios, in which a "Hypothetical Unit Sales" member is added to a Measures dimension.

Updating Member Properties

Dimension write-back is supported in MDX by the ALTER CUBE statement. For instance, the following statement updates the VALUE property:

ALTER CUBE Sales UPDATE DIMENSION MEMBER [Forecast Sales]
   Name='Forecase Sales – Qtr1'

To modify a calculated member, use the "AS '<mdx_expression>'" clause:

ALTER CUBE Sales UPDATE DIMENSION MEMBER [Profit]
   AS '[Unit Sales] * ([Unit Price] – [Unit Cost])'

Deleting Members

The ALTER CUBE statement can also be used to delete a dimension member or an entire subtree:

ALTER CUBE Sales DROP DIMENSION MEMBER
   [Measures].[Forecast].[Forecast Sales]

ALTER CUBE Sales DROP DIMENSION MEMBER
   [Measures].[Forecast] WITH DESCENDANTS

The first of the examples above shows the deletion of a single dimension member, in this case the member [Forecast Sales], which is a child of the member [Forecast]. The second example deletes the member [Forecast] along with its entire subtree.

If a parent member is deleted without using the WITH DESCENDANTS clause, the subtree is attached to its parent. For example, the statement

ALTER CUBE Sales DROP DIMENSION MEMBER [Measures].[Forecast]

would delete the [Forecast] member. The current children of [Forecast] would be attached to the [Measures] member, producing [Measures].[Forecast Sales] and [Measures].[Forecast Profit], for example.

Adding Members

To add a member to a dimension, use the CREATE DIMENSION MEMBER clause:

ALTER CUBE Sales CREATE DIMENSION MEMBER [Joe's QuickMart]
   KEY = [Geography].[USA].[WA].[Seattle],
   [Store Number]='12'

Here, a new store is added under Seattle in the Geography dimension. The member property [Store Number] is also set.

Moving Members

Dimension members can be moved within the hierarchy, either alone or with their entire subtrees. The following statement moves the [Beverages] member, with all its associated children, to reside under the [Food Products] member.

ALTER CUBE Sales MOVE DIMENSION MEMBER [Products].[Beverages]
   WITH DESCENDANTS
   UNDER [Products].[Food Products]

The move operation is equivalent to a deletion-and-addition operation performed in sequence. See the above section on deletion for more information about the semantics of deleting a member.

Syntax

ALTER CUBE <cube> <alter_statement> [[, <alter_statement>]...]

alter_statement ::= <create_statement> |
                   <remove_statement> |
                   <move_statement> |
                   <update_statement>

<create_statement> ::= CREATE DIMENSION MEMBER <member_spec>,
                        KEY='<key_value>'
                        [[, <property_name>='<value>']
                         [, <property_name>='<value>']...]

<remove_statement> ::= DROP DIMENSION MEMBER <member_spec> [WITH DESCENDANTS]

<move_statement> ::= MOVE DIMENSION MEMBER <member_spec>
                     [WITH DESCENDANTS]
                     UNDER <member_spec>

<update_statement> ::= UPDATE DIMENSION MEMBER <member_spec>
                        [AS '<mdx_expression>', ] |
                        <property_name>='<value>'
                        [[, <property_name>='<value>']...]


User-Defined Functions

OLE DB for OLAP allows the user to create and register functions that operate on multidimensional data. User-defined functions (UDFs) can accept arguments and return values within the context of an MDX statement.

To register one or more UDFs, client applications execute the statement USE LIBRARY. Its syntax is as follows:

USE LIBRARY <program_id> [, <program_id>…]

where <program_id> is a ProgId that identifies either a type library or an individual class.

To unload the library, client applications issue the DROP LIBRARY statement. Its syntax is as follows:

DROP LIBRARY <program_id> [, <program_id>…] | ALL

When used with the ALL flag, DROP LIBRARY will unload all libraries loaded for that user session. Otherwise DROP LIBRARY expects a list of <program_id> values identifying the libraries to be unloaded.

Calling a User-Defined Function in MDX

OLE DB for OLAP defines a standard mechanism for passing data types between the OLE DB for OLAP provider and the library containing the user-defined functions.

When MDX objects are to be passed to a user-defined function, the object must first be explicitly converted to a VARIANT data type using one of the string and array manipulation functions, such as SETTOSTR or SETTOARRAY.

The string manipulation functions convert an MDX set, tuple, or member to a BSTR of the same format. For example,

SETTOSTR({[Seattle], [Tacoma], [Stowe]})

would return the following string:

"{[Geography].[City].[Seattle],
[Geography].[City].[Tacoma],
[Geography].[City].[Stowe]}"

Note that the unique names were returned.

Cell values are passed to the UDF using one of the VARIANT data types. This table lists the supported values.

VARIANT type | Description
VT_I2 | 16-bit integer
VT_I4 | 32-bit integer
VT_BOOL | Boolean
VT_UI1 | Single character
VT_R4 | Single precision floating point
VT_R8 | Double precision floating point
VT_CY | Currency
VT_DATE | Date
VT_BSTR | String
VT_VARIANT | Variant
VT_ARRAY | <type> | Safe Array of any of the other types listed in this table

User-defined functions can take any number of parameters, and optional parameters are supported. Optional parameters are specified using the optional tag in the IDL file.

Overloading functions based on type or return value is not allowed.

When resolving function names, the OLE DB for OLAP provider will give standard MDX functions highest precedence. UDFs are then evaluated according to precedence rules that are provider-specific. Use of the fully qualified function name will avoid ambiguous references. Function names are fully qualified by using the syntax <library_name>!<interface_name>!<function_name>.

USE LIBRARY CurrencyLib.ConversionClass

WITH MEMBER Measures.ProfitInPounds AS
   'CurrencyLib!ICurrencyConvert!ConvertToGBP([Profit])'
SELECT {Measures.ProfitInPounds} ON COLUMNS,
[Product Family].MEMBERS ON ROWS
FROM Sales

Note the use of the syntax <library_name>!<interface_name>!<function_name> in the query above. The formal syntax is as follows:

[<library_name>!][<interface_name>!]<function_name>(<parameters>)

The <library_name> and <interface_name> modifiers can be used independently or together.

The SETTOARRAY function can be used to convert a set to an array of the set's evaluated values. The values are consistent with the context of the entire MDX statement. For example, the following would return a table of the weighted average of unit sales for the year 1998:

SELECT MyLibrary!WeightedAverage(SETTOARRAY({[Seattle], [Tacoma]})) ON COLUMNS,
[Product Family].MEMBERS ON ROWS
WHERE [1998], [Measures].[Unit Sales]
FROM Sales

The value passed to the WeightedAverage UDF would be a SAFEARRAY of the appropriate type. For the preceding example, if we made certain assumptions about the data types and contents of the cube, we would get back a two-dimensional safe-array. One dimension of the array would be defined by {Seattle, Tacoma}, the other dimension defined by [Product Family].MEMBERS. The values in the array would be a numeric type such as VT_R4 and would be the unit sales for 1998 at that coordinate.

Handling Return Values

The following table indicates allowable UDF return types. Of course, for an Automation interface, the actual return value of the function is an HRESULT. The return value discussed here is in fact a parameter to the function, specified using retval in the IDL file.

VARIANT type | Description
VT_I2 | 16-bit integer
VT_I4 | 32-bit integer
VT_BOOL | Boolean
VT_UI1 | Single character
VT_R4 | Single precision floating point
VT_R8 | Double precision floating point
VT_CY | Currency
VT_DATE | Date
VT_BSTR | String
VT_VARIANT | Variant
VT_ARRAY | <type> | Safe Array of any of the other types listed in this table

If the return type is an MDX object such as a set, tuple, or member, the UDF return value should be a string. The appropriate string manipulation function should be used to convert to the correct type.


Chapter 26: MDX Grammar

This chapter 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. For specific information about MDX statements, see "Advanced MDX" in Chapter 25: Multidimensional Expressions. The parts of an MDX statement are described in "Parts of an MDX Statement," also in Chapter 25.

The complete construct of the MDX statement and the leveling rules for the statement are presented in "MDX Statement Construction," in this chapter.

At the end of each subclause 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.


MDX Elements

The construct of elements in MDX are as follows:

<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 <end_delimiter>

<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

<dimension> ::= <dimension_name>

<hierarchy> ::= <hierarchy_name>

<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, to 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 MDX Elements

Several of the following leveling rules 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, the ability to qualify is optional. For more information, see "Provider Implementation Considerations for Unique Names" in Chapter 25: Multidimensional Expressions.

  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 only support one of the first two productions for <level>. (Support for the third production is mandatory.) Therefore, in the following case, the ability to qualify by <dim_hier> is optional:
    <level> ::= [<dim_hier>.] <identifier>

    Consumers can check the value of the property MDPROP_NAMED_LEVELS to see whether the provider supports named levels. If it does, 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.


MDX Statements

<value_expression> ::= <numeric_value_expression>
                     | <string_value_expression>

Statements in MDX operate on the following basic entities:

  • Set. A collection of tuple elements.
  • Member. A member of a dimension.
  • Tuple. A collection of members from different dimensions.
  • Numeric. Can be an exact numeric, such as integer or decimal, or an approximate numeric, such as float or double.
  • Time dimension. Time series functions operate on the dimension that is specified implicitly as part of their <set>, <member>, or <level> arguments.

MDX statements can perform a variety of other functions, such as the following:

  • Expressing user-interface operations
  • Searched case expressions
  • Conditional expressions (described in Chapter 25: Multidimensional Expressions)
  • Dimension write-back (covered in detail in Chapter 25: Multidimensional Expressions)


General Leveling Rules for MDX Statements

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.

    Note The syntax of <value_expression> is generally the same as SQL-92, subclause 6.11, <value_expression>. Differences are as follows:

    • <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.


MDX Statement Definitions

<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 must 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>
                                    [, <value_expression> ]...)

<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> ::= ||


MDX Set Value Expressions

The construct of the MDX set value expression is as follows:

Note <index> denotes an integer argument. If an arbitrary <numeric_value_expression> appears here, it is truncated to the nearest integer.

<index> ::= <numeric_value_expression>

<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>)

Note The following set functions require that their <set> arguments be formed from a single dimension, not the result of a CROSSJOIN: DRILLDOWNMEMBER, DRILLUPMEMBER, DRILLDOWNMEMBERTOP, DRILLUPMEMBERTOP, DRILLDOWNMEMBERBOTTOM, DRILLDOWNLEVEL, DRILLDOWNLEVELTOP, DRILLDOWNLEVELBOTTOM, DRILLUPLEVEL, and TOGGLEDRILLSTATE.

<desc_flags> ::= SELF
               | AFTER
               | BEFORE
               | BEFORE_AND_AFTER
               | SELF_AND_AFTER
               | SELF_AND_BEFORE
               | SELF_BEFORE_AFTER

Set value expressions are described in "Set Value Expressions" in Chapter 25: Multidimensional Expressions.


Leveling Rules for MDX Set Value Expressions

  1. Support for <desc_flags> other than SELF is optional. Consumers can check the value of MDPROP_MDX_DESCFLAGS to see which <desc_flags> the provider supports.
  2. Consumers can check the value of the MDPROP_MDX_SET_FUNCTIONS property to see whether a provider supports set functions.

The ability to have expressions containing nonmeasure dimension members in the <numeric_value_expression> argument of set functions is optional. For example, if Revenue and Cost are measures and USA is a member of the 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 is mandatory. However, support for expressions such as (USA, Sales)/3 is optional. Consumers can check the value of the property MDPROP_MDX_NONMEASURE_EXPRESSIONS to see whether a provider supports this feature.


MDX Member Value Expressions

Member value expressions identify the elements of the members dimension. The construct is as follows:

<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>]]])

The following table identifies and describes the elements of a member value expression.

Element | Description
<member>.{PARENT} | Names the parent of a member.
<member>.{FIRSTCHILD} | Returns the first child of a member according to an implicit natural order. (The children of any member are arranged along this natural order.)
<member>.{LASTCHILD} | Names the last child of a member according to the natural order.
<member>.{PREVMEMBER} | Names the previous member along the level implied by <member>.
<member>.{NEXTMEMBER} | Names the next member along the level implied by <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, 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 of the <member>. For example,
May.FIRSTSIBLING

returns April, and the expression

May.LASTSIBLING

returns June (assuming that the parent level is Quarters).

Consumers can check the value of the property MDPROP_MDX_MEMBER_FUNCTIONS to see whether a provider supports these expressions.


Leveling Rules for Member Value Expressions

The following member functions are optional: COUSIN, PARALLELPERIOD, OPENINGPERIOD, and CLOSINGPERIOD. Consumers can check the value of the property MDPROP_MDX_MEMBER_FUNCTIONS to see whether a provider supports these functions.


MDX Tuple Value Expressions

The construct of the tuple value expression is as follows:

<tuple_value_expression> ::= <set>.CURRENTMEMBER
                           | <set>[.ITEM]({<string_value_expression>
                                       [, <string_value_expression>...]}
                                       | <index>)

The following table describes the tuple value expressions.

Element | Description
<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.


MDX Conditional Expressions

MDX has conditional clauses of several types, with the different types steadily increasing in functionality and complexity. For more information, including examples of proper usage, see the following topics in Chapter 25: Multidimensional Expressions.

  • IF Clause
  • Simple Case Expression
  • Searched Case Expression


MDX Numeric Value Functions

Some numeric functions are described further in other sections of this guide. Following is the complete list of the MDX numeric value functions:

<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 MDX Numeric Value Functions

The following numeric functions are optional: MEDIAN, VAR, STDEV, RANK, AGGREGATE, COVARIANCE, CORRELATION, LINREGINTERCEPT, LINREGSLOPE, LINREGVARIANCE, LINREGR2, and LINREGPOINT. Consumers can check the value of the property MDPROP_MDX_NUMERIC_FUNCTIONS to see whether a provider supports this feature.


MDX 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 MDX Search Condition

If <value_expression>s in a <boolean_primary> value are <string_value_expression>s, 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.


MDX Statement Construction

The complete construct of the MDX statement is presented in "The MDX Statement" in Chapter 25: Multidimensional Expressions. The leveling rules for the statement are presented in the section "Leveling Rules for MDX Statement."


Leveling Rules for MDX Statement

  1. Support for WITH clause 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> and <drop_formula_statement> is optional. Consumers can check the value of the property MDPROP_MDX_FORMULAS to see whether a provider supports this feature.
  7. If a provider supports <create_formula_statement>, support for a <scope> of GLOBAL is optional. Consumers can check the value of the property MDPROP_MDX_FORMULAS to see whether a provider supports this feature.


Chapter 27: Mapping MDX to SQL Statements

The mappings described in this chapter 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, but 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 not difficult to alter mappings to accommodate the differences.


Concept of 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). The following topics in this section provide information about converting a set expression into a table:

  • Tables and Sets
  • Literal Sets


Tables and Sets

When mapping a statement in MDX, a set maps to a table, which reflects the dimensionality of the set.

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, and a tuple is a vector of members.

Let n be the number of members in any given tuple. The value n is the dimensionality of the set. The following rules apply:

  • 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

The set, specifically the set of axis, 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 there is a RANK column that has the rank of a tuple within the set. This column is needed because sets in MDX are ordered, while a table in SQL is not.

Any intermediate table in the expansion of an MDX statement 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 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 a 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 statement.


Literal Sets

Literal set, as used here, denotes a set that is specified by an enumerated list of tuples. Consider the following set:

{(x,y), (a,b)}

In this set, x and a are members of dimension Name1, and y and b belong to dimension Name2. This literal set can be expanded into a table value expression such as the following:

CREATE LOCAL TEMPORARY VIEW S1(Name1, Name2, Rank) AS
SELECT Name1, Name2
FROM ( VALUES (x,y,1), (a,b, 2) ) AS S2(Name1, Name2, Rank)
ORDER BY Rank

In the preceding 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 means that a table can be defined from literals as follows:

<literal_table> ::= (VALUES(<literal>[{, <literal>}...)
                     [{,( <literal> [ {,<literal>}...)}...] )
                     [AS] <correlation_name>[ <left_paren>
                          <derived_column_list> <right_paren> ]

The result is a table S 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 to be qualified only to the extent needed to make them unique. When generating an SQL expression for the table, 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 statement.

In order to get a table S from S1, which contains the fully qualified names, use the following construct:

CREATE LOCAL TEMPORARY VIEW S(Name1, Name2, Rank) AS
SELECT M1.Name1, M2.Name2, S1.Rank
FROM MEMBER M1, MEMBER M2, S1
WHERE M1.Name LIKE ("%." || S.Name1)
AND M2.Name LIKE ("%." || S.Name2)
ORDER BY S1.Rank

In the above query, "||" is the string concatenation operator.


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 following 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.

For further information about MDX statements, see the following topics in this section:

  • FROM Clause
  • Axis Expressions
  • WHERE Clause


FROM Clause

In an MDX statement, the FROM clause has a cube name. In the corresponding SQL statements (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, the LEVELS table might also be 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.

Note This table can be joined with the MEMBERS table to yield the axis rowset.

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.Kansas.Topeka | 3
Products.[All].Office.Access | Geography.[All].USA | 4
Products.[All].Office.Access | Geography.[All].Canada | 5
Products.[All].Office.Word | Geography.[All].USA.Kansas | 6
Products.[All].Office.Word | Geography.[All].USA.NewYork.Buffalo | 7
Products.[All].Office.Word | Geography.[All].USA.Kansas.Topeka | 8
Products.[All].Office.Word | Geography.[All].USA | 9
Products.[All].Office.Word | Geography.[All].Canada | 10

The generation of the preceding 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 "Mapping Example" offers 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 in the section "Axis Expressions," this table represents the entire dataset.


Sample Cube

The cube examples in this chapter assume a cube called SalesCube with the following structure:

  • The cube has four dimensions: Geography, Measures, Time, Products.
  • The Geography dimension has these levels and members:
    • 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, Quebec)
    • Level 3: City (Kansas has Topeka, Wichita; New York has Buffalo, Ithaca; BC has Vancouver, Victoria; Quebec has Montreal, Sherbrooke)
    • Level 4: ZIP code
  • The Time dimension has these levels and members:
    • Level 0: [All] (One member, also [All])
    • Level 1: Year
    • Level 2: Month
    • Level 3: Day
  • 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 Microsoft Word belonging to the Products level (at level 2).


MEMBERS Table

The MEMBERS table contains all members in all dimensions of the cube. The following table provides a sample selection from the MEMBERS table. It does not include all the columns.

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 and is the rank among all the children of a member. This makes inserts easier. For example, if a new city is inserted into the list of cities for Germany, there should be no need to update the natural sort rank of all cities in the world.


LEVELS Table

The LEVELS table contains all levels of all dimensions of the cube. The following table provides a sample selection from the LEVELS table.

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 | - | -

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 of related data. For example, 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", and so on, which are all aggregations.


Converting a Set Expression into a Table

The set is fundamental in MDX. Each axis is composed of a set of tuples. The slicer condition (WHERE clause) is a set (usually with just one tuple). This section does not describe how to map every set value expression. For illustration purposes, it does present a limited number of topics to convey the concepts, which will help with mappings optimized for your SQL implementations.

The following topics provide introductory information about the process:

  • Tables and Sets
  • Literal Sets

And the topics below provide information about converting a set expression into a table:

  • UNION Function
  • EXCEPT Function
  • INTERSECT Function
  • CROSSJOIN Function
  • HIERARCHIZE Function


UNION Function

This section explains how the UNION function handles duplicates.

Removing Duplicates

Consider UNION(S1, S2) where:

S1 = {Kansas, Buffalo, Topeka, USA, Canada}

S2 = {Kansas, Topeka, Wichita, Canada}

As shown in the section "Literal Sets," create two tables—S1 and S2—that contain the fully qualified member names in the sets S1 and S2, respectively.

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 does the following:

  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, orders each group by Src and Rank, and then picks up the first one. This ensures that duplicates are eliminated from the tail.
  3. The Rank operation 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 preceding section, create the tables S1 and S2. Then 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 with a list of columns (which is equivalent to the number of dimensions in the input sets).


EXCEPT Function

This section explains how the EXCEPT function handles duplicates.

Removing Duplicates

Consider EXCEPT(S1, S2), where:

S1 = {Kansas, Buffalo, Buffalo, Topeka, Topeka, Wichita, Canada, BC}

S2 = {Buffalo, Topeka, USA, Quebec}

As shown in the section "Literal Sets," create two tables—S1 and S2—that contain the fully qualified member names in the sets S1 and S2, respectively.

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 does the following:

  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, orders each group by Rank, and then 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 preceding section, create the tables S1 and S2. Then 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 produces 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 of 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 with 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 Function

This section explains how the INTERSECT function handles duplicates.

Removing Duplicates

Consider INTERSECT(S1, S2) where:

S1 = {Kansas, Buffalo, Buffalo, Topeka, Topeka, Wichita, Canada, BC}

S2 = {Buffalo, Topeka, Topeka, Canada, Quebec}

As shown in the section "Literal Sets," create two tables—S1 and S2—that contain the fully qualified member names in the sets S1 and S2, respectively.

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 does the following:

  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, orders each group by Rank, and then 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 preceding section, create the tables S1 and S2. Then 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 produces 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 with 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 Function

Consider CROSSJOIN(S1, S2). This can be expanded as follows:

  • Make a table out of the sets S1 and S2.
  • If a set is literal (that is, its members are explicitly enumerated), make this table as shown in the section "Literal Sets."
  • If a set is the result of an expression, look at the section dealing with that expression to see how to get the table.

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 Function

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}. As detailed in the section "Literal Sets," create 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 might be no need to do this because the input set to HIERARCHIZE might be derived from another expression.

Gather information as follows:

  1. Use the following query to verify that all members belong to the same dimension. If the returned count is greater than 1, it is an invalid set.
    SELECT COUNT DISTINCT COMPONENT(S.Name, -1)
   FROM S
  2. Get the dimension name by using the following query:
    SELECT DISTINCT COMPONENT(S.Name, -1)
   FROM S
  3. Get the level number of each level represented in the input set:
    SELECT DISTINCT LEVEL(S.Name)
   FROM S

The objective is to create a table T that looks like the following table.

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 that contains the rank of each of its ancestors. To get the hierarchized set from T, just use the following:

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 the COUNTRY and CITY levels but none from the STATE level.

To create the table, perform the following steps:

  1. Define a table that has elements of set S and its levels:
    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.)

    Note The WHERE clause on each UNION iterates from Level = 0 through the level of the lowermost member in S1. Also, the ANCESTOR function in the FROM iterates from 1 through 1 + the level of the lowermost member in S1.

    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

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 above 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.

Note Even when none of the ancestors of a member are present in the input set, the sorting is done as if the appropriate ancestor were present. Thus, in the preceding example, Buffalo and NYC appear after LA because California (the parent of LA) sorts before New York (the parent of Buffalo and NYC).


Simple Extensions to SQL-92

The mapping of MDX to a set of SQL statements requires a few simple extensions to SQL-92. The following sections specify these extensions:

  • Local Temporary Views
  • Special Value: ALL()
  • Rank Operations
  • New Functions Extending SQL-92


Local Temporary Views

An MDX statement results in a sequence of SQL SELECT statements. The result of each such statement is a virtual table that is used in the FROM clause of one or more subsequent statements. It is necessary 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 as follows:

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()

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 function allows you to define simpler semantics to access these rows.

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

Both the order of tuples in the sets processed by MDX and the placement level are important for many OLAP operations.

RANK Clause

To preserve order, a RANK column must be generated for every set. This 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 as follows:

<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, 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, 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, the value is row count. If LEVELS is specified, the value is level count.
  • If a name for the new column is not specified (using the AS <column_name> clause), 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 that the top x rows be picked from each group (according to some criteria). As always, it is required that the ordering be preserved. The RANKTOP clause has the ability to pick the top x rows.

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 as follows:

<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; their output is a table. Therefore, if a query expression has more than one RANK or RANKTOP clause, they are evaluated in the order of appearance. 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, consider 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)
      RANKTOP 1 ROWS RANKORDER BY Src, Rank RANKGROUP BY Name)
   RANK ROWS AS NewRank RANKORDER BY Src, Rank)
ORDER BY Rank

In the preceding example, the innermost query expression is used as an input to the RANKTOP clause. The output of the RANKTOP clause, which is again a table (indented two levels above), is used as an input to the RANK clause. The output of the RANK clause is also a table (indented one level above), which is used as input to the FROM clause.


New Functions Extending SQL-92

The following functions are new. They operate on a fully qualified member name. These functions are merely string manipulation functions and therefore 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 the following: Output the substring of <fq_name> that is the result of deleting 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>, as in the following example:

ANCESTOR(Geography.[All].USA.Kansas.Topeka, 1) =
Geography.[All].USA.Kansas

Semantically, the output results from deleting the <generation> period and everything following it and then returning the resulting string. If LEVEL(<fq_name>) + 1 <= <generation>, 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 the count by 1. If <fq_name> does not have any periods, –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, and ALL() is returned if <level> is greater than LEVEL(<fq_name>). For more information about ALL(), see "Special Value: ALL()."


Mapping Example

Consider the following query:

SELECT
   CROSSJOIN ({Access, Word}, {Kansas, Buffalo, Topeka, USA, Canada})
      ON COLUMNS,
   {[1994].Qtr1.MEMBERS, [1994].Qtr2, [1994].Qtr3, [1994].Qtr4.MEMBERS} ON ROWS
FROM SalesCube
WHERE (Sales)

For a detailed analysis of this example, see the following topics in this section:

  • COLUMNS Axis Expression
  • ROWS Axis Expression
  • Getting Cell Values


COLUMNS Axis Expression

The COLUMNS axis expression consists of the CROSSJOIN function applied on two literal sets. Applying the mappings described in "Literal Sets" generates two virtual tables. These virtual tables are used as input to the SQL statements described in "CROSSJOIN Function."

The result of these operations yields the following two tables (denoted as Table C and Table CPrime):

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.Kansas.Topeka | 3
Products.[All].Office.Access | Geography.[All].USA | 4
Products.[All].Office.Access | Geography.[All].Canada | 5
Products.[All].Office.Word | Geography.[All].USA.Kansas | 6
Products.[All].Office.Word | Geography.[All].USA.NewYork.Buffalo | 7
Products.[All].Office.Word | Geography.[All].USA.Kansas.Topeka | 8
Products.[All].Office.Word | Geography.[All].USA | 9
Products.[All].Office.Word | Geography.[All].Canada | 10
Product | ProdLine | Product.[All] | City | State | Country | Geography.[All] | Rank
Products.[All].Office.Access | Products.[All].Office | Product.[All] | ALL() | Geography.USA.Kansas | Geography.USA | Geography.[All] | 1
Products.[All].Office.Access | Products.[All].Office | Product.[All] | Geography.USA.NewYork.Buffalo | Geography.USA.NewYork | Geography.USA | Geography.[All] | 2
Products.[All].Office.Access | Products.[All].Office | Product.[All] | Geography.USA.Kansas.Topeka | Geography.USA.Kansas | Geography.USA | Geography.[All] | 3
Products.[All].Office.Access | Products.[All].Office | Product.[All] | ALL() | ALL() | Geography.USA | Geography.[All] | 4
Products.[All].Office.Access | Products.[All].Office | Product.[All] | ALL() | ALL() | Geography.Canada | Geography.[All] | 5
Products.[All].Office.Word | Products.[All].Office | Product.[All] | ALL() | Geography.USA.Kansas | Geography.USA | Geography.[All] | 6
Products.[All].Office.Word | Products.[All].Office | Product.[All] | Geography.USA.NewYork.Buffalo | Geography.USA.NewYork | Geography.USA | Geography.[All] | 7
Products.[All].Office.Word | Products.[All].Office | Product.[All] | Geography.USA.Kansas.Topeka | Geography.USA.Kansas | Geography.USA | Geography.[All] | 8
Products.[All].Office.Word | Products.[All].Office | Product.[All] | ALL() | ALL() | Geography.USA | Geography.[All] | 9
Products.[All].Office.Word | Products.[All].Office | Product.[All] | ALL() | ALL() | Geography.Canada | Geography.[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

    In the current example, this is 2.

  2. Find the names of each dimension:
    SELECT DISTINCT COMPONENT(Name, -1) FROM C

    In the current 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)

    In the current example, this is 3 for Geography and 2 for Products.

  4. Create 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 create 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 the section "COLUMNS Axis Expression," obtain the following tables R and Rprime:

Name | Rank
Time.[All].1994.Jan | 1
Time.[All].1994.Feb | 2
Time.[All].1994.Mar | 3
Time.[All].1994.Qtr2 | 4
Time.[All].1994.Qtr3 | 5
Time.[All].1994.Oct | 6
Time.[All].1994.Nov | 7
Time.[All].1994.Dec | 8
Month | Quarter | Year | Time.[All] | Rank
Time.[All].1994.Jan | Time.[All].1994.Qtr1 | 1994 | Time.[All] | 1
Time.[All].1994.Feb | Time.[All].1994.Qtr1 | 1994 | Time.[All] | 2
Time.[All].1994.Mar | Time.[All].1994.Qtr1 | 1994 | Time.[All] | 3
ALL() | Time.[All].1994.Qtr2 | 1994 | Time.[All] | 4
ALL() | Time.[All].1994.Qtr3 | 1994 | Time.[All] | 5
Time.[All].1994.Oct | Time.[All].1994.Qtr4 | 1994 | Time.[All] | 6
Time.[All].1994.Nov | Time.[All].1994.Qtr4 | 1994 | Time.[All] | 7
Time.[All].1994.Dec | Time.[All].1994.Qtr4 | 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 28: 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 object directly by using the provider's CLSID
  • Create a schema rowset
  • Create a dataset object and obtain axis information
  • Create a dataset object and obtain the cell data


Connecting to an MDP by Using the Root Enumerator

Connection to an 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 object:

//
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 Object 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 section "Creating a Schema Rowset") to traverse the schema rowset for the MDP data source object.

//
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. IDBSchemaRowset 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 whether
   // 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. IMDDataset::GetAxisInfo and IMDDataset::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++)
   {
      // MDAXISINFO_GETAT(rgAxisInfo, iAxis).rgpwszDimensionNames points
      // to the dimension name.
      assert( MDAXISINFO_GETAT(rgAxisInfo, iAxis).rgpwszDimensionNames);
   }

      // Fetch the axis rowset for each axis.
      IRowset* pIrowset = NULL;
      hr = pIMDDataset->GetAxisRowse(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, and 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;
}


Part 4 OLE DB Reference

OLE DB Programmer's Reference

Part 4: OLE DB Reference

This section of the OLE DB Programmer's Reference contains syntax and semantic information for all OLE DB interfaces and methods.

OLE DB Interfaces

IAccessor

IAlterIndex

IAlterTable

IBindResource

IChapteredRowset

IColumnsInfo

IColumnsInfo2

IColumnsRowset

ICommand

ICommandPersist

ICommandPrepare

ICommandProperties

ICommandText

ICommandWithParameters

IConvertType

ICreateRow

IDBAsynchNotify

IDBAsynchStatus

IDBBinderProperties

IDBCreateCommand

IDBCreateSession

IDBDataSourceAdmin

IDBInfo

IDBInitialize

IDBProperties

IDBSchemaRowset

IErrorInfo

IErrorLookup

IErrorRecords

IGetDataSource

IGetRow

IGetSession

IGetSourceRow

IIndexDefinition

IMultipleResults

IOpenRowset

IParentRowset

IRegisterProvider

IRow

IRowChange

IRowSchemaChange

IRowset

IRowsetChange

IRowsetChapterMember

IRowsetCurrentIndex

IRowsetFind

IRowsetIdentity

IRowsetIndex

IRowsetInfo

IRowsetLocate

IRowsetNotify

IRowsetRefresh

IRowsetResynch

IRowsetScroll

IRowsetUpdate

IRowsetView

IScopedOperations

ISequentialStream

ISessionProperties

ISourcesRowset

ISQLErrorInfo

ISupportErrorInfo

ITableCreation

ITableDefinition

ITableDefinitionWithConstraints

ITransaction

ITransactionJoin

ITransactionLocal

ITransactionObject

ITransactionOptions

IViewChapter

IViewFilter

IViewRowset

IViewSort

OLE DB Core Interfaces

IDataConvert

IDataInitialize

IDBPromptInitialize

IDCInfo

IRowPosition

IRowPositionChange

OLE DB for OLAP Interfaces

IMDDataset

IMDFind

IMDRangeRowset


OLE DB Interfaces

This section of the OLE DB Programmer's Reference contains syntax and semantic information for the OLE DB interfaces.

IAccessor

IAlterIndex

IAlterTable

IBindResource

IChapteredRowset

IColumnsInfo

IColumnsInfo2

IColumnsRowset

ICommand

ICommandPersist

ICommandPrepare

ICommandProperties

ICommandStream

ICommandText

ICommandWithParameters

IConvertType

ICreateRow

IDBAsynchNotify

IDBAsynchStatus

IDBBinderProperties

IDBCreateCommand

IDBCreateSession

IDBDataSourceAdmin

IDBInfo

IDBInitialize

IDBProperties

IDBSchemaRowset

IErrorInfo

IErrorLookup

IErrorRecords

IGetDataSource

IGetRow

IGetSession

IGetSourceRow

IIndexDefinition

IMultipleResults

IOpenRowset

IParentRowset

IRegisterProvider

IRow

IRowChange

IRowSchemaChange

IRowset

IRowsetBookmark

IRowsetChange

IRowsetChapterMember

IRowsetCurrentIndex

IRowsetFind

IRowsetIdentity

IRowsetIndex

IRowsetInfo

IRowsetLocate

IRowsetNotify

IRowsetRefresh

IRowsetResynch

IRowsetScroll

IRowsetUpdate

IRowsetView

IScopedOperations

ISequentialStream

ISessionProperties

ISourcesRowset

ISQLErrorInfo

ISupportErrorInfo

ITableCreation

ITableDefinition

ITableDefinitionWithConstraints

ITransaction

ITransactionJoin

ITransactionLocal

ITransactionObject

ITransactionOptions

IViewChapter

IViewFilter

IViewRowset

IViewSort


IAccessor

IAccessor provides methods for accessor management. For information about accessors, see "Accessors" in Chapter 6: Getting and Setting Data.

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 available only 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 IAccessor::AddRefAccessor increment the reference count of the accessor, and calls to IAccessor::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 IAccessor::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 IAccessor::AddRefAccessor to increment the reference count of that accessor.

When the consumer is done with a rowset, it calls IAccessor::ReleaseAccessor to release any accessors on the rowset, including accessors inherited from the command. When the consumer is done with a command, it calls IAccessor::ReleaseAccessor to release any accessors created on the command. In either case, the consumer must call IAccessor::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,
   DBREFCOUNT *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 IAccessor::AddRefAccessor before passing it to another thread.

See Also

IAccessor::CreateAccessor | IAccessor::ReleaseAccessor


IAccessor::CreateAccessor

Creates an accessor from a set of bindings.

HRESULT CreateAccessor (
   DBACCESSORFLAGS   dwAccessorFlags,
   DBCOUNTITEM       cBindings,
   const DBBINDING   rgBindings[],
   DBLENGTH          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 IAccessor::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: Getting and Setting Data.
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 convertible 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 and may be ignored by providers that do not provide further optimizations or restrictions based on its setting. Providers that enforce restrictions, such as limiting the number or types of columns bound in optimized accessors, must set this flag when IAccessor::GetBindings is called on an optimized accessor. Providers that do not impose any restrictions on optimized accessors or that ignore this flag entirely are not required to return this flag when IAccessor::GetBindings is called.
DBACCESSOR_INHERITED Indicates an accessor on a command should be inherited by a rowset when the rowset implementation is in a separate component from the command object. IAccessor::CreateAccessor is then used to pass an existing command accessor to the rowset implementation.

When DBACCESSOR_INHERITED is specified, phAccessor is used as an [in] argument and contains a pointer to the value of the existing accessor handle. The rowset component then creates an internal accessor according to the specified bindings for this handle value.
cBindings
[in]
The number of bindings in the accessor.

If cBindings is zero, IAccessor::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 IRowsetChange::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: Getting and Setting Data.
cbRowSize
[in]
The number 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 either case, 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 criteria 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 ICommand::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 IViewFilter::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 IAccessor::CreateAccessor fails, it must attempt to set *phAccessor to a null handle.
rgStatus
[out]
An array of cBindings DBBINDSTATUS values in which IAccessor::CreateAccessor returns the status of each binding—that is, whether or not it was successfully validated. 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 reasons described in the following table.
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 IAccessor::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_UNSUPPORTEDCONVERSION If the accessor is validated against the metadata when IAccessor::CreateAccessor is called, DBBINDSTATUS_UNSUPPORTEDCONVERSION can be returned for the following reasons:
  • The specified conversion was not supported by the provider. For a list of conversions the accessor must support, see "Data Type Conversion" in Chapter 13: Data Types in OLE DB.
  • 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 a combination of two or more 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.

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, and the provider does not support binding long data to provider owned memory.

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.

The provider is an OLE DB 2.5 or later provider, and dwFlags was set to an unknown or invalid value.

dwMemOwner was invalid. Providers are not required to return DBBINDSTATUS_BADBINDINFO. If they ignore an invalid value of dwMemOwner, they must default to DBMEMOWNER_CLIENTOWNED.

If the accessor is validated against the metadata when IAccessor::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 IRowsetIndex::Seek or IRowsetIndex::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 IRowsetIndex::Seek or IRowsetIndex::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_BADSTORAGEFLAGS dwFlags, in the DBOBJECT structure pointed to by a binding, specified invalid storage flags.

If the accessor is validated against the metadata when IAccessor::CreateAccessor is called, DBBINDSTATUS_BADSTORAGEFLAGS can be returned if dwFlags, in the DBOBJECT structure pointed to by a binding, specified a valid storage flag that was not supported by the object.

This causes 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 IAccessor::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 COM 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, the provider does not support setting data with multiple storage objects in a single accessor, and wType in more than one binding was DBTYPE_IUNKNOWN. Providers for whom DBPROP_MULTIPLESTORAGEOBJECTS is VARIANT_FALSE that still support setting data with multiple consumer storage objects specified in a single accessor allow the creation of accessors with more than one binding to DBTYPE_IUNKNOWN and return DBSTATUS_E_CANTCREATE if the accessor is used to open multiple storage objects from the provider (such as in a call to IRowset::GetData).

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 IAccessor::CreateAccessor was called on a rowset.

DB_E_BYREFACCESSORNOTSUPPORTED
dwAccessorFlags was DBACCESSOR_PASSBYREF, and the value of the DBPROP_BYREFACCESSORS property is VARIANT_FALSE.

Consumers should always check to see whether the provider supports the property DBPROP_BYREFACCESSORS and call IAccessor::CreateAccessor with the DBACCESSOR_PASSBYREF flag only if the provider supports this property. For performance reasons, some service providers might be unable to detect whether the underlying data provider supports DBACCESSOR_PASSBYREF on the CreateAccessor call and might return DB_E_BYREFACCESSORNOTSUPPORTED.

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 IRowsetNofity in the consumer that had not yet returned, and the provider does not support reentrancy in this method.
DB_E_NULLACCESSORNOTSUPPORTED
cBindings was zero, and either the rowset does not expose IRowsetChange::InsertRow or IAccessor::CreateAccessor was called on a command.

Comments

For general information about accessors, see "Accessors" in Chapter 6: Getting and Setting Data.

IAccessor::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 IAccessor::CreateAccessor does not validate the accessor against the metadata, 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 return codes listed in the following table 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_ERRORSOCCURRED 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,
   DBCOUNTITEM        *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: Getting and Setting Data.

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 IRowsetNofity 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 the accessor is a null accessor, 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,
   DBREFCOUNT *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 IRowsetNofity in the consumer that had not yet returned, and the provider does not support reentrancy in this method.

Comments

IAccessor::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 IAccessor::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 indexes.

Method | Description
AlterIndex | Alters the index ID and/or properties associated with an index.


IAlterIndex::AlterIndex

Alters the ID and/or properties associated with an index.

HRESULT AlterIndex(
   DBID       *pTableID,
   DBID       *pIndexID,
   DBID       *pNewIndexID,
   ULONG       cPropertySets,
   DBPROPSET   rgPropertySets[]);

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 is a null pointer, 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.
DB_S_ERRORSOCCURRED
The index was altered 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 following:
  • 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.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pTableID was a null pointer.

pIndexID was a null pointer.

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_BADINDEXID
*pIndexID or *pNewIndexID was an invalid index ID.
DB_E_BADTABLEID
*pTableID was an invalid table ID.
DB_E_DUPLICATEINDEXID
The index specified in *pNewIndexID already exists in the data store and is not the same as pIndexID.
DB_E_ERRORSOCCURRED
The index was not altered 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_INDEXINUSE
The specified index was in use.
DB_E_NOINDEX
The index specified in *pIndexID does not exist in the data store or does not apply to the specified table.
DB_E_NOTABLE
The table specified in *pTableID does not exist in the data store.
DB_E_TABLEINUSE
The specified table was in use, and the provider could not modify the index with the table open.
DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to alter the index.
XACT_E_XTIONEXISTS
The provider supports transactional DDL, the session is participating in a transaction, and the value of DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DML.

Comments

IAlterIndex::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, the index ID is unchanged. If cPropertySets is zero, no properties are changed. If pNewIndexID is null or the same as pIndexID, and cPropertySets is zero, 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 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

Alters the column ID and/or properties associated with a column in a table.

HRESULT AlterColumn(
   DBID               *pTableID,
   DBID               *pColumnID,
   DBCOLUMNDESCFLAGS   dwColumnDescFlags,
   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.
dwColumnDescFlags
[in]
Values as described in the enumeration DBCOLUMNDESCFLAGS. As illustrated in the following table, 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.
DBCOLUMNDESCFLAGS values | Associated portion of DBCOLUMNDESC
DBCOLUMNDESCFLAGS_TYPENAME | pwszTypeName
DBCOLUMNDESCFLAGS_ITYPEINFO | ITypeInfo *
DBCOLUMNDESCFLAGS_PROPERTIES | rgPropertySets AND cPropertySets
DBCOLUMNDESCFLAGS_CLSID | pclsid
DBCOLUMNDESCFLAGS_COLSIZE | ulColumnSize
DBCOLUMNDESCFLAGS_DBCID | dbcid
DBCOLUMNDESCFLAGS_WTYPE | wType
DBCOLUMNDESCFLAGS_PRECISION | bPrecision
DBCOLUMNDESCFLAGS_SCALE | bScale
pColumnDesc
[in]
A pointer to a DBCOLUMNDESC structure defining new values for the columns. Only the values defined in dwColumnDescFlags 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.
DB_S_ERRORSOCCURRED
The column was altered, 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 following:
  • The property was not supported by the provider.
  • The property was not in the Column 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 pColumnID was a null pointer.

pColumnDesc was a null pointer.

DB_E_ALTERRESTRICTED
The provider could not alter the column because it was referenced by a constraint.
DB_E_BADCOLUMNID
The dbcid member of *pColumnDesc was invalid.
DB_E_BADPRECISION
The bPrecision member of *pColumnDesc was invalid.
DB_E_BADSCALE
The bScale member of *pColumnDesc was invalid.
DB_E_BADTYPE
One or more of the wType, pwszTypeName, or *pTypeInfo members of *pColumnDesc was invalid, or the provider was unable to change the existing column type to the new column type.
DB_E_DUPLICATECOLUMNID
The column ID specified in the dbcid member of *pColumnDesc was the same as an existing column ID.
DB_E_ERRORSOCCURRED
The column was not altered 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_NOCOLUMN
The column specified in *pColumnID does not exist in the specified table.
DB_E_NOTABLE
The specified table does not exist in the data store.
DB_E_NOTSUPPORTED
dwColumnDescFlags was an invalid value. Providers return valid values in the property DBPROP_ALTERCOLUMN.
DB_E_TABLEINUSE
The specified table was in use, and the provider could not alter the column while the table was open.
DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to alter the columns of this table.

Comments

IAlterTable::AlterColumn changes the column ID, selected column metadata (such as type, column size, precision, and scale) or OLE DB column properties. The altered column preserves all properties associated with the existing column that are not explicitly changed by properties specified in the *rgPropertySets member of the DBCOLUMNDESC structure pointed to by *pColumnDesc. If DBCOLUMNDESCFLAGS_DBCID is not set, or if it is the same as *pColumnID, the column ID is not changed. If cPropertySets is zero, no properties are changed. The method will be non-operational but will return S_OK if any one of the following conditions is satisfied:

  • if none of the DBCOLUMNDESCFLAGS bits are set
  • if only DBCOLUMNDESCFLAGS_PROPERTIES is set but cPropertySets is zero
  • if only DBCOLUMNDESCFLAGS_DBCID is set but the new column ID (dbcid) is the same as the old one

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 dwColumnDescFlags parameter.

Calls to IAlterTable::AlterColumn should be atomic. Failure should leave the column in the same state as before the call to the method.

If the column is referenced by a constraint, it is provider-specific which modifications can be made. If the provider can detect that the operation failed because of a constraint, it returns DB_E_ALTERRESTRICTED.


IAlterTable::AlterTable

Alters the ID and/or properties associated with a table.

HRESULT AlterTable(
   DBID       *pTableID,
   DBID       *pNewTableID,
   ULONG       cPropertySets,
   DBPROPSET   rgPropertySets[]);

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 is a null pointer, 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.
DB_S_ERRORSOCCURRED
The table was altered, 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 following:
  • The property was not supported by the provider.
  • The property was not in the 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 was a null pointer.

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_ALTERRESTRICTED
The provider could not alter the table because it was referenced by a constraint.
DB_E_BADTABLEID
*pNewTableID was an invalid table ID.
DB_DUPLICATETABLEID
The table specified in *pNewTableID already exists in the data store.
DB_E_ERRORSOCCURRED
The table was not altered 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 table specified in *pTableID does not exist in the data store.
DB_E_TABLEINUSE
The specified table was in use, and the provider could not alter the table while it was open.
DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to alter the table or index.
XACT_E_XTIONEXISTS
The provider supports transactional DDL, the session is participating in a transaction, and the value of DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DML.

Comments

IAlterTable::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 rgPropertySets. If pNewTableID is null or the same as pTableID, the table ID is unchanged. If cPropertySets is zero, no properties are changed. If pNewTableID is null or the same as pTableID and if cPropertySets is zero, the method makes no change to the state of the table and returns S_OK.

Calls to IAlterTable::AlterTable should be atomic. Failure should leave the table in the same state as before the call to the method.

If the table is referenced by a constraint, it is provider-specific which modifications can be made. If the provider can detect that the operation failed because of a constraint, it returns DB_E_ALTERRESTRICTED.


IBindResource

IBindResource binds to an object named by a URL and returns a data source, session, rowset, row, command, or stream object.

Note The term "resource" in the name of this interface refers to the use of URLs to name OLE DB objects.

When to Implement

IBindResource is a mandatory interface on the OLE DB root binder object and on all provider binder objects. It is also an optional interface on session and row objects.

When to Call

Method | Description
Bind | Binds to an object named by a URL.


IBindResource::Bind

Binds to an object named by a URL. When implemented on a row object, binds to a tree-structured namespace, such as a file system directory, named by the URL. Returns a data source, session, rowset, row, command, or stream object.

HRESULT Bind(
   IUnknown *            pUnkOuter,
   LPCOLESTR             pwszURL,
   DBBINDURLFLAG         dwBindURLFlags,
   REFGUID               rguid,
   REFIID                riid,
   IAuthenticate *       pAuthenticate,
   DBIMPLICITSESSION *   pImplSession,
   DBBINDURLSTATUS *     pdwBindStatus,
   IUnknown **           ppUnk
);

Parameters

pUnkOuter
[in]
If the returned object is to be aggregated, pUnkOuter is an interface pointer to the controlling IUnknown. Otherwise, it is a null pointer.

Note When IBindResource is implemented on an object other than a binder and Bind is requesting a data source or session object, pUnkOuter must be a null pointer because the data source and session objects have already been created.

pwszURL
[in]
The canonical URL of the object for which an OLE DB object is to be returned.
dwBindURLFlags
[in]
Bitmask of bind flags that control how the OLE DB object is opened.
Flag | Meaning
DBBINDURLFLAG_READ Open object for reading only.

Note At least one of DBBINDURLFLAG_READ and DBBINDURLFLAG_WRITE must be set, either by setting the appropriate bits on dwBindURLFlags or by setting the corresponding bits of DBPROP_INIT_MODE.
DBBINDURLFLAG_WRITE | Open object for writing only. See note above for DBBINDURLFLAG_READ.
DBBINDURLFLAG_READWRITE | Open object for reading and writing. See note above for DBBINDURLFLAG_READ.
DBBINDURLFLAG_SHARE_DENY_READ Deny others read access.

Note Not all providers support all types of DENY semantics.

If the provider does not support this type of DENY semantics, the bind will succeed but return DB_S_ERRORSOCCURRED and a status of DBBINDURLSTATUS_S_DENYTYPENOTSUPPORTED.

If the provider does not support any type of DENY semantics, the bind will succeed but return DB_S_ERRORSOCCURRED and a status of DBBINDURLSTATUS_S_DENYNOTSUPPORTED.
DBBINDURLFLAG_SHARE_DENY_WRITE | Deny others write access. See the notes for SHARE_DENY_READ for information about the behavior of providers that do not support this type of DENY semantics.
DBBINDURLFLAG_SHARE_EXCLUSIVE | Deny others read and write access. See the notes for SHARE_DENY_READ for information about the behavior of providers that do not support this type of DENY semantics.
DBBINDURLFLAG_SHARE_DENY_NONE | Do not deny others read or write access (equivalent to the omission of SHARE_DENY_READ and SHARE_DENY_WRITE).
DBBINDURLFLAG_RECURSIVE Modifies DBBINDURLFLAG_SHARE_*.

Propagate sharing restrictions to all objects in the subtree. Has no effect when binding to leaf nodes. Specifying this flag with only DBBINDURLFLAG_SHARE_DENY_NONE (or equivalently, with no SHARE_DENY flags) will result in E_INVALIDARG.
DBBINDURLFLAG_OUTPUT | Bind to the resource's executed output rather than its source. (For example, this will retrieve the HTML generated by an ASP file rather than its source.) This flag is ignored on binds to collections.
DBBINDURLFLAG_ASYNCHRONOUS Return immediately and perform the binding on a separate thread. If DBBINDURLFLAG_WAITFORINIT is not specified, this flag affects the behavior of the IBindResource::Bind or ICreateRow::CreateRow call. If DBBINDURLFLAG_WAITFORINIT is specified, this flag affects the behavior of the IDBInitialize::Initialize call.

For more information, see the "Comments" section below.

DBBINDURLFLAG_WAITFORINIT Return an interface supported on an uninitialized object, but do not initiate the bind. Used in the following cases:
  • The object supports IDBInitialize, and the caller must use IDBInitialize::Initialize to initiate the bind.
  • The caller intends to register callback interfaces by querying from the returned interface to IConnectionPointContainer.
  • To poll for status or cancel an operation, the caller requests an IDBAsynchStatus interface.

The consumer may ask only for interfaces supported on uninitialized objects. The provider returns E_NOINTERFACE if any other interface is requested.

Note When IBindResource is implemented on an object other than a binder (for example, when it is implemented on a session or row object as IScopedOperations, which is derived from IBindResource), this flag is disallowed because the data source object is already initialized.

DBBINDURLFLAG_DELAYFETCHSTREAM | On a bind to a row, overrides the consumer's intent to immediately open the default stream. Absence of this flag is a hint to the provider to instantiate the default stream object and prefetch its contents.
DBBINDURLFLAG_DELAYFETCHCOLUMNS | On a bind to a row, this flag overrides the consumer's intent to immediately access the row's columns. Absence of this flag is a hint to the provider to download or prefetch the row's columns.

Most of the options specified by these flags can also be specified by setting DBPROP_INIT_MODE or DBPROP_INIT_BINDFLAGS initialization properties. (See "IBindResource::Bind Flags and Initialization Property Flags" below.) If any flags are specified in dwBindURLFlags (that is, dwBindURLFlags does not equal zero), this set of flags overrides any flags that are specified in DBPROP_INIT_MODE or DBPROP_INIT_BINDFLAGS. If dwBindURLFlags is zero, the flags specified in DBPROP_INIT_MODE and DBPROP_INIT_BINDFLAGS are used instead.

If dwBindURLFlags is zero and DBPROP_INIT_MODE is not explicitly set on the bind, the provider returns E_INVALIDARG because one of DBBINDURLFLAG_READ or DBBINDURLFLAG_WRITE was not set. If dwBindURLFlags is zero and DBPROP_INIT_BINDFLAGS is not explicitly set, DBPROP_INIT_BINDFLAGS defaults to zero (no options set).

Note The sets of flags that can be specified in DBPROP_INIT_MODE and DBPROP_INIT_BINDFLAGS are deliberately disjoint so that flags from both sets can be used in the same call to IBindResource::Bind. Nevertheless, the division of flags between the two properties specified below in "IBindResource::Bind Flags and Initialization Property Flags" must be honored. If the client specifies flags from DBPROP_INIT_MODE in the DBPROP_INIT_BINDFLAGS property, or vice versa, they will be ignored.

Not all bind flags may be used with all object types (as specified in the rguid parameter). The following table describes the restrictions. If a client does not adhere to these restrictions, the bind will fail with E_INVALIDARG.

Object type | Restrictions
DBGUID_DSO Only the following flags may be used:
DBBINDURLFLAG_ASYNCHRONOUS
DBBINDURLFLAG_READ
DBBINDURLFLAG_WAITFORINIT
DBGUID_SESSION Only the following flag may be used:
DBBINDURLFLAG_READ
DBGUID_COMMAND Only the following flags may be used:
DBBINDURLFLAG_READ
DBBINDURLFLAG_WAITFORINIT
DBGUID_ROW | No restrictions. All flags are allowed.
DBGUID_ROWSET The following flags are disallowed:
DBBINDURLFLAG_DELAYFETCHCOLUMNS
DBBINDURLFLAG_DELAYFETCHSTREAM

A consumer may bind to a rowset object, but the behavior is provider-specific.
DBGUID_STREAM The following flags are disallowed:
DBBINDURLFLAG_DELAYFETCHCOLUMNS
DBBINDURLFLAG_DELAYFETCHSTREAM
rguid
[in]
A pointer to a GUID indicating the type of OLE DB object to be returned. The GUID must be one of the following values:

DBGUID_DSO
DBGUID_SESSION
DBGUID_COMMAND
DBGUID_ROW
DBGUID_ROWSET
DBGUID_STREAM

Note When implemented on a row object, the GUID must be set to one of the following values:

DBGUID_ROW
DBGUID_ROWSET (can be requested only if the row represents a collection)
DBGUID_STREAM

riid
[in]
Interface ID of the interface pointer to be returned.
pAuthenticate
[in]
Optional pointer to the caller's IAuthenticate interface. If supplied, it is provider-specific whether the IAuthenticate credentials are used before or after anonymous or default login credentials are used.
pImplSession
[in/out]
A pointer to a DBIMPLICITSESSION structure used to request and return aggregation information for the implicit session object. If the bind fails and pImplSession is not a null pointer, pImplSession->pSession should be set to NULL. The DBIMPLICITSESSION structure is defined as follows:
typedef struct tagDBIMPLICITSESSION {
   IUnknown   *pUnkOuter;    // [in] Controlling IUnknown
                           //  for session object
   IID *        piid,      // [in] Interface requested
                           //  on session
   IUnknown *   pSession   // [out] The returned session object.
} DBIMPLICITSESSION;
Element | Description
pUnkOuter | If the implicit session object is to be aggregated, pUnkOuter is an interface pointer to the controlling IUnknown. Otherwise, it is a null pointer.
piid | A pointer to the IID of the interface (on the implicit session) on which to return a pointer. Cannot be NULL. If pUnkOuter is not a null pointer, this IID must be IID_IUnknown.
pSession | An interface pointer to the implicit session object returned by the provider. If the session object is aggregated, this is a pointer to the IUnknown interface of the inner object.

Note IBindResource::Bind uses pImplSession only when implemented on a binder object and binding to a row, rowset, or stream object (rguid is DBGUID_ROW, DBGUID_ROWSET, or DBGUID_STREAM). When binding to a data source or session object (rguid is DBGUID_DSO or DBGUID_SESSION), the provider ignores pImplSession because no recursive backing session is created. If implemented on any other object (for example, a session or row object), the provider ignores pImplSession because the existing object already has a session context.

pdwBindStatus
[out]
A pointer to memory in which to return a bitmask containing warning status values for the requested bind flags. If pdwBindStatus is a null pointer, no status values are returned. If IBindResource::Bind returns a result other than DB_S_ERRORSOCCURRED and pdwBindStatus is not a null pointer, the provider returns DBBINDURLSTATUS_S_OK. The values described in the following table may be returned.
Status value | Description
DBBINDURLSTATUS_S_
DENYNOTSUPPORTED 		The bind succeeded, but the provider does not support any DENY semantics. No lock was obtained. If the consumer requires the lock, it should release the object and report that a lock could not be obtained.
DBBINDURLSTATUS_S_
DENYTYPENOTSUPPORTED 		The bind succeeded, but the provider does not support the requested kind of DENY semantics. For example, the provider may support only DENY_WRITE or DENY_EXCLUSIVE. No lock was obtained. If the consumer requires the lock type, it should release the object and report that the type of lock could not be obtained.
DBBINDURLSTATUS_S_REDIRECTED | The bind was successful, but the server for the object indicated that its URL has changed. The client should query the RESOURCE_ABSOLUTEPARSENAME column on the returned object to determine the new URL. This column is available on the resource rowset, so this status should be used only by document source providers. (DBPROP_DATASOURCE_TYPE is DBPROPVAL_DST_DOCSOURCE.)
DBBINDURLSTATUS_S_OK | The bind was successful and the status is reported as OK, because the provider returned a result other than DB_S_ERRORSOCCURRED and pdwBindStatus was not a null pointer.
ppUnk
[out]
A pointer to memory in which to return an interface pointer on the requested object. If IBindResource::Bind fails, *ppUnk is set to a null pointer.

Return Code

S_OK
The bind succeeded.
DB_S_ASYNCHRONOUS
The call to IBindResource::Bind has initiated asynchronous binding to the data source, rowset, or row. 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, rowset, row, or stream object remains in an uninitialized state.

DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED.

DB_S_ERRORSOCCURRED
The bind succeeded, but some bind flags or properties were not satisfied. The consumer should examine the bind status reported in *pdwBindStatus.

DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED. Once rowset population is complete, the consumer can see DB_S_ERRORSOCCURRED either by calling IDBAsynchStatus::GetStatus or by receiving IDBAsynchNotify::OnStop.

DB_E_ASYNCNOTSUPPORTED
The provider does not support asynchronous binding.
DB_E_CANNOTCONNECT
The provider could not connect to the server for this object.
DB_E_NOAGGREGATION
pUnkOuter was not a null pointer, and the object being created does not support aggregation.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

pImplSession was not a null pointer, pImplSession->pUnkOuter was not a null pointer, and pImplSession->piid did not point to IID_IUnknown.

pImplSession was not a null pointer, pImplSession->pUnkOuter was not a null pointer, and the object being created does not support aggregation.

IBindResource was implemented on an object other than a binder, rguid was DBGUID_DSO or DBGUID_SESSION, and pUnkOuter was not a null pointer.

DB_E_NOTCOLLECTION
The client requested an object type that is valid only for a collection (such as a rowset), but the resource at the specified URL is not a collection.
DB_E_NOTFOUND
The provider was unable to locate an object named by the specified URL.
DB_E_NOTSUPPORTED
The provider supports direct binding to OLE DB objects but does not support the object type requested in rguid.
DB_E_OBJECTMISMATCH
The object named by rguid does not represent the resource named by pwszURL.
DB_E_OBJECTOPEN
The provider would have to open a new connection to support this operation, and DBPROP_MULTIPLECONNECTIONS is set to VARIANT_FALSE.
DB_E_READONLY
The caller requested write access to a read-only object.
DB_E_RESOURCELOCKED
One or more other processes using the DBBINDURLFLAG_SHARE_* flag have locked the OLE DB object represented by this URL. If a provider supports extended error information, IErrorInfo::GetDescription returns a string consisting of user names separated by semicolons. These are the names of the users who have the object locked.
DB_E_RESOURCEOUTOFSCOPE
IBindResource is implemented on a session object, and the caller tried to bind to a URL that is not within the scope of this session.
DB_E_TIMEOUT
The attempt to bind to the object timed out.
DB_SEC_E_PERMISSIONDENIED
The caller's authentication context does not permit access to the object.
DB_SEC_E_SAFEMODE_DENIED
The provider was called within a safe mode or context, and pwszURL specified an unsafe URL.
REGDB_E_CLASSNOTREG
The root binder was unable to instantiate the provider binder.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pwszURL or ppUnk was null.

pImplSession was not a null pointer, and pImplSession->piid was null.

dwBindURLFlags contained a flag labeled "Modifies DBBINDURLFLAG_*" in the table above but did not contain the flag to be modified.

The provider does not support one or more values of dwBindURLFlags.

One of more of the bind flags in dwBindURLFlags are either not supported by the provider or disallowed for the object type denoted by rguid. For flags allowed for each object type, see the table in the reference entry for IBindResource::Bind.

E_NOINTERFACE
The object did not support the interface specified in riid, or riid was IID_NULL.

The provider binder did not implement IBindResource.

E_UNEXPECTED
IBindResource::Bind is implemented on a row object; ITransaction::Commit or ITransaction::Abort was called; and the object is in a zombie state.

Comments

If the caller binds to a URL in the namespace serviced by a provider but sets rguid to DBGUID_DSO or DBGUID_SESSION, no row or rowset is instantiated or bound. Even if the URL does not name an existing resource, the provider instantiates its data source or session object if needed, sets **ppUnk to the requested interface on the data source or session object, and returns S_OK.

The caller must bind again and set rguid to DBGUID_ROW or DBGUID_ROWSET to obtain the row or rowset, respectively. When binding to a data source object, the returned data source object is initialized using properties as described in "OLE DB Properties and Binding" below. When implemented on the session object, the consumer may bind to the data source or session object, but the existing data source or session object is returned, and the provider ignores pImplSession.

The scope of the session object associated with the object obtained in this call to IBindResource::Bind is defined by the URL that was requested in the bind call. For more information, see "Direct Binding" in Chapter 8: Binder Objects and Direct Binding.

Note "Provider Binder Objects" in Chapter 8 discusses case-sensitivity issues for URL-to-provider mapping.

If the caller binds to a stream (rguid is DBGUID_STREAM), no implicit row is created. To obtain the row object in order to access other columns besides the default stream, the caller must bind again and request the row.

Updatability of the row obtained by this method (whether or not the row exposes IRowChange) depends on the bits of dwBindURLFlags.

To execute a script and open the results of the script, the results must be opened read-only because the contents cannot be changed. If the results are opened for write access, the Bind call will fail with DB_E_READONLY.

When IBindResource is implemented on a provider binder object and a data source object is requested (rguid is DBGUID_DSO), the provider returns an initialized data source unless the DBBINDURLFLAG_WAITFORINIT is set. Providers are required to support DBBINDURLFLAG_WAITFORINIT as a way to obtain an uninitialized data source.

Bind operations can be performed asynchronously. For more information on asynchronous processing in OLE DB, see Chapter 17: Asynchronous Processing.

To provide information to authenticate the user, the consumer may also support and register the IAuthenticate interface via pAuthenticate. If the client registers an IAuthenticate pointer, it will be called if access to the requested resource is denied. The initial attempt to access the resource uses anonymous credentials or the default logged-in credentials. For more information, see IAuthenticate in the MSDN Library documentation.

The caller should convert noncanonical URLs to canonical form before invoking this method. Providers must return URLs in canonical form. This can be done by calling the InternetCanonicalizeUrl Win32® Internet API function. For more information, see the Win32 Internet API function InternetCanonicalizeUrl in the Site Builder Network (SBN) Workshop documentation at http://msdn.microsoft.com/workshop/networking/default.asp.

When supporting direct binding to commands, providers are free to return a command in one of the following states:

  • Initial—A command on which no command text has been set, no properties have been set, and which is unprepared.
  • Unprepared—If the provider is able to determine a specific command syntax from the URL (by mapping a URL to an instance, or by encoding the command within the URL), a command on which the command text has been set.

The consumer can infer the state of the command object by calling IColumnsInfo::GetColumnInfo and evaluating the return code. (see "Command States" in Chapter 3: Commands.)

The consumer can check the command text by using ICommandText::GetCommandText.

OLE DB Properties and Binding

The IBindResource interface allows specification of the most common binding options. Additional binding options may be specified by setting OLE DB properties on an IDBBinderProperties pointer associated with the object implementing IBindResource. If a provider does not support a binding option, the provider returns an error on the call to IBindResource::Bind, whether the binding option was set on properties via IDBBinderProperties or was specified as a bind flag on the call to Bind. The root binder does not raise an error when a property is set via IDBBinderProperties.

Two new initialization properties, DBPROP_INIT_LOCKOWNER and DBPROP_INIT_BINDFLAGS, have been added to cover binding semantics that were introduced in OLE DB 2.5. These properties are described in Appendix C: OLE DB Properties. In addition, some existing initialization properties have been modified to accommodate OLE DB 2.5 providers.

IBindResource::Bind Flags and Initialization Property Flags

The following table shows the mapping between flags in the IBindResource::Bind call and flags in the DBPROP_INIT_MODE and DBPROP_INIT_BINDFLAGS initialization properties.

If a consumer sets the DBPROP_INIT_MODE and/or DBPROP_INIT_BINDFLAGS properties when using IBindResource::Bind, the flag values specified in the Bind call override those specified in the properties.

DBBINDURLFLAG_ | Initialization property | DB_MODE_ or DB_BINDFLAGS_
READ | DBPROP_INIT_MODE | READ
WRITE | DBPROP_INIT_MODE | WRITE
READWRITE | DBPROP_INIT_MODE | READWRITE
SHARE_DENY_READ | DBPROP_INIT_MODE | SHARE_DENY_READ
SHARE_DENY_WRITE | DBPROP_INIT_MODE | SHARE_DENY_WRITE
SHARE_DENY_EXCLUSIVE | DBPROP_INIT_MODE | SHARE_DENY_EXCLUSIVE
SHARE_DENY_NONE | DBPROP_INIT_MODE | SHARE_DENY_NONE
RECURSIVE | DBPROP_INIT_BINDFLAGS | RECURSIVE
OUTPUT | DBPROP_INIT_BINDFLAGS | OUTPUT
ASYNCHRONOUS | N/A | N/A
WAITFORINIT | N/A | N/A
DELAYFETCHCOLUMNS | DBPROP_INIT_BINDFLAGS | DELAYFETCHCOLUMNS
DELAYFETCHSTREAM | DBPROP_INIT_BINDFLAGS | DELAYFETCHSTREAM


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 IRowset::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 IChapteredRowset::AddRefChapter. Both reference counts must be decremented by calling IChapteredRowset::ReleaseChapter.

If any HROWs in the chapter have pending changes when IChapteredRowset::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,
   DBREFCOUNT *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,
   DBREFCOUNT *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 that had not yet returned, and the provider does not support reentrancy in this method.

Comments

If the chapter's reference count is reduced to zero and retrieved again later, the next 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 both on commands and on rowsets.

When to Call

IColumnsInfo::GetColumnInfo returns the most commonly used metadata: column IDs, data types, updatability, and so on. IColumnsInfo::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 (
   DBORDINAL     *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 IColumnsInfo::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, 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_NOTABLE
ICommandPrepare was not implemented, and the specific table or view does not exist in the data store.
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 that had not yet returned, and the provider does not support reentrancy in this method.
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.

IColumnsInfo::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. Rowset column ordinals start at 1 and are numbered sequentially in increasing order (for example, 1, 2, 3, and so on). When a row object is derived from a rowset, the row-specific column ordinals must come after the rowset column ordinals.

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 IColumnsInfo::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).

IColumnsInfo::GetColumnInfo provides a quick alternative to IColumnsRowset::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 IColumnsInfo::GetColumnInfo on a command before the command is executed can be an expensive operation.

DBCOLUMNINFO Structures

IColumnsInfo::GetColumnInfo returns column metadata in DBCOLUMNINFO structures.

typedef struct tagDBCOLUMNINFO {
   LPOLESTR        pwszName;
   ITypeInfo      *pTypeInfo;
   DBORDINAL       iOrdinal;
   DBCOLUMNFLAGS   dwFlags;
   DBLENGTH        ulColumnSize;
   DBTYPE          wType;
   BYTE            bPrecision;
   BYTE            bScale;
   DBID            columnid;
} DBCOLUMNINFO;

The elements of this structure are used as described in the following table.

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 in bytes, for DBTYPE_BYTES and DBTYPE_VARNUMERIC, 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 in bytes (for DBTYPE_BYTES and DBTYPE_VARNUMERIC) if the column does not have a defined length.
  • ~0 (bitwise, the value is not 0; 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; all bits are set to 1).
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_VARNUMERIC, 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; 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; 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. Each column's column ID must be unique.

The column ID of a base table should be invariant under views.

For more information, see "Column IDs" in Chapter 4: Rowsets.

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 described in the following table.

Important The enumerated values below are listed alphabetically, excepting those specific to OLE DB 2.6 (DBCOLUMNFLAGS_ISROW, DBCOLUMNFLAGS_ISROWSET, DBCOLUMNFLAGS_ISSTREAM, DBCOLUMNFLAGS_ROWSPECIFICCOLUMN), which are listed last. Earlier providers may not set these flags.

Value | Description
DBCOLUMNFLAGS_CACHEDEFERRED Set if when a deferred column is first read its value the column 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 row or rowset column contains a chapter value. Otherwise, not set.
DBCOLUMNFLAGS_ISCOLLECTION | Set if the provider supports direct URL binding and the column determines whether the row models a collection or a simple resource. For document source providers, this flag is set for the RESOURCE_ISCOLLECTION column of the resource rowset.
DBCOLUMNFLAGS_ISDEFAULTSTREAM | Set if the provider supports direct URL binding, IColumnsInfo is implemented on a row object, and the column has been designated by the provider to contain the default stream of the row object.
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 IRowset::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: BLOBs and COM Objects.
DBCOLUMNFLAGS_ISNULLABLE Set if consumer can set the column NULL or if the provider cannot determine whether or not the consumer can set the column to NULL. Otherwise, not set. A column may contain null values, even if it cannot be set to NULL.

If DBCOLUMNFLAGS_ISNULLABLE is set, DBCOLUMNFLAGS_MAYBENULL must also be set.

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_ISROWURL | Set if the provider supports direct URL binding, IColumnsInfo is implemented on a row object, and the column contains the URL that names the row object in the data store.
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 the column can contain null values or if the provider cannot guarantee that the column cannot contain null values. 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.

DBCOLUMNFLAGS_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 store 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: Rowsets.
DBCOLUMNFLAGS_SCALEISNEGATIVE | Set if the column type is DBTYPE_VARNUMERIC and bScale represents the absolute value of the negative scale of the column. This flag is used when setting data in a DBTYPE_VARNUMERIC column. For more information, refer to "Conversions Involving DBTYPE_NUMERIC or DBTYPE_DECIMAL" in Appendix A: Data Types.
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 the column in the base table may be updatable through some means, either through IRowsetChange::SetData or through some other mechanism.

Providers never set both DBCOLUMNFLAGS_WRITE and DBCOLUMNFLAGS_WRITEUNKNOWN; they are mutually exclusive. Absence of these flags means the column is read-only.
DBCOLUMNFLAGS_ISROW This enumerated value is supported only for OLE DB 2.6 providers and later.

Identifies a row-valued column.
DBCOLUMNFLAGS_ISROWSET This enumerated value is supported only for OLE DB 2.6 providers and later.

Identifies a rowset-valued column.
DBCOLUMNFLAGS_ISSTREAM This enumerated value is supported only for OLE DB 2.6 providers and later.

Identifies stream-valued columns, in addition to the default stream.
DBCOLUMNFLAGS_ROWSPECIFICCOLUMN This enumerated value is supported only for OLE DB 2.6 providers and later.

Identifies that a row column is row-specific (would be returned by GetColumnInfo on the row).

See Also

IColumnsInfo::MapColumnIDs | IColumnsRowset::GetColumnsRowset | 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 (
   DBORDINAL    cColumnIDs,
   const DBID   rgColumnIDs[],
   DBORDINAL    rgColumns[]);

Parameters

cColumnIDs
[in]
The number of column IDs to map. If cColumnIDs is 0, IColumnsInfo::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.

cColumnIDs was zero; the method succeeded but did nothing. This return code supersedes E_INVALIDARG if cColumnIDs was zero and either or both rgColumnIDs and rgColumns was a null pointer.

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_NOTABLE
ICommandPrepare was not implemented, and the specific table or view does not exist in the data store.
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 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.

IColumnsInfo::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 lifetime 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.

Rowset column ordinals start at 1 and are numbered sequentially in increasing order (for example, 1, 2, 3, and so on). When a row object is derived from a rowset, the row-specific column ordinals must come after the rowset column ordinals.

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


IColumnsInfo2 : IColumnsInfo

The IColumnsInfo2 interface allows a consumer to obtain column names or metadata for the columns in a row or rowset.

When to Implement

IColumnsInfo2 is an optional interface that providers can choose to implement on row or rowset objects.

When to Call

Consumers call IColumnsInfo2::GetRestrictedColumnInfo to obtain the column names or column metadata for a row or rowset. IColumnsInfo2::GetRestrictedColumnInfo can return an array of column names (as column IDs) to be used as input to IRowSchemaChange::DeleteColumns.

Method | Description
GetRestrictedColumnInfo | Lists column names or column metadata optionally restricted by column name prefix.
(Inherited) | All methods of IColumnsInfo.


IColumnsInfo2::GetRestrictedColumnInfo

Retrieves column names or column metadata for a row or rowset. Column names are returned in an array of column IDs (DBIDs). Column metadata is returned in an array of DBCOLUMNINFO structures.

For information about column IDs, see "Column IDs" in Chapter 4: Rowsets. For information about DBCOLUMNINFO structures, see IColumnsInfo::GetColumnInfo.

HRESULT GetRestrictedColumnInfo(
   DBORDINAL         cColumnIDMasks,
   const DBID        rgColumnIDMasks[ ],
   DWORD             dwFlags,
   DBORDINAL        *pcColumns,
   DBID            **prgColumnIDs,
   DBCOLUMNINFO    **prgColumnInfo,
   OLECHAR         **ppStringsBuffer);

Parameters

cColumnIDMasks
[in]
The count of column name masks in rgColumnIDMasks. If cColumnIDMasks is zero, IColumnsInfo2::GetRestrictedColumnInfo enumerates all columns in the row.
rgColumnIDMasks
[in]
An array of cColumnIDMasks column name masks used to restrict the set of columns enumerated. All columns whose DBID matches the elements of rgColumnIDMasks or whose names begin with the string contained in the pwszName element of any of the DBIDs in this array will be returned in the enumeration. If cColumnIDMasks is zero, rgColumnIDMasks is ignored.
dwFlags
[in]
Reserved. Must be zero.
pcColumns
[out]
A pointer to memory in which to return a count of the column names and DBCOLUMNINFO structures returned in prgColumnIDs and prgColumnInfo, respectively. If no columns are described or if the method terminates with an error, *pcColumns is set to zero.
prgColumnIDs
[out]
A pointer to memory in which to return an array of column IDs (DBIDs) containing the requested column names. IColumnsInfo2::GetRestrictedColumnInfo returns either all columns or the columns selected by rgColumnIDMasks. This argument duplicates the DBIDs provided in the prgColumnInfo argument but is provided for direct use with methods such as IRowSchemaChange::DeleteColumns that require an array of DBIDs. The provider allocates memory for the DBIDs. The consumer is responsible for releasing this memory with IMalloc::Free. If an error code is returned and prgColumnIDs is not a null pointer, *prgColumnIDs should be set to NULL.
prgColumnInfo
[out]
A pointer to memory in which to return an array of DBCOLUMNINFO structures containing the requested column names. IColumnsInfo2::GetRestrictedColumnInfo returns either all columns or the columns selected by rgColumnIDMasks. The provider allocates memory for the DBCOLUMNINFO structures. The consumer is responsible for releasing this memory with IMalloc::Free. If an error code is returned and prgColumnInfo is not a null pointer, *prgColumnInfo should be set to NULL.
ppStringsBuffer
[out]
A pointer to memory in which to return a pointer to storage for all string values (names used with either columnid or *pwszName of the DBCOLUMNINFO structure) within a single allocation block. If no columns are returned, ppStringsBuffer should be set to NULL. If there are any string names, ppStringsBuffer points to a buffer containing all the values of those names. The consumer is responsible for releasing this memory with IMalloc::Free. If an error code is returned and ppStringsBuffer is not a null pointer, *ppStringsBuffer should be set to NULL.

Return Code

S_OK
The requested columns were successfully enumerated.
DB_E_BADCOLUMNID
An element of rgColumnIDMasks was an invalid DBID.
DB_E_DELETEDROW
The row was deleted or moved.
DB_E_NOCOLUMN
No columns matched the mask criteria. The provider sets *pcColumns to zero and other output arguments to NULL.
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_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to retrieve the column information.
E_INVALIDARG
cColumnIDMasks was not zero, and rgColumnIDMasks was a null pointer.

pcColumns was a null pointer.

ppStringsBuffer was a null pointer.

prgColumnIDs and prgColumnInfo are both null pointers.

E_FAIL
A provider-specific error occurred.
E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the column information structures or string buffer.
E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called, and the object is in a zombie state.

Comments

If prgColumnIDs is a null pointer, the array of column names is not returned.

If prgColumnInfo is a null pointer, the array of DBCOLUMNINFO structures is not returned.

prgColumnIDs points to a filtered list of columns that can be used directly as input to methods such as IRowSchemaChange::DeleteColumns.

If a column ID mask element in rgColumnIDMasks is any GUID or PROPID kind of DBID, IColumnsInfo2::GetRestrictedColumnInfo matches all columns with that GUID and/or PROPID. For example, passing in a DBID of kind DBKIND_GUID, containing the GUID that denotes the resource rowset, will match all columns of the resource rowset (since they differ only by PROPID).

If the column ID mask element is a name kind of DBID, IColumnsInfo2::GetRestrictedColumnInfo matches any columns whose name (pwszName of the DBID) begins with the pwszName string in the column ID mask DBID. Names may be used to further qualify a GUID-based match.

For more information on the use of DBCOLUMNINFO and the string buffer, see IColumnsInfo::GetColumnInfo. For information about DBID structures, see "Column IDs" in Chapter 4: Rowsets.

The order in which the columns in the row are enumerated is implementation-dependent. Each column is enumerated only once.

DBCOLUMNINFO Structure Elements for Row Objects

Providers must implement the following elements of the DBCOLUMNINFO structure for row objects:

pwszName
Wide-character string containing the name of the column.
wType
DBTYPE of the column.
columnid
Row columns are identified using column IDs, which are DBID structures. DBIDs allow columns to be identified by GUID, name (URL), or PROPID (a ULONG). This allows providers to access and set columns by name, GUID, or number. For information about column IDs, see "Column IDs" in Chapter 4: Rowsets.

If ordinals are used to identify row columns, the following rules apply:

  • The ordinal zero is used to denote a bookmark column. It cannot be used to denote an ordinary column.
  • Ordinal values must remain constant throughout the life of a row object. This is true even if one or more row columns are deleted.
  • Rowset column ordinals start at 1 and are numbered sequentially in increasing order (for example, 1, 2, 3, and so on).
  • When a row object is derived from a rowset, the row-specific column ordinals must come after the rowset column ordinals.

    Rowset ordinals are faster to use than row column IDs. To retrieve column ordinals from column IDs, a consumer calls IColumnsInfo::MapColumnIDs.

    The columnid may specify the type of an object stored in a column when wType has one of the following values:

    DBTYPE_IUNKNOWN
    DBTYPE_ IDISPATCH
    DBTYPE_VARIANT and vt = VT_UNKNOWN or VT_DISPATCH

    When identifying an object, within the columnid DBID, the eKind element should be set to DBKIND_GUID and the guid element should identify the object type.


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 both on commands and on 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 IColumnsRowset 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 (
   DBORDINAL   *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 IColumnsRowset::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 IRowsetNofity in the consumer that had not yet returned, and the provider does not support reentrancy in this method.
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 IColumnsRowset::GetAvailableColumns on a command before the command is executed might 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,
   DBORDINAL     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, 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, 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 14: Properties.

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 IColumnsRowset::GetColumnsRowset is called on a command that does not return rows, 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_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED.

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 that was specified to be applied to all columns could not be applied to one or more columns.
  • The property was not supported by the provider.
  • The property set was not supported by the provider.
  • It was not possible to set the property.
  • The property was not in the Rowset property group.

DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED. Once rowset population is complete, the consumer can see DB_S_ERRORSOCCURRED either by calling IDBAsynchStatus::GetStatus or by receiving IDBAsynchNotify::OnStop. Of course, the properties-in-error are not available, but for any properties that could not be set, the provider should report that status in the property array passed to IColumnsRowset::GetColumnsRowset (assuming that property failures can all be determined ahead of rowset population).

DB_S_NOTSINGLETON
The provider supports returning row objects on IColumnsRowset::GetColumnsRowset, and the consumer requested a row object but the result was not a singleton. A row object of the first row of the rowset is returned if the provider supports returning the row object. Because returning this result may be expensive, providers are not required to do so. If DB_S_ERRORSOCCURRED also applies to the execution of this method, it takes precedence over DB_S_NOTSINGLETON.
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.
DB_E_NOAGGREGATION
pUnkOuter was not a null pointer, and the provider is an OLE DB 1.0 or 1.1 provider that does not support aggregation of the rowset object being created.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

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_NOTFOUND
The provider supports the return of singleton row objects on a method that typically returns a rowset, a row was requested via riid or DBPROP_IRow, and no rows existed in the rowset.
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 that had not yet returned, and the provider does not support reentrancy in this method.
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.

IColumnsRowset::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 object, 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, the order is determined by the provider—for example, 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 that are requested.

IColumnsRowset::GetColumnsRowset can be called for rowsets created by GetColumnsRowset.

Calling IColumnsRowset::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 IColumnsInfo::GetColumnInfo.

Column ID | Type indicator | Description
DBCOLUMN_IDNAME DBTYPE_WSTR Column name. This column, together with the DBCOLUMN_GUID and DBCOLUMN_PROPID columns, forms the ID of the column. One or more (but not all) 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 IColumnsRowset::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 in bytes (for DBTYPE_BYTES and DBTYPE_VARNUMERIC), 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 in bytes (for DBTYPE_BYTES and DBTYPE_VARNUMERIC), if the column does not have a defined length.
  • ~0 (bitwise, the value is not 0; 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; 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 store 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 store. 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 or if the rowset column is derived, but not identical to, a column in the data store.

The default of this column is NULL.
DBCOLUMN_BASESCHEMANAME DBTYPE_WSTR The name of the schema in the data store 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 or view in the data store 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.

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 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 whether the column has a default value or whether 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 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 in the rowset that, taken together, uniquely identify the row. The set of columns with DBCOLUMN_KEYCOLUMN set to VARIANT_TRUE must uniquely identify a row in the rowset. There is no requirement that this set of columns is a minimal set of columns. This set of columns may be generated from a base table primary key, a unique constraint or a unique index.

VARIANT_FALSE—The column is not required to uniquely identify the row.
DBCOLUMN_BASETABLEVERSION | DBTYPE_UI8 | Version number of the table in the data store 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.
DBCOLUMN_DERIVEDCOLUMNNAME DBTYPE_WSTR The name of the column in the data store from which this rowset column is derived if, for example, derived from a single-column expression or an invertible function. Mutually exclusive with DBCOLUMN_BASECOLUMNNAME.

The default of this column is NULL.

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

ICommand::Cancel may be called on a separate thread after ICommand::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, ICommand::Execute 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 be canceled only 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,
   DBROWCOUNT   *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 the command has any open rowsets, requesting an interface that is not supported on those open rowsets will generally return E_NOINTERFACE as it is not possible to change the properties supported on a command with open rowsets. In addition, specifying the IID of a nonmandatory interface that has not been explicitly requested through rowset properties set on a prepared command might reduce the provider's ability to optimize the command, because the provider might have to rebuild the access plan to satisfy the additional interface.

If this is IID_NULL, 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, ICommand::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;
   DB_UPARAMS   cParamSets;
   HACCESSOR    hAccessor;
};

The elements of this structure are used as described in the following table.

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 "Getting Data" and "Setting Data" in Chapter 6: Getting and Setting Data.

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, 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 IAccessor::CreateAccessor was 0), ICommand::Execute does not retrieve or return any parameter values.

If the provider is able to determine the number of parameters in the command and 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. However, if the DBPROP_OUTPUTSTREAM property is set and the result of ICommand::Execute is a stream object, a null pointer is an acceptable value.

If ppRowset is not a null pointer and an interface on a stream object was requested, the provider returns to *ppRowset the interface pointer set in the DBPROP_OUTPUTSTREAM property. The consumer must release the interface pointer in *ppRowset when it is no longer needed.

If *ppRowset is a null pointer, the provider did not write any results to the stream specified in DBPROP_OUTPUTSTREAM.

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_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED.
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: Getting and Setting Data.
  • The command was an SQL singleton SELECT WHERE statement, and one or more output parameters could not be set because the WHERE clause returned zero rows. In this case, the provider should set all returned columns to DBSTATUS_E_UNAVAILABLE. However, some providers cannot distinguish this case from the case where the WHERE clause returned a single row containing null values, and instead set all output parameters to NULL.
  • 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_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED. Once rowset population is complete, the consumer can see DB_S_ERRORSOCCURRED either by calling IDBAsynchStatus::GetStatus or by receiving IDBAsynchNotify::OnStop.

DB_S_ERRORSOCCURRED should be returned before DB_S_NOTSINGLETON because the status values can be checked, and providers are not required to return DB_S_NOTSINGLETON.

DB_S_NOTSINGLETON
The provider supports returning row objects on ICommand::Execute, and the consumer requested a row object but the result was not a singleton. A row object of the first row of the rowset is returned. Returning this result may be expensive, so providers are not required to do so. If DB_S_ERRORSOCCURRED also applies to the execution of this method, it takes precedence over DB_S_NOTSINGLETON.
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
riid was not IID_NULL, and ppRowset was a null pointer. Any streams used to pass input parameters are not released.

The parameter information was invalid. Parameter information may be invalid for any of the following reasons. In all cases, any streams used to pass input parameters are not released.

  • 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.
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. Any streams used to pass input parameters are not released.
DB_E_BADACCESSORTYPE
hAccessor in the DBPARAMS structure pointed to by pParams was not the handle of a parameter accessor. Any streams used to pass input parameters are not released.
DB_E_CANCELED
The command was canceled by a call to ICommand::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 nonvalid input parameter values. To determine which input parameter values were not valid, 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: Getting and Setting Data.) If any streams were used to pass input parameters, only those with the status value equal to DBSTATUS_S_OK on input are released; otherwise, no attempt is made to release the stream. If the consumer provides storage objects for additional bound parameters that are not used by the current command, it is a consumer programming error and the provider does not attempt to release these parameters.

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.

Multiple parameter sets were passed with a command that returns a rowset. (Some providers might not return this error.)

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 provider is an OLE DB 1.0 or 1.1 provider that does not support aggregation of the rowset object being created. Any streams used to pass input parameters are not released.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown. Any streams used to pass input parameters are not released.

DB_E_NOCOMMAND
No command text was currently set on the command object. Any streams used to pass input parameters are not released.
DB_E_NOTABLE
The specific table or view does not exist in the data store.
DB_E_NOTFOUND
The provider supports row objects, a row was requested via riid or DBPROP_IRow, and no rows satisfied the selection criteria of the command.
DB_E_PARAMNOTOPTIONAL
A value was not supplied for a required parameter.

The command text used parameters and pParams was a null pointer.

DB_E_PARAMUNAVAILABLE
Provider cannot derive parameter information, and ICommandWithParameters::SetParameterInfo has not been called.
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. Any streams used to pass input parameters are not released.

If a session is enlisted in a global transaction and a command requires the creation of an additional connection, the provider should return DB_E_OBJECTOPEN. Providers written prior to OLE DB 2.6 may return E_FAIL.

Comments

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. In all cases, any streams used to pass input parameters are not released.

E_NOINTERFACE
DB_E_BADBINDINFO
DB_E_BADORDINAL
DB_E_BADSTORAGEFLAGS
DB_E_UNSUPPORTEDCONVERSION

If the command is a row-returning command, 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 is a non-row-returning command, such as an SQL INSERT statement, *ppRowset is set to NULL and no rowset is returned.

If the command returns multiple results (row counts, rowset objects, or row objects), the consumer requests a multiple-results object by setting riid to IID_IMultipleResults. ICommand::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: Commands.

Rowsets returned by IMultipleResults::GetResult have the properties set as defined by the command that created the multiple results object. These properties are identical for each result set. There is no way to set different properties for results of a multiple result.

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.

When populating output parameters and the command produces a nonsingleton result, the provider returns S_OK and populates the output parameters with values from the first row of the result. If the underlying command processor does not support nonsingleton results with output parameters, the provider returns a provider-specific error.

If ICommand::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.

When executing a command whose command text is a sequence of subcommands and also requesting a multiple results object, the provider must ensure that subcommands are executed in the order they appear in the command text and that any command whose results have been retrieved via IMultipleResults::GetResult has been executed.

It is provider-specific whether each of the subcommands is executed at ICommand::Execute or just-in-time for IMultipleResults::GetResult, and it is also provider-specific whether subcommands whose results have not been obtained have been executed.

ICommand::Execute can be called when a rowset is already open on the command 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 ICommand::Execute creates a new rowset, which must be explicitly released by IUnknown::Release.

ICommand::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, ICommand::Execute ignores pParams. However, if the command text includes parameters, ICommand::Execute returns DB_E_ERRORSINCOMMAND.

Consumers should not assume providers will derive parameter information. Use ICommandWithParameters::SetParameterInfo to explictly set or ICommandWithParameters::GetParameterInfo to explictly derive parameter information.

If an input parameter value is not specified, ICommand::Execute returns DB_E_PARAMNOTOPTIONAL. If the provider cannot describe parameters and the consumer has not called ICommandWithParameters::SetParameterInfo for all parameters, the behavior of ICommand::Execute is undefined. For example, ICommand::Execute might guess at the parameter information or it might fail completely. For more information, see SetParameterInfo.

If ICommand::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 Appendix C: OLE DB Properties.

ICommand::Execute does not alter the value of any properties. That is, ICommandProperties::GetProperties returns the same value for a property whether or not it is called before or after Execute and whether or not 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.

ICommand::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.

Except where otherwise indicated, the provider frees all streams used to pass input parameters.

ICommand::Execute operates on the last command specified by either ICommandText::SetCommandText or ICommandStream::SetCommandStream. If Execute is called again, the same command is re-executed even if the command is contained in a stream whose contents are normally inaccessible after having been read once.

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 ICommand::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.

Comments

If the provider is an OLE DB 2.5 or later provider that implements IDBCreateCommand on the row object to support scoped commands, calling ICommand::GetDBSession on a row-scoped command returns an interface pointer on the row object's underlying session object.


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, property settings, or parameter information associated with the command object.

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 | 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.
E_INVALIDARG
pCommandID was a null pointer.


ICommandPersist::GetCurrentCommand

Returns the DBID of the current command.

HRESULT GetCurrentCommand(
   DBID **ppCommandID);

Parameters

ppCommandID
[out]
A pointer to memory in which to return the pointer to 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.
E_INVALIDARG
ppCommandID was a null pointer.


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.
E_INVALIDARG
pCommandID was a null pointer, or dwFlags was not zero.

Comments

ICommandPersist::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 and specified parameter information are not affected by loading a persisted command definition. ICommandPersist::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 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.

DBCOMMANDPERSISTFLAG_PERSISTVIEW
The command is to be persisted as a view. Views are row-returning objects that do not contain parameters or return values and can generally be used anywhere a base table is used. Views can be enumerated through the DBSCHEMA_VIEWS schema rowset.

DBCOMMANDPERSISTFLAG_PERSISTPROCEDURE
The command is to be persisted as a procedure. Procedures may or may not return rows and may or may not contain parameters or return values. Procedures can be enumerated through the DBSCHEMA_PROCEDURES schema rowset.

DBCOMMANDPERSISTFLAG_PERSISTVIEW and DBCOMMANDPERSISTFLAG_PERSISTPROCEDURE are mutually exclusive. If neither is specified, the provider persists the command as a default type.

Return Code

S_OK
The command was successfully saved.
DB_E_BADCOMMANDID
The DBID specified in *pCommandID was invalid.
DB_E_BADCOMMANDFLAGS
dwFlags was invalid.

In dwFlags, both DBCOMMANDPERSISTFLAG_PERSISTVIEW and DBCOMMANDPERSISTFLAG_PERSISTPROCEDURE were specified.

DBCOMMANDPERSISTFLAG_PERSISTVIEW was specified, and the command could not be persisted as a view.

DBCOMMANDPERSISTFLAG_PERSISTPROCEDURE was specified, and the command could not be persisted as a procedure.

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.
E_INVALIDARG
pCommandID was a null pointer, or dwFlags was not valid.

Comments

ICommandPersist::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 ICommandPrepare::Prepare, will be used—that is, how often the command is likely to be executed without renewed optimization. This guides the optimizer in determining trade-offs 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_NOTABLE
The specific table or view does not exist in the data store.
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 ICommandPrepare::Prepare because these properties might be relevant to preparing the command.

If ICommandPrepare::Prepare is called redundantly, the provider determines whether command optimization is reinvoked; the provider returns S_OK.

If ICommandPrepare::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 Appendix C: OLE DB Properties."

When a call to ICommandProperties::SetProperties follows a call to ICommandPrepare::Prepare, the command should remain prepared even if the provider must re-prepare the command.

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 if 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 cPropertyIDSets 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, no 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, 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 14: Properties.

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, 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, 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. Therefore, 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 14: Properties.

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. ICommandProperties::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

ICommandProperties::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, ICommandProperties::GetProperties returns the initial property value.

Even though IDBProperties::GetPropertyInfo lists a property as being supported by the provider, ICommandProperties::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 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 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 ICommandProperties::GetProperties with the DBPROPSET_PROPERTIESINERROR property set to return all the properties that were in error. For more information, see "Property Sets" in Appendix C: OLE DB Properties.

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, the 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 14: Properties.

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. ICommandProperties::SetProperties can fail to set properties for a number of reasons, including the following:
  • 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.
  • The property was not supported by the provider.
  • The property set was not supported by the provider.
  • It was not possible to set the property.
  • The property is read-only and was not set to its default value.
  • The value of dwOptions in a DBPROP structure was invalid.
  • The property was not in the Rowset property group.
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.
DB_E_OBJECTOPEN
Properties cannot be set while there is an open rowset.

Comments

When a call to ICommandProperties::SetProperties follows a call to ICommandPrepare::Prepare, the command should remain prepared even if the provider must re-prepare the command.

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 ICommandProperties::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 ICommandProperties::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.

ICommandProperties::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, ICommandProperties::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 ICommandProperties::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 ICommandProperties::SetProperties returns DBPROPSTATUS_NOTSETTABLE for them.

Even if IDBProperties::GetPropertyInfo lists a property as being supported by the provider, ICommandProperties::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, ICommandProperties::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 re-prepare the command at execution.

Generally, consumers should not set the same property twice, because if they do, the value of the property is provider-specific, meaning it is unknown to the consumer. The status for the successfully set value should be DBPROPSTATUS_OK, but it is provider-specific what the status value is for the other property values.

See Also

ICommandProperties::GetProperties | IDBProperties::GetPropertyInfo | IRowsetInfo::GetProperties


ICommandStream

ICommandStream, while similar to ICommandText, passes a command as a stream object rather than as a string. ICommandStream is not a mandatory interface.

When to Implement

ICommandStream should be implemented to facilitate commands as streams. Unlike ICommandText, which expects the command to be in Unicode format, ICommandStream can accept any provider-specific encoding.

Method | Description
GetCommandStream | Returns the command stream object set by the last call to ICommandStream::SetCommandStream.
SetCommandStream | Sets the command stream object, replacing the existing stream object.


ICommandStream::GetCommandStream

Returns the currently set interface pointer of a stream object containing a command.

HRESULT GetCommandStream (
   IID         *piid,
   GUID        *pguidDialect,
   IUnknown   **ppCommandStream);

Parameters

piid
[out]

A pointer to memory where the provider returns an IID that specifies the type of object referenced by *ppCommandStream.

If piid is a null pointer on input, the provider does not return the IID.

If this method returns an error, *piid is set to IID_NULL.

pguidDialect
[in/out]

A pointer to memory containing a GUID that specifies the syntax and general rules for parsing the command stream—that is, the dialect that will be used by the provider to interpret the statement. This is usually the dialect specified in ICommandStream::SetCommandStream.

However, if DBGUID_DEFAULT is specified in SetCommandStream, the provider returns the particular dialect that it will use to interpret the statement. If the provider must choose 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 might be different from the command set in ICommandStream::SetCommandStream, it returns DBGUID_DEFAULT. Providers can define GUIDs for their own dialects.

If ICommandStream::GetCommandStream returns an error, *pguidDialect is set to DB_NULLGUID.

If pguidDialect is a null pointer on input, the provider does not return the dialect.

ppCommandStream
[out]

A pointer to memory where the command stream interface pointer is returned. If ICommandStream::GetCommandStream returns an error, *ppCommandStream 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 ICommandStream::SetCommandStream or in a provider-specific dialect. The value returned in *pguidDialect represents that dialect.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
ppCommandStream was a null pointer.
E_OUTOFMEMORY
The provider was unable to allocate sufficient memory to return the command stream.
DB_E_NOCOMMAND
No command was currently set on the command object.
DB_E_CANTTRANSLATE
The last command was set using ICommandText::SetCommandText, not ICommandStream::SetCommandStream.

See Also

ICommand::Execute | ICommandStream::SetCommandStream | ICommandText::GetCommandText


ICommandStream::SetCommandStream

Sets the interface pointer of a stream object containing a command.

HRESULT SetCommandStream (
   REFIID      riid,
   REFGUID     rguidDialect,
   IUnknown   *pCommandStream);

Parameters

riid
[in]

Specifies the type of stream object representing the command.

rguidDialect
[in]

Specifies the dialect of the command.

If this parameter is set to DBGUID_DEFAULT, the provider uses its default dialect.

pCommandStream
[in]

Pointer to a stream object of a type specified by riid.

If pCommandStream is a null pointer, the specification of the current command stream object is cleared and the command is put in an initial state.

Return Code

S_OK
The method succeeded.
E_FAIL
A provider-specific error occurred.
E_NOINTERFACE
One of the following occurred:
  • The type of interface indicated by riid was not supported by the provider.
  • The riid specified did not match the type of stream object passed in *pCommandStream.
DB_E_DIALECTNOTSUPPORTED
The provider did not support the dialect specified in rguidDialect.
DB_E_OBJECTOPEN
The output stream returned by a previous call to ICommand::Execute is still open. Release that stream before calling this method.

Comments

ICommand::Execute uses the last command set by either ICommandStream::SetCommandStream or ICommandText::SetCommandText.

If the command is contained in a stream, ICommand::Execute uses the stream object specified at the time SetCommandStream was called, even if that stream object is subsequently modified by other means.

A provider may or may not support the same set of dialects (rguidDialect) on ICommandStream::SetCommandStream as are supported on ICommandText::SetCommandText.

See Also

ICommand::Execute | ICommandStream::GetCommandStream | ICommandText::SetCommandText


ICommandText : ICommand

This interface is mandatory on commands.

A command object can have only one text command. When the command text is specified through ICommandText::SetCommandText, it replaces the existing command text.

Method | Description
GetCommandText | Returns the text command set by the last call to ICommandText::SetCommandText.
SetCommandText | Sets the command text, replacing the existing command text.
(Inherited) | All methods of ICommand.


ICommandText::GetCommandText

Returns the text command set by the last call to ICommandText::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 usually the dialect specified in ICommandText::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 must choose 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 ICommandText::SetCommandText, it returns DBGUID_DEFAULT. Providers can define GUIDs for their own dialects.

If pguidDialect is a null pointer on input, the provider does not return the dialect.

If ICommandText::GetCommandText returns an error, *pguidDialect is set to DB_NULLGUID.

For more information about dialects, see "Using Commands" in Chapter 3: Commands.

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 ICommandText::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 ICommandText::SetCommandText or in 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_CANTTRANSLATE
The last command was set using ICommandStream::SetCommandStream, not ICommandText::SetCommandText.
DB_E_NOCOMMAND
No command text was currently set on the command object.

Comments

The GUID returned in ICommandText::GetCommandText is the dialect that will be used by the provider to interpret the statement. This is usually the dialect specified in ICommandText::SetCommandText.

See Also

ICommandText::SetCommandText


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 (and command stream object, if it exists) is cleared and the command is put in an initial 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::GetColumnInfo, 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.
DB_SEC_E_SAFEMODE_DENIED
The provider was called within a safe mode or context, and the command text specified a FROM SCOPE= clause with an unsafe URL.

Comments

A command object contains a single text command, usually an SQL statement. The new command text is copied into the command object; therefore, 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. ICommandText::SetCommandText verifies only 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 ICommandText::SetCommandText, the command object is left in an unprepared state.

ICommandText::SetCommandText does not alter the value of any properties. That is, ICommandProperties::GetProperties returns the same value for a property whether or not it is called before or after ICommandText::SetCommandText and whether or not SetCommandText succeeded or failed. Furthermore, setting the command text does not reset parameter information set through ICommandWithParameters::SetParameterInfo.

See Also

ICommand::Execute | ICommandPrepare::Prepare | ICommandText::GetCommandText | ICommandWithParameters::SetParameterInfo | ICommandStream::SetCommandStream


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

OLE DB Programmer's Reference

ICommandWithParameters::GetParameterInfo

Gets a list of the command’s parameters, their names, and their types.

HRESULT GetParameterInfo (
   DB_UPARAMS      *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;
   DBORDINAL      iOrdinal;
   LPOLESTR       pwszName;
   ITypeInfo     *pTypeInfo;
   DBLENGTH       ulParamSize;
   DBTYPE         wType;
   BYTE           bPrecision;
   BYTE           bScale;
} DBPARAMINFO;

The elements of this structure are used as described in the following table.

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 store.
  • 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 ICommandWithParameters::SetParameterInfo so that 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 can 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 ICommandWithParameters::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: BLOBs and COM Objects.

  • DBPARAMFLAGS_SCALEISNEGATIVE—Set if the parameter type is DBTYPE_VARNUMERIC and bScale represents the absolute value of the negative scale of the parameter. This flag is used when setting data in a DBTYPE_VARNUMERIC parameter. For more information, refer to "Conversions Involving DBTYPE_NUMERIC or DBTYPE_DECIMAL" in Appendix A.
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 in bytes (for DBTYPE_BYTES and DBTYPE_VARNUMERIC), 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 in bytes (for DBTYPE_BYTES and DBTYPE_VARNUMERIC), if the parameter does not have a defined length.
  • ~0 (bitwise, the value is not 0; 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; all bits are set to 1).
wType | The indicator of the parameter's data type, or a type from which the data can be converted for the parameter if the provider cannot determine the exact data type of the parameter.
bPrecision If wType is a numeric type or DBTYPE_DBTIMESTAMP, bPrecision is the maximum number of digits, expressed in base 10. Otherwise, this is ~0 (bitwise, the value is not 0; all bits are set to 1).

Note Some 1.x or 2.x providers, for whom the precision is fixed for DBTYPE_DBTIMESTAMP, may return ~0 for the precision of DBTYPE_DBTIMESTAMP parameters.
bScale If wType is a numeric type with a fixed scale or if wType is DBTYPE_DBTIMESTAMP, 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; all bits are set to 1).

Note Some 1.x or 2.x providers, for whom the scale is fixed for DBTYPE_DBTIMESTAMP, may return ~0 for the scale of DBTYPE_DBTIMESTAMP parameters.
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. However, no command text was currently set on the command object and no parameter information had been specified with ICommandWithParameters::SetParameterInfo.

Note Some 2.1 or earlier providers that support command preparation may return DB_E_NOTPREPARED when the command text has not been set.

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 ICommandWithParameters::SetParameterInfo.
DB_E_PARAMUNAVAILABLE
The provider cannot derive parameter information from the command, and ICommandWithParameters::SetParameterInfo has not been called.

Comments

This method makes no logical change to the state of the object.

If ICommandWithParameters::SetParameterInfo has not been called for any parameters or ICommandWithParameters::SetParameterInfo has been called with cParams equal to zero, ICommandWithParameters::GetParameterInfo returns information about the parameters only if the provider can derive parameter information. If the provider cannot derive parameter information, ICommandWithParameters::GetParameterInfo returns DB_E_PARAMUNAVAILABLE.

If ICommandWithParameters::SetParameterInfo has been called for at least one parameter, ICommandWithParameters::GetParameterInfo returns the parameter information only for those parameters for which SetParameterInfo has been 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. It is provider-specific whether or not the provider also returns derived information about the parameters for which ICommandWithParameters::SetParameterInfo was not called. For performance reasons, even providers that can derive this additional parameter information will usually return only the information that was specified when ICommandWithParameters::SetParameterInfo was called.

Note Providers are permitted to overwrite parameter information set by the consumer with the actual parameter information for the statement or stored procedure. However, they are not encouraged to do so if such validation requires a round-trip to the server. Consumers must not rely on providers to validate parameter information in this manner.

See Also

ICommandWithParameters::MapParameterNames | ICommandWithParameters::SetParameterInfo


ICommandWithParameters:: MapParameterNames

OLE DB Programmer's Reference

ICommandWithParameters::MapParameterNames

Returns an array of parameter ordinals when given named parameters.

HRESULT MapParameterNames (
   DB_UPARAMS        cParamNames,
   const OLECHAR    *rgParamNames[],
   DB_LPARAMS        rgParamOrdinals[]);

Parameters

cParamNames
[in]
The number of parameter names to map. If cParamNames is zero, ICommandWithParameters::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.

cParamNames was zero; the method succeeded but did nothing. This return code supersedes E_INVALIDARG if cParamNames was zero and either or both rgParamNames and rgParamOrdinals was a null pointer.

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 ICommandWithParameters::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 ICommandWithParameters::SetParameterInfo.

See Also

ICommandWithParameters::GetParameterInfo


ICommandWithParameters:: SetParameterInfo

OLE DB Programmer's Reference

ICommandWithParameters::SetParameterInfo

Specifies the native data type of each parameter.

HRESULT SetParameterInfo (
   DB_UPARAMS              cParams,
   const DB_UPARAMS        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, 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;
   DBLENGTH       ulParamSize;
   DBPARAMFLAGS   dwFlags;
   BYTE           bPrecision;
   BYTE           bScale;
} DBPARAMBINDINFO;

The elements of this structure are used as described in the following table.

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 ICommandWithParameters::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" below.

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 in bytes (for DBTYPE_BYTES and DBTYPE_VARNUMERIC), 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 in bytes (for DBTYPE_BYTES and DBTYPE_VARNUMERIC), if the parameter does not have a defined length.
  • ~0 (bitwise, the value is not 0; 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; 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 DBTYPE_DBTIMESTAMP or 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 or if wType is DBTYPE_DBTIMESTAMP, 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 ICommandWithParameters::SetParameterInfo was called. The parameter type information specified in ICommandWithParameters::SetParameterInfo was used.

ICommandWithParameters::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_BADORDINAL
An element of rgParamOrdinals 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 (but not all) elements of rgParamBindInfo, 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

This method does not set type information for any parameter if it does not successfully set type information for every parameter. Otherwise, this method sets information for all parameters.

Providers generally derive parameter type information from the data store and return it to the consumer through ICommandWithParameters::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 store. For these providers, the consumer must supply the native parameter type information through ICommandWithParameters::SetParameterInfo. The provider uses the type information specified by ICommandWithParameters::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 store. 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 ICommandWithParameters::SetParameterInfo, the provider converts the data to a string before sending it to the data store. Because the data store is expecting a LONG, this will likely result in an error.

When the consumer calls ICommandWithParameters::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".

When the consumer specifies a storage object of type DBTYPE_IUNKNOWN for a parameter, the consumer should call ICommandWithParameters::SetParameterInfo to designate a standard type name for the parameter that corresponds to the contents of the storage object (for example, "DBTYPE_WCHAR" for a Unicode stream). This standard type name must match the parameter type expected by the data store; the consumer should not specify a standard type name different from the data store type and expect the provider to perform a conversion. For maximum interoperability, providers are encouraged to accept the OLE DB DBTYPEs as synonyms for the standard type names.

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
"DBTYPE_FILETIME" | DBTYPE_FILETIME
"DBTYPE_VARNUMERIC" | DBTYPE_VARNUMERIC
"DBTYPE_PROPVARIANT" | DBTYPE_PROPVARIANT

After the consumer calls ICommandWithParameters::SetParameterInfo to specify the parameter type information, it can call ICommandWithParameters::GetParameterInfo to retrieve the type indicator for each parameter. These values—which are based on the information specified in ICommandWithParameters::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 ICommandWithParameters::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.

Calling SetParameterInfo with (0, NULL, NULL) discards existing parameter information and should return S_OK. However, if DB_S_TYPEINFOOVERRIDDEN is returned instead, consumers should query for verification that the method succeeded.

See Also

ICommandWithParameters::GetParameterInfo


IConvertType

This interface is mandatory on commands, rowsets, index rowsets, and rows.

This interface contains a single method that gives information about the availability of type conversions on a command, a rowset, or a row.

Method | Description
CanConvert | Gives information about the availability of type conversions on a command, a rowset, or a row.


IConvertType::CanConvert

Gives information about the availability of type conversions on a command, a rowset, or a row.

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 IConvertType::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 | IConvertType::CanConvert is to determine whether the conversion is supported when setting, getting, finding, filtering, or seeking on columns of the rowset or row. This flag is mutually exclusive with DBCONVERTFLAGS_PARAMETER.
DBCONVERTFLAGS_ISFIXEDLENGTH | IConvertType::CanConvert is to determine whether 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 supported only by OLE DB version 2.0 or later providers.
DBCONVERTFLAGS_ISLONG | IConvertType::CanConvert is to determine whether the long version of the DBTYPE specified in the dwFromType can be converted to the DBTYPE specified in wToType. This flag is valid only when used in conjunction with a variable-length DBTYPE. This flag can coexist with either DBCONVERTFLAGS_COLUMN or DBCONVERTFLAGS_PARAMETER. This flag is supported only by OLE DB version 2.0 or later providers.
DBCONVERTFLAGS_PARAMETER | IConvertType::CanConvert is to determine whether the conversion is supported on the parameters of the command.
DBCONVERTFLAGS_FROMVARIANT | The DBTYPE specified in the wFromType represents a type within a VARIANT. IConvertType::CanConvert returns whether the conversion from a VARIANT of that type to the type specified in wToType is allowed. For providers that support PROPVARIANT, this applies to conversions from PROPVARIANT as well.

Return Code

S_OK
The requested conversion is available.
S_FALSE
The requested conversion is not available.

wFromType or wToType was not a valid type for the provider, and the provider was a version 2.0 or later 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 IConvertType::CanConvert was called on a rowset or row.

The DBCONVERTFLAGS_PARAMETER bit was set, and IConvertType::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.

DBCONVERTFLAGS_COLUMN and DBCONVERTFLAGS_PARAMETER were both specified. (These two flags are mutually exclusive.)

DB_E_BADTYPE
DBCONVERTFLAGS_FROMVARIANT was specified, and the type specified in wFromType was not a VARIANT type.

Comments

IConvertType::CanConvert can be called from a command, a rowset, or a row. The consumer can call it on a command to inquire about conversions supported by that command and on a rowset or row to inquire about conversions supported by that rowset or row. The consumer might be able to call it on a command to inquire about conversions supported on rowsets or rows generated from that command; whether this is supported is specified by the DBPROP_ROWSETCONVERSIONSONCOMMAND property.

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_IUNKNOWN, even though they may support binding such objects as streams.

See Also

IAccessor::CreateAccessor


ICreateRow

ICreateRow creates and binds to an object named by a URL.

When to Implement

ICreateRow is a mandatory interface on the OLE DB root binder object and on all provider binder objects. It is also an optional interface on session and row objects.

When to Call

Consumers call ICreateRow::CreateRow to create and bind to an object named by a URL.

Method | Description
CreateRow | Creates and binds to an object named by a URL and returns the requested interface pointer.


ICreateRow::CreateRow

Creates and binds to an object named by a URL and returns the requested interface pointer.

HRESULT CreateRow(
   IUnknown *            pUnkOuter,
   LPCOLESTR             pwszURL,
   DBBINDURLFLAG         dwBindURLFlags,
   REFGUID               rguid,
   REFIID                riid,
   IAuthenticate *       pAuthenticate,
   DBIMPLICITSESSION *   pImplSession,
   DBBINDURLSTATUS *     pdwBindStatus,
   LPOLESTR *            ppwszNewURL,
   IUnknown **           ppUnk
);

Parameters

pUnkOuter
[in]
If the returned object is to be aggregated, pUnkOuter is an interface pointer to the controlling IUnknown. Otherwise, it is a null pointer.
pwszURL
[in]
The canonical URL naming the object to be created. If DBPROP_GENERATEURL is DBPROPVAL_GU_SUFFIX, this string defines the path to the new object and the provider will generate the URL suffix.
dwBindURLFlags
[in]
Bitmask of bind flags that control how the object is to be bound. For more information, see the dwBindURLFlags value table in the reference entry for IBindResource::Bind.

Note Creating a row requires DBBINDURLFLAG_WRITE be set. This is independent of whether a row, rowset, or stream view of the resource is requested.

The flag values listed in the following table are also available on ICreateRow::CreateRow, in addition to the bind flags defined in IBindResource::Bind.

Flags | Meaning
DBBINDURLFLAG_COLLECTION | Creates the object as a collection.
DBBINDURLFLAG_ISSTRUCTUREDDOCUMENT | Creates the object as a structured document.
DBBINDURLFLAG_OPENIFEXISTS If the resource exists and is not a collection, it is opened and S_OK is returned. If it exists and is a collection, DB_E_RESOURCEEXISTS is returned. If the resource does not exist, it is created.

This flag may not be used with DBBINDURLFLAG_OVERWRITE.

Note All implementers of ICreateRow must support this flag.
DBBINDURLFLAG_OVERWRITE | Deletes and re-creates a named object if it exists. This flag may not be used with DBBINDURLFLAG_OPENIFEXISTS.
DBBINDURLFLAG_COLLECTION | DBBINDURLFLAG_OPENIFEXISTS | If the resource is not a collection, DB_E_NOTCOLLECTION is returned. If the resource exists and is a collection, it is opened and S_OK is returned. If the resource does not exist, it is created.

Not all bind flags may be used with all object types (as specified in the rguid parameter). The following table describes the restrictions. If a client does not adhere to these restrictions, the bind will fail with E_INVALIDARG.

Object type Restrictions
DBGUID_ROW All flags are allowed.

DBBINDURLFLAG_OPENIFEXISTS and DBBINDURLFLAG_OVERWRITE may not be used together.
DBGUID_ROWSET DBBINDURLFLAG_COLLECTION must be specified.

DBBINDURLFLAG_OPENIFEXISTS and DBBINDURLFLAG_OVERWRITE may not be used together.

In addition, the following flags are disallowed:

DBBINDURLFLAG_DELAYFETCHCOLUMNS
DBBINDURLFLAG_DELAYFETCHSTREAM
DBGUID_STREAM DBBINDURLFLAG_OPENIFEXISTS and DBBINDURLFLAG_OVERWRITE may not be used together.

In addition, the following flags are disallowed:

DBBINDURLFLAG_DELAYFETCHCOLUMNS
DBBINDURLFLAG_DELAYFETCHSTREAM
rguid
[in]
A pointer to a GUID indicating the type of OLE DB object to be returned. The GUID must be set to one of the following values:

DBGUID_ROW
DBGUID_ROWSET
DBGUID_STREAM

Note DBGUID_ROWSET can be requested only if the row represents a collection (that is, dwBindURLFlags set to DBBINDURLFLAG_COLLECTION).

riid
[in]
Interface ID of the interface pointer to be returned.
pAuthenticate
[in]
Optional pointer to the caller's IAuthenticatei nterface. If supplied, it is provider-specific whether the IAuthenticate credentials are used before or after anonymous or default login credentials are used.
pImplSession
[in/out]
A pointer to a DBIMPLICITSESSION structure used to request and return aggregation information for the implicit session object. The DBIMPLICITSESSION structure is defined in the reference entry for IBindResource::Bind. If ICreateRow::CreateRow fails and pImplSession is not a null pointer, pImplSession->pSession should be set to NULL.

Note ICreateRow::CreateRow uses pImplSession only when implemented on a binder object and binding to a row, rowset, or stream object (rguid is DBGUID_ROW, DBGUID_ROWSET, or DBGUID_STREAM). If implemented on any other object (for example, a session or row object), the provider ignores pImplSession because the existing object already has a session context.

pdwBindStatus
[out]
A pointer to memory in which to return a bitmask that describes warning status for requested bind flags. If pdwBindStatus is a null pointer, no status is returned. The bind status values are listed in the table for pdwBindStatus for IBindResource::Bind. This parameter should be set to NULL if an error code is returned.
ppwszNewURL
[out]
A pointer to memory in which to return a string containing a provider-generated canonical URL that names the created row. If DBPROP_GENERATEURL is DBPROPVAL_GU_SUFFIX, callers are required to pass this pointer. If DBPROP_GENERATEURL is DBPROPVAL_GU_NOTSUPPORTED and the caller passes a null value, the provider does not return the absolute URL of the created row. If the caller passes this pointer, the provider must return the absolute URL of the created row. The provider allocates the memory for this string. The consumer should free the memory with IMalloc::Free. If ICreateRow::CreateRow fails and ppwszNewURL is not a null pointer, *ppwszNewURL is set to NULL.
ppUnk
[out]
A pointer to memory in which to return an interface pointer on the requested object. If ICreateRow::CreateRow fails, *ppUnk is set to a null pointer.

Return Code

S_OK
The method succeeded, the object named by the URL was created and bound, and the requested interface pointer was returned.
E_INVALIDARG
pwszURL or ppUnk was a null pointer.

rguid was DBGUID_ROWSET, but the DBBINDURLFLAG_COLLECTION bit of dwBindURLFlags was not set.

pImplSession was not a null pointer, and pImplSession->piid was a null pointer.

dwBindURLFlags contained a flag labeled "Modifies DBBINDURLFLAG_*" in the dwBindURLFlags value table in the reference entry for IBindResource::Bind but did not contain the flag to be modified.

dwBindURLFlags contained both DBBINDURLFLAG_OPENIFEXISTS and DBBINDURLFLAG_OVERWRITE.

The provider does not support one or more values of dwBindURLFlags.

One of more of the bind flags in dwBindURLFlags are either not supported by the provider or disallowed for the object type denoted by rguid. For flags allowed for each object type, see the table in the reference entry for IBindResource::Bind.

DB_S_ASYNCHRONOUS
The method has initiated asynchronous creation of an object. The consumer can call IDBAsynchStatus::GetStatus to poll for status or can register for notifications of asynchronous processing. Until asynchronous processing is complete, the object is not populated.

DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED.

DB_S_ERRORSOCCURRED
The bind succeeded, but some bind flags or properties were not satisfied. The consumer should examine the bind status returned in *pdwBindStatus.

DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED. Once rowset population is complete, the consumer can see DB_S_ERRORSOCCURRED either by calling IDBAsynchStatus::GetStatus or by receiving IDBAsynchNotify::OnStop.

DB_E_OBJECTMISMATCH
The object named by rguid does not represent the resource named by pwszURL.
DB_E_OBJECTOPEN
The provider would have to open a new connection to support this operation, and DBPROP_MULTIPLECONNECTIONS is set to VARIANT_FALSE.
DB_E_RESOURCELOCKED
One or more other processes using the DBBINDURLFLAG_SHARE_* flag have locked the OLE DB object represented by this URL. IErrorInfo::GetDescription returns a string consisting of user names separated by semicolons. These are the names of the users who have the object locked.
DB_E_RESOURCENOTSUPPORTED
The consumer attempted to create a type of row not supported by the provider. For example, DBBINDURLFLAG_ISSTRUCTUREDDOCUMENT was set but the provider does not support structured documents.
DB_E_RESOURCEOUTOFSCOPE
ICreateRow is implemented on a session or row object, and the caller tried to bind to an object that is not within the scope of this session or row object, respectively.
DB_SEC_E_PERMISSIONDENIED
The caller's authentication context does not permit access to the object. IErrorInfo::GetDescription returns the default error string.
DB_SEC_E_SAFEMODE_DENIED
The provider was called within a safe mode or context, and pwszURL specified an unsafe URL.
DB_E_READONLY
The caller requested write access to a read-only object.
DB_E_ASYNCNOTSUPPORTED
The provider does not support asynchronous binding.
DB_E_CANNOTCONNECT
The provider could not connect to the server for this object.
DB_E_NOAGGREGATION
pUnkOuter was not a null pointer, and the object being created does not support aggregation.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

pImplSession was not a null pointer, pImplSession->pUnkOuter was not a null pointer, and pImplSession->piid did not point to IID_IUnknown.

pImplSession was not a null pointer, pImplSession->pUnkOuter was not a null pointer, and the object being created does not support aggregation.

DB_E_OUTOFSPACE
The provider was unable to create an object at this URL using ICreateRow::CreateRow because the server was out of physical storage.
DB_E_NOTCOLLECTION
The provider was unable to create an object with the specified URL because the parent URL is not a collection.
DB_E_NOTFOUND
The provider was unable to create an object with the specified URL because the parent URL does not exist.
DB_E_NOTSUPPORTED
The provider supports direct binding to OLE DB objects but does not support the object type requested in rguid.
DB_E_TIMEOUT
The attempt to bind to the object timed out.
DB_E_RESOURCEEXISTS
The provider was unable to create an object at this URL because an object named by this URL already exists and either the caller did not specify OPENIFEXISTS or OVERWRITE or the provider does not support this behavior on object creation.
REGDB_E_CLASSNOTREG
The root binder was unable to instantiate the provider binder object.
E_FAIL
A provider-specific error occurred.
E_NOINTERFACE
The object did not support the interface specified in riid, or riid was IID_NULL.

The provider does not allow the creation of resources via ICreateRow.

E_UNEXPECTED
ICreateRow::CreateRow is implemented on a row object, ITransaction::Commit or ITransaction::Abort was called, and the object is in a zombie state.

Comments

ICreateRow::CreateRow provides the ability to create a new object named by a URL and bind it directly to a row, to a rowset, or to a row and a default stream object. If a consumer calls CreateRow on a URL that does not exist and rguid is set to DBGUID_STREAM, both a row and a default stream object are created. Only the stream object may be aggregated. The provider returns the requested interface pointer on the stream object.

The created row object "inherits" the default columns of its container's class. The provider determines what these default columns are. Whether or not to allow creation of a root row object with no inherited columns is provider-specific. For "document source" providers (that is, providers for which the value of DBPROP_DATASOURCE_TYPE is DBPROPVAL_DST_DOCSOURCE), the default columns are the columns of the resource rowset. For example, a new document inherits the default columns of its folder's class, which are the columns of the resource rowset.

Columns may be added by calling IRowSchemaChange::AddColumns. Column values may also be set with IRowChange::SetColumns.

The provider determines what the container, or parent row, is for its hierarchy. Atomic rows (for example, simple documents) are leaf nodes in a hierarchy. Collections are root nodes or intermediate nodes in a hierarchy. For example, a document's container is a folder. Given a row object, a consumer can obtain the parent by obtaining the parent URL and binding to it.

The DBBINDURLFLAG_OPENIFEXISTS and DBBINDURLFLAG_OVERWRITE bind flags may be used to specify behavior when the URL denotes an existing row. If a consumer calls ICreateRow::CreateRow on a URL that exists and does not specify DBBINDURLFLAG_OPENIFEXISTS or DBBINDURLFLAG_OVERWRITE, the provider must return DB_E_RESOURCEEXISTS. The result does not depend on the value of rguid. (The value of rguid could be DBGUID_ROW or DBGUID_STREAM.) Providers are not required to support these flags. If these flags are not supported and the consumer uses them to open a resource, the provider returns DB_E_RESOURCEEXISTS (the same error that is returned if the flag is not specified). These flags are disallowed on IBindResource::Bind. The following table outlines the behavior with and without these two flags.

Method, bind flag | Row exists | Row does not exist
Bind | Open | Fail
CreateRow | Fail | Create
CreateRow, DBBINDURLFLAG_OPENIFEXISTS | Open | Create
CreateRow, DBBINDURLFLAG_OVERWRITE | Create | Create

If the object was created and no warnings are issued, the provider returns S_OK. If the object was created with warnings, the provider returns DB_S_ERRORSOCCURRED. *pdwBindStatus contains any warning flags.

Binding semantics, except where noted, are identical to those of IBindResource::Bind. See the reference entry for IBindResource::Bind for a full discussion of bind flags and additional binding properties.

The scope of the session object associated with the object obtained in this call to ICreateRow::CreateRow is defined by the URL that was requested in the bind call. For more information, see "Direct Binding" in Chapter 8: Binder Objects and Direct Binding, as well as the "Comments" section of the reference entry for IBindResource::Bind.

Note "Provider Binder Objects" in Chapter 8 discusses case-sensitivity issues for URL-to-provider mapping.

Row creation and binding operations can be performed asynchronously. For more information on asynchronous processing in OLE DB, see Chapter 17: Asynchronous Processing.

The consumer may also support and register the IAuthenticate interface via pAuthenticate to provide information to authenticate the user. If the client registers an IAuthenticate pointer, it will be called if access to the requested resource is denied. The initial attempt to access the resource uses anonymous credentials or the default logged-in credentials. For more information, see IAuthenticate in the MSDN Library documentation.

The caller should convert noncanonical URLs to canonical form before invoking this method. Providers must return URLs in canonical form. For more information, see the Win32® Internet API function InternetCanonicalizeUrl in the Site Builder Network (SBN) Workshop documentation at http://msdn.microsoft.com/workshop/networking/default.asp.

If returning a result code that indicates success, the provider must return it in the following order of precedence:

  • If an asynchronous operation was requested, the provider returns DB_S_ASYNCHRONOUS.
  • If the creation was successful but at least one warning was issued, the provider returns DB_S_ERRORSOCCURRED.
  • Otherwise, the provider returns S_OK.

Providers may own the responsibility for determining the suffix portion of the new URL. If DBPROP_GENERATEURL is DBPROPVAL_GU_SUFFIX, the caller specifies the path of the new URL in *pwszURL. The provider generates the URL suffix and returns the entire URL in **ppwszNewURL.


IDBAsynchNotify

IDBAsynchNotify is the callback interface that a consumer must support to get notified of the progress of asynchronous operations such as initializing a data source object 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 initializing a data source object, 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

Called when low resources are detected by the provider.

HRESULT OnLowResource(
   DB_DWRESERVE 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::OnProgress | IDBAsynchNotify::OnStop


IDBAsynchNotify::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,
   DBCOUNTITEM   ulProgress,
   DBCOUNTITEM   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, the caller must set hChapter to DB_NULL_HCHAPTER.
ulOperation
[in]
The operation about which status is being returned. Must be set to the following value:

DBASYNCHOP_OPEN—The information applies to the asynchronous opening or population of a rowset or the asynchronous initialization of a data source object.

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 IDBAsynchNotify::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 if 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 initialized and the full range of interfaces is 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 off 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 object 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 listener 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.

Listeners 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.

All listeners are always notified of the DBASYNCHPHASE_COMPLETE phase when all asynchronous processing has completed. If a listener registers for asynchronous notifications on a rowset after all processing has already completed, that listener's IDBAsynchNotify::OnProgress callback is immediately called with the DBASYNCHPHASE_COMPLETE phase. Listeners might or might not receive other phases.

The DBASYNCHPHASE_CANCELED phase is always passed to all listeners when asynchronous processing has been canceled. If a listener registers for asynchronous notifications on a rowset after processing has been canceled, that listener's IDBAsynchNotify::OnProgress callback is immediately called with the DBASYNCHPHASE_CANCELED phase. Listeners might or might not receive other phases.

See Also

ICommand::Execute | IDBAsynchStatus::GetStatus | IDBSchemaRowset::GetRowset | IOpenRowset::OpenRowset | ISourcesRowset::GetSourcesRowset


IDBAsynchNotify::OnStop

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, the caller must set hChapter to DB_NULL_HCHAPTER.
ulOperation
[in]
The operation that has stopped.

DBASYNCHOP_OPEN—The completion information applies to the asynchronous opening or population of a rowset or to the asynchronous initialization of a data source object.

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 about 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 after 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, after all asynchronous phases for that operation have completed. IDBAsynchNotify::OnStop is always called, whether the asynchronous operation succeeded, failed, or was aborted by the consumer.

For operations that complete successfully, IDBAsynchNotify::OnProgress is always called prior to IDBAsynchNotify::OnStop with ulProgress equal to ulProgressMax. The only exception is if the listener has explicitly requested not to receive further IDBAsynchNotify::OnProgress notifications by returning E_NOTIMPL or DB_S_UNWANTEDOPERATION, and if the provider has optimized by not making additional calls to IDBAsynchNotify::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 object 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, the caller must set hChapter to DB_NULL_HCHAPTER.
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 to the asynchronous initialization of a data source object.

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 might 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 object on which IDBInitialize::Initialize has not been called.

IDBAsynchStatus::Abort was called on a data source object on which IDBInitialize::Initialize was called but subsequently canceled prior to initialization. The data source object 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 IDBAsynchNotify::OnProgress notification, the provider returns DB_E_CANCELED from the IDBAsynchStatus::Abort function and continues processing the operation.

Aborting the initialization of a rowset or data source object might leave the rowset or data source object in a zombie state, such that all methods other than IUnknown methods return E_UNEXPECTED. When this happens, the only possible action for the consumer is to release the rowset or data source object.

Calling IDBAsynchStatus::Abort and passing a value for ulOperation other than DBASYNCHOP_OPEN returns S_OK. This does not imply that the operation completed or was canceled.

Note Consumers should avoid requesting more than one asynchronous scoped operation (IScopedOperations::Copy, Delete, or Move) at a time, because there is no way for the methods of IDBAsynchStatus or IDBAsynchNotify to request or provide information per operation. If multiple asynchronous scoped operations are underway, it is undefined and provider-specific which operation is reported by IDBAsynchStatus::GetStatus or IDBAsynchNotify::Notify or is affected by a call to IDBAsynchStatus::Abort.

If multiple IScopedOperations operations are invoked asynchronously on the same row instance, the behavior of IDBAsynchStatus and IDBAsynchNotify is undefined. (For more information on asynchronous operations, see "Asynchronous Scoped Operations" in Chapter 17: Asynchronous Processing.

See Also

IDBAsynchNotify::OnStop


IDBAsynchStatus::GetStatus

Returns the status of an asynchronously executing operation.

HRESULT GetStatus (
   HCHAPTER       hChapter,
   ULONG          ulOperation,
   DBCOUNTITEM   *pulProgress,
   DBCOUNTITEM   *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 about the asynchronous initialization of a data source object. If the provider is an OLE DB 2.5-compliant provider that supports direct URL binding, the consumer requests information about the asynchronous initialization or population of a data source, rowset, row, or stream object.

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 if 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 is 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 object 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.

DBASYNCHPHASE_CANCELED—Asynchronous processing of the object was aborted. IDBAsynchStatus::GetStatus returns DB_E_CANCELED. If the asynchronous operation was a result of calling ICommand::Execute for a command that updates, deletes, or inserts rows, pulProgress is equal to the total number of rows, for all parameter sets, affected by the command prior to cancellation.

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 example, 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 IDBAsynchStatus::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 ppwszStatusText is NULL on input, no status string is returned and the provider returns information about any element of the operation or about 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 might fail, and the full set of interfaces might not be available on the object.
  • If pulAsynchPhase is equal to DBASYNCHPHASE_POPULATION, the rowset is fully initialized and the full range of interfaces is available on the object; however, there might 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 is fully initialized and populated.
DB_E_CANCELED
Asynchronous processing was canceled during rowset population. Population stops, but the rowset remains valid for the rows already populated.

Asynchronous processing was canceled during data source object initialization. The data source object is in an uninitialized state.

E_UNEXPECTED
IDBAsynchStatus::GetStatus was called on a data source object, and IDBInitialize::Initialize has not been called on the data source object.

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 is 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 IDBAsynchStatus::GetStatus on an initialized data source object 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 IDBAsynchStatus::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


IDBBinderProperties : IDBProperties

IDBBinderProperties allows a consumer to get, set, and reset direct binding properties.

When to Implement

This interface is mandatory on binder objects and optional on data source and enumerator objects.

When to Call

Consumers can call IDBBinderProperties::Reset to reset the properties of a binder, data source, or enumerator object.

Note Because IDBBinderProperties inherits from IDBProperties, IDBProperties::GetProperties may be called on an object to retrieve the values of properties that have been set or (if they have not been set) have default values.

Method | Description
Reset | Reset all properties of the object.
(Inherited) | All methods of IDBProperties.


IDBBinderProperties::Reset

Clears any properties set on a binder object. If the binder object has any properties with defaults, Reset returns them to their default values.

HRESULT Reset();

Parameters

None

Return Code

S_OK
The reset was successful.
E_OUTOFMEMORY
The provider did not have enough memory to reset the properties.

Comments

None


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 provider is an OLE DB 1.0 or 1.1 provider that does not support aggregation of the command object being created.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

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 provider is an OLE DB 1.0 or 1.1 provider that does not support aggregation of the session object being created.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

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 returned only 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 stores.

It is important to distinguish between the data store and the data source object. The data store is the actual source of data, such as a server database, a file in a directory, or an e-mail system. The data source object is the object that represents, and is used by the consumer code to interact with, the data store.

The methods in this interface are intended to affect a data store, although OLE DB objects, such as the data source object, may incidentally be affected as well.

Method | Description
CreateDataSource | Creates and initializes a new data store.
DestroyDataSource | Destroys the current data store.
GetCreationProperties | Returns information about the data store creation properties supported by the provider.
ModifyDataSource | Modifies the current data store.


IDBDataSourceAdmin::CreateDataSource

Creates and initializes a new data store.

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 store creation. If the same property is specified more than once in rgPropertySets, the value used is provider-specific. If a provider cannot support a property, 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 Property Group," "Initialization Property Group," and "Session Property Group" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 14: Properties.

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 store 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 following:
  • 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 store 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 store.
DB_E_ALREADYINITIALIZED
IDBInitialize::Initialize had already been called for the data source object, and an intervening call to IDBInitialize::Uninitialize had not been made.
DB_E_DUPLICATEDATASOURCE
A data store with the same name already exists.
DB_E_ERRORSOCCURRED
The data store 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 following:
  • 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 store creation.
  • The property set was not supported by the provider.
DB_E_NOAGGREGATION
pUnkOuter was not a null pointer, and the provider is an OLE DB 1.0 or 1.1 provider that does not support aggregation of the data source object being created.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

DB_SEC_E_AUTH_FAILED
The provider required initialization, but an authentication failed. The data store was not created.
DB_SEC_E_PERMISSIONDENIED
The consumer did not have permission to create a new data store.

Comments

IDBDataSourceAdmin::CreateDataSource creates a data store and initializes a data source object in one atomic operation.

The consumer must set any Data Source Creation or Initialization properties required by the provider to create the data store and initialize the data source object. If DBPROP_INIT_LOCATION is set to VT_EMPTY, the data source object is created in the default location.

See Also

IDBDataSourceAdmin::DestroyDataSource | IDBDataSourceAdmin::ModifyDataSource


IDBDataSourceAdmin::DestroyDataSource

Destroys the current data store.

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 store.

Comments

IDBDataSourceAdmin::DestroyDataSource destroys the current data store. Any open OLE DB objects on this data source, such as sessions, commands, rowsets, rows, and views, must be released before calling this method. Consumers cannot destroy a data store if they have not successfully connected to the provider by initializing the data source object.

See Also

IDBDataSourceAdmin::CreateDataSource


IDBDataSourceAdmin::GetCreationProperties

Returns information about the data store 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 cPropertyIDSets is zero, the provider ignores rgPropertyIDSets and returns information about all of the properties in the Data Source Creation property group it supports and about all of the properties in the Initialization property group it supports for use in creating data stores.

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 stores, 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 property group or belong to the Initialization property group and be supported for use in creating data stores. The provider returns information about the properties specified in these structures. If cPropertyIDSets is zero, this parameter 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 Property Group," "Initialization Property Group," and "Session Property Group" in Appendix C. For information about the DBPROPIDSET structure, see "DBPROPIDSET Structure" in Chapter 14: 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 in the Data Source Creation or Initialization property group. 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, one structure is returned for each property set that contains at least one property belonging to the Data Source Creation or Initialization property group. If cPropertyIDSets is not zero, 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, *prgPropertyInfoSets must be a null pointer on output.

For information about the DBPROPINFOSET and DBPROPINFO structures, see "DBPROPINFOSET Structure" and "DBPROPINFO Structure" in Chapter 14: Properties.

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, IDBDataSourceAdmin::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 or Initialization property group. 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 stores. 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

IDBDataSourceAdmin::GetCreationProperties returns properties in the Data Source Creation property group as well as properties in the Initialization property group that can be used in data store creation. It is possible that there are properties in the Initialization property group that cannot be used in data store creation. The properties for which values must be set to create a data store 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 store.

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 store creation. If the same property is specified more than once in rgPropertySets, the value used is provider-specific. If a provider cannot support a property, 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 Property Group" and "Initialization Property Group" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 14: Properties.

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 store 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 following:
  • 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 store 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 store 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 following:
  • 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 store 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 store.

Comments

IDBDataSourceAdmin::ModifyDataSource modifies the current data store. Any open OLE DB objects on this data source object, such as sessions, commands, rowsets, rows, and views, must be released before calling this method. Consumers cannot modify a data store if they have not successfully connected to the provider by initializing the data source object.

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 is a list of the keywords from OLE DB:

ABSOLUTE | ESCAPE | OUTPUT
ACTION | EXCEPT | OVERLAPS
ADD | EXCEPTION | PARTIAL
ALL | EXEC | POSITION
ALLOCATE | EXECUTE | PRECISION
ALTER | EXISTS | PREPARE
AND | EXTERNAL | PRESERVE
ANY | EXTRACT | PRIMARY
ARE | FALSE | PRIOR
AS | FETCH | PRIVILEGES
ASC | FIRST | PROCEDURE
ASSERTION | FLOAT | PUBLIC
AT | FOR | READ
AUTHORIZATION | FOREIGN | REAL
AVG | FOUND | REFERENCES
BEGIN | FROM | RELATIVE
BETWEEN | FULL | RESTRICT
BIT | GET | REVOKE
BIT_LENGTH | GLOBAL | RIGHT
BOTH | GO | ROLLBACK
BY | GOTO | ROWS
CASCADE | GRANT | SCHEMA
CASCADED | GROUP | SCROLL
CASE | HAVING | SECOND
CAST | HOUR | SECTION
CATALOG | IDENTITY | SELECT
CHAR | IMMEDIATE | SESSION
CHAR_LENGTH | IN | SESSION_USER
CHARACTER | INDICATOR | SET
CHARACTER_LENGTH | INITIALLY | SIZE
CHECK | INNER | SMALLINT
CLOSE | INPUT | SOME
COALESCE | INSENSITIVE | SQL
COLLATE | INSERT | SQLCODE
COLLATION | INT | SQLERROR
COLUMN | INTEGER | SQLSTATE
COMMIT | INTERSECT | SUBSTRING
CONNECT | INTERVAL | SUM
CONNECTION | INTO | SYSTEM_USER
CONSTRAINT | IS | TABLE
CONSTRAINTS | ISOLATION | TEMPORARY
CONTINUE | JOIN | THEN
CONVERT | KEY | TIME
CORRESPONDING | LANGUAGE | TIMESTAMP
COUNT | LAST | TIMEZONE_HOUR
CREATE | LEADING | TIMEZONE_MINUTE
CROSS | LEFT | TO
CURRENT | LEVEL | TRAILING
CURRENT_DATE | LIKE | TRANSACTION
CURRENT_TIME | LOCAL | TRANSLATE
CURRENT_TIMESTAMP | LOWER | TRANSLATION
CURRENT_USER | MATCH | TRIGGER
CURSOR | MAX | TRIM
DATE | MIN | TRUE
DAY | MINUTE | UNION
DEALLOCATE | MODULE | UNIQUE
DEC | MONTH | UNKNOWN
DECIMAL | NAMES | UPDATE
DECLARE | NATIONAL | UPPER
DEFAULT | NATURAL | USAGE
DEFERRABLE | NCHAR | USER
DEFERRED | NEXT | USING
DELETE | NO | VALUE
DESC | NOT | VALUES
DESCRIBE | NULL | VARCHAR
DESCRIPTOR | NULLIF | VARYING
DIAGNOSTICS | NUMERIC | VIEW
DISCONNECT | OCTET_LENGTH | WHEN
DISTINCT | OF | WHENEVER
DISTINCTROW | ON | WHERE
DOMAIN | ONLY | WITH
DOUBLE | OPEN | WORK
DROP | OPTION | WRITE
ELSE | OR | YEAR
END | ORDER | ZONE
END-EXEC | OUTER |

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 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, IDBInfo::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 if 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 IDBInfo::GetLiteralInfo, a literal is one of several things, including the following:

  • A special character or characters used by text commands, such as the character used to quote identifiers. IDBInfo::GetLiteralInfo returns the character or characters.
  • A literal data value, such as a character literal in an SQL statement. For such literal data values, IDBInfo::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, IDBInfo::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 described in the following table.

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_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_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 IDBInfo::GetLiteralInfo returns information only 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; 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_CONSTRAINT_NAME | A constraint name in a text command.
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 can 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 can 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 can 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, 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 object. 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 object remains in an uninitialized state.

DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED.

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. To return properties in error, the provider uses DBPROPSET_PROPERTIESINERROR as described in Chapter 14: Properties. The method can fail to set properties for a number of reasons, including the following:
  • 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.
  • DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED. Once rowset population is complete, the consumer can see DB_S_ERRORSOCCURRED either by calling IDBAsynchStatus::GetStatus or by receiving IDBAsynchNotify::OnStop.
E_FAIL
A provider-specific error occurred.
E_OUTOFMEMORY
The provider was unable to allocate sufficient memory to initialize the data source object or enumerator.
E_UNEXPECTED
The data source object is in the process of being initialized asynchronously. To cancel asynchronous execution, call IDBAsynchStatus::Abort.
DB_E_ALREADYINITIALIZED
IDBInitialize::Initialize had already been called for the data source object or enumerator, and an intervening call to IDBInitialize::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 initialized, but one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_OPTIONAL—were not set. To return properties in error, the provider uses DBPROPSET_PROPERTIESINERROR as described in Chapter 14: Properties. The method can fail to set properties for a number of reasons, including the following:
  • 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.
DB_SEC_E_AUTH_FAILED
Authentication of the consumer to the data source object or enumerator failed. The data source object or enumerator remains in the uninitialized state.

Comments

IDBInitialize::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, IDBInitialize::Initialize can prompt for values.

If IDBInitialize::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 Appendix C: OLE DB Properties.

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: Data Source and Session Objects.

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,
                                  (void**)&pIDBProperties);
   pIDBProperties->SetProperties(1, &PropSet);
   pIDBProperties->Release();

   // Initialize the data source object.
   hr = pIDBInitialize->Initialize();
   pIDBInitialize->Release();
   return hr;
};

See Also


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 object 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 IDBInitialize::Initialize or a new state loaded on a data source object, such as with IPersistFile::Load.

It is an error to call IDBInitialize::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 IDBInitialize::Uninitialize. It is not an error to call IDBInitialize::Uninitialize if IDBInitialize::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: Data Source and Session Objects.

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. For OLE DB 2.5 providers that support direct URL binding, this interface is implemented on binder objects via IDBBinderProperties, which is derived from IDBProperties.

A data source object or enumerator that has not been initialized or that has been uninitialized is in an uninitialized state. When a data source object or enumerator is in this state, the consumer can work only with properties of that object that belong to the Initialization property group.

After the data source object or enumerator is initialized, it is in an initialized state (until it is uninitialized). When the data source object or enumerator is in this state, the consumer can work with properties of that object that belong to the Initialization, Data Source, and Data Source Information property groups. However, the consumer 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 in 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 is in an uninitialized state, the provider returns the values of all properties in the Initialization property group for which values have been set or defaults exist. This should include any provider-specific properties belonging to this group. If the data source object or enumerator is in an initialized state, the provider returns the values of all properties in the Data Source, Data Source Information, and Initialization property groups (for data source objects) or just in 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 to the Initialization property group, for enumerators. The provider returns the values of the properties specified in these structures. If cPropertyIDSets is zero, 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 Property Group," "Data Source Information Property Group," and "Initialization Properties" in Appendix C. For information about the DBPROPIDSET structure, see "DBPROPIDSET Structure" in Chapter 14: Properties.

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 in 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, 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 to the Initialization property group (if the enumerator is initialized). If cPropertyIDSets is not zero, 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 14: Properties.

Return Code

S_OK
The method succeeded. For property sets other than DBPROPSET_PROPERTIESINERROR, dwStatus is set to DBPROPSTATUS_OK in all DBPROP structures returned. If the requested property set is DBPROPSET_PROPERTIESINERROR, dwStatus reflects the individual error for each DBPROP structure returned from the method.
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. IDBProperties::GetProperties can fail to return properties for a number of reasons, including the following:
  • 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, IBindResource::Bind, or ICreateRow::CreateRow returns DB_E_ERRORSOCCURRED or DB_S_ERRORSOCCURRED, the consumer can immediately call IDBProperties::GetProperties with the DBPROPSET_PROPERTIESINERROR property set to return all the properties that were in error. IDBProperties::GetProperties will return S_OK, *pcPropertySets will contain the number of property sets having properties in error, and *prgPropertySets will contain an array of DBPROPSET structures. For more information, see "Property Sets" in Appendix C: OLE DB Properties.

When called on the root binder object, which supports no properties, IDBProperties::GetProperties returns the values of properties that have been set on the root binder.

  • If cPropertyIDSets is zero, the root binder returns the value of any properties that have been set by the consumer and returns S_OK, sets *pcPropertySets to 0, and sets *prgPropertySets to NULL.
  • If cPropertyIDSets is not zero, the root binder returns the value of any requested properties. If a requested property has not been set, the root binder returns a status of DBPROPSTATUS_NOTSET.

See Also

IDBProperties::GetPropertyInfo | IDBProperties::SetProperties


IDBProperties::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 is in an uninitialized state, the provider returns information about all properties in the Initialization property group. This should include any provider-specific properties belonging to this group. If the data source object is in an initialized state, 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. For information about the DBPROPINFO structure, see "DBPROPINFO Structure" in Chapter 14: Properties.

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, this parameter is ignored. For information about the DBPROPIDSET structure, see "DBPROPIDSET Structure" in Chapter 14: Properties.

The following special GUIDs are defined for use with IDBProperties::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 IDBProperties::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 prgPropertyInfoSets. Instead, the GUID of the property set to which the property belongs is returned.

Property set GUID | Description
DBPROPSET_COLUMNALL | Returns all properties in the Column property group, including provider-specific properties.
DBPROPSET_CONSTRAINTALL | Returns all properties in the Constraint property group, including provider-specific properties.
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_INDEXALL | Returns all properties in the Index 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.
DBPROPSET_STREAMALL | Returns all properties of the Stream property group, including provider-specific properties.
DBPROPSET_TABLEALL | Returns all properties in the Table property group, including provider-specific properties.
DBPROPSET_TRUSTEEALL | Returns all properties in the Trustee property group, including provider-specific properties.
DBPROPSET_VIEWALL | Returns all properties in the View 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 cPropertyIDSets. 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, one structure is returned for each property set that contains at least one property supported by the provider. If cPropertyIDSets is not zero, 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, *prgPropertyInfoSets must be a null pointer on output.

For information about the DBPROPINFOSET and DBPROPINFO structures, see "DBPROPINFOSET Structure" and "DBPROPINFO Structure" in Chapter 14: Properties.

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, IDBProperties::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.

Note For method calls that return multiple strings within an allocated string buffer, consumers should not make assumptions about the relationship between the individual string pointers and the returned string buffer. Specifically, the consumer should always treat the returned string values as read-only memory and should not assume that the individual string values all occur within the string buffer. The only requirement on the provider is that freeing the string buffer releases any task memory allocated for that call. This applies to the ppDescBuffer returned by IDBProperties::GetPropertyInfo to hold the pwszDescription elements of the DBPROPINFO structure, as well as to the ppStringsBuffer returned by IColumnsInfo::GetColumnInfo to return string values contained within the DBCOLUMNINFO structure.

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
Information was successfully returned for at least one property. However, one of the following errors occurred:
  • 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. No property information was returned. 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

IDBProperties::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 IDBInitialize::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 IDBProperties::GetPropertyInfo indicate the set of properties that the provider knows about. The properties that may actually be supported for any particular rowset can be a subset of these properties. For example, a schema rowset might not be able to support all of the same properties as a rowset resulting from calling IOpenRowset::OpenRowset, or the properties that can be supported on a rowset generated from a command might vary depending on the command text. In general, consumers should not make assumptions based on the information returned by IDBProperties::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.

For OLE DB 2.5 providers that support direct URL binding, IDBProperties::GetPropertyInfo is implemented on binder objects via IDBBinderProperties:IDBProperties. The behavior of IDBProperties::GetPropertyInfo on a binder object depends on the type of binder object and the argument cPropertyIDSets.

When called on the root binder object, which supports no properties, IDBProperties::GetPropertyInfo behaves as follows:

  • If cPropertyIDSets is zero, the root binder returns S_OK; *pcPropertyInfoSets is set to zero and sets *prgPropertyInfoSets and *ppDescBuffer to null pointers on output.
  • If cPropertyIDSets is not zero, the root binder returns DB_E_ERRORSOCCURRED and sets each requested property to DBPROPFLAGS_NOTSUPPORTED.

When called on provider binder objects, IDBProperties::GetPropertyInfo behaves as follows:

  • If cPropertyIDSets is zero, the provider ignores rgPropertyIDSets and 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, 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.

See Also

IDBProperties::GetProperties | IDBProperties::SetProperties


IDBProperties::SetProperties

Sets properties in the Data Source and Initialization property groups, for data source objects, or in 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 except return S_OK.
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, the value 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 Property Group" and "Initialization Property Group" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 14: Properties.

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. IDBProperties::SetProperties can fail to set properties for a number of reasons, including the following:
  • 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.

Comments

For OLE DB 2.5 providers that support direct URL binding, IDBProperties::SetProperties is implemented on binder objects via IDBBinderProperties:IDBProperties. The behavior of IDBProperties::SetProperties on a binder object depends on the type of binder object.

When called on the root binder object, IDBProperties::SetProperties can be used to set properties from any property group. This includes provider-specific properties, which are propagated to provider binder objects at bind time.

When called on provider binder objects, IDBProperties::SetProperties behaves as follows:

  • If cPropertySets is zero, the provider ignores rgPropertySets and the method does not do anything.
  • If cPropertySets is not zero, the provider sets the requested properties. If a property is not supported, 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. If the same property is specified more than once in rgPropertySets, the value used is provider-specific.

Generally, consumers should not set the same property twice, because if they do, the value of the property is provider-specific, meaning it is unknown to the consumer. The status for the successfully set value should be DBPROPSTATUS_OK, but it is provider-specific what the status value is for the other property values.

See Also

IDBProperties::GetProperties | IDBProperties::GetPropertyInfo


IDBSchemaRowset

IDBSchemaRowset is an optional interface on sessions. It is used to provide advanced schema information.

Consumers can get information about a data store without knowing its structure by using the IDBSchemaRowset methods. For example, the data store might be one of the following types:

  • A Microsoft® SQL Server database that organizes each database into a set of schemas that contain the tables and queries for each schema.
  • A Microsoft Access 2.0 database that has a container of tables and a container of queries.
  • A Microsoft Access for Windows® 95 database that enables users to define folders to group tables into an arbitrary hierarchy.

The schemas of each of the preceding data stores can be exposed to the consumer by using the methods of IDBSchemaRowset.

Schema rowsets in OLE DB are identified by GUIDs and have predefined restriction columns. The restrictions allow the consumer to limit the set of rows returned by the schema rowset but does not define the format for those restrictions.

IDBSchemaRowset::GetSchemas returns to the consumer a list of schema rowsets, identified by their GUIDs, that are accessible by IDBSchemaRowset::GetRowset. Also returned is a pointer to descriptions of the restriction columns supported for that schema rowset.

IDBSchemaRowset::GetRowset allows the consumer to retrieve the specified schema rowset by passing in the GUID identifying the rowset and the count of restriction columns or an array of restriction values. Each schema rowset will return all of the columns in the order indicated by that rowset's respective table in Appendix B: Schema Rowsets.

Specifying restriction columns allows the consumer to limit the returned rowset to only those rows in the data store whose columns and values match the values specified in rgRestrictions. These values are applied in the same order as the restriction columns.

Note The number of restriction columns for each schema rowset is defined as a constant 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".

Programming considerations

At a minimum, providers that implement IDBSchemaRowset must support the passing of the restrictions according to the VARIANT types indicated in the following table. Providers are allowed to support restrictions passed as other types but must convert as dictated by the OLE DB specification.

Restriction column OLE DB type | VARIANT type of passed restriction
DBTYPE_BOOL | VT_BOOL
DBTYPE_DATE | VT_DATE
DBTYPE_GUID | VT_BSTR (string in registry format: "{xxxxxxxx-xxxx...}")
DBTYPE_I2 | VT_I2
DBTYPE_I8 | VT_I8
DBTYPE_UI2 | VT_UI2
DBTYPE_UI4 | VT_UI4
DBTYPE_UI8 | VT_UI8
DBTYPE_WSTR | VT_BSTR

All providers must support the TABLES, COLUMNS, and PROVIDER_TYPES schema rowsets, identified by the following GUIDs: DBSCHEMA_TABLES, DBSCHEMA_COLUMNS, and DBSCHEMA_PROVIDER_TYPES.

Providers can return provider-specific columns after the last column defined by OLE DB. Other OLE DB models, such as OLE DB for OLAP, can define their own schema rowsets to meet specific needs.

Provider writers should pay particular attention to rowset construction and data retrieval performance, because these methods will be used frequently by consumers.

Although the OLE DB schema rowsets define a minimum content of the system schema, consumers can also look at the schema tables in the database and thus discover other schema tables and attributes beyond the minimum set.

It is suggested that consumers precompile and store the specialized queries used for processes such as command planning and execution-time plan validation. Storage engines may have specialized optimizations associated with those plans.

Providers must return all of the schema rowset columns. If they cannot return the information in a column, they must return an appropriate value, usually NULL. Providers can return provider-specific columns after the last column defined by OLE DB.

For more information about schema rowsets, see Appendix B: Schema Rowsets.

Method | Description
GetRowset | Returns the specified schema rowset and its column restrictions, if requested.
GetSchemas | Returns a list of schema rowsets accessible by IDBSchemaRowset::GetRowset and the restriction columns supported.


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 the order of 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, 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 14: Properties.

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 might fail, and the full set of interfaces might not be available on the object until asynchronous initialization of the rowset has completed.

DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED.

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 following:
  • 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 that was specified to be applied to all columns 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.
  • The property was not in the Rowset property group.
  • The property set was not supported by the provider.

DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED. Once rowset population is complete, the consumer can see DB_S_ERRORSOCCURRED either by calling IDBAsynchStatus::GetStatus or by receiving IDBAsynchNotify::OnStop. Of course, the properties-in-error are not available, but for any properties that could not be set, the provider should report that status in the property array passed to IDBSchemaRowset::GetRowset (assuming that property failures can all be determined ahead of rowset population).

DB_S_NOTSINGLETON
The provider supports returning row objects on IDBSchemaRowset::GetRowset, and the consumer requested a row object but the result was not a singleton. A row object of the first row of the rowset is returned if the provider supports returning the row object. Because returning this result can be expensive, providers are not required to do so. If DB_S_ERRORSOCCURRED also applies to the execution of this method, it takes precedence over DB_S_NOTSINGLETON.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
rguidSchema was invalid.

rguidSchema specified a schema rowset that was not supported by the provider.

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.

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.
DB_E_NOAGGREGATION
pUnkOuter was not a null pointer, and the provider is an OLE DB 1.0 or 1.1 provider that does not support aggregation of the rowset object being created.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

DB_E_NOTFOUND
The provider supports the return of singleton row objects on a method that typically returns a rowset, a row was requested via riid or DBPROP_IRow, and no rows existed in the rowset.
DB_E_NOTSUPPORTED
In an element of rgRestrictions, the vt element of the VARIANT was not VT_EMPTY and the provider did not support the corresponding restriction. Some providers may also return E_INVALIDARG for this case, particularly if the restriction was added in a later version of the specification.
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

IDBSchemaRowset::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.

In general, for schema rowset column types that do not have a corresponding variant type, restrictions should be specified using VT_BSTR. For DBTYPE_GUID, the format of the BSTR string is "{<guid>}".

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.

The value of prgRestrictionSupport reflects on which of the columns listed as restriction columns the provider supports restrictions. The table of GUID restriction columns earlier in the overview of IDBSchemaRowset indicates the array of possible restriction columns to which this bitmask applies.

For example, the GUID table at the beginning of this reference entry 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 session 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 session 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 or prgSchemas 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 Microsoft® Data Access Components (MDAC) 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 IErrorRecords::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 are specified by the consumer in IErrorRecords::GetErrorInfo.

When to Call

Automation consumers call IErrorRecords::GetErrorInfo in the Automation DLL to get an IErrorInfo interface pointer on the OLE DB error object.

OLE DB consumers also call IErrorRecords::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 MDAC SDK. Otherwise, IErrorInfo::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 IErrorInfo::GetHelpFile is a null pointer), 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 MDAC 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 MDAC 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 MDAC 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 18: Programming Considerations.

When to Call

IErrorLookup is called by the code shipped in the Microsoft® Data Access Components (MDAC) 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 MDAC 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, 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), 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 IErrorLookup::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 IErrorLookup::ReleaseErrors to release all of this information in a single call.

For more information, see "Error Lookup Services" in Chapter 16: Errors.


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 16: Errors.

When to Implement

IErrorRecords is implemented by code in the Microsoft® Data Access Components (MDAC) 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 IErrorRecords::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 IErrorRecords::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 16: Errors.
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 MDAC SDK to ignore the provider's lookup service and use the description supplied in the MDAC 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 16: Errors.
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 16: Errors.
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 16: Errors.

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.

IErrorRecords::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 16: Errors.

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 IErrorRecords::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 IErrorRecords::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 16: Errors.

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 IErrorRecords::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 IErrorRecords::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 IErrorRecords::GetBasicErrorInfo, IErrorRecords::GetErrorInfo, IErrorRecords::GetErrorParameters, and IErrorRecords::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 IGetDataSource::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


IGetRow

Consumers use the IGetRow interface to get a row object or URL for a row in a rowset.

When to Implement

Providers implement this interface on rowset objects. IGetRow is an optional interface.

When to Call

A consumer calls IGetRow::GetRowFromHROW to create a row object to represent a row in a rowset. IGetRow::GetURLFromHROW returns the URL for the row.

Method | Description
GetRowFromHROW | Returns an interface pointer on a newly created row object associated with a row in a rowset.
GetURLFromHROW | Returns the URL for a row in a rowset.


IGetRow::GetRowFromHROW

Creates a new row object from a row in a rowset and returns an interface pointer on the newly created row object.

HRESULT GetRowFromHROW(
   IUnknown     **pUnkOuter,
   HROW          hRow,
   REFIID        riid,
   IUnknown    **ppUnk);
}

Parameters

pUnkOuter
[in]
If the row object is to be aggregated, pUnkOuter is an interface pointer to the controlling IUnknown. Otherwise, it is a null pointer.
hRow
[in]
The handle of the row from which the row object is to be created.

Caution The consumer must ensure that hRow contains a valid row handle because 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 must not terminate abnormally. For example, the provider might return DB_E_BADROWHANDLE or DB_E_DELETEDROW, or it might get data from a different row. The result of passing an invalid row handle in hRow is undefined.

riid
[in]
The IID of the interface for which to return a pointer on the newly created row object.
ppUnk
[out]
A pointer to memory in which to return an interface pointer on the row object. If IGetRow::GetRowFromHROW fails, the provider must attempt to set *ppUnk to a null pointer.

Return Code

S_OK
The row object was created, and the interface pointer was successfully returned.
DB_S_NOROWSPECIFICCOLUMNS
The row object does not contain any row-specific columns. Providers are encouraged (but not required) to return this warning as a performance hint to consumers. As a result, the consumer can avoid the overhead of calling IColumnsInfo::GetColumnInfo for each row and allocating column access structures for row columns.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
ppUnk was a null pointer.
E_NOINTERFACE
The row object did not support the interface specified in riid, or riid was IID_NULL.
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_NOAGGREGATION
pUnkOuter was not a null pointer, and riid was not IID_IUnknown.
DB_E_DELETEDROW
hRow referred to a pending delete row or to a row for which a deletion had already been transmitted to the data store.
DB_E_OBJECTOPEN
The provider would have to open a new connection to support the operation, and DBPROP_MULTIPLECONNECTIONS is set to VARIANT_FALSE.
E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called, and the row object is in a zombie state.

Comments

IGetRow::GetRowFromHROW is a synchronous operation that instantiates a new row object. If multiple row objects have been created from the same row in a rowset, the visibility of changes made to one of these row objects as seen by the others is governed by properties of the source rowset. (For more information, see "Visibility of Changes" in Chapter 5: Updating Data in Rowsets.)

If a row is obtained from a source rowset using this method, the updatability of the row is inherited from the source rowset.

Providers that implement IGetRow::GetRowFromHROW are allowed to implement the row object on the same data structure that implements the source rowset, instead of on a separately cached data structure. In this case, the provider should increment the reference count of the row handle in the rowset when returning the corresponding row object with IGetRow::GetRowFromHROW. Consumers may then receive DB_E_ROWSNOTRELEASED on IRowset::GetNextRows, and must release the row object before obtaining additional rows.


IGetRow::GetURLFromHROW

Given a handle of a row in a rowset, IGetRow::GetURLFromHROW returns the URL for that row.

HRESULT GetURLFromHROW(
   HROW         hRow,
   LPOLESTR    *ppwszURL);
}

Parameters

hRow
[in]
The row handle (HROW).

Caution The consumer must ensure that hRow contains a valid row handle because 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 must not terminate abnormally. For example, the provider might return DB_E_BADROWHANDLE or DB_E_DELETEDROW, or it might get data from a different row. The result of passing an invalid row handle in hRow is undefined.

ppwszURL
[out]
A pointer to provider-allocated memory in which to return the canonical URL for the row. The consumer is responsible for freeing this memory with IMalloc::Free when it no longer needs the URL. If IGetRow::GetURLFromHROW returns an error, *ppwszURL is set to NULL.

Return Code

S_OK
The URL was successfully returned.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
ppwszURL was a null pointer.
E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called, and the row object is in a zombie state.
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 to a row for which a deletion had already been transmitted to the data store.

Comments

None


IGetSession

The IGetSession interface returns an interface pointer on the session object within whose context the row object was created.

Every row object is created within the context of a session object. When a consumer creates a row object from a rowset by calling IGetRow::GetRowFromHROW, the row object exists within the context of the session associated with that rowset. When the consumer instantiates a row directly by calling a method such as IBindResource::Bind, the provider creates an implicit session object first and then instantiates the row object in the context of that session.

For many operations, such as creating a containing rowset, creating views, and transacting scoped operations, the consumer needs an interface on the session object within whose context the row exists. The IGetSession interface has a single method that returns the desired interface on the session object associated with a row.

When to Implement

IGetSession is a mandatory interface on row objects.

When to Call

A consumer calls IGetSession::GetSession to get an interface pointer on the session object associated with the row.

Method | Description
GetSession | Returns an interface pointer on the session object associated with the row.


IGetSession::GetSession

Returns an interface pointer on the session object associated with the row object.

HRESULT GetSession(
   REFIID        riid,
   IUnknown    **ppSession
);

Parameters

riid
[in]
The interface ID (IID) of the requested interface to return in ppSession.
ppSession
[out]
A pointer to memory in which to return the interface pointer on the session. If the provider does not have a session object as the context for the row, it sets *ppSession to a null pointer. If IGetSession::GetSession fails and ppSession is not a null pointer, the provider must attempt to set *ppSession to a null pointer.

Return Code

S_OK
The method succeeded.
DB_E_NOSOURCEOBJECT
The provider did not have a session object as the context for the row. 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.
E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called, and the object is in a zombie state.

Comments

If IGetSession::GetSession succeeds, it increments the reference count on the session object.


IGetSourceRow

The IGetSourceRow interface returns an interface on the row object within whose context a stream object was created.

When to Implement

A provider implements this interface on a stream object. This is an optional interface on stream objects. However, every stream object that is the default stream for a row object should support IGetSourceRow. This includes stream objects that are obtained by calling IBindResource::Bind, ICreateRow::CreateRow, or IRow::Open.

When to Call

An object calls the IGetSourceRow::GetSourceRow method on this interface to get an interface pointer on the row object associated with the stream.

Method | Description
GetSourceRow | Returns an interface pointer on the row object associated with the stream object.


IGetSourceRow::GetSourceRow

Returns an interface pointer on the row object associated with the stream object.

HRESULT GetSourceRow(
   REFIID        riid,
   IUnknown **   ppRow
);

Parameters

riid
[in]
The IID of the interface on which to return a pointer.
ppRow
[out]
A pointer to memory in which to return the interface pointer. If the provider does not have a row object as the context for the stream object, it sets *ppRow to a null pointer. If IGetSourceRow::GetSourceRow fails, the provider must attempt to set *ppRow to a null pointer.

Return Code

S_OK
The method succeeded.
DB_E_NOSOURCEOBJECT
The provider did not have a row object as the context for the stream object. Therefore, it set *ppRow to a null pointer.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
ppRow was a null pointer.
E_NOINTERFACE
The row object 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.

Comments

None.


IIndexDefinition

IIndexDefinition exposes simple methods to create and drop indexes from the data store.

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,
   DBORDINAL                 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, 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 Property Group" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 14: Properties.

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.

Note The contents of *ppIndexID might not exactly match the passed *pIndexID. The consumer should use *ppIndexID to identify the newly created index. If ppIndexID is NULL on input and the provider cannot create an index that exactly matches *pIndexID, IIndexDefinition::CreateIndex should fail with DB_E_BADINDEXID.

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 following:
  • 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_BADINDEXID
*pIndexID was an invalid index ID.
DB_E_DUPLICATEINDEXID
The specified index already exists in the data store.
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.
DB_E_NOCOLUMN
A column specified by *pColumnID in an element of rgIndexColumnDescs does not exist in the specified table.
DB_E_NOTABLE
The specified table does not exist in the data store.
DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to create the index.

Comments

If IIndexDefinition::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_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 store or did not apply to the specified table.
DB_E_NOTABLE
The specified table does not exist in the current data store.
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 IIndexDefinition::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 (row counts, rowset objects, or row objects) created by a command. For more information, see "Multiple Results" in Chapter 3: Commands.

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,
   DBRESULTFLAG  lResultFlag,
   REFIID        riid,
   DBROWCOUNT   *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.
lResultFlag
[in]
This flag is ignored when returning row counts. When returning result objects (for example, rowset or row objects), the values described in the following table apply.
Enum | Description
DBRESULTFLAG_DEFAULT | The type of the returned object is defined by riid or by properties set on the command object. If this is ambiguous, the provider should return a rowset. Prior to OLE DB 2.6, providers were required to return E_INVALIDARG when lResultFlag was not zero. Consumers should not pass nonzero values unless the provider is a 2.6 or later provider and has added support for lResultFlag.
DBRESULTFLAG_ROWSET | The consumer explicitly requests a rowset object.
DBRESULTFLAG_ROW | The consumer explicitly requests a row object.

Providers that support the return of row objects from ICommand::Execute and also support multiple results should support returning row objects from calls to IMultipleResults::GetResult, as described by the flag values in the preceding table.

As in ICommand::Execute, when returning a row object from IMultipleResults::GetResult, if the statement would have returned multiple rows, the provider is encouraged, but not required, to return DB_S_NOTSINGLETON.

If lResultFlag is not zero and the riid does not match the requested object type, the provider should return E_NOINTERFACE.

If lResultFlag is not zero and a command property (for example, DBPROP_IRow or DBPROP_IRowset) conflicts with the requested object type, the provider should return DB_S_ERRORSOCCURRED with a suitable DBPROPSTATUS value, such as DBPROPSTATUS_CONFLICTING if the property is optional, or DB_E_ERRORSOCCURRED if the property is required.

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, 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, *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 ICommand::Execute, or they 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 example, 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: Getting and Setting Data.
  • 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_NOTSINGLETON
The provider supports returning row objects on IMultipleResults::GetResult, and the consumer requested a row object but the result was not a singleton. A row object of the first row of the rowset is returned if the provider supports returning the row object. Because returning this result may be expensive, providers are not required to do so. If DB_S_ERRORSOCCURRED also applies to the execution of this method, it takes precedence over DB_S_NOTSINGLETON.
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 IMultipleResults::GetResult again returns information for the next result or returns 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
The value passed in for lResultFlag was invalid or not supported.
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 IMultipleResults::GetResult again returns information for the next result or returns 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: Getting and Setting Data.

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 provider is an OLE DB 1.0 or 1.1 provider that does not support aggregation of the object being created.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

DB_E_NOTFOUND
The provider supports the return of singleton row objects on a method that typically returns a rowset, a row was requested via riid or DBPROP_IRow, and no rows existed in the rowset.
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

IMultipleResults::GetResult returns the next in a series of multiple results from the provider. A result is returned for each separate statement in the command text or from each set of parameters passed to a command. If the next result is the result of a row-returning command or operation, 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 next result is the result of a non-row-returning command, such as an SQL INSERT statement, *ppRowset is set to NULL and *pcRowsAffected is set to the count of rows affected, if applicable.

Rowsets returned by IMultipleResults::GetResult have the properties set as defined by the command that created the multiple results object. These properties are identical for each result set. There is no way to set different properties for results of a multiple result.

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 IMultipleResults::GetResult before requesting the next result.

If ICommand::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.

When executing a command whose command text is a sequence of subcommands and also requesting a multiple results object, the provider must ensure that subcommands are executed in the order they appear in the command text and that any command whose results have been retrieved via IMultipleResults::GetResult has been executed.

It is provider-specific whether each of the subcommands is executed at ICommand::Execute or just-in-time for IMultipleResults::GetResult, and it is also provider-specific whether subcommands whose results have not been obtained have been executed.

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 IMultipleResults::GetResult. In this case, the next call to IMultipleResults::GetResult moves on to the next result or returns DB_S_NORESULT if no more results are available.

When IMultipleResults::GetResult returns an error, its behavior depends on the error that occurred, as in the following scenarios:

  • If IMultipleResults::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 IMultipleResults::GetResult returns the current result.
  • If IMultipleResults::GetResult returns any other error, the current result is lost. The next call to IMultipleResults::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,(IUnknown**)&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 store 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, 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 14: Properties.

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 IOpenRowset::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 might not be available on the object until asynchronous initialization of the rowset has completed. DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED.
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 following:
  • 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.
  • The property was not in the Rowset property group.

DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED. Once rowset population is complete, the consumer can see DB_S_ERRORSOCCURRED either by calling IDBAsynchStatus::GetStatus or by receiving IDBAsynchNotify::OnStop. Of course, the properties-in-error are not available, but for any properties that could not be set, the provider should report that status in the property array passed to IOpenRowset::OpenRowset (assuming that property failures can all be determined ahead of rowset population).

DB_S_ERRORSOCCURRED should be returned before DB_S_NOTSINGLETON because the status values can be checked, and providers are not required to return DB_S_NOTSINGLETON.

DB_S_NOTSINGLETON
The provider supports returning row objects on IOpenRowset::OpenRowset, and the consumer requested a row object but the result was not a singleton. A row object of the first row of the rowset is returned. Returning this result may be expensive, so providers are not required to do so. If DB_S_ERRORSOCCURRED also applies to the execution of this method, it takes precedence over DB_S_NOTSINGLETON.
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 rgPropertySets 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_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.
DB_E_NOAGGREGATION
pUnkOuter was not a null pointer, and the provider is an OLE DB 1.0 or 1.1 provider that does not support aggregation of the rowset object being created.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

DB_E_NOINDEX
The specified index does not exist in the current data store or did not apply to the specified table.

The provider does not support opening indexes through IOpenRowset.

DB_E_NOSTATISTIC
The specified statistic does not exist in the current data source or did not apply to the specified table, or it does not support a histogram.

Providers that do not support histograms do not have to return this code.

Providers that are unable to easily detect whether a histogram is available for the specified table are allowed to return S_OK and an empty histogram rowset.

DB_E_NOTABLE
The specified table does not exist in the data store.
DB_E_NOTFOUND
The provider supports row objects, a row was requested via riid or DBPROP_IRow, and no rows existed in the 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.
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.

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.

The threading model of the returned rowset is determined by the property DBPROP_ROWTHREADMODEL.

Programming Considerations

This method may be used to obtain a histogram rowset for a specified table to complement additional information found in the TABLE_STATISTICS schema rowset. Provider support for histogram rowsets is indicated by the DBPROP_TABLESTATISTICS and DBPROP_OPENROWSETSUPPORT properties. A conceptual overview of this feature is provided in the "Special-Purpose Rowsets" section of Chapter 1: Overview of OLE DB.

To retrieve a histogram rowset, the following parameters are set:

  • The pTableID parameter indicates the base table on which the histogram is defined.
  • The pIndexID parameter points to a DBID structure where the members are set as follows:
    • The uGUID member of the DBID structure is set to DBGUID_HISTOGRAM_ROWSET.
    • The pwszName member of the DBID structure is set to the name of the desired histogram statistic. The consumer and provider must have previously agreed to use this name. (This is the same name reported in the STATISTICS_NAME column of the TABLE_STATISTICS schema rowset.)

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: Rowsets.

When to Implement

IParentRowset is a mandatory interface on a hierarchical rowset.

When to Call

The consumer calls IParentRowset::GetChildRowset 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,
   DBORDINAL     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 Code

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 the provider is an OLE DB 1.0 or 1.1 provider that does not support aggregation of the rowset object being created.

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

IParentRowset::GetChildRowset can be called only once for each child rowset and if called, must be called before any calls to IRowsetInfo::GetReferencedRowset for that child. The pUnkOuter argument specified in IParentRowset::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 states, 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 IParentRowset::GetChildRowset for a child rowset that has already been obtained returns DB_E_OBJECTOPEN.

See Also

IRowsetInfo::GetReferencedRowset


IRegisterProvider

The root binder's IRegisterProvider interface manages the mapping of URL schemes and prefixes to OLE DB providers.

When to Implement

IRegisterProvider is an optional interface on binder objects. However, only the root binder implements it. It should not be implemented by provider binder objects.

When to Call

A provider calls IRegisterProvider methods, typically during installation, to register or unregister its ability to process a particular URL scheme or scheme and prefix.

Method | Description
GetURLMapping | Returns the CLSID of the provider binder object that is mapped to a particular URL scheme or scheme and prefix.
SetURLMapping | Registers the ability of a provider binder object to process a particular URL scheme or scheme and prefix.
UnregisterProvider | Unregisters one or more URL mappings for a provider.


IRegisterProvider::GetURLMapping

Returns the CLSID of the provider binder object that is mapped to a URL scheme or scheme and prefix.

HRESULT GetURLMapping(
   LPCOLESTR      pwszURL,
   DB_DWRESERVE   dwReserved,
   CLSID         *pclsidProvider
);

Parameters

pwszURL
[in]
The canonical URL scheme or scheme and prefix whose mapping is to be returned.
dwReserved
[in]
Reserved for future use; caller should set this to zero.
pclsidProvider
[out]
A pointer to the CLSID of the provider binder object that is mapped to this URL scheme or scheme and prefix. *pclsidProvider is set to DB_NULLGUID if an error code is returned.

Return Code

S_OK
The method succeeded. pclsidProvider points to the CLSID of the provider binder object.
S_FALSE
No provider binder object was mapped to this URL scheme or scheme and prefix. *pclsidProvider is set to DB_NULLGUID.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pwszURL or pclsidProvider was a null pointer.

dwReserved was not 0.

Comments

Each URL scheme and prefix combination may be mapped to at most a single OLE DB provider binder object.

Note "Provider Binder Objects," in Chapter 8, discusses case-sensitivity issues for URL-to-provider mapping.


IRegisterProvider::SetURLMapping

Registers the ability of a provider binder object to process a particular URL scheme or scheme and prefix.

HRESULT SetURLMapping(
   LPCOLESTR      pwszURL,
   DB_DWRESERVE   dwReserved,
   REFCLSID       rclsidProvider
);

Parameters

pwszURL
[in]
The canonical URL scheme or scheme and prefix to be mapped.
dwReserved
[in]
Reserved for future use; providers should set this to zero.
rclsidProvider
[in]
A reference to the CLSID of the provider binder object, which is to be mapped to this URL scheme or scheme and prefix.

Return Code

S_OK
The method succeeded.
DB_E_RESOURCEEXISTS
The URL scheme or scheme and prefix combination was already mapped to a different provider binder object.
DB_SEC_E_PERMISSIONDENIED
The caller did not have permission to register this URL mapping.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pwszURL was a null pointer or pointed to an invalid URL.

dwReserved was not 0.

Comments

Each URL scheme or scheme and prefix combination may be mapped to at most a single OLE DB provider. A URL scheme and prefix may be mapped to the same provider multiple times; however, the root binder should return S_OK on each call.

Note "Provider Binder Objects" in Chapter 8, discusses case-sensitivity issues for URL-to-provider mapping.

IRegisterProvider::SetURLMapping uses InternetCrackUrl to validate URLs for mapping. In addition to the URLs accepted by InternetCrackUrl, IRegisterProvider::SetURLMapping accepts scheme-only URLs such as "http", "http:", "myscheme" and "myscheme:".

For more information about InternetCrackUrl, see the MSDN Library documentation.


IRegisterProvider::UnregisterProvider

Unregisters one or more URL mappings for a provider binder object.

HRESULT UnregisterProvider(
   LPCOLESTR      pwszURL,
   DB_DWRESERVE   dwReserved,
   REFCLSID       rclsidProvider
);

Parameters

pwszURL
[in]
The canonical URL scheme or scheme and prefix to be unmapped for the provider designated by rclsidProvider. If pwszURL is a null pointer, all URL scheme or scheme and prefix combinations are unmapped for this provider.
dwReserved
[in]
Reserved for future use; providers should set this to zero.
rclsidProvider
[in]
A reference to the CLSID of the provider binder object for which the root binder will unregister the named URL mapping. This CLSID must be provided and is never ignored.

Return Code

S_OK
The method succeeded.
S_FALSE
*pwszURL is a null pointer, and no scheme/prefix combinations were mapped to rclsidProvider.

*pwszURL was specified, and no matching scheme/prefix combinations were found or were mapped to rclsidProvider.

DB_SEC_E_PERMISSIONDENIED
The caller did not have permission to unregister URL mappings.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pwszURL was not a well-formed or canonical URL.

dwReserved was not 0.

Comments

If pwszURL is a null pointer, any URL mappings for the OLE DB provider binder object named by rclsidProvider are removed.

If pwszURL is not a null pointer, the scheme or scheme and prefix combination named by *pwszURL is unmapped.

IRegisterProvider::UnregisterProvider is not transacted. If the root binder encounters a failure, it is possible that some (but not all) mappings might have been removed.


IRow

The IRow interface allows a consumer to read column data from a row object or to obtain the source rowset of the row object.

When to Implement

IRow is a mandatory interface on row objects.

When to Call

Consumers call IRow::GetColumns to read column values for a row object, IRow::GetSourceRowset to get an interface pointer on the row object's source rowset, and IRow::Open to open an object-valued column.

Method | Description
GetColumns | Gets multiple columns from the row.
GetSourceRowset | Returns an interface pointer on the rowset object containing the row from which the row object was created.
Open | Opens an object-valued column.


IRow::GetColumns

Retrieves the values of one or more named columns from the row object.

HRESULT GetColumns(
   DBORDINAL        cColumns,
   DBCOLUMNACCESS   rgColumns[ ]
);

Parameters

cColumns
[in]
Count of columns specified in the rgColumns array. If cColumns is zero, no columns are created or updated.
rgColumns
[in/out]
A caller-supplied array of DBCOLUMNACCESS structures. The DBCOLUMNACCESS structure is defined as follows:
typedef struct tagDBCOLUMNACCESS {
   void          *pData;
   DBID           columnid;
   DBLENGTH       cbDataLen;
   DBSTATUS       dwStatus;
   DBLENGTH       cbMaxLen;
   DB_DWRESERVE   dwReserved;
   DBTYPE         wType;
   BYTE           bPrecision;
   BYTE           bScale;
} DBCOLUMNACCESS;
pData
Pointer of type wType to caller-allocated storage. On return, the area of storage contains the value stored in the column specified by columnid. The provider should attempt to coerce the column value to type wType. If wType is DBTYPE_VARIANT, the provider is responsible for allocating any variable-length storage pointed to by the VARIANT. If the caller passes a null pointer, the provider returns only the untruncated length (cbDataLen) and status (dwStatus) and does not return a data value.

If the row is in immediate mode and a row-specific columns has been deleted, the provider returns DBSTATUS_E_DOESNOTEXIST. If the row is in deferred mode and a row-specific column has been deleted, the provider returns a null value and DBSTATUS_S_ISNULL.

For more information, see Chapter 6: Getting and Setting Data.

columnid
columnid is a DBID that identifies the column to be accessed. columnid values must be unique within a row.
cbDataLen
The returned length of the value contained in *pData. If the length of the column value is greater than cbMaxLen, the provider truncates the data to fit the buffer, returns the full data size in cbDataLen, and sets dwStatus to DBSTATUS_S_TRUNCATED.
dwStatus
A DBSTATUS status field set by the provider on return, indicating whether pData or some other value should be used. On return, dwStatus indicates whether the field was successfully retrieved and provides other information about this column. For more information about status values, see "Status" in Chapter 6: Getting and Setting Data.
cbMaxLen
The maximum length of the caller-initialized memory pointed to by pData. For more information on cbMaxLen in the binding structure, see "DBBINDING Structures" in Chapter 6: Getting and Setting Data.
dwReserved
Reserved. Consumers should set this parameter to zero.
wType
On input, wType identifies the data type requested by the consumer. The provider attempts to convert the value from the type of the column to this type.

wType should not change on return; if the provider could not convert, a status of DBSTATUS_E_CANTCONVERTVALUE would be returned.

bPrecision
The maximum precision to use when getting data and wType is DBTYPE_NUMERIC. bPrecision is ignored when setting data or if wType is not DBTYPE_NUMERIC. For more information, see "Conversions Involving DBTYPE_NUMERIC or DBTYPE_DECIMAL" in Appendix A: Data Types.
bScale
The scale to use when getting data and wType is DBTYPE_NUMERIC or DBTYPE_DECIMAL. This is ignored when setting data. It is also ignored if wType is not DBTYPE_NUMERIC or DBTYPE_DECIMAL. For more information, see "Conversions Involving DBTYPE_NUMERIC or DBTYPE_DECIMAL" in Appendix A: Data Types.

Return Code

S_OK
The provider successfully retrieved all of the columns.
E_INVALIDARG
rgColumns was a null pointer, and cColumns was not zero.
DB_SEC_E_PERMISSIONDENIED
The provider was unable to access any of the columns.
DB_E_ERRORSOCCURRED
The provider was unable to retrieve any of the specified columns. The caller should check dwStatus of each element of rgColumns to determine why each column was not retrieved.
DB_S_ERRORSOCCURRED
The provider was unable to retrieve some of the specified columns. The caller should check dwStatus of each element of rgColumns to determine whether a column was retrieved.
DB_E_DELETEDROW
The row was either a pending delete row or a row for which a deletion had already been transmitted to the data store.
E_FAIL
A provider-specific error occurred.
E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called, and the object is in a zombie state.

Comments

IRow::GetColumns retrieves as many of the columns specified in the rgColumns array as can be found on the row object. As long as at least one of the requested columns is retrieved, a failure to retrieve one or more of the remaining columns is not an error. Instead, the caller should examine the corresponding status field to ensure that a column was retrieved. A return value of DBSTATUS_E_DOESNOTEXIST indicates that a named column does not exist. When none of the requested columns exist, the provider should return DB_E_ERRORSOCCURRED and not set any of the *pData values pointed to by the elements of rgColumns. If any other error is returned, no column values are retrieved.

If a row is obtained from a parent row using this method, the updatability of the row is inherited from the parent row.

If a column is of type DBTYPE_IDISPATCH or DBTYPE_IUNKNOWN or is a DBTYPE_VARIANT containing an object value, IRow::GetColumns returns an IDispatch or IUnknown pointer, as appropriate. Callers that wish to request another interface supported by the object should call IRow::Open.

If a row object has a source rowset and changes to the source rowset are deferred, changes to all columns of the row object are also deferred. Therefore, IRow::GetColumns returns the current value of all row columns and the original values of row columns that are not part of the source rowset are unavailable.

If the provider supports type conversions to or from the data types described by the DBCOLUMNINFO structure, it should implement IConvertType on row objects. This interface is used to report whether a given conversion is supported. Providers are strongly encouraged to delegate data type conversions to the OLE DB Data Conversion library (Oledb32.dll).

For more information, see "Data Conversion Library" in Appendix H: Data Type Conversion Interfaces.


IRow::GetSourceRowset

Returns an interface pointer on the rowset that contains the row represented by a row object.

HRESULT GetSourceRowset(
   REFIID        riid,
   IUnknown    **ppRowset,
   HROW         *phRow
);

Parameters

riid
[in]
The interface ID (IID) of the requested interface to return in ppRowset. This argument is ignored if ppRowset is a null pointer.
ppRowset
[out]
A pointer to memory in which to return the interface for the rowset. If the caller sets ppRowset to a null pointer, no rowset is returned. If the provider does not have a rowset object as the source for the row, it sets *ppRowset to a null pointer. If IRow::GetSourceRowset fails, the provider must attempt to set *ppRowset to a null pointer.
phRow
[out]
A pointer to memory in which to return the row handle of this row within the source rowset. If phRow is a null pointer, no row handle is returned. If the provider does not have a rowset object as the source for the row, it sets *phRow to NULL. If IRow::GetSourceRowset fails, the provider must attempt to set *phRow to NULL.

Return Code

S_OK
The method succeeded.
DB_E_DELETEDROW
The row was either a pending delete row or a row for which a deletion had already been transmitted to the data store.
DB_E_NOSOURCEOBJECT
The provider did not have a rowset object as the source for the row. Therefore, it set *ppRowset and *phRow to NULL.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
ppRowSet and phRow were null pointers.
E_NOINTERFACE
The 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.

Comments

By providing ppRowset, phRow, or both, the caller may request the source rowset, the row handle, or both. The provider increments the reference count of any row handle that is returned to the consumer.

If the row object was created as a result of executing a command or if it was instantiated directly by calling IBindResource::Bind or ICreateRow::CreateRow, no source rowset exists and the provider must return DB_E_NOSOURCEOBJECT.

IRow::GetSourceRowset returns an interface pointer on the rowset from which the row object was created. The consumer can call IRowset::GetData using the row handle returned in *phRow. If the consumer held a reference to the source rowset, IRow::GetSourceRowset does not affect the next fetch position of the rowset. If the consumer did not hold a reference to the source rowset, it is provider-specific whether or not that previous next fetch position is returned. However, the consumer can call IRowset::RestartPosition to reset it to a known position.


IRow::Open

Returns an interface pointer to be used to access an object-valued column. IRow::Open will generally return a rowset, row, or stream object, allowing the provider to create the appropriate object for tabular columns and streams. The returned object inherits the access privileges from the original flags used for binding to the row. IRow::Open also can return an interface pointer to a child rowset when called on a chapter-valued column.

HRESULT Open(
   IUnknown     *pUnkOuter,
   DBID         *pColumnID,
   REFGUID       rguidColumnType,
   DWORD         dwFlags,
   REFIID        riid,
   Iunknown    **ppUnk
);

Parameters

pUnkOuter
[in]
The controlling IUnknown if the returned interface is to be aggregated; otherwise, a null pointer.
pColumnID
[in]
A pointer to a DBID containing the name of the column to open. Must not be a null pointer. When a chapter-valued column, rguidColumnType must be DBGUID_ROWSET and riid should be a rowset interface.
rguidColumnType
[in]
A pointer to a GUID that identifies the type of object to be opened from this column. If the GUID does not match the column type, DB_E_OBJECTMISMATCH is returned. Possible values are described in the following table.
Object type | Description
DBGUID_STREAM | Column contains a stream of binary data. Use IStream or ISequentialStream.
DBGUID_ROW | Column contains a nested collection of columns. Use IRow.
DBGUID_ROWSET | Column contains a nested rowset. Use IRowset.
GUID_NULL | Column contains a COM object. Open the object as its native type, and return the interface specified by riid.
dwFlags
[in]
Reserved for flags to control the open operation. Must be zero.
riid
[in]
Interface ID of the interface pointer to be returned.
ppUnk
[out]
A pointer to memory in which to return the requested interface pointer. If an error code is returned and ppUnk is not a null pointer, *ppUnk should be set to NULL.

Return Code

S_OK
The object was successfully opened. The caller becomes responsible for releasing the returned interface pointer.
DB_E_BADCOLUMNID
pColumnID was an invalid DBID or a shortcut DBID, such as DBROWCOL_DEFAULTSTREAM, that does not exist on this row.

Note For more information about DBROWCOL_DEFAULTSTREAM, see "Special Row Column DBIDs" in Chapter 9: Row and Stream Objects.

DB_E_COLUMNUNAVAILABLE
Requested column is valid, but could not be retrieved. The caller should check that the DBPROP_ACCESSORDER property is compatible with this operation.
DB_E_DELETEDROW
The row is either a pending delete row or a row for which a deletion had already been transmitted to the data store.
DB_E_NOAGGREGATION
pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

The provider does not support aggregation.

The object has already been created.

DB_E_NOTFOUND
The data value of this column is NULL.
DB_E_OBJECTMISMATCH
rguidColumnType pointed to a GUID that did not match the object type of this column.

pcolumnID is a chapter-valued column, and rguidColumnType is not DBGUID_ROWSET.

DB_E_OBJECTOPEN
The provider can support only a single open storage object at a time (DBPROP_MULTIPLESTORAGEOBJECTS = VARIANT_FALSE) and already has a storage object open.

The provider would have to open a new connection to support the operation, and DBPROP_MULTIPLECONNECTIONS is set to VARIANT_FALSE.

E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pColumnID or ppUnk was a null pointer.
E_NOINTERFACE
The object does not support the interface requested in riid, or riid was IID_NULL.
E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called, and the object is in a zombie state.

Comments

IRow::Open is a synchronous operation.

The DBBINDURLFLAG_* flags used in IBindResource for read and write access to open the row are carried through to the access privileges for columns. For example, a row opened with DBBINDURLFLAG_READ will not allow a column to be updated.

Note For more information about DBBINDURLFLAG_* flags, see the reference entry for IBindResource::Bind.

For columns that are themselves rowsets, it is suggested that the object returned has a well-known list of supported interfaces. IRowset, IRowsetInfo, IRowsetChange (only for write access), IAccessor, and IColumnsInfo are the supported interfaces.

If pColumnID specifies a shortcut data type identifier (DBID) such as DBROWCOL_DEFAULTSTREAM or DBROWCOL_URL and the row object does not contain these columns, the provider returns DB_E_BADCOLUMNID.


IRowChange

The IRowChange interface allows a consumer to quickly set columns of a row.

When to Implement

IRowChange is an optional interface on row objects. Providers should implement IRowChange if they want consumers to set columns of a row object.

When to Call

Consumers should call methods of IRowChange when they want to set column values of a row.

Method | Description
SetColumns | Sets multiple column values of the row.


IRowChange::SetColumns

Sets the values of one or more named columns of a row object.

HRESULT SetColumns(
   DBORDINAL        cColumns,
   DBCOLUMNACCESS   rgColumns[ ]
);

Parameters

cColumns
[in]
Count of columns specified in the rgColumns array. If cColumns is zero, no columns are set.
rgColumns[ ]
[in/out]
A caller-supplied array of DBCOLUMNACCESS structures. The DBCOLUMNACCESS structure is described in the reference entry for IRow::GetColumns.

For setting column values, the elements of each DBCOLUMNACCESS structure are used in the manner described in the following table.

Element | Description
columnid | Unique DBID that identifies the column to be accessed.
wType | Identifies the type of the value pointed to by pData.
pData Caller-allocated pointer to storage of the data type specified by wType. On input, the area pointed to contains the value of the column specified by columnid. The caller must allocate and initialize the area of storage pointed to by pData. The provider should attempt to coerce the value from wType to the underlying value of the column. If wType is DBTYPE_VARIANT, the provider is responsible for allocating any variable-length storage pointed to by the VARIANT.

Note For more information about data type indicators (DBTYPE values), see "Type Indicators" in Appendix A: Data Types.
cbMaxLen | The maximum length of the caller-initialized memory pointed to by pData. The provider checks the length in bytes of variable-length data types against cbMaxLen. If the length is greater than cbMaxLen, this is an error and the provider sets the status to DBSTATUS_E_CANTCONVERTVALUE.
bPrecision | Indicates the precision of the value stored in *pData for data types equiring precision.
bScale | Indicates the scale of the value stored in *pData for data types requiring scale.
dwStatus On input, dwStatus indicates whether pData or some other value should be used. When value DBSTATUS_S_ISNULL is used to set the column to null, other fields (wType, pData, cbDataLen) are ignored. On return, dwStatus indicates whether the field was successfully set. The following status values may apply when setting column values. (For more information, see "Status" in Chapter 6; Getting and Setting Data.)
  • DBSTATUS_S_OK—The column value was set.
  • DBSTATUS_E_DOESNOTEXIST—The column does not exist on this row.
  • DBSTATUS_E_INTEGRITYVIOLATION—The column is a calculated or derived value and cannot be directly modified.
  • DBSTATUS_E_PERMISSIONDENIED—The column is defined as read-only, or the consumer has bound to the row in read-only mode.
  • DBSTATUS_E_UNAVAILABLE—On input, the status was DBSTATUS_S_OK, IColumnsInfo::GetColumnInfo did not return DBCOLUMNFLAGS_ISLONG for this column, and pData was a null pointer.
cbDataLen | On input, the length of the data value pointed to by pData. Ignored for fixed-length types.

Return Code

S_OK
The provider successfully set all of the columns.
E_INVALIDARG
cColumns was not zero, and rgColumns was a null pointer.
E_OUTOFMEMORY
The provider was unable to allocate sufficient memory for this operation.
DB_S_ERRORSOCCURRED
The provider was able to set at least one column but was unable to set at least one column. The caller should examine dwStatus of each element of rgColumns to determine whether, and why, an individual column was not set.
DB_E_DELETEDROW
The row is either a pending delete row or a row for which a deletion had already been transmitted to the data store.
DB_E_ERRORSOCCURRED
An error occurred while setting data for one or more columns, and data was not successfully set for any columns. The caller should examine dwStatus of each element of rgColumns to determine why each individual column was not set.
DB_E_NEWLYINSERTED
The value of the DBPROP_CHANGEINSERTEDROWS property on the source rowset was VARIANT_FALSE, and the method was called on a row for which the insertion has been transmitted to the data store.
DB_SEC_E_PERMISSIONDENIED
The provider was unable to set any columns because of a permission failure.
E_FAIL
A provider-specific error occurred. No columns were set.
E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called, and the object is in a zombie state.

Comments

IRowChange::SetColumns sets the columns named in columnid of the rgColumns array with the values pointed to by pData.

If a named column exists in the row, the provider attempts to coerce the value pointed to by pData from wType to the underlying type of the column.

If a named column does not exist, the provider should return either DB_S_ERRORSOCCURRED (if at least one column was set) or DB_E_ERRORSOCCURRED (if no columns were set) and set the corresponding status value dwStatus to a DBSTATUS value of DBSTATUS_E_DOESNOTEXIST.

Providers are not required to support column creation. If a provider does support column creation, it should implement IRowSchemaChange.

IRowChange::SetColumns may not change the underlying type of a column. The provider should attempt to coerce the value pointed to by pData to the defined type of the column. If the provider cannot perform the coercion, the provider should return either DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED and set the dwStatus to DBSTATUS_E_CANTCONVERTVALUE.

If a row does not have a source rowset or if changes to the source rowset are immediate, changes to all row column values are also immediate. If a row has a source rowset and changes to this rowset are deferred, changes to all row column values are also deferred. IRowsetUpdate::Update commits pending changes for row columns as well as for rowset columns.

If the provider supports type conversions to or from the row types described by the DBCOLUMNINFO structure, it should implement IConvertType on the row. This interface is used to report whether a given conversion is supported. It is recommended that providers delegate conversions to the OLE DB Data Conversion library, Oledb32.dll. For more information, see "Data Conversion Library" in Appendix H: Data Type Conversion Interfaces.

When setting row column data values from a consumer's storage object, the provider is responsible for releasing the storage object. When the provider has finished using the consumer's storage object, it calls IUnknown::Release to release the pointer. If the consumer wants to ensure access to its storage object after IRowChange::SetColumns returns, it must call IUnknown::AddRef on the pointer before calling SetColumns.


IRowSchemaChange : IRowChange

The IRowSchemaChange interface allows a consumer to create, set, or delete columns of a row object.

When to Implement

IRowSchemaChange is an optional interface on row objects. Providers should implement IRowSchemaChange if they want consumers to create, set, or delete columns of row objects.

When to Call

Consumers call methods of IRowSchemaChange to create, set, or delete columns of a row object.

Method | Description
AddColumns | Creates and sets one or more columns of a row object.
DeleteColumns | Deletes one or more columns of a row.
(Inherited) | All methods of IRowChange.


IRowSchemaChange::AddColumns

Creates or sets the values of one or more named columns of a row object. If the columns do not already exist, they are created.

HRESULT AddColumns(
   DBORDINAL            cColumns,
   const DBCOLUMNINFO   rgNewColumnInfo[ ],
   DBCOLUMNACCESS       rgColumns[ ]
);

Parameters

cColumns
[in]
Count of columns specified in the rgNewColumnInfo array. If cColumns is zero, no columns are created or set.
rgNewColumnInfo
[in]
A consumer-allocated array of cColumns DBCOLUMNINFO structures that define the additional columns to be added to the row object. If cColumns is zero, rgNewColumnInfo is ignored. The order of columns in rgColumns must match the order of columns in rgNewColumnInfo.

Note For information about DBCOLUMNINFO structures, see IColumnsInfo::GetColumnInfo.

The following table lists special instructions for defining row columns using DBCOLUMNINFO.

Element | Instructions for row columns
pwszName | Ignored on input when creating columns with AddColumns.
iOrdinal | Ignored on input when creating columns with AddColumns.
dwFlags A bitmask that describes consumer-specified row column characteristics. The DBCOLUMNFLAGS enumerated type specifies the bits in the bitmask, which are described in the reference entry for IColumnsInfo::GetColumnInfo. The following flag values may apply when creating row columns:
DBCOLUMNFLAGS_CACHEDEFERRED
DBCOLUMNFLAGS_ISFIXEDLENGTH
DBCOLUMNFLAGS_ISLONG
DBCOLUMNFLAGS_ISNULLABLE
DBCOLUMNFLAGS_ISROWVER
DBCOLUMNFLAGS_MAYBENULL
DBCOLUMNFLAGS_MAYDEFER
DBCOLUMNFLAGS_SCALEISNEGATIVE
DBCOLUMNFLAGS_WRITE
DBCOLUMNFLAGS_WRITEUNKNOWN
ulColumnSize | Minimum size required to store the consumer's largest data for this column. For fixed-length data types, this is the size of the data type in bytes. For variable-length data types, this is the maximum number of bytes (for DBTYPE_BYTES) or characters (for DBTYPE_STR or DBTYPE_WSTR). For more information, see the description of DBCOLUMNINFO in the reference entry for IColumnsInfo::GetColumnInfo.
wType | Requested DBTYPE data type for this column.
bPrecision | Maximum precision of the column.
bScale | Number of digits to the right of the decimal point.
columnid | Unique DBID used to name this row column. For example, if columns are named (eKind is DBKIND_NAME), uName.pwszName points to the column name.
rgColumns
[in/out]
An optional consumer-allocated array of cColumns DBCOLUMNACCESS structures.

Note The DBCOLUMNACCESS structure is described in the reference entry for IRow::GetColumns.

The order of columns in rgColumns must match the order of columns in rgNewColumnInfo. If rgColumns is a null pointer, new columns are created but no values are set (except for default values defined by the provider).

For setting column values, the elements of each DBCOLUMNACCESS structure are used in the manner described in the following table.

Element | Description
columnid | This element is ignored by AddColumns. The column DBID is designated by the columnid element of the corresponding member of rgNewColumnInfo. (This is done so that consumers do not need to allocate two sets of DBIDs.)
wType | Identifies the type of the value pointed to by pData.
pData | Caller-allocated pointer to storage of the DBTYPE defined by wType. On input, the area pointed to contains the value of the column specified by the columnid element of rgNewColumnInfo. The caller must allocate and initialize the area of storage pointed to by pData. The provider should attempt to coerce the value from wType to the underlying value of the column. If wType is DBTYPE_VARIANT, the provider is responsible for allocating any variable-length storage pointed to by the VARIANT.
cbMaxLen | The maximum length of the caller-initialized memory pointed to by pData. The provider checks the length in bytes of variable-length data types against cbMaxLen. If the length is greater than cbMaxLen, this is an error and the provider sets the status to DBSTATUS_E_CANTCONVERTVALUE.
bPrecision | Indicates the precision of the value stored in *pData for data types requiring precision.
bScale | Indicates the scale of the value stored in *pData for data types requiring scale.
dwStatus On input, dwStatus indicates whether pData or some other value should be used. All status values used in OLE DB for rowset columns apply to row columns. On input, if dwStatus is DBSTATUS_S_DEFAULT or DBSTATUS_S_IGNORE, the provider skips this column when setting data. On return, dwStatus indicates whether the field was successfully set. The following status values may apply when updating column values. Standard status values when setting data do apply in addition to those listed below. (For more information, see "Status" in Chapter 6: Getting and Setting Data.)
  • DBSTATUS_S_OK—The column value was set.
  • DBSTATUS_S_ALREADYEXISTS—The column already exists on the row. The new column metadata was ignored.

    Note DBSTATUS_S_ALREADYEXISTS supersedes other applicable status values on output. If the column already exists and as long as any column value was set successfully, the provider returns this value instead of others that apply (such as DBSTATUS_S_ISNULL).

  • DBSTATUS_S_DEFAULT—The provider skips this column when setting data.
  • DBSTATUS_S_IGNORE—The provider skips this column when setting data.
  • DBSTATUS_S_ISNULL—A null value was sent to the provider.
  • DBSTATUS_E_CANTCONVERTVALUE—The provider was unable to match the type or metadata of the column definition with a type supported by the provider.
  • DBSTATUS_E_CANTCREATE—The provider exceeded a limitation (such as the number of columns allowed) and was unable to add the column, or the provider did not create the column because it did not support a requested column flag in dwFlags of DBCOLUMNINFO.
  • DBSTATUS_E_INTEGRITYVIOLATION—The column is a calculated or derived value and cannot be directly modified.
  • DBSTATUS_E_PERMISSIONDENIED—The consumer did not have sufficient permission to add the column.
  • DBSTATUS_E_UNAVAILABLE—On input, the status was DBSTATUS_S_OK, IColumnsInfo::GetColumnInfo did not return DBCOLUMNFLAGS_ISLONG for this column, and pData was a null pointer.
cbDataLen | On input, the length of the data value pointed to by pData. Ignored for fixed-length types.

Return Code

S_OK
The provider successfully created or set the values of all of the columns.
E_INVALIDARG
cColumns was not zero, and rgNewColumnInfo was a null pointer.
E_OUTOFMEMORY
The provider was unable to allocate sufficient memory for this operation.
DB_S_ERRORSOCCURRED
The provider was able to add or set the value of at least one column but was unable to do so for at least one column. The caller should examine the dwStatus value of each element of rgColumns to determine whether, and why, an individual column was not set.
DB_E_DELETEDROW
The row is either a pending delete row or a row for which a deletion had already been transmitted to the data store.
DB_E_ERRORSOCCURRED
An error occurred while setting data for one or more columns, and data was not successfully set for any columns. The caller should examine the dwStatus value of each element of rgColumns to determine whether, and why, an individual column was not set.
DB_E_NEWLYINSERTED
DBPROP_CHANGEINSERTEDROWS on the source rowset was VARIANT_FALSE, and the method was called on a row for which the insertion has been transmitted to the data store.
DB_E_NOTSUPPORTED
The provider does not support this method.
DB_SEC_E_PERMISSIONDENIED
The provider was unable to set any columns due to a permission failure.
E_FAIL
A provider-specific error occurred. No columns were added.
E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called, and the object is in a zombie state.

Comments

IRowSchemaChange::AddColumns sets the columns named by columnid in the rgNewColumnInfo array with the values pointed to by pData.

The provider first attempts to create new columns using the column metadata supplied in the elements of rgNewColumnInfo. If rgNewColumnInfo contains an element for a column that already exists, the provider sets dwStatus to DBSTATUS_S_ALREADYEXISTS and ignores the column metadata provided in rgNewColumnInfo. The attributes of an existing column cannot be altered. Consumers are not required to know the data types or maximum column sizes of the underlying data store's types. Using ulColumnSize, wType, and dwFlags, the provider determines the appropriate data type, maximum column size (which may exceed ulColumnSize), and other column attributes of the new column. Once the column is created, the consumer can call IColumnsInfo::GetColumnInfo to obtain the defined maximum length of the created column. If the provider is unable to create a column definition, it returns the appropriate status code for that column and does not attempt to set the column value. A failure creating or setting a column has no direct impact on the creation or setting of other row columns in the method call. Providers are not required to support all of the column flags listed for dwFlags and are allowed to ignore flags they don't support. If the provider fails column creation due to an unsupported flag, it should return a status of DBSTATUS_E_CANTCREATE.

Next the provider sets the row column values with the values supplied by the caller in rgColumns. If rgColumns is a null pointer, the provider creates the columns but does not set any column values. This behavior is identical to that provided by IRowChange::SetColumns. If rgColumns is supplied and if the dwStatus value passed on input is bad, the provider returns DBSTATUS_E_BADSTATUS. If the column is successfully created and set, dwStatus is set to either DBSTATUS_S_OK or DBSTATUS_S_ISNULL.

If all columns were created and set, the provider returns S_OK. If no columns were created and set, the provider returns DB_E_ERRORSOCCURRED. If at least one column was created and set and at least one column failed to be created and set, the provider returns DB_S_ERRORSOCCURRED. If a column is added but the value is not set, the method could return either of two return codes. DB_S_ERRORSOCCURRED means at least one column was added and set successfully. DB_E_ERRORSOCCURRED means no columns were added and set successfully. The dwStatus value indicates the problem, which may involve the add (DBSTATUS_E_ALREADYEXISTS) or the set (DBSTATUS_E_BADSTATUS or other) value. The dwStatus value for each column indicates which columns failed to be created or set.

The creation of new columns occurs immediately, even if the row object is in deferred mode. Changes to the column values depend on the deferred mode of the row object. If a row object does not have a source rowset or if changes to the source rowset are immediate, changes to all row column values are also immediate. If a row object has a source rowset and changes to this rowset are deferred, changes to all row object column values are also deferred. IRowsetUpdate::Update commits pending changes for row column values as well as for rowset columns. If the row object has a source rowset, newly created row columns will not appear in this rowset.

If the provider supports type conversions to or from the data types described by the DBCOLUMNINFO structure, it should implement IConvertType on row objects. This interface is used to report whether a given conversion is supported. Providers are strongly encouraged to delegate data type conversions to the OLE DB Data Conversion library (Oledb32.dll). For more information, see "Data Conversion Library" in Appendix H: Data Type Conversion Interfaces.

If a provider also supports the adding or modification of columns to base tables using IAlterTable, ITableCreation, ITableDefinition or ITableDefinitionWithConstraints, it needs to maintain the uniqueness of base table column IDs and row-specific column IDs. Providers should reject attempts to create columns with non-unique column IDs and return either DB_E_DUPLICATECOLUMNID or DBSTATUS_ALREADYEXISTS.

When setting row column data values from a consumer's storage object, the provider is responsible for releasing the storage object. When the provider has finished using the consumer's storage object, it calls IUnknown::Release to release the pointer. If the consumer wants to ensure access to its storage object after AddColumns returns, it must call IUnknown::AddRef on the pointer before calling AddColumns.


IRowSchemaChange::DeleteColumns

Deletes one or more named columns from a row. For row-specific columns of a row that is in immediate update mode, this is an immediate schema operation.

HRESULT DeleteColumns(
   DBORDINAL    cColumns,
   const DBID   rgColumnIDs[ ],
   DBSTATUS     rgdwStatus[ ]
);

Parameters

cColumns
[in]
Count of column names specified in the rgColumnIDs array. If cColumns is zero, no column values are deleted.
rgColumnIDs[ ]
[in]
Consumer-allocated array of cColumns names of the columns to be deleted. It is not necessary to specify columns in any particular order in the array. If cColumns is zero, rgColumnIDs is ignored. If cColumns is not zero and rgColumnIDs is a null pointer, the provider returns E_INVALIDARG. It is not an error if a column name is specified more than once.
rgdwStatus[ ]
[in/out]
Optional consumer-allocated array of cColumns status fields indicating whether the value of the corresponding element of rgColumnIDs was deleted. Consumers not interested in receiving status may set rgdwStatus to null. The status fields described in the following table apply to the deletion of column values.
Status field | Description
DBSTATUS_S_OK | Indicates that the column value was deleted.
DBROWSTATUS_S_ROWSETCOLUMN | Providers may return this status flag when asked to delete a rowset column from a row. The value is actually nulled, but the column remains in the row and rowset.
DBSTATUS_E_DOESNOTEXIST | Indicates that the column does not exist on this row.
DBSTATUS_E_PERMISSIONDENIED | The consumer did not have sufficient permission to delete the column.

The order of status elements must match the order of the column names in rgColumnIDs.

For more information, see "Status" in Chapter 6: Getting and Setting Data.

Return Code

S_OK
The provider successfully deleted values for all of the requested column names.
E_INVALIDARG
cColumns was not zero, and rgColumnIDs was a null pointer.
DB_SEC_E_PERMISSIONDENIED
The provider was unable to delete any column values because of a permission failure.
DB_S_ERRORSOCCURRED
The provider was able to delete at least one column value but was unable to delete at least one column value. The caller should examine each element of rgdwStatus to determine whether, and why, an individual column was not set.
DB_E_DELETEDROW
The row has been deleted or moved. (A delete either is pending or has been transmitted to the data store.)
DB_E_ERRORSOCCURRED
The provider was unable to delete values of any of the named columns. The caller should examine each element of rgdwStatus to determine why each individual column was not set.
DB_E_NEWLYINSERTED
DBPROP_CHANGEINSERTEDROWS on the source rowset was VARIANT_FALSE, and the method was called on a row for which the insertion has been transmitted to the data store.
E_FAIL
A provider-specific error occurred. No columns were deleted.
E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called, and the object is in a zombie state.

Comments

IRowSchemaChange::DeleteColumns deletes the columns named in the rgColumnIDs array from the row. The behavior when deleted columns also belong to a source rowset or when the row is in deferred update mode is described below. If one or more of the named columns do not exist, the provider should set the corresponding element(s) of rgdwStatus to a DBSTATUS of DBSTATUS_E_DOESNOTEXIST and return DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED as appropriate.

If a row column also belongs to the row's source rowset or if the row is in deferred update mode, IRowSchemaChange::DeleteColumns is semantically equivalent to setting the column value to null. Subsequent attempts to retrieve the column, either using an accessor on the source rowset or using IRow::GetColumns, will return the null value. Furthermore, subsequent calls to IColumnsInfo on the containing rowset (if any) or to IColumnsInfo2::GetRestrictedColumnInfo will return metadata information for this column. The provider may elect to delete a row-specific column from the row once the source rowset and row have been released and re-instantiated. If a rowset column is non-nullable, IRowSchemaChange::DeleteColumns cannot set it to null. Instead, the provider should return DBSTATUS_E_INTEGRITYVIOLATION. If the column is typed as DBTYPE_VARIANT, the provider sets the column value to VT_EMPTY.

If a row column does not belong to the row's source rowset and the row is in immediate mode, or if there is no source rowset for the row, IRowSchemaChange::DeleteColumns removes the column from the row. Subsequent attempts to retrieve the column using IRow::GetColumns or to set the column using IRowChange::SetColumns or IRowSchemaChange::AddColumns will return an error and set the corresponding status value to DBSTATUS_E_DOESNOTEXIST. Furthermore, subsequent calls to IColumnsInfo2::GetRestrictedColumnInfo will not return metadata information for this column.

IRowSchemaChange::AddColumns or IRowChange::SetColumns can also be used to set column values to null.


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 a mandatory interface on the rowset object.

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. Rowset column ordinals start at 1 and are numbered sequentially in increasing order (for example, 1, 2, 3, and so on). When a row object is derived from a rowset, the row-specific column ordinals must come after the rowset column ordinals.

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: Getting and Setting Data.

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: Getting and Setting Data.

To fetch rows from the database, the consumer calls a method such as IRowset::GetNextRows or IRowsetLocate::GetRowsAt. To create and initialize a new row to be inserted into the data store, 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 IRowset::GetData and passes it the handle to a row, the handle to an accessor, and a pointer to a consumer-allocated buffer. IRowset::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 IRowset::GetData more than once for a row, using different accessors and buffers; therefore, the consumer can have multiple copies of the same data. For example, if a column contains a text document, the consumer might call IRowset::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 IRowset::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 whether 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: Rowsets.

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

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 IRowset::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 when the command was executed 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(
   DBCOUNTITEM   cRows,
   const HROW    rghRows[],
   DBREFCOUNT    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.

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 store. 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

IRowset::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 IRowset::AddRefRows while it is processing a method in IRowsetNotify. That is, the rowset must be reentrant through IRowset::AddRefRows during notification.

If IRowset::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), IRowset::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: Getting and Setting Data.
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. Some providers may return DB_E_BADACCESSORHANDLE instead of this error code when command accessors are passed to the rowset.
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 store. 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: Getting and Setting Data.

Comments

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

This method makes no logical change to the state of the object.

A consumer calls IRowset::GetData to retrieve data from rows that have been fetched by prior calls to methods such as IRowset::GetNextRows. For a complete description of how IRowset::GetData retrieves data, see "Getting Data" in Chapter 6: Getting and Setting Data.

A consumer can call IRowset::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.

IRowset::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 IRowset::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 IRowset::GetData never encounters problems accessing a row. Such rows might exist if the DBPROP_ROWRESTRICT property is VARIANT_TRUE.

If IRowset::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. The same is true if DB_E_ERRORSOCCURRED was returned but the consumer did not request status values in the binding.

IRowset::GetData must be reentrant during notifications. If the provider calls a method from IRowsetNotify in the consumer, the consumer must be able to call IRowset::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,
   DBROWOFFSET    lRowsOffset,
   DBROWCOUNT     cRows,
   DBCOUNTITEM   *pcRowsObtained,
   HROW         **prghRows);

Parameters

hChapter
[in]
The chapter handle designating the rows to fetch. For nonchaptered rowsets, the caller must set hChapter to DB_NULL_HCHAPTER. For chaptered rowsets, DB_NULL_HCHAPTER designates the entire rowset.
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 either to IRowset::GetNextRows or to IRowsetFind::FindNextRow with a null pBookmark value, 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, 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. A negative value means skipping the rows in a backward direction. There is no guarantee that skipping rows is done efficiently on a sequential rowset. If the data store 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, the provider sets *pcRowsObtained to zero and performs no further processing, returning immediately from the method invocation. No rows are fetched, the fetch direction and the next fetch position are unchanged, and lRowsOffset is ignored.

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, IRowset::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 consumer-allocated memory large enough to return the handles of the requested number of rows. If the consumer-allocated memory is larger than needed, the provider fills in as many row handles as specified by pcRowsObtained; the contents of the remaining memory are undefined.

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
IRowset::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, as reported by DBPROP_MAXOPENROWS. The number of rows that were actually fetched is returned in *pcRowsObtained.
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 provider called a method from IRowsetNofity in the consumer that had not yet returned, and the provider does not support reentrancy in this method.
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

IRowset::GetNextRows fetches a sequence of rows. The provider maintains a next fetch position that is used in subsequent calls to either IRowset::GetNextRows or IRowsetFind::FindNextRow. The next fetch position is changed either by calling IRowset::GetNextRows or by calling IRowsetFind::FindNextRow with a null pBookmark value. Calling IRowsetFind::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, 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 IRowset::GetNextRows or by IRowsetFind::FindNextRow with a null pBookmark value.

For any rowset except a newly created rowset (or an existing rowset on which IRowset::RestartPosition has been called and before the consumer has fetched any rows), IRowset::GetNextRows or IRowsetFind::FindNextRow may adjust the next fetch position, until the end of the rowset (or the beginning, if DBPROP_CANSCROLLBACKWARDS is VARIANT_TRUE) is reached. After the end of the rowset is reached, calls to IRowset::GetNextRows or IRowsetFind::FindNextRow return DB_S_ENDOFROWSET. (Earlier 1.x providers may return DB_E_BADSTARTPOSITION.)

IRowset::RestartPosition is used to reset the next fetch position to its initial state—that is, its state when the rowset was first created.

For a newly created rowset (or a rowset on which IRowset::RestartPosition has been called and before the consumer has fetched any rows), 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 | Value of cRows | Initial next fetch position
lRowsOffset > 0 | cRows is not zero | After lRowsOffset
lRowsOffset < 0 | cRows is not zero | After N – abs(lRowsOffset)
lRowsOffset = 0 | cRows > 0 | Before first row
lRowsOffset = 0 | cRows < 0 | After last row

The following table shows the row fetched and the resulting next fetch position when IRowset::GetNextRows is called with various positive and negative values of lRowsOffset and cRows. Assume the rowset contains five rows.

Note For each IRowset::GetNextRows call in the first column, the second and third columns show the result when the call is made to a newly created rowset (or a rowset on which IRowset::RestartPosition has been called and before the consumer has fetched any rows).

If the first call to IRowset::GetNextRows is: Row fetched Resulting next fetch position
GetNextRows(hReserved, 2, 1,

   pcRowsObtained,
   prghRows);
3rd row After 3rd row
GetNextRows(hReserved, 2, -1,

   pcRowsObtained,
   prghRows);
2nd row Before 2nd row
GetNextRows(hReserved, -2, 1,

   pcRowsObtained,
   prghRows);
4th row After 4th row
GetNextRows(hReserved, -2, -1,

   pcRowsObtained,
   prghRows);
3rd row Before 3rd row
GetNextRows(hReserved, 0, 1,

   pcRowsObtained,
   prghRows);
1st row After 1st row
GetNextRows(hReserved, 0, -1,

   pcRowsObtained,
   prghRows);
5th row Before 5th row

The following example shows how the next fetch position is affected by a series of consecutive calls to IRowset::GetNextRows, beginning with a newly created rowset (or a rowset on which IRowset::RestartPosition has been called and before the consumer has fetched any rows) that contains six rows and has DBPROP_CANSCROLLBACKWARDS set to VARIANT_TRUE.

Note An error will occur if the row fetched or next fetch position specified is beyond the range of the rowset.

Series of IRowset::GetNextRows calls
Row(s) fetched 		New next
fetch position
GetNextRows(hReserved, 0, 1,

   pcRowsObtained,
   prghRows);
1st row After 1st row
GetNextRows(hReserved, 1, 3,

   pcRowsObtained,
   prghRows);
3rd, 4th, and 5th rows After 5th row
GetNextRows(hReserved, -2, -1,

   pcRowsObtained,
   prghRows);
3rd row Before 3rd row
GetNextRows(hReserved, 0, -1,

   pcRowsObtained,
   prghRows);
2nd row Before 2nd row
GetNextRows(hReserved, -1, 2,

   pcRowsObtained,
   prghRows);
1st and 2nd rows After 2nd row
GetNextRows(hReserved, 4, -1,

   pcRowsObtained,
   prghRows);
6th row Before 6th row
GetNextRows(hReserved, -2, 1,

   pcRowsObtained,
   prghRows);
4th row After 4th 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 IRowset::RestartPosition resets the next fetch position to the same position as when the rowset is first created.

IRowset::GetNextRows increments, by 1, the reference count of each row for which it returns a handle. Therefore, if a handle is returned for a row that has already been fetched, the reference count of that row will be greater than 1. IRowset::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—IRowset::GetNextRows 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.

IRowset::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 usually calls IRowset::RestartPosition to return it to a known position.

Whether a provider returns DB_S_ENDOFROWSET, DB_S_ROWLIMITEXCEEDED, or DB_S_STOPLIMITREACHED generally depends on which limit is encountered first. If the maximum number of row handles is obtained before trying to read past the rowset or beyond a resource limit, DB_S_ROWLIMITEXCEEDED is returned. If a resource limitation is reached when there are still row handles available and before attempting to read past the rowset, DB_S_STOPLIMITREACHED is returned. If the provider comes to the end of the rowset before using all of the available row handles and before reaching a resource limitation, DB_S_ENDOFROWSET is returned.

For information about what IRowset::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: Rowsets. For information about whether IRowset::GetNextRows can detect changes made to rows in the rowset, see "Visibility of Changes" in Chapter 5: Updating Data in Rowsets.

See Also

IRowset::GetData | IRowsetLocate::GetRowsAt | IRowsetLocate::GetRowsByBookmark | IRowsetScroll::GetRowsAtRatio


IRowset::ReleaseRows

Releases rows.

HRESULT ReleaseRows (
   DBCOUNTITEM    cRows,
   const HROW     rghRows[],
   DBROWOPTIONS   rgRowOptions[]
   DBREFCOUNT     rgRefCounts[],
   DBROWSTATUS    rgRowStatus[]);

Parameters

cRows
[in]
The number of rows to release. If cRows is zero, IRowset::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. 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.

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.
E_UNEXPECTED
DBPROP_BLOCKINGSTORAGEOBJECTS is VARIANT_TRUE, and IRowset::ReleaseRows is called on a row with an open storage object. If the consumer, on cleanup, encounters an error while releasing the row, it should release the storage object first.
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 provider called a method from IRowsetNofity in the consumer that had not yet returned, and the provider does not support reentrancy in this method.

Comments

IRowset::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, IRowset::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 IRowset::ReleaseRows until the rowset itself is released.

If a consumer calls IRowset::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 under the following circumstances:

  • 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 store, 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 IRowsetLocate::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 IRowsetUpdate::Update or IRowsetUpdate::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 store, 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 IRowset::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 IRowset::GetNextRows or IRowsetFind::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 designating the rows on which to reposition. For nonchaptered rowsets, the caller must set hChapter to DB_NULL_HCHAPTER. For chaptered rowsets, DB_NULL_HCHAPTER designates the entire rowset.

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, the consumer will see its own changes. If the properties DBPROP_OWNINSERT or DBPROP_OWNUPDATEDELETE are VARIANT_FALSE, 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
IRowset::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 IRowsetNofity in the consumer that had not yet returned, and the provider does not support reentrancy in this method.
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 IRowset::GetNextRows.

If the underlying command contains output parameters, IRowset::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 to satisfy the call to IRowset::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, IRowset::RestartPosition should not reexecute it.

How expensive IRowset::RestartPosition is depends on the provider, the rowset characteristics, and the tables underlying the rowset. If the rowset supports IRowsetLocate, IRowset::RestartPosition is always an inexpensive operation.

If the rowset is sequential, IRowset::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, 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 IRowset::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 IRowset::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 IRowset::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 IRowset::RestartPosition was called. If such accessors are subsequently used, such as in a call to IRowset::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


IRowsetBookmark

IRowsetBookmark is an optional interface on the TRowset cotype that enables rowsets that support bookmarks to set the next fetch position based on a bookmark.

When IRowsetBookmark is present on a rowset, 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. Support for IRowsetBookmark does not imply that IRowsetLocate is supported on the rowset, although it does not preclude it.

When to Implement

Providers that support bookmarks are encouraged to implement this interface as a mechanism for fetching rows.

Method | Description
PositionOnBookmark | Sets the next fetch position for the rowset to be immediately before the specified bookmark.


IRowsetBookmark::PositionOnBookmark

Sets the next fetch position for the rowset to be immediately before the specified bookmark.

HRESULT PositionOnBookmark (
   HCHAPTER      hChapter,
   DBBKMARK      cbBookmark,
   const BYTE   *pBookmark);

Parameters

hChapter
[in]
The chapter handle. For nonchaptered rowsets, the caller must set hChapter to DB_NULL_HCHAPTER.
cbBookmark
[in]
The length in bytes of the bookmark.
pBookmark
[in]
A pointer to a bookmark that identifies the row to be used. The bookmark can be for a designated row or either DBBMK_FIRST or DBBMK_LAST.

Return Code

S_OK
The method succeeded.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
cbBookmark was zero.

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.

Note Consumers should attempt to use only bookmarks that they have received from the provider. The provider is guaranteed to handle only bookmarks it gives out in a predictable manner. Attempting to use a random value as a bookmark is undefined; the provider might return DB_E_BADBOOKMARK, might return an unexpected row, or might 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.
DB_E_ROWSNOTRELEASED
The provider requires release of existing rows before new ones can be fetched. For more information, see DBPROP_CANHOLDROWS in Appendix C, "OLE DB Properties."

Comments

After using this method, the client uses IRowset::GetNextRows to fetch rows from this new position. This interface is analogous to IRowsetIndex::Seek on integrated indexes in that it merely positions the next fetch position.

If this method fails, the next fetch position is unchanged.

See Also

IRowsetLocate::GetRowsAt


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.

To request updatability of the rowset, the consumer should set DBPROP_IRowsetChange to VARIANT_TRUE and specify the desired updatability in the DBPROP_UPDATABILITY rowset property when creating the rowset.

When to Call

The consumer calls methods in IRowsetChange to modify rows as follows.

  • IRowsetChange::SetData sets column data in an existing row.
  • IRowsetChange::DeleteRows deletes existing rows.
  • IRowsetChange::InsertRow creates a new row and sets initial values.

IRowsetChange::SetData and IRowsetChange::InsertRow require the use of an accessor. For more information about accessors, see "Accessors" in Chapter 6: Getting and Setting Data.

IRowsetChange::SetData and IRowsetChange::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, changes made through IRowsetChange are buffered in the rowset and not transmitted to the data store until IRowsetUpdate::Update is called; this is known as delayed update mode. If IRowsetUpdate is not exposed on the rowset, changes made through IRowsetChange are immediately transmitted to the data store; this is known as immediate update mode. For more information, see "Changing Data" in Chapter 5: Updating Data in Rowsets.

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,
   DBCOUNTITEM   cRows,
   const HROW    rghRows[],
   DBROWSTATUS   rgRowStatus[]);

Parameters

hChapter
[in]
The chapter handle. Providers are allowed to ignore this argument. For maximum interoperability, consumers should set hChapter to DB_NULL_HCHAPTER.
cRows
[in]
The number of rows to be deleted. If cRows is zero, IRowsetChange::DeleteRows does not do anything.
rghRows
[in]
An array of handles of the rows to be deleted.

If rghRows includes a duplicate row handle, IRowsetChange::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 status information is returned.

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 store. For more information, see the DBPROP_REPORTMULTIPLECHANGES property in Appendix C. 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 store. 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 store. The corresponding element of rgRowStatus contains DBROWSTATUS_E_NEWLYINSERTED.
  • The consumer did not have sufficient permission to delete a row. 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_ABORTLIMITREACHED
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.
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, or the corresponding bit of DBPROP_UPDATABILITY is not set.

Note The spec, as of 2.1, does not require this return code if the corresponding bit is not set.

Comments

In delayed update mode, IRowsetChange::DeleteRows marks rows for deletion rather than actually deleting them. Rows with pending deletes cannot be used in any methods except IRowsetRefresh::GetLastVisibleData, IRowsetRefresh::RefreshVisibleData, IRowsetUpdate::Undo, IRowsetUpdate::Update, IRowsetUpdate::GetOriginalData, IRowsetUpdate::GetRowStatus, IRowsetUpdate::GetPendingRows, IRowset::ReleaseRows, and IRowsetIdentity::IsSameRow. The deletion is not transmitted to the data store until IRowsetUpdate::Update is called. In immediate update mode, IRowsetChange::DeleteRows transmits deletions to the data store immediately. For more information, see "Changing Data" in Chapter 5: Updating Data in Rowsets.

After a deletion has been transmitted to the data store, it cannot be undone. The row cannot be used with any method except IRowset::ReleaseRows. Neither IRowsetChange::DeleteRows nor IRowsetUpdate::Update releases rows after transmitting deletions to the data store. The consumer must release the row with IRowset::ReleaseRows.

If IRowsetChange::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 store. 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 IRowset::ReleaseRows, which must be called to release it.

If an error occurs while deleting a row, IRowsetChange::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. Providers are allowed to ignore this argument. For maximum interoperability, consumers should set hChapter to DB_NULL_HCHAPTER.
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), 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. Passing an accessor with all columns set to DB_S_IGNORE is equivalent to passing a null accessor.

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, 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 IRowset::GetData for this row handle depends on the setting of the DBPROP_SERVERDATAONINSERT. If IRowsetChange::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 store. If DBPROP_CANHOLDROWS is VARIANT_FALSE and the rowset is in deferred update mode, then, in addition to freeing any reference counts on the row handle, the consumer must call IRowsetUpdate::Update in order to transmit the pending change to the data store 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: Getting and Setting Data.
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. Some providers may return DB_E_BADACCESSORHANDLE instead of this error code when command accessors are passed to the rowset.
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: Getting and Setting Data.
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, or the corresponding bit of DBPROP_UPDATABILITY is not set.

Note The spec, as of 2.1, does not require this return code if the corresponding bit is not set.

DB_E_ROWLIMITEXCEEDED
Creating another row would have exceeded the total number of active rows supported by the rowset.

The provider does not allow a rowset containing more than DBPROP_MAXROWS rows, and the insert would cause the rowset to exceed this limit.

DB_E_ROWSNOTRELEASED
The consumer attempted to insert a new row before releasing previously retrieved row handles or transmitting pending changes to the data store, 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.

Comments

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

IRowsetChange::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 store only when IRowsetUpdate::Update is called. In immediate update mode, the row is immediately transmitted to the data store. For more information, see "Changing Data" in Chapter 5: Updating Data in Rowsets.

To the consumer, newly inserted rows are almost indistinguishable from other rows. For example, they can be deleted with IRowsetChange::DeleteRows and updated with IRowsetChange::SetData. However, methods that fetch rows might not be able to return them. Furthermore, they might not contain the correct values for computed columns, including bookmark columns on some providers. For more information, see "Visibility of Pending Changes" and "Visibility of Transmitted Changes" in Chapter 5: Updating Data in Rowsets. For information about where rows are inserted in the rowset, see "Position of Inserted Rows," also 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 will not have write permission on at least one column. If the consumer attempts to write to these columns, IRowsetChange::InsertRow 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, IRowsetChange::InsertRow 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. Conceptually, initialization consists of the following sequence of events.

Note This sequence is not intended to specify how providers must implement initialization.

  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 store.

    If the column status is DBSTATUS_E_UNAVAILABLE, the consumer can still send this value to the data store to use the default. In this case, the default is available after the insertion is transmitted to the data store. To see the default, the consumer must call IRowsetRefresh::GetLastVisibleData or IRowsetRefresh::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. IRowsetChange::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. If the accessor is not a null accessor, the provider uses it to set columns with the values provided by the consumer in *pData. During this process, the provider does not generate notifications as it does when setting data in IRowsetChange::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 IRowsetChange::InsertRow sets data, see "Setting Data" in Chapter 6: Getting and Setting Data.

    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 store do so, the computed value is not available until after the change is transmitted to the data store—that is, after IRowsetChange::InsertRow is called in immediate update mode or after IRowsetUpdate::Update if IRowsetChange::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—for example, when the bookmark is the primary key or is a ROWID assigned by the data store.

Domain and schema validation is enforced as it is with IRowsetChange::SetData.

If IRowsetChange::InsertRow returns an error, it does not create a new row.

When setting rowset column data values from a consumer's storage object, the provider is responsible for releasing the storage object. When the provider has finished using the consumer's storage object, it calls IUnknown::Release to release the pointer. If the consumer wants to ensure access to its storage object after IRowsetChange::InsertRow returns, it must call IUnknown::AddRef on the pointer before calling IRowsetChange::InsertRow.

It is the consumer's responsibility to release VARIANTs stored in *pData. The consumer should call the COM method VariantClear when a VARIANT is no longer needed.

The use of null accessors is not recommended 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), IRowsetChange::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: Getting and Setting Data.
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 store. 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. Some providers may return DB_E_BADACCESSORHANDLE instead of this error code when command accessors are passed to the rowset.
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. IRowsetChange::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 store.
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: Getting and Setting Data.
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 store.
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, or the corresponding bit of DBPROP_UPDATABILITY is not set.

Note The spec, as of 2.1, does not require this return code if the corresponding bit is not set.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to update the row, or the row was not in a state suitable for updating. (Typical reasons for returning this code are the row is read-only, or changes have not yet been committed on a rowset in delayed update mode.)

Comments

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

IRowsetChange::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: Getting and Setting Data.

In delayed update mode, these changes are buffered locally in the rowset and are transmitted to the data store only when IRowsetUpdate::Update is called. In immediate update mode, the changes are immediately transmitted to the data store. For more information, see "Changing Data" in Chapter 5; Updating Data in Rowsets.

If a computed column depends on a column that is changed with IRowsetChange::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 store do so, the computed value is not available until after the change is transmitted to the data store—that is, after IRowsetChange::SetData is called in immediate update mode or after IRowsetUpdate::Update if SetData is called in delayed update mode. Whenever the provider detects that the data store has computed a new column value, it is required to send a COLUMN_RECALCULATED notification to the consumer. To retrieve the computed value in this case, the consumer calls RefreshVisibleData or GetLastVisibleData in IRowsetRefresh. Bookmark columns are often computed—for example, when the bookmark is the primary key or is a ROWID assigned by the data store.

If IRowsetChange::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, the position of updated rows is not changed. 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. Therefore, 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 IRowsetChange::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: BLOBs and COM Objects. If the rowset is in immediate update mode, storage object data is always transmitted immediately to the data store. If it is in delayed update mode, whether it is transmitted immediately or delayed depends on the DBPROP_DELAYSTORAGEOBJECTS property.

When setting rowset column data values from a consumer's storage object, the provider is responsible for releasing the storage object. When the provider has finished using the consumer's storage object, it calls IUnknown::Release to release the pointer. If the consumer wants to ensure access to its storage object after IRowsetChange::SetData returns, it must call IUnknown::AddRef on the pointer before calling IRowsetChange::SetData.

It is the consumer's responsibility to release VARIANTs stored in *pData. The consumer should call the COM method VariantClear when a VARIANT is no longer needed.

Although IRowsetChange::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 store with IRowsetUpdate::Update or the transaction is committed with ITransaction::Commit. This delay is often necessary because of dependencies on values in other columns or tables.

IRowsetChange::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 will not have write permission on at least one column. If the consumer attempts to write to these columns, IRowsetChange::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, IRowsetChange::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 IRowsetUpdate::Update, if the update is in delayed mode, the provider does not send additional DBREASON_COLUMN_SET notifications for the rows. However, if IRowsetUpdate::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 if 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 IRowsetChange::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 IRowsetChapterMember::IsRowInChapter whenever they need to determine whether 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 | Determines whether a row is a member of a chapter.


IRowsetChapterMember:: IsRowInChapter

OLE DB Programmer's Reference

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 identifying the chapter in which the row is to be tested for membership. Providers should return S_OK if hRow is a member of the rowset identified by hChapter. An hChapter value of DB_NULL_HCHAPTER refers to the entire rowset. For a chaptered rowset, such as one involved in a hierarchy or in a filter or sort operation, this includes hRows that are members of any chapter of the rowset. Providers that do not support access to the unchaptered rowset may return DB_E_BADCHAPTER.
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 whether it is a member of a chapter that the consumer already holds.


IRowsetCurrentIndex

IRowsetCurrentIndex is the interface for determining and selecting a specific index for a rowset. This is limited to rowsets that expose IRowsetIndex. IRowsetCurrentIndex provides a mechanism for providers to reorder their rows without reopening the rowset. This interface can be effective for both integrated and nonintegrated indexes. For a complete description of indexes, see Chapter 10: Index Rowsets and Chapter 11: Integrated Indexes.

When to Implement

IRowsetCurrentIndex is optional for providers that support integrated or nonintegrated indexes through IRowsetIndex. Providers that support IRowsetIndex and can change the current index dynamically should implement this interface.

When to Call

Method | Description
GetIndex | Gets the current index on the rowset.
SetIndex | Sets the current index on the rowset.


IRowsetCurrentIndex::GetIndex

Gets the current index on the rowset.

HRESULT GetIndex (
   DBID            **ppIndexID);

Parameters

ppIndexID
[out]
The DBID of the active index on the base table.

Return Code

S_OK
The method succeeded. If there is no current index, ppIndexID is set to NULL.
E_INVALIDARG
ppIndexID was a null pointer.
E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return ppIndexID.
E_FAIL
A provider-specific error occurred.

Comments

IRowsetCurrentIndex::GetIndex returns the index ID of the current index for the rowset. If a rowset is opened via the ppIndexID argument to IOpenRowset::OpenRowset, the value can be retrieved via this interface, assuming it has not been changed since the rowset was opened. The current index is always returned via this method.

The provider allocates memory for ppIndexID. The consumer must free this memory by calling IMalloc::Free.


IRowsetCurrentIndex::SetIndex

Sets the current index on the rowset.

HRESULT SetIndex (
   DBID      *pIndexID);

Parameters

pIndexID
[in]
The DBID of the current index on the rowset.

Return Code

S_OK
The method succeeded.
DB_S_COLUMNSCHANGED
The metadata for the rowset changed based on the index selected. The provider had to reexecute the command for the newly selected index, and the order of the columns changed.
E_INVALIDARG
pIndexID was a null pointer.
DB_E_BADINDEXID
*pIndexID was an invalid index ID.
DB_E_ROWSNOTRELEASED
The provider requires release of existing rows before changing the index 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 in Appendix C.
DB_E_NOINDEX
The index specified in pIndexID did not exist.
E_FAIL
A provider-specific error occurred.

Comments

IRowsetCurrentIndex::SetIndex sets the current index used by IRowsetIndex in navigating the indexed rowset.

Providers may require that all existing row handles be released prior to successfully processing a call to IRowsetCurrentIndex::SetIndex.

If IRowsetCurrentIndex::SetIndex 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 IRowsetCurrentIndex::SetIndex was called. If such accessors are subsequently used, such as in a call to IRowset::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.

It may not be possible for a provider to change to a new index in all cases. For example, if the provider is currently exposing a clustered index as an integrated index rowset, changing to a nonclustered index might not be possible because it does not contain the same set of columns. In this case, the consumer can close the existing rowset and open a new rowset, specifying the desired index.

If IRowsetCurrentIndex::SetIndex 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 IRowsetCurrentIndex::SetIndex was called. If such accessors are subsequently used, such as in a call to IRowset::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.

Changing the index of a rowset may affect the ordering of chapters generated from that rowset that do not have explicit sort orders defined. However, if the chapter has been specified with an explicit sort order, that sort order survives changing of the root rowset's index.


IRowsetFind

IRowsetFind is the interface that allows consumers to find a row within the rowset matching a specified value.

When to Implement

IRowsetFind is an optional interface on the rowset object. However, general consumers expect the rowset object to implement this interface, either natively or via OLE DB Services.

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

Begins at the specified bookmark and finds the next row matching the specified value.

HRESULT FindNextRow (
   HCHAPTER            hChapter,
   HACCESSOR           hAccessor,
   void               *pFindValue,
   DBCOMPAREOP         CompareOp,
   DBBKMARK            cbBookmark,
   const BYTE         *pBookmark,
   DBROWOFFSET         lRowsOffset,
   DBROWCOUNT          cRows,
   DBCOUNTITEM        *pcRowsObtained,
   HROW              **prghRows);

Parameters

hChapter
[in]
The chapter handle designating the rows to find. For nonchaptered rowsets, the caller must set hChapter to DB_NULL_HCHAPTER. For chaptered rowsets, DB_NULL_HCHAPTER designates the entire rowset.
hAccessor
[in]
Accessor describing the value to be matched. This accessor must describe only a single column. If it describes more than one column, IRowsetFind::FindNextRow returns DB_E_BADBINDINFO.

Valid coercions for IRowsetFind::FindNextRow are the same as those for IRowsetChange::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 equal to null values and not equal to non-null values, within the rowset. It is provider-specific whether null values are compared greater than or less than non-null values.
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 values of CompareOp:

DBTYPE_BOOL
DBTYPE_ERROR
DBTYPE_GUID
DBTYPE_IDISPATCH
DBTYPE_IUNKNOWN
DBTYPE_BTYES

The values in DBCOMPAREOPSENUM have the meanings described in the following table.

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 valid only 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 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_NOTCONTAINS | Match the first value that does not contain 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 search value. If the search value is NULL, this matches the first non-NULL value. If the search value is non-NULL, this matches the first non-NULL value that is not equal to the search value.
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.

Note DBCOMPAREOPS_IGNORE must be supported by all providers that implement IRowsetFind or IViewFilter.
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 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 to start from the next fetch location. (See the description of pBookmark.)
pBookmark
[in]
Pointer to a bookmark that, in combination with lRowsOffset, 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.

To request that the find occur starting with the next fetch location, the consumer can specify a cbBookmark value of zero and set pBookmark to null. The next fetch location is the same as the next fetch position used by IRowset::GetNextRows.

If cbBookmark equals zero, the provider ignores pBookmark.

If the rowset does not support IRowsetLocate, only the special bookmarks DBBMK_FIRST and DBBMK_LAST, and the null value, may be used.

If the rowset does not support scrolling backward, only the null bookmark value may be used.

If the rowset is chaptered, the identified row must fall within the specified chapter.

lRowsOffset
[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 lRowsOffset is 0, the first row checked is the bookmarked row; if lRowsOffset is 1, the first row checked is the row after the bookmarked row; if lRowsOffset 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 indicates a backward fetch. The consumer can determine whether the provider supports fetching backward by checking the value of the DBPROP_CANFETCHBACKWARDS property returned by IRowsetInfo::GetProperties.

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. In this case, if cbBookmark is zero, the next fetch position is moved to the same position as if calling IRowsetFind::FindNextRow with cRows equal to 1 and releasing the retrieved row. In general, consumers should avoid calling IRowsetFind::FindNextRow with cRows equal to zero.

Note Calling IRowsetFind::FindNextRow with a cbBookmark value of zero searches for a column value relative to the current IRowset::GetNextRows position. Following the call to IRowsetFind::FindNextRow, the fetch for IRowset::GetNextRows is also moved. Calling IRowsetFind::FindNextRow with a cbBookmark value of zero and a cRows value of zero is defined as setting the next fetch position to the same location as by calling IRowsetFind::FindNextRow with a cbBookmark value of zero and a cRows value of one.

In general, it is not useful to call IRowsetFind::FindNextRow with a cRows value of zero. Due to ambiguity in the previous versions of the OLE DB Programmer's Reference, some providers may not implement the next fetch position as defined, and consumers should not rely on the next fetch position following a call to IRowsetFind::FindNextRow with a cbBookmark value of zero and a cRows value of zero.

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
IRowsetFind::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. Therefore, 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 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. IRowsetFind::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. Therefore, 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 attempt to use only 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.

The specified CompareOp could not be supported on the column, or the column was not searchable.

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
A bookmark value of DBBMK_FIRST or DBBMK_LAST was specified, and the rowset cannot scroll backward.

lRowsOffset was negative, and the rowset cannot scroll backward.

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_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

The provider maintains a next fetch position that is used in subsequent calls to either IRowset::GetNextRows or IRowsetFind::FindNextRow. The next fetch position is changed either by calling IRowset::GetNextRows or by calling IRowsetFind::FindNextRow with a null pBookmark value. Calling IRowsetFind::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, 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 IRowset::GetNextRows or by IRowsetFind::FindNextRow with a null pBookmark value.

See IRowsetLocate::GetRowsAt for a complete description of the results for given values for a bookmark, offset, and number of rows to fetch.

IRowsetFind::FindNextRow searches the rowset in the direction indicated by cRows for a row that matches pFindValue according to the specified CompareOp. When a match is found, if cRows is greater than 1, additional rows are returned in rowset order as if the consumer called IRowset::GetNextRows starting with the first matching row. If no rows match the criteria, IRowsetFind::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 IRowset::GetNextRows for newly created rowsets.

It is not required that the rowset be ordered on the column searched. IRowsetFind::FindNextRow always finds the next row according to the criteria, regardless of the sort order.

IRowsetFind::FindNextRow increments the reference count of each row for which it returns a handle by 1. Therefore, if a handle is returned for a row that has already been fetched, the reference count of that row will be greater than 1. IRowset::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—IRowsetFind::FindNextRow 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 an error occurs when calling IRowsetFind::FindNextRow, the next fetch position is left unchanged.

If IRowsetFind::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 IRowsetFind::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: Rowsets. For information about whether IRowsetFind::FindNextRow can detect changes made to rows in the rowset, see "Visibility of Changes" in Chapter 5: Updating Data in Rowsets. For information about how IRowsetFind::FindNextRow interprets data from *pFindValue, see "Setting Data" in Chapter 6, "Getting and Setting Data."

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 an optional interface on the rowset object. However, general consumers expect the rowset object to implement this interface, either natively or via OLE DB Services.

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, 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: Rowsets.

When to Call

Consumers call IRowsetIdentity::IsSameRow whenever they need to determine whether 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 whether it is the handle of a row of interest.

Method | Description
IsSameRow | Compares two row handles to see whether they refer to the same row instance.


IRowsetIdentity::IsSameRow

Compares two row handles to see whether 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 store.
DB_E_NEWLYINSERTED
The provider is unable to determine identity for a row for which an insertion had been transmitted to the data store. 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 IRowsetIdentity::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 10: Index Rowsets, and Chapter 11: 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 10: Index Rowsets.
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 (
   DBORDINAL             *pcKeyColumns,
   DBINDEXCOLUMNDESC    **prgIndexColumnDesc,
   ULONG                 *pcIndexPropertySets,
   DBPROPSET            **prgIndexPropertySets);

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.The size of the array is equal to *pcKeyColumns.

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 *pcKeyColumns is zero on output or if an error occurs, the provider does not allocate any memory and ensures that *prgIndexColumnDesc is a null pointer on output.

For more information, see IIndexDefinition::CreateIndex.

pcIndexPropertySets
[out]
A pointer to memory in which to return the number of DBPROPSET structures returned in *prgIndexPropertySets. *pcIndexPropertySets 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, *pcIndexPropertySets is set to zero.
prgIndexPropertySets
[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 Property Group" 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 *pcIndexPropertySets is zero on output or if an error occurs, the provider does not allocate any memory and ensures that *prgIndexPropertySets is a null pointer on output.

For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 14: Properties.

Return Code

S_OK
The method succeeded.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pcKeyColumns, prgIndexColumnDesc, pcIndexPropertySets, or prgIndexPropertySets 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,
   DBORDINAL   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, 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, 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), IRowsetIndex::Seek does not change the next fetch position.

cKeyValues
[in]
The number of bindings in hAccessor for which *pData contains valid data. IRowsetIndex::Seek 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. IRowsetIndex::Seek 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 IRowsetIndex::Seek method. The values in DBSEEKENUM have the meanings described in the following table.
Value | Description
DBSEEK_FIRSTEQ | First key with values equal to the values in *pData, in index order.
DBSEEK_LASTEQ | Last key with values equal to the values in *pData.
DBSEEK_AFTEREQ | Last key with values equal to the values in *pData or, if there are no keys equal to the values in *pData, first key with values after the values in *pData, in index order.
DBSEEK_AFTER | First key with values following the values in *pData, in index order.
DBSEEK_BEFOREEQ | First key with values equal to the values in *pData or, if there are no keys equal to the values in *pData, last key with values before the values in *pData, in index order.
DBSEEK_BEFORE | Last key with values before the values in *pData, in index order.

dwSeekOptions examples

The following table shows the expected results when given the columns and index value for the DBSEEK predicates above.

Col 1 Col 2 Col 3 With index setting of Predicate Result
0
1
2
3 		index = col1 asc BEFORE (2)

BEFOREEQ (2)

(1)

(2)
1
2
2
3 		1
2
3
4 		index = (col1 asc) BEFOREEQ (2)

AFTEREQ (2)

 (2,2)

(2,3)
1
3
3
4 		4
3
3
1 		0
1
2
3 		index = (col1 asc, col2 desc) BEFOREEQ (3,3)

AFTEREQ (3,3)

 (3,3,1)

(3,3,2)

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 or was greater than the number of bindings specified in hAccessor.

pData was a null pointer.

DB_E_BADACCESSORHANDLE
hAccessor was invalid.
DB_E_BADACCESSORTYPE
The specified accessor was not a row accessor. Some providers may return DB_E_BADACCESSORHANDLE instead of this error code when command accessors are passed to the rowset.
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: Getting and Setting Data.
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.
DB_E_ROWSNOTRELEASED
The provider requires release of existing rows before new ones can be fetched. For more information, see DBPROP_CANHOLDROWS in Appendix C: OLE DB Properties.

Comments

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

The IRowsetIndex::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 IRowsetIndex::Seek transfers data from *pData, see "Setting Data" in Chapter 6: Getting and Setting Data.

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,
   DBORDINAL   cStartKeyColumns,
   void       *pStartData,
   DBORDINAL   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, 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, 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), IRowsetIndex::SetRange does not set a range.

cStartKeyColumns
[in]
The number of bindings in hAccessor for which *pStartData contains valid data. IRowsetIndex::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 that cStartKeyValues is 2. IRowsetIndex::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. IRowsetIndex::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 that cEndKeyValues is 2. IRowsetIndex::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 meanings described in the following table.
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 0xff.
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.

dwRangeOptions included DBRANGE_PREFIX or DBRANGE_MATCH, and pEndData was not a NULL pointer.

dwRangeOptions was DBRANGE_MATCH_N, and the provider did not support that option. For more information about DBRANGE_MATCH_N, see "Equality Matching."

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. Some providers may return DB_E_BADACCESSORHANDLE instead of this error code when command accessors are passed to the rowset.
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: Getting and Setting Data.
DB_E_NOINDEX
The rowset uses integrated indexes, and there is no current index.

Comments

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

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. Therefore, 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.

As long as the *pStartData and *pEndData values specified are valid for the column, IRowsetIndex::SetRange should succeed, even if the start and end values are outside of the range of values contained in the index. If the index does not contain any rows within the range, calling IRowset::GetNextRows after the call to IRowsetIndex::SetRange should return DB_S_ENDOFROWSET.

A range on the entire index is defined by calling IRowsetIndex::SetRange (hAcc, 0, NULL, 0, NULL, 0). When a range is set, IRowsetIndex::Seek can position only to rows in the current range.

For information about how IRowsetIndex::SetRange transfers data from *pDataStart and *pDataEnd, see "Setting Data" in Chapter 6: Getting and Setting Data.

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 IRowsetIndex::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 IRowsetIndex::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 IRowsetIndex::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 IRowsetIndex::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 IRowsetIndex::SetRange call
A > 5 and A ≤ 10
SetRange(hAcc, 1, {5}, 1, {10},
DBRANGE_EXCLUSIVESTART |
DBRANGE_INCLUSIVEEND);

Prefix Matching

IRowsetIndex::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 for an ascending index, as follows :

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 IRowsetIndex::SetRange call
A = 1 and
B like "abc*"
SetRange(hAcc, 2, {1, "abc"}, 0, NULL,
DBRANGE_PREFIX);

This call is the same whether or not DBRANGE_PREFIX is used with an ascending or descending index.

Equality Matching

To facilitate the formulation of ranges involving equality, the IRowsetIndex::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 IRowsetIndex::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 whether or not 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 nonzero. If so, 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 be used only 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. Therefore, 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. Therefore, the range will end before the first NULL value in the column.

Therefore, 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 IRowsetIndex::GetIndexInfo method or the INDEXES schema rowset of IDBSchemaRowset.

Examples

Desired range IRowsetIndex::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

An IRowsetIndex::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, IRowsetIndex::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. Following are some examples.

Examples with full keys

a) open ranges (single column key)

Desired range IRowsetIndex::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 IRowsetIndex::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 IRowsetIndex::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 IRowsetIndex::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 IRowsetIndex::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 IRowsetInfo::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. IRowsetInfo::GetSpecification gets the object (command or session) that created the rowset. IRowsetInfo::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, 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 14: Properties.

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, 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, 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. Therefore, 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 14: Properties.

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. IRowsetInfo::GetProperties can fail to return properties for a number of reasons, including the following:
  • 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, IRowsetInfo::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 (
   DBORDINAL     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: Rowsets.

See Also

IChapteredRowset | IColumnsInfo | IColumnsRowset


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 IRowsetInfo::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.

IRowsetInfo::GetSpecification returns an interface pointer on the object that created the rowset. If a rowset was obtained from a multiple results object, IRowsetInfo::GetSpecification returns an interface pointer on the command or session object that created the multiple results object. 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, 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 it must be used to gain additional information about the rowset. If the object cannot expose such interfaces, IRowsetInfo::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, IRowsetInfo::GetSpecification returns a null pointer in *ppSpecification.

If the rowset is the COLUMNS rowset (as returned by IColumnsRowset::GetColumnRowset), IRowsetInfo::GetSpecification returns an interface on the rowset that implemented IColumnsRowset, and not the command or session that is the parent of this rowset.

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, 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 an optional interface on the rowset object. However, general consumers expect the rowset object to implement this interface, either natively or via OLE DB Services.

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.
(Inherited) | All methods of IRowset.


IRowsetLocate::Compare

Compares two bookmarks.

HRESULT Compare (
   HCHAPTER       hChapter,
   DBBKMARK       cbBookmark1,
   const BYTE    *pBookmark1,
   DBBKMARK       cbBookmark2,
   const BYTE    *pBookmark2,
   DBCOMPARE     *pComparison);

Parameters

hChapter
[in]
The chapter handle. For nonchaptered rowsets or to designate the root rowset, the caller must set hChapter to DB_NULL_HCHAPTER.

When comparing literal bookmarks and hChapter is DB_NULL_HCHAPTER, IRowsetLocate::Compare must return the same ordering as an arithmetic comparison. If hChapter is not DB_NULL_HCHAPTER, IRowsetLocate::Compare must reflect the ordering within that chapter. Also, the row designated by the special bookmarks DBBMK_FIRST or DBBMK_LAST depends on the chapter.

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 values in the following table.
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. When to return DBCOMPARE_NOTCOMPARABLE:
  • When calling IRowsetLocate::Compare, the consumer passed a bookmark for a row that does not belong to the chapter designated by hChapter. This bookmark could have been handed out on the base rowset or on another chapter for this rowset.
  • The provider supports key value bookmarks, and one of the bookmarks passed to IRowsetLocate::Compare is now disassociated from the row due to a prior update of a key value.

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 attempt to use only bookmarks that they have received from the provider. The provider is guaranteed to handle only 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, the returned comparison value will be DBCOMPARE_EQ or DBCOMPARE_NE. If bookmarks are ordered, 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.

IRowsetLocate::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, 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

IRowsetIdentity::IsSameRow | IRowsetLocate::Hash


IRowsetLocate::GetRowsAt

Fetches rows starting with the row specified by an offset from a bookmark.

HRESULT GetRowsAt (
   HWATCHREGION   hReserved1,
   HCHAPTER       hChapter,
   DBBKMARK       cbBookmark,
   const BYTE    *pBookmark,
   DBROWOFFSET    lRowsOffset,
   DBROWCOUNT     cRows,
   DBCOUNTITEM   *pcRowsObtained,
   HROW         **prghRows);

Parameters

hReserved1
[in]
Reserved for future use. Providers ignore this parameter.
hChapter
[in]
The chapter handle. For nonchaptered rowsets or to designate the root rowset, the caller must set hChapter to DB_NULL_HCHAPTER.
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, the provider fetches this row first; otherwise, the provider skips this and subsequent rows up to the count specified in the offset and 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, IRowsetLocate::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 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.

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. IRowsetLocate::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 that 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. Therefore, whenever a consumer receives the return code for another warning condition, it should check to see whether this condition occurred.

DB_S_ENDOFROWSET
IRowsetLocate::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 later 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. Therefore, 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 attempt to use only bookmarks that they have received from the provider. The provider is guaranteed to handle only 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 provider called a method from IRowsetNofity in the consumer that had not yet returned, and the provider does not support reentrancy in this method.
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_S_ENDOFROWSET.
  • 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 the provider 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 IRowsetLocate::GetRowsAt and specifies cRows equal to –5 and lRowsOffset equal to 0, IRowsetLocate::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 IRowsetLocate::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_S_ENDOFROWSET
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 IRowsetLocate::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 IRowsetLocate::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 following conditions apply to hChapter and pBookmark:

  • hChapter defines which rows are retrieved. Rows not in the chapter are skipped.
  • If DBPROP_BOOKMARKSKIPPED is false, pBookmark must identify a row in the chapter. If pBookmark identifies a row not in the chapter, the provider returns DB_E_BADBOOKMARK.
  • If DBPROP_BOOKMARKSKIPPED is true, it is possible for pBookmark to identify a row not in the chapter. IRowsetLocate::GetRowsAt starts from that row, returns the next rows that are in the chapter, and returns DB_S_BOOKMARKSKIPPED.

The DBPROP_BOOKMARKSKIPPED property defines the behavior for cases in which there is no exact match for the bookmark pointed to by pBookmark. If providers support DBPROP_BOOKMARKSKIPPED and chapters, they need to be able to determine where in a chapter a bookmark would positioned a row. If this property is set to VARIANT_FALSE, DB_E_BADBOOKMARK will be returned because the corresponding position is not well defined or the rows are not in the chapter. The behavior when this property is set to VARIANT_TRUE is as follows:

  • If lRowsOffset is zero, 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 IRowsetLocate::GetRowsAt with lRowsOffset set to 5 and a bookmark that points to row 20, 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—IRowsetLocate::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 IRowsetLocate::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: Rowsets. For information about whether IRowsetLocate::GetRowsAt can detect changes made to rows in the rowset, see "Visibility of Changes" in Chapter 5: Updating Data in Rowsets.

IRowsetLocate::GetRowsAt increments the reference count of each row for which it returns a handle by 1. Therefore, if a handle is returned for a row that has already been fetched, the reference count of that row will be greater than 1. IRowset::ReleaseRows must be called once for each time the handle to a row has been returned.

To use IRowsetLocate::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,
   DBCOUNTITEM    cRows,
   const DBBKMARK rgcbBookmarks[],
   const BYTE    *rgpBookmarks[],
   HROW           rghRows[],
   DBROWSTATUS    rgRowStatus[]);

Parameters

hChapter
[in]
The chapter handle. For nonchaptered rowsets or to designate the root rowset, the caller must set hChapter to DB_NULL_HCHAPTER.
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.

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 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_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

IRowsetLocate::GetRowsByBookmark increments the reference count of each row for which it returns a handle by 1. Therefore, if a handle is returned for a row that has already been fetched, the reference count of that row will be greater than 1. IRowset::ReleaseRows must be called once for each time the handle to a row has been returned.

hChapter restricts which rows can be returned. If a bookmark identifies a row not in the chapter, the provider should return a status of DBROWSTATUS_E_INVALID for that row.

If the provider encounters a problem fetching a row—for example, data stored in a text file contains a letter in a numeric column—IRowsetLocate::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 IRowsetLocate::GetRowsByBookmark encounters an error while attempting to fetch a row, such as a bad bookmark or a deleted 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 that the consumer can perform a side-by-side scan of each array to determine which rows were not fetched.

For information about what IRowsetLocate::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: Rowsets. For information about whether IRowsetLocate::GetRowsByBookmark can detect changes made to rows in the rowset, see "Visibility of Changes" in Chapter 5: Updating Data in Rowsets.

See Also

IRowset::GetData | IRowset::GetNextRows | IRowsetLocate::GetRowsAt | IRowsetScroll::GetRowsAtRatio


IRowsetLocate::Hash

Returns hash values for the specified bookmarks.

HRESULT Hash (
   HCHAPTER       hChapter,
   DBBKMARK       cBookmarks,
   const DBBKMARK rgcbBookmarks[],
   const BYTE    *rgpBookmarks[],
   DBHASHVALUE    rgHashedValues[],
   DBROWSTATUS    rgBookmarkStatus[]);

Parameters

hChapter
[in]
The chapter handle. hChapter is ignored. For maximum interoperability, consumers should set hChapter to DB_NULL_HCHAPTER.
cBookmarks
The number of bookmarks to hash. If cBookmarks is zero, IRowsetLocate::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. Therefore, 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.

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 IRowsetLocate::Compare returns DBCOMPARE_EQ for two bookmarks, IRowsetLocate::Hash must return the same value for these two bookmarks.

If IRowsetLocate::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 that the consumer can perform a side-by-side scan of each array to determine which rows were not hashed.

IRowsetLocate::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 object. 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 COM connection point scheme for operations or events on rowset objects. A rowset object 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.

Providers are required to support all notifications for which they have the underlying functionality.

For more information about notifications, see Chapter 12: OLE DB Object Notifications.

When to Implement

IRowsetNotify is implemented by consumers that require notification. If the command requests support for IConnectionPointContainer, the rowset object 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 rowset objects. 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 "Event Notifications" in Chapter 15: Transactions.

IRowset is a prerequisite for notification.

DBEVENTPHASE

Notifications have multiple phases. The provider uses the IRowsetNotify callback method for every phase of the notification. A phase describes the state of the notification as the provider and consumer signal intent, approval, rejection, or completion of the change. The phases are similar to phases in a two-phase transaction commit protocol because the problem of ensuring that all controls authorize and succeed in handling a event or action on a rowset is very similar to the problem of ensuring that all resource managers agree to, and succeed in, committing a transaction.

In OLE DB, phases are enumerated by DBEVENTPHASE values, as listed in the following table. In the descriptions, all consumer objects that connect their IRowsetNotify interface to the rowset object 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 impending event (DBREASON) 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 (DBREASON), 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 that 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 object's copy of the row has been modified but before the data store 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 changes. If a listener cancels the event (DBREASON) 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 action on the rowset object 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 changes. The listener should now commit its changes.

The final phase of a notification is always either DBEVENTPHASE_FAILEDTODO or DBEVENTPHASE_DIDEVENT. If there are 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 simplest 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 one notification for the phase, 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 multiple notifications, such as DBREASON_ROW_UPDATE or DBREASON_ROW_UNDOCHANGE, the number of calls to IRowsetNotify::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. A listener must be prepared to get only the final phases of an event when registering for notifications on an object that is in the midst of sending notifications.

DBREASON

Reasons are types of events or "fine-tunings" of the notifications generated by events on rowset objects. The receiver of notifications 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 DBEVENTPHASE represents the basic state of the event, and the DBREASON is a reason for the event.

Providers are required to support all DBREASONs for which they have the underlying functionality. 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 values described in the following table 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.

For more information, see "Asynchronous Rowset Population" in Chapter 17: Asynchronous Processing.
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 the notification for this DBREASON is sent out, so recipients must not make any calls upon it. In COM, 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. For best results, the provider should send this reason for newly fetched rows only, but it might be expensive for providers to determine whether or not a row is newly fetched.
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 reason 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
ICommand::Execute | _ROW_ASYNCINSERT [2] | DIDEVENT
IOpenRowset::OpenRowset | _ROW_ASYNCINSERT [2] | DIDEVENT
IRowset::AddRefRows | None | N/A
IRowset::GetData | None | N/A
IRowset::GetNextRows | _ROW_ACTIVATE | DIDEVENT
 | _ROWSET_FETCHPOSITIONCHANGE | All Phases
IRowset::ReleaseRows | _ROW_RELEASE | DIDEVENT
IRowset::RestartPosition | _ROWSET_FETCHPOSITIONCHANGE | All Phases
 | _ROWSET_CHANGED [3] | All Phases
IRowsetChange::DeleteRows | _ROW_DELETE | All Phases
IRowsetChange::InsertRow | _ROW_INSERT | All Phases
IRowsetChange::SetData | _ROW_FIRSTCHANGE [4] | All Phases
 | _COLUMN_SET | All Phases
 | _COLUMN_RECALCULATED | DIDEVENT
IRowsetIndex::Seek | _ROWSET_FETCHPOSITIONCHANGE | 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

[1] Methods generate only the listed DBREASON values, even though other reasons might seem appropriate. For example, IRowsetChange::InsertRow generates only DBREASON_ROW_INSERT even though DBREASON_COLUMN_SET, DBREASON_ROW_FIRSTCHANGE, and DBREASON_ROW_ACTIVATE might appear to be applicable.

[2] ICommand::Execute and IOpenRowset::OpenRowset generate this DBREASON only when the rowsets are being populated asynchronously.

[3] IRowset::RestartPosition generates this DBREASON only when the metadata for the columns has changed.

[4] DBREASON_ROW_FIRSTCHANGE is generated only the first time a column in the row is changed. For more information, see the table of DBREASON values 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 of 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,
   DBORDINAL      cColumns,
   DBORDINAL      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 for the change as indicated by the value of DBREASON. 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 notification, expressed by the value of DBREASON or DBEVENTPHASE or both, 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 provider continues the operation.

Comments

Possible reasons are DBREASON_COLUMN_SET and DBREASON_COLUMN_RECALCULATED.

All notification phases can occur for a DBREASON_COLUMN_SET change. The new value is visible beginning with the DBEVENTPHASE_SYNCHAFTER notification phase. For a DBREASON_COLUMN_RECALCULATED change, only the DBEVENTPHASE_DIDEVENT notification phase occurs.

Providers call IRowsetNotify::OnFieldChange once for all of the columns set in a single call to IRowsetChange::SetData, and once for all of the changes to calculated columns of a row based on an update to that row. Setting a column to its current value can, 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.

Providers are not required to send notifications for ignored columns—that is, columns where status is set to DBSTATUS_S_IGNORE. However, many providers send such notifications even though no work is done. In those cases, the provider must also send a completion notification with the DBEVENTPHASE_DIDEVENT phase. The DBEVENTPHASE_FAILEDTODO notification phase is not an appropriate completion notification for ignored columns.

See Also

IRowsetNotify::OnRowChange | IRowsetNotify::OnRowsetChange


IRowsetNotify::OnRowChange

Notifies the consumer of the first change to a row or of any change that affects the entire row.

HRESULT OnRowChange (
   IRowset       *pRowset,
   DBCOUNTITEM    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 for the change as indicated by the value of DBREASON. 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 notification, expressed by the value of DBREASON or DBEVENTPHASE or both, 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. 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 IRowsetNotify::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.

When calling IRowsetNotify::OnRowChange with DBREASON_ROW_INSERT, providers should assign and send the same valid row handle for all phases because consumers can use row handles to match notifications. This assumption was unstated prior to OLE DB 2.6, and for maximum interoperability, consumers should make allowances for DBEVENTPHASE_OKTODO and DBEVENTPHASE_ABOUTTODO phases for NULL row handles.

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 change as indicated by the value of DBREASON. 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 notification, expressed by the value of DBREASON or DBEVENTPHASE or both, 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 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 store, 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 store. The primary uses of IRowsetRefresh are as follows:

  • To implement optimistic concurrency.
  • To perform row fix-up in a disconnected environment.
Method | Description
GetLastVisibleData | Gets the most recent data either from the provider-implemented data cache that is visible to the transaction or from the data store.
RefreshVisibleData | Retrieves the data values from the data store that are visible to the transaction for the specified rows.


IRowsetRefresh::GetLastVisibleData

Gets the most recent data either from the provider-implemented data cache that is visible to the transaction or from the data store.

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), IRowsetRefresh::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: Getting and Setting Data.
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. Some providers may return DB_E_BADACCESSORHANDLE instead of this error code when command accessors are passed to the rowset.
DB_E_BADROWHANDLE
hRow was invalid.
DB_E_DELETEDROW
hRow referred to a pending delete row or a row for which a deletion had been transmitted to the data store. 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: Getting and Setting Data.
DB_E_NEWLYINSERTED
DBPROP_CHANGEINSERTEDROWS was VARIANT_FALSE, and hRow referred to a row for which the insertion has been transmitted to the data store.
DB_E_PENDINGINSERT
The rowset was in delayed update mode, and hRow referred to a pending insert row.
DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to retrieve the rows from the data store.

Comments

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

This method makes no logical change to the state of the object. A consumer calls IRowsetRefresh::GetLastVisibleData to retrieve the latest visible data from the data store.

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 store. For a complete description of how IRowsetRefresh::GetLastVisibleData retrieves data, see "Getting Data" in Chapter 6: Getting and Setting Data.

IRowsetRefresh::GetLastVisibleData 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 store 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 store that caused the collision (GetLastVisibleData).

If IRowsetRefresh::GetLastVisibleData fails for any reason other than DB_E_ERRORSOCCURRED, the memory to which pData points is not freed but its contents are undefined. If before IRowsetRefresh::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. The same is true if DB_E_ERRORSOCCURRED was returned but the consumer did not request status values in the binding.

There is a difference between calling IRowsetUpdate::GetOriginalData and calling IRowsetRefresh::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 store. Consumer B then changes the value in Column 1 to Z. If Consumer A calls IRowsetUpdate::GetOriginalData, it gets X. However, if it calls IRowsetRefresh::GetLastVisibleData, using a dirty read, it will retrieve Z.

See Also

IRowset::GetData | IRowsetRefresh::RefreshVisibleData | IRowsetUpdate::GetOriginalData


IRowsetRefresh::RefreshVisibleData

Retrieves the data values from the data store that are visible to the transaction for the specified rows.

HRESULT RefreshVisibleData (
   HCHAPTER         hChapter,
   DBCOUNTITEM      cRows,
   const HROW       rghRows[],
   BOOL             fOverwrite,
   DBCOUNTITEM     *pcRowsRefreshed,
   HROW           **prghRowsRefreshed,
   DBROWSTATUS    **prgRowStatus);

Parameters

hChapter
[in]
The chapter handle designating the rows to refresh. For nonchaptered rowsets, the caller must set hChapter to DB_NULL_HCHAPTER. For chaptered rowsets, DB_NULL_HCHAPTER designates the entire rowset.
cRows
[in]
The count of rows to refresh. If cRows is zero, IRowsetRefresh::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. This is still the case if DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED is returned. If any other error occurs, the provider sets *pcRowsRefreshed to zero. If this is a null pointer, no count of rows is returned.
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, 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 rows that have been updated by the operation. In this case, IRowsetRefresh::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 to 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 provider could easily detect that there were no changes made to the data store 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 store. 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 store. 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 that had not yet returned, and the provider does not support reentrancy in this method.
DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to refresh the rows.

Comments

IRowsetRefresh::RefreshVisibleData retrieves the data values from the data store that are visible to the transaction for the specified rows. Changes made to the row by the current transaction are always visible to IRowsetRefresh::RefreshVisibleData, including changes made by other rowsets in the same transaction. Whether changes made by other transactions are visible to IRowsetRefresh::RefreshVisibleData depends on the isolation level of the current transaction. Therefore, 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, IRowsetRefresh::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 IRowset::GetData or IRowsetUpdate::GetOriginalData. This affects rows with or without pending changes. Any changes already transmitted to the data store 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 if all columns in the row are deferred, the provider reports success without refreshing any rows.

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. Therefore, refreshing the visible values buffer for a row with pending changes via IRowsetRefresh::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 store and this deletion is visible, IRowsetRefresh::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 IRowsetRefresh::GetLastVisibleData (if fOverwrite is FALSE).

If the handle of a deleted row has been specified in rghRows, the handle is returned in the prghRowsRefreshed array and the status is set to DBROWSTATUS_E_DELETED.

When cRows is zero, the provider may opt to not return any reference to hard-deleted rows. This is the preferred behavior. However, some providers may return hard-deleted rows, represented in the prghRowsRefreshed array by DB_NULL_HROW and with a status of DBROWSTATUS_E_DELETED.

If IRowsetRefresh::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 that 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, IRowsetRefresh::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


IRowsetResynch

IRowsetResynch allows consumers to retrieve the current values for rows that may have been changed in the data store since retrieved.

When to Implement

IRowsetResynch is an interface defined in OLE DB 1.x. In OLE DB 2.0, IRowsetResynch is superseded by IRowsetRefresh, which provides better control over when data values are updated from the data store. IRowsetResynch should no longer be used.

In future releases of OLE DB, a common service component will expose IRowsetRefresh over providers that currently expose only IRowsetResynch. In the interim, consumers can work with legacy providers that support only IRowsetResynch by directly calling that interface. This interface is documented here for such consumers.

When to Call

The consumer calls IParentRowset::GetChildRowset to create an aggregated instance of the child rowset corresponding to a chapter-valued column in the parent rowset.

Method | Description
GetVisibleData | Gets the data in the data store that is visible to the transaction for the specified row.
ResynchRows | Gets the data in the data store 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 store that is visible to the transaction for the specified row.

HRESULT GetVisibleData
   (HROW       hRow,
   HACCESSOR   hAccessor,
   void       *pData);

Parameters

hRow
[in]
The handle of the row for which to get the visible data. This can be the handle of a row with a pending delete.
hAccessor
[in]
The handle of the accessor to use. If hAccessor is the handle of a null accessor (cBindings in IAccessor::CreateAccessor was zero), IRowsetResynch::GetVisibleData does not get any data values.
pData
[out]
A pointer to a buffer in which to return the data. The consumer allocates memory for this buffer.

Return Code

S_OK
The method succeeded. The status of all columns bound by the accessor is set to DBSTATUS_S_OK, DBSTATUS_S_ISNULL, or DBSTATUS_S_TRUNCATED.
DB_S_ERRORSOCCURRED
An error occurred while returning data for one or more columns, but data was successfully returned for at least one column.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pData was a null pointer, and hAccessor was not a null accessor.
E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called, and the object is in a zombie state.
DB_E_BADACCESSORHANDLE
hAccessor was invalid. It is possible for a reference accessor or an accessor that has a binding that uses provider-owned memory to be invalid for use with this method, even if the accessor is valid for use with IRowset::GetData or IRowsetChange::SetData.
DB_E_BADACCESSORTYPE
The specified accessor was not a row accessor. Some providers may return DB_E_BADACCESSORHANDLE instead of this error code when command accessors are passed to the rowset.
DB_E_BADROWHANDLE
hRow was invalid.
DB_E_DELETEDROW
hRow referred to a row for which a deletion had been transmitted to the data store.
DB_E_ERRORSOCCURRED
Errors occurred while returning data for all columns. To determine what errors occurred, the consumer checks the status values.
DB_E_ABORTLIMITREACHED
The provider was unable to retrieve the visible data due to reaching a limit on the server, such as a query execution timing out.
DB_E_NEWLYINSERTED
DBPROP_STRONGIDENTITY was VARIANT_FALSE, and hRow referred to a row for which an insertion had been transmitted to the data store.
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_PENDINGINSERT
The rowset was in delayed update mode, and hRow referred to a pending insert row.

Comments

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

This method makes no logical change to the state of the object.

A consumer calls IRowsetResynch::GetVisibleData to retrieve the data in the data store that is visible to the transaction for the specified row. However, IRowsetResynch::GetVisibleData does not affect the values in the rowset's copy of the row.

If IRowsetResynch::GetVisibleData fails for any reason other than DB_E_ERRORSOCCURRED, the memory that pData points to is not freed but its contents are undefined. If before IRowsetResynch::GetVisibleData failed the provider had allocated any memory to be returned to the consumer, the provider frees this memory and does not return it to the consumer. The same is true if DB_E_ERRORSOCCURRED was returned but the consumer did not request status values in the binding.


IRowsetResynch::ResynchRows

Gets the data in the data store that is visible to the transaction for the specified rows and updates the rowset's copies of those rows.

HRESULT ResynchRows (
   DBCOUNTITEM      cRows,
   const HROW       rghRows[],
   DBCOUNTITEM     *pcRowsResynched,
   HROW           **prghRowsResynched,
   DBROWSTATUS    **prgRowStatus);

Parameters

cRows
[in]
The count of rows to resynchronize. If cRows is zero, IRowsetResynch::ResynchRows ignores rghRows and reads the current value of all active rows.
rghRows
[in]
An array of cRows row handles to be resynchronized. If cRows is zero, this argument is ignored.
pcRowsResynched
[out]
A pointer to memory in which to return the number of rows the method attempted to resynchronize. The caller may supply a null pointer if no list is desired. If the method fails with an error other than DB_E_ERRORSOCCURRED, the provider sets *pcRowsResynched to zero.
prghRowsResynched
[out]
A pointer to memory in which to return the array of row handles the method attempted to resynchronize. If cRows is not zero, the elements of this array are in one-to-one correspondence with those of rghRows. If cRows is zero, the elements of this array are the handles of all active rows in the rowset. When cRows is zero, IRowsetResynch::ResynchRows will add to the reference count of the rows whose handles are returned in prghRowsResynched.

The rowset allocates memory for the handles and the client should release this memory with IMalloc::Free when no longer needed. This argument is ignored if pcRowsResynched is a null pointer and must not be a null pointer otherwise. If *pcRowsResynched is zero on output or the method fails, the provider does not allocate any memory and ensures that *prghRowsResynched is a null pointer on output.

If pcRowsResynched is a null pointer, prghRowsResynched and prgRowStatus are ignored.

If pcRowsResynched is not a null pointer and if *pcRowsResynched is zero on output or the method fails, the provider does not allocate any memory and ensures that *prghRowsResynched and *prgRowStatus are null pointers on output.

prgRowStatus
[out]
A pointer to memory in which to return an array of row status values. The elements of this array correspond one-to-one with the elements of *prghRowsResynched. If no errors occur while resynchronizing a row, the corresponding element of *prgRowStatus is set to DBROWSTATUS_S_OK. If the method fails while resynchronizing a row, the corresponding element is set as specified in DB_S_ERRORSOCCURRED. If prgRowStatus is a null pointer, no row status values are returned.

The rowset allocates memory for the row status values and returns the address to this memory; the client releases this memory with IMalloc::Free when it is no longer needed. This argument is ignored if pcRowsResynched is a null pointer. If *pcRowsResynched is zero on output or if 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.

If pcRowsResynched is a null pointer, prghRowsResynched and prgRowStatus are ignored.

If pcRowsResynched is not a null pointer and if *pcRowsResynched is zero on output or the method fails, the provider does not allocate any memory and ensures that *prghRowsResynched and *prgRowStatus are null pointers on output.

Return Code

S_OK
The method succeeded. All rows were successfully resynchronized. The following value can be returned in *prgRowStatus:

The row was successfully resynchronized. The corresponding element of *prgRowStatus contains DBROWSTATUS_S_OK.

DB_S_ERRORSOCCURRED
An error occurred while resynchronizing a row, but at least one row was successfully resynchronized. Successes can occur for the reason listed under S_OK. The following errors can occur:
  • An element of rghRows was invalid or referred to a row that this thread does not have access to. The corresponding element of *prgRowStatus contains DBROWSTATUS_E_INVALID.
  • Resynchronizing a row was canceled during notification. The row was not resynchronized, and the corresponding element of *prgRowStatus contains DBROWSTATUS_E_CANCELED.
  • An element of rghRows referred to a row for which a deletion had been transmitted to the data store. The corresponding element of *prgRowStatus contains DBROWSTATUS_E_DELETED.
  • The row was not resynchronized due to reaching a limit on the server, such as a query execution timing out. The error in the corresponding element of *prgRowStatus contains DBROWSTATUS_E_LIMITREACHED.
  • An element of rghRows referred to a row on which a storage object was open. The corresponding element of *prgRowStatus contains DBROWSTATUS_E_OBJECTOPEN.
  • An element of rghRows referred to a pending insert row. The corresponding element of *prgRowStatus contains DBROWSTATUS_E_PENDINGINSERT.
  • DBPROP_STRONGIDENTITY was VARIANT_FALSE, and an element of rghRows referred to a row for which an insertion had been transmitted to the data store. The row was not resynchronized, and the corresponding element of *prgRowStatus contains DBROWSTATUS_E_NEWLYINSERTED.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
cRows was not zero, and rghRows was a null pointer. pcRowsResynched was not a null pointer, and prghRowsResynched was a null pointer.
E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called, and the object is in a zombie state.
DB_E_ERRORSOCCURRED
Errors occurred while resynchronizing all of the rows. Errors can occur for the reasons listed under DB_S_ERRORSOCCURRED.
DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the consumer that had not yet returned, and the provider does not support reentrancy in this method.
DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to resynchronize the rows.

Comments

IRowsetResynch::ResynchRows refreshes the values in the rowset's copy of each of the specified rows with the currently visible contents of the underlying row. Changes made to the row by the current transaction are always visible to IRowsetResynch::ResynchRows, including changes made by other rowsets in the same transaction. Whether changes made by other transactions are visible to IRowsetResynch::ResynchRows depends on the isolation level of the current transaction.

If a specified row has been deleted from the data store and this deletion is visible, IRowsetResynch::ResynchRows returns DBROWSTATUS_E_DELETED in the error status array for the row and the row is treated as a deleted row.

Any changes transmitted to the data store are not lost; they will be committed or aborted when the transaction is committed or aborted. All pending changes are lost because they exist only in the rowset's copy of the row and IRowsetResynch::ResynchRows overwrites the contents of this copy. The pending change status is removed from the row.


IRowsetScroll : IRowsetLocate

IRowsetScroll enables consumers to fetch rows at approximate positions in the rowset.

When to Implement

IRowsetScroll is an optional interface on the rowset object. However, general consumers expect the rowset object to implement this interface, either natively or via OLE DB Services. 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.
(Inherited) | All methods of IRowsetLocate.


IRowsetScroll::GetApproximatePosition

Gets the approximate position of a row corresponding to a specified bookmark.

HRESULT GetApproximatePosition (
   HCHAPTER       hChapter,
   DBBKMARK       cbBookmark,
   const BYTE    *pBookmark,
   DBCOUNTITEM   *pulPosition,
   DBCOUNTITEM   *pcRows);

Parameters

hChapter
[in]
The chapter handle designating the rows on which to position. For nonchaptered rowsets, the caller must set hChapter to DB_NULL_HCHAPTER. For chaptered rowsets, DB_NULL_HCHAPTER designates the entire rowset.
cbBookmark
[in]
The length in bytes of the bookmark. If this is zero, 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 either DBPROP_BOOKMARKSKIPPED was 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 attempt to use only bookmarks that they have received from the provider. The provider is guaranteed to handle only 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,
   DBCOUNTITEM    ulNumerator,
   DBCOUNTITEM    ulDenominator,
   DBROWCOUNT     cRows,
   DBCOUNTITEM   *pcRowsObtained,
   HROW         **prghRows);

Parameters

hReserved1
[in]
Reserved for future use. Providers ignore this parameter.
hChapter
[in]
The chapter handle. For nonchaptered rowsets, the caller must set hChapter to DB_NULL_HCHAPTER.
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 		IRowsetScroll::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, IRowsetScroll::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
IRowsetScroll::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. Therefore, 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 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_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

IRowsetScroll::GetRowsAtRatio increments the reference count of each row for which it returns a handle by 1. Therefore, if a handle is returned for a row that has already been fetched, the reference count of that row will be greater than 1. IRowset::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—IRowsetScroll::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 IRowsetScroll::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: Rowsets. For information about whether IRowsetScroll::GetRowsAtRatio can detect changes made to rows in the rowset, see "Visibility of Changes" in Chapter 5: Updating Data in Rowsets.

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 store. 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, changes made through IRowsetChange are buffered in the rowset and not transmitted to the data store until IRowsetUpdate::Update is called; this is known as delayed update mode. If IRowsetUpdate is not exposed on the rowset, changes made through IRowsetChange are immediately transmitted to the data store; this is known as immediate update mode. For more information, see "Changing Data" in Chapter 5: Updating Data in Rowsets.

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 store, it calls IRowsetUpdate::Update. Before calling Update, the consumer can back out any changes with IRowsetUpdate::Undo.

Method | Description
GetOriginalData | Gets the data most recently fetched from or transmitted to the data store; 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.
(Inherited) | All methods of IRowsetChange.


IRowsetUpdate::GetOriginalData

Gets the data most recently fetched from or transmitted to the data store; 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), IRowsetUpdate::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 or other 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: Getting and Setting Data.

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. Some providers may return DB_E_BADACCESSORHANDLE instead of this error code when command accessors are passed to the rowset.
DB_E_BADROWHANDLE
hRow was invalid.
DB_E_DELETEDROW
hRow referred to a row for which a deletion had been transmitted to the data store.
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: Getting and Setting Data.
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

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

This method makes no logical change to the state of the object.

IRowsetUpdate::GetOriginalData retrieves the values the row contained when it was last fetched or had changes transmitted to the data store. 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 IRowsetUpdate::GetOriginalData retrieves data, see "Getting Data" in Chapter 6: Getting and Setting Data.

How IRowsetUpdate::GetOriginalData works is best illustrated in the following examples. In the first example, GetOriginalData fetches the values last fetched from the data store:

  1. The consumer fetches a row.
  2. The consumer calls IRowsetChange::SetData to update values in the row.
  3. The consumer calls IRowsetUpdate::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, IRowsetUpdate::GetOriginalData fetches the values last transmitted to the data store:

  1. The consumer fetches a row.
  2. The consumer calls IRowsetChange::SetData to update values in the row.
  3. The consumer calls IRowsetUpdate::Update.
  4. The consumer calls IRowsetChange::SetData to update values in the row.
  5. The consumer calls IRowsetUpdate::GetOriginalData. The consumer retrieves the values it would have retrieved had it called IRowset::GetData after step 3 and before step 4.

To implement IRowsetUpdate::GetOriginalData, the provider usually caches the original values just before making a change to a row and discards the cached values when IRowsetUpdate::Undo or IRowsetUpdate::Update is called for the row.

If hRow refers to a pending insert row, IRowsetUpdate::GetOriginalData returns the column defaults and, for columns without defaults or for which the provider was unable to determine the defaults, NULLs.

Whether IRowsetUpdate::GetOriginalData can retrieve the original value of a COM object that is stored in a column or a storage object that is created over a BLOB after changes have been made to that COM object or BLOB depends on the value of the DBPROP_DELAYSTORAGEOBJECTS rowset property.

There is a difference between calling IRowsetUpdate::GetOriginalData and calling IRowsetRefresh::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 store. Consumer B then changes the value in Column 1 to Z. If Consumer A calls IRowsetUpdate::GetOriginalData, it gets X. However, if it calls IRowsetRefresh::GetLastVisibleData, using a dirty read, it will retrieve Z.

IRowsetUpdate::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 IRowsetUpdate::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 IRowsetUpdate::GetOriginalData never encounters problems accessing a row. Such rows might exist if the DBPROP_ROWRESTRICT property is VARIANT_TRUE.

If IRowsetUpdate::GetOriginalData fails, the memory to which pData points is not freed but its contents are undefined. If before IRowsetUpdate::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. The same is true if DB_E_ERRORSOCCURRED was returned but the consumer did not request status values in the binding.

See Also

IRowset::GetData | IRowsetRefresh::GetLastVisibleData | IRowsetUpdate::Undo | IRowsetUpdate::Update


IRowsetUpdate::GetPendingRows

Returns a list of rows with pending changes.

HRESULT GetPendingRows (
   HCHAPTER             hReserved,
   DBPENDINGSTATUS      dwRowStatus,
   DBCOUNTITEM         *pcPendingRows,
   HROW               **prgPendingRows,
   DBPENDINGSTATUS    **prgPendingStatus);

Parameters

hReserved
[in]
Reserved for future use. Must be DB_NULL_HCHAPTER.
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 Deferred Update Mode" in Chapter 5: Updating Data in Rowsets.

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 Deferred Update Mode" in Chapter 5: Updating Data in Rowsets. 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 hReserved 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

IRowsetUpdate::GetPendingRows increments the reference of each row handle it returns in *prgPendingRows. The consumer must call IRowset::ReleaseRows for these rows. If multiple changes are made to a single row, IRowsetUpdate::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 IRowsetUpdate::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 Deferred Update Mode" in Chapter 5: Updating Data in Rowsets.

See Also

IRowsetUpdate::GetRowStatus


IRowsetUpdate::GetRowStatus

Returns the status of rows.

HRESULT GetRowStatus(
   HCHAPTER          hReserved,
   DBCOUNTITEM       cRows,
   const HROW        rghRows[],
   DBPENDINGSTATUS   rgPendingStatus[]);

Parameters

hReserved
[in]
Reserved for future use. Must be DB_NULL_HCHAPTER.
cRows
[in]
The count of elements in rghRows and rgPendingStatus. If this value is zero, IRowsetUpdate::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. IRowsetUpdate::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 Deferred Update Mode" in Chapter 5: Updating Data in Rowsets.

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 hReserved 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, IRowsetUpdate::GetRowStatus returns the status as described in IRowsetUpdate::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 Deferred Update Mode" in Chapter 5: Updating Data in Rowsets.

See Also

IRowsetUpdate::GetPendingRows


IRowsetUpdate::Undo

Undoes any changes made to a row since it was last fetched or IRowsetUpdate::Update was called for it.

HRESULT Undo (
   HCHAPTER         hReserved,
   DBCOUNTITEM      cRows,
   const HROW       rghRows[],
   DBCOUNTITEM     *pcRows,
   HROW           **prgRows,
   DBROWSTATUS    **prgRowStatus);

Parameters

hReserved
[in]
Reserved for future use. Must be DB_NULL_HCHAPTER.
cRows
[in]
The count of rows to undo. If cRows is nonzero, IRowsetUpdate::Undo undoes all pending changes in the rows specified in rghRows. If cRows is zero, IRowsetUpdate::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, IRowsetUpdate::Undo does not return an error. Instead, the row remains unchanged from its original state—which is the intention of IRowsetUpdate::Undo—and its row status is set to DBROWSTATUS_S_OK.

If rghRows includes a duplicate row, IRowsetUpdate::Undo treats the occurrences as if the row were passed to the method two times sequentially. Therefore, on the first occurrence, IRowsetUpdate::Undo undoes any pending changes. On the second occurrence, IRowsetUpdate::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 IRowsetUpdate::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 IRowsetUpdate::Undo attempted to undo. If rghRows is not a null pointer, 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, IRowsetUpdate::Undo does not add to the reference count of the rows it returns in *prgRows, because 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, whether or not IRowsetUpdate::Undo was successful at undoing those changes. The consumer checks *prgRowStatus to determine which rows were undone. When rghRows is a null pointer, IRowsetUpdate::Undo adds to the reference count of the rows it returns in *prgRows, because 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 IRowsetUpdate::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.

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 IRowsetUpdate::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 store. 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 or other 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 IRowsetUpdate::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 hReserved 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 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

IRowsetUpdate::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 IRowsetUpdate::Update was called for the row. If multiple changes were made to a row, IRowsetUpdate::Undo undoes all of these changes; the provider does not remember intermediate steps. If IRowsetUpdate::Update is called for a row immediately after IRowsetUpdate::Undo is called for the row, Update does not transmit any changes to the data store for the row. For more information about pending changes, see "Changing Data" in Chapter 5: Updating Data in Rowsets.

How IRowsetUpdate::Undo works is best illustrated in the following examples. In the first example, IRowsetUpdate::Undo backs out any changes made since the row was last fetched from the data store:

  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 IRowsetChange::DeleteRows for the row it cannot call either IRowsetChange::SetData or IRowsetChange::DeleteRows for the row.
  4. The consumer calls IRowsetUpdate::Undo for the row. The values in the row are changed to those it had after completing step 1.

In the second example, IRowsetUpdate::Undo backs out any changes made since values were last transmitted to the data store:

  1. The consumer fetches a row.
  2. The consumer updates the value in a row with IRowsetChange::SetData.
  3. The consumer repeats step 2 as often as it likes for the row.
  4. The consumer calls IRowsetUpdate::Update for the row.
  5. The consumer deletes the row with IRowsetChange::DeleteRows or updates the value in it with IRowsetChange::SetData.
  6. The consumer repeats step 5 as often as it likes for the row, except that after it calls IRowsetChange::DeleteRows for the row it cannot call either IRowsetChange::SetData or IRowsetChange::DeleteRows for the row.
  7. The consumer calls IRowsetUpdate::Undo for the row. The values in the row are changed to those it had after completing step 4.

To implement IRowsetUpdate::Undo, the provider generally caches the original values just before making a change to a row and discards the cached values when IRowsetUpdate::Undo or IRowsetUpdate::Update is called for the row. This same cache can be used by IRowsetUpdate::GetOriginalData to retrieve the original data for the row.

If IRowsetUpdate::Undo is called for a row with a pending insert, the row is deleted from the rowset. That is, calls to IRowset::GetData or IRowsetChange::SetData for the row fail with DB_E_DELETEDROW. The consumer must still call IRowset::ReleaseRows to release the row.

Whether IRowsetUpdate::Undo can undo changes made to a COM 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 IRowsetUpdate::Undo is called for a row that has a reference count of zero and exists only because the row has a pending change, IRowsetUpdate::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 IRowsetUpdate::Undo processes rows is provider-specific. If Undo encounters an error, it continues processing rows until it has attempted to undo all specified rows and then returns the appropriate warning. Because IRowsetUpdate::Undo is usually implemented by copying data from a cache of original data, such errors should be extremely rare and should usually 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 IRowsetUpdate::Update was called for it.

HRESULT Update (
   HCHAPTER        hReserved,
   DBCOUNTITEM     cRows,
   const HROW      rghRows[],
   DBCOUNTITEM    *pcRows,
   HROW          **prgRows,
   DBROWSTATUS   **prgRowStatus);

Parameters

hReserved
[in]
Reserved for future use. Must be DB_NULL_HCHAPTER.
cRows
[in]
The count of rows to update. If cRows is nonzero, IRowsetUpdate::Update updates all pending changes in the rows specified in rghRows. If cRows is zero, IRowsetUpdate::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, IRowsetUpdate::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, IRowsetUpdate::Update guarantees not to transmit any value for the row to the data store.

If rghRows includes a duplicate row, IRowsetUpdate::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 IRowsetUpdate::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 IRowsetUpdate::Update attempted to update. If rghRows is not a null pointer, 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, IRowsetUpdate::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, whether or not IRowsetUpdate::Update was successful at transmitting those changes to the data store. The consumer checks *prgRowStatus to determine which rows were updated. When rghRows is a null pointer, IRowsetUpdate::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 IRowsetUpdate::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.

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 IRowsetUpdate::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 store. 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 store. 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. 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 IRowsetUpdate::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 hReserved 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 that had not yet returned, and the provider does not support reentrancy in this method.

Comments

IRowsetUpdate::Update transmits pending changes for the specified rows to the data store and clears their pending change status. That is, it transmits any changes made to the row since it was last fetched or IRowsetUpdate::Update was called for the row. For more information about pending changes, see "Changing Data" in Chapter 5: Updating Data in Rowsets.

The order in which IRowsetUpdate::Update processes rows is provider-specific. If Update encounters an error, it continues processing rows until it has attempted to update all specified rows and 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 IRowsetUpdate::Update again and the provider guarantees it will not transmit data to the data store 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 store.

If IRowsetUpdate::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 IRowsetUpdate::GetOriginalData and IRowsetUpdate::Undo, it discards them as part of IRowsetUpdate::Update.

If the rowset is released with IUnknown::Release before IRowsetUpdate::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 to obtain a view object from a rowset on which to apply post-processing operations to an existing rowset. The consumer calls IRowsetView::CreateView to create a view object and calls methods on the view to apply post-processing operations to that view object. The consumer then calls IViewChapter::OpenViewChapter to retrieve a chapter handle that reflects the specified view conditions on the original rowset, or IViewRowset::OpenViewRowset 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 provider is an OLE DB 1.0 or 1.1 provider that does not support aggregation of the view object being created.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort 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 IRowsetView::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

IRowsetView::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 IViewFilter::GetFilter and IViewSort::GetSortOrder 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 to release the returned view when it is no longer needed.


IScopedOperations : IBindResource

The IScopedOperations interface supports recursive operations on a tree-structured namespace, such as a file system, that is bound to an OLE DB row object. The scope of the row object is defined by the tree or subtree whose root node is bound to the row object. Each node in the tree can be bound to its own row object. One of the columns in each row object can be a rowset containing a row for each child of the node that is bound to the row object. The entire tree can thus be bound by a structure of row and rowset objects.

In tree-structured namespaces such as a file system or an e-mail system, each row object can be associated with a default stream column that stores the contents of a file or an e-mail message. On the row object, the default stream column is identified by a special constant column ID (DBID). The consumer passes this column ID as an argument to IRow::Open to get the default stream. After the application has obtained the default stream from a row, it can return to the containing row by calling IGetSourceRow::GetSourceRow.

When to Implement

IScopedOperations is an optional interface on row objects. Providers may choose whether or not to support it for trees or subtrees rooted at a given row node.

URLs can be specified in either absolute or relative format but must reside with the scope of the current row object.

Note In addition to IScopedOperations, consumers can use IDBCreateCommand, which is an optional interface on row objects, to create a command object that can be used within the scope of the row object.

When to Call

Method | Description
Copy | Copies trees or subtrees from source URLs to destination URLs.
Delete | Deletes trees or subtrees specified by URLs.
Move | Moves trees or subtrees from source URLs to destination URLs.
OpenRowset | Opens a rowset containing the child rows of the current row or of a URL-named row within the scope of the current row. Due to interface and method factoring, IScopedOperations::OpenRowset is not inherited from IOpenRowset. However, it is the very same method, adapted for direct binding within the scope of the current row.
(Inherited) | All methods of IBindResource.


IScopedOperations::Copy

Copies trees or subtrees, designated by an array of source URLs, to the locations designated by a corresponding array of destination URLs.

HRESULT Copy (
   DBCOUNTITEM       cRows,
   LPCOLESTR         rgpwszSourceURLs[ ],
   LPCOLESTR         rgpwszDestURLs[ ],
   DBCOPYFLAGS       dwCopyFlags,
   IAuthenticate    *pAuthenticate,
   DBSTATUS          rgdwStatus[ ],
   LPOLESTR          rgpwszNewURLs[ ],
   OLECHAR         **ppStringsBuffer
);

Parameters

cRows
[in]
Count of trees and subtrees named by the rgpwszSourceURLs and rgpwszDestURLs arrays. If cRows is zero, all other arguments are ignored and the method returns S_OK.
rgpwszSourceURLs
[in]
Array of canonical URLs naming the source trees or subtrees to be copied. If cRows is greater than zero and an element of rgpwszSourceURLs is an empty string (L""), the current row object is copied.
rgpwszDestURLs
[in]
Array of canonical URLs naming the destination trees or subtrees. The elements of rgpwszDestURLs correspond to the elements of rgpwszSourceURLs. If DBPROP_GENERATEURL is DBPROPVAL_GU_SUFFIX, this array contains URL paths and the provider generates the URL suffixes.
dwCopyFlags
[in]
Flags describing additional semantics for the copy operation as described in the following table.

Note Some of these flags are similar to the flags used in the Microsoft® Windows® CopyFileEx file I/O function.

Flag value | Description
DBCOPY_ALLOW_EMULATION | If this flag is set and the attempt to copy the tree or subtree fails because the destination URL is on a different server or serviced by a different provider than the source, the provider is requested to attempt to simulate the copy using download and upload operations. When moving entities between providers, this may cause increased latency or data loss due to different provider capabilities.
DBCOPY_ASYNC | The copy operation is performed asynchronously. Progress and notifications are available by using IDBAsynchStatus and IDBAsynchNotify callbacks. Implementations that do not support asynchronous behavior should block and return a warning.
DBCOPY_ATOMIC | Either all trees or subtrees are copied successfully or none are copied. Whether all parts of an atomic operation are attempted if one part fails is provider-specific.
DBCOPY_NON_RECURSIVE | If this flag is set, only the root node of the tree is copied; no child nodes are copied.
DBCOPY_REPLACE_EXISTING | The copy operation succeeds even if a target object already exists at the destination URL. Otherwise, the copy fails if a target object already exists.
pAuthenticate
[in]
Optional pointer to the caller's IAuthenticate interface. If supplied, it is provider-specific whether the IAuthenticate credentials are used before or after anonymous or default login credentials are used.
rgdwStatus
[out]
Array of DBSTATUS status fields indicating whether each element of rgpwszSourceURLs was copied to rgpwszDestURLs. Whether the status fields are set for return codes other than DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED is provider-specific. The status fields described in the following table apply.
Status field | Description
DBSTATUS_S_OK | Indicates that the tree or subtree was successfully copied.
DBSTATUS_E_CANCELED | The operation was canceled, and the provider did not complete the copy operation for this resource.
DBSTATUS_E_CANNOTCOMPLETE The server that owns the source URL cannot complete the operation. The provider may return this error for one of the following reasons:
  • The server cannot service a destination URL, and the caller did not specify the DBCOPY_ALLOW_EMULATION flag.
  • The caller specified the DBCOPY_ALLOW_EMULATION flag, but some portion of the emulated operation failed.
  • The source and destination URLs overlap.
  • The consumer called IDBAsynchStatus::Abort in the middle of the operation.
DBSTATUS_E_DOESNOTEXIST | Indicates that either the source URL or the parent of the destination URL does not exist.
DBSTATUS_E_INVALIDURL | A source or destination URL string contains invalid characters.
DBSTATUS_E_NOTCOLLECTION | The destination URL contained in rgpwszDestURLs did not specify a path or collection as required by the operation.
DBSTATUS_E_OUTOFSPACE | The provider is unable to obtain enough storage space to complete the copy operation.
DBSTATUS_E_PERMISSIONDENIED | Unable to access a tree or subtree because of a permissions failure.
DBSTATUS_E_RESOURCEEXISTS | The provider was unable to perform the copy because an object already exists at the destination URL and the DBCOPY_REPLACE_EXISTING flag was not set.
DBSTATUS_E_RESOURCELOCKED | One or more other processes using the DBBINDURLFLAG_SHARE_* flag have locked the object named by this URL. If supported, IErrorInfo::GetDescription returns a string consisting of user names separated by semicolons. These are the names of the users who have the object locked.
DBSTATUS_E_RESOURCEOUTOFSCOPE | A source or destination URL is outside the scope of the current row object.
DBSTATUS_E_UNAVAILABLE | An atomic operation failed to complete, and status was unavailable for this part of the operation.
DBSTATUS_E_VOLUMENOTFOUND | The provider is unable to locate the storage volume indicated by a URL.
rgpwszNewURLs
[in/out]
Optional consumer-allocated array of cRows pointers to the provider-assigned and allocated canonical destination URLs. The caller may pass a null value to indicate it does not want the provider to return destination URLs. This parameter should be set to NULL if an error code is returned.
ppStringsBuffer
[out]
A pointer to memory in which to return a pointer to storage for all generated URL strings within a single allocation block. When it is finished working with the URLs, the consumer should free this buffer. Each of the individual string values stored in this buffer is terminated by a null character. If cRows is zero or this method terminates due to an error, the provider sets ppStringsBuffer to a null pointer. If rgpwszNewURLs is a null pointer, the consumer must set ppStringsBuffer to NULL as well.

Return Code

S_OK
All requested copy operations succeeded. All elements of rgdwStatus passed to the method are set to DBPROPSTATUS_OK.
DB_S_ASYNCHRONOUS
The method has initiated an asynchronous copy operation. The consumer can call IDBAsynchStatus::GetStatus to poll for status or can register for notifications of asynchronous processing.

DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED.

DB_S_BUFFERFULL
All copy operations succeeded, but the provider was unable to allocate ppStringsBuffer.
DB_S_ERRORSOCCURRED
DBCOPY_ATOMIC was not set, and the provider failed to copy from one or more source URL. The caller should examine rgdwStatus to determine which source URLs were not copied and why.

DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED. Once rowset population is complete, the consumer can see DB_S_ERRORSOCCURRED either by calling IDBAsynchStatus::GetStatus or by receiving IDBAsynchNotify::OnStop.

DB_E_ASYNCNOTSUPPORTED
The provider does not support asynchronous copy operations.
DB_E_CANCELED
The operation was called synchronously and, before it completed, was canceled by a call to IDBAsynchStatus::Abort. The consumer can examine rgdwStatus for a status of DBSTATUS_E_CANCELED to determine whether an individual URL was copied.
DB_E_ERRORSOCCURRED
DBCOPY_ATOMIC was set, and the provider failed to copy one or more source URLs. The caller should examine rgdwStatus to determine which URLs were not copied and why.
DB_E_NOTSUPPORTED
The provider does not support the value of dwCopyFlags.
DB_SEC_E_SAFEMODE_DENIED
The provider was called within a safe mode or context, and one of the URLs specified was unsafe.

Note When failing one of multiple operations, the status of the failing operation is DBSTATUS_E_CANNOTCOMPLETE and the status of any remaining uncompleted operations is DBSTATUS_E_UNAVAILABLE.

E_FAIL
A provider-specific error occurred.
E_INVALIDARG
cRows is not zero; and rgpwszSourceURLs, rgpwszDestURLs, or rgdwStatus is a null pointer.

cRows is not zero, and one of rgpwszNewURLs and ppStringsBuffer, but not both, are null pointers.

cRows is not zero, and an element of rgpwszSourceURLs or rgpwszDestURLs is a null pointer.

E_OUTOFMEMORY
The provider did not have enough memory to complete the operation.
E_UNEXPECTED
ITransaction::Commit, ITransaction::Abort, or IScopedOperations::Delete (on this row) was called, and the object is in a zombie state.

Comments

Providers that implement IScopedOperations on a row must at a minimum support IScopedOperations::Copy for source and destination URLs within the tree rooted at the row. It is provider-specific whether source and destination URLs outside the scope of this row can be operated on.

Rows cannot be copied onto themselves. In addition, the destination URL cannot be a descendant of the source URL on a recursive copy; if it were, the operation would recurse. If a provider is unable to perform a copy operation because the source and destination trees overlap, it returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, as appropriate, and sets the corresponding element of rgdwStatus to DBSTATUS_E_CANNOTCOMPLETE.

When calling IScopedOperations::Copy, a consumer can request asynchronous processing by setting the DBCOPY_ASYNC bit of dwCopyFlags. For more information on asynchronous processing in OLE DB, see Chapter 17: Asynchronous Processing.

The caller should convert noncanonical URLs to canonical form before calling this method. Providers must return URLs in canonical form. For more information, see the Win32® Internet API function InternetCanonicalizeUrl in the Site Builder Network (SBN) Workshop documentation at http://msdn.microsoft.com/workshop/networking/default.asp.

Providers may own the responsibility of determining the suffix portion of the destination URLs. If DBPROP_GENERATEURL is DBPROPVAL_GU_SUFFIX, the caller specifies the path of each destination URL in rgpwszDestURLs. The provider generates the URL suffix. If DBPROP_GENERATEURL is DBPROPVAL_GU_NOTSUPPORTED, the provider does not generate destination URLs. The consumer must pass all destination URLs in rgpwszDestURLs. The provider ignores rgpwszNewURLs. In either case, if the caller passes pointers in rgpwszNewURLs and ppStringsBuffer, the provider returns the pointers to the absolute URLs and to the URL strings using these arguments. If the consumer does not wish to receive the destination URLs, even if they were generated by a provider who supports DBPROP_GENERATEURL as DBPROPVAL_GU_SUFFIX, the consumer should set both rgpwszNewURLs and ppStringsBuffer to NULL.

If all copy operations succeed but the provider is unable to allocate ppStringsBuffer, the provider should return DB_S_BUFFERFULL and set the output arguments rgpwszNewURLs and ppStringsBuffer to NULL.


IScopedOperations::Delete

Deletes the trees or subtrees named by an array of URLs. The URLs must all be within the scope of the current row object.

HRESULT Delete (
   DBCOUNTITEM     cRows,
   LPCOLESTR       rgpwszURLs[ ],
   DBDELETEFLAGS   dwDeleteFlags,
   DBSTATUS        rgdwStatus[ ]
);

Parameters

cRows
[in]
Count of trees and subtrees named by the rgpwszURLs array. If cRows is zero, all other arguments are ignored and the method returns S_OK.
rgpwszURLs
[in]
Array of canonical URLs naming the trees or subtrees to be deleted. If cRows is greater than zero and an element of rgpwszURLs is an empty string (L""), the current row object is deleted.
dwDeleteFlags
[in]
A bitmask of flags that control additional semantics for the delete operation, as described in the following table.
Flag value | Description
DBDELETE_ASYNC | The delete operation is performed asynchronously. Progress and notifications are available via IDBAsynchStatus and IDBAsynchNotify callbacks. Implementations that do not support asynchronous behavior should block and return a warning. If DBDELETE_ASYNC is not specified, the operation is synchronous. A value of 0 is allowed.
DBDELETE_ATOMIC | Either all trees and subtrees are deleted or none are deleted. Whether all parts of an atomic operation are attempted if one part fails is provider-specific. If DBDELETE_ATOMIC is not specified, the operation is non-atomic. A value of 0 is allowed.
rgdwStatus
[out]
Array of DBSTATUS status fields indicating, for each element of rgpwszURLs, whether the tree or subtree named by that element was deleted. Whether the status fields are set for return codes other than DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED is provider-specific. The status fields described in the following table apply.
Status field | Description
DBSTATUS_S_OK | Indicates that the tree or subtree was deleted.
DBSTATUS_E_CANCELED | The operation was canceled, and the provider did not complete the delete operation for this resource.
DBSTATUS_E_DOESNOTEXIST | Indicates that the tree or subtree named by this URL did not exist.
DBSTATUS_E_INVALIDURL | A URL string contained invalid characters.
DBSTATUS_E_PERMISSIONDENIED | Unable to access a tree or subtree due to a permissions failure.
DBSTATUS_E_RESOURCELOCKED | One or more other processes using the DBBINDURLFLAG_SHARE_* flag have locked the object named by this URL. If supported, IErrorInfo::GetDescription returns a string consisting of user names separated by semicolons. These are the names of the users who have the object locked.
DBSTATUS_E_RESOURCEOUTOFSCOPE | A URL to be deleted is outside the scope of the current row object.
DBSTATUS_E_UNAVAILABLE | An atomic operation failed to complete, and status was unavailable for this part of the operation.
DBSTATUS_E_VOLUMENOTFOUND | The provider is unable to locate the storage volume named by a URL.

Return Code

S_OK
All requested deletions succeeded. All elements of rgdwStatus passed to the method are set to DBPROPSTATUS_OK.
DB_S_ASYNCHRONOUS
The method has initiated an asynchronous delete operation. The consumer can call IDBAsynchStatus::GetStatus to poll for status or can register for notifications of asynchronous processing.

DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED.

DB_S_ERRORSOCCURRED
DBDELETE_ATOMIC was not set, and the provider failed to delete one or more URLs. The caller should examine rgdwStatus to determine which URLs were not deleted and why.

DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED. Once rowset population is complete, the consumer can see DB_S_ERRORSOCCURRED either by calling IDBAsynchStatus::GetStatus or by receiving IDBAsynchNotify::OnStop.

DB_E_ASYNCNOTSUPPORTED
The provider does not support asynchronous deletions.
DB_E_CANCELED
The operation was called synchronously and, before it completed, was canceled by a call to IDBAsynchStatus::Abort. The consumer can examine rgdwStatus for a status of DBSTATUS_E_CANCELED to determine whether an individual URL was deleted.
DB_E_ERRORSOCCURRED
DBDELETE_ATOMIC was set, and the provider failed to delete one or more URLs. The caller should examine rgdwStatus to determine which URLs were not deleted and why.
DB_E_NOTSUPPORTED
The provider does not support the value of dwDeleteFlags.
DB_SEC_E_SAFEMODE_DENIED
The provider was called within a safe mode or context, and one of the URLs specified was unsafe.

Note When failing one of multiple operations, the status of the failing operation is DBSTATUS_E_CANNOTCOMPLETE and the status of any remaining, uncompleted operations is DBSTATUS_E_UNAVAILABLE.

E_FAIL
A provider-specific error occurred.
E_INVALIDARG
cRows is not zero, and rgpwszURLs or rgdwStatus is a null pointer.

cRows is not zero, and an element of rgpwszURLs is a null pointer.

E_OUTOFMEMORY
The provider did not have enough memory to complete the operation.
E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called, and the object is in a zombie state.

Comments

When the current row object is deleted, it enters a zombie state, and the provider should return E_UNEXPECTED for any other method call on the row object other than Release.

Providers that implement IScopedOperations on a row must at a minimum support Delete for URLs within the tree rooted at the row. It is provider-specific whether URLs outside the scope of this row can be operated on.

If IScopedOperations::Delete is called with an empty string for the URL or for the absolute URL of the object that is bound, the object is deleted. The only operation the consumer may then perform on the object is IUnknown::Release. All other methods on all other interfaces might return E_UNEXPECTED. (The exact behavior is provider-dependent.)

IScopedOperations::Delete can affect currently open rowset and row objects when the underlying object is deleted. Binding to a URL that has been deleted or moved will result in DB_E_NOTFOUND. Other methods that cause the provider to access the underlying data store will result in similar errors or DB_E_DELETEDROW. Refer to the return codes for each method.

When calling IScopedOperations::Delete, a consumer can request asynchronous processing by setting the DELETE_ASYNC bit of dwDeleteFlags. For more information on asynchronous processing in OLE DB, see Chapter 17: Asynchronous Processing.

The caller should convert noncanonical URLs to canonical form before invoking this method. For more information, see the Win32® Internet API function InternetCanonicalizeUrl in the Site Builder Network (SBN) Workshop documentation at http://msdn.microsoft.com/workshop/networking/default.asp.


IScopedOperations::Move

Moves trees or subtrees that are represented by OLE DB row objects. The trees or subtrees are designated by an array of source URLs and are moved to the locations designated by a corresponding array of destination URLs.

HRESULT Move (
   DBCOUNTITEM       cRows,
   LPCOLESTR         rgpwszSourceURLs[ ],
   LPCOLESTR         rgpwszDestURLs[ ],
   DBMOVEFLAGS       dwMoveFlags,
   IAuthenticate    *pAuthenticate,
   DBSTATUS          rgdwStatus[ ],
   LPOLESTR          rgpwszNewURLs[ ],
   OLECHAR         **ppStringsBuffer
);

Parameters

cRows
[in]
Count of rows named by the rgpwszSourceURLs and rgpwszDestURLs arrays. If cRows is zero, all other arguments are ignored and the method returns S_OK.
rgpwszSourceURLs
[in]
Array of canonical URLs naming the trees or subtrees to be moved from the source location. If cRows is greater than zero and an element of rgpwszSourceURLs is an empty string (L""), the current row object is moved.
rgpwszDestURLs
[in]
Array of canonical URLs naming the destination to which the trees or subtrees are to be moved. The elements of rgpwszDestURLs correspond to the elements of rgpwszSourceURLs. If DBPROP_GENERATEURL is DBPROPVAL_GU_SUFFIX, this array contains URL paths and the provider generates the URL suffixes. If DBPROP_GENERATEURL is DBPROPVAL_GU_NOTSUPPORTED and the caller passes a null value, the provider does not return the absolute URLs of the moved URLs.
dwMoveFlags
[in]
Flags describing additional semantics for the move operation as described in the following table.
Flag value | Description
DBMOVE_ALLOW_EMULATION | If the attempt to move the row fails because the destination URL is on a different server or serviced by a different provider than the source, the provider is requested to attempt to simulate the move using download, upload, and delete operations. When moving resources between providers, this may cause increased latency or data loss due to different provider capabilities.
DBMOVE_ASYNC | The move operation is performed asynchronously. Progress and notifications are available via IDBAsynchStatus and IDBAsynchNotify callbacks. Implementations that do not support asynchronous behavior should block and return a warning.
DBMOVE_ATOMIC | Either all sources are moved successfully or no sources are moved. Whether all parts of an atomic operation are attempted if one part fails is provider-specific.
DBMOVE_DONT_UPDATE_LINKS Request the server not to update links contained in an object on a move.

If this flag is not specified, the default behavior is to do what the server specifies. By default, IScopedOperations::Move will update links if the server is capable and if the option is turned on. If the server does not have the ability to fix up links or if the option is turned off, this call will still return S_OK.
DBMOVE_REPLACE_EXISTING | The move operation succeeds, even if a target object exists. Without this value, the move will fail if the target object already exists.
pAuthenticate
[in]
Optional pointer to the IAuthenticate interface. If supplied, it is provider-specific whether the IAuthenticate credentials are used before or after anonymous or default login credentials are used.
rgdwStatus
[out]
Array of DBSTATUS status fields indicating whether each element of rgpwszSourceURLs was moved to rgpwszDestURLs. Whether the status fields are set for return codes other than DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED is provider-specific. The status fields described in the following table apply to the move operations.
Status field | Description
DBSTATUS_S_OK | Indicates that a tree or subtree was successfully moved.
DBSTATUS_E_CANCELED | The operation was canceled, and the provider did not complete the move operation for this resource.
DBSTATUS_S_CANNOTDELETESOURCE | A tree or subtree was moved to a new location, but the source tree or subtree could not be deleted.
DBSTATUS_E_CANNOTCOMPLETE The server that owns the source URL cannot complete the operation. The provider may return this error for one of the following reasons:
  • The server cannot service a destination URL, and the caller did not specify the DBMOVE_ALLOW_EMULATION flag.
  • The caller specified the DBMOVE_ALLOW_EMULATION flag, but some portion of the emulated operation failed.
  • The source and destination URLs overlap.
  • The consumer called IDBAsyncStatus::Abort in the middle of the operation.
DBSTATUS_E_DOESNOTEXIST | Indicates that either the source URL or the parent of the destination URL does not exist.
DBSTATUS_E_INVALIDURL | A source or destination URL string contains invalid characters.
DBSTATUS_E_NOTCOLLECTION | The destination URL contained in rgpwszDestURLs did not specify a path or collection as required by the operation.
DBSTATUS_E_OUTOFSPACE | The provider is unable to obtain enough storage space to complete the move operation.
DBSTATUS_E_PERMISSIONDENIED | Unable to access a tree or subtree because of a permissions failure.
DBSTATUS_E_RESOURCEEXISTS | The provider was unable to perform the move because an object already exists at the destination URL and the DBMOVE_REPLACE_EXISTING flag was not set.
DBSTATUS_E_RESOURCELOCKED | One or more other processes using the DBBINDURLFLAG_SHARE_* flag have locked the object named by this URL. If supported, IErrorInfo::GetDescription returns a string consisting of user names separated by semicolons. These are the names of the users who have the object locked.
DBSTATUS_E_RESOURCEOUTOFSCOPE | A source or destination URL is outside the scope of the current row object.
DBSTATUS_E_UNAVAILABLE | An atomic operation failed to complete, and status was unavailable for this part of the operation.
DBSTATUS_E_VOLUMENOTFOUND | The provider is unable to locate the storage volume indicated by a URL.
rgpwszNewURLs
[in/out]
Optional consumer-allocated array of cRows pointers to the provider-assigned and allocated canonical destination URLs. The caller may pass a null value to indicate it does not want the provider to return destination URLs. This parameter should be set to NULL if an error code is returned.
ppStringsBuffer
[out]
A pointer to memory in which to return a pointer to storage for all generated URL strings within a single allocation block. When it is finished working with the URLs, the consumer should free this buffer. Each of the individual string values stored in this buffer is terminated by a null character. If cRows is zero or this method terminates due to an error, the provider sets ppStringsBuffer to a null pointer. If rgpwszNewURLs is a null pointer, the consumer must set ppStringsBuffer to NULL as well.

Return Code

S_OK
All requested move operations succeeded. All elements of rgdwStatus passed to the method are set to DBPROPSTATUS_OK.
DB_S_ASYNCHRONOUS
The method has initiated an asynchronous move operation. The consumer can call IDBAsynchStatus::GetStatus to poll for status or can register for notifications of asynchronous processing.

DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED.

DB_S_BUFFERFULL
All move operations succeeded, but the provider was unable to allocate ppStringsBuffer.
DB_S_ERRORSOCCURRED
DBMOVE_ATOMIC was not set, and the provider failed to move one or more source URLs. The caller should examine rgdwStatus to determine which source URLs were not copied from and why.

DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED. Once rowset population is complete, the consumer can see DB_S_ERRORSOCCURRED either by calling IDBAsynchStatus::GetStatus or by receiving IDBAsynchNotify::OnStop.

DB_E_ASYNCNOTSUPPORTED
The provider does not support asynchronous moves.
DB_E_CANCELED
The operation was called synchronously and, before it completed, was canceled by a call to IDBAsynchStatus::Abort. The consumer can examine rgdwStatus for a status of DBSTATUS_E_CANCELED to determine whether an individual URL was moved.
DB_E_ERRORSOCCURRED
DBMOVE_ATOMIC was set, and the provider failed to move one or more source URLs. The caller should examine rgdwStatus to determine which URLs were not moved and why.
DB_E_NOTSUPPORTED
The provider does not support the value of dwMoveFlags.
DB_SEC_E_SAFEMODE_DENIED
The provider was called within a safe mode or context, and one of the URLs specified was unsafe.

Note When failing one of multiple operations, the status of the failing operation is DBSTATUS_E_CANNOTCOMPLETE and the status of any remaining, uncompleted operations is DBSTATUS_E_UNAVAILABLE.

E_FAIL
A provider-specific error occurred.
E_INVALIDARG
cRows is not zero; and rgpwszSourceURLs, rgpwszDestURLs, or rgdwStatus is a null pointer.

cRows is not zero, and one of rgpwszNewURLs and ppStringsBuffer, but not both, are null pointers.

cRows is not zero, and an element of rgpwszSourceURLs or rgpwszDestURLs is a null pointer.

E_OUTOFMEMORY
The provider did not have enough memory to complete the operation.
E_UNEXPECTED
ITransaction::Commit, ITransaction::Abort, or IScopedOperations::Delete (on this row) was called, and the object is in a zombie state..

Comments

Providers implementing IScopedOperations on a row must at a minimum support IScopedOperations::Move for source and destination URLs within the tree rooted at the row. It is provider-specific whether source and destination URLs outside the scope of this row can be operated on.

Rows cannot be moved onto themselves. In addition, the destination URL cannot be a descendant of the source URL on a recursive move; if it were, the operation would recurse. If a provider is unable to perform a move operation because the source and destination trees overlap, it returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, as appropriate, and sets the corresponding element of rgdwStatus to DBSTATUS_E_CANNOTCOMPLETE.

When invoking IScopedOperations::Move, a consumer can request asynchronous processing by setting the DBMOVE_ASYNC bit of dwMoveFlags. For more information on asynchronous processing in OLE DB, see Chapter 17: Asynchronous Processing.

The caller should convert noncanonical URLs to canonical form before invoking this method. Providers must return URLs in canonical form. For more information, see the Win32® Internet API function InternetCanonicalizeUrl in the Site Builder Network (SBN) Workshop documentation at http://msdn.microsoft.com/workshop/networking/default.asp.

Providers may own the responsibility of determining the suffix portion of the destination URLs. If DBPROP_GENERATEURL is DBPROPVAL_GU_SUFFIX, the caller specifies the path of each destination URL in rgpwszDestURLs. The provider generates the URL suffix. If DBPROP_GENERATEURL is DBPROPVAL_GU_NOTSUPPORTED, the provider does not generate destination URLs. The consumer must pass all destination URLs in rgpwszDestURLs. The provider ignores rgpwszNewURLs. In either case, if the caller passes pointers in rgpwszNewURLs and ppStringsBuffer, the provider returns the pointers to the absolute URLs and the URL strings using these arguments. If the consumer does not wish to receive the destination URLs, even if they were generated by a provider who supports DBPROP_GENERATEURL as DBPROPVAL_GU_SUFFIX, the consumer should set both rgpwszNewURLs and ppStringsBuffer to NULL.

If all move operations succeed but the provider is unable to allocate ppStringsBuffer, the provider should return DB_S_BUFFERFULL and set the output arguments rgpwszNewURLs and ppStringsBuffer to NULL.

IScopedOperations::Move can affect currently open rowsets and rows when the underlying object is moved. Binding to a URL that has been moved will result in DB_E_NOTFOUND. Other methods that cause the provider to access the underlying store will result in similar errors or DB_E_DELETEDROW. Refer to the return codes for each method.


IScopedOperations::OpenRowset

Opens and returns a rowset containing the child rows of the current row or of a URL-named row within the scope of the current row. Due to interface and method factoring, IScopedOperations::OpenRowset is not inherited from IOpenRowset. However, it is the very same method, adapted for direct binding within the scope of the current row.

HRESULT OpenRowset (
   IUnknown     *pUnkOuter,
   DBID         *pTableID,
   DBID         *pIndexID,
   REFIID        riid,
   ULONG         cPropertySets,
   DBPROPSET     rgPropertySets[ ],
   IUnknown    **ppRowset
);

Parameters

pUnkOuter
[in]
If the rowset is to be aggregated, pUnkOuter is an interface pointer to the controlling IUnknown. Otherwise, it is a null pointer.
pTableID
[in]
The table ID (DBID) of the table to open. When implemented on row objects, this is an absolute or relative URL within the scope of the current row. If one of the following is true, pTableID designates the current row and the rowset will contain the child rows of this row:
  • pTableID is NULL.
  • pTableID is not NULL, pTableID->eKind is DBKIND_NAME, and pTableID->uName.pwszName is NULL.
  • pTableID is not null, pTableID->eKind is DBKIND_NAME, and pTableID->uName.pwszName is L"".
pIndexID
[in]
The DBID of the index to open. Not currently used in IScopedOperations. Consumers must set pIndexID to NULL. 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 in these structures must belong to the rowset property group. If the same property is specified more than once in rgPropertySets, the value used is provider-specific. If cPropertySets is zero, this argument is ignored.

For information about the rowset properties, see the rowset property group in Appendix C: OLE DB Properties. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 14: Properties.

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 IScopedOperations::OpenRowset fails, *ppRowset is set to a null pointer.

Return Code

S_OK
The method succeeded, and the rowset was opened.
DB_S_ASYNCHRONOUS
The method has initiated asynchronous creation of the rowset. The consumer can call IDBAsynchStatus to poll for status or can call IConnectionPointContainer to obtain the IID_IDBAsynchNotify connection point. Attempting to call any other interfaces might fail, and the full set of interfaces might not be available on the object until asynchronous initialization of the rowset has completed. DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED.
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 that was specified to be applied to all columns 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.
  • The property was not in the Rowset property group.
  • DB_S_ASYNCHRONOUS should be returned before DB_S_ERRORSOCCURRED. Once rowset population is complete, the consumer can see DB_S_ERRORSOCCURRED either by calling IDBAsynchStatus::GetStatus or by receiving IDBAsynchNotify::OnStop.
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 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.
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.
DB_E_NOAGGREGATION
pUnkOuter was not a null pointer, and the provider is an OLE DB 1.0 or 1.1 provider that does not support aggregation of the rowset object being created.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

DB_E_NOINDEX
The specified index does not exist in the current data store or did not apply to the specified table.

The provider does not support opening indexes through IScopedOperations.

DB_E_NOTABLE
The specified URL does not exist in the data store or does not reside within the scope of the current row.
DB_E_NOTCOLLECTION
pTableID specified a URL that does not name a collection.

pTableID was a null pointer or specified an empty string, and the current row is not a collection.

DB_E_OBJECTOPEN
The provider would have to open a new connection to support the operation, and DBPROP_MULTIPLECONNECTIONS is set to VARIANT_FALSE.
DB_E_RESOURCEOUTOFSCOPE
pTableID named a URL that is not within the scope of the current row.
DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to open the rowset object. For example, the rowset might have included a column for which the consumer does not have read permission.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pTableID is not NULL, and pTableID->eKind is not DBKIND_NAME.

ppRowset was a null pointer.

cPropertySets was not zero, and rgPropertySets was a null pointer.

In an element of rgPropertySets, cPropertieswas not zero and rgProperties was a null pointer.

pIndexID was not NULL.

E_NOINTERFACE
The rowset did not support the interface specified in riid, or riid was IID_NULL.
E_OUTOFMEMORY
The provider did not have enough memory to create the rowset.
E_UNEXPECTED
ITransaction::Commit, ITransaction::Abort, or IScopedOperations::Delete (on this row) was called, and the object is in a zombie state.

Comments

The IScopedOperations::OpenRowset syntax was copied from IOpenRowset. However, pIndexID does not apply to rowsets, and the consumers must set it to NULL.

When invoking IScopedOperations::OpenRowset, a consumer can request asynchronous processing by setting a DBPROPVAL_ASYNCH_* bit of the DBPROP_ROWSET_ASYNCH property. When this flag is set, the provider returns immediately from the call to IScopedOperations::OpenRowset, returning either S_OK, if the operation has completed immediately, or DB_S_ASYNCHRONOUS, if the operation will continue asynchronously. Consumers can call IDBAsynchStatus::GetStatus to get the status of the operation or can register for progress notifications by passing the consumer's IDBAsynchNotify interface to the requested object's connection point. The consumer must explicitly request an asynchronous operation. Otherwise, IScopedOperations::OpenRowset does not return until the requested operation is completed. For more information on asynchronous processing in OLE DB, see Chapter 17: Asynchronous Processing.

If multiple IScopedOperations operations are invoked asynchronously on the same row object, the behavior of IDBAsynchStatus and IDBAsynchNotify is undefined.

See Also

IOpenRowset


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 COM 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 it is not required to provide it separately even when an IStream implementation is provided. 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.

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.
Release | Decrements the reference count.
ISequentialStream Methods | Description
ISequentialStream::Read | Reads a specified number of bytes from the stream object into memory, starting at the current seek pointer.
ISequentialStream::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
ISequentialStream::Read | Reads a specified number of bytes from the stream.
ISequentialStream::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,
   ULONG     cb,
   ULONG    *pcbRead);

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 the documentation on the COM interface IFillLockBytes, and see "Asynchronous Storage" in the Structured Storage documentation.
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 into 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 the documentation on the COM interface IFillLockBytes, and see "Asynchronous Storage" in the Structured Storage documentation.
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 cPropertyIDSets 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, 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 14: Properties.

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, 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, 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 14: Properties.

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. ISessionProperties::GetProperties can fail to return properties for a number of reasons, including the following:
  • 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


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, the value 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 14: Properties.

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. ISessionProperties::SetProperties can fail to set properties for a number of reasons, including the following:
  • 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.

Comments

Generally, consumers should not set the same property twice, because if they do, the value of the property is provider-specific, meaning it is unknown to the consumer. The status for the successfully set value should be DBPROPSTATUS_OK, but it is provider-specific what the status value is for the other property values.

See Also


ISourcesRowset

ISourcesRowset returns a rowset of data source objects and enumerators visible from the current enumerator. For more information about enumerators, see "Enumerators" in Chapter 2: Data Source and Session Objects.

Method | Description
GetSourcesRowset | Returns a rowset of the data source objects and enumerators visible from the current enumerator.


ISourcesRowset::GetSourcesRowset

Returns a rowset of the data source objects and enumerators visible from the current enumerator.

HRESULT GetSourcesRowset(
   IUnknown     *pUnkOuter,
   REFIID        riid,
   ULONG         cPropertySets,
   DBPROPSET     rgPropertySets[],
   IUnknown    **ppSourcesRowset);

Parameters

pUnkOuter
[in]
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
[in]
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, 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 14: Properties.

ppSourcesRowset
[in]
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 following:
  • 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 that was specified to be applied to all columns could not be applied to one or more columns.
  • The property was not supported by the provider.
  • The property set was not supported by the provider.
  • It was not possible to set the property.
  • The property was not in the Rowset property group.
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, or riid is IID_NULL.
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.
DB_E_NOAGGREGATION
pUnkOuter was not a null pointer, and the provider is an OLE DB 1.0 or 1.1 provider that does not support aggregation of the rowset object being created.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

DB_E_NOTFOUND
The provider supports the return of singleton row objects on a method that typically returns a rowset, a row was requested via riid or DBPROP_IRow, and no rows existed in the rowset.

Comments

ISourcesRowset::GetSourcesRowset returns the following rowset. The rowset is read-only. The columns are returned in the order shown in the following table.

Column name | Type indicator | Description
SOURCES_NAME | DBTYPE_WSTR | The name of the data store or enumerator.
SOURCES_PARSENAME | DBTYPE_WSTR | String to pass to IParseDisplayName to obtain a moniker for the data source object or enumerator.
SOURCES_DESCRIPTION | DBTYPE_WSTR | The description of the data source object or enumerator.
SOURCES_TYPE DBTYPE_UI2 Whether the row describes a data source object or an enumerator:
  • DBSOURCETYPE_BINDER—Indicating a provider binder that supports direct URL binder
  • DBSOURCETYPE_DATASOURCE_MDP—Indicating a multidimensional (OLAP) provider
  • DBSOURCETYPE_DATASOURCE_TDP—Indicating a tabular data provider
  • DBSOURCETYPE_ENUMERATOR—Indicating an enumerator

If a single piece of code is capable of being used both as a data source object 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 object, 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 5-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

OLE DB Programmer's Reference

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

ISupportErrorInfo::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 IErrorRecords::GetErrorInfo in the Automation DLL if ISupportErrorInfo::InterfaceSupportsErrorInfo returns S_OK. IErrorRecords::GetErrorInfo returns an IErrorInfo interface pointer on the OLE DB error object. If ISupportErrorInfo::InterfaceSupportsErrorInfo returns S_FALSE, the consumer should discard any error object returned by IErrorRecords::GetErrorInfo because it applies to a different method and interface.


ITableCreation : ITableDefinition

The ITableCreation extends ITableDefinition to provide a full set of methods to create or describe the complete set of table definition information.

When to Implement

ITableCreation is an optional provider-supported interface that can describe the properties used in the creation of a table. If the provider implements ITableDefinitionWithConstraint, it must also implement ITableCreation because ITableDefinitionWithConstraints inherits from ITableCreation.

Method | Description
GetTableDefinition | Adds a new column to a base table.
(Inherited) | All methods of ITableDefinition.


ITableCreation::GetTableDefinition

Returns creation information for a table.

HRESULT GetTableDefinition(
   DBID               *pTableID,
   DBORDINAL          *pcColumnDescs,
   DBCOLUMNDESC       *prgColumnDescs[],
   ULONG              *pcPropertySets,
   DBPROPSET          *prgPropertySets[],
   ULONG              *pcConstraintDescs,
   DBCONSTRAINTDESC   *prgConstraintDescs[],
   OLECHAR           **ppwszStringBuffer);

Parameters

pTableID
[in]
A pointer to the ID of the table to describe.
pcColumnDescs
[out]
A pointer to the number of DBCOLUMNDESC structures in the prgColumnDescs array. If pcColumnDescs is NULL, the provider ignores prgColumnDescs and does not return any column descriptions.
prgColumnDescs
[out]
A pointer to an array of DBCOLUMNDESC structures that describe the columns of the table, or NULL if the consumer is not interested in getting back column descriptions. For more information on the DBCOLUMNDESC structure, see ITableDefinition::CreateTable.
pcPropertySets
[out]
A pointer to the number of DBPROPSET structures in prgPropertySets. If this is NULL, the provider ignores prgPropertySets and does not return any table creation properties.
prgPropertySets
[out]
A pointer to an array of DBPROPSET structures containing properties and values used in creation of the table, or NULL if the consumer is not interested in getting back table creation properties. The properties returned in these structures belong to the Table property group.

For information about the properties in the Tables property groups that are defined by OLE DB, see "Table Property Group" in Appendix C: OLE DB Properties. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 14: Properties.

pcConstraintDescs
[out]
A pointer to the number of DBCONSTRAINTDESC structures in the prgConstraintDescs array. If this is NULL, the provider ignores the value of prgConstraintDescs and does not return any column descriptions.
prgConstraintDescs
[out]
A pointer to an array of DBCONSTRAINTDESC structures that describe the columns of the table, or NULL if no constraint definitions are to be returned to the consumer. For more information on DBCONSTRAINTDESC structure, see ITableDefinitionWithConstraints::AddConstraint.
ppwszStringBuffer
[out]
A pointer to memory in which to return a pointer to storage for all string values returned in the pwszTypeName element of DBCOLUMNDESC structure or the pwszConstraintText element of the DBCONSTRAINTDESC structure. The provider allocates this memory with IMalloc, and the consumer frees it with IMalloc::Free when it no longer needs the descriptions. If ppwszStringBuffer is a null pointer on input, ITableCreation::GetTableDefinition does not return the string values. If no shared memory is allocated for pwszTypeName or pwszConstraintText for any elements of prgColumnDescs or prgConstraintDescs, respectively, or if an error occurs, the provider does not allocate any memory and ensures that *ppwszStringBuffer 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
pTableID was a null pointer.

pcColumnDescs was not null, and prgColumnDescs was a null pointer.

pcPropertySets was not null, and prgPropertySets was a null pointer.

pcConstraintDescs was not null, and prgConstraintDescs was a null pointer.

pcColumnDescs, pcPropertySets, and pcConstraintDescs were all null pointers.

DB_E_NOTABLE
The table specified in pTableID does not exist in the current data source.

Comments

ITableCreation::GetTableDefinition returns the properties and column definitions of the specified table. The provider also returns the definitions of the constraints currently applied to the table. These constraints may be different from the constraints with which the table was initially created if the consumer has added or dropped constraints since the table was created.

Providers are allowed to use a static property array to report table and column properties returned by this method, either within prgColumnDescs or within prgPropertySets. If a property does not apply to the table or column or if the provider cannot determine the setting for a table or column property, the provider can return DBPROPSTATUS_OK and a property value of VT_EMPTY.

See Also

ITableDefinition::CreateTable


ITableDefinition

The ITableDefinition interface exposes simple methods to create, drop, and alter tables on the data store.

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 store.
DropColumn | Drops a column from a base table.
DropTable | Drops a base table in the data store.


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. For more information about the DBCOLUMNDESC structure, see ITableDefinition::CreateTable.
ppColumnID
[out]
A pointer to memory in which to return the returned DBID of a 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.

Note The contents of *ppColumnID might not exactly match the passed dbcid in *pColumnDesc. The consumer should use *ppColumnID to identify the newly created column. If ppColumnID is NULL on input and the provider cannot create a column that exactly matches dbcid in *pColumnDesc, AddColumn should fail with DB_E_BADCOLUMNID.

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 colid element of the DBPROP structure for the property 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.
  • The property was not supported by the provider.
  • It was not possible to set the property.
  • The property set was not supported by the provider.
  • The property was not in the Column, Rowset, or Table property group.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pTableID or pColumnDesc was a null pointer.

cPropertySets was not zero, and rgPropertySets was a null pointer in the DBCOLUMNDESC structure.

In an element of rgPropertySets, cProperties was not zero and rgProperties 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_BADTYPE
The wType, pwszTypeName, or pTypeInfo element, in an element of rgColumnDescs was invalid.

In an element of rgColumnDescs, pwszTypeName was non-null and implied a DBTYPE other than the type specified by wType.

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 store.
DB_E_TABLEINUSE
The specified table was in use.
DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to add a column.
XACT_E_XTIONEXISTS
The provider supports transactional DDL, the session is participating in a transaction, and the value of DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DML.

Comments

If ITableDefinition::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.

If the session is participating in a transaction, if DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DDL_IGNORE, and if the method succeeds, the operation is complete and is unaffected by subsequent calls to abort or commit the transaction.

If the session is participating in a transaction, if DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DDL_COMMIT, and if the method succeeds, the transaction is committed without retention. No new transaction is created. Any new work done on the session is outside the scope of a transaction. Attempting to explicitly commit or abort when there is no outstanding transaction returns an error.

See Also

IDBProperties::GetPropertyInfo | ITableDefinition::CreateTable | ITableDefinition::DropColumn


ITableDefinition::CreateTable

Creates a new base table in the data store.

HRESULT CreateTable(
   IUnknown            *pUnkOuter,
   DBID                *pTableID,
   DBORDINAL            cColumnDescs,
   const 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;
   DBLENGTH     ulColumnSize;
   DBID         dbcid;
   DBTYPE       wType;
   BYTE         bPrecision;
   BYTE         bScale;
} DBCOLUMNDESC;

The elements of this structure are used as follows. The consumer decides the values to use in the nonproperties 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.

Tip Although support for IDBSchemaRowset is optional, providers should implement IDBSchemaRowset and the PROVIDER_TYPES schema rowset if also implementing ITableDefinition. Otherwise, it may force the consumer to know the provider's types in advance, because the provider may be unable to determine the column type if pwszTypeName is NULL.

If pwszTypeName is NULL, the provider determines the type of the column based upon wType and ulColumnSize, as well as any column properties, such as DBTYPE_COL_ISLONG and DBTYPE_COL_FIXEDLENGTH, passed in rgPropertySets.

There may be some types that can only be created by specifying the name in pwszTypeName because they cannot be unambiguously determined based on the wType, ulColumnSize, and property values specified.

Note Some 2.0 providers return an error if pwszTypeName is NULL.
pTypeInfo | If pTypeInfo is not a null pointer, 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 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, it is provider-specific which value is used. If cPropertySets is zero, this argument is ignored.

Note Although the colid element in the DBPROP structure for specified properties is ignored, the consumer must set the value of colid to DB_NULLID. Not to do so is a programming error.

For information about the properties in the Column property group that are defined by OLE DB, see "Column Property Group" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 14: Properties.
pclsid | If the column contains COM 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. If the column does not contain COM objects, this is ignored and pclsid should be a null pointer.
cPropertySets | The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets.
ulColumnSize The maximum length in characters for values in this column if wType is DBTYPE_STR or DBTYPE_WSTR, or in bytes if wType is DBTYPE_BYTES.

If ulColumnSize is zero and a default maximum column length is defined, the provider creates a column of that length. If no default is defined, the length of the created column is provider-specific.

For all other values of wType, ulColumnSize is ignored. Providers that do not require a length argument in the specification of the provider type (pwszTypeName) ignore this element.
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 DBTYPE_NUMERIC or DBTYPE_VARNUMERIC; 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, DBTYPE_VARNUMERIC, 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.

Note The provider does not modify any elements of the DBCOLUMNDESC structures. However, upon a return code of S_OK, DB_S_ERRORSOCCURRED, or DB_E_ERRORSOCCURRED, the dwStatus element in the DBPROP structure for each column property indicates whether or not that column property was set (DBPROPSTATUS_OK).

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, it is provider-specific which value is used. If cPropertySets is zero, this argument is ignored.

Note The colid element of every DBPROP structure passed to the method must be set either to a valid DBID value or to DB_NULLID. For rowset properties, the colid element of the DBPROP structure must be set either to the ID of the rowset column to which the property applies or to DB_NULLID if the property applies to the entire rowset. For table properties, the property applies to the entire table and therefore the colid element of the DBPROP structure must be set to DB_NULLID.

For information about the properties in the Rowset and Tables property groups that are defined by OLE DB, see "Rowset Property Group" and "Table Property Group" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 14: Properties.

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.

Note The contents of *ppTableID might not exactly match the passed pTableID. (For example, it might contain additional version or other information.) The consumer should use *ppTableID to identify the newly created table. If ppTableID is NULL on input and the provider cannot create a table that exactly matches pTableID, ITableDefinition::CreateTable should fail with DB_E_BADTABLEID.

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 following:
  • The colid element of the DBPROP structure for a rowset property was invalid. To be valid, this element must be set either to the ID of the rowset column to which the property applies or to DB_NULLID if the property applies to the entire rowset.
  • The colid element of the DBPROP structure for a table property 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.
  • The property was not supported by the provider.
  • The property set was not supported by the provider.
  • It was not possible to set the property.
  • The property was not in the Column, Rowset, or Table property group.
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.

E_NOINTERFACE
The rowset did not support the interface specified in riid.

riid was IID_NULL, and ppRowset was not 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
The wType, pwszTypeName, or pTypeInfo element, in an element of rgColumnDescs was invalid.

In an element of rgColumnDescs, pwszTypeName was non-null and implied a DBTYPE other than the type specified by wType.

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 store.
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 provider is an OLE DB 1.0 or 1.1 provider that does not support aggregation of the rowset object being created.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

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.
XACT_E_XTIONEXISTS
The provider supports transactional DDL, the session is participating in a transaction, and the value of DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DML.

Comments

If ppRowset is not a null pointer, an empty rowset is opened on the newly created table. If ITableDefinition::CreateTable returns any errors, the table is not created and ppTableID is set to NULL on output.

If the session is participating in a transaction, if DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DDL_IGNORE, and if the method succeeds, the operation is complete and is unaffected by subsequent calls to abort or commit the transaction.

If the session is participating in a transaction, if DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DDL_COMMIT, and if the method succeeds, the transaction is committed without retention. No new transaction is created. Any new work done on the session is outside the scope of a transaction. Attempting to explicitly commit or abort when there is no outstanding transaction returns an error.

See Also

IDBProperties::GetPropertyInfo | ITableDefinition::AddColumn | ITableDefinition::DropColumn | ITableDefinition::DropTable


ITableDefinition::DropColumn

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_NOCOLUMN
The column specified in *pColumnID does not exist in the specified table.
DB_E_DROPRESTRICTED
The provider could not drop the column because pColumnID was referenced in a view definition.

The provider could not drop the column because pColumnID was referenced in a constraint belonging to a table other than pTableID.

The provider could not drop the column because pColumnID was referenced, along with one or more other columns, in a constraint definition on pTableID.

DB_E_NOTABLE
The specified table does not exist in the current data store.
DB_E_TABLEINUSE
The specified table was in use.
DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to drop the column.
XACT_E_XTIONEXISTS
The provider supports transactional DDL, the session is participating in a transaction, and the value of DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DML.

Comments

If ITableDefinition::DropColumn returns any errors, the column is not dropped.

If the session is participating in a transaction, if DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DDL_IGNORE, and if the method succeeds, the operation is complete and is unaffected by subsequent calls to abort or commit the transaction.

If the session is participating in a transaction, if DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DDL_COMMIT, and if the method succeeds, the transaction is committed without retention. No new transaction is created. Any new work done on the session is outside the scope of a transaction. Attempting to explicitly commit or abort when there is no outstanding transaction returns an error.

See Also

ITableDefinition::AddColumn


ITableDefinition::DropTable

Drops a base table in the data store.

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_DROPRESTRICTED
The provider could not drop the table because pTableID was referenced in a view definition.

The provider could not drop the table because pTableID was referenced in a constraint belonging to a table other than pTableID.

DB_E_NOTABLE
The specified table does not exist in the current data store.
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.
XACT_E_XTIONEXISTS
The provider supports transactional DDL, the session is participating in a transaction, and the value of DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DML.

Comments

If the session is participating in a transaction, if DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DDL_IGNORE, and if the method succeeds, the operation is complete and is unaffected by subsequent calls to abort or commit the transaction.

If the session is participating in a transaction, if DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DDL_COMMIT, and if the method succeeds, the transaction is committed without retention. No new transaction is created. Any new work done on the session is outside the scope of a transaction. Attempting to explicitly commit or abort when there is no outstanding transaction returns an error.

See Also

ITableDefinition::CreateTable


ITableDefinitionWithConstraints : ITableCreation

The ITableDefinitionWithConstraints interface exposes simple methods to create, drop, and alter tables on the data store. It also provides a mechanism to create and delete multitable and multicolumn constraints on tables.

ITableDefinitionWithConstraints extends ITableCreation by adding the ability to create and drop constraints from the base table and create base tables with constraints in one, atomic operation.

When to Implement

ITableDefinitionWithConstraints is optional for providers that do not otherwise support table creation; it is mandatory for providers that support table creation through commands and have the ability to create referential constraints (such as primary keys, foreign keys, unique, or check constraints).

Method | Description
AddConstraint | Adds a new constraint to a base table.
CreateTableWithConstraints | Creates a new base table in the data store, allowing constraints to be added atomically during table creation.
DropConstraint | Drops a constraint from a base table.
(Inherited) | All methods of ITableCreation.


ITableDefinitionWithConstraints::AddConstraint

Adds a new constraint to a base table.

HRESULT AddConstraint(
   DBID               *pTableID,
   DBCONSTRAINTDESC   *pConstraintDesc);

Parameters

pTableID
[in]
A pointer to the DBID of the table to which the constraint is to be added.
pConstraintDesc
[in]
A pointer to the DBCONSTRAINTDESC structure that describes the new constraint. DBCONSTRAINTDESC is defined as follows:
typedef struct tagDBCONSTRAINTDESC {
   DBID              *pConstraintID;
   DBCONSTRAINTTYPE   ConstraintType;
   ULONG              cColumns;
   DBID               rgColumnList[];
   DBID              *pReferencedTableID;
   ULONG              cForeignKeyColumns;
   DBID               rgForeignKeyColumnList[];
   OLECHAR           *pwszConstraintText;
   DBUPDELRULE        UpdateRule;
   DBUPDELRULE        DeleteRule;
   DBMATCHTYPE        MatchType;
   DBDEFERRABILITY    Deferrability;
   DB_URESERVE        cReserved;
   DBPROPSET          rgReserved[];
} DBCONSTRAINTDESC;

The elements of this structure are used as described in the following table.

Element | Description
pConstraintID | The constraint identifier. If this is null, the provider generates a unique ID for the constraint. This ID is not returned to the consumer through the DBCONSTRAINTDESC structure. Consumers should generally specify a value for the constraint and not set pConstraintID to null, because there is no easy way to get back the ID for the added constraint.
ConstraintType The type of the constraint as defined in the DBCONSTRAINTTYPE enum. One of the following values:
DBCONSTRAINTTYPE_UNIQUE
DBCONSTRAINTTYPE_FOREIGNKEY
DBCONSTRAINTTYPE_PRIMARYKEY
DBCONSTRAINTTYPE_CHECK
cColumns | The number of elements in the array passed in rgColumnList. For check constraints, this is always zero. If cColumns is zero, rgColumnList is ignored.
rgColumnList For unique and primary key constraints, this contains the list of columns that comprise the constraint. For foreign key constraints, this defines the list of column IDs (DBIDs in the referenced table) that make up the referenced key in a relationship.

The order of the elements in this array comprise the key columns in descending order of significance. As such, the first element is the most significant key column and the last element in the array is the least significant key column.
pReferencedTableID | For foreign keys, this contains the referenced table in a relationship. For all other types of constraints, this is null.
cForeignKeyColumns | The number of elements in the array passed in rgForeignKeyColumnList. If cForeignKeyColumns is zero, rgForeignKeyColumnList is ignored. This field must be zero if pReferencedTableID is a null pointer.
rgForeignKeyColumnList The columns (DBIDs in the base table) that comprise the foreign key in a relationship.

The order of the elements in this array comprise the key columns in descending order of significance. As such, the first element is the most significant key column and the last element in the array is the least significant key column.

This field is ignored if cForeignKeyColumns is zero.
pwszConstraintText | The constraint clause. If ConstraintType is not DBCONSTRAINTTYPE_CHECK, this must be a null pointer.
UpdateRule The update rule (as defined in SQL-92). One of the following values:
DBUPDELRULE_NOACTION
DBUPDELRULE_CASCADE
DBUPDELRULE_SETNULL
DBUPDELRULE_SETDEFAULT

This field is ignored unless ConstraintType is DBCONSTRAINTTYPE_FOREIGNKEY.
DeleteRule The delete rule (as defined in SQL-92). One of the following values:
DBUPDELRULE_NOACTION
DBUPDELRULE_CASCADE
DBUPDELRULE_SETNULL
DBUPDELRULE_SETDEFAULT

This field is ignored unless ConstraintType is DBCONSTRAINTTYPE_FOREIGNKEY.
MatchType The match type (as defined in SQL-92). This field is ignored unless ConstraintType is DBCONSTRAINTTYPE_FOREIGNKEY. One of the following values:
DBMATCHTYPE_NONE
DBMATCHTYPE_FULL
DBMATCHTYPE_PARTIAL
Deferrability A bitmask that describes whether application or checking of the constraint is immediate or deferred. Values are as follows:
  • DBDEFERRABILITY_DEFERRED—Application of the constraint is deferred. This bit is ignored unless DBDEFERRABILITY_DEFERRABLE is set.
  • DBDEFERRABILITY_DEFERRABLE—Application of the constraint is deferrable.

Not specifying DBDEFERRABILITY_DEFERRED implies that the constraint is immediate.

Not specifying DBDEFERRABILITY_ DEFERRABLE implies that the constraint is not deferrable.

If DBDEFERRABILITY_DEFERRABLE is not specified, the DBDEFERRABILITY_DEFERRED bit is ignored and the constraint is immediate.

If the constraint is immediate, the constraint is applied or checked at the end of each SQL statement. If the constraint is deferred, the constraint is applied or checked when the constraint is changed to immediate, either explicitly by command execution or implicitly at the end of the current transaction.
cReserved The number of DBPROPSET structures in rgReserved. If this is zero, the provider ignores rgReserved.

This parameter applies to constraint properties, and consumers should set this element to 0.
rgReserved An array of DBPROPSET structures containing properties and values to be set. If cReserved is zero, this argument is ignored.

This parameter applies to constraint properties, and consumers should set this element to NULL.

Return Code

S_OK
The method succeeded.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pTableID or pConstraintDesc was a null pointer.

cForeignKeyColumns was not zero, and rgForeignKeyColumnList was null.

cForeignKeyColumns was not zero and was not equal to cColumns.

cColumns was not zero, and rgColumnList was null.

cReserved was not zero, or rgReserved was not a null pointer.

DB_E_NOCOLUMN
The DBCONSTRAINTDESC structure contained a reference to a column ID, either in rgForeignKeyColumnList, when rgForeignKeyColumnList is not ignored, or in rgColumnList, and the column was not found.
DB_E_BADCONSTRAINTFORM
ConstraintType was not DBCONSTRAINTTYPE_FOREIGNKEY, and cForeignKeyColumns was not zero.

ConstraintType was not DBCONSTRAINTTYPE_FOREIGNKEY, and pReferencedTableID was not a null pointer.

ConstraintType was DBCONSTRAINTTYPE_FOREIGNKEY, and pReferencedTableID was NULL.

The type of the constraint was not a check constraint, and pwszConstraintText was not NULL.

ConstraintType was DBCONSTRAINTTYPE_CHECK, and pwszConstraintText was a null pointer.

ConstraintType was DBCONSTRAINTTYPE_CHECK, and cColumns was not zero.

ConstraintType was DBCONSTRAINTTYPE_FOREIGNKEY, and cForeignKeyColumns was zero.

ConstraintType was DBCONSTRAINTTYPE_FOREIGNKEY, and the column type of a column in rgForeignKeyColumnList does not match the type of the corresponding column in rgColumnList.

ConstraintType was DBCONSTRAINTTYPE_FOREIGNKEY, DBCONSTRAINTTYPE_PRIMARYKEY, or DBCONSTRAINTTYPE_UNIQUE; and cColumns was zero.

DB_E_BADCONSTRAINTID
pConstraintID was invalid.
DB_E_BADCONSTRAINTTYPE
ConstraintType was invalid or not supported by the provider.
DB_E_BADDEFERRABILITY
Deferrability was invalid, or the value was not supported by the provider.
DB_E_BADMATCHTYPE
MatchType was not ignored and was invalid, or the value was not supported by the provider.
DB_E_BADUPDATEDELETERULE
UpdateRule or DeleteRule was not ignored and was invalid, or the value was not supported by the provider.
DB_E_DUPLICATECONSTRAINTID
pConstraintID was the same as an existing constraint.

ConstraintType was DBCONSTRAINTTYPE_PRIMARYKEY, and the base table already had a primary key.

DB_E_NOTABLE
The table specified by *pTableID does not exist in the data store.

The table specified by *pReferencedTableID in the DBCONSTRAINTDESC structure does not exist.

DB_E_SCHEMAVIOLATION
The type of the constraint conflicted with an attribute (for example, column type) of the referenced column.

The constraint conflicts with current contents of the table.

DB_E_TABLEINUSE
The specified table was in use, and the provider could not add a constraint as a result.
DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to add a constraint.
XACT_E_XTIONEXISTS
The provider supports transactional DDL, the session is participating in a transaction, and the value of DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DML.

Comments

If AddConstraint returns any errors, the constraint is not created.

If the session is participating in a transaction, if DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DDL_IGNORE, and if the method succeeds, the operation is complete and is unaffected by subsequent calls to abort or commit the transaction.

If the session is participating in a transaction, if DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DDL_COMMIT, and if the method succeeds, the transaction is committed without retention. No new transaction is created. Any new work done on the session is outside the scope of a transaction. Attempting to explicitly commit or abort when there is no outstanding transaction returns an error.

The following example illustrates which parameters should contain the base table and referenced table DBIDs when defining constraints in a foreign key relationship: Let Orders be the base table, and Customers the referenced table. The CustID field of Orders has a foreign key constraint on the CustID field of Customers. The rgColumnList parameter contains the DBID for Customers.CustID and rgForeignKeyColumnList contains the DBID for Orders.CustID.

See Also

ITableDefinitionWithConstraints::CreateTableWithConstraints | ITableDefinitionWithConstraints::DropConstraint


ITableDefinitionWithConstraints::CreateTableWithConstraints

Creates a new base table in the data store.

HRESULT CreateTableWithConstraints(
   IUnknown            *pUnkOuter,
   DBID                *pTableID,
   DBORDINAL            cColumnDescs,
   const DBCOLUMNDESC   rgColumnDescs[],
   ULONG                cConstraintDescs,
   DBCONSTRAINTDESC     rgConstraintDescs[],
   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. For more information on the DBCOLUMNDESC structure, please refer to ITableDefinition::CreateTable.
cConstraintDescs
[in]
The number of DBCONSTRAINTDESC structures in the rgConstraintDescs array. If this is zero, rgConstraintDescs is ignored.
rgConstraintDescs
[in]
An array of DBCONSTRAINTDESC structures that describe constraints to be created on the table. For more information on this structure, please refer to ITableDefinitionWithConstraints::AddConstraint.
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, 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 Property Group" and "Table Property Group" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 14: Properties.

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 following:
  • 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.

cConstraintDescs was not zero, and rgConstraintDescs was a null pointer.

In an element of rgConstraintDescs, cForeignKeyColumns was not zero and rgForeignKeyColumnList was null.

In an element of rgConstraintDescs, cForeignKeyColumns was not zero and was not equal to cColumns.

In an element of rgConstraintDescs, cColumns was not zero and rgColumnList was null.

In an element of rgConstraintDescs, 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_NOCOLUMN
The DBCONSTRAINTDESC structure contained a reference to a column ID, either in rgForeignKeyColumnList, when rgForeignKeyColumnList is not ignored, or in rgColumnList, and the column was not found.
DB_E_BADCONSTRAINTFORM
ConstraintType was not DBCONSTRAINTTYPE_FOREIGNKEY, and cForeignKeyColumns was not zero.

ConstraintType was not DBCONSTRAINTTYPE_FOREIGNKEY, and pReferencedTableID was not a null pointer.

ConstraintType was DBCONSTRAINTTYPE_FOREIGNKEY, and pReferencedTableID was NULL.

The type of the constraint was not a check constraint, and pwszConstraintText was not NULL.

ConstraintType was DBCONSTRAINTTYPE_CHECK, and pwszConstraintText was a null pointer.

ConstraintType was DBCONSTRAINTTYPE_CHECK, and cColumns was not zero.

ConstraintType was DBCONSTRAINTTYPE_FOREIGNKEY, and cForeignKeyColumns was zero.

ConstraintType was DBCONSTRAINTTYPE_FOREIGNKEY, DBCONSTRAINTTYPE_PRIMARYKEY, or DBCONSTRAINTTYPE_UNIQUE and cColumns was zero.

In an element of rgColumnDescs, ConstraintType was DBCONSTRAINTTYPE_FOREIGNKEY, and the column type of a column in rgForeignKeyColumnList does not match the type of the corresponding column in rgColumnList.

DB_E_BADCONSTRAINTID
In an element of rgConstraintDescs, *pConstraintID was invalid.
DB_E_BADCONSTRAINTTYPE
In an element of rgConstraintDescs, ConstraintType was invalid or not supported by the provider.
DB_E_BADDEFERRABILITY
In an element of rgConstraintDescs, Deferrability was invalid or the value was not supported by the provider.
DB_E_BADMATCHTYPE
In an element of rgConstraintDescs, MatchType was not ignored and was invalid or the value was not supported by the provider.
DB_E_BADPRECISION
The precision in an element of rgColumnDescs was invalid.
DB_E_BADPROPERTYVALUE
The value of a property was invalid.
DB_E_BADSCALE
The scale in an element of rgColumnDescs 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_BADUPDATEDELETERULE
In an element of rgConstraintDescs, UpdateRule or DeleteRule was not ignored and was invalid or the value was not supported by the provider.
DB_E_DUPLICATECOLUMNID
dbcid was the same in two or more elements of rgColumnDescs.
DB_E_DUPLICATECONSTRAINTID
In an element of rgConstraintDescs, the constraint was the same as an existing constraint, or the constraint ID was the same in two or more elements.
DB_E_DUPLICATETABLEID
The specified table already exists in the data store.
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—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 provider is an OLE DB 1.0 or 1.1 provider that does not support aggregation of the rowset object being created.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

DB_E_NOTABLE
In one or more elements of rgConstraintDescs, the table referenced by *pReferencedTableID does not exist.
DB_E_OBJECTOPEN
The provider would have to open a new connection to support the operation, and DBPROP_MULTIPLECONNECTIONS is set to VARIANT_FALSE.
DB_E_SCHEMAVIOLATION
The type of the constraint conflicted with an attribute (for example, column type) of the referenced column.
DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to create the table.
XACT_E_XTIONEXISTS
The provider supports transactional DDL, the session is participating in a transaction, and the value of DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DML.

Comments

If ppRowset is not a null pointer, an empty rowset is opened on the newly created table. If ITableDefinitionWithConstraints::CreateTableWithConstraints returns any errors, the table is not created nor are any relationships on that table created.

ITableDefinitionWithConstraints::CreateTableWithConstraints is similar to ITableDefinition::CreateTable except that it also provides the ability to create constraints such as primary keys, foreign keys, unique clauses, or check clauses in the same call as the creation of the table. This closely mimics the functionality of the CREATE TABLE SQL statement.

If the session is participating in a transaction, if DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DDL_IGNORE, and if the method succeeds, the operation is complete and is unaffected by subsequent calls to abort or commit the transaction.

If the session is participating in a transaction, if DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DDL_COMMIT, and if the method succeeds, the transaction is committed without retention. No new transaction is created. Any new work done on the session is outside the scope of a transaction. Attempting to explicitly commit or abort when there is no outstanding transaction returns an error.

See Also

ITableDefinition::DropTable | ITableDefinitionWithConstraints::AddConstraint | ITableDefinitionWithConstraints::DropConstraint


ITableDefinitionWithConstraints::DropConstraint

Drops a constraint from a base table.

HRESULT DropConstraint(
   DBID   *pTableID,
   DBID   *pConstraintID);

Parameters

pTableID
[in]
A pointer to the DBID of the table from which to drop the constraint.
pConstraintID
[in]
A pointer to the DBID of the constraint to drop.

Return Code

S_OK
The method succeeded, and the constraint was dropped from the base table.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pTableID or pConstraintID was a null pointer.
DB_E_BADTABLEID
The constraint specified in *pConstraintID does not exist in the table specified in *pTableID.
DB_E_DROPRESTRICTED
The provider could not drop the constraint because there were one or more table constraints dependent on pConstraintID.
DB_E_NOCONSTRAINT
pConstraintID did not exist in the data store.
DB_E_NOTABLE
The specified table does not exist in the data store.
DB_E_TABLEINUSE
The specified table was in use, and the provider could not drop the constraint as a result.
DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to drop the constraint.
XACT_E_XTIONEXISTS
The provider supports transactional DDL, the session is participating in a transaction, and the value of DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DML.

Comments

If ITableDefinitionWithConstraints::DropConstraint returns any errors, the constraint is not dropped.

If the session is participating in a transaction, if DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DDL_IGNORE, and if the method succeeds, the operation is complete and is unaffected by subsequent calls to abort or commit the transaction.

If the session is participating in a transaction, if DBPROP_SUPPORTEDTXNDDL is DBPROPVAL_TC_DDL_COMMIT, and if the method succeeds, the transaction is committed without retention. No new transaction is created. Any new work done on the session is outside the scope of a transaction. Attempting to explicitly commit or abort when there is no outstanding transaction returns an error.

See Also

ITableDefinitionWithConstraints::AddConstraint


ITransaction

The ITransaction interface is used to commit, abort, and obtain status information about transactions. For more information about transactions, see Chapter 15: 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.

Note A retaining commit or abort should not change the characteristics (isolation level, isolation flags, transaction options) of the transaction. The new unit of work retains the same characteristics as the committed work.

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 auto-commit mode.

Abort was called on a distributed transaction with fRetaining set to TRUE.

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_ABORTPRESERVE | fRetaining | Rowset state after abort | Resulting transaction mode of session
FALSE | FALSE | zombie | implicit/auto-commit
FALSE | TRUE | zombie | explicit/manual-commit
TRUE | FALSE | preserved | implicit/auto-commit
TRUE | TRUE | preserved | explicit/manual-commit

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.

Note A retaining commit or abort should not change the characteristics (isolation level, isolation flags, transaction options) of the transaction. The new unit of work retains the same characteristics as the committed work.

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 ITransaction::Commit returns after phase one of the two-phase commit protocol.
XACTTC_SYNC_PHASETWO | When this flag is specified, the call to ITransaction::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 implicitly aborted before ITransaction::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 auto-commit mode.

Commit was called on a distributed transaction with fRetaining set to TRUE.

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 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_COMMITPRESERVE | fRetaining | Rowset state after commit | Resulting transaction mode of session
FALSE | FALSE | zombie | implicit/auto-commit
FALSE | TRUE | zombie | explicit/manual-commit
TRUE | FALSE | preserved | implicit/auto-commit
TRUE | TRUE | preserved | explicit/manual-commit

If ITransaction::Commit fails for any reason that results in an aborted transaction, the session is left in auto-commit 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 described in the following table.

Element | Description
uow | The unit of work associated with this transaction. Cannot be NULL and must be unique per 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 distributed transactions. For more information about transactions, see Chapter 15: 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 ITransactionJoin::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 to unenlist from the coordinated transaction. If non-null, QueryInterface can be called for ITransaction on the transaction coordinator. If NULL, the remaining arguments to the method are ignored.
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, it is a pointer to an object previously returned from ITransactionJoin::GetOptionsObject called on this session.

Return Code

S_OK
The method succeeded.
DB_E_OBJECTOPEN
There were open Commands or Rowsets on the Session object, and the provider requires closing open objects before changing transaction enlistment.
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:
  • There is a local transaction with uncommitted work on the session.
  • punkTransactionCoord was non-NULL, the session can handle only one extant transaction at a time, and the session is already enlisted in a coordinated transaction with uncommitted work.
  • punkTransactionCoord was NULL, the session is enlisted in a coordinated transaction with uncommitted work, and the provider doesn't support unenlisting from transactions with pending work. To work well with the transaction services of Microsoft Transaction Server (MTS), providers should support unenlisting from distributed transactions with pending work and not return an error in this case.

Comments

Coordinated transactions do not exhibit retaining semantics. After a session enlists in a coordinated transaction and that coordinated transaction completes, the session is temporarily in a zombie state, although the provider may not detect this state until the consumer executes a method that communicates with the data store. To return the session from a zombie state, the consumer must call ITransactionJoin::JoinTransaction with a valid punkTransactionCoord to enlist in a new coordinated transaction, or with a null punkTransactionCoord to unenlist from the coordinated transaction.

If the provider needs to register for outcome events, it will usually first register its ITransactionOutcomeEvents notification sink with the transaction coordinator and then enlist with the transaction coordinator. It is important that providers needing outcome events first register with the outcome events so that they can be advised of any failures that may occur between enlistment registration of the outcome events.

Registering for ITransactionOutcomeEvents can be expensive, because it results in an extra message. Also, in some cases the Microsoft® Distributed Transaction Coordinator does not fire these events, which results in memory leaks from references on the connection sink not being released and in connections being held longer than necessary. For these reasons, providers should avoid registering for transaction outcome events.

If a provider does not support adjusting the isolation level when a session enlists in a transaction and if isoLevel does not match the isolation level of the transaction, the provider may either ignore isoLevel or return XACT_E_ISOLATIONLEVEL.

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 15: Transactions.

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.
(Inherited) | All methods of ITransaction.


ITransactionLocal::GetOptionsObject

Returns an object that can be used to specify configuration options for a subsequent call to ITransactionLocal::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.
DB_E_NOTSUPPORTED
The provider does not support ITransactionOptions. A call to ITransactionLocal::GetOptionsObject should return DB_E_NOTSUPPORTED for 2.6-compliant providers and later. For earlier providers, the return code is unspecified.
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 OLE DB" in Chapter 15: Transactions.
Value | Description
ISOLATIONLEVEL_UNSPECIFIED | Applicable only to ITransactionJoin::JoinTransaction. Invalid for ITransactionLocal or for setting isolation level while in auto-commit 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 ITransactionLocal::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 specifies 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::StartTransaction starts a new local transaction. If there is already an active transaction on the session (that is, StartTransaction has been called with no matching ITransaction::Commit or ITransaction::Abort), the new transaction is started as a nested transaction below the current transaction. Calling ITransaction::Commit or ITransaction::Abort always commits or aborts the transaction at the lowest level, respectively. If the provider does not support nested transactions, calling ITransactionLocal::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 "ITransaction Object" in Chapter 15: Transactions.

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. ITransactionOptions::GetOptions does not make any logical changes to the state of any open transactions.

See Also

ITransactionJoin::GetOptionsObject | ITransactionLocal::GetOptionsObject | ITransactionOptions::SetOptions


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 described in the following table.

Element | Description
ulTimeout | The amount of real time in milliseconds before the transaction is to be aborted automatically. Zero indicates an infinite time-out. 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

ITransactionLocal::StartTransaction | ITransactionOptions::GetOptions


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 to apply post-processing operations to an existing rowset. The consumer calls IRowsetView::CreateView on the rowset to create a view object, applies post-processing operations to that view object, and then calls IViewChapter::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 IViewChapter::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

IViewChapter::GetSpecification adds a reference count to the rowset. The consumer must call Release 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. 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 to also 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 IViewFilter::SetFilter or IViewFilter::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 rowset shown in the following table.

Name | Children | State
Joe | 0 | WA
Jane | 2 | WA
Fred | 3 | OR
Judy | 1 | OR
Jane | 2 | ID

Using the preceding rowset, the following table shows the results for various combinations of accessor bindings, criteria array, and comparison operators.

Accessor | Criteria array | Comparison operators | 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"

Note DBCOMPAREOPS_IGNORE must be supported by all providers that implement IRowsetFind or IViewFilter. In addition, providers that expose IViewFilter on the view object must also expose IAccessor and IColumnsInfo on the view object. Consumers should use only accessors created on the view object to get or set filters.

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,
   DBCOUNTITEM    *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, the cbRowSize argument used in the call to IAccessor::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.

Note If the consumer set the filter using IViewFilter::SetFilter, it knows the maximum size of the comparison operator array and can allocate memory for it accordingly. If the consumer was passed the view, it needs to allocate sufficiently large buffers to hold these arrays.

pCriteriaData
[out]
A pointer to a buffer in which to return the criteria data.

Note If the consumer set the filter using IViewFilter::SetFilter, it knows the maximum size of the criteria data array and can allocate memory for it accordingly. If the consumer was passed the view, it needs to allocate sufficiently large buffers to hold these arrays.

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: Getting and Setting Data.
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: Getting and Setting Data.

Comments

None.


IViewFilter::GetFilterBindings

Retrieves the bindings used to describe the filter conditions associated with a view.

HRESULT GetFilterBindings(
   DBCOUNTITEM   *pcBindings,
   DBBINDING    **prgBindings);

Parameters

pcBindings
[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.
prgBindings
[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: Getting and Setting Data.

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 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 to retrieve the information describing the filter conditions.


IViewFilter::SetFilter

Specifies a filter condition for a view.

HRESULT SetFilter (
   HACCESSOR     hAccessor,
   DBCOUNTITEM   cRows,
   DBCOMPAREOP   CompareOps[],
   void         *pCriteriaData);

Parameters

hAccessor
[in]
The handle of the accessor that 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.

Note The expression column DBCOMPAREOPS_IGNORE value always resolves to TRUE when used with the IViewFilter::SetFilter.

pCriteriaData
[in]
A pointer to memory containing the data values, at offsets that correspond to the bindings in the accessor that, 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, IViewFilter::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.

For example, given filter criteria of "(column1 < 10) or (column1 > 20)":

  1. Build two bindings, each referring to column 1, and suitable for the column's data type. In this case, assume the data type is DBTYPE_I4.
  2. Assign the bindings to an accessor.
  3. Construct a buffer for the criteria data. The buffer contains two values, each of a data type equivalent to DBTYPE_I4, with the first value set to 10 and the second to 20—in this case: LONG rgCriteriaData[] = {10, 20};
  4. Set a pCriteriaData with the address of the criteria data buffer. In this case, the array name is equivalent to the buffer address.
  5. Construct an array of comparison operators consisting of two elements—in this case:
    DBCOMPAREOPS rgCompareOps[] = {DBCOMPAREOPS_LT, DBCOMPAREOPSS_GT};
  6. Set cRows to 2 so that the comparison operators are treated as two rows—that is, so that the comparisons are joined in a logical OR operation.
  7. Execute IViewFilter::SetFilter. In this case, the method call is similar to
    pIViewFilter->SetFilter(hAccessor, 2, rgCompareOps, rgCriteriaData);

When the method executes, a filter condition is set where, as specified in the first binding, column 1 is compared via the DBCOMPAREOP_LT operator with the value 10. Then, as specified in the second binding, column 1 is compared via the DBCOMPAREOP_GT operator with the value 20. Then the two comparisons are joined in a logical OR operation.


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 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 to create a view object, applies operations to that view, and then calls IViewRowset::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 IViewRowset::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

IViewRowset::GetSpecification adds a reference count to the object. The consumer must call Release 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 IViewRowset::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 provider is an OLE DB 1.0 or 1.1 provider that does not support aggregation of the rowset object being created.

pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

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 IViewChapter::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 (
   DBORDINAL   *pcColumns,
   DBORDINAL   *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

IViewSort::GetSortOrder returns the latest sort order successfully set on the view.

If IViewSort::GetSortOrder returns an error and if pcColumns, prgColumns, and prgOrders are non-null, *pcColumns, *prgColumns, and *prgOrders are set to NULL, respectively.


IViewSort::SetSortOrder

Specifies a sort order to be applied to a view.

HRESULT SetSortOrder (
   DBORDINAL         cColumns,
   const DBORDINAL   rgColumns[],
   const DBSORT      rgOrders[]);

Parameters

cColumns
[in]
Count of the number of columns to be used in specifying the order, or zero 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 if cColumns is zero.
rgOrders
[in]
The sort order for the corresponding column in the rgColumns list. This argument is ignored if cColumns is zero.

The DBSORT structure is as follows:

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 support ordering only 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, IViewSort::SetSortOrder overrides the previous sort order.

The setting of sort columns is atomic. If any columns cannot be set, IViewSort::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.


OLE DB Core Components Interfaces

For detailed reference information about OLE DB Core Components interfaces and methods, see the following reference topics.

Interface Methods
IDataConvert IDataConvert::CanConvert
IDataConvert::DataConvert
IDataConvert::GetConversionSize
IDataInitialize IDataInitialize::CreateDBInstance
IDataInitialize::CreateDBInstanceEx
IDataInitialize::GetDataSource
IDataInitialize::GetInitializationString
IDataInitialize::LoadStringFromStorage
IDataInitialize::WriteStringToStorage
IDBPromptInitialize IDBPromptInitialize::PromptDataSource
IDBPromptInitialize::PromptFileName
IDCInfo IDCInfo::GetInfo
IDCInfo::SetInfo
IRowPosition IRowPosition::ClearRowPosition
IRowPosition::GetRowPosition
IRowPosition::GetRowset
IRowPosition::Initialize
IRowPosition::SetRowPosition


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 and then use IDataInitialize to get a data source object based on that connection string.

For more information, see "Creating Data Source Objects" in Chapter 2: Data Source and Session Objects.

When to Call

Call IDataInitialize to provide a common entry point for consumers to obtain a data source object or to 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. CLSCTX_ALL, CLSCTX_SERVER, and CLSCTX_INPROC_SERVER are acceptable values, but the service components will always attempt to load the provider in-process. Ignored if *ppDataSource is not NULL.
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.

E_UNEXPECTED
The data source object cannot return the requested interface because the data source object is not initialized.
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.

dwClsCtx required out-of-process operation, which is not supported.

The provider does not support in-process operation and cannot be aggregated.

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. CLSCTX_ALL, CLSCTX_SERVER, and CLSCTX_INPROC_SERVER are acceptable values, but the service components will always attempt to load the provider in-process. Ignored if *ppDataSource is not NULL.
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.

E_UNEXPECTED
The data source object cannot return the requested interface because the data source object is not initialized.
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.

dwClsCtx required out-of-process operation, which is not supported.

The provider does not support in-process operation and cannot be aggregated.

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 an uninitialized 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. CLSCTX_ALL, CLSCTX_SERVER, and CLSCTX_INPROC_SERVER are acceptable values, but the service components will always attempt to load the provider in-process. Ignored if *ppDataSource is not NULL.
pwszInitializationString [in]
A pointer to a connection string containing the information to be used for creating and setting initialization properties on a data source object. Initialization properties are those in DBPROPSET_DBINIT, as well as provider-specific properties. If pwszInitializationString 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, IDataInitialize::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 no provider is specified in pwszInitializationString, the data source specified by *ppDataSource will be used.

If *ppDataSource is non-null on entry and a provider is specified in pwszInitializationString, IDataInitialize::GetDataSource checks to see whether 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 object.

E_UNEXPECTED
The data source object cannot return the requested interface because the data source object is not initialized.
DB_E_MISMATCHEDPROVIDER
The data source object specified by *ppDataSource did not match the data source object specified in pwszInitializationString.
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.

dwClsCtx required out-of-process operation, which is not supported.

The provider does not support in-process operation and cannot be aggregated.

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

ppDataSource was a null pointer.

*ppDataSource and pUnkOuter were both non-null. *ppDataSource cannot be aggregated in *pUnkOuter because it has already been created.

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.

Properties were specified in pwszInitializationString but *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 with one another.

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

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_IDataInitialize,
   reinterpret_cast<LPVOID *>(&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<IUnknown **>(&pIDBInitialize));


IDataInitialize::GetInitializationString

Given a data source object, returns a connection string.

HRESULT GetInitializationString(
   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. If DBPROP_AUTH_PERSISTSENSITIVEAUTHINFO is set to VARIANT_FALSE, the password is not returned, even if fIncludePassword is true. To include the password in the returned initialization string, consumers should make sure that DBPROP_AUTH_PERSISTSENSITIVEAUTHINFO is set to VARIANT_TRUE.
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 initialization properties listed in the following table 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 good for only that specific instance.
Prompt (DBPROP_INIT_PROMPT) | The calling application should have control over prompting.
Timeout (DBPROP_INIT_TIMEOUT) | The property does not apply to the connection itself but to how the connection was established.
General timeout (DBPROP_INIT_GENERALTIMEOUT) | The property does not apply to the connection itself but to how the connection was established.
OLE DB services (OLEDB_SERVICES) | This property does not define the connection but rather specifies how the connection is going to be used. It is possible for multiple applications to use the same connection but with different sets of services.

Note 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 might not be exactly the same one as written using the IDataInitialize::WriteStringToStorage method. For example, passing in the string

   "   Provider=MSDASQL.1; User ID = sa   "

will be returned as

   "Provider=MSDASQL.1; User ID = sa"

When using .udl files to establish a connection, add "Udl Pooling" to the registry under the "SOFTWARE\Microsoft\DataAccess" key to improve performance. A Cache Size value greater than 0 (Cache Size=1) will enable the feature. A value of 100 is recommended.


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 path name 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 IDataInitialize::WriteStringToStorage method will fail and not overwrite the existing file.


IDBPromptInitialize

The IDBPromptInitialize interface allows the display of 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.

A data source object can then be obtained based on the resulting connection string or .udl file name using the IDataInitialize interface.

For more information, see "Creating Data Source Objects" in Chapter 2: Data Source and Session Objects.

When to Call

Call IDBPromptInitialize to prompt the user to create an uninitialized data source object with specified properties set or to select a (.udl) file.

IDBPromptInitialize Methods

Method | Description
IDBPromptInitialize::PromptDataSource | Opens the Data Link Properties dialog box. Returns an uninitialized data source object with the specified properties set.
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 an uninitialized data source object with the specified properties set.

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_DISABLE_PROVIDER_SELECTION | Do not prompt for provider selection. Valid only if *ppDataSource is a pointer to an existing data source object. Setting this flag without specifying a valid data source object in ppDataSource on input returns the error E_INVALIDARG.
DBPROMPTOPTIONS_DISABLESAVEPASSWORD | Disables the "Allow Saving password" checkbox. When set, the value of the property DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO is set to VARIANT_FALSE on the provider.
DBPROMPTOPTIONS_PROPERTYSHEET | Prompt with Data Link Properties dialog box.
DBPROMPTOPTIONS_WIZARDSHEET | Deprecated. Retained for backward compatibility only.
cSourceTypeFilter [in]
Number of DBSOURCETYPE values in rgSourceTypeFilter. If cSourceTypeFilter is zero, the value of rgSourceTypeFilter is ignored and the Provider selection tab will list standard tabular OLE DB providers.
rgSourceTypeFilter [in]
Types of providers to display in the Provider selection tab. Must point to a valid array of DBSOURCETYPE values. Valid source types include the values described in the following table.
Value | Meaning
DBSOURCETYPE_DATASOURCE_TDP | OLE DB tabular data provider
DBSOURCETYPE_DATASOURCE_MDP | OLE DB multidimensional data provider
pwszszzProviderFilter [in]
A double null-terminated string of ProgIDs.

This parameter must be a null pointer or must 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, the dialog is created with the connection tab displayed. If more than one provider is specified, the provider selection tab is displayed first.

Providers specified in pwszszzProviderFilter that are not registered on the machine are ignored. If none of the providers are registered, an error is returned.

riid [in]
The requested interface for the data link returned in *ppDataSource.
ppDataSource [in, out]
A pointer to a data source object.

If *ppDataSource is null on entry, IDBPromptInitialize::PromptDataSource generates a new data source object based on the information specified by the user and returns a pointer to that data source object in *ppDataSource.

If *ppDataSource is non-null on entry, IDBPromptInitialize::PromptDataSource uses the properties returned by IProperties::GetProperties as initial values. If the user selects a different provider, PromptDataSource will release the original *ppDataSource and create a new data source object. On exit, *ppDataSource will be set to a pointer to the interface specified by riid. If the user selects the same provider but requests a different interface by supplying a different value for riid, the output value of *ppDataSource will be a pointer to the required interface on the existing data source object.

If *ppDataSource is non-null on entry and the call returns an error, *ppDataSource is untouched. The caller is strongly advised to check the return value when calling IDBPromptInitialize::PromptDataSource on an existing data source object.

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 object did not support the interface specified in riid.

riid was IID_NULL.

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 and *ppDataSource were both non-null.

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.

*ppDataSource was a null pointer, and DBPROMPTOPTIONS_DISABLE_PROVIDER_SELECTION was specified in dwPromptOptions.

*ppDataSource did not refer to a valid data source object.

DB_E_NOPROVIDERSREGISTERED
No OLE DB providers of the type specified in rgSourceTypeFilter and matching the filter, if any, specified in pwszszzProviderFilter were found on the machine.
E_UNEXPECTED
The data source object cannot return the requested interface because the data source object is not initialized.

Comments

Providers that do not support aggregation cannot be initialized via IDBPromptInitialize::PromptDataSource.

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<IUnknown **>(&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_PROPERTYSHEET,
      0, NULL,
      NULL,
      IID_IDBProperties,
      reinterpret_cast<IUnknown **>(&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.

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_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. If NULL, the default path "<Program Files\Common Files>\System\OLE DB\Data Links" is used. The exact value for <Program Files\Common Files> is system dependent. It can be determined from the "CommonFilesDir" registry entry, at "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion".
pwszInitialFile [in]
A pointer to a string containing the initial file for the file name edit box of the dialog box. Wildcard characters ("*", "?") may be used to form a filtering expression.
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 could not be located. The file name may be too long or contain invalid characters.

Comments

To load a data source object from a .udl file after calling PromptFileName, the consumer must call IDataInitialize::LoadStringFromStorage to obtain the connection string from the file and then call IDataInitialize::GetDatasource to obtain the data source object based on the connection string.

If the initial directory cannot be accessed, an error message is displayed to the user. If the user chooses to continue, an accessible initial directory will be chosen automatically.

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 = pIDBPromptInitialize->PromptFileName(
         hWndParent,
         DBPROMPTOPTIONS_BROWSEONLY,
         L"C:\\Public",
         L"*.udl",
         &pwszSelectedFile);


OLE DB for OLAP Interfaces

The following topics in this section describe interfaces and methods specific to OLE DB for OLAP:

  • IMDDataset
  • IMDFind
  • IMDRangeRowset


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.
GetAxisInfo | Returns the number of axes in the dataset object 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 object.


IMDDataset::FreeAxisInfo

Frees the array of MDAXISINFO structures returned by IMDDataset::GetAxisInfo.

HRESULT FreeAxisInfo(
   DBCOUNTITEM    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, this method does nothing.


IMDDataset::GetAxisInfo

Returns the number of axes in the current dataset and detailed information about each axis.

HRESULT GetAxisInfo(
   DBCOUNTITEM   *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.")

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 follows:

// 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 might not free all the memory allocated for the array of MDAXISINFO structures.

If prgAxisInfo is a null pointer, 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,
   DBCOUNTITEM   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 is not being aggregated, it is a null pointer.
iAxis
[in]
The numeric identifier of an axis. OLE DB for OLAP defines the following manifest constants for commonly used axes: MDAXIS_COLUMNS, MDAXIS_ROWS, MDAXIS_PAGES, MDAXIS_SECTIONS, and MDAXIS_ CHAPTERS. These constants 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, the 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 were not set. The method can fail to set properties for a number of reasons, including the following:
  • 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.
  • 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 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
ppRowset was a null pointer.

cPropertySets was greater than 0, and rgPropertySets was a null pointer.

In an element of rgPropertySets, cProperties was not zero and rgProperties was a null pointer.

E_NOINTERFACE
The axis rowset did not support the interface specified in riid.
MD_E_INVALIDAXIS
iAxis was not 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 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 stating that it was not cheap to set the property.
DB_E_NOAGGREGATION
pUnkOuter was not a null pointer, and the axis rowset does not 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 by using IMDDataset::GetCellData.

Additional columns may be present for a dimension if the following conditions apply:

  • 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 ."

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 ."

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,
   DBORDINAL   ulStartCell,
   DBORDINAL   ulEndCell,
   VOID       *pData);

Parameters

hAccessor
[in]
The handle of the accessor to use. If hAccessor is the handle of a null accessor, IMDDataset::GetCellData does not 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" below.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pData was a null pointer, and the accessor was not a null accessor.

ulStartCell or ulEndCell is invalid.

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_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" below.
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 was not zero.

ulStartCell, ulEndCell, or both specified a cell ordinal that was not 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 than ulEndCell and greater than ulStartCell, but they 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. The following illustration displays 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 statement that generated the dataset. For a detailed discussion of cell properties, see "Retrieving Cell Properties ." IColumnsInfo can also be used to find out the number of columns or properties available, as well as 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 latter 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 could not be converted for reasons other than sign mismatch or data overflow. For example, the data in the data store was corrupted 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 | Value could not be determined by the provider. 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.

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 and their meanings are listed in "Status Values Used When Getting Data."

Using cbRowSize of IAccessor::CreateAccessor

Because this method fetches properties for more than one cell, the provider should know how long each row is. In this context, a "row" 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, 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 so 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 does not 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 does not 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(
   DBORDINAL     ulStartingOrdinal,
   DBCOUNTITEM   cMembers,
   LPCOLESTR    *rgpwszMember,
   DBORDINAL    *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, default members are used for all the constituent dimensions.

If an error occurs, the contents of *pulCellOrdinal are undefined.

Note The FindCell method calls IMDFind::FindTuple for each axis and uses the returned pulTupleOrdinal 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,
   DBORDINAL     ulStartingOrdinal,
   DBCOUNTITEM   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 then 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 in the rightward direction. 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.

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, the default member is used for all dimensions on the axis. If a dimension does not have a default member, this case results in MD_E_BADTUPLE.

If an error occurs, 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, and 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

Returns the range rowset for the desired set of cells in the dataset.

HRESULT GetRangeRowset(
   IUnknown     *pUnkOuter,
   DBORDINAL     ulStartCell,
   DBORDINAL     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, 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 following:
  • 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 stating 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 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, one or more columns of the range rowset can be updated. Typically, this includes the VALUE column.

For more information, see "Working with Cell Data."


Part 5 Appendixes

OLE DB Programmer's Reference

Part 5: Appendixes

OLE DB–related technical information is contained in the following appendixes:

  • Appendix A: Data Types
  • Appendix B: Schema Rowsets
  • Appendix C: OLE DB Properties
  • Appendix D: Cotypes, Structures, and Enumerated Types
  • Appendix E: Interface Summary
  • Appendix F: Sample OLE DB Consumer Application
  • Appendix G: OLE DB Methods to Interfaces Table
  • Appendix H: Data Type Conversion Interfaces
  • Appendix I: Row Position Object


Appendix A: Data Types

This appendix describes data types in OLE DB. For information about how consumers and providers use data types, see Chapter 13: Data Types in OLE DB.


Type Indicators

A type indicator indicates a data type. For more information, see Chapter 13: 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 = 1,
   DBTYPE_I2 = 2,
   DBTYPE_I4 = 3,
   DBTYPE_R4 = 4,
   DBTYPE_R8 = 5,
   DBTYPE_CY = 6,
   DBTYPE_DATE = 7,
   DBTYPE_BSTR = 8,
   DBTYPE_IDISPATCH = 9,
   DBTYPE_ERROR = 10,
   DBTYPE_BOOL = 11,
   DBTYPE_VARIANT = 12,
   DBTYPE_IUNKNOWN = 13,
   DBTYPE_DECIMAL = 14,
   DBTYPE_UI1 = 17,
   DBTYPE_ARRAY = 0x2000,
   DBTYPE_BYREF = 0x4000,
   DBTYPE_I1 = 16,
   DBTYPE_UI2 = 18,
   DBTYPE_UI4 = 19,

// The following values exactly match VARENUM
// in Automation but cannot be used in VARIANT.
   DBTYPE_I8 = 20,
   DBTYPE_UI8 = 21,
   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 = 130,
   DBTYPE_NUMERIC = 131,
   DBTYPE_UDT = 132,
   DBTYPE_DBDATE = 133,
   DBTYPE_DBTIME = 134,
   DBTYPE_DBTIMESTAMP = 135
   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_I2 A two-byte, signed integer:

SHORT
DBTYPE_I4 A four-byte, signed integer:

LONG
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_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_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 either case, 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 IRowset::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 "Responsibility for Freeing Memory" in Chapter 6: Getting and Setting Data.
DBTYPE_IDISPATCH A pointer to an IDispatch interface on a COM object:

IDispatch *
DBTYPE_ERROR A 32-bit error code:

SCODE
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_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, the buffer for that data is allocated separately.
DBTYPE_IUNKNOWN A pointer to an IUnknown interface on a COM object:
IUnknown *

For columns containing COM 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: Blobs and COM Objects.
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;
   };
} 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_UI1 A one-byte, unsigned integer:

BYTE
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 "Responsibility for Freeing Memory" in Chapter 6: Getting and Setting Data.
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 "Responsibility for Freeing Memory" in Chapter 6: Getting and Setting Data.
DBTYPE_I1 A one-byte, signed integer:
signed char
DBTYPE_UI2 A two-byte, unsigned integer:
USHORT
DBTYPE_UI4 A four-byte, unsigned integer:
ULONG
DBTYPE_I8 An eight-byte, signed integer:

LARGE_INTEGER
DBTYPE_UI8 An eight-byte, unsigned integer:

ULARGE_INTEGER
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).

DBTYPE_VECTOR A DBVECTOR structure:
typedef struct tagDBVECTOR {
   DBLENGTH   size;
   void      *ptr;
} 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 ptr, a pointer to an array of the other type, and size, the number of elements in this array. 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 "Responsibility for Freeing Memory" in Chapter 6: Getting and Setting Data.
DBTYPE_RESERVED | Reserved for future use by OLE DB. This indicator does not correspond to any C data type.
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: Blobs and COM Objects.

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_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; if no column metadata is available, it is indicated by table metadata or 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. The maximum length of the string is the number of allocated bytes divided by sizeof(wchar_t) and truncated to the nearest integer.
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];
} DB_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_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_HCHAPTER | A four-byte chapter value that can be used to identify rows in a child rowset.
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 Microsoft® 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, 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_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;

The elements of the 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. The scale can be negative to support the storage of very large numbers without using exponential notation; for example, "12000" could be stored as "12" with a scale of -3. Also, the scale can be greater than precision to represent very small numbers without using exponential notation; for example, "0.00012" could be stored as "12" with precision=2 and scale = 5.
Sign—The sign: 1 for positive numbers, 0 for negative numbers.
Val—A number stored as a variable-byte length scaled integer, with the least significant byte on the left.

For example, to specify the base 10 number "6553E+50" with 5 digits of precision, the number is scaled to "65530", which is 0xFFFA in hexadecimal. The value can be stored in two bytes as FA FF (val[0] = 0xFA and val[1] = 0xFF), the precision is 5, the scale is –49, and the sign is 1.


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 store. For example, if a data store can support a maximum precision of only 28 for a DBTYPE_NUMERIC column, the provider returns 28, not 39, as the maximum precision.

Note Only the following data types have a range of scale values:

  • DBTYPE_DECIMAL 0 to 28
  • DBTYPE_NUMERIC 0 to 39
  • DBTYPE_VARNUMERIC -128 to 127

Although generally invalid, the meaning of a precision value of 0 is provider-specific.

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-Length and Variable-Length Data Types" in Chapter 13: Data Types in OLE DB.

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-Length and Variable-Length Data Types" in Chapter 13: Data Types in OLE DB.

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 13: Data Types in OLE DB. 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
HCHAPTER | - | - | - | - | 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
HCHAPTER | - | - | S | - | -
To → From ↓ | UNK | GUID | ERROR | PROPVARIANT | HCHAPTER
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 | S
BSTR | – | S, E | – | S | -
STR | S, E | S, E | – | S | -
WSTR | S, E | S, E | – | S | 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, the string value "f222" is converted to 0xf2 0x22, and the value 0x01 0x01 is converted to the string "0101".
DBTYPE_CY | "...cccc.cccc"
DBTYPE_BOOL | "True" and "False"
DBTYPE_DATE As in Automation, this may be localized based on provider-specific settings. For example, for the "English(United States)" default setting, this would typically be the following:

"mm/dd/yy hh:mm:ss {AM|PM}"

To control how this is localized, consumers should bind as DBTYPE_DATE or DBTYPE_VARIANT and do their own conversions.
DBTYPE_DBDATE As specified by ISO:

"yyyy-mm-dd"
DBTYPE_FILETIME | "yyyyy-mm-dd hh:mm:ss.fff" where fff is 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
DBTYPE_HCHAPTER 		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, the consumer is responsible for the conversion. The LCID of a column is returned in the DBCOLUMN_COLLATING_SEQUENCE column returned by IColumnsRowset::GetColumnsRowset.

When DBTYPE_HCHAPTER is converted to DBTYPE_WSTR, the characters are the ASCII representation of the number in its hexadecimal form. For example, 0xf2220101 is converted to the string "f2220101".

When DBTYPE_WSTR is converted to DBTYPE_HCHAPTER, each byte of binary data represents two ASCII characters. For example, "f2220101" becomes 0xf2220101, and "f222" becomes 0xf222. The conversion will fail if the value does not appear to represent a valid HCHAPTER or if truncation would occur.


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 4-byte ANSI string; and to "123.4\0" when converted to a 6-byte ANSI string. It cannot be converted to an ANSI string of 3 or fewer bytes because such a conversion would overflow the string.

  • In conversions from DBTYPE_NUMERIC and DBTYPE_DECIMAL to string values, the conversion is allowed, provided it does not result in truncation to the left of the decimal point. For example, 123.4567 is truncated to "123.4\0" when converted to a 6-byte ANSI string but returns a truncation error when attempting to convert to a 2-byte ANSI string.
  • In conversions from DBTYPE_NUMERIC to other numeric types, the conversion is allowed, provided the difference between the source value and the target value is less than the difference between two successive values in the target data type. For example, 123.4567 can be converted with a scale of 2 to the numeric value 123.45 because the difference between 123.4567 and 123.45 is less than .01 (10-2).
  • 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 to 123.4 when converted to a NUMERIC(4, 1). However, the string "1234.5\0" cannot be converted to a 1-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 | 29 | scale element of DBTYPE_DECIMAL structure
DBTYPE_DECIMAL | setting | 29 | 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 | 29 | scale element of DBTYPE_DECIMAL structure
DBTYPE_DECIMAL | setting | 29 | 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 PROPVARIANTs

The following table describes conversions to and from PROPVARIANTs. Type indicator X can be modified by DBTYPE_ARRAY or DBTYPE_BYREF. X is not DBTYPE_VARIANT. No conversions are supported when it is modified by DBTYPE_VECTOR.

Source type indicator | Target type indicator | Notes
X DBTYPE_PROPVARIANT If X is a valid PROPVARENUM value, the conversion library sets vt in the target PROPVARIANT to X and transfers the data to the corresponding element of the union.

If X is not a valid PROPVARENUM value and X is DBTYPE_STR or DBTYPE_WSTR, the conversion library sets vt in the target PROPVARIANT to VT_BSTR and transfers the data to the corresponding element of the union.

If X is not a valid PROPVARENUM value and X is DBTYPE_GUID, the conversion library sets vt in the target PROPVARIANT to VT_CLSID and transfers the data to the corresponding element of the union. The client application is responsible for freeing the memory allocated for VT_CLSID.

If X is not a valid PROPVARENUM value and X is DBTYPE_BYTES, the conversion library sets vt in the target PROPVARIANT to VT_BLOB and transfers the data to the corresponding element of the union. The client application is responsible for freeing the memory allocated for VT_BLOB.
DBTYPE_PROPVARIANT X If the type indicator specified in the source of PROPVARIANT is X, the conversion library transfers the data.

If the type indicator specified in the source of PROPVARIANT is not X and is a valid PROPVARENUM value, the conversion library will support the conversion as long as DBTYPE_(the type indicator of the source) -> DBTYPE_X is supported. VT_LPSTR will be treated the same as DBTYPE_STR | DBTYPE_BYREF, VT_LPWSTR as DBTYPE_WSTR | DBTYPE_BYREF, VT_CLSID as DBTYPE_GUID, and VT_BLOB as DBTYPE_BYTES.
DBTYPE_PROPVARIANT | DBTYPE_PROPVARIANT | The conversion library sets vt in the target PROPVARIANT to the same value as vt in the source DBPROPVARIANT. It transfers the data from the union element in the source VARIANT to the same element in the target PROPVARIANT.
DBTYPE_VARIANT | DBTYPE_PROPVARIANT | If the type indicator specified in the source of VARIANT is a valid PROPVARENUM value, the conversion library sets vt in the target PROPVARIANT to the type indicator of the source and transfers the data to the corresponding element of the union.
DBTYPE_PROPVARIANT | DBTYPE_VARIANT | If the type indicator specified in the source of PROPVARIANT is a valid VARENUM and PROPVARNUM value, the conversion library sets vt in the target VARIANT to the type indicator of the source and transfers the data to the corresponding element of the union.


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: Getting and Setting Data.
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.


Appendix B: Schema Rowsets

Schema information in OLE DB is retrieved using predefined schema rowsets with IDBSchemaRowset::GetRowset. This appendix lists the contents of each schema rowset, including the GUID, the number of restriction columns, the names of the restriction columns, the default sort order, the column names, the data type indicators, and the column descriptions.

Note Each schema rowset defined in OLE DB is identified by a GUID, which is defined in the OLE DB header file (oledb.h). OLE DB also defines the columns of each rowset and specifies the restriction columns for each rowset. The number of restricted columns in a schema rowset is defined as a constant prefixed with CRESTRICTIONS_ in the OLE DB header file.

As listed in the following table, OLE DB defines two types of schema rowsets: logical information and physical information schema rowsets. Logical information schema rowsets are a superset of information presented by the INFORMATION_SCHEMA views defined in ANSI SQL-92 (with the exception of the COLUMNS and the ROWSETS rowsets, which contain subsets of this information). Physical information schema rowsets represent the physical database structure, such as the file system. This metadata contains OLE DB extensions to the standard, and these extensions are not defined in ANSI SQL-92.

Logical information schema rowsets | Physical information schema rowsets
ASSERTIONS | CATALOGS
CHARACTER_SETS | INDEXES
CHECK_CONSTRAINTS | PROVIDER_TYPES
CHECK_CONSTRAINTS_BY_TABLE | STATISTICS
COLLATIONS | TABLE_STATISTICS
COLUMN_DOMAIN_USAGE |
COLUMN_PRIVILEGES |
COLUMNS |
CONSTRAINT_COLUMN_USAGE |
CONSTRAINT_TABLE_USAGE |
FOREIGN_KEYS |
KEY_COLUMN_USAGE |
PRIMARY_KEYS |
PROCEDURE_COLUMNS |
PROCEDURE_PARAMETERS |
PROCEDURES |
REFERENTIAL_CONSTRAINTS |
SCHEMATA |
SQL_LANGUAGES |
TABLE_CONSTRAINTS |
TABLE_PRIVILEGES |
TABLES |
TABLES_INFO |
TRANSLATIONS |
USAGE_PRIVILEGES |
VIEW_COLUMN_USAGE |
VIEWS |
VIEW_TABLE_USAGE |


ASSERTIONS Rowset

GUID: DBSCHEMA_ASSERTIONS

Number of restriction columns: 3

Restriction columns: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME

Default sort order: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME

Description: The ASSERTIONS rowset identifies the assertions defined in the catalog that are owned by a given user.

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.


CATALOGS Rowset

GUID: DBSCHEMA_CATALOGS

Number of restriction columns: 1

Restriction columns: CATALOG_NAME

Default sort order: CATALOG_NAME

Description: 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.

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | Catalog name. Cannot be NULL.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the table.


CHARACTER_SETS Rowset

GUID: DBSCHEMA_CHARACTER_SETS

Number of restriction columns: 3

Restriction columns: CHARACTER_SET_CATALOG, CHARACTER_SET_SCHEMA, CHARACTER_SET_NAME

Default sort order: CHARACTER_SET_CATALOG, CHARACTER_SET_SCHEMA, CHARACTER_SET_NAME

Description: The CHARACTER_SETS rowset identifies the character sets defined in the catalog that are accessible to a given user.

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.


CHECK_CONSTRAINTS Rowset

GUID: DBSCHEMA_CHECK_CONSTRAINTS

Number of restriction columns: 3

Restriction columns: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME

Default sort order: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME

Description: The CHECK_CONSTRAINTS rowset identifies the check constraints defined in the catalog that are owned by a given user.

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


CHECK_CONSTRAINTS_BY_TABLE Rowset

GUID: DBSCHEMA_CHECK_CONSTRAINTS_BY_TABLE

Number of restriction columns: 6

Restriction columns: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME

Default sort order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME

Description: The CHECK_CONSTRAINTS_BY_TABLE rowset identifies the check constraints for a given table, defined in a catalog owned by a given user.

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.
CHECK_CLAUSE | DBTYPE_WSTR | The WHERE clause specified in the CHECK clause.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the column.


COLLATIONS Rowset

GUID: DBSCHEMA_COLLATIONS

Number of restriction columns: 3

Restriction columns: COLLATION_CATALOG, COLLATION_SCHEMA COLLATION_NAME

Default sort order: COLLATION_CATALOG, COLLATION_SCHEMA, COLLATION_CATALOG, CHARACTER_SET_CATALOG, CHARACTER_SET_SCHEMA, CHARACTER_SET_NAME

Description: The COLLATIONS rowset identifies the character collations defined in the catalog that are accessible to a given user.

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.


COLUMN_DOMAIN_USAGE Rowset

GUID: DBSCHEMA_COLUMN_DOMAIN_USAGE

Number of restriction columns: 4

Restriction columns: DOMAIN_CATALOG, DOMAIN_SCHEMA, DOMAIN_NAME COLUMN_NAME

Default sort order: DOMAIN_CATALOG, DOMAIN_SCHEMA, DOMAIN_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_GUID, COLUMN_PROPID

Description: 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.

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.


COLUMN_PRIVILEGES Rowset

GUID: DBSCHEMA_COLUMN_PRIVILEGES

Number of restriction columns: 6

Restriction columns: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, GRANTOR, GRANTEE

Default sort order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_GUID, COLUMN_PROPID, PRIVILEGE_TYPE

Description: 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.

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.


COLUMNS Rowset

GUID: DBSCHEMA_COLUMNS

Number of restriction columns: 4

Restriction columns: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME

Default sort order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME

Description: The COLUMNS rowset identifies the columns of tables (including views) defined in the catalog that are accessible to a given user.

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 SQL-92 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 in the reference section. This column cannot contain a NULL value.

If COLUMN_NAME refers to a column in a table or view that is updatable, one of either DBCOLUMNFLAGS_WRITE or DBCOLUMNFLAGS_WRITEUNKNOWN should be set. For more information about these flags, see "DBCOLUMNFLAGS Enumerated Type.
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 of a numeric data type other than VARNUMERIC, 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 or is VARNUMERIC, this is NULL.
NUMERIC_SCALE | DBTYPE_I2 | If the column's type indicator is DBTYPE_DECIMAL, DBTYPE_NUMERIC, or DBTYPE_VARNUMERIC, 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.


CONSTRAINT_COLUMN_USAGE Rowset

GUID: DBSCHEMA_CONSTRAINT_COLUMN_USAGE

Number of restriction columns: 7

Restriction columns: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME

Default sort order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_GUID, COLUMN_PROPID, CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME

Description: The CONSTRAINT_COLUMN_USAGE rowset identifies the columns used by referential constraints, unique constraints, check constraints, and assertions and that are defined in the catalog and owned by a given user.

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.


CONSTRAINT_TABLE_USAGE Rowset

GUID: DBSCHEMA_CONSTRAINT_TABLE_USAGE

Number of restriction columns: 6

Restriction columns: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME

Default sort order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME

Description: The CONSTRAINT_TABLE_USAGE rowset identifies the tables that are used by referential constraints, unique constraints, check constraints, and assertions and that are defined in the catalog and owned by a given user.

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.


FOREIGN_KEYS Rowset

GUID: DBSCHEMA_FOREIGN_KEYS

Number of restriction columns: 6

Restriction columns: PK_TABLE_CATALOG, PK_TABLE_SCHEMA, PK_TABLE_NAME, FK_TABLE_CATALOG, FK_TABLE_SCHEMA, FK_TABLE_NAME

Default sort order: FK_TABLE_CATALOG, FK_TABLE_SCHEMA, FK_TABLE_NAME

Description: The FOREIGN_KEYS rowset identifies the foreign key columns defined in the catalog by a given user. This schema rowset is built upon several SQL-92 schema views as a convenience to the non-SQL programmer, and if supported, this must be synchronized with the related SQL-92 views (REFERENTIAL_CONSTRAINTS and CONSTRAINT_COLUMN_USAGE).

Column name | Type indicator | Description
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_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.
DELETE_RULE DBTYPE_WSTR If a <delete rule> was specified, 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.
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.
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.
DEFERRABILITY DBTYPE_I2 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.


INDEXES Rowset

GUID: DBSCHEMA_INDEXES

Number of restriction columns: 5

Restriction columns: TABLE_CATALOG, TABLE_SCHEMA, INDEX_NAME, TYPE, TABLE_NAME

Default sort order: UNIQUE, TYPE, INDEX_CATALOG, INDEX_SCHEMA, INDEX_NAME, ORDINAL_POSITION

Description: The INDEXES rowset identifies the indexes defined in the catalog that are owned by a given user.

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.
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 0 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, 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, 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 multicolumn search key, if the consumer inserts an index entry with NULL value in some column of the search key, the provider ignores that entry and no error code is returned.

DBPROPVAL_IN_ALLOWNULL—The index allows entries where the key columns are NULL and sorts according to the collation described by DBPROP_INDEX_NULLCOLLATION.
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.


KEY_COLUMN_USAGE Rowset

GUID: DBSCHEMA_KEY_COLUMN_USAGE

Number of restriction columns: 7

Restriction columns: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME

Default sort order: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION

Description: The KEY_COLUMN_USAGE rowset identifies the columns defined in the catalog that are constrained as keys by a given user.

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.


PRIMARY_KEYS Rowset

GUID: DBSCHEMA_PRIMARY_KEYS

Number of restriction columns: 3

Restriction columns: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME

Default sort order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME

Description: 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 and, if supported, must be synchronized with the related SQL-92 view (CONSTRAINT_COLUMN_USAGE).

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_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.
PK_NAME | DBTYPE_WSTR | Primary key name. NULL if the provider does not support primary key constraints. This column is not returned by 1.x providers.


PROCEDURE_COLUMNS Rowset

GUID: DBSCHEMA_PROCEDURE_COLUMNS

Number of restriction columns: 4

Restriction columns: PROCEDURE_CATALOG, PROCEDURE_SCHEMA, PROCEDURE_NAME, COLUMN_NAME

Default sort order: PROCEDURE_CATALOG, PROCEDURE_SCHEMA, PROCEDURE_NAME

Description: The PROCEDURE_COLUMNS rowset returns information about the columns of rowsets returned by procedures.

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 of a numeric data type other than VARNUMERIC, 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 or is VARNUMERIC, this is NULL.
NUMERIC_SCALE | DBTYPE_I2 | If the column's type indicator is DBTYPE_DECIMAL, DBTYPE_NUMERIC, or DBTYPE_VARNUMERIC, 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."


PROCEDURE_PARAMETERS Rowset

GUID: DBSCHEMA_PROCEDURE_PARAMETERS

Number of restriction columns: 4

Restriction columns: PROCEDURE_CATALOG, PROCEDURE_SCHEMA, PROCEDURE_NAME, PARAMETER_NAME

Default sort order: PROCEDURE_CATALOG, PROCEDURE_SCHEMA, PROCEDURE_NAME

Description: The PROCEDURE_PARAMETERS rowset returns information about the parameters and return codes of procedures.

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 of a numeric data type other than VARNUMERIC, 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 or is VARNUMERIC, this is NULL.
NUMERIC_SCALE | DBTYPE_I2 | If the column's type indicator is DBTYPE_DECIMAL, DBTYPE_NUMERIC, or DBTYPE_VARNUMERIC, 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.


PROCEDURES Rowset

GUID: DBSCHEMA_PROCEDURES

Number of restriction columns: 4

Restriction columns: PROCEDURE_CATALOG, PROCEDURE_SCHEMA, PROCEDURE_NAME, PROCEDURE_TYPE

Default sort order: PROCEDURE_CATALOG, PROCEDURE_SCHEMA, PROCEDURE_NAME

Description: The PROCEDURES rowset is an OLE DB extension. It identifies the procedures defined in the catalog that are owned by a given user.

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. 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. 1.x providers do not return this column.


PROVIDER_TYPES Rowset

GUID: DBSCHEMA_PROVIDER_TYPES

Number of restriction columns: 2

Restriction columns: DATA_TYPE, BEST_MATCH

Default sort order: DATA_TYPE

Description: The PROVIDER_TYPES rowset identifies the (base) data types supported by the data provider.

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 length defined 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 and with no surrounding parentheses. If a creation parameter is length, maximum length, precision, scale, seed, or increment "length", "max length", "precision", "scale", "seed", and "increment" 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, "()" usually 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 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, 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, whether or not a column of this type is always autoincrementing depends on the provider's DBPROP_COL_AUTOINCREMENT column property. If the DBPROP_COL_AUTOINCREMENT property is read/write, 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, either all or none of the columns of this type are 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_VARNUMERIC, 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_VARNUMERIC, 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 | Intended for future use. The GUID of the type, if the type is described in a type library. Otherwise NULL.
TYPELIB | DBTYPE_WSTR | Intended for future use. The type library containing the description of the type, if the type is described in a type library. Otherwise NULL.
VERSION | DBTYPE_WSTR | Intended for future use. The version of the type definition. Providers might want to version type definitions. Different providers might use different versioning 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: Blobs and COM Objects.
BEST_MATCH DBTYPE_BOOL VARIANT_TRUE—The data type is the best match between all data types in the data store 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 data definition language (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-length or variable-length column.


REFERENTIAL_CONSTRAINTS Rowset

GUID: DBSCHEMA_REFERENTIAL_CONSTRAINTS

Number of restriction columns: 3

Restriction columns: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME

Default sort order: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UNIQUE_CONSTRAINT_CATALOG, UNIQUE_CONSTRAINT_SCHEMA, UNIQUE_CONSTRAINT_NAME

Description: The REFERENTIAL_CONSTRAINTS rowset identifies the referential constraints defined in the catalog that are owned by a given user.

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 If a <match type> was specified, the MATCH_OPTION value is one of the following:
"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, 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, 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.


SCHEMATA Rowset

GUID: DBSCHEMA_SCHEMATA

Number of restriction columns: 3

Restriction columns: CATALOG_NAME, SCHEMA_NAME, SCHEMA_OWNER

Default sort order: CATALOG_NAME, SCHEMA_NAME, SCHEMA_OWNER

Description: The SCHEMATA rowset identifies the schemas that are owned by a given user.

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.


SQL_LANGUAGES Rowset

GUID: DBSCHEMA_SQL_LANGUAGES

Number of restriction columns: 0

Restriction columns: none

Default sort order: SQL_LANGUAGE_SOURCE

Description: The SQL_LANGUAGES rowset identifies the conformance levels, options, and dialects supported by the SQL-implementation processing data defined in the catalog.

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 SQL-92–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.


STATISTICS Rowset

GUID: DBSCHEMA_STATISTICS

Number of restriction columns: 3

Restriction columns: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME

Default sort order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME

Description: The STATISTICS rowset identifies the statistics defined in the catalog that are owned by a given user.

Note This table is not related to the TABLE_STATISTICS rowset.

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.


TABLE_CONSTRAINTS Rowset

GUID: DBSCHEMA_TABLE_CONSTRAINTS

Number of restriction columns: 7

Restriction columns: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_TYPE

Default sort order: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_TYPE

Description: The TABLE_CONSTRAINTS rowset identifies the table constraints defined in the catalog that are owned by a given user.

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


TABLE_PRIVILEGES Rowset

GUID: DBSCHEMA_TABLE_PRIVILEGES

Number of restriction columns: 5

Restriction columns: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, GRANTOR, GRANTEE

Default sort order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, PRIVILEGE_TYPE

Description: The TABLE_PRIVILEGES rowset identifies the privileges on tables defined in the catalog that are available to or granted by a given user.

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.


TABLES_INFO Rowset

GUID: DBSCHEMA_TABLES_INFO

Number of restriction columns: 4

Restriction columns: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE

Default sort order: TABLE_TYPE, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME

Description: The TABLES_INFO rowset identifies the tables (including views) defined in the catalog that are accessible to a given user.

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"
"SYSTEM VIEW"
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: Rowsets.

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 IOpenRowset::OpenRowset on this table. If not set and if supported, bookmark values 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 that do not use PROPIDs to identify columns should return NULL in this column.


TABLES Rowset

GUID: DBSCHEMA_TABLES

Number of restriction columns: 4

Restriction columns: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE

Default sort order: TABLE_TYPE, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME

Description: The TABLES rowset identifies the tables (including views) defined in the catalog that are accessible to a given user.

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 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"
"SYSTEM VIEW"

This column cannot contain 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 that 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.

1.x providers do not return this column.


TABLE_STATISTICS Rowset

GUID: DBSCHEMA_TABLE_STATISTICS

Number of restriction columns: 7

Restriction columns: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, STATISTICS_CATALOG, STATISTICS_SCHEMA, STATISTICS_NAME, STATISTICS_TYPE

Default sort order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, STATISTICS_CATALOG, STATISTICS_SCHEMA, STATISTICS_NAME, ORDINAL_POSITION

Description: The TABLE_STATISTICS rowset describes the available set of statistics on tables in the provider.

Note This rowset is not related to the STATISTICS rowset. See additional information in the "Special-Purpose Rowsets" section of Chapter 1: Overview of OLE DB, and IOpenRowset::OpenRowset.

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name of the table of this statistic. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Schema name of the table. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
STATISTICS_CATALOG | DBTYPE_WSTR | Catalog name of the statistic. NULL if the provider does not support catalogs.
STATISTICS_SCHEMA | DBTYPE_WSTR | Schema name of the statistic. NULL if the provider does not support schemas.
STATISTICS_NAME | DBTYPE_WSTR | Name of the statistic.
STATISTICS_TYPE | DBTYPE_UI2 | Bit-mask specifying the kinds of statistics supported by this statistic: DBSTAT_HISTOGRAM—Indicates histogram support. DBSTAT_COLUMN_CARDINALITY— Indicates column cardinality support. DBSTAT_TUPLE_CARDINALITY—Indicates tuple cardinality support. Note Histograms correspond to only the first column for a multi-column statistic.
COLUMN_NAME | DBTYPE_WSTR | Name of the column that is part of this statistic.
COLUMN_GUID | DBTYPE_GUID | Column GUID. Providers that do not identify columns with GUIDs should return NULL for this column.
COLUMN_PROPID | DBTYPE_UI4 | Column PROPID. Providers that do not associate PROPIDs with columns should return NULL for this column.
ORDINAL_POSITION | DBTYPE_UI4 | Ordinal position of the column in the statistic starting with one.
SAMPLE_PCT | DBTYPE_UI2 | Percentage of the base table’s rows that were sampled to produce this statistic. NULL if not supported.
LAST_UPDATE_TIME | DBTYPE_DATE | The time when this statistic was last updated. NULL if not supported.
NO_OF_RANGES | DBTYPE_UI4 | Number of ranges in histogram. NULL if histograms are not supported as part of this statistic.
COLUMN_CARDINALITY | DBTYPE_I8 | Number of unique values of the specified column in the table. NULL if cardinality information is not available for this column.
TUPLE_CARDINALITY | DBTYPE_I8 | Number of unique values of the tuple <c1, c2, ..., ci> in the table where c1, c2, ..., ci are the columns in the statistic as indicated by ORDINAL_POSITION 1, 2, ..., i, respectively. NULL if cardinality information is not available for this tuple.
TABLE_CARDINALITY | DBTYPE_I8 | Number of rows in the table.
AVG_COLUMN_LENGTH | DBTYPE_UI4 | The average length in bytes of the column in the table. This is required only for variable-length character or binary columns (of types DBTYPE_WSTR, DBTYPE_STR, DBTYPE_BYTES, and DBTYPE_VARNUMERIC) and is optional for fixed-length data types. NULL if the provider does not maintain this information.


TRANSLATIONS Rowset

GUID: DBSCHEMA_TRANSLATIONS

Number of restriction columns: 3

Restriction columns: TRANSLATION_CATALOG, TRANSLATION_SCHEMA, TRANSLATION_NAME

Default sort order: TRANSLATION_CATALOG, TRANSLATION_SCHEMA, TRANSLATION_NAME

Description: The TRANSLATIONS rowset identifies the character translations defined in the catalog that are accessible to a given user.

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.


USAGE_PRIVILEGES Rowset

GUID: DBSCHEMA_USAGE_PRIVILEGES

Number of restriction columns: 6

Restriction columns: OBJECT_CATALOG, OBJECT_SCHEMA, OBJECT_NAME, OBJECT_TYPE, GRANTOR, GRANTEE

Default sort order: OBJECT_CATALOG, OBJECT_SCHEMA, OBJECT_NAME, OBJECT_TYPE, PRIVILEGE_TYPE

Description: The USAGE_PRIVILEGES rowset identifies the USAGE privileges on objects defined in the catalog that are available to or granted by a given user.

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.


VIEW_COLUMN_USAGE Rowset

GUID: DBSCHEMA_VIEW_COLUMN_USAGE

Number of restriction columns: 3

Restriction columns: VIEW_CATALOG, VIEW_SCHEMA, VIEW_NAME

Default sort order: VIEW_CATALOG, VIEW_SCHEMA, VIEW_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_GUID, COLUMN_PROPID

Description: The VIEW_COLUMN_USAGE rowset identifies the columns on which viewed tables, defined in the catalog and owned by a given user, are dependent.

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.


VIEWS Rowset

GUID: DBSCHEMA_VIEWS

Number of restriction columns: 3

Restriction columns: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME

Default sort order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME

Description: The VIEWS rowset identifies the views defined in the catalog that are accessible to a given user.

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. 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. 1.x providers do not return this column.


VIEW_TABLE_USAGE Rowset

GUID: DBSCHEMA_VIEW_TABLE_USAGE

Number of restriction columns: 3

Restriction columns: VIEW_CATALOG, VIEW_SCHEMA, VIEW_NAME

Default sort order: VIEW_CATALOG, VIEW_SCHEMA, VIEW_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME

Description: The VIEW_TABLE_USAGE rowset identifies the tables on which viewed tables, defined in the catalog and owned by a given user, are dependent.

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.


Appendix C: OLE DB Properties

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.

  • Property Sets lists all property sets defined by OLE DB.
  • Properties by Property Groups lists all properties contained within each property group.
  • Properties (Alphabetical) gives a detailed description of each OLE DB property. The properties are listed alphabetically at the beginning of this section. Click a property name in the alphabetical list to go to its description.


Property Sets

The following table shows property sets that are defined by OLE DB. Providers also can define their own property sets.

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_ROWSET | All OLE DB–defined properties in the Rowset property group.
DBPROPSET_SESSION | All OLE DB–defined properties in the Session property group.
DBPROPSET_STREAM | All OLE DB–defined properties in the Stream property group.
DBPROPSET_TABLE | All OLE DB–defined properties in the Table property group.
DBPROPSET_PROPERTIESINERROR If ICommand::Execute, ICommandPrepare::Prepare, IDBInitialize::Initialize, IBindResource::Bind, or ICreateRow::CreateRow 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 by IDBProperties::GetPropertyInfo.

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. 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_VIEW | All OLE DB–defined properties in the View property group.


Properties by Property Groups

This section lists all properties contained within each property group. For more information about a specific property, click its link in the Property Group column.

Property Group | Description
Column | Properties used to create and describe columns.
Data Source | Properties that apply to data source objects. These properties can be set for some providers.
Data Source Creation | Properties used to create data source objects.
Data Source Information | Properties that describe data source objects. These properties are read-only for all providers and constitute a set of static information about the provider and data source object.
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.
Stream | Properties that apply to streams.
Table | Properties used to create and describe tables.
View | Properties used to create views.


Column Property Group

Property set: DBPROPSET_COLUMN

Properties

DBPROP_COL_AUTOINCREMENT | DBPROP_COL_NULLABLE
DBPROP_COL_DEFAULT | DBPROP_COL_PRIMARYKEY
DBPROP_COL_DESCRIPTION | DBPROP_COL_SEED
DBPROP_COL_FIXEDLENGTH | DBPROP_COL_UNIQUE
DBPROP_COL_INCREMENT | DBPROP_COLUMNLCID
DBPROP_COL_ISLONG |


Data Source Property Group

Property set: DBPROPSET_DATASOURCE

Properties:

DBPROP_CURRENTCATALOG | DBPROP_RESETDATASOURCE
DBPROP_MULTIPLECONNECTIONS |


Data Source Creation Property Group

Property set: None

Properties: None. OLE DB does not define any data source object creation properties. Providers can define their own data source object creation properties.


Data Source Information Property Group

Property set: DBPROPSET_DATASOURCEINFO

Properties:

DBPROP_ACTIVESESSIONS | DBPROP_PROCEDURETERM
DBPROP_ALTERCOLUMN | DBPROP_PROVIDERFRIENDLYNAME
DBPROP_ASYNCTXNABORT | DBPROP_PROVIDERMEMORY
DBPROP_ASYNCTXNCOMMIT | DBPROP_PROVIDERFILENAME
DBPROP_BYREFACCESSORS | DBPROP_PROVIDEROLEDBVER
DBPROP_CATALOGLOCATION | DBPROP_PROVIDERVER
DBPROP_CATALOGTERM | DBPROP_QUOTEDIDENTIFIERCASE
DBPROP_CATALOGUSAGE | DBPROP_ROWSETCONVERSIONSONCOMMAND
DBPROP_COLUMNDEFINITION | DBPROP_SCHEMATERM
DBPROP_COMSERVICES | DBPROP_SCHEMAUSAGE
DBPROP_CONCATNULLBEHAVIOR | DBPROP_SERVERNAME
DBPROP_CONNECTIONSTATUS | DBPROP_SQLSUPPORT
DBPROP_DATASOURCENAME | DBPROP_STRUCTUREDSTORAGE
DBPROP_DATASOURCEREADONLY | DBPROP_SUBQUERIES
DBPROP_DATASOURCE_TYPE | DBPROP_SUPPORTEDTXNDDL
DBPROP_DBMSNAME | DBPROP_SUPPORTEDTXNISOLEVELS
DBPROP_DBMSVER | DBPROP_SUPPORTEDTXNISORETAIN
DBPROP_DSOTHREADMODEL | DBPROP_TABLESTATISTICS
DBPROP_GENERATEURL | DBPROP_TABLETERM
DBPROP_GROUPBY | DBPROP_USERNAME
DBPROP_HETEROGENEOUSTABLES | MDPROP_AGGREGATECELL_UPDATE
DBPROP_IDENTIFIERCASE | MDPROP_AXES
DBPROP_MAXINDEXSIZE | MDPROP_FLATTENING_SUPPORT
DBPROP_MAXOPENCHAPTERS | MDPROP_MDX_CASESUPPORT
DBPROP_MAXROWSIZE | MDPROP_MDX_DESCFLAGS
DBPROP_MAXROWSIZEINCLUDESBLOB | MDPROP_MDX_FORMULAS
DBPROP_MAXTABLESINSELECT | MDPROP_MDX_JOINCUBES
DBPROP_MULTIPLEPARAMSETS | MDPROP_MDX_MEMBER_FUNCTIONS
DBPROP_MULTIPLERESULTS | MDPROP_MDX_NONMEASURE_EXPRESSIONS
DBPROP_MULTIPLESTORAGEOBJECTS | MDPROP_MDX_NUMERIC_FUNCTIONS
DBPROP_MULTITABLEUPDATE | MDPROP_MDX_OBJQUALIFICATION
DBPROP_NULLCOLLATION | MDPROP_MDX_OUTERREFERENCE
DBPROP_OLEOBJECTS | MDPROP_MDX_QUERYBYPROPERTY
DBPROP_OPENROWSETSUPPORT | MDPROP_MDX_RANGEROWSET
DBPROP_ORDERBYCOLUMNSINSELECT | MDPROP_MDX_SET_FUNCTIONS
DBPROP_OUTPUTPARAMETERAVAILABILITY | MDPROP_MDX_SLICER
DBPROP_PERSISTENTIDTYPE | MDPROP_MDX_STRING_COMPOP
DBPROP_PREPAREABORTBEHAVIOR | MDPROP_NAMED_LEVELS
DBPROP_PREPARECOMMITBEHAVIOR | MDPROP_VISUALMODE


Index Property Group

Property set: DBPROPSET_INDEX

Properties

DBPROP_INDEX_AUTOUPDATE | DBPROP_INDEX_PRIMARYKEY
DBPROP_INDEX_CLUSTERED | DBPROP_INDEX_SORTBOOKMARKS
DBPROP_INDEX_FILLFACTOR | DBPROP_INDEX_TEMPINDEX
DBPROP_INDEX_INITIALSIZE | DBPROP_INDEX_TYPE
DBPROP_INDEX_NULLCOLLATION | DBPROP_INDEX_UNIQUE
DBPROP_INDEX_NULLS |


Initialization Property Group

Property set: DBPROPSET_DBINIT

Properties

DBPROP_AUTH_CACHE_AUTHINFO | DBPROP_INIT_GENERALTIMEOUT
DBPROP_AUTH_ENCRYPT_PASSWORD | DBPROP_INIT_HWND
DBPROP_AUTH_INTEGRATED | DBPROP_INIT_IMPERSONATION_LEVEL
DBPROP_AUTH_MASK_PASSWORD | DBPROP_INIT_LCID
DBPROP_AUTH_PASSWORD | DBPROP_INIT_LOCATION
DBPROP_AUTH_PERSIST_ENCRYPTED | DBPROP_INIT_LOCKOWNER
DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO | DBPROP_INIT_MODE
DBPROP_AUTH_USERID | DBPROP_INIT_OLEDBSERVICES
DBPROP_INIT_ASYNCH | DBPROP_INIT_PROMPT
DBPROP_INIT_BINDFLAGS | DBPROP_INIT_PROTECTION_LEVEL
DBPROP_INIT_CATALOG | DBPROP_INIT_PROVIDERSTRING
DBPROP_INIT_DATASOURCE | DBPROP_INIT_TIMEOUT


Rowset Property Group

Property set: DBPROPSET_ROWSET

Properties

DBPROP_ABORTPRESERVE | DBPROP_IRowsetScroll
DBPROP_ACCESSORDER | DBPROP_IRowsetUpdate
DBPROP_APPENDONLY | DBPROP_IRowsetView
DBPROP_BLOCKINGSTORAGEOBJECTS | DBPROP_IScopedOperations
DBPROP_BOOKMARKINFO | DBPROP_ISequentialStream
DBPROP_BOOKMARKS | DBPROP_IStorage
DBPROP_BOOKMARKSKIPPED | DBPROP_IStream
DBPROP_BOOKMARKTYPE | DBPROP_ISupportErrorInfo
DBPROP_CACHEDEFERRED | DBPROP_LITERALBOOKMARKS
DBPROP_CANFETCHBACKWARDS | DBPROP_LITERALIDENTITY
DBPROP_CANHOLDROWS | DBPROP_LOCKMODE
DBPROP_CANSCROLLBACKWARDS | DBPROP_MAXOPENROWS
DBPROP_CHANGEINSERTEDROWS | DBPROP_MAXPENDINGROWS
DBPROP_CLIENTCURSOR | DBPROP_MAXROWS
DBPROP_COLUMNRESTRICT | DBPROP_MAYWRITECOLUMN
DBPROP_COMMANDTIMEOUT | DBPROP_MEMORYUSAGE
DBPROP_COMMITPRESERVE | DBPROP_NOTIFICATIONGRANULARITY
DBPROP_DEFERRED | DBPROP_NOTIFICATIONPHASES
DBPROP_DELAYSTORAGEOBJECTS | DBPROP_NOTIFYCOLUMNSET
DBPROP_FINDCOMPAREOPS | DBPROP_NOTIFYROWDELETE
DBPROP_HIDDENCOLUMNS | DBPROP_NOTIFYROWFIRSTCHANGE
DBPROP_IAccessor | DBPROP_NOTIFYROWINSERT
DBPROP_IBindResource | DBPROP_NOTIFYROWRESYNCH
DBPROP_IChapteredRowset | DBPROP_NOTIFYROWSETCHANGED
DBPROP_IColumnsInfo | DBPROP_NOTIFYROWSETFETCHPOSITIONCHANGE
DBPROP_IColumnsInfo2 | DBPROP_NOTIFYROWSETRELEASE
DBPROP_IColumnsRowset | DBPROP_NOTIFYROWUNDOCHANGE
DBPROP_IConnectionPointContainer | DBPROP_NOTIFYROWUNDODELETE
DBPROP_IConvertType | DBPROP_NOTIFYROWUNDOINSERT
DBPROP_ICreateRow | DBPROP_NOTIFYROWUPDATE
DBPROP_IDBAsynchStatus | DBPROP_ORDEREDBOOKMARKS
DBPROP_IGetRow | DBPROP_OTHERINSERT
DBPROP_IGetSession | DBPROP_OTHERUPDATEDELETE
DBPROP_IGetSourceRow | DBPROP_OWNINSERT
DBPROP_ILockBytes | DBPROP_OWNUPDATEDELETE
DBPROP_IMMOBILEROWS | DBPROP_QUICKRESTART
DBPROP_IMultipleResults | DBPROP_REENTRANTEVENTS
DBPROP_IParentRowset | DBPROP_REMOVEDELETED
DBPROP_IRow | DBPROP_REPORTMULTIPLECHANGES
DBPROP_IRowChange | DBPROP_RETURNPENDINGINSERTS
DBPROP_IRowSchemaChange | DBPROP_ROW_BULKOPS
DBPROP_IRowset | DBPROP_ROWRESTRICT
DBPROP_IRowsetBookmark | DBPROP_ROWSET_ASYNCH
DBPROP_IRowsetChange | DBPROP_ROWTHREADMODEL
DBPROP_IRowsetCurrentIndex | DBPROP_SERVERCURSOR
DBPROP_IRowsetFind | DBPROP_SERVERDATAONINSERT
DBPROP_IRowsetIdentity | DBPROP_SKIPROWCOUNTRESULTS
DBPROP_IRowsetIndex | DBPROP_STRONGIDENTITY
DBPROP_IRowsetInfo | DBPROP_TRANSACTEDOBJECT
DBPROP_IRowsetLocate | DBPROP_UNIQUEROWS
DBPROP_IRowsetRefresh | DBPROP_UPDATABILITY


Session Property Group

Property set: DBPROPSET_SESSION

Property: DBPROP_SESS_AUTOCOMMITISOLEVELS


Stream Property Group

Property set: DBPROPSET_STREAM

Properties


Table Property Group

Property set: DBPROPSET_TABLE

Property: DBPROP_TBL_TEMPTABLE


View Property Group

Property set: DBPROPSET_VIEW

Properties

DBPROP_FILTERCOMPAREOPS | DBPROP_IViewRowset
DBPROP_IAccessor | DBPROP_IViewSort
DBPROP_IColumnsInfo | DBPROP_MAXORSINFILTER
DBPROP_ISupportErrorInfo | DBPROP_MAXSORTCOLUMNS
DBPROP_IViewChapter | DBPROP_SORTONINDEX
DBPROP_IViewFilter |


Properties (Alphabetical)

The properties listed in this section are contained in the designated property sets and property groups. (For a listing of properties organized by property group, see "Properties by Property Groups.") 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.

DBPROP_ABORTPRESERVE | DBPROP_IRowsetScroll
DBPROP_ACCESSORDER | DBPROP_IRowsetUpdate
DBPROP_ACTIVESESSIONS | DBPROP_IRowsetView
DBPROP_ALTERCOLUMN | DBPROP_IScopedOperations
DBPROP_APPENDONLY | DBPROP_ISequentialStream
DBPROP_ASYNCTXNABORT | DBPROP_IStorage
DBPROP_ASYNCTXNCOMMIT | DBPROP_IStream
DBPROP_AUTH_CACHE_AUTHINFO | DBPROP_ISupportErrorInfo
DBPROP_AUTH_ENCRYPT_PASSWORD | DBPROP_IViewChapter
DBPROP_AUTH_INTEGRATED | DBPROP_IViewFilter
DBPROP_AUTH_MASK_PASSWORD | DBPROP_IViewRowset
DBPROP_AUTH_PASSWORD | DBPROP_IViewSort
DBPROP_AUTH_PERSIST_ENCRYPTED | DBPROP_LITERALBOOKMARKS
DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO | DBPROP_LITERALIDENTITY
DBPROP_AUTH_USERID | DBPROP_LOCKMODE
DBPROP_BLOCKINGSTORAGEOBJECTS | DBPROP_MAXINDEXSIZE
DBPROP_BOOKMARKINFO | DBPROP_MAXOPENCHAPTERS
DBPROP_BOOKMARKS | DBPROP_MAXOPENROWS
DBPROP_BOOKMARKSKIPPED | DBPROP_MAXORSINFILTER
DBPROP_BOOKMARKTYPE | DBPROP_MAXPENDINGROWS
DBPROP_BYREFACCESSORS | DBPROP_MAXROWS
DBPROP_CACHEDEFERRED | DBPROP_MAXROWSIZE
DBPROP_CANFETCHBACKWARDS | DBPROP_MAXROWSIZEINCLUDESBLOB
DBPROP_CANHOLDROWS | DBPROP_MAXSORTCOLUMNS
DBPROP_CANSCROLLBACKWARDS | DBPROP_MAXTABLESINSELECT
DBPROP_CATALOGLOCATION | DBPROP_MAYWRITECOLUMN
DBPROP_CATALOGTERM | DBPROP_MEMORYUSAGE
DBPROP_CATALOGUSAGE | DBPROP_MULTIPLECONNECTIONS
DBPROP_CHANGEINSERTEDROWS | DBPROP_MULTIPLEPARAMSETS
DBPROP_CLIENTCURSOR | DBPROP_MULTIPLERESULTS
DBPROP_COL_AUTOINCREMENT | DBPROP_MULTIPLESTORAGEOBJECTS
DBPROP_COL_DEFAULT | DBPROP_MULTITABLEUPDATE
DBPROP_COL_DESCRIPTION | DBPROP_NOTIFICATIONGRANULARITY
DBPROP_COL_FIXEDLENGTH | DBPROP_NOTIFICATIONPHASES
DBPROP_COL_INCREMENT | DBPROP_NOTIFYCOLUMNSET
DBPROP_COL_ISLONG | DBPROP_NOTIFYROWDELETE
DBPROP_COL_NULLABLE | DBPROP_NOTIFYROWFIRSTCHANGE
DBPROP_COL_PRIMARYKEY | DBPROP_NOTIFYROWINSERT
DBPROP_COL_SEED | DBPROP_NOTIFYROWRESYNCH
DBPROP_COL_UNIQUE | DBPROP_NOTIFYROWSETCHANGED
DBPROP_COLUMNDEFINITION | DBPROP_NOTIFYROWSETFETCHPOSITIONCHANGE
DBPROP_COLUMNLCID | DBPROP_NOTIFYROWSETRELEASE
DBPROP_COLUMNRESTRICT | DBPROP_NOTIFYROWUNDOCHANGE
DBPROP_COMMANDTIMEOUT | DBPROP_NOTIFYROWUNDODELETE
DBPROP_COMMITPRESERVE | DBPROP_NOTIFYROWUNDOINSERT
DBPROP_COMSERVICES | DBPROP_NOTIFYROWUPDATE
DBPROP_CONCATNULLBEHAVIOR | DBPROP_NULLCOLLATION
DBPROP_CONNECTIONSTATUS | DBPROP_OLEOBJECTS
DBPROP_CURRENTCATALOG | DBPROP_OPENROWSETSUPPORT
DBPROP_DATASOURCENAME | DBPROP_ORDERBYCOLUMNSINSELECT
DBPROP_DATASOURCEREADONLY | DBPROP_ORDEREDBOOKMARKS
DBPROP_DATASOURCE_TYPE | DBPROP_OTHERINSERT
DBPROP_DBMSNAME | DBPROP_OTHERUPDATEDELETE
DBPROP_DBMSVER | DBPROP_OUTPUTENCODING
DBPROP_DEFERRED | DBPROP_OUTPUTPARAMETERAVAILABILITY
DBPROP_DELAYSTORAGEOBJECTS | DBPROP_OUTPUTSTREAM
DBPROP_DSOTHREADMODEL | DBPROP_OWNINSERT
DBPROP_FILTERCOMPAREOPS | DBPROP_OWNUPDATEDELETE
DBPROP_FINDCOMPAREOPS | DBPROP_PERSISTENTIDTYPE
DBPROP_GENERATEURL | DBPROP_PREPAREABORTBEHAVIOR
DBPROP_GROUPBY | DBPROP_PREPARECOMMITBEHAVIOR
DBPROP_HETEROGENEOUSTABLES | DBPROP_PROCEDURETERM
DBPROP_HIDDENCOLUMNS | DBPROP_PROVIDERFRIENDLYNAME
DBPROP_IAccessor | DBPROP_PROVIDERMEMORY
DBPROP_IBindResource | DBPROP_PROVIDERFILENAME
DBPROP_IChapteredRowset | DBPROP_PROVIDEROLEDBVER
DBPROP_IColumnsInfo | DBPROP_PROVIDERVER
DBPROP_IColumnsInfo2 | DBPROP_QUICKRESTART
DBPROP_IColumnsRowset | DBPROP_QUOTEDIDENTIFIERCASE
DBPROP_IConnectionPointContainer | DBPROP_REENTRANTEVENTS
DBPROP_IConvertType | DBPROP_REMOVEDELETED
DBPROP_ICreateRow | DBPROP_REPORTMULTIPLECHANGES
DBPROP_IDBAsynchStatus | DBPROP_RESETDATASOURCE
DBPROP_IDENTIFIERCASE | DBPROP_RETURNPENDINGINSERTS
DBPROP_IGetRow | DBPROP_ROW_BULKOPS
DBPROP_IGetSession | DBPROP_ROWRESTRICT
DBPROP_IGetSourceRow | DBPROP_ROWSET_ASYNCH
DBPROP_ILockBytes | DBPROP_ROWSETCONVERSIONSONCOMMAND
DBPROP_IMMOBILEROWS | DBPROP_ROWTHREADMODEL
DBPROP_IMultipleResults | DBPROP_SCHEMATERM
DBPROP_INDEX_AUTOUPDATE | DBPROP_SCHEMAUSAGE
DBPROP_INDEX_CLUSTERED | DBPROP_SERVERCURSOR
DBPROP_INDEX_FILLFACTOR | DBPROP_SERVERDATAONINSERT
DBPROP_INDEX_INITIALSIZE | DBPROP_SERVERNAME
DBPROP_INDEX_NULLCOLLATION | DBPROP_SESS_AUTOCOMMITISOLEVELS
DBPROP_INDEX_NULLS | DBPROP_SKIPROWCOUNTRESULTS
DBPROP_INDEX_PRIMARYKEY | DBPROP_SORTONINDEX
DBPROP_INDEX_SORTBOOKMARKS | DBPROP_SQLSUPPORT
DBPROP_INDEX_TEMPINDEX | DBPROP_STRONGIDENTITY
DBPROP_INDEX_TYPE | DBPROP_STRUCTUREDSTORAGE
DBPROP_INDEX_UNIQUE | DBPROP_SUBQUERIES
DBPROP_INIT_ASYNCH | DBPROP_SUPPORTEDTXNDDL
DBPROP_INIT_BINDFLAGS | DBPROP_SUPPORTEDTXNISOLEVELS
DBPROP_INIT_CATALOG | DBPROP_SUPPORTEDTXNISORETAIN
DBPROP_INIT_DATASOURCE | DBPROP_TABLESTATISTICS
DBPROP_INIT_GENERALTIMEOUT | DBPROP_TABLETERM
DBPROP_INIT_HWND | DBPROP_TBL_TEMPTABLE
DBPROP_INIT_IMPERSONATION_LEVEL | DBPROP_TRANSACTEDOBJECT
DBPROP_INIT_LCID | DBPROP_UNIQUEROWS
DBPROP_INIT_LOCATION | DBPROP_UPDATABILITY
DBPROP_INIT_LOCKOWNER | DBPROP_USERNAME
DBPROP_INIT_MODE | MDPROP_AGGREGATECELL_UPDATE
DBPROP_INIT_OLEDBSERVICES | MDPROP_AXES
DBPROP_INIT_PROMPT | MDPROP_FLATTENING_SUPPORT
DBPROP_INIT_PROTECTION_LEVEL | MDPROP_MDX_CASESUPPORT
DBPROP_INIT_PROVIDERSTRING | MDPROP_MDX_DESCFLAGS
DBPROP_INIT_TIMEOUT | MDPROP_MDX_FORMULAS
DBPROP_IParentRowset | MDPROP_MDX_JOINCUBES
DBPROP_IRow | MDPROP_MDX_MEMBER_FUNCTIONS
DBPROP_IRowChange | MDPROP_MDX_NONMEASURE_EXPRESSIONS
DBPROP_IRowSchemaChange | MDPROP_MDX_NUMERIC_FUNCTIONS
DBPROP_IRowset | MDPROP_MDX_OBJQUALIFICATION
DBPROP_IRowsetBookmark | MDPROP_MDX_OUTERREFERENCE
DBPROP_IRowsetChange | MDPROP_MDX_QUERYBYPROPERTY
DBPROP_IRowsetCurrentIndex | MDPROP_MDX_RANGEROWSET
DBPROP_IRowsetFind | MDPROP_MDX_SET_FUNCTIONS
DBPROP_IRowsetIdentity | MDPROP_MDX_SLICER
DBPROP_IRowsetIndex | MDPROP_MDX_STRING_COMPOP
DBPROP_IRowsetInfo | MDPROP_NAMED_LEVELS
DBPROP_IRowsetLocate | MDPROP_VISUALMODE
DBPROP_IRowsetRefresh |


DBPROP_ABORTPRESERVE

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: Preserve on Abort

Value | Meaning
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.

Note DBPROP_ABORTPRESERVE applies only to local transactions.


DBPROP_ACCESSORDER

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_I4

Typical R/W: R/W

Description: Access Order

Sets the order in which columns must be accessed by methods (such as IRowset::GetData, IRow::GetColumns, and IRow::Open) that operate on rowsets, rows, and streams.

Note For optimal performance and interoperability, consumers should set this property to DBPROPVAL_AO_SEQUENTIALSTORAGEOBJECTS.

Value | Meaning
DBPROPVAL_AO_RANDOM | Columns can be accessed in any order.
DBPROPVAL_AO_SEQUENTIALSTORAGEOBJECTS Columns bound as storage objects can be accessed only in sequential order as determined by the column ordinal. Further, storage objects from one row must be retrieved before calling IRowset::GetData on any columns in any subsequent row. Calling IRowset::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 IRowset::GetData for that row.
  • IRowset::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 IRowsetInfo::GetProperties 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 IRowset::GetData on any columns in any subsequent row. Calling IRowset::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 IRowset::GetData for that row.
  • IRowset::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 IRowsetInfo::GetProperties 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 session objects that can be active at one time.

Note For some providers, this value represents the number of active connections or a combination of session objects and active connections. This value can reflect a limitation imposed by either the provider or the data store. At minimum, if the value is zero, there is no specified limit or the limit is unknown; or if the value is one, there can be only one active session; or lastly, if the value is greater than one, there is a known limit. The number of supported sessions is provider-specific.


DBPROP_ALTERCOLUMN

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Alter Column Support

Comprises a bitmask describing which portions of the DBCOLUMNDESC structure can be used in a call to IAlterTable::AlterColumn.

Value is a combination of one or more of the following:

  • DBCOLUMNDESCFLAGS_TYPENAME
  • DBCOLUMNDESCFLAGS_ITYPEINFO
  • DBCOLUMNDESCFLAGS_PROPERTIES
  • DBCOLUMNDESCFLAGS_CLSID
  • DBCOLUMNDESCFLAGS_COLSIZE
  • DBCOLUMNDESCFLAGS_DBCID
  • DBCOLUMNDESCFLAGS_WTYPE
  • DBCOLUMNDESCFLAGS_PRECISION
  • DBCOLUMNDESCFLAGS_SCALE

For more information about the mapping of these values to fields in the DBCOLUMNDESC structure, see the description of the dwColumnDescFlags parameter in the reference entry 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 and will be populated only by those rows inserted through the rowset. Append-only rowsets are used for adding records to a table without having to get a rowset over existing rows.

DBPROP_APPENDONLY set to VARIANT_TRUE implies the following:

  • 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

Indicates whether transactions can be aborted asynchronously.

Value | Meaning
VARIANT_TRUE | Transactions can be aborted asynchronously.
VARIANT_FALSE | Transactions cannot be aborted asynchronously.


DBPROP_ASYNCTXNCOMMIT

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_BOOL

Typical R/W: R

Description: Asynchable Commit

Indicates whether transactions can be committed asynchronously.

Value | Meaning
VARIANT_TRUE | Transactions can be committed asynchronously
VARIANT_FALSE | Transactions cannot 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

Value | Meaning
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

Value | Meaning
VARIANT_TRUE | The consumer requires that the password be sent to the data source object 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® Windows NT®/Windows® 2000 Integrated Security, this is "SSPI" (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.

The security context is established when the data source object is initialized. The provider should always use this security context when creating sessions and on all calls made on that connection. This security context may differ from the ones in effect for subsequent calls on other threads.


DBPROP_AUTH_MASK_PASSWORD

Property group: Initialization

Property set: DBPROPSET_DBINIT

Type: VT_BOOL

Typical R/W: R/W

Description: Mask Password

Value | Meaning
VARIANT_TRUE | The consumer requires that the password be sent to the data source object 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

Indicates the password to be used when connecting to the data source object 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. 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

Value | Meaning
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

Value | Meaning
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 object is initialized, the data source object cannot persist sensitive authentication information. Furthermore, requesting properties that contain sensitive authentication information, such as a password, returns some default value instead of the actual password.

After the data source object has been uninitialized, sensitive information that was set when the data source object 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 object, but new properties set after the data source object has been uninitialized can be persisted or obtained as long as DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO is VARIANT_TRUE.

Before the data source object is initialized for the first time, sensitive information can be obtained by calling IDBProperties::GetProperties and can be persisted, regardless of the setting of DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO. For this reason, consumers should not pass around uninitialized data source objects.


DBPROP_AUTH_USERID

Property group: Initialization

Property set: DBPROPSET_DBINIT

Type: VT_BSTR

Typical R/W: R/W

Description: User ID

Indicates the user ID to be used when connecting to the data source object or enumerator.


DBPROP_BLOCKINGSTORAGEOBJECTS

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: Blocking Storage Objects

Indicates whether storage objects might prevent use of other methods on the rowset.

Value | Meaning
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. The following flag can be set:

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

Indicates whether the rowset supports bookmarks.

Value | Meaning
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

Indicates whether the rowset allows IRowsetLocate::GetRowsAt, IRowsetScroll::GetApproximatePosition, or IRowsetFind::FindNextRow to continue if a bookmark row was deleted; contains a bookmarked row to which the consumer does not have access rights; contains a bookmark identifying a row not in the chapter; or contains a bookmarked row that is no longer a member of the rowset.

Value | Meaning
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

Indicates the bookmark type supported by the rowset. One of the following:

  • DBPROPVAL_BMK_NUMERIC—The bookmark type is numeric. Numeric bookmarks are based on a row property that is not dependent on the values of the row's columns. For example, 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: Rowsets.


DBPROP_BYREFACCESSORS

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_BOOL

Typical R/W: R

Description: Pass By Ref Accessors

Indicates whether the provider supports the DBACCESSOR_PASSBYREF flag in IAccessor::CreateAccessor. This applies both to row and to 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

Value | Meaning
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. Note 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 Backwards

Indicates whether the rowset can fetch backward.

Value | Meaning
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

Value | Meaning
VARIANT_TRUE | The rowset allows the consumer to retrieve more rows or change the next fetch position, while holding previously fetched rows or rows with pending changes.
VARIANT_FALSE | The rowset might require pending changes to be transmitted to the data store and all rows to be released before fetching additional rows, inserting new rows, or changing the next fetch position.

Note If the provider makes no optimizations for releasing all rows between fetches, it is not required to return DB_E_ROWSNOTRELEASED when retrieving rows without releasing the previously held set of row handles. Such providers do not return an error when setting DBPROP_CANHOLDROWS to VARIANT_FALSE but always return VARIANT_TRUE if the consumer calls IRowsetInfo::GetProperties for DBPROP_CANHOLDROWS.


DBPROP_CANSCROLLBACKWARDS

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: Scroll Backwards

Indicates whether the rowset can scroll backward.

Value | Meaning
VARIANT_TRUE | lRowsOffset in IRowset::GetNextRows or IRowsetLocate::GetRowsAt can be negative.
VARIANT_FALSE | lRowsOffset must be non-negative.

If the rowset supports IRowsetLocate, 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

Indicates 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—for example, "\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 "mailto: ADMIN.EMP@EMPDATA".


DBPROP_CATALOGTERM

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_BSTR

Typical R/W: R

Description: Catalog Term

Indicates the name the data source object 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 (DML) 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 and may apply only to the table name, not the index name, depending on the SQL implementation.
  • 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

Value | Meaning
VARIANT_TRUE | The consumer can call IRowsetChange::DeleteRows or IRowsetChange::SetData for newly inserted rows.
VARIANT_FALSE | If the consumer calls IRowsetChange::DeleteRows or IRowsetChange::SetData for newly inserted rows, DeleteRows returns a status of DBROWSTATUS_E_NEWLYINSERTED for the row and SetData returns DB_E_NEWLYINSERTED.

Note A newly inserted row is defined to be a row for which the insertion has been transmitted to the data source object, as opposed to a pending insert row. For some providers, this property may imply other properties.


DBPROP_CLIENTCURSOR

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: Client Cursor

DBPROP_CLIENTCURSOR requires a provider to materialize a cursor-capable rowset on the client. This property overrides other rowset properties used to determine where to materialize a cursor. Providers may support this property if they offer client cursor capability. The Client Cursor Engine, an OLE DB service component that provides scrolling and find support over OLE DB rowsets, implements DBPROP_CLIENTCURSOR.

Use of this property by a consumer implies knowledge of the cursor engine and its functionality. Otherwise, the consumer should request the properties it wants, state its preference for cursor location (using DBPROP_SERVERCURSOR), and proceed.

  • If the value of this property is set to VARIANT_TRUE, the service components will ignore the setting of DBPROP_SERVERCURSOR. The service components request a forward-only rowset from the provider, which is then used to populate a client-side cursor.
  • If the value of this property is not set to VARIANT_TRUE, the service components determine cursor location according to DBPROP_SERVERCURSOR, any properties that require cursor functionality, and the capabilities of the provider. See DBPROP_SERVERCURSOR for more information.

Providers should never see this property and should return an error if asked.


DBPROP_COL_AUTOINCREMENT

Property group: Column

Property set: DBPROPSET_COLUMN

Type: VT_BOOL

Typical R/W: R/W

Description: Autoincrement

Indicates whether the values of the column are autoincrementing.

Value | Meaning
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, whether or not a column is autoincrementing is a property of the data type. If this property is read/write, DBPROP_COL_AUTOINCREMENT can be specified for the column, provided the AUTO_UNIQUE_VALUE property of the underlying data type 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 that it can be distinguished from an object of the same name. If the provider supports the PROVIDER_TYPES schema rowset, quoting should use the LITERAL_PREFIX and LITERAL_SUFFIX characters. For example, 'Salary' is a string, but Salary is an object, such as a column.

Specify any VARIANT type except VT_VARIANT (that is, do not nest a VARIANT within a VARIANT). The IDBProperties::GetPropertyInfo method will report the type of this property as VT_VARIANT.

If a column data type is an OLE DB data type that cannot be used in a VARIANT, set the default to a VARIANT type for which OLE DB defines a conversion (that is, a coercion) and which the provider also supports.


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

Indicates whether a column is fixed-length or variable-length.

Value | Meaning
VARIANT_TRUE | The column is fixed-length, and ulColumnSize in the DBCOLUMNDESC structure contains the fixed-length value.
VARIANT_FALSE or unspecified | The column is variable-length and ulColumnSize represents the maximum size of the column.

Note This property does not apply to and should be ignored for types other than DBTYPE_STR, DBYTPE_WSTR, and DBTYPE_BYTES. For information about the DBCOLUMNDESC structure, see ITableDefinition::CreateTable.


DBPROP_COL_INCREMENT

Property group: Column

Property set: DBPROPSET_COLUMN

Type: Any

Typical R/W: R/W

Description: Increment

A VARIANT specifying the increment value for a numeric column.

Caution Consumers should specify a VARIANT subtype that corresponds to the type of the column; otherwise the provider may not convert the increment values correctly.

Specify any VARIANT type except VT_VARIANT (that is, do not nest a VARIANT within a VARIANT). The IDBProperties::GetPropertyInfo method will report the type of this property as VT_VARIANT.


DBPROP_COL_ISLONG

Property group: Column

Property set: DBPROPSET_COLUMN

Type: VT_BOOL

Typical R/W: R/W

Description: Is Long

Indicates whether a column can contain a long value.

Value | Meaning
VARIANT_TRUE The column can contain a long value, and pwszTypeName in the DBCOLUMNDESC structure is not required to be specified.

If pwszTypeName is not specified, the provider determines the best underlying type as follows:

  • If wType is a fixed length type, creates a column using the best match provider type for that DBTYPE.
  • If wType is a variable length type, the provider looks at the ulColumnSize, bPrecision, and bScale—as well as DBPROP_COL_FIXEDLENGTH, DBPROP_COL_ISLONG, and any other column properties—to determine the best-fit provider type.

Note Some 2.0 providers may not support leaving pwszTypeName unspecified. Also, to ensure a specific type, the consumer must specify that type explicitly. (This is true for all providers.)

If pwszTypeName is specified, the provider uses wType, ulColumnSize, bPrecision, and bScale—as well as DBPROP_COL_FIXEDLENGTH, DBPROP_COL_ISLONG, and any other column properties—to verify that the selected pwszTypeName matches the specified values.
VARIANT_FALSE | The column cannot contain a long value.


DBPROP_COL_NULLABLE

Property group: Column

Property set: DBPROPSET_COLUMN

Type: VT_BOOL

Typical R/W: R/W

Description: Nullable

Indicates whether or not the consumer can set the column to NULL.

Value | Meaning
VARIANT_TRUE | The consumer can set the column to NULL.
VARIANT_FALSE | The consumer cannot set the column to NULL.


DBPROP_COL_PRIMARYKEY

Property group: Column

Property set: DBPROPSET_COLUMN

Type: VT_BOOL

Typical R/W: R/W

Description: Primary Key

Indicates whether the column is part of the primary key.

Value | Meaning
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_SEED

Property group: Column

Property set: DBPROPSET_COLUMN

Type: Any

Typical R/W: R/W

Description: Seed

A VARIANT specifying the seed value for a numeric column.

Caution Consumers should specify a VARIANT subtype that corresponds to the type of the column; otherwise the provider may not convert the seed values correctly.

Specify any VARIANT type except VT_VARIANT (that is, do not nest a VARIANT within a VARIANT). The IDBProperties::GetPropertyInfo method will report the type of this property as VT_VARIANT.


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. The following flag can be set:

DBPROPVAL_CD_NOTNULL—Columns can be created non-nullable. For providers that support the SQL CREATE TABLE statement, this implies that the NOT NULL clause is supported in general. Individual provider data types may or may not support non-nullable behavior, and the consumer should consult the PROVIDER_TYPES schema rowset for more information.


DBPROP_COLUMNLCID

Property group: Column

Property set: DBPROPSET_COLUMN

Type: VT_I4

Typical R/W: R

Description: Column LCID

Indicates 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

Value | Meaning
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, 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; 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), the rowset should not count or return any rows that would have this condition. For more information, see DBPROP_ROWRESTRICT.


DBPROP_COL_UNIQUE

Property group: Column

Property set: DBPROPSET_COLUMN

Type: VT_BOOL

Typical R/W: R/W

Description: Unique

Indicates whether values of the column must be unique within the table.

Value | Meaning
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_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 time-out.

Note This property can apply to any operations implemented by the provider using commands to the data store.


DBPROP_COMMITPRESERVE

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: Preserve on Commit

Value | Meaning
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.

Note DBPROP_COMMITPRESERVE applies only to local transactions.


DBPROP_COMSERVICES

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: COM Service Support

A bitmask specifying the provider's support for COM services. The following flag can be set:

DBPROPVAL_CM_TRANSACTIONS—The provider enlists in COM+ transactions directly. The provider either does not implement ITransactionLocal or returns XACT_E_XTIONEXISTS when ITransactionLocal::StartTransaction is called. The consumer can use this value to determine whether to use OLE DB or COM+ methods to commit or abort a transaction.


DBPROP_CONCATNULLBEHAVIOR

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: NULL Concatenation Behavior

Indicates how the data source object 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

Indicates the status of the current connection. One of the following:

  • DBPROPVAL_CS_INITIALIZED—The data source object is in an initialized state and able to communicate with the data store.
  • DBPROPVAL_CS_COMMUNICATIONFAILURE—The data source object is unable to communicate with the data store.

Some providers may have to silently spawn multiple connections to the database to support multiple concurrent command, session, and rowset objects, according to the value of DBPROP_MULTIPLECONNECTIONS. For such providers, DBPROP_CONNECTIONSTATUS refers to the status of the primary connection, which was created when the user called IDBInitialize::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.

Note Some providers support changing the current catalog (database) on a connection through provider-specific mechanisms, such as executing SQL SET statements. However, to ensure interoperability and to prevent inconsistent provider states, consumers should instead change the current catalog by setting DBPROP_CURRENTCATALOG rather than by executing provider-specific statements.


DBPROP_DATASOURCENAME

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_BSTR

Typical R/W: R

Description: Data Source Name

Indicates the name of the data source object. 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

Value | Meaning
VARIANT_TRUE | The data store is read-only.
VARIANT_FALSE | The data store is updatable. Note Depending on permissions and metadata, updates can occur through IRowsetChange or command execution.

Note Read-only providers are required to support this property.


DBPROP_DATASOURCE_TYPE

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Data Source Type

Following are returned values of this property and resulting behaviors:

  • DBPROPVAL_DST_TDP—The provider is a tabular data provider.
  • DBPROPVAL_DST_MDP—The provider is a multidimensional provider.
  • DBPROPVAL_DST_TDPANDMDP—The provider is both a TDP and an MDP.
  • DBPROPVAL_DST_DOCSOURCE—The provider supports direct URL binding and is a document source provider. A consumer can expect a document source provider to exhibit the following behavior:
    • When binding to a collection and requesting a rowset, the resource rowset defines the shape of that rowset.
    • When creating a new row via ICreateRow::CreateRow, the container class is defined to be a folder and the default columns of the new row are the columns of the resource rowset.

The columns of the resource rowset are defined in the following table. Distributed Authoring and Versioning (DAV) is a protocol enhancement to HyperText Transfer Protocol (HTTP). For more information about Microsoft's implementation of DAV for resource rowsets, see the DAV namespace discussion in the Exchange Platinum section of the Platform SDK.

Column name | Column ID | Type indicator | Description | DAV property
RESOURCE_PARSENAME | DBROWCOL_PARSENAME | DBTYPE_WSTR | Read-only. URL of the resource. This name is usually a relative URL, although it can be an absolute URL. If the resource is the root of a tree, its parse name is defined to be an empty string. | DAV:lastpathsegment
RESOURCE_PARENTNAME | DBROWCOL_PARENTNAME | DBTYPE_WSTR | Read-only. URL of the parent resource. The string always contains an absolute URL. If the resource is the root of a tree, its parent name is defined to be an empty string. | DAV:parentname
RESOURCE_ABSOLUTEPARSENAME | DBROWCOL_ABSOLUTEPARSENAME | DBTYPE_WSTR | Read-only URL of the resource. This name is always an absolute URL and for nonroot resources is the concatenation of its parent name and its parse name. | DAV:href
RESOURCE_ISHIDDEN | DBROWCOL_ISHIDDEN | DBTYPE_BOOL | VARIANT_TRUE: The resource is hidden. Therefore, no row for it will be returned in rowsets unless the command that created the rowset explicitly selected rows where RESOURCE_ISHIDDEN is VARIANT_TRUE. | DAV:ishidden
RESOURCE_ISREADONLY | DBROWCOL_ISREADONLY | DBTYPE_BOOL | VARIANT_TRUE: The resource is read-only. Attempts to open this resource with DBBINDURLFLAG_WRITE return DB_E_READONLY. This property can be edited even when the resource has been opened only for reading. | DAV:isreadonly
RESOURCE_CONTENTTYPE | DBROWCOL_CONTENTTYPE | DBTYPE_WSTR | The MIME type of the document; for example, "text/html". | DAV:getcontenttype
RESOURCE_CONTENTCLASS | DBROWCOL_CONTENTCLASS | DBTYPE_WSTR | The likely use of the document. This might correspond to the Microsoft® Office template used to create the document. | DAV:contentclass
RESOURCE_CONTENTLANGUAGE | DBROWCOL_CONTENTLANGUAGE | DBTYPE_WSTR | The language in which the content is written; for example, "U.S. English". | DAV:getcontentlanguage
RESOURCE_CREATIONTIME | DBROWCOL_CREATIONTIME | DBTYPE_FILETIME | Read-only. Specifies a FILETIME structure containing the time the resource was created. The time is reported in Coordinated Universal Time (UTC) format. | DAV:creationdate
RESOURCE_LASTACCESSTIME | DBROWCOL_LASTACCESSTIME | DBTYPE_FILETIME | Read-only. Specifies a FILETIME structure containing the time that the resource was last accessed. The time is in UTC format. The FILETIME members are zero if the provider does not support this time member. | DAV:lastaccessed
RESOURCE_LASTWRITETIME | DBROWCOL_LASTWRITETIME | DBTYPE_FILETIME | Read-only. Time when the resource was last modified. The time is stored in a FILETIME structure in UTC format. If the provider does not support the time member, the remaining FILETIME members are set to zero. | DAV:getlastmodified
RESOURCE_STREAMSIZE | DBROWCOL_STREAMSIZE | DBTYPE_UI8 | Read-only. Size of the resource's default stream, in bytes. | DAV:getcontentlength
RESOURCE_ISCOLLECTION | DBROWCOL_ISCOLLECTION | DBTYPE_BOOL | Read-only. VARIANT_TRUE: The resource is a collection. VARIANT_FALSE: The resource is atomic. | DAV:iscollection
RESOURCE_ISSTRUCTUREDDOCUMENT | DBROWCOL_ISSTRUCTUREDDOCUMENT | DBTYPE_BOOL | VARIANT_TRUE: The resource is a structured document. VARIANT_FALSE: The resource is not a structured document. It could be a collection or an atomic resource. | DAV:isstructureddocument
DEFAULT_DOCUMENT | DBROWCOL_DEFAULTDOCUMENT | DBTYPE_WSTR | Read-only. This resource contains a URL to the default simple document of a folder or a structured document. This is used when the default IStream is requested from a resource. This property is blank for atomic resources. | DAV:defaultdocument
CHAPTERED_CHILDREN | CHAPTERED_CHILDREN | OLE DB Chapter | Optional. Read-only. Specifies the chapter of the rowset containing the children of the resource. |
RESOURCE_DISPLAYNAME | DBROWCOL_DISPLAYNAME | DBTYPE_WSTR | Read-only. Display name of the resource. | DAV:displayname
RESOURCE_ISROOT | DBROWCOL_ISROOT | DBTYPE_BOOL | Read-only. VARIANT_TRUE: The resource is a root of a collection. Can be used with RESOURCE_ISCOLLECTION. | DAV:isroot


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

Indicates 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

Value | Meaning
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.

Note 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, storage objects are also used in delayed update mode. In particular:

Changes to the object are not transmitted to the data source object until IRowsetUpdate::Update is called.

  • IRowsetUpdate::Undo undoes any pending changes.
  • 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, storage objects are used in immediate update mode. In particular:

  • Changes to the object are immediately transmitted to the data source object.
  • IRowsetUpdate::Update has no effect on the object.
  • IRowsetUpdate::Undo does not undo changes made to the object since the row was last fetched or updated.
  • IRowsetUpdate::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 In the case where additional threading limitations are imposed by underlying components, the threading model returned in this property might be stricter than the thread model registered under the provider's CLSID.

OLE DB 1.x providers may set only one bit of this property, commonly to the highest level of threading supported.


DBPROP_FILTERCOMPAREOPS

Property group: View

Property set: DBPROPSET_VIEW

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, the full set of comparison operators that may be supported is as follows:

  • 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 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.

Note 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, the full set of comparison operators that may be supported is as follows:

  • 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 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.

Note 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_GENERATEURL

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: URL Generation

A bitmask indicating whether the provider requires data store–generated URLs for row creation and scoped operations.

  • DBPROPVAL_GU_NOTSUPPORTED—Consumers must specify full URLs of created rows or destinations for copy and move operations.
  • DBPROPVAL_GU_SUFFIX—The provider generates the URL suffix. Consumers must specify a path.


DBPROP_GROUPBY

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: GROUP BY Support

Indicates 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 object–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_HIDDENCOLUMNS

Property group: Rowset

Property set: DBPROPSET_ROWSET

Type: VT_I4

Typical R/W: R

Description: Hidden Column Count

If DBPROP_UNIQUEROWS is VARIANT_TRUE, the DBPROP_HIDDENCOLUMNS property returns the number of additional "hidden" columns added by the provider to uniquely identify rows within the rowset. These columns are returned by IColumnsInfo::GetColumnInfo and IColumnsRowset::GetColumnsRowset. However, they are not included in the count of rows returned by the pcColumns argument returned by IColumnsInfo::GetColumnInfo.

To determine the total number of columns represented in the prgInfo structure returned by IColumnsInfo::GetColumnInfo, including hidden columns, the consumer adds the value of DBPROP_HIDDENCOLUMNS to the count of columns returned from IColumnsInfo::GetColumnInfo in pcColumns. If DBPROP_UNIQUEROWS is VARIANT_FALSE, DBPROP_HIDDENCOLUMNS is zero.


DBPROP_IAccessor

Property group: Rowset, View

Property set: DBPROPSET_ROWSET, DBPROPSET_VIEW

Column? N

Type: VT_BOOL

Typical R/W: R

Description: IAccessor

The value of this property is read-only and is always set to VARIANT_TRUE, indicating that the rowset supports the specified interface. The value of this property cannot be set to VARIANT_FALSE. (This is also true of DBPROP_IColumnsInfo, DBPROP_IConvertType, DBPROP_IRowset, and DBPROP_IRowsetInfo.) If the consumer does not set the value of this property to true, the resulting rowset supports IAccessor.

Note 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.


DBPROP_IBindResource

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IBindResource

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IChapteredRowset

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IChapteredRowset

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IColumnsInfo

Property group: Rowset, View

Property set: DBPROPSET_ROWSET, DBPROPSET_VIEW

Column? N

Type: VT_BOOL

Typical R/W: R

Description: IColumnsInfo

The value of this property is read-only and is always set to VARIANT_TRUE, indicating that the rowset supports the specified interface. The value of this property cannot be set to VARIANT_FALSE. (This is also true of DBPROP_IAccessor, DBPROP_IConvertType, DBPROP_IRowset, and DBPROP_IRowsetInfo.) If the consumer does not set the value of this property to true, the resulting rowset supports IColumnsInfo.

Note 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.


DBPROP_IColumnsInfo2

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IColumnsInfo2

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IColumnsRowset

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IColumnsRowset

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IConnectionPointContainer

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IConnectionPointContainer

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IConvertType

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R

Description: IConvertType

The value of this property is read-only and is always set to VARIANT_TRUE, indicating that the rowset supports the specified interface. The value of this property cannot be set to VARIANT_FALSE. (This is also true of DBPROP_IAccessor, DBPROP_IColumnsInfo, DBPROP_IRowset, and DBPROP_IRowsetInfo.) If the consumer does not set the value of this property to true, the resulting rowset supports IConvertType.

Note 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.


DBPROP_ICreateRow

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: ICreateRow

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IDBAsynchStatus

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IDBAsynchStatus

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IDENTIFIERCASE

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Identifier Case Sensitivity

Indicates how identifiers treat case in data definition commands or interfaces (such as IIndexDefinition, IAlterTable, IAlterIndex, ITableCreation, ITableDefinition, and ITableDefinitionWithConstraints). For SQL providers, this refers to SQL data definition commands and storage in system catalogs. One of the following:

  • DBPROPVAL_IC_UPPER—Identifiers in SQL are case-insensitive and are stored in uppercase.
  • DBPROPVAL_IC_LOWER—Identifiers in SQL are case-insensitive and are stored in lowercase.
  • DBPROPVAL_IC_SENSITIVE—Identifiers in SQL are case-sensitive and are stored in mixed case.
  • DBPROPVAL_IC_MIXED—Identifiers in SQL are case-insensitive and are stored in mixed case.


DBPROP_IGetRow

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IGetRow

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IGetSession

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IGetSession

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IGetSourceRow

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IGetSourceRow

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_ILockBytes

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? Y

Type: VT_BOOL

Typical R/W: R/W

Description: ILockBytes

If the value of this property is set to VARIANT_TRUE, 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.

Whether or not 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

Value | Meaning
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, 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, inserted rows are not guaranteed to appear in a determinate position and the position of updated rows is not changed.

Note This property is meaningful only if DBPROP_OWNINSERT is VARIANT_TRUE.


DBPROP_IMultipleResults

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IMultipleResults

Value | Meaning
VARIANT_TRUE | Executing the command or opening the rowset returns a multiple results object (supporting IMultipleResults) in place of the rowset. This multiple results object can be used to obtain a series of results generated from a single method call.
VARIANT_FALSE | Executing the command or opening the rowset returns a rowset object. If a series of results are generated from the method call, only the first such result is available.


DBPROP_INDEX_AUTOUPDATE

Property group: Index

Property set: DBPROPSET_INDEX

Type: VT_BOOL

Typical R/W: R/W

Description: Auto-Update

Indicates whether the index is maintained automatically when changes are made to the corresponding base table.

Value | Meaning
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.

Note 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

Indicates whether an index is clustered.

Value | Meaning
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

Indicates 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

Indicates 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

Indicates 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, 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, 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 multicolumn search key, if the consumer inserts an index entry with NULL value in some column of the search key, the provider ignores that entry and no error code is returned.
  • DBPROPVAL_IN_ALLOWNULL—The index allows entries where the key columns are NULL and sorts according to the collation described by DBPROP_INDEX_NULLCOLLATION.


DBPROP_INDEX_PRIMARYKEY

Property group: Index

Property set: DBPROPSET_INDEX

Type: VT_BOOL

Typical R/W: R/W

Description: Primary Key

Indicates whether the index represents the primary key on the table.

Value | Meaning
VARIANT_TRUE | The index represents the primary key on the table.
VARIANT_FALSE | The index does not represent 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

Indicates how the index treats repeated keys.

Value | Meaning
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

Indicates whether the index is temporary.

Value | Meaning
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

Indicates 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

Indicates whether index keys must be unique.

Value | Meaning
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 object. The following flag can be set:

DBPROPVAL_ASYNCH_INITIALIZE: IDBInitialize::Initialize returns immediately, but the actual initialization of the data source object is done asynchronously. The data source object behaves as an uninitialized data source object 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 object is completely initialized.


DBPROP_INIT_BINDFLAGS

Property group: Initialization

Property set: DBPROPSET_DBINIT

Type: VT_I4

Typical R/W: R/W

Description: Bind Flags

A bitmask specifying flags that modify the behavior of the URL binding operation. A combination of zero or more of the following:

  • DB_BINDFLAGS_COLLECTION—Creates the object as a collection.
  • DB_BINDFLAGS_DELAYFETCHCOLUMNS—On a bind to a row object, this flag overrides the consumer's intent to immediately access the row's columns. Absence of this flag is a hint to the provider to download or prefetch the row's columns.
  • DB_BINDFLAGS_DELAYFETCHSTREAM—On a bind to a row object, this flag overrides the consumer's intent to immediately open the default stream. Absence of this flag is a hint to the provider to instantiate the default stream object and prefetch its contents.
  • DB_BINDFLAGS_ISSTRUCTUREDDOCUMENT—Creates the object as a structured document.
  • DB_BINDFLAGS_OPENIFEXISTS—If the resource exists and is not a collection, it is opened. If the resource does not exist, it is created.
  • DB_BINDFLAGS_OUTPUT—Bind to the resource's executed output rather than its source. (For example, this will retrieve the HTML generated by an ASP file rather than its source.) This flag is ignored on binds to collections.
  • DB_BINDFLAGS_OVERWRITE—Deletes and re-creates a named object if it exists. This flag may not be used with DBBINDURLFLAG_OPENIFEXISTS.
  • DB_BINDFLAGS_RECURSIVE—Modifies DBBINDURLFLAG_SHARE_*. Propagates sharing restrictions to all objects in the subtree. Has no effect when binding to leaf nodes. Specifying this flag with only DBBINDURLFLAG_SHARE_DENY_NONE (or equivalently, with no SHARE_DENY flags) will result in E_INVALIDARG.

For more information, see the reference entries for IBindResource::Bind and ICreateRow::CreateRow.


DBPROP_INIT_CATALOG

Property group: Initialization

Property set: DBPROPSET_DBINIT

Type: VT_BSTR

Typical R/W: R/W

Description: Initial Catalog

Indicates the name of the initial (or default) catalog to use when connecting to the data source object. If the provider supports changing the catalog for an initialized object, the consumer can specify a different catalog name through the DBPROP_CURRENTCATALOG 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

Indicates the name of the database or enumerator to connect to. DBPROP_INIT_DATASOURCE is used to identify the data source object to connect to—for example, a relational database server or a local file. If the provider uses two-part naming to identify the data source object, the data source object name is qualified with the location specified in DBPROP_INIT_LOCATION.


DBPROP_INIT_GENERALTIMEOUT

Property group: Initialization

Property set: DBPROPSET_DBINIT

Type: VT_I4

Typical R/W: R/W

Description: General Timeout

Indicates the number of seconds before a request, other than data source initialization and command execution, times out. A value of 0 indicates an infinite time-out. Providers that work over network connections or in distributed or transacted scenarios can support this property to advise an enlisted component to time-out in the event of a long-running request. Time-outs for data source initialization and command execution remain governed by DBPROP_INIT_TIMEOUT and DBPROP_COMMANDTIMEOUT, respectively.


DBPROP_INIT_HWND

Property group: Initialization

Property set: DBPROPSET_DBINIT

Type: VT_I4

Typical R/W: R/W

Description: Window Handle

Indicates 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

Indicates 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. 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

Indicates 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

Indicates the location of the data source object 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 object specified in the DBPROP_INIT_DATASOURCE property. For example, if the data source object is defined on a different machine, this might be the machine name on which to look for the data source object definition. This is typically not used if the provider can identify the data source object by using a single name, such as the name of an RDBMS server, that the consumer can use to identify the data source object directly.


DBPROP_INIT_LOCKOWNER

Property group: Initialization

Property set: DBPROPSET_DBINIT

Type: VT_BSTR

Typical R/W: R/W

Description: Lock Owner

Indicates the string that the client wants displayed as its identifier when other clients attempt to access this resource. This property is ignored unless DBBINDURLFLAG_SHARE_* is specified. If DBPROP_INIT_LOCKOWNER is not specified, is NULL, or is empty, the provider should use the currently logged-in user name as the lock owner string.

For more information, see the reference entries for IBindResource::Bind and ICreateRow::CreateRow.


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 or disable. To use this property, the provider must support service components and must have been invoked with IDataInitialize on the OLE DB Initialization core service. For more information about OLE DB services, see "OLE DB Services." This property overrides the settings of the OLEDB_SERVICES registration key. For more information about these settings, see "Overriding Provider Service Defaults" and related topics. A combination of zero or more of the following:

  • DBPROPVAL_OS_RESOURCEPOOLING—Resources should be pooled.
  • DBPROPVAL_OS_TXNENLISTMENT—Sessions in a Component Services (or MTS, if you are using Microsoft® Windows NT®) environment should automatically be enlisted in a global transaction where required.
  • 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-AND of DBPROPVAL_OS_ENABLEALL along with the bitwise complement of any services to be deselected. For example, DBPROPVAL_OS_ENABLEALL &~DBPROPVAL_OS_TXNENLISTMENT enables all services except automatic transaction enlistment in the Component Services environment.
  • DBPROPVAL_OS_DISABLEALL—All services should be disabled.
  • DBPROPVAL_OS_AGR_AFTERSESSION—Indicates support for services operating beyond the session level, such as the Client Cursor Engine. For maximum performance, consumers should not set this bit if such services are not required. The setting for DBPROPVAL_OS_AGR_AFTERSESSION will be ignored if contraindicated by another bit setting. For example, if DBPROPVAL_OS_CLIENTCURSOR is set, the value for DBPROPVAL_OS_AGR_AFTERSESSION is ignored.
  • DBPROPVAL_OS_CLIENTCURSOR—Enables the Client Cursor Engine as needed, to support rowset behavior requested by the consumer and not implemented by the native provider. When this bit is set, the value for DBPROPVAL_OS_AGR_AFTERSESSION is ignored.


DBPROP_INIT_PROMPT

Property group: Initialization

Property set: DBPROPSET_DBINIT

Type: VT_I2

Typical R/W: R/W

Description: Prompt

Indicates 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.

Providers that do not wish to build their own prompting user interface can use IDBPromptInitialize::PromptDataSource, passing in their own data source object as the value of the ppDataSource argument. Such providers are still required to support DBPROP_INIT_PROMPT but can leverage the user interface provided by OLE DB services.


DBPROP_INIT_PROTECTION_LEVEL

Property group: Initialization

Property set: DBPROPSET_DBINIT

Type: VT_I4

Typical R/W: R/W

Description: Protection Level

Indicates 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. 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.

Note If the provider supports setting OLE DB properties via the provider string, and if an OLE DB property is set both via its own property ID and within the Extended Properties provider string, whichever setting takes effect is provider-specific.


DBPROP_INIT_TIMEOUT

Property group: Initialization

Property set: DBPROPSET_DBINIT

Type: VT_I4

Typical R/W: R/W

Description: Connect Timeout

Indicates 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—for example, connection across a network—are strongly encouraged to support this property.


DBPROP_IParentRowset

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IParentRowset

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IRow

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IRow

Value | Meaning
VARIANT_TRUE | The provider creates a row object instead of a rowset object when opening a rowset or executing a command.
VARIANT_FALSE (default value) | The provider creates a rowset object when opening a rowset or executing a command.

The consumer is requesting a row object instead of a rowset. If the provider has more than one row satisfying the request, the provider is encouraged to return DB_S_NOTSINGLETON but may return S_OK.


DBPROP_IRowChange

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IRowChange

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IRowSchemaChange

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IRowSchemaChange

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IRowset

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R

Description: IRowset

The value of this property is read-only and is always set to VARIANT_TRUE, indicating that the rowset supports the specified interface. The value of this property cannot be set to VARIANT_FALSE. (This is also true of DBPROP_IAccessor, DBPROP_IColumnsInfo, DBPROP_IConvertType, and DBPROP_IRowsetInfo.) If the consumer does not set the value of this property to true, the resulting rowset supports IRowset.

Note 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.


DBPROP_IRowsetBookmark

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IRowsetBookmark

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface. This setting implicitly causes the created rowset to support bookmarks, and IRowsetInfo::GetProperties will return VARIANT_TRUE for the property DBPROP_BOOKMARKS.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IRowsetChange

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IRowsetChange

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Comments

Setting DBPROP_IRowsetUpdate to VARIANT_TRUE automatically sets DBPROP_IRowsetChange to VARIANT_TRUE.

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.

When DBPROP_IRowsetChange is set to VARIANT_FALSE, DBPROP_UPDATABILITY, if supported by the rowset, will be 0. Any attempt to set DBPROP_UPDATABILITY to a value other than 0 will result in a conflict reported by the provider, which will return DBPROPSTATUS_CONFLICTING.


DBPROP_IRowsetCurrentIndex

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IRowsetCurrentIndex

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IRowsetFind

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IRowsetFind

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IRowsetIdentity

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IRowsetIdentity

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IRowsetIndex

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IRowsetIndex

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IRowsetInfo

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R

Description: IRowsetInfo

The value of this property is read-only and is always set to VARIANT_TRUE, indicating that the rowset supports the specified interface. The value of this property cannot be set to VARIANT_FALSE. (This is also true of DBPROP_IAccessor, DBPROP_IColumnsInfo, DBPROP_IConvertType, and DBPROP_IRowset.) If the consumer does not set the value of this property to true, the resulting rowset supports IRowsetInfo.

Note 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.


DBPROP_IRowsetLocate

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IRowsetLocate

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface. This setting implicitly causes the created rowset to support bookmarks, and IRowsetInfo::GetProperties will return VARIANT_TRUE for the property DBPROP_BOOKMARKS.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IRowsetRefresh

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IRowsetRefresh

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IRowsetScroll

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IRowsetScroll

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IRowsetUpdate

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IRowsetUpdate

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface; automatically sets DBPROP_IRowsetChange to VARIANT_TRUE.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IRowsetView

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IRowsetView

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IScopedOperations

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IScopedOperations

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_ISequentialStream

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? Y

Type: VT_BOOL

Typical R/W: R/W

Description: ISequentialStream

If the value of this property is set to VARIANT_TRUE, 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.

Whether or not 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_IStorage

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? Y

Type: VT_BOOL

Typical R/W: R/W

Description: IStorage

If the value of this property is set to VARIANT_TRUE, 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.

Whether or not 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_IStream

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? Y

Type: VT_BOOL

Typical R/W: R/W

Description: IStream

If the value of this property is set to VARIANT_TRUE, 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.

Whether or not 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_ISupportErrorInfo

Property group: Rowset, View

Property set: DBPROPSET_ROWSET, DBPROPSET_VIEW

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: ISupportErrorInfo

Value | Meaning
VARIANT_TRUE | The rowset supports the specified interface.
VARIANT_FALSE | The rowset does not support the specified interface.

Note 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.


DBPROP_IViewChapter

Property group: View

Property set: DBPROPSET_VIEW

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IViewChapter

For all methods returning a Rowset or View object, in addition to Rowset properties, DBPROPSET_VIEW should be set before creating a view.


DBPROP_IViewFilter

Property group: View

Property set: DBPROPSET_VIEW

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IViewFilter

For all methods returning a Rowset or View object, in addition to Rowset properties, DBPROPSET_VIEW should be set before creating a view. Providers that support this interface (and thereby support view filters) must support setting this property to true.


DBPROP_IViewRowset

Property group: View

Property set: DBPROPSET_VIEW

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IViewRowset

For all methods returning a Rowset or View object, in addition to Rowset properties, DBPROPSET_VIEW should be set before creating a view.


DBPROP_IViewSort

Property group: View

Property set: DBPROPSET_VIEW

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: IViewSort

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

Value | Meaning
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 of their scalar types yields the same result as a call to IRowsetLocate::Compare.
VARIANT_FALSE | Bookmarks can be compared only with IRowsetLocate::Compare.

Note Setting the value of this property to VARIANT_TRUE automatically sets the value of DBPROP_BOOKMARKS to VARIANT_TRUE.


DBPROP_LITERALIDENTITY

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R

Description: Literal Row Identity

Value | Meaning
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. If DBPROP_LITERALIDENTITY is set to VARIANT_FALSE, multiple different concurrently held row handles can represent the same row in the underlying data store. To the rowset, these generally appear as separate rows. Therefore, a change made (either in immediate mode or via IRowsetUpdate) to a retrieved column value (for example, a nondeferred column or a deferred column with DBPROP_CACHEDEFERRED set to VARIANT_TRUE) is not reflected when retrieving the row through a second row handle. Calling IRowsetRefresh::RefreshVisibleData, IRowsetUpdate::Undo, or IRowsetUpdate::Update affects only changes made through the specified row handle.

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: Rowsets.


DBPROP_LOCKMODE

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_I4

Typical R/W: R/W

Description: Lock Mode

Indicates the level of locking performed 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. Therefore, when using deferred update mode, IRowsetUpdate::Update will not fail due to a concurrency violation. This may mean that the provider takes a lock on the row when IRowsetChange::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 operations on the row, such as updates, will succeed. The implications of DBPROPVAL_LM_SINGLEROW, and DBPROP_LOCKMODE in general, are the same in both immediate and deferred update modes.

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 to ensure that updates succeed. The provider may use locking to enforce higher levels of isolation, in which case a higher level of locking may occur than is 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

Indicates 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

Indicates 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, this value is 1; if the provider has no limit on the number of open chapters or does not support chapters, this value is 0.


DBPROP_MAXOPENROWS

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_I4

Typical R/W: R/W

Description: Maximum Open Rows

Indicates 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: View

Property set: DBPROPSET_VIEW

Type: VT_I4

Typical R/W: R

Description: Maximum OR Conditions

Indicates the maximum number of disjoint 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. If there is no specified limit or the limit is unknown, the provider returns 0.

This value applies only to the conditions that can be joined in a logical OR in a call to IViewFilter::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

Indicates 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

Indicates 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, at initial rowset population the provider must ensure that the rowset never contains more than the specified number of rows. If the consumer attempts to fetch beyond DBPROP_MAXROWS number of rows in a rowset, the rowset behaves as if the table contained or the query returned only DBPROP_MAXROWS, and the provider returns DB_S_ENDOFROWSET. Pending deletes do not count against the rowset limit set by DBPROP_MAXROWS. Providers are not required to check DBPROP_MAXROWS when inserting or deleting rows.


DBPROP_MAXROWSIZE

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Maximum Row Size

Indicates 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

Value | Meaning
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: View

Property set: DBPROPSET_VIEW

Type: VT_I4

Typical R/W: R

Description: Maximum Sort Columns

Indicates 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 IViewSort::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

Indicates 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

Indicates whether or not a particular column is writable. 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, 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 command, session, and rowset objects. 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 command, session, and rowset objects without spawning multiple connections do not support this property.

Note If the provider supports this property, the provider must support the value VARIANT_TRUE and it must be the default. If the provider does not support this property, the provider must support creating multiple connections.

Value | Meaning
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

Value | Meaning
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: Commands.


DBPROP_MULTIPLESTORAGEOBJECTS

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_BOOL

Typical R/W: R

Description: Multiple Storage Objects

Value | Meaning
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, whether or not 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

Value | Meaning
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 and 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

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_I4

Typical R/W: R

Description: Column Set 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_NOTIFYROWDELETE

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_I4

Typical R/W: R

Description: Row Delete 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_NOTIFYROWFIRSTCHANGE

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_I4

Typical R/W: R

Description: Row First Change 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_NOTIFYROWINSERT

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_I4

Typical R/W: R

Description: Row Insert 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_NOTIFYROWRESYNCH

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_I4

Typical R/W: R

Description: Row Resynchronization 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_NOTIFYROWSETCHANGED

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_I4

Typical R/W: R

Description: Rowset Change Notification

A bitmask specifying whether the notification phase of reason DBREASON_ROWSET_CHANGE is cancelable. A combination of zero or more of the following:

  • DBPROPVAL_NP_OKTODO
  • DBPROPVAL_NP_ABOUTTODO
  • DBPROPVAL_NP_SYNCHAFTER


DBPROP_NOTIFYROWSETRELEASE

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_I4

Typical R/W: R

Description: Rowset Release 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_NOTIFYROWSETFETCHPOSITIONCHANGE

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_I4

Typical R/W: R

Description: Rowset Fetch Position Change 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_NOTIFYROWUNDOCHANGE

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_I4

Typical R/W: R

Description: Row Undo Change 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_NOTIFYROWUNDODELETE

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_I4

Typical R/W: R

Description: Row Undo Delete 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_NOTIFYROWUNDOINSERT

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_I4

Typical R/W: R

Description: Row Undo Insert 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_NOTIFYROWUPDATE

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_I4

Typical R/W: R

Description: 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

Indicates 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 COM 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_DIRECTBIND—The provider supports direct binding. If this bit is set, the IBindResource and ICreateRow interfaces are supported on the session object and the provider implements a provider binder object.
  • DBPROPVAL_OO_IPERSIST—The provider supports access to COM objects through IPersistStream, IPersistStreamInit, or IPersistStorage.
  • DBPROPVAL_OO_ROWOBJECT—The provider supports row objects. IGetRow is supported on rowsets. Row objects support the mandatory interfaces IRow, IGetSession, IColumnsInfo, and IConvertType. If the provider supports direct URL binding, it must support binding to row objects by passing DBGUID_ROW in IBindResource::Bind and, if supported, ICreateRow::CreateRow.
  • DBPROPVAL_OO_SCOPED—Indicates that row objects implement IScopedOperations.
  • DBPROPVAL_OO_SINGLETON—The provider supports singleton selects. The provider supports the return of row objects on ICommand::Execute and IOpenRowset::OpenRowset.


DBPROP_OPENROWSETSUPPORT

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Open Rowset Support

A bitmask describing support for opening objects through IOpenRowset.

  • DBPROPVAL_ORS_TABLE—The provider supports opening tables through IOpenRowset (true for all providers).
  • DBPROPVAL_ORS_INDEX—The provider supports specifying an index through IOpenRowset.
  • DBPROPVAL_ORS_INTEGRATEDINDEX—The provider supports specifying both a table and an index in the same call to IOpenRowset::OpenRowset in order to open the rowset using the specified index.
  • DBPROPVAL_ORS_STOREDPROC—The provider supports opening a rowset over stored procedures by specifying the stored procedure name in TableID. (For more information about TableID, see "Rowset Creation Example" in Chapter 4: Rowsets.)
  • DBPROPVAL_ORS_HISTOGRAM—The provider supports opening a histogram rowset through the IOpenRowset::OpenRowset method. Providers supporting histograms should set this bit, as well as DBPROPVAL_TS_HISTOGRAM, in the DBPROP_TABLESTATISTICS property.


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

Value | Meaning
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

Value | Meaning
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 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

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: Others' Inserts Visible

For more information about visibility of inserted rows and a description of how these properties relate to transaction isolation level and inserts, see "Visibility of Other Changes" in Chapter 5: Updating Data in Rowsets.

Value | Meaning
VARIANT_TRUE | Rows inserted by a consumer or process other than a consumer of the rowset are visible. That is, any consumer of the rowset will see those rows the next time it fetches a set of rows containing the changed rows, whatever process changed that row. This includes rows inserted in the same transaction as well as rows inserted outside the transaction by others. Note The transaction isolation level does not affect the visibility of rows inserted by others in the same transaction, such as other rowsets in the same session. However, it does restrict the visibility of rows inserted by others outside the transaction.
VARIANT_FALSE | Inserts to the rowset made by other consumers of the rowset are not visible unless the command is reexecuted.

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
    • 62DBPROP_OTHERINSERT = VARIANT_FALSE
    • DBPROP_OTHERUPDATEDELETE = VARIANT_TRUE
  • Dynamic cursor
    • DBPROP_OTHERINSERT = VARIANT_TRUE
    • DBPROP_OTHERUPDATEDELETE = VARIANT_TRUE


DBPROP_OTHERUPDATEDELETE

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: Others' Changes Visible

For information about the visibility of specific changes, see "Visibility of Changes" in Chapter 5: Updating Data in Rowsets.

For information about how this property relates to the cursor types in ODBC, see the DBPROP_OTHERINSERT property.

Value | Meaning
VARIANT_TRUE | Rows modified (updated or deleted) by a consumer or process other than a consumer of the rowset are visible. For example, suppose this other process or consumer 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 others in the same transaction as well as updates and deletes made by others outside the transaction. Note The transaction isolation level does not affect the visibility of rows inserted by others in the same transaction, such as other rowsets in the same session. However, it does restrict the visibility of rows inserted by others outside the transaction.
VARIANT_FALSE | Changes to the rowset (updates and deletes) made by other consumers of the rowset are not visible unless the command is reexecuted.


DBPROP_OUTPUTENCODING

Property group: Stream

Property set: DBPROPSET_STREAM

Column? N

Type: VT_BSTR

Typical R/W: R/W

Description: Output Encoding

Specifies the encoding to be used in the stream returned by ICommand::Execute.

  • The values for this property are provider-specific.
  • If the value of this property is NULL, the default encoding used in the stream is provider-specific.
  • The provider optionally can prefix the output stream with a header to identify the encoding used.


DBPROP_OUTPUTPARAMETERAVAILABILITY

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Output Parameter Availability

Indicates 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: Commands.


DBPROP_OUTPUTSTREAM

Property group: Stream

Property set: DBPROPSET_STREAM

Column? N

Type: VT_UNKNOWN

Typical R/W: R/W

Description: Output Stream

The value passed in this property is a Variant containing a pointer to either IStream or ISequentialStream. When this property is set, ICommand::Execute will return results in the stream specified by this property. Each successive execution of the Execute method will append its results to the specified stream.


DBPROP_OWNINSERT

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: Own Inserts Visible

For information about the visibility of specific changes, see "Visibility of Changes" in Chapter 5: Updating Data in Rowsets.

Note This property corresponds to the value returned by the SQL_STATIC_SENSITIVITY information type in SQLGetInfo in ODBC.

Value | Meaning
VARIANT_TRUE | The inserts to the rowset are visible. That is, if a consumer of a rowset inserts a row, that row will be visible to any consumer of the rowset the next time that consumer fetches a set of rows containing that row. This ability is independent of the transaction isolation level because all consumers of the rowset share the same transaction.
VARIANT_FALSE | Changes to the rowset (updates and deletes) made by consumers of the rowset are not visible unless the command is reexecuted.


DBPROP_OWNUPDATEDELETE

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: Own Changes Visible

For information about the visibility of specific changes, see "Visibility of Changes" in Chapter 5: Updating Data in Rowsets.

For information about how this relates to the SQL_STATIC_SENSITIVITY information type in ODBC, see the DBPROP_OTHERINSERT property.

Value | Meaning
VARIANT_TRUE | The updates and deletes made by the rowset consumer are visible. For example, suppose a consumer of the rowset updates or deletes a row. If the row is released completely, the update or delete will be visible to any consumer of the rowset 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 | Changes to the rowset (updates and deletes) made by consumers of the rowset are not visible unless the command is reexecuted.


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 that name entities in the data store, such as tables, indexes, columns, commands, or constraints. This is generally the type of DBID that the provider considers 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

Indicates 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 reexecute 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

Indicates 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 reexecute 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 store 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

Indicates the friendly name of the provider—for example, "Microsoft OLE DB Provider for ODBC Drivers".


DBPROP_PROVIDERMEMORY

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_BOOL

Typical R/W: R

Description: Provider Owned Memory

Indicates whether the provider supports provider-owned memory.


DBPROP_PROVIDERFILENAME

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_BSTR

Typical R/W: R

Description: Provider Name

Indicates the file name of the provider—for example, "Myprvdr.dll". Prior to MDAC 2.5, this property was named DBPROP_PROVIDERNAME.


DBPROP_PROVIDEROLEDBVER

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_BSTR

Typical R/W: R

Description: OLE DB Version

Indicates 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 2.1 specification would return "02.10".


DBPROP_PROVIDERVER

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_BSTR

Typical R/W: R

Description: Provider Version

Indicates 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

Value | Meaning
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. Therefore, 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

Indicates 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

Value | Meaning
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, as 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

Value | Meaning
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.
VARIANT_FALSE The provider deletes the rows, but does not remove them from the rowset. If the user fetches a block of rows containing a deleted row, that row will have a handle that shows up in the rowset.

Any method that retrieves a row using the handle of a deleted row will return a code of DB_E_DELETEDROW.

The manner in which deleted rows are presented to the consumer is provider-specific. Some providers will not show the deleted rows, and some will show all rows.

If the provider does not support this property, the consumer cannot assume true or false. Providers are encouraged at minimum to support a default value for this property.

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

Value | Meaning
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

Indicates the data source object state should be reset. The following value can be set:

  • DBPROPVAL_RD_RESETALL—The provider should reset all initialization properties and other states associated with the data source and session objects to their values at initialization time, with the exception that any transactions, including distributed transactions, remain active. Any open rowsets or commands must be released by the consumer prior to setting this property, or the provider may return DBPROPSTATUS_NOTSETTABLE. After successfully setting DBPROP_RESETDATASOURCE to DBPROPVAL_RD_RESETALL, the data source object appears as it did when initialized, with the exception that any open session objects and their transaction states are maintained.

DBPROP_RESETDATASOURCE is generally used by a consumer to reuse an existing data source object without paying the cost of uninitializing and reinitializing the data source object.

If the data source object has a state that cannot be reset, the provider returns DBPROPSTATUS_NOTSET for this property, indicating that the data source object should not be reused. DBPROPSTATUS_NOTSET will also be returned if this property is set to DBPROPOPTIONS_REQUIRED because the state that cannot be reset takes precedence.

Other data source properties specified in the same call, regardless of their settings, are also reset.

The IDBProperties::GetProperties method always retrieves this property successfully and reports its value is zero.


DBPROP_RETURNPENDINGINSERTS

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R

Description: Return Pending Inserts

Value | Meaning
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 by 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

Value | Meaning
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, 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_ASYNCH_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_ASYNCH_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_ASYNCH_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_ASYNCH_SEQUENTIALPOPULATION and DBPROPVAL_ASYNCH_RANDOMPOPULATION bits to request that the rowset be asynchronously populated either sequentially or randomly. The consumer is prepared for asynchronous notifications in IRowsetNotify::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.

  • DBPROPVAL_ASYNCH_BACKGROUNDPOPULATION—The rowset is to be populated asynchronously in the background. The rowset supports IDBAsynchStatus 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 is 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.

Because 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 asynchronous population properties DBPROPVAL_ASYNCH_SEQUENTIALPOPULATION or DBPROPVAL_ASYNCH_RANDOMPOPULATION, 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 cannot be supported, the provider should fail if DBPROPOPTIONS_REQUIRED was specified for the property or should 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

Value | Meaning
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 object 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 (DML) 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 and may apply only to the table name, not the index name, depending on the SQL implementation.
  • 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

DBPROP_SERVERCURSOR works in conjunction with other cursor properties, including DBPROP_CLIENTCURSOR, to determine where a cursor, if required, is materialized.

  • If the value of this property is set to VARIANT_TRUE, the provider attempts to support any requested cursor functionality by materializing a cursor on the server. If DBPROPOPTIONS_REQUIRED is specified and materializing a cursor on the server cannot support any required cursor functionality, the provider returns an error and does not open the rowset. If DBPROPOPTIONS_OPTIONAL is specified, the provider attempts to support the requested cursor functionality at the server but may materialize a cursor on the client, if necessary, to support required properties. The provider never materializes a cursor on the client to support optional properties if DBPROP_SERVERCURSOR is set to VARIANT_TRUE.
  • If the value of this property is set to VARIANT_FALSE, the provider attempts to support any requested cursor functionality by materializing a cursor on the client. If DBPROPOPTIONS_REQUIRED is specified and materializing a cursor on the client cannot support any required cursor functionality, the provider returns an error and does not open the rowset. If DBPROPOPTIONS_OPTIONAL is specified, the provider attempts to support the requested functionality by materializing a cursor on the client but may materialize a cursor on the server, if necessary, to support required properties. The provider never materializes a cursor on the server to support optional properties if DBPROP_SERVERCURSOR is set to VARIANT_FALSE.

If DBPROPOPTIONS_REQUIRED is specified and this property is set to VARIANT_TRUE for a provider that does not support server-side cursors, or to VARIANT_FALSE for a provider that does not support client-side cursors, the provider returns an error. Otherwise, this property is ignored if no additional cursor properties are specified.

Local providers, for whom the data is always located on the same machine as the rowset consumer, may choose either of the following options:

  • Ignore this property and return DBPROPSTATUS_OK, since the server cursor and client cursor are the same thing.
  • Not support this property and return DBPROPSTATUS_NOTSUPPORTED.

Client/server providers, for whom the data resides on a different machine than the consumer, typically do not support client cursors. These providers generally return an error if DBPROP_SERVERCURSOR is set to VARIANT_FALSE with DBPROPOPTIONS_REQUIRED and if any other cursor properties are specified as required, or if any required properties are not supported by a cursor implemented on the server.

The following table documents the behavior for client/server providers when DBPROP_SERVERCURSOR is used in combination with any properties that request cursor functionality.

Cursor properties | _SERVERCURSOR | Cursor location (Success) | Cursor location (Failure)
true/required | true/required | Server | Error
true/required | true/optional | Server | Client or Error
true/required | false/required | Client | Error
true/required | false/optional | Client | Server or Error
true/optional | true/required | Server | Not satisfied
true/optional | true/optional | Server | Not satisfied
true/optional | false/required | Client | Not satisfied
true/optional | false/optional | Client | Not satisfied

When service components are invoked, they handle the invocation of the client-side cursor as follows:

  • If the consumer specifies a value of VARIANT_TRUE for DBPROP_SERVERCURSOR with DBPROPOPTIONS_REQUIRED, the service components pass the cursor properties through to the provider unchanged. If any required properties cannot be satisfied, the provider returns an error to the consumer.
  • If the consumer specifies a value of VARIANT_TRUE for DBPROP_SERVERCURSOR with DBPROPOPTIONS_OPTIONAL specified, the service components pass all cursor properties as DBPROPOPTIONS_OPTIONAL. If any required cursor properties could not be supported, the service components materialize a cursor on the client, populated by the provider's cursor; otherwise, they return the provider's rowset directly to the consumer.
  • If the consumer specifies a value of VARIANT_FALSE for DBPROP_SERVERCURSOR with DBPROPOPTIONS_REQUIRED specified, the service components determine whether or not the provider supports materializing a client cursor (by attempting to set the property on a command or by passing the specified properties unchanged to IOpenRowset::OpenRowset). If the provider cannot support the client cursor, the service components clear all cursor properties and request a forward-only rowset from the provider, which is then used to populate a client-side cursor.
  • If the consumer specifies a value of VARIANT_FALSE for DBPROP_SERVERCURSORS with DBPROPOPTIONS_OPTIONAL specified, the service components set the DBPROP_SERVERCURSORS property to DBPROPOPTIONS_REQUIRED and pass all cursor properties as DBPROPOPTIONS_OPTIONAL. If any required cursor properties could not be supported, the service components materialize a cursor on the client, populated by the provider's cursor.
  • If the consumer specifies a value of VARIANT_TRUE for DBPROP_CLIENTCURSOR, with DBPROPOPTIONS_REQUIRED specified, and uses ICommand::Execute to populate the rowset, the service components ignore any setting for DBPROP_SERVERCURSOR. The service components request a forward-only rowset from the provider, which is then used to populate a client-side cursor.


DBPROP_SERVERDATAONINSERT

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? N

Type: VT_BOOL

Typical R/W: R/W

Description: Server Data on Insert

Value | Meaning
VARIANT_TRUE | After an insert is transmitted to the server (when IRowsetChange::InsertRow is called in immediate mode or when IRowsetUpdate::Update is called for an inserted row in deferred update mode), the consumer can call IRowset::GetData to retrieve the actual values that appeared in the data store, including calculated columns and defaults not explicitly set in the call to IRowsetChange::InsertRow.
VARIANT_FALSE | The provider does not retrieve values from the data store for newly inserted rows. The consumer can retrieve only data values explicitly set in the call to IRowsetChange::InsertRow or by calls to IRowsetChange::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

Indicates 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 object that the user specifies when connecting, or may be the actual name of the server if the provider connects through "friendly" data source object 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_SKIPROWCOUNTRESULTS

Property group: Rowset

Property set: DBPROPSET_ROWSET

Column? No

Type: VT_BOOL

Typical R/W: R/W

Description: Skip Row Count Results

When set, requires a provider that supports multiple results to either return or skip control row count results on calls to IMultipleResults::GetResult. Providers that support multiple results but do not support this property return results for each statement.

Value | Meaning
VARIANT_TRUE | Calls to IMultipleResults::GetResult skip any row count results and return only rowsets or rows.
VARIANT_FALSE | Calls to IMultipleResults::GetResult return one result, which can be a row count, a rowset, or a row, for each statement in the command.


DBPROP_SORTONINDEX

Property group: View

Property set: DBPROPSET_VIEW

Type: VT_BOOL

Typical R/W: R

Description: Sort on Index

Value | Meaning
VARIANT_TRUE | The provider supports IViewSort::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 IViewSort::SetSortOrder, or the provider does not support IViewSort::SetSortOrder.

This value applies only to the ability to specify a column in IViewSort::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 SQL-92. 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 or the ANSI SQL-92 Entry level. This level is not cumulative; providers that support at least the minimal ODBC Level or ANSI SQL-92 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

Value | Meaning
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 object, as opposed to a pending insert row. For more information, see "Uniqueness of Rows in the Rowset" in Chapter 4: Rowsets.


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
  • DBPROPVAL_SQ_TABLE

The DBPROPVAL_SQ_CORRELATEDSUBQUERIES bit indicates that all predicates that support subqueries support correlated subqueries.

The DBPROPVAL_SQ_TABLE bit indicates that subqueries are supported in place of tables (for example, in the FROM clause of an SQL statement).


DBPROP_SUPPORTEDTXNDDL

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Transaction DDL

Indicates the relationship of transactions to table and index modification data definition language (DDL) statements. One of the following:

  • DBPROPVAL_TC_NONE—Transactions are not supported.
  • DBPROPVAL_TC_DML—Transactions can contain only data manipulation language (DML) statements. Attempting to modify tables or indexes within a transaction causes an error.
  • DBPROPVAL_TC_DDL_COMMIT—Transactions can contain only DML statements. Modifying tables or indexes within a transaction causes the transaction to be committed. The provider's commit mode remains unchanged in accordance with the value of DBPROP_COMMITPRESERVE. If the provider was in auto-commit mode, it remains in auto-commit mode, and likewise for manual-commit mode.
  • DBPROPVAL_TC_DDL_IGNORE—Transactions can contain only DML statements. Attempts to modify tables or indexes within a transaction are ignored.

    Note DDL operations are not transacted even if a session is participating in a transaction. If the DDL method succeeds, the operation is complete and unaffected by subsequent calls to abort or commit the transaction.

  • DBPROPVAL_TC_DDL_LOCK—Transactions can contain both DML and table or index modifications, but modifying a table or index within a transaction causes the table or index to be locked until the transaction completes.
  • DBPROPVAL_TC_ALL—Transactions can contain DML statements, as well as table or index modifications, 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 OLE DB" in Chapter 15: Transactions.


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 a retaining abort.
  • DBPROPVAL_TR_OPTIMISTIC—Optimistic concurrency control is to be used. If DBPROPVAL_TR_OPTIMISTIC is specified, and 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_TABLESTATISTICS

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Table Statistics Support

Bitmask indicating statistics support. One of the following:

  • DBPROPVAL_TS_CARDINALITY—Supports column and tuple cardinality information on columns in a statistic.
  • DBPROPVAL_TS_HISTOGRAM—Supports histogram information on the first column of a statistic. Providers supporting histograms should set this bit, as well as DBPROPVAL_ORS_HISTOGRAM, in the DBPROP_OPENROWSETSUPPORT property.


DBPROP_TABLETERM

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_BSTR

Typical R/W: R

Description: Table Term

Indicates the name the data source object 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

Indicates whether the table is temporary. One of the following:

Value | Meaning
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

Value | Meaning
VARIANT_TRUE | Any object created on the specified column is transacted. That is, data made visible to the data store 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 store.

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: Unique Rows

Value | Meaning
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, 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 typically are not displayed to the user but are used by components such as update services to uniquely identify a row.

The provider optionally may duplicate existing columns in the rowset to ensure that key columns are included.

If this property is set to VARIANT_TRUE and the provider supports the optional IColumnsRowset metadata column DBCOLUMN_KEYCOLUMN, the set of columns that 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.


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—IRowsetChange::SetData is supported.
  • DBPROPVAL_UP_DELETE—IRowsetChange::DeleteRows is supported.
  • DBPROPVAL_UP_INSERT—IRowsetChange::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, it is provider-specific what methods are supported on IRowsetChange.

DBPROP_UPDATABILITY is considered successfully set if all of the bits specified are supported. It is not necessary for the provider to disable support for the bits that are not set, although providers may do so as an optimization.


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.


MDPROP_AGGREGATECELL_UPDATE

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Support for updating aggregated cells

This is an OLE DB for OLAP property.

Value | Meaning
MDPROPVAL_AU_UNSUPPORTED | The provider does not support updating nonatomic cells.
MDPROPVAL_AU_UNCHANGED | The provider supports updating of aggregated cells, but the value of cells beneath an aggregated cell remains unchanged.
MDPROPVAL_AU_UNKNOWN | The provider supports updating of aggregated cells, but the value of cells beneath an aggregated cell remains undefined.


MDPROP_AXES

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Number of axes in the dataset

This is an OLE DB for OLAP property. 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_FLATTENING_SUPPORT

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Flattening Support

This is an OLE DB for OLAP property.

Value | Meaning
MDPROPVAL_FS_FULL_SUPPORT | The provider supports flattening as described in "Flattening a Dataset to Produce a Rowset" in Chapter 24: Rowsets in OLE DB for OLAP.
MDPROPVAL_FS_GENERATED_COLUMN | The provider supports flattening by using dummy names, as described in "Support Level for Named Levels" in Chapter 24: Rowsets in OLE DB for OLAP.
MDPROPVAL_FS_GENERATED_DIMENSION | The provider supports flattening by generating one column per dimension as described in "Support Level for Named Levels" in Chapter 24: Rowsets in OLE DB for OLAP.
MDPROPVAL_FS_NO_SUPPORT | 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_MDX_CASESUPPORT

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Support for MDX case statements

This is an OLE DB for OLAP property. A bitmask that specifies the support in a provider for case statements. Can be a combination of zero or more of the values described in the following table.

Value | Meaning
MDPROPVAL_MC_SIMPLECASE | The provider supports a simple case statement.
MDPROPVAL_MC_SEARCHEDCASE | The provider supports a searched case statement.


MDPROP_MDX_DESCFLAGS

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Support for various <desc flag> values in the DESCENDANTS function

This is an OLE DB for OLAP property. Can be a combination of zero or more of the values described in the following table.

Value | Meaning
MDPROPVAL_MD_BEFORE | The provider supports the flag BEFORE.
MDPROPVAL_MD_AFTER | The provider supports the flag AFTER.
MDPROPVAL_MD_SELF | The provider supports the flag SELF. 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_FORMULAS

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Support for creation of named sets and calculated members

This is an OLE DB for OLAP property. Can be a combination of zero or more of the values described in the following table.

Value | Meaning
MDPROPVAL_MF_WITH_CALCMEMBERS | The provider supports the creation of calculated members by using the WITH clause before a SELECT.
MDPROPVAL_MF_WITH_NAMEDSETS | The provider supports the creation of named sets by using the WITH clause before a SELECT.
MDPROPVAL_MF_CREATE_CALCMEMBERS | The provider supports the creation of named calculated members by using the CREATE clause.
MDPROPVAL_MF_CREATE_NAMEDSETS | The provider supports the creation of named sets by using the CREATE clause.
MDPROPVAL_MF_SCOPE_SESSION | The provider supports the scope value of SESSION during the creation of named sets and calculated members.
MDPROPVAL_MF_SCOPE_GLOBAL | The provider supports the scope value of GLOBAL during the creation of named sets and calculated members.


MDPROP_MDX_JOINCUBES

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Support for query joining multiple cubes

This is an OLE DB for OLAP property. A bitmask that can be a combination of zero or more of the values described in the following table.

Value | Meaning
MDPROPVAL_MJC_IMPLICITCUBE | The provider supports an empty FROM clause. The cube is implicitly resolved by the axis and slicer dimensions.
MDPROPVAL_MJC_SINGLECUBE | The provider supports only one cube in the FROM clause of the MDX statement.
MDPROPVAL_MJC_MULTICUBES | The provider supports more than one cube in the FROM clause of the MDX statement.


MDPROP_MDX_MEMBER_FUNCTIONS

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Support for various member functions

This is an OLE DB for OLAP property. Can be a combination of zero or more of the values described in the following table.

Value | Meaning
MDPROPVAL_MMF_COUSIN | The provider supports the function COUSIN.
MDPROPVAL_MMF_PARALLELPERIOD | The provider supports the function PARALLELPERIOD.
MDPROPVAL_MMF_OPENINGPERIOD | The provider supports the function OPENINGPERIOD.
MDPROP_MMF_CLOSINGPERIOD | The provider supports the function CLOSINGPERIOD.


MDPROP_MDX_NONMEASURE_EXPRESSIONS

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: The capabilities in the <numeric_value_expression> argument of set functions

This is an OLE DB for OLAP property.

Value | Meaning
MDPROPVAL_NME_MEASURESONLY | The provider supports only expressions involving Measures dimension members.
MDPROPVAL_NME_ALLDIMENSIONS | The provider supports expressions involving members from any dimension. Example Revenue and Cost are measures, and USA is a member of the 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, expressions such as (USA, Sales)/3 are made of measures and nonmeasures.


MDPROP_MDX_NUMERIC_FUNCTIONS

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Support for various numeric functions

This is an OLE DB for OLAP property. Can be a combination of zero or more of the values described in the following table.

Value | Meaning
MDPROPVAL_MNF_MEDIAN | The provider supports the function MEDIAN.
MDPROPVAL_MNF_VAR | The provider supports the function VAR.
MDPROPVAL_MNF_STDDEV | The provider supports the function STDDEV.
MDPROP_MNF_RANK | The provider supports the function RANK.
MDPROPVAL_MNF_AGGREGATE | The provider supports the function AGGREGATE.
MDPROPVAL_MNF_COVARIANCE | The provider supports the function COVARIANCE.
MDPROPVAL_MNF_CORRELATION | The provider supports the function CORRELATION.
MDPROP_MNF_LINREGSLOPE | The provider supports the function LINREGSLOPE.
MDPROPVAL_MNF_LINREGVARIANCE | The provider supports the function LINREGVARIANCE.
MDPROPVAL_MNF_LINREGR2 | The provider supports the function LINREGR2.
MDPROPVAL_MNF_LINREGPOINT | The provider supports the function LINREGPOINT.
MDPROPVAL_MNF_LINREGINTERCEPT | The provider supports the function LINREGINTERCEPT.


MDPROP_MDX_OBJQUALIFICATION

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Provider's ability to qualify a cube name

This is an OLE DB for OLAP property. A bitmask specifying how multidimensional schema object names can be qualified in an MDX statement. Can be a combination of zero or more of the values described in the following table.

Value | Meaning
MDPROPVAL_MOQ_DATASOURCE_CUBE | Cubes can be qualified by the data source name.
MDPROPVAL_MOQ_CATALOG_CUBE | Cubes can be qualified by the catalog name.
MDPROPVAL_MOQ_SCHEMA_CUBE Cubes can be qualified by the schema name.
Note If none of the above bits are set, the provider does not support cube qualification.
MDPROPVAL_MOQ_CUBE_DIM | Dimensions can be qualified by cube name.
MDPROPVAL_MOQ_DIM_HIER | Hierarchies can be qualified by dimension names.
MDPROPVAL_MOQ_DIMHIER_LEVEL | Levels can be qualified by the dimension name and/or hierarchy name. 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, this bit must be set.
MDPROPVAL_MOQ_DIMHIER_MEMBER | Members can be qualified by a dimension name and/or a hierarchy name.
MDPROPVAL_MOQ_LEVEL_MEMBER | Members can be qualified by a level name.
MDPROPVAL_MOQ_MEMBER_MEMBER | Members can be qualified by their ancestor names.


MDPROP_MDX_OUTERREFERENCE

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Support for outer reference in an MDX statement

This is an OLE DB for OLAP property.

Value | Meaning
MDPROPVAL_MO_TUPLE | The provider supports <tuple>[.VALUE] to be qualified by a <cube_name> argument in an MDX statement.


MDPROP_MDX_QUERYBYPROPERTY

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_BOOL

Typical R/W: R

Description: Support for querying by property values in an MDX statement

This is an OLE DB for OLAP property.


MDPROP_MDX_RANGEROWSET

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Support for cell updates

This is an OLE DB for OLAP property.

Value | Meaning
MDPROPVAL_RR_NORANGEROWSET | The provider does not support IMDRangeRowset.
MDPROPVAL_RR_READONLY | The provider supports a read-only range rowset.
MDPROPVAL_RR_UPDATE | The provider supports an updatable range rowset.


MDPROP_MDX_SET_FUNCTIONS

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Support for various set functions.

This is an OLE DB for OLAP property. Can be a combination of zero or more of the values described in the following table.

Value | Meaning
MDPROPVAL_MSF_TOPPERCENT | The provider supports the function TOPPERCENT.
MDPROPVAL_MSF_BOTTOMPERCENT | The provider supports the function BOTTOMPERCENT.
MDPROPVAL_MSF_TOPSUM | The provider supports the function TOPSUM.
MDPROPVAL_MSF_BOTTOMSUM | The provider supports the function BOTTOMSUM.
MDPROPVAL_MSF_DRILLDOWNLEVEL | The provider supports the function DRILLDOWNLEVEL.
MDPROPVAL_MSF_DRILLDOWNMEMBER | The provider supports the function DRILLDOWNMEMBER.
MDPROPVAL_MSF_DRILLDOWNMEMBERTOP | The provider supports the function DRILLDOWNMEMBERTOP.
MDPROPVAL_MSF_DRILLDOWNMEMBERBOTTOM | The provider supports the function DRILLDOWNMEMBERBOTTOM.
MDPROPVAL_MSF_DRILLDOWNLEVELTOP | The provider supports the function DRILLDOWNLEVELTOP.
MDPROPVAL_MSF_DRILLDOWNLEVELBOTTOM | The provider supports the function DRILLDOWNLEVELBOTTOM.
MDPROPVAL_MSF_DRILLUPMEMBER | The provider supports the function DRILLUPMEMBER.
MDPROPVAL_MSF_DRILLUPLEVEL | The provider supports the function DRILLUPLEVEL.
MDPROPVAL_MSF_PERIODSTODATE | The provider supports the function PERIODSTODATE.
MDPROPVAL_MSF_LASTPERIODS | The provider supports the function LASTPERIODS.
MDPROPVAL_MSF_YTD | The provider supports the function YTD.
MDPROPVAL_MSF_QTD | The provider supports the function QTD.
MDPROPVAL_MSF_MTD | The provider supports the function MTD.
MDPROPVAL_MSF_WTD | The provider supports the function WTD.


MDPROP_MDX_SLICER

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: The capabilities in the WHERE clause of an MDX statement

This is an OLE DB for OLAP property.

Value | Meaning
MDPROPVAL_MS_SINGLETUPLE | The provider supports only one tuple in the WHERE clause.
MDPROPVAL_MS_MULTIPLETUPLES | The provider supports more than one tuple in the WHERE clause.


MDPROP_MDX_STRING_COMPOP

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Support for string comparison operators other than equals and not-equals operators

This is an OLE DB for OLAP property. A bitmask specifying the support in a provider for string comparison operators. Can be a combination of zero or more of the values described in the following table.

Value | Meaning
MDPROPVAL_MSC_LESSTHAN | The provider supports the less-than operator.
MDPROPVAL_MSC_GREATERTHAN | The provider supports the greater-than operator.
MDPROPVAL_MSC_LESSTHANEQUAL | The provider supports the less-than-or-equal-to operator.
MDPROPVAL_MSC_GREATERTHANEQUAL | The provider supports the greater-than-or-equal-to operator.


MDPROP_NAMED_LEVELS

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: R

Description: Support for named levels

This is an OLE DB for OLAP property. A bitmask specifying whether the provider supports named levels and/or numbered levels. Can be one or more of the values described in the following table.

Value | Meaning
MDPROPVAL_NL_NAMEDLEVELS | The provider supports named levels.
MDPROPVAL_NL_NOSUPPORT | The provider does not support named levels. If this value is set, the provider is not required to support the LEVELS schema rowset or to include the LEVEL_UNIQUE_NAME in the MEMBERS and PROPERTIES rowsets.
MDPROPVAL_NL_NUMBEREDLEVELS | The provider supports numbered levels using the LEVELS(n) function.
MDPROPVAL_NL_SCHEMAONLY | The provider supports "dummy" named levels. These level names are for display only and are frequently just provider-generated names for a given level number. These names cannot be used in an MDX statement (such as in <level_name>.MEMBERS). These names appear in the LEVEL_NAME and LEVEL_UNIQUE_NAME columns of the schema rowset; providers may choose to have the same name in both the columns, or they can generate LEVEL_UNIQUE_NAME by appropriately qualifying the LEVEL_NAME. It is provider-specific whether these dummy names also appear in the LEVEL_UNIQUE_NAME column of the MEMBERS and PROPERTIES rowsets.


MDPROP_VISUALMODE

Property group: Data Source Information

Property set: DBPROPSET_DATASOURCEINFO

Type: VT_I4

Typical R/W: W

Description: Defines visibility of precalculated totals

Indicates whether the provider is to calculate visual totals, which dynamically totals child members of parent members specified in a set. When visual totals mode is on, displayed aggregate values are equal to the sum of the displayed values being aggregated. Can be one or more of the values described in the following table.

Value | Meaning
MDPROPVAL_VISUAL_MODE_DEFAULT | Default mode, provider-specific
MDPROPVAL_VISUAL_MODE_VISUAL | Visual totals on
MDPROPVAL_VISUAL_MODE_VISUALOFF | Visual totals off


Appendix D: 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]   interface IParseDisplayName;
      [mandatory]   interface ISourcesRowset;
      [optional]    interface IDBInitialize;
      [optional]    interface IDBProperties;
      [optional]    interface 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 IAlterIndex;
      [optional]    interface IAlterTable;
      [optional]    interface IBindResource;
      [optional]    interface ICreateRow;
      [optional]    interface IDBCreateCommand;
      [optional]    interface IDBSchemaRowset;
      [optional]    interface IIndexDefinition;
      [optional]    interface ISupportErrorInfo;
      [optional]    interface ITableCreation;
      [optional]    interface ITableDefinition;
      [optional]    interface ITableDefinitionWithConstraints;
      [optional]    interface ITransaction;
      [optional]    interface ITransactionJoin;
      [optional]    interface ITransactionLocal;
      [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 ICommandPersist;
      [optional]    interface ICommandPrepare;
      [optional]    interface ICommandWithParameters;
      [optional]    interface ISupportErrorInfo;
      [optional]    interface ICommandStream;
   }

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 IColumnsInfo2;
      [optional]    interface IColumnsRowset;
      [optional]    interface IConnectionPointContainer;
      [optional]    interface IDBAsynchStatus;
      [optional]    interface IGetRow;
      [optional]    interface IRowsetChange;
      [optional]    interface IRowsetChapterMember;
      [optional]    interface IRowsetCurrentIndex;
      [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;
      [optional]    interface IRowsetBookmark;
   }

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 IRowsetCurrentIndex;
      [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 ISQLErrorInfo;
   }

Error records:

   CoType TErrorRecord {
      [mandatory]   interface IErrorInfo;
   }

Binder objects:

   CoType TBinder {
      [mandatory]   interface IBindResource;
      [mandatory]   interface ICreateRow;
      [mandatory]   interface IDBBinderProperties;
      [optional]    interface IRegisterProvider;        // Root binder only
      [optional]    interface ISupportErrorInfo;
   }

Row objects:

   CoType TRow {
      [mandatory]   interface IColumnsInfo;
      [mandatory]   interface IConvertType;
      [mandatory]   interface IGetSession;
      [mandatory]   interface IRow;
      [optional]    interface IColumnsInfo2;
      [optional]    interface IConnectionPointContainer;
      [optional]    interface ICreateRow;
      [optional]    interface IDBAsynchStatus;
      [optional]    interface IDBCreateCommand;
      [optional]    interface IDBInitialize;
      [optional]    interface IRowChange;
      [optional]    interface IRowSchemaChange;
      [optional]    interface IScopedOperations;
      [optional]    interface ISupportErrorInfo;
   }

Stream objects:

CoType TStream {
   [mandatory]   interface ISequentialStream;
   [optional]    interface IConnectionPointContainer;
   [optional]    interface IDBAsynchStatus;
   [optional]    interface IDBInitialize;
   [optional]    interface IGetSourceRow;
   [optional]    interface ISupportErrorInfo;
   [optional]    interface IStream;
}


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,
     DBACCESSOR_INHERITED
};
IAccessor::
CreateAccessor 		IAccessor::CreateAccessor

IAccessor::GetBindings

typedef struct tagDBBINDEXT {

     BYTE         *pExtension;
     DBCOUNTITEM   ulExtension;
} DBBINDEXT;
"DBBINDING Structures" in Chapter 6: Getting and Setting Data DBBINDING
typedef struct tagDBBINDING {

     DBORDINAL      iOrdinal;
     DBBYTEOFFSET   obValue;
     DBBYTEOFFSET   obLength;
     DBBYTEOFFSET   obStatus;
     ITypeInfo     *pTypeInfo;
     DBOBJECT      *pObject;
     DBBINDEXT     *pBindExt;
     DBPART         dwPart;
     DBMEMOWNER     dwMemOwner;
     DBPARAMIO      eParamIO;
     DBLENGTH       cbMaxLen;
     DWORD          dwFlags;
     DBTYPE         wType;
     BYTE           bPrecision;
     BYTE           bScale;
} DBBINDING;
"DBBINDING Structures" in Chapter 6: Getting and Setting Data IAccessor::CreateAccessor

IAccessor::GetBindings

typedef DWORD DBBINDFLAG;

enum DBBINDFLAGENUM {
     DBBINDFLAG_HTML
};
A possible value of dwFlags as defined in "DBBINDING Structures" in Chapter 6: Getting and Setting Data IAccessor::
CreateAccessor
typedef DWORD DBBINDSTATUS;

enum DBBINDSTATUSENUM {
     DBBINDSTATUS_OK
     DBBINDSTATUS_BADORDINAL
DBBINDSTATUS_UNSUPPORTEDCONVERSION
     DBBINDSTATUS_BADBINDINFO
     DBBINDSTATUS_BADSTORAGEFLAGS
     DBBINDSTATUS_NOINTERFACE
};
IAccessor::
CreateAccessor 		IAccessor::CreateAccessor
typedef DWORD DBBINDURLFLAG;

enum DBBINDURLFLAGENUM {
     DBBINDURLFLAG_READ,
     DBBINDURLFLAG_WRITE,
     DBBINDURLFLAG_READWRITE,
     DBBINDURLFLAG_SHARE_DENY_READ,
     DBBINDURLFLAG_SHARE_DENY_WRITE,
     DBBINDURLFLAG_SHARE_EXCLUSIVE,
     DBBINDURLFLAG_SHARE_DENY_NONE,
     DBBINDURLFLAG_ASYNCHRONOUS,
     DBBINDURLFLAG_COLLECTION,
     DBBINDURLFLAG_DELAYFETCHSTREAM,
     DBBINDURLFLAG_DELAYFETCHCOLUMNS,
     DBBINDURLFLAG_RECURSIVE,
     DBBINDURLFLAG_OUTPUT,
     DBBINDURLFLAG_WAITFORINIT,
     DBBINDURLFLAG_OPENIFEXISTS,
     DBBINDURLFLAG_OVERWRITE,
     DBBINDURLFLAG_ISSTRUCTUREDDOCUMENT
};
IBindResource::Bind IBindResource::Bind
typedef DWORD DBBINDURLSTATUS;

enum DBBINDURLSTATUSENUM {
     DBBINDURLSTATUS_S_OK,
     DBBINDURLSTATUS_S_DENYNOTSUPPORTED,
     DBBINDURLSTATUS_S_DENYTYPENOTSUPPORTED,
     DBBINDURLSTATUS_S_REDIRECTED    };
IBindResource::Bind IBindResource::Bind
typedef enum tagDBBOOKMARK {

     DBBMK_INVALID,
     DBBMK_FIRST,
     DBBMK_LAST
} DBBOOKMARK;
"Standard Bookmarks" in Chapter 4: Rowsets
typedef struct tagDBCOLUMNACCESS {

     void          *pData;
     DBID           columnid;
     DBLENGTH       cbDataLen;
     DBSTATUS       dwStatus;
     DBLENGTH       cbMaxLen;
     DB_DWRESERVE   dwReserved;
     DBTYPE         wType;
     BYTE           bPrecision;
     BYTE           bScale;
} DBCOLUMNACCESS;
IRow::GetColumns IRow::GetColumns
typedef struct tagDBCOLUMNDESC {

     LPOLESTR     pwszTypeName;
     ITypeInfo   *pTypeInfo;
     DBPROPSET   *rgPropertySets;
     CLSID       *pclsid;
     ULONG        cPropertySets;
     DBLENGTH     ulColumnSize;
     DBID         dbcid;
     DBTYPE       wType;
     BYTE         bPrecision;
     BYTE         bScale;
} DBCOLUMNDESC;
ITableDefinition::
CreateTable 		ITableDefinition::AddColumn

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,
     DBCOLUMNFLAGS_SCALEISNEGATIVE,
     DBCOLUMNFLAGS_RESERVED,
     DBCOLUMNFLAGS_ISROWURL,
     DBCOLUMNFLAGS_ISDEFAULTSTREAM,
     DBCOLUMNFLAGS_ISCOLLECTION,
     DBCOLUMNFLAGS_ISSTREAM,
     DBCOLUMNFLAGS_ISROWSET,
     DBCOLUMNFLAGS_ISROW,
     DBCOLUMNFLAGS_ROWSPECIFICCOLUMN
};
IColumnsInfo::
GetColumnInfo 		DBCOLUMNINFO
typedef struct tagDBCOLUMNINFO {

     LPOLESTR        pwszName;
     ITypeInfo      *pTypeInfo;
     DBORDINAL       iOrdinal;
     DBCOLUMNFLAGS   dwFlags;
     DBLENGTH        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 struct tagDBCONSTRAINTDESC {

     DBID       *pConstraintID;
     DBCONSTRAINTTYPE ConstraintType;
     DBORDINAL   cColumns;
     DBID        rgColumnList[];
     DBID       *pReferencedTableID;
     DBORDINAL   cForeignKeyColumns;
     DBID        rgForeignKeyColumnList[];
     OLECHAR    *pwszConstraintText;
     DBUPDELRULE UpdateRule;
     DBUPDELRULE DeleteRule;
     DBMATCHTYPE MatchType;
     DBDEFERRABILITY Deferrability;
     DB_URESERVE cReserved;
     DBPROPSET   rgReserved[];
} DBCONSTRAINTDESC;
ITableDefinitionWithConstraints::
AddConstraint 		ITableDefinitionWithConstraints::AddConstraint
typedef DWORD DBCONVERTFLAGS;

enum DBCONVERTFLAGSENUM {
     DBCONVERTFLAGS_COLUMN,
     DBCONVERTFLAGS_PARAMETER,
     DBCONVERTFLAGS_FROMVARIANT,
     DBCONVERTFLAGS_ISFIXEDLENGTH,
     DBCONVERTFLAGS_ISLONG
};
IConvertType::
CanConvert 		IConvertType::CanConvert
typedef DWORD DBCOPYFLAGS;

enum DBCOPYFLAGSENUM {
     DBCOPY_ASYNC,
     DBCOPY_REPLACE_EXISTING,
     DBCOPY_ALLOW_EMULATION,
     DBCOPY_NON_RECURSIVE,
     DBCOPY_ATOMIC
};
IScopedOperations::Copy IScopedOperations::Copy
typedef struct tagDBDATE {

     SHORT  year;
     USHORT month;
     USHORT day;
} DBDATE;
"Type Indicators" in Appendix A: Data Types
typedef DWORD DBDELETEFLAGS;

enum DBDELETEFLAGSENUM {
     DBDELETE_ASYNC,
     DBDELETE_ATOMIC
};
IScopedOperations::Delete IScopedOperations::Delete
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: Rowsets 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 struct tagDBIMPLICITSESSION {

     IUnknown *   pUnkOuter;
     IID * piid;
     IUnknown *   pSession;
} DBIMPLICITSESSION;
IBindResource::Bind IBindResource::Bind
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: Rowsets 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_CONSTRAINT_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: Getting and Setting Data DBBINDING
typedef DWORD DBMOVEFLAGS;

enum DBMOVEFLAGSENUM {
     DBMOVE_REPLACE_EXISTING,
     DBMOVE_ASYNC,
     DBMOVE_DONT_UPDATE_LINKS,
     DBMOVE_ALLOW_EMULATION,
     DBMOVE_ATOMIC
};
IScopedOperations::Move IScopedOperations::Move
typedef struct tagDB_NUMERIC {

     BYTE precision;
     BYTE scale;
     BYTE sign;
     BYTE val[16];
} DB_NUMERIC;
"Type Indicators" in Appendix A: Data Types
typedef struct tagDB_VARNUMERIC {

     BYTE precision;
     SBYTE scale;
     BYTE sign;
     BYTE val[1];
} DB_VARNUMERIC;
"Type Indicators" in Appendix A: Data Types
typedef struct tagDBOBJECT {

     DWORD dwFlags;
     IID iid;
} DBOBJECT;
"DBBINDING Structures" in Chapter 6: Getting and Setting Data DBBINDING
typedef struct tagDBPARAMBINDINFO {

     LPOLESTR pwszDataSourceType;
     LPOLESTR     pwszName;
     DBLENGTH     ulParamSize;
     DBPARAMFLAGS dwFlags;
     BYTE         bPrecision;
     BYTE         bScale;
} DBPARAMBINDINFO;
ICommandWithParameters::
SetParameterInfo 		ICommandWithParameters::SetParameterInfo
typedef DWORD DBPARAMFLAGS;

enum DBPARAMFLAGSENUM {
     DBPARAMFLAGS_ISINPUT,
     DBPARAMFLAGS_ISOUTPUT,
     DBPARAMFLAGS_ISSIGNED,
     DBPARAMFLAGS_ISNULLABLE,
     DBPARAMFLAGS_ISLONG,
     DBPARAMFLAGS_SCALEISNEGATIVE
};
ICommandWithParameters::
GetParameterInfo 		DBPARAMBINDINFO
DBPARAMINFO
typedef struct tagDBPARAMINFO {

     DBPARAMFLAGS dwFlags;
     DBORDINAL    iOrdinal;
     LPOLESTR     pwszName;
     ITypeInfo   *pTypeInfo;
     DBLENGTH     ulParamSize;
     DBTYPE       wType;
     BYTE         bPrecision;
     BYTE         bScale;
} DBPARAMINFO;
ICommandWithParameters::
GetParameterInfo 		ICommandWithParameters::GetParameterInfo
typedef DWORD DBPARAMIO;

enum DBPARAMIOENUM {
     DBPARAMIO_NOTPARAM,
     DBPARAMIO_INPUT,
     DBPARAMIO_OUTPUT
};
"DBBINDING Structures" in Chapter 6: Getting and Setting Data DBBINDING
typedef struct tagDBPARAMS {

     void        *pData;
     DB_UPARAMS   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: Getting and Setting Data DBBINDING
typedef DWORD DBPENDINGSTATUS;

enum DBPENDINGSTATUSENUM {
     DBPENDINGSTATUS_NEW,
     DBPENDINGSTATUS_CHANGED,
     DBPENDINGSTATUS_DELETED,
     DBPENDINGSTATUS_UNCHANGED,
     DBPENDINGSTATUS_INVALIDROW
};
"Row States in Deferred Update Mode" in Chapter 5: Updating Data in Rowsets IRowsetUpdate::GetPendingRows

IRowsetUpdate::GetRowStatus

typedef struct tagDBPROP {

     DBPROPID      dwPropertyID;
     DBPROPOPTIONS dwOptions;
     DBPROPSTATUS  dwStatus;
     DBID          colid;
     VARIANT       vValue;
} DBPROP;
"DBPROP Structure" in Chapter 14: Properties 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_TRUSTEE,
     DBPROPFLAGS_VIEW,
     DBPROPFLAGS_STREAM
};
"DBPROPFLAGS Enumerated Type" in Chapter 14: Properties DBPROPINFO
typedef struct tagDBPROPIDSET {

     DBPROPID * rgPropertyIDs;
     ULONG      cPropertyIDs;
     GUID       guidPropertySet;
} DBPROPIDSET;
"DBPROPIDSET Structure" in Chapter 14: Properties 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 14: Properties DBPROPINFOSET
typedef struct tagDBPROPINFOSET {

     DBPROPINFO * rgPropertyInfos;
     ULONG        cPropertyInfos;
     GUID         guidPropertySet;
} DBPROPINFOSET;
"DBPROPINFOSET Structure" in Chapter 14: Properties IDBDataSourceAdmin::GetCreationProperties

IDBProperties::GetPropertyInfo

typedef DWORD DBPROPOPTIONS;

enum DBPROPOPTIONSENUM {
     DBPROPOPTIONS_REQUIRED,
     DBPROPOPTIONS_OPTIONAL
};
"DBPROPOPTIONS Enumerated Type" in Chapter 14: Properties DBPROP
typedef struct tagDBPROPSET {

     DBPROP * rgProperties;
     ULONG    cProperties;
     GUID     guidPropertySet;
} DBPROPSET;
"DBPROPSET Structure" in Chapter 14: Properties 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_NOTAVAILABLE
};
"DBPROPSTATUS Enumerated Type" in Chapter 14: Properties 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_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,
     DBREASON_ROWSET_CHANGED,
     DBREASON_ROWPOSITION_CHANGED,
     DBREASON_ROWPOSITION_CHAPTERCHANGED,
     DBREASON_ROWPOSITION_CLEARED,
     DBREASON_ROW_ASYNCHINSERT
};
IRowsetNotify IRowsetNotify::OnFieldChange

IRowsetNotify::OnRowChange

IRowsetNotify::OnRowsetChange

typedef DWORD DBROWSTATUS;

enum DBROWSTATUSENUM {
     DBROWSTATUS_S_OK,
     DBROWSTATUS_S_MULTIPLECHANGES,
     DBROWSTATUS_S_PENDINGCHANGES,
     DBROWSTATUS_S_ROWSETCOLUMN,
     DBROWSTATUS_S_NOCHANGE,
     DBROWSTATUS_E_CANCELED,
     DBROWSTATUS_E_CANTRELEASE,
     DBROWSTATUS_E_CONCURRENCYVIOLATION,
     DBROWSTATUS_E_DELETED,
     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,
     DBROWSTATUS_E_FAIL
};
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_BINDER,
     DBSOURCETYPE_DATASOURCE_MDP,
     DBSOURCETYPE_DATASOURCE_TDP,
     DBSOURCETYPE_ENUMERATOR
};
Enumerated values as shown here or in Oledb.h 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_E_INTEGRITYVIOLATION,
     DBSTATUS_E_SCHEMAVIOLATION,
     DBSTATUS_E_BADSTATUS,
     DBSTATUS_S_DEFAULT,
     MDSTATUS_S_CELLEMPTY,
     DBSTATUS_S_IGNORE,
     DBSTATUS_E_DOESNOTEXIST,
     DBSTATUS_E_INVALIDURL,
     DBSTATUS_E_RESOURCELOCKED,
     DBSTATUS_E_RESOURCEEXISTS,
     DBSTATUS_E_CANNOTCOMPLETE,
     DBSTATUS_E_VOLUMENOTFOUND,
     DBSTATUS_E_OUTOFSPACE,
     DBSTATUS_S_CANNOTDELETESOURCE,
     DBSTATUS_E_READONLY,
     DBSTATUS_E_RESOURCEOUTOFSCOPE,
     DBSTATUS_S_ALREADYEXISTS,
     DBSTATUS_E_CANCELED,
     DBSTATUS_E_NOTCOLLECTION,
     DBSTATUS_S_ROWSETCOLUMN
     };
"Status" in Chapter 6: Getting and Setting Data
enum DBTABLESTATISTICSTYPE {

     DBSTAT_HISTOGRAM,
     DBSTAT_COLUMN_CARDINALITY,
     DBSTAT_TUPLE_CARDINALITY
     };
TABLE_STATISTICS Rowset in Appendix B: Schema Rowsets
typedef struct tagDBTIME {

     USHORT hour;
     USHORT minute;
     USHORT second;
} DBTIME;
"Type Indicators" in Appendix A: Data Types
typedef struct tagDBTIMESTAMP {

     SHORT  year;
     USHORT month;
     USHORT day;
     USHORT hour;
     USHORT minute;
     USHORT second;
     ULONG  fraction;
} DBTIMESTAMP;
"Type Indicators" in Appendix A: Data Types
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: Data Types DBBINDING

DBCOLUMNINFO

DBPARAMINFO

DBCOLUMNDESC

IConvertType::CanConvert

typedef struct tagDBVECTOR {

     DBLENGTH   size;
     void      *ptr;
} DBVECTOR;
"Type Indicators" in Appendix A: Data Types
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: Data Types
typedef struct tagERRORINFO {

     HRESULT hrError;
     DWORD   dwMinor;
     CLSID   clsid;
     IID     iid;
     DISPID  dispid;
} ERRORINFO;
"Error Records" in Chapter 16: Errors 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


Appendix E: Interface Summary

This appendix lists the prototypes of all interfaces in OLE DB.

interface IAccessor {
   HRESULT AddRefAccessor (
      HACCESSOR   hAccessor,
      DBREFCOUNT *pcRefCount);

   HRESULT CreateAccessor (
      DBACCESSORFLAGS   dwAccessorFlags,
      DBCOUNTITEM       cBindings,
      const DBBINDING   rgBindings[],
      DBLENGTH          cbRowSize,
      HACCESSOR        *phAccessor,
      DBBINDSTATUS      rgStatus[]);

   HRESULT GetBindings (
      HACCESSOR           hAccessor,
      DBACCESSORFLAGS    *pdwAccessorFlags,
      DBCOUNTITEM        *pcBindings,
      DBBINDING         **prgBindings);

   HRESULT ReleaseAccessor (
      HACCESSOR   hAccessor,
      DBREFCOUNT *pcRefCount);
};

interface IAlterIndex {
   HRESULT AlterIndex (
      DBID *      pTableID,
      DBID *      pIndexID,
      DBID *      pNewIndexID,
      ULONG       cPropertySets,
      DBPROPSET   rgPropertySet[]);
};

interface IAlterTable {
   HRESULT AlterColumn (
      DBID *              pTableID,
      DBID *              pColumnID,
      DBCOLUMNDESCFLAGS   dwColumnDescFlags,
      DBCOLUMNDESC *      pColumnDesc);

   HRESULT AlterTable (
      DBID *      pTableID,
      DBID *      pNewTableID,
      ULONG       cPropertySets,
      DBPROPSET   rgPropertySet[]);
};

interface IBindResource {
   HRESULT Bind(
      IUnknown *           pUnkOuter,
      LPCOLESTR            pwszURL,
      DBBINDURLFLAG        dwBindURLFlags,
      REFGUID              rguid,
      REFIID               riid,
      IAuthenticate *       pAuthenticate,
      DBIMPLICITSESSION *   pImplSession,
      DBBINDURLSTATUS *     pdwBindStatus,
      IUnknown **           ppUnk);
};

interface IChapteredRowset {
   HRESULT AddRefChapter (
      HCHAPTER    hChapter,
      DBREFCOUNT *pcRefCount);

   HRESULT ReleaseChapter (
      HCHAPTER    hChapter,
      DBREFCOUNT *pcRefCount);
};

interface IColumnsInfo {
   HRESULT GetColumnInfo (
      DBORDINAL     *pcColumns,
      DBCOLUMNINFO **prgInfo,
      OLECHAR      **ppStringsBuffer);

   HRESULT MapColumnIDs (
      DBORDINAL    cColumnIDs,
      const DBID   rgColumnIDs[],
      DBORDINAL    rgColumns[]);
};

interface IColumnsInfo2 {
HRESULT GetRestrictedColumnInfo(
      DBORDINAL         cColumnIDMasks,
      const DBID        rgColumnIDMasks[ ],
      DWORD             dwFlags,
      DBORDINAL        *pcColumns,
      DBID            **prgColumnIDs,
      DBCOLUMNINFO    **prgColumnInfo,
      OLECHAR         **ppStringsBuffer);
};

interface IColumnsRowset {
   HRESULT GetAvailableColumns (
      DBORDINAL   *pcOptColumns,
      DBID       **prgOptColumns);

   HRESULT GetColumnsRowset (
      IUnknown     *pUnkOuter,
      DBORDINAL     cOptColumns,
      const DBID    rgOptColumns[],
      REFIID        riid,
      ULONG         cPropertySets,
      DBPROPSET     rgPropertySets[],
      IUnknown    **ppColRowset);

};

interface ICommand {
   HRESULT Cancel();

   HRESULT Execute (
      IUnknown     *pUnkOuter,
      REFIID        riid,
      DBPARAMS     *pParams,
      DBROWCOUNT   *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 ICommandStream {
   HRESULT GetCommandStream (
      IID         *piid,
      GUID        *pguidDialect,
      IUnknown   **ppCommandStream);

   HRESULT SetCommandStream (
      REFIID      riid,
      REFGUID     rguidDialect,
      IUnknown   *pCommandStream);
};

interface ICommandText : ICommand {
   HRESULT GetCommandText (
      GUID *       pguidDialect,
      LPOLESTR *   ppwszCommand);

   HRESULT SetCommandText (
      REFGUID     rguidDialect,
      LPCOLESTR   pwszCommand);
};

interface ICommandWithParameters {
   HRESULT GetParameterInfo (
      DB_UPARAMS      *pcParams,
      DBPARAMINFO    **prgParamInfo,
      OLECHAR        **ppNamesBuffer);

   HRESULT MapParameterNames (
      DB_UPARAMS        cParamNames,
      const OLECHAR    *rgParamNames[],
      DB_LPARAMS        rgParamOrdinals[]);

   HRESULT SetParameterInfo (
      DB_UPARAMS              cParams,
      const DB_UPARAMS        rgParamOrdinals[],
      const DBPARAMBINDINFO   rgParamBindInfo[]);
};

interface IConvertType {
   HRESULT CanConvert (
      DBTYPE           wFromType,
      DBTYPE           wToType,
      DBCONVERTFLAGS   dwConvertFlags);
};

interface ICreateRow {
   HRESULT CreateRow(
      IUnknown *           pUnkOuter,
      LPCOLESTR            pwszURL,
      DBBINDURLFLAG        dwBindURLFlags,
      REFGUID              rguid,
      REFIID               riid,
      IAuthenticate *       pAuthenticate,
      DBIMPLICITSESSION *   pImplSession,
      DBBINDURLSTATUS *     pdwBindStatus,
      LPOLESTR *            ppwszNewURL,
      IUnknown **           ppUnk);
};

interface IDBAsynchNotify {
   HRESULT OnLowResource (
      DB_DWRESERVE dwReserved);

   HRESULT OnProgress (
      HCHAPTER      hChapter,
      ULONG         ulOperation,
      DBCOUNTITEM   ulProgress,
      DBCOUNTITEM   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,
      DBCOUNTITEM   *pulProgress,
      DBCOUNTITEM   *pulProgressMax,
      ULONG         *pulAsynchPhase,
      LPOLESTR      *ppwszStatusText);
};

interface IDBBinderProperties {
   HRESULT Reset();
};

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 IGetRow {
   HRESULT GetRowFromHROW(
      IUnknown *    pUnkOuter,
      HROW          hRow,
      REFIID        riid,
      IUnknown **   ppUnk);

   HRESULT GetURLFromHROW(
      HROW         hRow,
      LPOLESTR *   ppwszURL);
};

interface IGetSession {
   HRESULT GetSession(
      REFIID        riid,
      IUnknown **   ppSession);
};

interface IGetSourceRow {
   HRESULT GetSourceRow(
      REFIID        riid,
      IUnknown **   ppRow);
};

interface IIndexDefinition {
   HRESULT CreateIndex(
      DBID                     *pTableID,
      DBID                     *pIndexID,
      DBORDINAL                 cIndexColumnDescs,
      const DBINDEXCOLUMNDESC   rgIndexColumnDescs[],
      ULONG                     cPropertySets,
      DBPROPSET                 rgPropertySets[],
      DBID                    **ppIndexID);

   HRESULT DropIndex(
      DBID *   pTableID,
      DBID *   pIndexID);
};

interface IMultipleResults {
   HRESULT GetResult(
      IUnknown      *pUnkOuter,
      DBRESULTFLAG   lResultFlag,
      REFIID         riid,
      DBROWCOUNT    *pcRowsAffected,
      IUnknown     **ppRowset);
};

interface IOpenRowset {
   HRESULT OpenRowset(
      IUnknown *    pUnkOuter,
      DBID *        pTableID,
      DBID *        pIndexID,
      REFIID        riid,
      ULONG         cPropertySets,
      DBPROPSET     rgPropertySets[],
      IUnknown **   ppRowset);
};

interface IParentRowset {
   HRESULT GetChildRowset(
      IUnknown     *pUnkOuter,
      DBORDINAL     iOrdinal,
      REFIID        riid,
      IUnknown    **ppRowset);
};

interface IRegisterProvider {
   HRESULT GetURLMapping(
      LPCOLESTR      pwszURL,
      DB_DWRESERVE   dwReserved,
      CLSID         *pclsidProvider);

   HRESULT SetURLMapping(
      LPCOLESTR      pwszURL,
      DB_DWRESERVE   dwReserved,
      REFCLSID       rclsidProvider);

   HRESULT UnregisterProvider(
      LPCOLESTR      pwszURL,
      DB_DWRESERVE   dwReserved,
      REFCLSID       rclsidProvider);
};

interface IRow {
   HRESULT GetColumns(
      DBORDINAL        cColumns,
      DBCOLUMNACCESS   rgColumns[ ]);

   HRESULT GetSourceRowset(
      REFIID        riid,
      IUnknown **   ppRowset,
      HROW *        phRow);

   HRESULT Open(
      IUnknown *    pUnkOuter,
      DBID *        pColumnID,
      REFGUID       rguidColumnType,
      DWORD         dwFlags,
      REFIID        riid,
      IUnknown **   ppUnk);
};

interface IRowChange {
   HRESULT SetColumns(
      DBORDINAL        cColumns,
      DBCOLUMNACCESS   rgColumns[ ]);
};

interface IRowSchemaChange {
   HRESULT AddColumns(
      DBORDINAL            cColumns,
      const DBCOLUMNINFO   rgNewColumnInfo[ ],
      DBCOLUMNACCESS       rgColumns[ ]);

   HRESULT DeleteColumns(
      DBORDINAL    cColumns,
      const DBID   rgColumnIDs[ ],
      DBSTATUS     rgdwStatus[ ]);
};

interface IRowset {
   HRESULT AddRefRows(
      DBCOUNTITEM   cRows,
      const HROW    rghRows[],
      DBREFCOUNT    rgRefCounts[],
      DBROWSTATUS   rgRowStatus[]);

   HRESULT GetData (
      HROW        hRow,
      HACCESSOR   hAccessor,
      void *      pData);

   HRESULT GetNextRows (
      HCHAPTER       hChapter,
      DBROWOFFSET    lRowsOffset,
      DBROWCOUNT     cRows,
      DBCOUNTITEM   *pcRowsObtained,
      HROW         **prghRows);

   HRESULT ReleaseRows (
      DBCOUNTITEM    cRows,
      const HROW     rghRows[],
      DBROWOPTIONS   rgRowOptions[]
      DBREFCOUNT     rgRefCounts[],
      DBROWSTATUS    rgRowStatus[]);

   HRESULT RestartPosition (
      HCHAPTER   hChapter);
};

interface IRowsetBookmark {
   HRESULT PositionOnBookmark (
      HCHAPTER      hChapter,
      DBBKMARK      cbBookmark,
      const BYTE   *pBookmark);
};

interface IRowsetChange {
   HRESULT DeleteRows (
      HCHAPTER      hChapter,
      DBCOUNTITEM   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 IRowsetCurrentIndex {
   HRESULT GetIndex (
      DBID **   ppIndexID);

   HRESULT SetIndex (
      DBID *    pIndexID);
};

interface IRowsetFind {
   HRESULT FindNextRow (
      HCHAPTER            hChapter,
      HACCESSOR           hAccessor,
      void               *pFindValue,
      DBCOMPAREOP         CompareOp,
      DBBKMARK            cbBookmark,
      const BYTE         *pBookmark,
      DBROWOFFSET         lRowsOffset,
      DBROWCOUNT          cRows,
      DBCOUNTITEM        *pcRowsObtained,
      HROW              **prghRows);
};

interface IRowsetIdentity {
   HRESULT IsSameRow (
      HROW   hThisRow,
      HROW   hThatRow);
};

interface IRowsetIndex {
   HRESULT GetIndexInfo (
      DBORDINAL             *pcKeyColumns,
      DBINDEXCOLUMNDESC    **prgIndexColumnDesc,
      ULONG                 *pcIndexProperties,
      DBPROPSET            **prgIndexProperties);

   HRESULT Seek (
      HACCESSOR   hAccessor,
      DBORDINAL   cKeyValues,
      void       *pData,
      DBSEEK      dwSeekOptions);

   HRESULT SetRange (
      HACCESSOR   hAccessor,
      DBORDINAL   cStartKeyColumns,
      void       *pStartData,
      DBORDINAL   cEndKeyColumns,
      void       *pEndData,
      DBRANGE     dwRangeOptions);
};

interface IRowsetInfo {
   HRESULT GetProperties (
      const ULONG         cPropertyIDSets,
      const DBPROPIDSET   rgPropertyIDSets[],
      ULONG *             pcPropertySets,
      DBPROPSET **        prgPropertySets);

   HRESULT GetReferencedRowset (
      DBORDINAL     iOrdinal,
      REFIID        riid,
      IUnknown    **ppReferencedRowset);

   HRESULT GetSpecification (
      REFIID        riid,
      IUnknown **   ppSpecification);
};

interface IRowsetLocate : IRowset {
   HRESULT Compare (
      HCHAPTER       hChapter,
      DBBKMARK       cbBookmark1,
      const BYTE    *pBookmark1,
      DBBKMARK       cbBookmark2,
      const BYTE    *pBookmark2,
      DBCOMPARE     *pComparison);

   HRESULT GetRowsAt (
      HWATCHREGION   hReserved1,
      HCHAPTER       hChapter,
      DBBKMARK       cbBookmark,
      const BYTE    *pBookmark,
      DBROWOFFSET    lRowsOffset,
      DBROWCOUNT     cRows,
      DBCOUNTITEM   *pcRowsObtained,
      HROW         **prghRows);

   HRESULT GetRowsByBookmark (
      HCHAPTER       hChapter,
      DBCOUNTITEM    cRows,
      const DBBKMARK rgcbBookmarks[],
      const BYTE    *rgpBookmarks[],
      HROW           rghRows[],
      DBROWSTATUS    rgRowStatus[]);

   HRESULT Hash (
      HCHAPTER       hChapter,
      DBBKMARK       cBookmarks,
      const DBBKMARK rgcbBookmarks[],
      const BYTE    *rgpBookmarks[],
      DBHASHVALUE    rgHashedValues[],
      DBROWSTATUS    rgBookmarkStatus[]);
};

interface IRowsetNotify {
   HRESULT OnFieldChange (
      IRowset       *pRowset,
      HROW           hRow,
      DBORDINAL      cColumns,
      DBORDINAL      rgColumns[],
      DBREASON       eReason,
      DBEVENTPHASE   ePhase,
      BOOL           fCantDeny);

   HRESULT OnRowChange (
      IRowset       *pRowset,
      DBCOUNTITEM    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,
      DBCOUNTITEM      cRows,
      const HROW       rghRows[],
      BOOL             fOverwrite,
      DBCOUNTITEM     *pcRowsRefreshed,
      HROW           **prghRowsRefreshed,
      DBROWSTATUS    **prgRowStatus);
};

interface IRowsetResynch {
HRESULT GetVisibleData
   (HROW       hRow,
   HACCESSOR   hAccessor,
   void *      pData);

HRESULT ResynchRows (
   DBCOUNTITEM      cRows,
   const HROW       rghRows[],
   ULONG           *pcRowsResynched,
   HROW           **prghRowsResynched,
   DBROWSTATUS    **prgRowStatus);
};

interface IRowsetScroll : IRowsetLocate {
   HRESULT GetApproximatePosition (
      HCHAPTER       hChapter,
      DBBKMARK       cbBookmark,
      const BYTE    *pBookmark,
      DBCOUNTITEM   *pulPosition,
      DBCOUNTITEM   *pcRows);

   HRESULT GetRowsAtRatio (
      HWATCHREGION   hReserved1,
      HCHAPTER       hChapter,
      DBCOUNTITEM    ulNumerator,
      DBCOUNTITEM    ulDenominator,
      DBROWCOUNT     cRows,
      DBCOUNTITEM   *pcRowsObtained,
      HROW         **prghRows);
};

interface IRowsetUpdate : IRowsetChange {
   HRESULT GetOriginalData (
      HROW        hRow,
      HACCESSOR   hAccessor,
      void *      pData);

   HRESULT GetPendingRows (
      HCHAPTER             hReserved,
      DBPENDINGSTATUS      dwRowStatus,
      DBCOUNTITEM         *pcPendingRows,
      HROW               **prgPendingRows,
      DBPENDINGSTATUS    **prgPendingStatus);

   HRESULT GetRowStatus(
      HCHAPTER          hChapter,
      DBCOUNTITEM       cRows,
      const HROW        rghRows[],
      DBPENDINGSTATUS   rgPendingStatus[]);

   HRESULT Undo (
      HCHAPTER         hReserved,
      DBCOUNTITEM      cRows,
      const HROW       rghRows[],
      DBCOUNTITEM     *pcRows,
      HROW           **prgRows,
      DBROWSTATUS    **prgRowStatus);

   HRESULT Update (
      HCHAPTER        hReserved,
      DBCOUNTITEM     cRows,
      const HROW      rghRows[],
      DBCOUNTITEM    *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 IScopedOperations {
   HRESULT Copy (
      DBCOUNTITEM       cRows,
      LPCOLESTR         rgpwszSourceURLs[ ],
      LPCOLESTR         rgpwszDestURLs[ ],
      DBCOPYFLAGS       dwCopyFlags,
      IAuthenticate    *pAuthenticate,
      DBSTATUS          rgdwStatus[ ],
      LPOLESTR          rgpwszNewURLs[ ],
      OLECHAR         **ppStringsBuffer);

   HRESULT Delete (
      DBCOUNTITEM     cRows,
      LPCOLESTR       rgpwszURLs[ ],
      DBDELETEFLAGS   dwDeleteFlags,
      DBSTATUS        rgdwStatus[ ]);

   HRESULT Move (
      DBCOUNTITEM       cRows,
      LPCOLESTR         rgpwszSourceURLs[ ],
      LPCOLESTR         rgpwszDestURLs[ ],
      DBMOVEFLAGS       dwMoveFlags,
      IAuthenticate    *pAuthenticate,
      DBSTATUS          rgdwStatus[ ],
      LPOLESTR          rgpwszNewURLs[ ],
      OLECHAR         **ppStringsBuffer);

   HRESULT OpenRowset (
      IUnknown *    pUnkOuter,
      DBID *        pTableID,
      DBID *        pIndexID,
      REFIID        riid,
      ULONG         cPropertySets,
      DBPROPSET     rgPropertySets[ ],
      IUnknown **   ppRowset);
};

interface ISessionProperties {
HRESULT Read(
   void *    pv,
   ULONG     cb,
   ULONG *   pcbRead);

HRESULT Write(
   Void const *   pv,
   ULONG          cb,
   ULONG *        pcbWritten);
};

interface ISequentialStream {
   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 ITableCreation {
   HRESULT GetTableDefinition(
      DBID               *pTableID,
      DBORDINAL          *pcColumnDescs,
      DBCOLUMNDESC       *prgColumnDescs[],
      ULONG              *pcPropertySets,
      DBPROPSET          *prgPropertySets[],
      ULONG              *pcConstraintDescs,
      DBCONSTRAINTDESC   *prgConstraintDescs[],
      OLECHAR           **ppwszStringBuffer);
};

interface ITableDefinition {
   HRESULT AddColumn(
      DBID *           pTableID,
      DBCOLUMNDESC *   pColumnDesc,
      DBID **          ppColumnID);

   HRESULT CreateTable(
      IUnknown            *pUnkOuter,
      DBID                *pTableID,
      DBORDINAL            cColumnDescs,
      const DBCOLUMNDESC   rgColumnDescs[],
      REFIID               riid,
      ULONG                cPropertySets,
      DBPROPSET            rgPropertySets[],
      DBID               **ppTableID,
      IUnknown           **ppRowset);

   HRESULT DropColumn(
      DBID *   pTableID,
      DBID *   pColumnID);

   HRESULT DropTable (
      DBID *   pTableID);
};

interface ITableDefinitionWithConstraints {
HRESULT AddConstraint(
   DBID *               pTableID,
   DBCONSTRAINTDESC *   pConstraintDesc);

   HRESULT CreateTableWithConstraints(
      IUnknown             *pUnkOuter,
      DBID                 *pTableID,
      DBORDINAL             cColumnDescs,
      const DBCOLUMNDESC    rgColumnDescs[],
      ULONG                 cConstraintDescs,
      DBCONSTRAINTDESC      rgConstraintDescs[],
      REFIID                riid,
      ULONG                 cPropertySets,
      DBPROPSET             rgPropertySets[],
      DBID                **ppTableID,
      IUnknown            **ppRowset);

HRESULT DropConstraint(
   DBID * pTableID,
   DBID * pConstraintID);
};

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,
      DBCOUNTITEM    *pcRows,
      DBCOMPAREOP    *pCompareOps[],
      void           *pCriteriaData);

   HRESULT GetFilterBindings(
      DBCOUNTITEM   *pcBindings,
      DBBINDING    **prgBindings);

   HRESULT SetFilter (
      HACCESSOR     hAccessor,
      DBCOUNTITEM   cRows,
      DBCOMPAREOP   CompareOps[],
      void         *pCriteriaData);
};

interface IViewRowset {
   HRESULT GetSpecification (
      REFIID        riid,
      IUnknown **   ppObject);

   HRESULT OpenViewRowset (
      IUnknown *    pUnkOuter,
      REFIID        riid,
      IUnknown **   ppRowset);
};

interface IViewSort {
   HRESULT GetSortOrder (
      DBORDINAL   *pcColumns,
      DBORDINAL   *prgColumns[],
      DBSORT      *prgOrders[]);

   HRESULT SetSortOrder (
      DBORDINAL         cColumns,
      const DBORDINAL   rgColumns[],
      const DBSORT      rgOrders[]);
};


Appendix F: Sample OLE DB Consumer Application

This sample demonstrates the rowset programming and object model for an OLE DB consumer. It creates data source, session, and rowset objects; allows the user to display and navigate the rows in the rowset; and handles errors, if any. Command line switches determine whether an enumerator, class ID, user prompt, or connection string is used to create the data source object, whether or not a command is used to create the rowset, and so on.

Sample OLE DB Consumer Application Source

Source files for the Sample OLE DB Consumer Application are located in the PRSample directory where the OLE DB samples are installed, and consist of the following files:

Source and Project Files

command.cpp | prsample.h
datasource.cpp | prsample.mak
enum.cpp | prsample.rc
error.cpp | resource.h
main.cpp | rowset.cpp
prsample.dsp | session.cpp

Summary of Routines

The table below shows the structure of the routines in the sample. The # column is the ordinal number of the routine named in the Routine column; the Routine column gives a brief description of what the routine does. Some routines contain subroutines, which are shown indented within their containing routine. The ordinal number of the called subroutine is listed in the Calls column. Click any routine name in this table to display the actual routine.

Within a routine, click the "Summary of Routines" link in the block comment to display the following table. Click any linked interface name to display the interface topic in the OLE DB Programmer's Reference. Click any subroutine name to display the actual routine.

# Calls Routine
0 | Preprocessor statements and prototypes—Necessary preprocessor definitions and declarations.
1 | main—Begin the sample consumer application.
2
myParseCommandLine
3
myDisplayInstructions
7
myCreateDataSource
11
myCreateSession
13
myCreateRowset
16
myDisplayRowset
2 | myParseCommandLine—Parses command line switches that control whether the sample creates the data source object with the Data Link UI or uses an enumerator and connection string; whether or not a command creates the rowset; whether to fetch a BLOB; and whether to display method call strings when reporting errors.
3 | myDisplayInstructions—Displays instructions on how to use the sample based on the command line switches.
5
myGetChar
4 | myGetInputFromUser—Prompts the user for variable input, such as command text.
5 | myGetChar—Gets a character from the keyboard.
6 | myCreateEnumerator—Enumerates the providers on the user's machine and allows the user to select one.
8
myDoInitialization
21
myAddRowsetProperties
16
myDisplayRowset
4
myGetInputFromUser
7 | myCreateDataSource—Creates a data source object, based on the user's previous specifications.
6
myCreateEnumerator
8
myDoInitialization
8 | myDoInitialization—Sets the provider initialization properties and then initializes the data source object.
10
myAddProperty
9 | myGetProperty—Gets the value of the specified property.
10 | myAddProperty—Adds a property to an array of properties.
11 | myCreateSession—Creates a session object from a data source object.
12 | myCreateSchemaRowset—Obtains the names of, and allows the user to select, tables supported by a provider from a table schema rowset.
21
myAddRowsetProperties
16
myDisplayRowset
13 | myCreateRowset—Creates a rowset from a default table name, command, or user-specified table name.
12
myCreateSchemaRowset
21
myAddRowsetProperties
24
myCreateCommand
4
myGetInputFromUser
25
myExecuteCommand
14 | mySetupBindings—Uses the columns of the obtained rowset to specify an array of bindings. Bindings specify how data in columns should optionally be converted and stored in user variables.
9
myGetProperty
15 | myCreateAccessor—Creates an accessor, which specifies the bindings to use to fetch data from the rowset.
14
mySetupBindings
16 | myDisplayRowset—Displays data from a rowset and performs basic rowset navigation.
9
myGetProperty
23
myFindColumn
15
myCreateAccessor
22
myUpdateDisplaySize
18
myDisplayColumnNames
19
myDisplayRow
17
myInteractWithRowset
20
myFreeBindings
17 | myInteractWithRowset—Prompts the user for rowset navigation directives (next, previous, and restart); allows the user to select a row; exits the sample program upon request.
5
myGetChar
18 | myDisplayColumnNames—Displays rowset column names.
19 | myDisplayRow—Displays the data in a row of the rowset.
20 | myFreeBindings—Frees memory allocated for bindings.
21 | myAddRowsetProperties—Specifies optional properties that identify features the rowset should support.
10
myAddProperty
22 | myUpdateDisplaySize—Manages the size of the column displayed.
23 | myFindColumn—Find the specified index column.
24 | myCreateCommand—Creates a command object, if possible.
25 | myExecuteCommand—Sets properties on a command object, executes the command, and returns a rowset object.
26 | myHandleResult—Handles and displays error information.
27
myDisplayErrorRecord
28
myDisplayErrorInfo
27 | myDisplayErrorRecord—Displays error information for a single error record.
29
myGetSqlErrorInfo
28 | myDisplayErrorInfo—Displays basic error information for an error object that doesn't support error records.
29 | myGetSqlErrorInfo—Displays, if possible, SQL and native error information.

Sample OLE DB Consumer Application Routines

//---------------------------------------------------------------------------
// Microsoft® OLE DB Programmer's Reference Sample.
// Copyright (C) 1998 By Microsoft® Corporation.
//
// @doc
//
// @module PRSAMPLE.H
//
//---------------------------------------------------------------------------

/////////////////////////////////////////////////////////////////
// Includes
// Summary of Routines
//
/////////////////////////////////////////////////////////////////
#ifndef __PRSAMPLE_H__
#define __PRSAMPLE_H__

#include "oledb.h"         // OLE DB Header
#include "oledberr.h"      // OLE DB Errors

#include "msdasc.h"        // OLE DB Service Component header
#include "msdaguid.h"      // OLE DB Root Enumerator
#include "msdasql.h"       // MSDASQL - Default provider

#include <stdio.h>         // input and output functions
#include <conio.h>         // getch, putch
#include <locale.h>        // setlocale

/////////////////////////////////////////////////////////////////
// Globals
//
/////////////////////////////////////////////////////////////////
extern DWORD g_dwFlags;

/////////////////////////////////////////////////////////////////
// Defines
//
/////////////////////////////////////////////////////////////////
#define __LONGSTRING(string) L##string
#define LONGSTRING(string) __LONGSTRING(string)

// Goes to CLEANUP on Failure_
#define CHECK_HR(hr)      \
   if(FAILED(hr))         \
      goto CLEANUP

// Goes to CLEANUP on Failure, and displays any ErrorInfo
#define XCHECK_HR(hr)                                               \
{                                                                   \
   if( g_dwFlags & DISPLAY_METHODCALLS )                            \
      fwprintf(stderr, LONGSTRING(#hr) L"\n");                      \
   if(FAILED(myHandleResult(hr, LONGSTRING(__FILE__), __LINE__)))   \
      goto CLEANUP;                                                 \
}
#define CHECK_MEMORY(hr, pv)   \
{                              \
   if(!pv)                     \
   {                           \
      hr = E_OUTOFMEMORY;      \
      CHECK_HR(hr);            \
   }                           \
}

#define MAX_COL_SIZE         5000
#define MAX_NAME_LEN         256

#define MAX_ROWS              10
#define MAX_DISPLAY_SIZE      20
#define MIN_DISPLAY_SIZE      3

// ROUNDUP on all platforms pointers must be aligned properly
#define ROUNDUP_AMOUNT            8
#define ROUNDUP_(size,amount) (((ULONG)(size)+((amount)-1))&~((amount)-1))
#define ROUNDUP(size)             ROUNDUP_(size, ROUNDUP_AMOUNT)

enum
{
   // Connecting
   USE_PROMPTDATASOURCE      = 0x0001,
   USE_ENUMERATOR            = 0x0002,

   // Rowset
   USE_COMMAND               = 0x0010,

   // Storage objects
   USE_ISEQSTREAM            = 0x0100,

   // Display options
   DISPLAY_METHODCALLS       = 0x1000,
   DISPLAY_INSTRUCTIONS      = 0x2000,
};

/////////////////////////////////////////////////////////////////////////////
// Function prototypes
//
/////////////////////////////////////////////////////////////////////////////
// Main
BOOL   myParseCommandLine();
void   myDisplayInstructions();
BOOL   myGetInputFromUser(LPWSTR pwszInput, LPCWSTR pwszFmt, ...);
CHAR   myGetChar();

// Enumerator
HRESULT myCreateEnumerator(REFCLSID clsidEnumerator, CLSID* pCLSID);

// Data source
HRESULT myCreateDataSource(IUnknown** ppUnkDataSource);
HRESULT myDoInitialization(IUnknown* pIUnknown);
HRESULT myGetProperty(IUnknown* pIUnknown, REFIID riid, DBPROPID
                      dwPropertyID, REFGUID guidPropertySet, BOOL*
                      pbValue);
void myAddProperty(DBPROP* pProp, DBPROPID dwPropertyID, VARTYPE vtType =
                   VT_BOOL, LONG lValue = VARIANT_TRUE, DBPROPOPTIONS
                   dwOptions =    DBPROPOPTIONS_OPTIONAL);

// Session
HRESULT myCreateSession(IUnknown* pUnkDataSource, IUnknown**
                        ppUnkSession);
HRESULT myCreateSchemaRowset(GUID guidSchema, IUnknown* pUnkSession,
                             ULONG cchBuffer, LPWSTR pwszBuffer);

// Command
HRESULT myCreateCommand(IUnknown* pUnkSession, IUnknown** ppUnkCommand);
HRESULT myExecuteCommand(IUnknown* pUnkCommand, WCHAR* pwszCommandText,
                         ULONG cPropSets, DBPROPSET* rgPropSets,
                         IUnknown** ppUnkRowset);

// Rowset
HRESULT myCreateRowset(IUnknown* pUnkSession, IUnknown** ppUnkRowset);
HRESULT mySetupBindings(IUnknown* pUnkRowset, ULONG* pcBindings,
                        DBBINDING** prgBindings, ULONG* pcbRowSize);
HRESULT myCreateAccessor(IUnknown* pUnkRowset, HACCESSOR* phAccessor,
                         ULONG* pcBindings, DBBINDING** prgBindings,
                         ULONG* pcbRowSize);

HRESULT myDisplayRowset(IUnknown* pUnkRowset, LPCWSTR pwszColToReturn,
                        ULONG cchBuffer, LPWSTR pwszBuffer);
HRESULT myDisplayColumnNames(IUnknown* pUnkRowset, ULONG* rgDispSize);
HRESULT myDisplayRow(ULONG iRow, ULONG cBindings, DBBINDING* rgBindings,
                     void* pData, ULONG * rgDispSize);

HRESULT myInteractWithRowset(IRowset* pIRowset, LONG* pcRows, ULONG
                             cRowsObtained, BOOL fCanFetchBackwards,
                             void* pData, ULONG cbRowSize, DBBINDING*
                             pBinding, ULONG cchBuffer, LPWSTR
                             pwszBuffer);
HRESULT myFindColumn(IUnknown * pUnkRowset, LPCWSTR pwszName, LONG*
                     plIndex);
HRESULT myUpdateDisplaySize(ULONG cBindings, DBBINDING* rgBindings, void*
                            pData, ULONG* rgDispSize);
void myFreeBindings(ULONG cBindings, DBBINDING* rgBindings);
void myAddRowsetProperties(DBPROPSET* pPropSet, ULONG cProperties,
                           DBPROP* rgProperties);

// Error
HRESULT myHandleResult(HRESULT hrReturned, LPCWSTR pwszFile, ULONG
                       ulLine);
HRESULT myDisplayErrorRecord(HRESULT hrReturned, ULONG iRecord,
                             IErrorRecords* pIErrorRecords, LPCWSTR
                             pwszFile, ULONG ulLine);
HRESULT myDisplayErrorInfo(HRESULT hrReturned, IErrorInfo* pIErrorInfo,
                           LPCWSTR pwszFile, ULONG ulLine);
HRESULT myGetSqlErrorInfo(ULONG iRecord, IErrorRecords* pIErrorRecords,
                          BSTR* pBstr, LONG* plNativeError);

#endif   // __PRSAMPLE_H__

//---------------------------------------------------------------------------
// Microsoft OLE DB Programmer's Reference Sample.
// Copyright (C) 1998 By Microsoft Corporation.
//
// @doc
//
// @module MAIN.CPP
//
//---------------------------------------------------------------------------

/////////////////////////////////////////////////////////////////
// Includes
//
/////////////////////////////////////////////////////////////////
#define DBINITCONSTANTS   // Store all OLE DB consts inside
                          // this .obj file.
#include "prsample.h"     // Programmer's Reference Sample includes

/////////////////////////////////////////////////////////////////
// Globals
//
/////////////////////////////////////////////////////////////////
DWORD g_dwFlags = USE_PROMPTDATASOURCE | DISPLAY_METHODCALLS;

/////////////////////////////////////////////////////////////////
// main
// Summary of Routines
//
// This is a simple OLE DB application that will display a
// rowset and will allow basic navigation of that rowset by the
// user.
//
// In the sample, functions that begin with 'my' are implemented
// in the sample code; all other functions are either OLE DB
// methods or standard system methods. In addition, two
// macros are used repeatedly throughout the sample:
//  - CHECK_HR(hr) - this macro goes to the CLEANUP label if
//     FAILED(hr), where hr is usually a method call.
//  - XCHECK_HR(hr) - this macro prints the string
//     representation of hr to stderr and if FAILED(hr), attempts
//     to obtain and display any extended error information
//     posted by the last method call and then jumps to the CLEANUP
//     label.
//
// This is the entry point for the sample. This function will:
//  - parse command line arguments passed to the sample.
//  - display appropriate instructions based on these arguments.
//  - create an OLE DB data source object for a user-chosen
//     provider.
//  - create an OLE DB session object from the provider's
//     data source object.
//  - create an OLE DB rowset object, over a table specified by
//     the user, from the provider's session object.
//  - display the rowset data and will allow the user to
//     navigate over the rowset.
//
/////////////////////////////////////////////////////////////////
int main()
{
   HRESULT      hr;
   IUnknown *   pUnkDataSource   = NULL;
   IUnknown *   pUnkSession      = NULL;
   IUnknown *   pUnkRowset       = NULL;

   // Parse command line arguments, if any; this will update
   // the value of g_dwFlags as appropriate for the arguments.
   if( !myParseCommandLine() )
      return EXIT_FAILURE;

   // Display instructions for the given command line arguments.
   myDisplayInstructions();

   // Initialize OLE
   hr = CoInitialize(NULL);
   if( FAILED(hr) )
      return EXIT_FAILURE;

   // Create the data source object using the OLE DB service components.
   CHECK_HR(hr = myCreateDataSource(&pUnkDataSource));

   // Create a session object from the data source object.
   CHECK_HR(hr = myCreateSession(pUnkDataSource, &pUnkSession));

   // Create a rowset object from the session object, either directly
   // from the session or through a command object.
   CHECK_HR(hr = myCreateRowset(pUnkSession, &pUnkRowset));

   // Display the rowset object data to the user.
   CHECK_HR(hr = myDisplayRowset(pUnkRowset, NULL, 0, NULL));

CLEANUP:
   if( pUnkRowset )
      pUnkRowset->Release();
   if( pUnkSession )
      pUnkSession->Release();
   if( pUnkDataSource )
      pUnkDataSource->Release();

   CoUninitialize();

   if( FAILED(hr) )
      return EXIT_FAILURE;

   return EXIT_SUCCESS;
}

/////////////////////////////////////////////////////////////////
// myParseCommandLine
// Summary of Routines
//
// This function parses the application's command line arguments
// and sets the appropriate bits in g_dwFlags. If an invalid
// argument is encountered, a usage message is displayed and
// the function returns FALSE; otherwise, TRUE is returned.
//
/////////////////////////////////////////////////////////////////
BOOL myParseCommandLine
   (
   void
   )
{
   int      iArg;
   CHAR *   psz;

   // Set the locale for all C run-time functions.
   setlocale(LC_ALL, ".ACP");

   // Go through each command line argument and set the appropriate
   // bits in g_dwFlags, depending on the chosen options.
   for( iArg = 1; iArg < __argc; iArg++ )
   {
      // Inspect the current argument string.
      psz = __argv[iArg];

      // Valid options begin with '-' or '/'.
      if( psz[0] == '-' || psz[0] == '/' )
      {
         // The next character is the option.
         switch( tolower(psz[1]) )
         {
         case 'u':
            // Use the service components UI to prompt for and create
            // the data source object; the enumerator is not used.
            g_dwFlags |= USE_PROMPTDATASOURCE;
            g_dwFlags &= ~USE_ENUMERATOR;
            continue;
         case 'e':
            // Use the enumerator to select the provider, and then use
            // IDataInitialize to create the data source object.
            // Don't use the UI to prompt for the data source.
            g_dwFlags |= USE_ENUMERATOR;
            g_dwFlags &= ~USE_PROMPTDATASOURCE;
            continue;
         case 'c':
            // Use ICommand instead of IOpenRowset.
            g_dwFlags |= USE_COMMAND;
            continue;
         case 'b':
            // Use ISequentialStream to fetch BLOB column data.
            g_dwFlags |= USE_ISEQSTREAM;
            continue;
         case 'n':
            // Don't display method call strings as part of
            // the extended error checking macro.
            g_dwFlags &= ~DISPLAY_METHODCALLS;
            continue;
         }
      }

      // Invalid argument; show the usage flags to the user.
      fprintf(stderr, "Usage: %s [-u] [-e] [-c] [-b] [-n]\n\nWhere:\n\t" \
         "u = Use the Microsoft Data Links UI " \
            "to create the DataSource\n\t" \
         "e = Use the Enumerator and IDataInitialize " \
            "to create the DataSource\n\t" \
         "c = Use ICommand instead of IOpenRowset to create the Rowset\n\t" \
         "b = Use ISequentialStream for BLOB columns\n\t" \
         "n = Don't display method call strings\n",
         __argv[0]);

      return FALSE;
   }

   return TRUE;
}

/////////////////////////////////////////////////////////////////
// myDisplayInstructions
// Summary of Routines
//
// This function asks the user whether they would like
// instructions displayed for the application. If so, it
// displays the instructions appropriate to the flags set
// in g_dwFlags.
//
/////////////////////////////////////////////////////////////////
void myDisplayInstructions
   (
   void
   )
{
   CHAR ch;

   // Display header and ask the user if they want instructions.
   printf("\nOLE DB Programmer's Reference Sample\n" \
          "====================================\n\n");
   printf("Display instructions [Y or N]? ");
   do
   {
      ch = myGetChar();
   }
   while( ch != 'y' && ch != 'n' );
   printf("%c\n\n", ch);

   // No instructions, so we're done.
   if( ch == 'n' )
      return;

   // Display basic instructions.
   printf("This application is a simple OLE DB sample that will display\n" \
         "a rowset and will allow basic navigation of that rowset by\n" \
         "the user. The application will perform the following\n" \
         "steps:\n\n");

   // Display data source creation instructions.
   if( g_dwFlags & USE_PROMPTDATASOURCE )
   {
      printf(" - Creates a data source object through the Microsoft
             Data\n" \ "   Links UI. This allows the user to select
             the OLE DB\n" \ "   provider to use and to set connection
             properties.\n");
   }
   else
   {
      printf(" - Creates a data source object through
             IDataInitialize::\n" \
             "   CreateDBInstance, which allows the OLE DB service\n" \
             "   component manager to add additional functionality to\n"\
             "   the provider as requested. The user will select the\n" \
             "   provider to use from a rowset obtained from the OLE
             DB\n" \ "   enumerator.\n");
   }

   // Display session creation and table-selection instructions.
   printf(" - Creates a session object from the data source object.\n");
   printf(" - If the provider supports the schema rowset interface,\n" \
          "   creates a TABLES schema rowset and allows the user to\n" \
          "   select a table name from this rowset.\n");

   // Display rowset creation instructions
   if( g_dwFlags & USE_COMMAND )
   {
      printf(" - Creates a cxommand object from the session object and\n" \
            "   allows the user to specify command text for this Command,\n" \
            "   then executes the command to create the final rowset.\n");
   }
   else
   {
      printf(" - Creates the final rowset over the table specified by the\n" \
            "   user.\n");
   }

   printf(" - Displays this rowset and allows the user to perform basic\n" \
         "   navigation of that rowset.\n\n");

   // Wait for the user to press a key before continuing.
   printf("Press a key to continue...");
   myGetChar();
   printf("\n\n");
}

/////////////////////////////////////////////////////////////////
// myGetInputFromUser
// Summary of Routines
//
// This function prompts the user with the contents of pwszFmt
// and any accompanying variable arguments and then gets a string
// as input from the user. If the string is non-empty, it is
// copied into pwszInput and the function returns TRUE;
// otherwise, this function returns FALSE.
//
/////////////////////////////////////////////////////////////////
BOOL myGetInputFromUser
   (
   LPWSTR    pwszInput,
   LPCWSTR   pwszFmt,
   ...
   )
{
   va_list   vargs;
   WCHAR     wszBuffer[MAX_NAME_LEN + 1]   = {0};

   // Create the string with variable arguments...
   va_start(vargs, pwszFmt);
   _vsnwprintf(wszBuffer, MAX_NAME_LEN, pwszFmt, vargs);
   va_end(vargs);

   // Output the string...
   wprintf(wszBuffer);

   // Now get the Input from the user....
   _getws(wszBuffer);
   if( wszBuffer[0] )
   {
      wcscpy(pwszInput, wszBuffer);
      return TRUE;
   }

   return FALSE;
}

/////////////////////////////////////////////////////////////////
// myGetChar
// Summary of Routines
//
// This function gets a character from the keyboard and
// converts it to lowercase before returning it.
//
/////////////////////////////////////////////////////////////////
CHAR myGetChar
   (
   void
   )
{
   CHAR ch;

   // Get a character from the keyboard.
   ch = _getch();

   // Re-read for the actual key value if necessary.
   if( !ch || ch == 0xE0 )
      ch = _getch();

   return tolower(ch);
}

//---------------------------------------------------------------------------
// Microsoft OLE DB Programmer's Reference Sample.
// Copyright (C) 1998 By Microsoft Corporation.
//
// @doc
//
// @module ENUM.CPP
//
//---------------------------------------------------------------------------

/////////////////////////////////////////////////////////////////
// Includes
//
/////////////////////////////////////////////////////////////////
#include "prsample.h"      // Programmer's Reference Sample includes

/////////////////////////////////////////////////////////////////
// myCreateEnumerator
// Summary of Routines
//
// This function creates an enumerator, obtains a sources rowset
// from it, displays the rowset to the user, and allows the user
// to specify the ProgID of a provider. The CLSID that matches
// this ProgID is retuned to the caller in *pCLSID.
//
/////////////////////////////////////////////////////////////////
HRESULT myCreateEnumerator
   (
   REFCLSID           clsidEnumerator,
   CLSID *            pCLSID
   )
{
   HRESULT            hr;
   IUnknown *         pIUnkEnumerator               = NULL;
   ISourcesRowset *   pISourcesRowset               = NULL;
   IRowset *          pIRowset                      = NULL;
   IDBInitialize *    pIDBInitialize                = NULL;
   WCHAR              wszProgID[MAX_NAME_LEN + 1]   = {0};

   const ULONG        cProperties                   = 2;
   DBPROP             rgProperties[cProperties];
   DBPROPSET          rgPropSets[1];

   // Create the enumerator object. We ask for IUnknown when creating
   // the enumerator because some enumerators may require initialization
   // before we can obtain a sources rowset from the enumerator. This is
   // indicated by whether the enumerator object exposes IDBInitialize
   // or not. (We don't want to ask for IDBInitialize, since enumerators
   // that don't require initialization will cause the CoCreateInstance
   // to fail.)
   XCHECK_HR(hr = CoCreateInstance(
            clsidEnumerator,                    // clsid -- enumerator
            NULL,                               // pUnkOuter
            CLSCTX_INPROC_SERVER,               // dwClsContext
            IID_IUnknown,                       // riid
            (void**)&pIUnkEnumerator            // ppvObj
            ));

   // If the enumerator exposes IDBInitialize, we need to initialize it.
   // See IDBInitialize
   if( SUCCEEDED(hr = pIUnkEnumerator->QueryInterface(IID_IDBInitialize,
            (void**)&pIDBInitialize)) )
   {
      CHECK_HR(hr = myDoInitialization(pIUnkEnumerator));
   }

   // Set properties on the rowset, to request additional functionality.
   myAddRowsetProperties(rgPropSets, cProperties, rgProperties);

   // Obtain a sources rowset from the enumerator. This rowset contains
   // all of the OLE DB providers that this enumerator is able to list.
   // See ISourcesRowset and IRowset
   XCHECK_HR(hr = pIUnkEnumerator->QueryInterface(IID_ISourcesRowset,
            (void**)&pISourcesRowset));
   XCHECK_HR(hr = pISourcesRowset->GetSourcesRowset(
            NULL,                               // pUnkOuter
            IID_IRowset,                        // riid
            1,                                  // cPropSets
            rgPropSets,                         // rgPropSets
            (IUnknown**)&pIRowset               // ppRowset
            ));

   // Display the rowset to the user. This will allow the user to
   // perform basic navigation of the rowset and will allow the user
   // to select a row containing a desired provider.
   CHECK_HR(hr = myDisplayRowset(pIRowset,
            L"SOURCES_NAME", MAX_NAME_LEN, wszProgID));

   // Obtain the ProgID for the provider to use from the user.
   // The default value for this is the value of the SOURCES_NAME
   // column in the row selected by the user previously.
   myGetInputFromUser(wszProgID, L"\nType the ProgID of a provider"
            L" to use [Enter = `%s`]: ", wszProgID);
   XCHECK_HR(hr = CLSIDFromProgID(wszProgID, pCLSID));

CLEANUP:
   if( pIUnkEnumerator )
      pIUnkEnumerator->Release();
   if( pISourcesRowset )
      pISourcesRowset->Release();
   if( pIRowset )
      pIRowset->Release();
   if( pIDBInitialize )
      pIDBInitialize->Release();
   return hr;
}

//---------------------------------------------------------------------------
// Microsoft OLE DB Programmer's Reference Sample.
// Copyright (C) 1998 By Microsoft Corporation.
//
// @doc
//
// @module DATASOURCE.CPP
//
//---------------------------------------------------------------------------

/////////////////////////////////////////////////////////////////
// Includes
//
/////////////////////////////////////////////////////////////////
#include "prsample.h"      // Programmer's Reference Sample includes

/////////////////////////////////////////////////////////////////
// myCreateDataSource
// Summary of Routines
//
// This function creates an OLE DB data source object for a
// provider selected by the user, sets initialization properties
// for the data source, and initializes the data source. The
// function returns a pointer to the data source object's
// IUnknown in *ppUnkDataSource.
//
/////////////////////////////////////////////////////////////////
HRESULT myCreateDataSource
   (
   IUnknown **             ppUnkDataSource
   )
{
   HRESULT                 hr;
   IDataInitialize *       pIDataInitialize       = NULL;
   IDBPromptInitialize *   pIDBPromptInitialize   = NULL;
   IDBInitialize *         pIDBInitialize         = NULL;
   CLSID                   clsid                  = CLSID_MSDASQL;

   // Use the Microsoft Data Links UI to create the data source
   // object. This will allow the user to select the provider
   // to connect to and to set the initialization properties
   // for the data source object, which will be created by the
   // Data Links UI.
   if( g_dwFlags & USE_PROMPTDATASOURCE )
   {
      // Create the Data Links UI object, and obtain the
      // IDBPromptInitialize interface from it.
      // See IDBPromptInitialize
      XCHECK_HR(hr = CoCreateInstance(
               CLSID_DataLinks,                 // clsid -- Data Links UI
               NULL,                            // pUnkOuter
               CLSCTX_INPROC_SERVER,            // dwClsContext
               IID_IDBPromptInitialize,         // riid
               (void**)&pIDBPromptInitialize    // ppvObj
               ));

      // Invoke the Data Links UI to allow the user to select
      // the provider and set initialization properties for
      // the data source object that this will create.
      // See IDBPromptInitialize and IDBInitialize
      XCHECK_HR(hr = pIDBPromptInitialize->PromptDataSource(
               NULL,                             // pUnkOuter
               GetDesktopWindow(),               // hWndParent
               DBPROMPTOPTIONS_PROPERTYSHEET,    // dwPromptOptions
               0,                                // cSourceTypeFilter
               NULL,                             // rgSourceTypeFilter
               NULL,                             // pwszszzProviderFilter
               IID_IDBInitialize,                // riid
               (IUnknown**)&pIDBInitialize       // ppDataSource
               ));

      // We've obtained a data source object from the Data Links UI. This
      // object has had its initialization properties set, so all we
      // need to do is Initialize it.
      XCHECK_HR(hr = pIDBInitialize->Initialize());
   }
   // We are not using the Data Links UI to create the data source
   // object. Instead, we will enumerate the providers installed on this
   // system through the OLE DB enumerator and will allow the user to
   // select the ProgID of the provider for which we will create a
   // data source object.
   else
   {
      // Use the OLE DB enumerator to obtain a rowset of installed
      // providers, and then allow the user to select a provider from
      // this rowset.
      CHECK_HR(hr = myCreateEnumerator(CLSID_OLEDB_ENUMERATOR, &clsid));

      // We will create the data source object through the OLE DB service
      // component IDataInitialize interface, so we need to create an
      // instance of the data initialization object.
      // See IDataInitialize
      XCHECK_HR(hr = CoCreateInstance(
               CLSID_MSDAINITIALIZE,          // clsid -- data initialize
               NULL,                          // pUnkOuter
               CLSCTX_INPROC_SERVER,          // dwClsContext
               IID_IDataInitialize,           // riid
               (void**)&pIDataInitialize      // ppvObj
               ));

      // Use IDataInitialize::CreateDBInstance to create an uninitialized
      // data source object for the chosen provider. By using this
      // service component method, the service component manager can
      // provide additional functionality beyond what is natively
      // supported by the provider if the consumer requests that
      // functionality.
      // See IDBInitialize
      XCHECK_HR(hr = pIDataInitialize->CreateDBInstance(
               clsid,                                // clsid -- provider
               NULL,                                 // pUnkOuter
               CLSCTX_INPROC_SERVER,                 // dwClsContext
               NULL,                                 // pwszReserved
               IID_IDBInitialize,                    // riid
               (IUnknown**)&pIDBInitialize           // ppDataSource
               ));

      // Initialize the data source object by setting any required
      // initialization properties and calling IDBInitialize::Initialize.
      CHECK_HR(hr = myDoInitialization(pIDBInitialize));
   }

CLEANUP:
   *ppUnkDataSource = pIDBInitialize;
   if( pIDataInitialize )
      pIDataInitialize->Release();
   if( pIDBPromptInitialize )
      pIDBPromptInitialize->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////
// myDoInitialization
// Summary of Routines
//
// This function sets initialization properties that tell the
// provider to prompt the user for any information required to
// initialize the provider and then calls the provider's
// initialization function.
//
/////////////////////////////////////////////////////////////////
HRESULT myDoInitialization
   (
   IUnknown *        pIUnknown
   )
{
   HRESULT           hr;
   IDBInitialize *   pIDBInitialize   = NULL;
   IDBProperties *   pIDBProperties   = NULL;
   HWND              hWnd             = GetDesktopWindow();

   const ULONG       cProperties      = 2;
   DBPROP            rgProperties[cProperties];
   DBPROPSET         rgPropSets[1];

   // To initialize the data source object, most providers require
   // some initialization properties to be set by the consumer. For
   // example, these might include the data source to connect to and the
   // user ID and password to use to establish identity. We will ask the
   // provider to prompt the user for this required information by
   // setting the following properties:
   myAddProperty(&rgProperties[0],DBPROP_INIT_PROMPT,VT_I2,DBPROMPT_COMPLETE);
   myAddProperty(&rgProperties[1],DBPROP_INIT_HWND,  VT_I4,(LONG)hWnd);

   rgPropSets[0].rgProperties      = rgProperties;
   rgPropSets[0].cProperties       = cProperties;
   rgPropSets[0].guidPropertySet   = DBPROPSET_DBINIT;

   // Obtain the needed interfaces.
   // See IDBProperties and IDBInitialize
   XCHECK_HR(hr = pIUnknown->QueryInterface(IID_IDBProperties,
            (void**)&pIDBProperties));
   XCHECK_HR(hr = pIUnknown->QueryInterface(IID_IDBInitialize,
            (void**)&pIDBInitialize));

   // If a provider requires initialization properties, it must support
   // the properties that we are setting (_PROMPT and _HWND). However,
   // some providers do not need initialization properties and may
   // therefore not support the _PROMPT and _HWND properties. Because of
   // this, we will not check the return value from SetProperties.
   hr = pIDBProperties->SetProperties(1, rgPropSets);

   // Now that we've set our properties, initialize the provider.
   XCHECK_HR(hr = pIDBInitialize->Initialize());

CLEANUP:
   if( pIDBProperties )
      pIDBProperties->Release();
   if( pIDBInitialize )
      pIDBInitialize->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////
// myGetProperty
// Summary of Routines
//
// This function gets the BOOL value for the specified property
// and returns the result in *pbValue.
//
/////////////////////////////////////////////////////////////////
HRESULT myGetProperty
   (
   IUnknown *             pIUnknown,
   REFIID                 riid,
   DBPROPID               dwPropertyID,
   REFGUID                guidPropertySet,
   BOOL *                 pbValue
   )
{
   HRESULT                hr;
   DBPROPID               rgPropertyIDs[1];
   DBPROPIDSET            rgPropertyIDSets[1];

   ULONG                  cPropSets        = 0;
   DBPROPSET *            rgPropSets       = NULL;

   IDBProperties *        pIDBProperties   = NULL;
   ISessionProperties *   pISesProps       = NULL;
   ICommandProperties *   pICmdProps       = NULL;
   IRowsetInfo *          pIRowsetInfo     = NULL;

   // Initialize the output value
   *pbValue = FALSE;

   // Set up the property ID array
   rgPropertyIDs[0] = dwPropertyID;

   // Set up the Property ID Set
   rgPropertyIDSets[0].rgPropertyIDs     = rgPropertyIDs;
   rgPropertyIDSets[0].cPropertyIDs      = 1;
   rgPropertyIDSets[0].guidPropertySet   = guidPropertySet;

   // Get the property value for this property from the provider, but
   // don't try to display extended error information, since this may
   // not be a supported property. A failure is, in fact, expected if
   // the property is not supported.
   // See IDBProperties, ISessionProperties, ICommandProperties,
   // and IRowsetInfo
   if( riid == IID_IDBProperties )
   {
      XCHECK_HR(hr = pIUnknown->QueryInterface(IID_IDBProperties,
               (void**)&pIDBProperties));
      CHECK_HR(hr = pIDBProperties->GetProperties(
               1,                                 // cPropertyIDSets
               rgPropertyIDSets,                  // rgPropertyIDSets
               &cPropSets,                        // pcPropSets
               &rgPropSets                        // prgPropSets
               ));
   }
   else if( riid == IID_ISessionProperties )
   {
      XCHECK_HR(hr = pIUnknown->QueryInterface(IID_ISessionProperties,
               (void**)&pISesProps));
      CHECK_HR(hr = pISesProps->GetProperties(
               1,                                 // cPropertyIDSets
               rgPropertyIDSets,                  // rgPropertyIDSets
               &cPropSets,                        // pcPropSets
               &rgPropSets                        // prgPropSets
               ));
   }
   else if( riid == IID_ICommandProperties )
   {
      XCHECK_HR(hr = pIUnknown->QueryInterface(IID_ICommandProperties,
               (void**)&pICmdProps));
      CHECK_HR(hr = pICmdProps->GetProperties(
               1,                                 // cPropertyIDSets
               rgPropertyIDSets,                  // rgPropertyIDSets
               &cPropSets,                        // pcPropSets
               &rgPropSets                        // prgPropSets
               ));
   }
   else
   {
      XCHECK_HR(hr = pIUnknown->QueryInterface(IID_IRowsetInfo,
               (void**)&pIRowsetInfo));
      CHECK_HR(hr = pIRowsetInfo->GetProperties(
               1,                                 // cPropertyIDSets
               rgPropertyIDSets,                  // rgPropertyIDSets
               &cPropSets,                        // pcPropSets
               &rgPropSets                        // prgPropSets
               ));
   }

   // Return the value for this property to the caller if
   // it's a VT_BOOL type value, as expected.
   if( V_VT(&rgPropSets[0].rgProperties[0].vValue) == VT_BOOL )
      *pbValue = V_BOOL(&rgPropSets[0].rgProperties[0].vValue);

CLEANUP:
   if( rgPropSets )
   {
      CoTaskMemFree(rgPropSets[0].rgProperties);
      CoTaskMemFree(rgPropSets);
   }
   if( pIDBProperties )
      pIDBProperties->Release();
   if( pISesProps )
      pISesProps->Release();
   if( pICmdProps )
      pICmdProps->Release();
   if( pIRowsetInfo )
      pIRowsetInfo->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////
// myAddProperty
// Summary of Routines
//
// This function initializes the property structure pProp.
//
/////////////////////////////////////////////////////////////////
void myAddProperty
   (
   DBPROP *           pProp,
   DBPROPID           dwPropertyID,
   VARTYPE            vtType,
   LONG               lValue,
   DBPROPOPTIONS      dwOptions
   )
{
   // Set up the property structure.
   pProp->dwPropertyID    = dwPropertyID;
   pProp->dwOptions       = dwOptions;
   pProp->dwStatus        = DBPROPSTATUS_OK;
   pProp->colid           = DB_NULLID;
   V_VT(&pProp->vValue)   = vtType;

   // Since VARIANT data is a union, we can place the value in any
   // member (except for VT_DECIMAL, which is a union with the whole
   // VARIANT structure -- but we know we're not passing VT_DECIMAL).
   V_I4(&pProp->vValue)   = lValue;
}

//---------------------------------------------------------------------------
// Microsoft OLE DB Programmer's Reference Sample.
// Copyright (C) 1998 By Microsoft Corporation.
//
// @doc
//
// @module SESSION.CPP
//
//---------------------------------------------------------------------------

/////////////////////////////////////////////////////////////////
// Includes
//
/////////////////////////////////////////////////////////////////
#include "prsample.h"      // Programmer's Reference Sample includes

/////////////////////////////////////////////////////////////////
// myCreateSession
// Summary of Routines
//
// Create an OLE DB session object from the given data source
// object. The IDBCreateSession interface is mandatory, so this
// is a simple operation.
//
/////////////////////////////////////////////////////////////////
HRESULT   myCreateSession
   (
   IUnknown *           pUnkDataSource,
   IUnknown **          ppUnkSession
   )
{
   HRESULT              hr;
   IDBCreateSession *   pIDBCreateSession      = NULL;

   //Create a session object from a data source object
   // See IDBCreateSession and IOpenRowset
   XCHECK_HR(hr = pUnkDataSource->QueryInterface(
            IID_IDBCreateSession, (void**)&pIDBCreateSession));
   XCHECK_HR(hr = pIDBCreateSession->CreateSession(
            NULL,                                   // pUnkOuter
            IID_IOpenRowset,                        // riid
            ppUnkSession                            // ppSession
            ));

CLEANUP:
   if( pIDBCreateSession )
      pIDBCreateSession->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////
// myCreateSchemaRowset
// Summary of Routines
//
// If the provider supports IDBSchemaRowset, this function will
// obtain the tables schema rowset, will display this rowset to
// the user, and will allow the user to select a row in the
// rowset containing the name of a table of interest.
//
/////////////////////////////////////////////////////////////////
HRESULT   myCreateSchemaRowset
   (
   GUID                guidSchema,
   IUnknown *          pUnkSession,
   ULONG               cchBuffer,
   LPWSTR              pwszBuffer
   )
{
   HRESULT             hr                 = S_OK;
   IDBSchemaRowset *   pIDBSchemaRowset   = NULL;
   IUnknown *          pUnkRowset         = NULL;

   const ULONG         cProperties        = 2;
   DBPROP              rgProperties[cProperties];
   DBPROPSET           rgPropSets[1];

   // Attempt to obtain the IDBSchemaRowset interface on the session
   // object. This is not a mandatory interface; if it is not supported,
   // we are done.
   // See IDBSchemaRowset
   CHECK_HR(pUnkSession->QueryInterface(
               IID_IDBSchemaRowset, (void**)&pIDBSchemaRowset));

   // Set properties on the rowset, to request additional functionality.
   myAddRowsetProperties(rgPropSets, cProperties, rgProperties);

   // Get the requested schema rowset. If IDBSchemaRowset is supported,
   // the following schema rowsets are required to be supported:
   // DBSCHEMA_TABLES, DBSCHEMA_COLUMNS, and DBSCHEMA_PROVIDERTYPES
   // We know that we will be asking for one of these, so it is not
   // necessary to call IDBSchemaRowset::GetSchemas in this case.
   // See IRowset
   XCHECK_HR(hr = pIDBSchemaRowset->GetRowset(
            NULL,                              // pUnkOuter
            guidSchema,                        // guidSchema
            0,                                 // cRestrictions
            NULL,                              // rgRestrictions
            IID_IRowset,                       // riid
            1,                                 // cPropSets
            rgPropSets,                        // rgPropSets
            &pUnkRowset                        // ppRowset
            ));

   // Display the rowset to the user. This will allow the user to
   // perform basic navigation of the rowset and will allow the user
   // to select a row containing a desired table name (taken from the
   // TABLE_NAME column).
   CHECK_HR(hr = myDisplayRowset(pUnkRowset,
      L"TABLE_NAME", cchBuffer, pwszBuffer));

CLEANUP:
   if( pIDBSchemaRowset )
      pIDBSchemaRowset->Release();
   if( pUnkRowset )
      pUnkRowset->Release();
   return hr;
}

_
//---------------------------------------------------------------------------
// Microsoft OLE DB Programmer's Reference Sample.
// Copyright (C) 1998 By Microsoft Corporation.
//
// @doc
//
// @module ROWSET.CPP
//
//---------------------------------------------------------------------------

/////////////////////////////////////////////////////////////////
// Includes
//
/////////////////////////////////////////////////////////////////
#include "prsample.h"         // Programmer's Reference Sample includes

/////////////////////////////////////////////////////////////////
// myCreateRowset
// Summary of Routines
//
// This function creates an OLE DB rowset object from the given
// provider's session object. It first obtains a default table
// name from the user through the tables schema rowset, if
// supported, and then creates a rowset object by one of two methods:
//
// - If the user requested that the rowset object be created
//    from a command object, it creates a command object and then
//    obtains command text from the user, sets properties and
//    the command text, and finally executes the command to
//    create the rowset object.
// - Otherwise, the function obtains a table name from the user
//    and calls IOpenRowset::OpenRowset to create a rowset object
//    over that table that supports the requested properties.
//
/////////////////////////////////////////////////////////////////
HRESULT   myCreateRowset
   (
   IUnknown *      pUnkSession,
   IUnknown **     ppUnkRowset
   )
{
   HRESULT         hr;
   IUnknown *      pUnkCommand                      = NULL;
   IOpenRowset *   pIOpenRowset                     = NULL;
   WCHAR           wszTableName[MAX_NAME_LEN + 1]   = {0};

   const ULONG     cProperties                      = 2;
   DBPROP          rgProperties[cProperties];
   DBPROPSET       rgPropSets[1];

   // Obtain a default table name from the user by displaying the
   // tables schema rowset if schema rowsets are supported.
   CHECK_HR(hr = myCreateSchemaRowset(DBSCHEMA_TABLES, pUnkSession,
      MAX_NAME_LEN, wszTableName));

   // Set properties on the rowset, to request additional functionality.
   myAddRowsetProperties(rgPropSets, cProperties, rgProperties);

   // If the user requested that the rowset be created from a
   // Ccommand object, create a command, set its properties and
   // text, and execute it to create the rowset object.
   if( g_dwFlags & USE_COMMAND )
   {
      WCHAR      wszCommandText[MAX_NAME_LEN + 1];

      // Attempt to create the command object from the provider's
      // session object. Note that commands are not supported by
      // all providers, and this will fail in that case.
      CHECK_HR(hr = myCreateCommand(pUnkSession, &pUnkCommand));

      // From the user, get the command text that we will execute.
      if( !myGetInputFromUser(wszCommandText, L"\nType the command "
         L"to execute [Enter = `select * from %s`]: ", wszTableName) )
      {
         swprintf(wszCommandText, L"select * from %s", wszTableName);
      }

      // And execute the command the user entered.
      CHECK_HR(hr = myExecuteCommand(pUnkCommand, wszCommandText,
               1, rgPropSets, ppUnkRowset));
   }
   // Otherwise, the user gets the default behavior, which is to use
   // IOpenRowset to create the rowset object from the session object.
   // IOpenRowset is supported by all providers; it takes a TableID
   // and creates a rowset containing all rows in that table. It is
   // similar to using SQL command text of "SELECT * FROM TableID".
   else
   {
      DBID TableID;

      // Create the TableID.
      TableID.eKind            = DBKIND_NAME;
      TableID.uName.pwszName   = wszTableName;

      // Obtain the table name from the user.
      myGetInputFromUser(wszTableName, L"\nType the name of the table "
         L"to use [Enter = `%s`]: ", wszTableName);

      // Get the IOpenRowset interface, and create a rowset object
      // over the requested table through OpenRowset.
      // See IOpenRowset

      XCHECK_HR(hr = pUnkSession->QueryInterface(
               IID_IOpenRowset, (void**)&pIOpenRowset));
      XCHECK_HR(hr = pIOpenRowset->OpenRowset(
               NULL,                                 // pUnkOuter
               &TableID,                             // pTableID
               NULL,                                 // pIndexID
               IID_IRowset,                          // riid
               1,                                    // cPropSets
               rgPropSets,                           // rgPropSets
               ppUnkRowset                           // ppRowset
               ));
   }

CLEANUP:
   if( pIOpenRowset )
      pIOpenRowset->Release();
   if( pUnkCommand )
      pUnkCommand->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////
// mySetupBindings
// Summary of Routines
//
// This function takes an IUnknown pointer from a rowset object
// and creates a bindings array that describes how we want the
// data we fetch from the rowset to be laid out in memory. It
// also calculates the total size of a row so that we can use
// this to allocate memory for the rows that we will fetch
// later.
//
// For each column in the rowset, there will be a corresponding
// element in the bindings array that describes how the
// provider should transfer the data, including length and
// status, for that column. This element also specifies the data
// type that the provider should return the column as. We will
// bind all columns as DBTYPE_WSTR, with a few exceptions
// detailed below, as providers are required to support the
// conversion of their column data to this type in the vast
// majority of cases. The exception to our binding as
// DBTYPE_WSTR is if the native column data type is
// DBTYPE_IUNKNOWN or if the user has requested that BLOB
// columns be bound as ISequentialStream objects, in which case
// we will bind those columns as ISequentialStream objects.
//
/////////////////////////////////////////////////////////////////
HRESULT mySetupBindings
   (
   IUnknown *       pUnkRowset,
   ULONG *          pcBindings,
   DBBINDING **     prgBindings,
   ULONG *          pcbRowSize
   )
{
   HRESULT          hr;
   ULONG            cColumns;
   DBCOLUMNINFO *   rgColumnInfo    = NULL;
   LPWSTR           pStringBuffer   = NULL;
   IColumnsInfo *   pIColumnsInfo   = NULL;

   ULONG            iCol;
   ULONG            dwOffset        = 0;
   DBBINDING *      rgBindings      = NULL;

   ULONG            cStorageObjs    = 0;
   BOOL             fMultipleObjs   = FALSE;

   // Obtain the column information for the rowset; from this, we can
   // find out the following information that we need to construct the
   // bindings array:
   // - the number of columns
   // - the ordinal of each column
   // - the precision and scale of numeric columns
   // - the OLE DB data type of the column
   // See IColumnsInfo
   XCHECK_HR(hr = pUnkRowset->QueryInterface(
            IID_IColumnsInfo, (void**)&pIColumnsInfo));
   XCHECK_HR(hr = pIColumnsInfo->GetColumnInfo(
            &cColumns,                            // pcColumns
            &rgColumnInfo,                        // prgColumnInfo
            &pStringBuffer                        // ppStringBuffer
            ));

   // Allocate memory for the bindings array; there is a one-to-one
   // mapping between the columns returned from GetColumnInfo and our
   // bindings.
   rgBindings = (DBBINDING*)CoTaskMemAlloc(cColumns * sizeof(DBBINDING));
   CHECK_MEMORY(hr, rgBindings);
   memset(rgBindings, 0, cColumns * sizeof(DBBINDING));

   // Determine if the rowset supports multiple storage object bindings.
   // If it does not, we will bind only the first BLOB column or IUnknown
   // column as an ISequentialStream object, and we will bind the rest as
   // DBTYPE_WSTR.
   // See ISequentialStream
   myGetProperty(pUnkRowset, IID_IRowset, DBPROP_MULTIPLESTORAGEOBJECTS,
      DBPROPSET_ROWSET, &fMultipleObjs);

   // Construct the binding array element for each column.
   for( iCol = 0; iCol < cColumns; iCol++ )
   {
      // This binding applies to the ordinal of this column.
      rgBindings[iCol].iOrdinal = rgColumnInfo[iCol].iOrdinal;

      // We are asking the provider to give us the data for this column
      // (DBPART_VALUE), the length of that data (DBPART_LENGTH), and
      // the status of the column (DBPART_STATUS).
      rgBindings[iCol].dwPart = DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS;

      // The following values are the offsets to the status, length, and
      // data value that the provider will fill with the appropriate
      // values when we fetch data later. When we fetch data, we will
      // pass a pointer to a buffer that the provider will copy column
      // data to, in accordance with the binding we have provided for
      // that column; these are offsets into that future buffer.
      rgBindings[iCol].obStatus   = dwOffset;
      rgBindings[iCol].obLength   = dwOffset + sizeof(DBSTATUS);
      rgBindings[iCol].obValue    = dwOffset + sizeof(DBSTATUS) + sizeof(ULONG);

      // Any memory allocated for the data value will be owned by us, the
      // client. Note that no data will be allocated in this case, as the
      // DBTYPE_WSTR bindings we are using will tell the provider to
      // simply copy data directly into our provided buffer.
      rgBindings[iCol].dwMemOwner   = DBMEMOWNER_CLIENTOWNED;

      // This is not a parameter binding.
      rgBindings[iCol].eParamIO   = DBPARAMIO_NOTPARAM;

      // We want to use the precision and scale of the column.
      rgBindings[iCol].bPrecision   = rgColumnInfo[iCol].bPrecision;
      rgBindings[iCol].bScale       = rgColumnInfo[iCol].bScale;

      // Bind this column as DBTYPE_WSTR, which tells the provider to
      // copy a Unicode string representation of the data into our
      // buffer, converting from the native type if necessary.
      rgBindings[iCol].wType = DBTYPE_WSTR;

      // Initially, we set the length for this data in our buffer to 0;
      // the correct value for this will be calculated directly below.
      rgBindings[iCol].cbMaxLen = 0;

      // Determine the maximum number of bytes required in our buffer to
      // contain the Unicode string representation of the provider's
      // native data type, including room for the NULL-termination
      // character.
      switch( rgColumnInfo[iCol].wType )
      {
         case DBTYPE_NULL:
         case DBTYPE_EMPTY:
         case DBTYPE_I1:
         case DBTYPE_I2:
         case DBTYPE_I4:
         case DBTYPE_UI1:
         case DBTYPE_UI2:
         case DBTYPE_UI4:
         case DBTYPE_R4:
         case DBTYPE_BOOL:
         case DBTYPE_I8:
         case DBTYPE_UI8:
         case DBTYPE_R8:
         case DBTYPE_CY:
         case DBTYPE_ERROR:
            // When the above types are converted to a string, they
            // will all fit into 25 characters, so use that plus space
            // for the NULL terminator.
            rgBindings[iCol].cbMaxLen = (25 + 1) * sizeof(WCHAR);
            break;

         case DBTYPE_DECIMAL:
         case DBTYPE_NUMERIC:
         case DBTYPE_DATE:
         case DBTYPE_DBDATE:
         case DBTYPE_DBTIMESTAMP:
         case DBTYPE_GUID:
            // Converted to a string, the above types will all fit into
            // 50 characters, so use that plus space for the terminator.
            rgBindings[iCol].cbMaxLen = (50 + 1) * sizeof(WCHAR);
            break;

         case DBTYPE_BYTES:
            // In converting DBTYPE_BYTES to a string, each byte
            // becomes two characters (e.g. 0xFF -> "FF"), so we
            // will use double the maximum size of the column plus
            // include space for the NULL terminator.
            rgBindings[iCol].cbMaxLen =
               (rgColumnInfo[iCol].ulColumnSize * 2 + 1) * sizeof(WCHAR);
            break;

         case DBTYPE_STR:
         case DBTYPE_WSTR:
         case DBTYPE_BSTR:
            // Going from a string to our string representation,
            // we can just take the maximum size of the column,
            // a count of characters, and include space for the
            // terminator, which is not included in the column size.
            rgBindings[iCol].cbMaxLen =
               (rgColumnInfo[iCol].ulColumnSize + 1) * sizeof(WCHAR);
            break;

         default:
            // For any other type, we will simply use our maximum
            // column buffer size, since the display size of these
            // columns may be variable (e.g. DBTYPE_VARIANT) or
            // unknown (e.g. provider-specific types).
            rgBindings[iCol].cbMaxLen = MAX_COL_SIZE;
            break;
      };

      // If the provider's native data type for this column is
      // DBTYPE_IUNKNOWN or this is a BLOB column and the user
      // has requested that we bind BLOB columns as ISequentialStream
      // objects, bind this column as an ISequentialStream object if
      // the provider supports our creating another ISequentialStream
      // binding.
      if( (rgColumnInfo[iCol].wType == DBTYPE_IUNKNOWN ||
          ((rgColumnInfo[iCol].dwFlags & DBCOLUMNFLAGS_ISLONG) &&
          (g_dwFlags & USE_ISEQSTREAM))) &&
          (fMultipleObjs || !cStorageObjs) )
      {
         // To create an ISequentialStream object, we will
         // bind this column as DBTYPE_IUNKNOWN to indicate
         // that we are requesting this column as an object.
         rgBindings[iCol].wType = DBTYPE_IUNKNOWN;

         // We want to allocate enough space in our buffer for
         // the ISequentialStream pointer we will obtain from
         // the provider.
         rgBindings[iCol].cbMaxLen = sizeof(ISequentialStream *);

         // To specify the type of object that we want from the
         // provider, we need to create a DBOBJECT structure and
         // place it in our binding for this column.
         rgBindings[iCol].pObject =
                        (DBOBJECT *)CoTaskMemAlloc(sizeof(DBOBJECT));
         CHECK_MEMORY(hr, rgBindings[iCol].pObject);

         // Direct the provider to create an ISequentialStream
         // object over the data for this column.
         rgBindings[iCol].pObject->iid = IID_ISequentialStream;

         // We want read access on the ISequentialStream
         // object that the provider will create for us.
         rgBindings[iCol].pObject->dwFlags = STGM_READ;

         // Keep track of the number of storage objects
         // (ISequentialStream is a storage interface) that we have
         // requested, so that we can avoid requesting multiple storage
         // objects from a provider that supports only a single storage
         // object in our bindings.
         cStorageObjs++;
      }

      // Ensure that the bound maximum length is no more than the
      // maximum column size in bytes that we've defined.
      rgBindings[iCol].cbMaxLen
         = min(rgBindings[iCol].cbMaxLen, MAX_COL_SIZE);

      // Update the offset past the end of this column's data so
      // that the next column will begin in the correct place in
      // the buffer.
      dwOffset = rgBindings[iCol].cbMaxLen + rgBindings[iCol].obValue;

      // Ensure that the data for the next column will be correctly
      // aligned for all platforms, or if we're done with columns,
      // that if we allocate space for multiple rows that the data
      // for every row is correctly aligned.
      dwOffset = ROUNDUP(dwOffset);
   }

   // Return the row size (the current dwOffset is the size of the row),
   // the count of bindings, and the bindings array to the caller.
   *pcbRowSize    = dwOffset;
   *pcBindings    = cColumns;
   *prgBindings   = rgBindings;

CLEANUP:
   CoTaskMemFree(rgColumnInfo);
   CoTaskMemFree(pStringBuffer);
   if( pIColumnsInfo )
      pIColumnsInfo->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////
// myCreateAccessor
// Summary of Routines
//
// This function takes an IUnknown pointer for a rowset object
// and creates an accessor that describes the layout of the
// buffer we will use when we fetch data. The provider will fill
// this buffer according to the description contained in the
// accessor that we will create here.
//
/////////////////////////////////////////////////////////////////
HRESULT myCreateAccessor
   (
   IUnknown *     pUnkRowset,
   HACCESSOR *    phAccessor,
   ULONG *        pcBindings,
   DBBINDING **   prgBindings,
   ULONG *        pcbRowSize
   )
{
   HRESULT        hr;
   IAccessor *    pIAccessor = NULL;

   // An accessor is basically a handle to a collection of bindings.
   // To create the accessor, we need to first create an array of
   // bindings for the columns in the rowset.
   CHECK_HR(hr = mySetupBindings(pUnkRowset, pcBindings, prgBindings,
      pcbRowSize));

   // Now that we have an array of bindings, tell the provider to
   // create the accessor for those bindings. We get back a handle
   // to this accessor, which we will use when fetching data.
   // See IAccessor
   XCHECK_HR(hr = pUnkRowset->QueryInterface(
            IID_IAccessor, (void**)&pIAccessor));
   XCHECK_HR(hr = pIAccessor->CreateAccessor(
            DBACCESSOR_ROWDATA,                  // dwAccessorFlags
            *pcBindings,                         // cBindings
            *prgBindings,                        // rgBindings
            0,                                   // cbRowSize
            phAccessor,                          // phAccessor
            NULL                                 // rgStatus
            ));

CLEANUP:
   if( pIAccessor )
      pIAccessor->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////
// myDisplayRowset
// Summary of Routines
//
// This function will display data from a rowset object and will
// allow the user to perform basic navigation of the rowset.
//
// The function takes a pointer to a rowset object's IUnknown
// and, optionally, the name of a column and a buffer that will
// receive the value of that column when the user selects a row.
//
/////////////////////////////////////////////////////////////////
HRESULT myDisplayRowset
   (
   IUnknown *    pUnkRowset,
   LPCWSTR       pwszColToReturn,
   ULONG         cchBuffer,
   LPWSTR        pwszBuffer
   )
{
   HRESULT       hr;
   IRowset *     pIRowset               = NULL;
   ULONG         cBindings;
   DBBINDING *   rgBindings             = NULL;
   HACCESSOR     hAccessor              = DB_NULL_HACCESSOR;
   ULONG         cbRowSize;
   void *        pData                  = NULL;
   ULONG *       rgDispSize             = NULL;
   ULONG         cRowsObtained;
   HROW *        rghRows                = NULL;
   ULONG         iRow;
   LONG          cRows                  = MAX_ROWS;
   LONG          iRetCol                = -1;
   BOOL          fCanFetchBackwards;
   ULONG         iIndex;
   void *        pCurData;

   // Obtain the IRowset interface for use in fetching rows and data
   // See IRowset
   XCHECK_HR(hr = pUnkRowset->QueryInterface(
            IID_IRowset, (void**)&pIRowset));

   // Determine whether this rowset supports fetching data backwards;
   // we use this to determine whether the rowset can support moving
   // to the previous set of rows, described in more detail below
   myGetProperty(pUnkRowset, IID_IRowset, DBPROP_CANFETCHBACKWARDS,
      DBPROPSET_ROWSET, &fCanFetchBackwards);

   // If the caller wants us to return the data for a particular column
   // from a user-selected row, we need to turn the column name into a
   // column ordinal.
   if( pwszColToReturn )
      CHECK_HR(hr = myFindColumn(pUnkRowset, pwszColToReturn, &iRetCol));

   // Create an accessor. An accessor is basically a handle to a
   // collection of bindings that describes to the provider how to
   // copy (and convert, if necessary) column data into our buffer.
   // The accessor that this creates will bind all columns either as
   // DBTYPE_WSTR (a Unicode string) or as an ISequentialStream object
   // (used for BLOB data). This will also give us the size of the
   // row buffer that the accessor describes to the provider.
   CHECK_HR(hr = myCreateAccessor(pUnkRowset, &hAccessor,
                                  &cBindings, &rgBindings, &cbRowSize));

   // Allocate enough memory to hold cRows rows of data; this is
   // where the actual row data from the provider will be placed.
   pData = CoTaskMemAlloc(cbRowSize * MAX_ROWS);
   CHECK_MEMORY(hr, pData);

   // Allocate memory for an array that we will use to calculate the
   // maximum display size used by each column in the current set of
   // rows.
   rgDispSize = (ULONG *)CoTaskMemAlloc(cBindings * sizeof(ULONG));
   CHECK_MEMORY(hr, rgDispSize);

   // In this loop, we perform the following process:
   // - reset the maximum display size array
   // - try to get cRows row handles from the provider
   // - these handles are then used to actually get the row data from the
   //    provider copied into our allocated buffer
   // - calculate the maximum display size for each column
   // - release the row handles to the rows we obtained
   // - display the column names for the rowset
   // - display the row data for the rows that we fetched
   // - get user input
   // - free the provider-allocated row handle array
   // - repeat unless the user has chosen to quit or has selected a row
   while( hr == S_OK )
   {
      // Clear the maximum display size array.
      memset(rgDispSize, 0, cBindings * sizeof(ULONG));

      // Attempt to get cRows row handles from the provider.
      XCHECK_HR(hr = pIRowset->GetNextRows(
               DB_NULL_HCHAPTER,                      // hChapter
               0,                                     // lOffset
               cRows,                                 // cRows
               &cRowsObtained,                        // pcRowsObtained
               &rghRows                               // prghRows
               ));

      // Loop over the row handles obtained from GetNextRows,
      // actually fetching the data for these rows into our buffer.
      for( iRow = 0; iRow < cRowsObtained; iRow++ )
      {
         // Find the location in our buffer where we want to place
         // the data for this row. Note that if we fetched rows
         // backwards (cRows < 0), the row handles obtained from the
         // provider are reversed from the order in which we want to
         // actually display the data on the screen, so we will
         // account for this. This ensures that the resulting order
         // of row data in the pData buffer matches the order we
         // wish to use to display the data.
         iIndex     = cRows > 0 ? iRow : cRowsObtained - iRow - 1;
         pCurData   = (BYTE*)pData + (cbRowSize * iIndex);

         // Get the data for this row handle. The provider will copy
         // (and convert, if necessary) the data for each of the
         // columns that are described in our Aaccessor into the given
         // buffer (pCurData).
         XCHECK_HR(hr = pIRowset->GetData(
               rghRows[iRow],                          // hRow
               hAccessor,                              // hAccessor
               pCurData                                // pData
               ));

         // Update the maximum display size array, accounting for
         // this row.
         CHECK_HR(hr = myUpdateDisplaySize(cBindings, rgBindings,
            pCurData, rgDispSize));
      }

      // If we obtained rows, release the row handles for the retrieved
      // rows and display the names of the rowset columns before we
      // display the data.
      if( cRowsObtained )
      {
         // Release the row handles that we obtained.
         XCHECK_HR(hr = pIRowset->ReleaseRows(
                  cRowsObtained,                        // cRows
                  rghRows,                              // rghRows
                  NULL,                                 // rgRowOptions
                  NULL,                                 // rgRefCounts
                  NULL                                  // rgRowStatus
                  ));

         // Display the names of the rowset columns.
         CHECK_HR(hr = myDisplayColumnNames(pIRowset, rgDispSize));
      }

      // For each row that we obtained the data for, display this data.
      for( iRow = 0; iRow < cRowsObtained; iRow++ )
      {
         // Get a pointer to the data for this row.
         pCurData = (BYTE*)pData + (cbRowSize* iRow);

         // And display the row data.
         CHECK_HR(hr = myDisplayRow(iRow, cBindings, rgBindings,
            pCurData, rgDispSize));
      }

      // Allow the user to navigate the rowset. This displays the
      // appropriate prompts, gets the user's input, may call
      // IRowset::RestartPosition, and may copy data from a selected row
      // to the selection buffer, if so directed. This will return S_OK
      // if the user asked for more rows, S_FALSE if the user selected a
      // row, or E_FAIL if the user quits.
      hr = myInteractWithRowset(
         pIRowset,                // IRowset pointer, for RestartPosition
         &cRows,                  // updated with fetch direction value
         cRowsObtained,           // to indicate selection range
         fCanFetchBackwards,      // whether [P]revious is supported
         pData,                   // data pointer for copying selection
         cbRowSize,               // size of rows for copying selection
         iRetCol >= 0 ?           // bindings for the selection column,
            &rgBindings[iRetCol] :   // or NULL if no selection column
            NULL,
         cchBuffer,                // size of the selection buffer
         pwszBuffer);             // pointer to the selection buffer

      // Since we are allowing the provider to allocate the memory for
      // the row handle array, we will free this memory and reset the
      // pointer to NULL. If this is not NULL on the next call to
      // GetNextRows, the provider will assume that it points to an
      // allocated array of the required size (which may not be the case
      // if we obtained less than cRows rows from this last call to
      // GetNextRows).
      CoTaskMemFree(rghRows);
      rghRows = NULL;
   }

CLEANUP:
   myFreeBindings(cBindings, rgBindings);
   CoTaskMemFree(rgDispSize);
   CoTaskMemFree(pData);
   if( pIRowset )
      pIRowset->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////
// myInteractWithRowset
// Summary of Routines
//
// This function allows the user to interact with the rowset. It
// prompts the user appropriately, gets the user's input, may
// call IRowset::RestartPosition if the user requests a restart,
// and will copy data from a selected row to the selection
// buffer.
//
/////////////////////////////////////////////////////////////////
HRESULT myInteractWithRowset
   (
   IRowset *     pIRowset,
   LONG *        pcRows,
   ULONG         cRowsObtained,
   BOOL          fCanFetchBackwards,
   void *        pData,
   ULONG         cbRowSize,
   DBBINDING *   pBinding,
   ULONG         cchBuffer,
   LPWSTR        pwszBuffer
   )
{
   HRESULT       hr = S_OK;
   CHAR          ch;

   // Let the user know if no rows were fetched.
   if( !cRowsObtained )
      printf("\n*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\n" \
             "*                                 *\n" \
             "* No rows obtained on this fetch! *\n" \
             "*                                 *\n" \
             "*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\n");

   // Print navigation options.
   if( fCanFetchBackwards )
      printf("\n[P]revious; [N]ext; [R]estart; ");
   else
      printf("\n[N]ext; [R]estart; ");

   // Print selection options.
   if( cRowsObtained && pwszBuffer && pBinding )
      printf("[0]-[%d] for a row; ", cRowsObtained - 1);

   // User can always quit the program.
   printf("[Q]uit? ");

   // Get the user's input.
   while( TRUE )
   {
      // Get a character from the console.
      ch = myGetChar();

      // Look for one of the allowed options; if not found, go
      // back around and wait for another input from the user.

      // If we're looking for a row selection, allow the user to select
      // a row that we fetched, and then copy the data from the requested
      // column into the selection buffer we were passed.
      if( pwszBuffer && pBinding &&
         ch >= '0' && ch < (int)('0' + cRowsObtained) )
      {
         // Save the data for the selected row.
         ULONG nSelection = ch - '0';
         _snwprintf(pwszBuffer, cchBuffer, L"%s",
                    (WCHAR *)((BYTE *)pData + cbRowSize * nSelection +
                    pBinding->obValue));
         pwszBuffer[cchBuffer] = L'\0';
         hr = S_FALSE;
      }
      // If the provider supports fetching backwards, set *pcRows
      // to -MAX_ROWS. When GetNextRows is called with this value,
      // it will fetch rows backwards from the current position
      // until it fetches MAX_ROWS rows or hits the end of the rowset.
      else if( fCanFetchBackwards && ch == 'p' )
      {
         // Fetch backwards.
         *pcRows = -MAX_ROWS;
      }
      // Set *pcRows so that the next call to GetNextRows fetches
      // MAX_ROWS rows forward from the current position.
      else if( ch == 'n' )
      {
         // Fetch forward
         *pcRows = MAX_ROWS;
      }
      // Call IRowset::RestartPosition, and fetch the first MAX_ROWS
      // rows of the rowset forward from there.
      else if( ch == 'r' )
      {
         // RestartPosition
         *pcRows = MAX_ROWS;
         XCHECK_HR(hr = pIRowset->RestartPosition(DB_NULL_HCHAPTER));

         // Restarting a command may return the DB_S_COMMANDREEXECUTED
         // warning. If this happens, we still want the caller to
         // continue to display data, so we will reset the result code.
         // to S_OK.
         hr = S_OK;
      }
      // Quit the program.
      else if( ch == 'q' )
      {
         hr = E_FAIL;
      }
      // Invalid option; go back up and get another character from the
      // user.
      else
      {
         continue;
      }

      // Echo the character and stop waiting for input.
      printf("%c\n", ch);
      break;
   }

CLEANUP:
   return hr;
}

/////////////////////////////////////////////////////////////////
// myDisplayColumnNames
// Summary of Routines
//
// This function takes an IUnknown pointer to a rowset object
// and displays the names of the columns of that rowset.
//
/////////////////////////////////////////////////////////////////
HRESULT myDisplayColumnNames
   (
   IUnknown *       pUnkRowset,
   ULONG *          rgDispSize
   )
{
   HRESULT          hr;
   IColumnsInfo *   pIColumnsInfo    = NULL;
   ULONG            cColumns;
   DBCOLUMNINFO *   rgColumnInfo     = NULL;
   LPOLESTR         pStringsBuffer   = NULL;
   WCHAR            wszColumn[MAX_DISPLAY_SIZE + 1];
   LPWSTR           pwszColName;
   ULONG            iCol;
   ULONG            cSpaces;
   ULONG            iSpace;

   // Get the IColumnsInfo interface for the rowset.
   // See IColumnsInfo
   XCHECK_HR(hr = pUnkRowset->QueryInterface(
            IID_IColumnsInfo, (void**)&pIColumnsInfo));

   // Get the columns information.
   XCHECK_HR(hr = pIColumnsInfo->GetColumnInfo(
            &cColumns,                            // pcColumns
            &rgColumnInfo,                        // prgColumnInfo
            &pStringsBuffer                       // ppStringBuffer
            ));

   // Display the title of the row index column.
   wprintf(L" Row | ");

   // Display all column names.
   for( iCol = 0; iCol < cColumns; iCol++ )
   {
      pwszColName = rgColumnInfo[iCol].pwszName;

      // If the column name is NULL, we'll use a default string.
      if( !pwszColName )
      {
         // Is this the bookmark column?
         if( !rgColumnInfo[iCol].iOrdinal )
            pwszColName = L"Bmk";
         else
            pwszColName = L"(null)";
      }

      // Ensure that the name is no longer than MAX_DISPLAY_SIZE.
      wcsncpy(wszColumn, pwszColName, MAX_DISPLAY_SIZE);
      wszColumn[min(rgDispSize[iCol], MAX_DISPLAY_SIZE)] = L'\0';

      // Figure out how many spaces we need to print after
      // this column name.
      cSpaces = min(rgDispSize[iCol], MAX_DISPLAY_SIZE) - wcslen(wszColumn);

      // Print the column name.
      wprintf(L"%s", wszColumn);

      // Now print any spaces necessary to align this column.
      for(iSpace = 0; iSpace < cSpaces; iSpace++ )
         putch(' ');

      // Now end the column with a separator marker if necessary.
      if( iCol < cColumns - 1 )
         wprintf(L" | ");
   }

   // Done with the header, so print a new line.
   wprintf(L"\n");

CLEANUP:
   CoTaskMemFree(rgColumnInfo);
   CoTaskMemFree(pStringsBuffer);
   if( pIColumnsInfo )
      pIColumnsInfo->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////
// myDisplayRow
// Summary of Routines
//
// This function displays the data for a row.
//
/////////////////////////////////////////////////////////////////
HRESULT myDisplayRow
   (
   ULONG                 iRow,
   ULONG                 cBindings,
   DBBINDING *           rgBindings,
   void *                pData,
   ULONG *               rgDispSize
   )
{
   HRESULT               hr = S_OK;
   WCHAR                 wszColumn[MAX_DISPLAY_SIZE + 1];
   DBSTATUS              dwStatus;
   ULONG                 ulLength;
   void *                pvValue;
   ULONG                 iCol;
   ULONG                 cbRead;
   ISequentialStream *   pISeqStream = NULL;
   ULONG                 cSpaces;
   ULONG                 iSpace;

   // Display the row number.
   wprintf(L" [%d] | ", iRow);

   // For each column that we have bound, display the data.
   for( iCol = 0; iCol < cBindings; iCol++ )
   {
      // We have bound status, length, and the data value for all
      // columns, so we know that these can all be used.
      dwStatus   = *(DBSTATUS *)((BYTE *)pData + rgBindings[iCol].obStatus);
      ulLength   = *(ULONG *)((BYTE *)pData + rgBindings[iCol].obLength);
      pvValue    = (BYTE *)pData + rgBindings[iCol].obValue;

      // Check the status of this column. This decides
      // exactly what will be displayed for the column.
      switch( dwStatus )
      {
         // The data is NULL, so don't try to display it.
         case DBSTATUS_S_ISNULL:
            wcscpy(wszColumn, L"(null)");
            break;

         // The data was fetched, but may have been truncated.
         // Display string data for this column to the user.
         case DBSTATUS_S_TRUNCATED:
         case DBSTATUS_S_OK:
         case DBSTATUS_S_DEFAULT:
         {
            // We have bound the column either as a Unicode string
            // (DBTYPE_WSTR) or as an ISequentialStream object
            // (DBTYPE_IUNKNOWN), and we have to do different processing
            // for each one of these possibilities.
            switch( rgBindings[iCol].wType )
            {
               case DBTYPE_WSTR:
               {
                  // Copy the string data.
                  wcsncpy(wszColumn, (WCHAR *)pvValue, MAX_DISPLAY_SIZE);
                  wszColumn[MAX_DISPLAY_SIZE - 1] = L'\0';
                  break;
               }

               case DBTYPE_IUNKNOWN:
               {
                  // We've bound this as an ISequentialStream object,
                  // therefore the data in our buffer is a pointer
                  // to the object's ISequentialStream interface.
                  pISeqStream = *(ISequentialStream**)pvValue;

                  // We call ISequentialStream::Read to read bytes from
                  // the stream blindly into our buffer, simply as a
                  // demonstration of ISequentialStream. To display the
                  // data properly, the native provider type of this
                  // column should be accounted for; it could be
                  // DBTYPE_WSTR, in which case this works, or it could
                  // be DBTYPE_STR or DBTYPE_BYTES, in which case this
                  // won't display the data correctly.
                  CHECK_HR(hr = pISeqStream->Read(
                           wszColumn,                     // pBuffer
                           MAX_DISPLAY_SIZE,              // cBytes
                           &cbRead                        // pcBytesRead
                           ));

                  // Since streams don't provide NULL-termination,
                  // we'll NULL-terminate the resulting string ourselves.
                  wszColumn[cbRead / sizeof(WCHAR)] = L'\0';

                  // Release the stream object, now that we're done.
                  pISeqStream->Release();
                  pISeqStream = NULL;
                  break;
               }
            }
            break;
         }

         // This is an error status, so don't try to display the data.
         default:
            wcscpy(wszColumn, L"(error status)");
            break;
      }

      // Determine how many spaces we need to add after displaying this
      // data to align it with this column in other rows.
      cSpaces = min(rgDispSize[iCol], MAX_DISPLAY_SIZE) - wcslen(wszColumn);

      // Print the column data.
      wprintf(L"%s", wszColumn);

      // Now print any spaces necessary.
      for(iSpace = 0; iSpace < cSpaces; iSpace++ )
         putch(' ');

      // Now end the column with a separator marker if necessary.
      if( iCol < cBindings - 1 )
         wprintf(L" | ");
   }

CLEANUP:
   if( pISeqStream )
      pISeqStream->Release();

   // Print the row separator.
   wprintf(L"\n");
   return hr;
}

/////////////////////////////////////////////////////////////////
// myFreeBindings
// Summary of Routines
//
//   This function frees a bindings array and any allocated
// structures contained in that array.
//
/////////////////////////////////////////////////////////////////
void myFreeBindings
   (
   ULONG         cBindings,
   DBBINDING *   rgBindings
   )
{
   ULONG         iBind;

   // Free any memory used by DBOBJECT structures in the array.
   for( iBind = 0; iBind < cBindings; iBind++ )
      CoTaskMemFree(rgBindings[iBind].pObject);

   // Now free the bindings array itself.
   CoTaskMemFree(rgBindings);
}

/////////////////////////////////////////////////////////////////
// myAddRowsetProperties
// Summary of Routines
//
// This function sets up the given DBPROPSET and DBPROP
// structures, adding two optional properties that describe
// features that we would like to use on the rowset created
// with these properties applied:
// - DBPROP_CANFETCHBACKWARDS -- the rowset should support
//    fetching rows backwards from our current cursor position.
// - DBPROP_IRowsetLocate -- the rowset should support
//    the IRowsetLocate interface and its semantics.
//
/////////////////////////////////////////////////////////////////
void myAddRowsetProperties(DBPROPSET* pPropSet, ULONG cProperties, DBPROP* rgProperties)
{
   // Initialize the property set array.
   pPropSet->rgProperties      = rgProperties;
   pPropSet->cProperties       = cProperties;
   pPropSet->guidPropertySet   = DBPROPSET_ROWSET;

   // Add the following two properties (as OPTIONAL) to the property
   // array contained in the property set array in order to request
   // that they be supported by the rowset we will create. Because
   // these are optional, the rowset we obtain may or may not support
   // this functionality. We will check for the functionality that
   // we need once the rowset is created and will modify our behavior
   // appropriately.
   myAddProperty(&rgProperties[0], DBPROP_CANFETCHBACKWARDS);
   myAddProperty(&rgProperties[1], DBPROP_IRowsetLocate);
}

/////////////////////////////////////////////////////////////////
// myUpdateDisplaySize
// Summary of Routines
//
// This function updates the rgDispSize array, keeping the
// maximum of the display size needed for the given data and
// the previous maximum size already in the array.
//
/////////////////////////////////////////////////////////////////
HRESULT myUpdateDisplaySize
   (
   ULONG         cBindings,
   DBBINDING *   rgBindings,
   void *        pData,
   ULONG *       rgDispSize
   )
{
   DBSTATUS      dwStatus;
   ULONG         cchLength;
   ULONG         iCol;

   // Loop through the bindings, comparing the size of each column
   // against the previously found maximum size for that column.
   for( iCol = 0; iCol < cBindings; iCol++ )
   {
      dwStatus = *(DBSTATUS *)((BYTE *)pData + rgBindings[iCol].obStatus);
      cchLength = ((*(ULONG *)((BYTE *)pData + rgBindings[iCol].obLength))
               / sizeof(WCHAR));

      // The length that we need to display depends on the status
      // of this column and generally on the data in the column.
      switch( dwStatus )
      {
         case DBSTATUS_S_ISNULL:
            cchLength = 6;                              // "(null)"
            break;

         case DBSTATUS_S_TRUNCATED:
         case DBSTATUS_S_OK:
         case DBSTATUS_S_DEFAULT:
            if( rgBindings[iCol].wType == DBTYPE_IUNKNOWN )
               cchLength = 2 + 8;                      // "0x%08lx"

            // Ensure that the length is at least the minimum
            // display size.
            cchLength = max(cchLength, MIN_DISPLAY_SIZE);
            break;

         default:
            cchLength = 14;                        // "(error status)"
            break;
      }

      if( rgDispSize[iCol] < cchLength )
         rgDispSize[iCol] = cchLength;
   }

   return S_OK;
}

/////////////////////////////////////////////////////////////////
// myFindColumn
// Summary of Routines
//
// Find the index of the column described in pwszName, and return
// S_OK or, if not found, S_FALSE.
//
/////////////////////////////////////////////////////////////////
HRESULT myFindColumn
   (
   IUnknown *       pUnkRowset,
   LPCWSTR          pwszName,
   LONG *           plIndex
   )
{
   HRESULT          hr;
   IColumnsInfo *   pIColumnsInfo    = NULL;
   ULONG            cColumns;
   DBCOLUMNINFO *   rgColumnInfo     = NULL;
   OLECHAR *        pStringsBuffer   = NULL;
   ULONG            iCol;

   // Get the IColumnsInfo interface.
   // See IColumnsInfo
   XCHECK_HR(hr = pUnkRowset->QueryInterface(
            IID_IColumnsInfo, (void**)&pIColumnsInfo));

   // Get the columns information.
   XCHECK_HR(hr = pIColumnsInfo->GetColumnInfo(
            &cColumns,                            // pcColumns
            &rgColumnInfo,                        // prgColumnInfo
            &pStringsBuffer                       // ppStringBuffer
            ));

   // Assume that we'll find the column.
   hr = S_OK;

   // Search for the column we need.
   for( iCol = 0; iCol < cColumns; iCol++ )
   {
      // If the column name matches, we've found the column....
      if( rgColumnInfo[iCol].pwszName &&
         !wcscmp(pwszName, rgColumnInfo[iCol].pwszName) )
      {
         *plIndex = iCol;
         goto CLEANUP;
      }
   }

   // If we didn't find the column, we'll return S_FALSE.
   hr = S_FALSE;

CLEANUP:
   CoTaskMemFree(rgColumnInfo);
   CoTaskMemFree(pStringsBuffer);
   if( pIColumnsInfo )
      pIColumnsInfo->Release();
   return hr;
}

//---------------------------------------------------------------------------
// Microsoft OLE DB Programmer's Reference Sample.
// Copyright (C) 1998 By Microsoft Corporation.
//
// @doc
//
// @module COMMAND.CPP
//
//---------------------------------------------------------------------------

/////////////////////////////////////////////////////////////////
// Includes
//
/////////////////////////////////////////////////////////////////
#include "prsample.h"         // Programmer's Reference Sample includes

/////////////////////////////////////////////////////////////////
// myCreateCommand
// Summary of Routines
//
// This function takes an IUnknown pointer on a session object
// and attempts to create a command object using the session's
// IDBCreateCommand interface. Since this interface is optional,
// this may fail.
//
/////////////////////////////////////////////////////////////////
HRESULT myCreateCommand
   (
   IUnknown *           pUnkSession,
   IUnknown **          ppUnkCommand
   )
{
   HRESULT              hr;
   IDBCreateCommand *   pIDBCreateCommand = NULL;

   // Attempt to create a command object from the session object
   // See IDBCreateCommand
   XCHECK_HR(hr = pUnkSession->QueryInterface(
            IID_IDBCreateCommand, (void**)&pIDBCreateCommand));
   XCHECK_HR(hr = pIDBCreateCommand->CreateCommand(
            NULL,                                       // pUnkOuter
            IID_ICommand,                               // riid
            ppUnkCommand                                // ppCommand
            ));

CLEANUP:
   if( pIDBCreateCommand )
      pIDBCreateCommand->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////
// myExecuteCommand
// Summary of Routines
//
// This function takes an IUnknown pointer on a command object
// and performs the following steps to create a new rowset
// object:
// - sets the given properties on the command object; these
//    properties will be applied by the provider to any rowset
//    created by this command.
// - sets the given command text for the command.
// - executes the command to create a new rowset object.
//
/////////////////////////////////////////////////////////////////
HRESULT myExecuteCommand
   (
   IUnknown *             pUnkCommand,
   WCHAR *                pwszCommandText,
   ULONG                  cPropSets,
   DBPROPSET*             rgPropSets,
   IUnknown **            ppUnkRowset
   )
{
   HRESULT                hr;
   ICommandText *           pICommandText         = NULL;
   ICommandProperties *   pICommandProperties   = NULL;

   // Set the properties on the command object.
   // See ICommandProperties
   XCHECK_HR(hr = pUnkCommand->QueryInterface(
            IID_ICommandProperties, (void**)&pICommandProperties));
   XCHECK_HR(hr = pICommandProperties->SetProperties(cPropSets, rgPropSets));

   // Set the text for this command, using the default command text
   // dialect. All providers that support commands must support this
   // dialect, and providers that support SQL must be able to recognize
   // an SQL command as SQL when this dialect is specified.
   // See ICommandText
   XCHECK_HR(hr = pUnkCommand->QueryInterface(
            IID_ICommandText, (void**)&pICommandText));
   XCHECK_HR(hr = pICommandText->SetCommandText(
            DBGUID_DEFAULT,                        // guidDialect
            pwszCommandText                        // pwszCommandText
            ));

   // And execute the command. Note that the user could have
   // entered a non-row returning command, so we will check for
   // that and return failure to prevent the display of the
   // nonexistent rowset by the caller.
   XCHECK_HR(hr = pICommandText->Execute(
            NULL,                              // pUnkOuter
            IID_IRowset,                       // riid
            NULL,                              // pParams
            NULL,                              // pcRowsAffected
            ppUnkRowset                        // ppRowset
            ));

   if( !*ppUnkRowset )
   {
      printf("\nThe command executed successfully, but did not " \
             "return a rowset.\nNo rowset will be displayed.\n");
      hr = E_FAIL;
   }

CLEANUP:
   if( pICommandText )
      pICommandText->Release();
   if( pICommandProperties )
      pICommandProperties->Release();
   return hr;
}

//---------------------------------------------------------------------------
// Microsoft OLE DB Programmer's Reference Sample.
// Copyright (C) 1998 By Microsoft Corporation.
//
// @doc
//
// @module ERROR.CPP
//
//---------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////
// Includes
//
////////////////////////////////////////////////////////////////////////
#include "prsample.h"         // Programmer's Reference Sample includes

////////////////////////////////////////////////////////////////////////
// myHandleResult
// Summary of Routines
//
// This function is called as part of the XCHECK_HR macro; it takes an
// HRESULT, which is returned by the method called in the XCHECK_HR
// macro, and the file and line number where the method call was made.
// If the method call failed, this function attempts to get and display
// the extended error information for the call from the IErrorInfo,
// IErrorRecords, and ISQLErrorInfo interfaces.
//
////////////////////////////////////////////////////////////////////////
HRESULT myHandleResult
   (
   HRESULT           hrReturned,
   LPCWSTR           pwszFile,
   ULONG             ulLine
   )
{
   HRESULT           hr;
   IErrorInfo *      pIErrorInfo      = NULL;
   IErrorRecords *   pIErrorRecords   = NULL;
   ULONG             cRecords;
   ULONG             iErr;

   // If the method called as part of the XCHECK_HR macro failed,
   // we will attempt to get extended error information for the call.
   if( FAILED(hrReturned) )
   {
      // Obtain the current error object, if any, by using the
      // Automation GetErrorInfo function, which will give
      // us back an IErrorInfo interface pointer if successful.
      hr = GetErrorInfo(0, &pIErrorInfo);

      // We've got the IErrorInfo interface pointer on the error object.
      if( SUCCEEDED(hr) && pIErrorInfo )
      {
         // OLE DB extends the Automation error model by allowing
         // error objects to support the IErrorRecords interface. This
         // interface can expose information on multiple errors.
         // See IErrorRecords
         hr = pIErrorInfo->QueryInterface(IID_IErrorRecords,
                  (void**)&pIErrorRecords);
         if( SUCCEEDED(hr) )
         {
            // Get the count of error records from the object.
            CHECK_HR(hr = pIErrorRecords->GetRecordCount(&cRecords));

            // Loop through the set of error records, and
            // display the error information for each one.
            for( iErr = 0; iErr < cRecords; iErr++ )
            {
               myDisplayErrorRecord(hrReturned, iErr, pIErrorRecords,
                  pwszFile, ulLine);
            }
         }
         // The object didn't support IErrorRecords; display
         // the error information for this single error.
         else
         {
            myDisplayErrorInfo(hrReturned, pIErrorInfo, pwszFile, ulLine);
         }
      }
      // There was no error object, so just display the HRESULT
      // to the user.
      else
      {
         wprintf(L"\nNo Error Info posted; HResult: 0x%08x\n"
            L"File: %s, Line: %d\n", hrReturned, pwszFile, ulLine);
      }
   }

CLEANUP:
   if( pIErrorInfo )
      pIErrorInfo->Release();
   if( pIErrorRecords )
      pIErrorRecords->Release();
   return hrReturned;
}

////////////////////////////////////////////////////////////////////////
// myDisplayErrorRecord
// Summary of Routines
//
// This function displays the error information for a single error
// record, including information from ISQLErrorInfo, if supported.
//
////////////////////////////////////////////////////////////////////////
HRESULT myDisplayErrorRecord
   (
   HRESULT           hrReturned,
   ULONG             iRecord,
   IErrorRecords *   pIErrorRecords,
   LPCWSTR           pwszFile,
   ULONG             ulLine
   )
{
   HRESULT           hr;
   IErrorInfo *      pIErrorInfo       = NULL;
   BSTR              bstrDescription   = NULL;
   BSTR              bstrSource        = NULL;
   BSTR              bstrSQLInfo       = NULL;

   static LCID       lcid              = GetUserDefaultLCID();

   LONG              lNativeError      = 0;
   ERRORINFO         ErrorInfo;

   // Get the IErrorInfo interface pointer for this error record.
   CHECK_HR(hr = pIErrorRecords->GetErrorInfo(iRecord, lcid, &pIErrorInfo));

   // Get the description of this error.
   CHECK_HR(hr = pIErrorInfo->GetDescription(&bstrDescription));

   // Get the source of this error.
   CHECK_HR(hr = pIErrorInfo->GetSource(&bstrSource));

   // Get the basic error information for this record.
   CHECK_HR(hr = pIErrorRecords->GetBasicErrorInfo(iRecord, &ErrorInfo));

   // If the error object supports ISQLErrorInfo, get this information.
   myGetSqlErrorInfo(iRecord, pIErrorRecords, &bstrSQLInfo, &lNativeError);

   // Display the error information to the user.
   if( bstrSQLInfo )
   {
      wprintf(L"\nErrorRecord:  HResult: 0x%08x\nDescription: %s\n"
         L"SQLErrorInfo: %s\nSource: %s\nFile: %s, Line: %d\n",
         ErrorInfo.hrError,
         bstrDescription,
         bstrSQLInfo,
         bstrSource,
         pwszFile,
         ulLine);
   }
   else
   {
      wprintf(L"\nErrorRecord:  HResult: 0x%08x\nDescription: %s\n"
         L"Source: %s\nFile: %s, Line: %d\n",
         ErrorInfo.hrError,
         bstrDescription,
         bstrSource,
         pwszFile,
         ulLine);
   }

CLEANUP:
   if( pIErrorInfo )
      pIErrorInfo->Release();
   SysFreeString(bstrDescription);
   SysFreeString(bstrSource);
   SysFreeString(bstrSQLInfo);
   return hr;
}

////////////////////////////////////////////////////////////////////////
// myDisplayErrorInfo
// Summary of Routines
//
// This function displays basic error information for an error object
// that doesn't support the IErrorRecords interface.
//
////////////////////////////////////////////////////////////////////////
HRESULT myDisplayErrorInfo
   (
   HRESULT        hrReturned,
   IErrorInfo *   pIErrorInfo,
   LPCWSTR        pwszFile,
   ULONG          ulLine
   )
{
   HRESULT        hr;
   BSTR           bstrDescription   = NULL;
   BSTR           bstrSource        = NULL;

   // Get the description of the error.
   CHECK_HR(hr = pIErrorInfo->GetDescription(&bstrDescription));

   // Get the source of the error -- this will be the window title.
   CHECK_HR(hr = pIErrorInfo->GetSource(&bstrSource));

   // Display this error information.
   wprintf(L"\nErrorInfo:  HResult: 0x%08x, Description: %s\nSource:
            %s\n" L"File: %s, Line: %d\n",
            hrReturned,
            bstrDescription,
            bstrSource,
            pwszFile,
            ulLine);

CLEANUP:
   SysFreeString(bstrDescription);
   SysFreeString(bstrSource);
   return hr;
}

////////////////////////////////////////////////////////////////////////
// myGetSqlErrorInfo
// Summary of Routines
//
// If the error object supports ISQLErrorInfo, get the SQL error
// string and native error code for this error.
//
////////////////////////////////////////////////////////////////////////
HRESULT myGetSqlErrorInfo
   (
   ULONG             iRecord,
   IErrorRecords *   pIErrorRecords,
   BSTR *            pBstr,
   LONG *            plNativeError
   )
{
   HRESULT           hr;
   ISQLErrorInfo *   pISQLErrorInfo   = NULL;
   LONG              lNativeError     = 0;

   // Attempt to get the ISQLErrorInfo interface for this error
   // record through GetCustomErrorObject. Note that ISQLErrorInfo
   // is not mandatory, so failure is acceptable here.
   // See ISQLErrorInfo
   CHECK_HR(hr = pIErrorRecords->GetCustomErrorObject(
            iRecord,                               // iRecord
            IID_ISQLErrorInfo,                     // riid
            (IUnknown**)&pISQLErrorInfo            // ppISQLErrorInfo
            ));

   // If we obtained the ISQLErrorInfo interface, get the SQL
   // error string and native error code for this error.
   if( pISQLErrorInfo )
      hr = pISQLErrorInfo->GetSQLInfo(pBstr, &lNativeError);

CLEANUP:
   if( plNativeError )
      *plNativeError = lNativeError;
   if( pISQLErrorInfo )
      pISQLErrorInfo->Release();
   return hr;
}


Appendix G: OLE DB Methods to Interfaces Table

This table lists each documented OLE DB method and all corresponding OLE DB interfaces with which it can be associated. References are provided to documentation for each interface and method pair. The OLE DB component column shows where the API reference and additional documentation for each method can be found, using the following abbreviations:

  • OLE DB
  • OLE DB Core
  • OLE DB for OLAP
  • OSP Toolkit
Method
Interface
 OLE DB component
Abort
IDBAsynchStatus::Abort
ITransaction::Abort

OLE DB

OLE DB

aboutToChangeCell
OLEDBSimpleProviderListener::aboutToChangeCell

OSP Toolkit
aboutToDeleteRows
Listener::aboutToDeleteRows

OSP Toolkit
aboutToInsertRows
OLEDBSimpleProviderListener::aboutToInsertRows

OSP Toolkit
AddColumn
ITableDefinition::AddColumn

OLE DB

AddColumns
IRowSchemaChange::AddColumns

OLE DB

AddConstraint
ITableDefinitionWithConstraints::AddConstraint

OLE DB

addDataSourceListener
IDataSource::addDataSourceListener

OSP Toolkit
AddErrorRecord
IErrorRecords::AddErrorRecord

OLE DB

addOLEDBSimpleProviderListener
OLEDBSimpleProvider::addOLEDBSimpleProviderListener

OSP Toolkit
AddRefAccessor
IAccessor::AddRefAccessor

OLE DB

AddRefChapter
IChapteredRowset::AddRefChapter

OLE DB

AddRefRows
IRowset::AddRefRows

OLE DB

AlterColumn
IAlterTable::AlterColumn

OLE DB

AlterIndex
IAlterIndex::AlterIndex

OLE DB

AlterTable
IAlterTable::AlterTable

OLE DB

Bind
IBindResource::Bind

OLE DB

Cancel
ICommand::Cancel

OLE DB

CanConvert
IConvertType::CanConvert
IDataConvert::CanConvert

OLE DB

OLE DB Core

cellChanged
OLEDBSimpleProviderListener::cellChanged

OSP Toolkit
ClearRowPosition
IRowPosition::ClearRowPosition

OLE DB Core
Commit
ITransaction::Commit

OLE DB
Compact
IJetCompact::Compact

Microsoft® Jet Provider
Compare
IRowsetLocate::Compare

OLE DB

Copy
IScopedOperations::Copy

OLE DB

CreateAccessor
IAccessor::CreateAccessor

OLE DB

CreateCommand
IDBCreateCommand::CreateCommand

OLE DB

CreateDataSource
IDBDataSourceAdmin::CreateDataSource

OLE DB

CreateDBInstance
IDataInitialize::CreateDBInstance

OLE DB Core
CreateDBInstanceEx
IDataInitialize::CreateDBInstanceEx

OLE DB Core
CreateIndex
IIndexDefinition::CreateIndex

OLE DB

CreateRow
ICreateRow::CreateRow

OLE DB

CreateSession
IDBCreateSession::CreateSession

OLE DB

CreateTable
ITableDefinition::CreateTable

OLE DB

CreateTableWithConstraints
ITableDefinitionWithConstraints::CreateTableWithConstraints

OLE DB

CreateView
IRowsetView::CreateView

OLE DB

DataConvert
IDataConvert::DataConvert

OLE DB Core
dataMemberAdded
IDataSourceListener::dataMemberAdded

OSP Toolkit
dataMemberChanged
IDataSourceListener::dataMemberChanged

OSP Toolkit
dataMemberRemoved
IDataSourceListener::dataMemberRemoved

OSP Toolkit
DataSourceListener
IDispatch::DataSourceListener

OSP Toolkit
Delete
IScopedOperations::Delete

OLE DB

DeleteColumns
IRowSchemaChange::DeleteColumns

OLE DB

DeleteCommand
ICommandPersist::DeleteCommand

OLE DB

deletedRows
OLEDBSimpleProviderListener::deletedRows

OSP Toolkit
DeleteRows
IRowsetChange::DeleteRows

OLE DB

deleteRows
OLEDBSimpleProvider::deleteRows

OSP Toolkit
DestroyDataSource
IDBDataSourceAdmin::DestroyDataSource

OLE DB

DropColumn
ITableDefinition::DropColumn

OLE DB

DropConstraint
ITableDefinitionWithConstraints::DropConstraint

OLE DB

DropIndex
IIndexDefinition::DropIndex

OLE DB

DropTable
ITableDefinition::DropTable

OLE DB

Execute
ICommand::Execute

OLE DB

find
OLEDBSimpleProvider::find

OSP Toolkit
FindCell
IMDFind::FindCell

OLE DB for OLAP
FindNextRow
IRowsetFind::FindNextRow

OLE DB

FindTuple
IMDFind::FindTuple

OLE DB for OLAP
FreeAxisInfo
IMDDataset::FreeAxisInfo

OLE DB for OLAP
GetApproximatePosition
IRowsetScroll::GetApproximatePosition

OLE DB

GetAvailableColumns
IColumnsRowset::GetAvailableColumns

OLE DB

GetAxisInfo
IMDDataset::GetAxisInfo

OLE DB for OLAP
GetAxisRowset
IMDDataset::GetAxisRowset

OLE DB for OLAP
GetBasicErrorInfo
IErrorRecords::GetBasicErrorInfo

OLE DB

GetBindings
IAccessor::GetBindings

OLE DB

GetCellData
IMDDataset::GetCellData

OLE DB for OLAP
GetChildRowset
IParentRowset::GetChildRowset

OLE DB

getColumnCount
OLEDBSimpleProvider::getColumnCount

OSP Toolkit
GetColumnInfo
IColumnsInfo::GetColumnInfo

OLE DB

GetColumns
IRow::GetColumns

OLE DB

GetColumnsRowset
IColumnsRowset::GetColumnsRowset

OLE DB

GetCommandStream
ICommandStream::GetCommandStream

OLE DB

GetCommandText
ICommandText::GetCommandText

OLE DB

GetConversionSize
IDataConvert::GetConversionSize

OLE DB Core
GetCreationProperties
IDBDataSourceAdmin::GetCreationProperties

OLE DB

GetCurrentCommand
ICommandPersist::GetCurrentCommand

OLE DB

GetCustomErrorObject
IErorRecords::GetCustomErrorObject
 OLE DB
GetData
IRowset::GetData

OLE DB

getDataMember
IDataSource::getDataMember

OSP Toolkit
getDataMemberCount
IDataSource::getDataMemberCount

OSP Toolkit
getDataMemberName
IDataSource::getDataMemberName

OSP Toolkit
GetDataSource
IDataInitialize::GetDataSource
IGetDataSource::GetDataSource

OLE DB Core

OLE DB

GetDBSession
ICommand::GetDBSession

OLE DB

GetDescription
IErrorInfo::GetDescription

OLE DB

GetErrorDescription
IErrorLookup::GetErrorDescription

OLE DB

GetErrorInfo
IErrorRecords::GetErrorInfo

OLE DB
GetErrorParameters
IErrorRecords::GetErrorParameters

OLE DB

getEstimatedRows
OLEDBSimpleProvider::getEstimatedRows

OSP Toolkit
GetFilter
IViewFilter::GetFilter

OLE DB

GetFilterBindings
IViewFilter::GetFilterBindings

OLE DB

GetGUID
IErrorInfo::GetGUID

OLE DB

GetHelpContext
IErrorInfo::GetHelpContext

OLE DB

GetHelpFile
IErrorInfo::GetHelpFile

OLE DB

GetHelpInfo
IErrorLookup::GetHelpInfo

OLE DB

GetIndex
IRowsetCurrentIndex::GetIndex

OLE DB

GetIndexInfo
IRowsetIndex::GetIndexInfo

OLE DB

GetInfo
IDCInfo::GetInfo

OLE DB Core
GetInitializationString
IDataInitialize::GetInitializationString

OLE DB Core
GetKeywords
IDBInfo::GetKeywords

OLE DB

GetLastVisibleData
IRowsetRefresh::GetLastVisibleData

OLE DB

GetLiteralInfo
IDBInfo::GetLiteralInfo

OLE DB

getLocale
OLEDBSimpleProvider::getLocale

OSP Toolkit
GetNextRows
IRowset::GetNextRows

OLE DB

GetOptions
ITransactionOptions::GetOptions

OLE DB

GetOptionsObject
ITransactionJoin::GetOptionsObject
ITransactionLocal::GetOptionsObject

OLE DB

OLE DB

GetOriginalData
IRowsetUpdate::GetOriginalData

OLE DB

GetParameterInfo
ICommandWithParameters::GetParameterInfo

OLE DB

GetPendingRows
IRowsetUpdate::GetPendingRows

OLE DB

GetProperties
ICommandProperties::GetProperties
IDBProperties::GetProperties
IRowsetInfo::GetProperties
ISessionProperties::GetProperties

OLE DB

OLE DB

OLE DB

OLE DB

GetPropertyInfo
IDBProperties::GetPropertyInfo

OLE DB

GetRangeRowset
IMDRangeRowset::GetRangeRowset

OLE DB for OLAP
GetRecordCount
IErrorRecords::GetRecordCount

OLE DB

GetReferencedRowset
IRowsetInfo::GetReferencedRowset

OLE DB

GetRestrictedColumnInfo
IColumnsInfo2::GetRestrictedColumnInfo

OLE DB

GetResult
IMultipleResults::GetResult

OLE DB

getRowCount
OLEDBSimpleProvider::getRowCount

OSP Toolkit
GetRowFromHROW
IGetRow::GetRowFromHROW

OLE DB

GetRowPosition
IRowPosition::GetRowPosition

OLE DB Core
GetRowsAt
IRowsetLocate::GetRowsAt

OLE DB

GetRowsAtRatio
IRowsetScroll::GetRowsAtRatio

OLE DB

GetRowsByBookmark
IRowsetLocate::GetRowsByBookmark

OLE DB

GetRowset
IDBSchemaRowset::GetRowset
IRowPosition::GetRowset

OLE DB

OLE DB Core
GetRowStatus
IRowsetUpdate::GetRowStatus

OLE DB

getRWStatus
OLEDBSimpleProvider::getRWStatus

OSP Toolkit
GetSchemas
IDBSchemaRowset::GetSchemas

OLE DB

GetSession
IGetSession::GetSession

OLE DB

GetSortOrder
IViewSort::GetSortOrder

OLE DB

GetSource
IErrorInfo::GetSource

OLE DB

GetSourceRow
IGetSourceRow::GetSourceRow

OLE DB

GetSourceRowset
IRow::GetSourceRowset

OLE DB

GetSourcesRowset
ISourcesRowset::GetSourcesRowset

OLE DB

GetSpecification
IMDDataset::GetSpecification
IRowsetInfo::GetSpecification
IViewChapter::GetSpecification
IViewRowset::GetSpecification

OLE DB for OLAP

OLE DB

OLE DB

OLE DB

GetSQLInfo
ISQLErrorInfo::GetSQLInfo

OLE DB

GetStatus
IDBAsynchStatus::GetStatus

OLE DB

GetTableDefinition
ITableCreation::GetTableDefinition

OLE DB

GetTransactionInfo
ITransaction::GetTransactionInfo

OLE DB

GetTransactionObject
ITransactionObject::GetTransactionObject

OLE DB

GetURLFromHROW
IGetRow::GetURLFromHROW

OLE DB

GetURLMapping
IRegisterProvider::GetURLMapping

OLE DB

getVariant
OLEDBSimpleProvider::getVariant

OSP Toolkit
GetView
IRowsetView::GetView

OLE DB

GetVisibleData
IRowsetResynch::GetVisibleData

OLE DB

Hash
IRowsetLocate::Hash

OLE DB

Idle
IIdle::Idle

Microsoft Jet Provider
Initialize
IDBInitialize::Initialize
IRowPosition::Initialize

OLE DB

OLE DB Core
insertedRows
OLEDBSimpleProviderListener::insertedRows

OSP Toolkit
InsertRow
IRowsetChange::InsertRow

OLE DB
insertRows
OLEDBSimpleProvider::insertRows

OSP Toolkit
InterfaceSupportsErrorInfo
ISupportErrorInfo::InterfaceSupportsErrorInfo

OLE DB

isAsync
OLEDBSimpleProvider::isAsync

OSP Toolkit
IsRowInChapter
IRowsetChapterMember::IsRowInChapter

OLE DB

IsSameRow
IRowsetIdentity::IsSameRow

OLE DB

ITransaction
ITransactionLocal::ITransaction

Microsoft Jet Provider
JoinTransaction
ITransactionJoin::JoinTransaction

OLE DB

LoadCommand
ICommandPersist::LoadCommand

OLE DB

LoadStringFromStorage
IDataInitialize::LoadStringFromStorage

OLE DB Core
MapColumnIDs
IColumnsInfo::MapColumnIDs

OLE DB

MapParameterNames
ICommandWithParameters::MapParameterNames

OLE DB

ModifyDataSource
IDBDataSourceAdmin::ModifyDataSource

OLE DB

Move
IScopedOperations::Move

OLE DB

msDataSourceObject
IDispatch::msDataSourceObject

OSP Toolkit
OnFieldChange
IRowsetNotify::OnFieldChange

OLE DB

OnLowResource
IDBAsynchNotify::OnLowResource

OLE DB

OnProgress
IDBAsynchNotify::OnProgress

OLE DB

OnRowChange
IRowsetNotify::OnRowChange

OLE DB

OnRowPositionChange
IRowPositionChange::OnRowPositionChange

OLE DB Core
OnRowsetChange
IRowsetNotify::OnRowsetChange

OLE DB

OnStop
IDBAsynchNotify::OnStop

OLE DB

Open
IRow::Open

OLE DB

OpenRowset
IOpenRowset::OpenRowset
IScopedOperations::OpenRowset

OLE DB

OLE DB

OpenViewChapter
IViewChapter::OpenViewChapter

OLE DB

OpenViewRowset
IViewRowset::OpenViewRowset

OLE DB

PositionOnBookmark
IRowsetBookmark::PositionOnBookmark

OLE DB

Prepare
ICommandPrepare::Prepare

OLE DB

PromptDataSource
IDBPromptInitialize::PromptDataSource

OLE DB Core
PromptFileName
IDBPromptInitialize::PromptFileName

OLE DB Core
Read
ISequentialStream::Read

OLE DB

RefreshVisibleData
IRowsetRefresh::RefreshVisibleData

OLE DB

ReleaseAccessor
IAccessor::ReleaseAccessor

OLE DB

ReleaseChapter
IChapteredRowset::ReleaseChapter

OLE DB

ReleaseErrors
IErrorLookup::ReleaseErrors

OLE DB

ReleaseRows
IRowset::ReleaseRows

OLE DB

removeDataSourceListener
IDataSource::removeDataSourceListener

OSP Toolkit
removeOLEDBSimpleProviderListener
OLEDBSimpleProvider::removeOLEDBSimpleProviderListener

OSP Toolkit
Reset
IDBBinderProperties::Reset

OLE DB

RestartPosition
IRowset::RestartPosition

OLE DB

ResynchRows
IRowsetResynch::ResynchRows

OLE DB

rowsAvailable
OLEDBSimpleProviderListener::rowsAvailable

OSP Toolkit
SaveCommand
ICommandPersist::SaveCommand

OLE DB

Seek
IRowsetIndex::Seek

OLE DB

SetColumns
IRowChange::SetColumns

OLE DB

SetCommandStream
ICommandStream::SetCommandStream

OLE DB

SetCommandText
ICommandText::SetCommandText

OLE DB

SetData
IRowsetChange::SetData

OLE DB

SetFilter
IViewFilter::SetFilter

OLE DB

SetIndex
IRowsetCurrentIndex::SetIndex

OLE DB

SetInfo
IDCInfo::SetInfo

OLE DB Core
SetOptions
ITransactionOptions::SetOptions

OLE DB

SetParameterInfo
ICommandWithParameters::SetParameterInfo

OLE DB

SetProperties
ICommandProperties::SetProperties
IDBProperties::SetProperties
ISessionProperties::SetProperties

OLE DB

OLE DB

OLE DB

SetRange
IRowsetIndex::SetRange

OLE DB

SetRowPosition
IRowPosition::SetRowPosition

OLE DB Core
SetSortOrder
IViewSort::SetSortOrder

OLE DB

SetURLMapping
IRegisterProvider::SetURLMapping

OLE DB

setVariant
OLEDBSimpleProvider::setVariant

OSP Toolkit
StartTransaction
ITransactionLocal::StartTransaction

OLE DB

stopTransfer
OLEDBSimpleProvider::stopTransfer

OSP Toolkit
transferComplete
OLEDBSimpleProviderListener::transferComplete

OSP Toolkit
Undo
IRowsetUpdate::Undo

OLE DB

Uninitialize
IDBInitialize::Uninitialize

OLE DB

Unprepare
ICommandPrepare::Unprepare

OLE DB

UnregisterProvider
IRegisterProvider::UnregisterProvider

OLE DB

Update
IRowsetUpdate::Update

OLE DB

Write
ISequentialStream::Write

OLE DB

WriteStringToStorage
IDataInitialize::WriteStringToStorage

OLE DB Core


Appendix H: Data Type Conversion Interfaces

This appendix describes data type conversion interfaces in OLE DB. For information about data types, see Appendix A: Data Types. For information about how consumers and providers use data types, see Chapter 13: Data Types in OLE DB.

  • Possible data type conversions are described in the section Data Conversion Library.
  • Data type conversion interfaces are described in the sections IDataConvert and IDCInfo.


Data Conversion Library

The OLE DB Data Conversion Library manages data type conversions for OLE DB consumers and providers and exposes the IDataConvert data conversion methods to consumers.

For more information about data type conversions, see "Library-Supported Data Type Conversions."


Library-Supported Data Type Conversions

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

Note Supported data conversions can be determined through the CanConvert method of the IDataConvert interface.

Click an entry in the following table to see the supported data type conversions.

Convert from data type:

I1 | I2 | I4 | I8
UI1 | UI2 | UI4 | UI8
R4 | R8 | BOOL | ERROR
BSTR | STR | WSTR | BYTES
DATE | DBDATE | DBTIME | DBTIMESTAMP
FILETIME | GUID | CY | DECIMAL
NUMERIC | VARNUMERIC | VARIANT | PROPVARIANT
IDispatch | IUnknown |  |


Conversions Supported from Data Type I1

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, DATE, DBDATE, DBTIME, DBTIMESTAMP, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type I2

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, DATE, DBDATE, DBTIME, DBTIMESTAMP, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type I4

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, BYTES, DATE, DBDATE, DBTIME, DBTIMESTAMP, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type I8

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BSTR, STR, WSTR, BYTES, FILETIME, DECIMAL, NUMERIC, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type UI1

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, DATE, DBDATE, DBTIME, DBTIMESTAMP, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type UI2

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, DATE, DBDATE, DBTIME, DBTIMESTAMP, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type UI4

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, BYTES, DATE, DBDATE, DBTIME, DBTIMESTAMP, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type UI8

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BSTR, STR, WSTR, BYTES, FILETIME, DECIMAL, NUMERIC, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type R4

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, DATE, DBDATE, DBTIME, DBTIMESTAMP, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type R8

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, DATE, DBDATE, DBTIME, DBTIMESTAMP, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type BOOL

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, DATE, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type ERROR

To data types:

ERROR, BSTR, WSTR, VARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type BSTR

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, BYTES, DATE, DBDATE, DBTIME, DBTIMESTAMP, FILETIME, GUID, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type STR

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, BYTES, DATE, DBDATE, DBTIME, DBTIMESTAMP, FILETIME, GUID, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type WSTR

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, BYTES, DATE, DBDATE, DBTIME, DBTIMESTAMP, FILETIME, GUID, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type BYTES

To data types:

I4, I8, UI4, UI8, BSTR, STR, WSTR, BYTES, GUID, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type DATE

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, DATE, DBDATE, DBTIME, DBTIMESTAMP, FILETIME, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type DBDATE

To data types:

BSTR, STR, WSTR, DATE, DBDATE, DBTIME, DBTIMESTAMP, FILETIME, VARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type DBTIME

To data types:

BSTR, STR, WSTR, DATE, DBDATE, DBTIME, DBTIMESTAMP, FILETIME, VARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type DBTIMESTAMP

To data types:

BSTR, STR, WSTR, DATE, DBDATE, DBTIME, DBTIMESTAMP, FILETIME, VARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type FILETIME

To data types:

I8, UI8, BSTR, STR, WSTR, DATE, DBDATE, DBTIME, DBTIMESTAMP, FILETIME, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type GUID

To data types:

BSTR, STR, WSTR, BYTES, GUID, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type CY

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type DECIMAL

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, DBDATE, DBTIME, DBTIMESTAMP, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type NUMERIC

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type VARNUMERIC

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, BSTR, STR, WSTR, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type VARIANT

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, ERROR, BSTR, STR, WSTR, BYTES, DATE, DBDATE, DBTIME, DBTIMESTAMP, FILETIME, GUID, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT, IDispatch, IUnknown

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type PROPVARIANT

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, ERROR, BSTR, STR, WSTR, BYTES, DATE, DBDATE, DBTIME, DBTIMESTAMP, FILETIME, GUID, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT, IDispatch, IUnknown

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type IDispatch

To data types:

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, BOOL, ERROR, BSTR, STR, WSTR, BYTES, DATE, DBDATE, DBTIME, DBTIMESTAMP, FILETIME, GUID, CY, DECIMAL, NUMERIC, VARNUMERIC, VARIANT, PROPVARIANT, IDispatch, IUnknown

Back to Library-Supported Data Type Conversions


Conversions Supported from Data Type IUnknown

To data types:

VARIANT, IDispatch, IUnknown

Back to Library-Supported Data Type Conversions


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 methods described in the following table.

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. This is a pointer to the underlying data type, based on 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 on 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 following:
  • 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.

Comments

The size of a fixed-width destination type can be discovered by passing a null pointer in the pSrc parameter. For example, the width of wType can be determined by adding the following code:

pConvert->GetConversionSize(wType, wType, 0, &ulWidth, 0);


IDCInfo

IDCInfo provides methods that can be used to furnish the Data Conversion Library with information, such as provider version, that can affect how conversions are performed. If IDCInfo::SetInfo is not called, the Data Conversion Library assumes the provider is an OLE DB 1.0–compliant provider.

IDCInfo can be obtained by calling QueryInterface from IDataConvert using IID_IDCInfo. IDCInfo includes the methods described in the following table.

Method | Description
GetInfo | Gets the Data Conversion Library's current provider information settings.
SetInfo | Sets the Data Conversion Library's provider information settings.


IDCInfo::GetInfo

HRESULT GetInfo(
   ULONG        cInfo,
   DCINFOTYPE   rgeInfoType[],
   DCINFO **    prgInfo);

Parameters

cInfo

[in]
The number of settings for which to return the information.

rgeInfoType

[in]
An array of DCINFOTYPE structures. The indicator of the information type. The Data Conversion component supports the DCINFOTYPE_VERSION information type.

prgInfo

[out]
A pointer to memory in which to return an array of DCINFO structures.

The DCINFO structure is as follows:

typedef struct   tagDCINFO {
   DCINFOTYPE    eInfoType;
   VARIANT       vData;
} DCINFO;

The elements of this structure are used as described in the following table.

Element | Description
eInfoType | The type of information.
vData | A VARIANT that contains the information to be set. For DCINFOTYPE_VERSION, the variant type is VT_UI4.


IDCInfo::SetInfo

HRESULT SetInfo(
   ULONG    cInfo,
   DCINFO   rgInfo[]);

Parameters

cInfo

[in]
The number of settings for which to set version information.

rgInfo

[in]
An array of cInfo DCINFO structures.


Appendix I: Row Position Object

This appendix describes OLE DB interfaces that manipulate a Row Position object, which embodies the concept of a current row in a rowset.

  • The IRowPosition interface manages the Row Position object.
  • The IRowPositionChange interface enables a consumer to be notified when the current row position changes.


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 IRowPosition::SetRowPosition to specify that the hRow be considered the current row and call IRowPosition::GetRowPosition 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.


IRowPosition Code Example

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

// Code to generate a pRowset

#include "msdaguid.h"
…
IRowPosition * pRowPosition = NULL;
HRESULT hr;

hr = CoCreateInstance(CLSID_OLEDB_ROWPOSITIONLIBRARY, NULL,
      CLSCTX_INPROC_SERVER, IID_IRowPosition,
      (LPVOID *)&pRowPosition);
if (FAILED(hr))
      return hr;

hr = pRowPosition->Initialize(pRowset);   // Initialize 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::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 pdwPositionFlags 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
IRowPosition::ClearRowPosition was called twice in a row.

There is no current rowset.

Comments

IRowPosition::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 IRowPosition::SetRowPosition fires the remaining phases.

IRowPosition::ClearRowPosition must be called prior to the first and any subsequent IRowPosition::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 can indicate one of the conditions described in the following table.
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 pdwPositionFlags, 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 IRowset::AddRefRows to increment the hRow's reference count and if the chapter is non-null, calls IChapteredRowset::AddRefChapter to increment the phChapter's reference count.

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


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, the row position object calls IUnknown::AddRef on it.


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 with 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 values described in the following table.
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
IRowPosition::SetRowPosition was called without first calling IRowPosition::ClearRowPosition.

There is no current rowset.

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

Comments

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

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

  • DBEVENTPHASE_SYNCHAFTER
  • DBEVENTPHASE_DIDEVENT or DBEVENTPHASE_FAILEDTODO

The row position object maintains a reference count on the both the hRow and the hChapter by respectively calling IRowset::AddRefRows for the hRow and IChapteredRowset::AddRefChapter for the hChapter when IRowPosition::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 row position object supports IConnectionPointContainer, and the consumer calls FindConnectionPoint for IID_IRowPositionChange 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 12: OLE DB Object 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

OLE DB Programmer's Reference

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 because 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 are not defined in this interface by returning S_OK or DB_S_UNWANTEDREASON.


OLE DB Glossary

A

accessor A data structure or group of structures created by the consumer that describes how row or parameter data from the data store is to be laid out in the consumer's data buffer, enabling providers to optimize access. An accessor is a collection of bindings. See also bindings, parameter accessor, reference accessor, and row accessor.

accessor handle A pointer that identifies an accessor.

actions rowset In OLE DB for OLAP, a type of schema rowset containing descriptive and content information that allows the client application to perform a procedure.

aggregated dataset In OLE DB for OLAP, a dataset containing data derived from a mathematical operation, such as a summation.

aggregation In COM, the ability of an object to present one or more of another object's interfaces as its own.

asynchronously populated rowset When a consumer requests immediate retrieval of rows from a data store, returns 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 row-returning method returns DB_S_ENDOFROWSET, even though additional rows may appear later. See also lazily populated rowset, randomly asynchronously populated rowset, and sequentially asynchronously populated rowset.

atomic operation An operation considered or guaranteed to be indivisible. Either the operation is uninterruptible or, if it is aborted, a mechanism is provided that ensures the return of the system to its state prior to initiation of the operation.

auto-commit mode The mode in which all work that is visible to the data store, and that is done within the scope of a session that is outside of a transaction, is immediately committed on each method call automatically (or without further intervention). Also called implicit mode. See also commit and manual-commit mode.

Automation A COM-based technology that enables dynamic binding to COM objects at run time. See also COM.

Automation error object An error object that that is exposed to applications or programming tools through Automation interfaces. See also OLE DB error object.

axis dimension In OLE DB for OLAP, a cube dimension for which data is retrieved for multiple members—for example, for a grouping of selected products. See also slicer dimension.

axis rowset In OLE DB for OLAP, a rowset that provides detailed information about the structure of each tuple on a dataset axis. See also dataset and tuple.

B

balanced hierarchy A hierarchy in which the parent of each member comes from the level immediately above the member. See also network hierarchy, ragged hierarchy, and unbalanced hierarchy.

base table A table that exists as a file on disk and from which a view, or virtual table, is derived. Also called underlying table. See also view.

binder object An OLE DB object that binds resources named by URLs to other OLE DB objects. See also provider binder object and root binder object.

binding An accessor element that an OLE DB provider uses to determine where and how to return or retrieve data in the consumer's buffer. Each binding associates, or binds, a single column or parameter to the consumer's buffer. A binding contains information including 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. See also accessor.

BLOB Acronym for binary large object. Any binary or character data that, in OLE DB, is treated by the OLE DB provider as a sequence of uninterpreted bytes. Such data is generally sent to and retrieved from the data store in parts. Also known as long data.

bookmark A saved placeholder value that identifies a row in a rowset and can later be used by the consumer to retrieve that particular row.

buffer A region of memory used to pass information, usually data, between two locations—for example, in OLE DB, between the consumer and provider.

Browse See Read Uncommitted.

C

cardinal number A number that indicates how many items there are in a set.

catalog An OLE DB object that contains one or more schemas.

chapter A value used to specify a group of related rows within a rowset.

chapter handle A reference to a chapter.

class ID A globally unique identifier (GUID) associated with a COM class.

clustered index An index where all the searchable columns are adjacent—applies to indexes that contain searchable columns about (and a reference enabling quick access to) a corresponding row in a rowset.

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 Acronym for Component Object Model. Microsoft's 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.

command object An OLE DB object that encapsulates a provider-specific text command.

command state One of four possible conditions applying to the status of a command object: Initial—indicating that no command text is set; Prepared—indicating that the command text is set and the command is prepared; Unprepared—indicating that the command text is set but the command is not prepared; Executed—indicating that the command text is executed.

command text The text command (usually an SQL statement) associated with a command object.

commit To make the changes in a transaction permanent. See also auto-commit mode and manual-commit mode.

COM object An object that conforms to Microsoft's Component Object Model (COM). A COM object is an instance of a COM class. Clients interact with a COM object only through its interfaces. See also COM and interface.

Component Object Model See COM.

concurrency The ability of more than one transaction or process to access the same data at the same time.

consumer In OLE DB, any piece of system or application code that consumes an OLE DB interface. See also provider.

coordinated transaction See distributed transaction.

cotype A concrete implementation of a group of COM objects, such as rowset objects or command objects. All COM objects that belong to a particular cotype must expose the mandatory interfaces in that cotype. In addition, they can expose any optional interfaces, including other COM interfaces.

cube In OLE DB for OLAP, the conceptual representation of multidimensional data The cube—or more appropriately, the hypercube—is also the central metadata object recognized by OLE DB for OLAP. See also hypercube and multicube.

Cursor Stability See Read Committed.

D

database A discrete collection of data in a DBMS (database management system). Can also refer to a specific DBMS. See also DBMS.

database management system See DBMS.

data definition language (DDL) A language that defines all attributes and properties of a database, especially record layouts, field definitions, key fields, file locations, and storage strategy.

data manipulation language (DML) A language that is used to insert data in, update, and query a database. Data manipulation languages are often capable of performing mathematical and statistical calculations that facilitate report generation.

data part A reference to one of the following data characteristics: the data value, the length of the data value, or the status of the data value.

data provider An OLE DB provider that directly exposes data from a data store. See also service provider.

dataset In OLE DB for OLAP, a grouping of data along a set of axes. The dataset encapsulates a multidimensional result set and is created by the execution of an MDX statement. See also MDX.

dataset object An OLE DB for OLAP object that expresses the dataset cube (a hypercube) and that consists of a set of value points, or cells, taken from the multidimensional data store cube (or cubes) and built into the dataset cube. See also cube and hypercube.

data source object In OLE DB, a COM object through which a consumer connects to a provider's underlying data store.

data store The data the user wants to access, such as the data in a database, file, or array.

data type A definition of a set of data that specifies the possible range of values of the set, the operations that can be performed on the values, and the way in which the values are stored in memory. See also type indicator.

data type conversion The changing of data from one type to another, as from an integer to a string.

DBMS Abbreviation for database management system. A layer of software between the physical database and the user. The DBMS manages all access to the database.

deferred column A rowset column for which the provider is not required to retrieve data from the data store until the consumer calls IRowset::GetData for that column. If the column contains a COM object, the provider need not retrieve data until a method used to access that object is called.

deferred update mode The update mode, automatically invoked for rowset objects that expose IRowsetUpdate, in which changes made through IRowsetChange are cached in the rowset and transmitted to the data store only when IRowsetUpdate::Update is called. Also called delayed update mode. See also immediate update mode.

delayed update mode See deferred update mode.

direct binding The process of associating an OLE DB object with a resource named by a URL.

dirty read Occurs when a transaction reads data that has not yet been committed. See also nonrepeatable read and phantom.

distributed transaction A transaction that updates data on two or more networked computer systems. Also known as a coordinated transaction.

document source provider A type of provider that manages access to folders and documents in a data store.

dynamic error An error message that is created in a run-time environment and cannot be predicted because the error lookup service is not database-specific.

dynamic error ID The identifier used by an error lookup service to identify a dynamic error, which is usually associated with a single error object.

E

enumerator object An OLE DB object that searches for and lists available data source objects and other enumerator objects. See also root enumerator object.

error code A return code that begins with E_ or DB_E_ and indicates that the method failed completely and was unable to do any useful work. Usually indicates a programming error.

error lookup service A provider-specific service that contains error messages and help file information.

error object An object that contains detailed information about an error. See also Automation error object and OLE DB error object.

event An action or occurrence, often generated by the user, to which a program might respond.

F

fact table A table used in OLE DB for OLAP that contains atomic values and rules as well as the aggregated values of related data.

field See column.

fixed-length data type A data type that is always stored in the same number of bytes, such as a 2-byte integer. See also variable-length data type.

flattened view In OLE DB for OLAP, a tabular view of the dataset from one or more cubes.

foreign key A column or columns in a table that match the primary key in another table within the same data store.

G

getting data In OLE DB, transferring data from the provider to the consumer, as in getting column or output parameter data.

GUID (globally unique identifier) In COM, a 16-byte value that uniquely identifies an interface to an object across all computers and networks. Also known as UUID (universally unique identifier).

H

hierarchical rowset A collection of rowsets that are relationally linked together by using chapters.

HRESULT A type of return code, returned by an OLE DB method, that indicates the overall success or failure of that method. Can be of either of two classes: success and warning codes; or error codes.

hypercube In OLE DB for OLAP, a multidimensional data model in which all data appears logically as a single cube. All parts of the manifold represented by a hypercube have identical dimensionality. See also cube and multicube.

I

immediate update mode The update mode, applying to rowsets that do not expose IRowsetUpdate, in which changes made through IRowsetChange are immediately transmitted to the data store. See also deferred update mode.

index rowset A rowset built over an index in a data store and generally used in conjunction with a rowset built over a base table in the same data store. Each row in an index rowset contains a bookmark that points to a row in the rowset built over the corresponding table.

initialize To change the state of an enumerator or data source object so that 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/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.

input parameter A parameter in a text command for which the consumer supplies a value to the provider.

integrated index An index that is part of the rowset it references.

interface In COM-based technologies, a collection of related public functions that provide access to a COM object. The set of interfaces on a COM object composes a contract that specifies how programs and other objects can interact with the COM object. See also COM and COM object.

IID Abbreviation for interface identifier. 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.

IPersist* object A COM object that allows control of how an object's state is saved.

isolation level See transaction isolation level.

Isolated See Serializable.

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 In database management, an identifier for a record or group of records in a data store. See also foreign key and primary key.

key value bookmark A bookmark that uses some combination of columns (not necessarily the primary keys of the base tables) that form a unique key for each row in an ordered rowset. See also numeric bookmark.

L

lazily populated rowset May return a rowset prior to completely populating the rows, but row-returning methods wait, if necessary, until they can return all of the requested rows. See also asynchronously populated rowset, randomly asynchronously populated rowset, and sequentially asynchronously populated rowset.

listener A consumer, usually of a rowset object that is being shared by more than one consumer, that requires notification of any updates to that rowset by any consumer having access to the rowset.

literal set In OLE DB for OLAP, a multidimensional expression denoting a set that is specified by an enumerated list of tuples. See also tuple.

long data See BLOB.

M

manual-commit mode The mode in which all work that is visible to the data store, and that is done within the scope of a session that has entered a transaction, is part of that transaction and is committed or aborted as a single unit. See also auto-commit and commit.

MDX Abbreviation for multidimensional expressions. In OLE DB for OLAP, MDX provides a syntax for specifying a dataset and can be viewed as a set of macros mapped to the underlying SQL language.

metadata Data about data. For example, the data type, length, and updatability of a column in a rowset.

member In OLE DB for OLAP, a data element tied to a specific dimension on a cube.

moniker A very general abstraction of file name and file path. Monikers are more precise than file names because they can identify an object within a document. Monikers can be combined into composite monikers in the same way a directory is added to the file name to get the complete path.

multicube In OLE DB for OLAP, a multidimensional data model in which all data is segmented into a set of smaller cubes, each of which is composed of a subset of the available dimensions. See also cube and hypercube.

multidimensional data provider (MDP) An OLE DB provider that accesses a multidimensional database and returns data in a dataset object that can have more than two dimensions. See also tabular data provider.

multidimensional data store A data store in which the data is stored in inherently multidimensional structures. See also relational data store.

multidimensional expressions See MDX.

multiple results object An OLE DB object, through which multiple results (row counts, rowset objects, or row objects) can be retrieved, that contains command text comprising multiple, separate text commands—such as a batch of SQL statements—or that contains a text command having more than one set of parameters.

N

namespace The programming domain within which an object name must be unique.

nested transaction Occurs when a transaction (an atomic unit of work) is begun within the scope of another transaction.

network hierarchy A hierarchy in which nodes can contain more than one parent, such as a family tree. See also balanced hierarchy, ragged hierarchy, and unbalanced hierarchy.

next fetch position The position of the next row that will be fetched by a call to IRowset::GetNextRows.

nonrepeatable read Occurs when a transaction reads the same row twice but gets different data each time. See also dirty read and phantom.

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 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 null value.

null value Having no explicitly assigned value. In particular, a null value is different from a zero or a blank. See also null pointer.

numeric bookmark Based on a position or index into the rows selected for a rowset, a bookmark value that uses a unique number to identify a row. See also key value bookmark.

O

object In COM, a programming structure encapsulating both data and functionality, which 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 COM and interface.

OLE DB A specialized set of COM interfaces that expose data from a variety of data stores, both relational and nonrelational.

OLE DB error object An error object used by OLE DB objects to return an error. OLE DB error objects are extensions of Automation error objects.

OLE DB object A COM object defined in OLE DB. The COM objects defined in OLE DB are enumerators, data source objects, sessions, commands, rowsets, multiple results objects, transaction objects, and transaction options objects.

OLE DB provider Any software component that exposes an OLE DB interface.

optimistic concurrency A strategy to increase the simultaneous modifications of data, in which rows of data are not locked. The term derives its name from the optimistic assumption that collisions between transactions will rarely occur. See also pessimistic concurrency.

ordinal number A number whose form indicates position in an ordered sequence of items, such as first, third, or twentieth.

output parameter A parameter in a text command for which the provider returns a value to the consumer.

P

parameter accessor Specialized accessor created and used only on command objects to access parameter data that describes bindings to parameters in the command text. See also accessor, reference accessor, and row accessor.

pending change A change, such as an insert, update, or delete, that has been cached in a rowset and not yet transmitted to the data store. Also refers to a row state in which a change is pending. See also deferred update mode, immediate update mode, and row state.

pessimistic concurrency A strategy for implementing serializability, in which rows of data are locked so that other transactions cannot change them. Also called locking. See also optimistic concurrency.

phantom A row that matches the search criteria of a previous transaction but is not initially seen until that transaction is reexecuted. See also dirty read and nonrepeatable read.

phase A step in a sequence of notifications caused by a single event.

phased event A notification method that is called multiple times, each time with a different phase in the sequence.

precision The degree of detail expressed by a number, determined by how many base 10-digits are used to indicate the number's value.

preserved rowset A rowset in which all previously defined functionality is retained after a transaction ends.

primary key A column or columns that uniquely identifies a row in a table.

property Attributes of an object—for example, the maximum number of rows in a rowset that can be active at one time.

property group A set of logically related properties that could apply to an OLE DB object such as a rowset or a data source object.

property set A set of all OLE DB properties that share the same GUID.

provider See OLE DB provider.

provider binder object An OLE DB object that performs direct binding operations on the URL namespace for which it is registered. See also binder object and root binder object.

Q

query The process of extracting data from a database and presenting it for use.

R

ragged hierarchy A hierarchy in which the parent of a member can come from any level above the level of the member, not just from the level immediately above. Also called ragged-balanced hierarchy. See also balanced hierarchy, network hierarchy, and unbalanced hierarchy.

randomly asynchronously populated rowset An asynchronously populated rowset in which newly retrieved rows can be inserted randomly within the rowset. See also asynchronously populated rowset, lazily populated rowset, and sequentially asynchronously populated rowset.

range rowset object An OLE DB rowset object in which each row corresponds to a cell in a dataset object and in which each selected cell property appears as a column in the rowset. See also dataset object and rowset object.

Read Committed A transaction isolation level at which a specific transaction 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. Also called Cursor Stability. See also dirty read, nonrepeatable read, phantom, Read Uncommitted, Repeatable Read, Serializable, and transaction isolation level.

Read Uncommitted A transaction isolation level at which a specific transaction can see uncommitted changes made by other transactions. At this level of isolation, dirty reads, nonrepeatable reads, and phantoms are all possible. Also called Browse. See also dirty read, nonrepeatable read, phantom, Read Committed, Repeatable Read, Serializable, and transaction isolation level.

reason Detailed information that is provided about an event and that is made available to the consumer.

record See row.

reference accessor An accessor that enables a consumer to get rowset data directly from the provider's buffer and enables providers to get input parameter data directly from the consumer's buffer. See also accessor.

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.

relational data store A data store in which relational data is stored as a set of tables, each table having at least one column that ties the data in that table to another table or other tables. See also multidimensional data store.

Repeatable Read A transaction isolation level at which a specific transaction 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. See also dirty read, nonrepeatable read, phantom, Read Committed, Read Uncommitted, Serializable, and transaction isolation level.

resource pooling In OLE DB, a management technique where a collection of previously used resources (that is, a resource pool), such as connections to a data source, are typically stored and reused by an application as they are needed. When the resource is no longer needed, it is not destroyed but returned to the resource pool instead.

resource rowset A rowset supported by a special class of provider, called a document source provider, that manages folders and documents. The resource rowset contains columns that describe characteristics of the document instead of the actual document itself.

restrictions In OLE DB, a set of parameter values that limit the number and values of the columns in a schema rowset. Also called restriction columns.

return code The value returned by an OLE DB method.

root binder object An OLE DB object that oversees the direct binding process and manages the mapping of URL namespaces to OLE DB providers. See also binder object, direct binding, and provider binder object.

root enumerator object An OLE DB object that identifies the data source objects and enumerators listed in the registry. See also enumerator object.

row A set of related columns that describe a specific entity in a data store. Also known as a record.

row accessor Can be created on command and rowset objects but can be used only on rowset objects. Row accessors describe the bindings to columns in the rowset and, if created on a command object, are inherited by any rowsets created by that command object. See also accessor, parameter accessor, and reference accessor.

row handle An identifier in the rowset, of data type HROW, used to access a specific data row in the consumer's buffer.

row object An OLE DB object that contains a set of columns of data. A row object can represent a row in a rowset, the result of a singleton SQL SELECT statement, or a node in a tree-structured namespace, such as a file in a directory or a message in a mail folder.

rowset object An OLE DB object that contains a rowset (a set of rows), which is composed of columns of data.

row state Can be either a nonpending change state (Unchanged or Invalid) or a pending change state (Pending Insert, Pending Update, or Pending Delete). See also pending change.

S

schema An object or collection of database objects that contain structural information, or metadata, about a database.

schema rowset object A predefined rowset object that provides metadata information about the structure of a database or of a table in the database.

scoped operation A function performed on a row or stream element that is limited to that element and a predetermined number of subordinate elements.

self bookmark A bookmark in a rowset, stored in column 0 of a row, that is used to return to that row.

sequentially asynchronously populated rowset An asynchronously populated rowset in which newly retrieved rows are always appended to the end of the rowset. See also asynchronously populated rowset, lazily populated rowset, and randomly asynchronously populated rowset.

Serializable A transaction isolation level at which a specific transaction guarantees that all concurrent transactions 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. Also called Isolated. See also dirty read, nonrepeatable read, phantom, Read Committed, Read Uncommitted, Repeatable Read, and transaction isolation level.

service provider A provider that does not directly expose data but instead facilitates the manipulation of data, such as query processing. Used in conjunction with data providers.

session object An OLE DB object that provides methods for creating command objects and rowset objects and for creating and modifying tables and indexes. Session objects also define transaction scope and can be used to create transaction objects, which are used to control nested transactions.

setting data In OLE DB, transferring data from the consumer to the provider, as in setting column or input parameter data.

slicer dimension In OLE DB for OLAP, a cube dimension for which data is retrieved for a single member—for example, data pertaining to a single geographical location. See also axis dimension.

SQL Structured Query Language. A language used by relational databases to query, update, and manage data. Text commands are often formatted for SQL.

solve order In online analytical processing, the order in which applied formulas are resolved.

star schema In OLE DB for OLAP, a special schema configuration resulting from the combination of a fact table and a set of related dimension tables. See also fact table.

static error A hard-coded error (text) that is stored by a specific provider lookup service and most commonly tied to a single data store.

stream object An OLE DB object that encapsulates the contents of files, e-mail messages, or other stream-based objects.

success code A class of return code, of the type HRESULT, that begins with S_ or DB_S_ and that indicates the success of a method call.

T

table A collection of rows in the data store. Types of tables include base tables and views. See also base table and view.

tabular data provider (TDP) An OLE DB provider that accesses relational databases and returns only two dimensional data in a rowset object. See also multidimensional data provider.

target type The intended or resulting data type after a data conversion in OLE DB.

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 the extent to which changes made outside a transaction are visible to that transaction.

transaction object An OLE DB object used to represent a nested transaction. See also nested transaction.

transaction options object An OLE DB object used to define various options for a transaction.

transmitted change A change that has been sent to the data store. See also pending change.

tree operation An operation that applies to data represented by a tree-shaped namespace, such as a file or e-mail system—for example, a "delete" operation on a directory in a file system that removes all of its files and subdirectories. See also namespace, tree operator, and tree-shaped namespace.
tree operator A method that affects data represented as a tree-shaped namespace, such as a file or e-mail system—for example, a "move" method that destroys a directory and all its subdirectories at one location in a file system and then re-creates them at another location. See also namespace, tree operation, and tree-shaped namespace.
tree-shaped namespace Information, which when graphed resembles the branches of a tree—for example, the shape of files and directories in a Microsoft® Windows® File Open dialog box. See also namespace, tree operation, and tree operator.

tuple In OLE DB for OLAP, in a multidimensional result set, an ordered collection of members from different dimensions that can be represented as a compound coordinate on a dataset axis.

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

unbalanced hierarchy A hierarchy in which the concept of levels does not apply. See also balanced hierarchy, network hierarchy, and ragged hierarchy.

underlying table See base table.

uninitialize To change the state of an enumerator or data source object so that 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.

update To change an existing row of data in the data store. Also, to transmit pending changes to the data store.

UUID (universally unique identifier) See GUID (globally unique identifier).

V

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.

view A virtual table, usually created as a subset of columns from one or more tables. Views are derived from base tables but can also be derived from other views. A view differs from a base table in that the view contains no data of its own. The data that appears in the view is actually stored in the base tables.

view object An OLE DB object that defines a subset of the rows and columns from a rowset. A view object contains no data of its own.

W

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 all functionality of a COM object is lost and for which, usually, the only valid consumer action on that object is to release it.


Microsoft OLE DB Providers Overview

OLE DB Providers

OLE DB Providers Overview

OLE DB architecture is built upon the precept of an application accessing diverse data stores through a small application built specifically for that purpose. The application that uses OLE DB functionality is called the consumer, while the one that accesses the data by exposing OLE DB interfaces is called the provider.

Providers fall into two categories: those providing services and those providing data. A service provider encapsulates a service by producing and consuming data through OLE DB interfaces. It does not own its own data and, in reality, serves a dual role of consumer and provider. A service provider may also be further defined as a service component, which must work in conjunction with other service providers or components. The Cursor Service for OLE DB is an example of this. A data provider owns its own data and exposes it in tabular form. It is not dependent on other providers—service or data—to provide data to the consumer.

In addition to these two categories of providers, a special class of providers—called document source providers—has been introduced in OLE DB 2.5. These providers manage folders and documents and are explained in "Resource Rowsets and Document Source Providers" in Chapter 9 of the OLE DB Programmer's Reference.

Providers can be simple or complex, depending on the needs of the consumer. They can return data intact or perform operations on the data. A set of mandatory interfaces is required to expose the minimal level of functionality supported by the data store. OLE DB providers are not required to support the complete OLE DB interface. Optional interfaces can be used for additional functionality.

Because providers are made up of COM components that contain a series of standard interfaces, consumers can access them using any programming language.

Various forms of assistance are available for provider implementation and use.

For more information about | See
OLE DB architecture | OLE DB Programmer's Reference
Consumers and providers architecture | Conceptual Programming Models in OLE DB
Guidelines for providers | Rules and guidelines for building and using providers are stated in specific provider documentation. Each OLE DB interface description also contains implementation guidelines.
Provider templates | MSDN Library\Visual C++ Documentation\Reference\Microsoft Foundation Class Library and Templates\OLE DB Templates\Provider Technical article Delivering Data to Universal Data Access via the OLE DB Provider Templates for Visual C++ 6.0 at the Microsoft® Visual C++® Web site.
Simple Provider Toolkit designed for quickly building OLE DB providers over simple data stores | OLE DB Simple Provider (OSP)
Samples for writing consumers and providers, including Rowset Viewer and Table Copy | OLE DB Samples
Provider documentation template | The OLE DB Provider Standard Documentation Template

Microsoft OLE DB providers are available from the Microsoft OLE DB Web site at http://www.microsoft.com/data/oledb/. Documentation exists for those listed in the following tables. Other providers are available from other vendors.

Data Providers

Source data | OLE DB data provider | Purpose
ODBC | Microsoft OLE DB Provider for ODBC | Exposes data traditionally accessed using ODBC.
Oracle | Microsoft OLE DB Provider for Oracle | Accesses Oracle 7.3 and Oracle8 data.
Microsoft Jet database | Microsoft OLE DB Provider for Microsoft Jet | Accesses data from Access, Paradox, dBASE, Excel, FoxPro®, and more.

Service Providers

Source data | OLE DB service provider | Purpose
Any | Microsoft OLE DB Persistence Provider | Persists retrieved data to a local file.
Remote | Microsoft OLE DB Remoting Provider | Allows remote data exchange through existing providers.
Microsoft Active Directory Service | Microsoft OLE DB Provider for Microsoft Active Directory Service (ADS) | Provides access and management services for network resources and access to heterogeneous directory services. See the Microsoft Windows® 2000 Server Web site for more information.
Microsoft Indexing Service (formerly known as Microsoft Index Server) | Microsoft OLE DB Provider for Microsoft Indexing Service | Provides access to indexed file system and Web data using SQL queries. See the technical article Programmer's Guide to using Index Server Provider in VC++ at the Microsoft Visual C++ Web site.
Any non-multidimensional | Microsoft Data Shaping Service for OLE DB | Creates hierarchical recordsets from a tabular format.
AS/400 and VSAM | Microsoft OLE DB Provider for AS/400 and VSAM | Allows access to mainframe AS/400 or VSAM data through Microsoft's object-based OLE DB data access technology. See the Microsoft SNA Server Web site for more information.
Any | Microsoft Cursor Service for OLE DB | Caches result sets on the client or the server.
Any | Visual C++ 6.0 OLE DB Template Library | Examples and templates for writing consumers and providers in Microsoft Visual C++. See the Microsoft Visual C++ Web site for more information.

Additional Resources

See the "Technical Materials" page of the Universal Data Access Web site at http://www.microsoft.com/data/techmat.htm or the "Microsoft Data Access Technical Articles" section of the Microsoft Data Access Components (MDAC) SDK for late-breaking information about MDAC SDK component technologies.

Other articles can be found in the Microsoft Developer Network (MSDN™) Library. Detailed articles concerning known provider limitations or workarounds can be found in the MSDN Knowledge Base.

For information on support for Microsoft Data Access Components, see "Getting Help from Microsoft Product Support Services."


OLE DB Simple Provider (OSP) Toolkit

The Microsoft® OLE DB Simple Provider (OSP) Toolkit offers a framework that simplifies the task of writing OLE DB providers over simple data and exposing that data to any OLE DB consumer (such as ActiveX® Data Objects or any OLE DB–compliant application or control). Using the OSP Toolkit, developers can quickly build OLE DB providers over simple data sources (for example, in-memory arrays) and can expose OLE DB interfaces without having to build an entire OLE DB provider from scratch. The OSP Toolkit is language neutral, supporting implementations in Microsoft Visual Basic®, Microsoft Visual C++®, and Microsoft Visual J++®.

The OSP Toolkit employs several new Microsoft component technologies, including the following:

  • OLE DB Simple Provider interfaces. Also part of the Microsoft Internet Explorer SDK, this set of easily implemented application programming interfaces (APIs) is used by the OSP Toolkit to map OLE DB requests to simple data (for example, arraylike data) and raise notifications on changes to that 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 receive 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 (the OSP Language Reference is supplied with the Toolkit), implementing the OSP 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 the Toolkit are less functional than a full OLE DB provider written using the native OLE DB interfaces. You might want to read about these limitations before using the Toolkit.

The OSP is part of the MDAC SDK, which is installed with the Microsoft Windows® 2000 operating system or can be downloaded from the Platform SDK site on MSDN. For more information about the installation location of OSP files, see "File Listing."

For more information about developing a full OLE DB provider, see "Using the OLE DB Provider Templates" in the Visual C++ documentation.


Toolkit 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 OSP Toolkit consist of an OLE DB Simple Provider DLL (Msdaosp.dll) that interoperates with one or more implementations of the Microsoft® Internet Explorer OSP API, found in the OSP Language Reference. The OSP Toolkit supplies interfaces for basic provider capabilities over simple data and 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 with limited data conversion capabilities.

The following illustration shows, at a high level, the architecture of an OLE DB provider written with the OSP Toolkit.

Note OSP implements its own data source object through IDataSource or msDataSourceObject. The OSP implementation of the data source object is different from the OLE DB implementation. In this document, the OLE DB object is called a "data source object," whereas the OSP implementation is referred to as the "OSP data source object." In the following illustration, msDataSourceObject is used to create the OSP data source object, which appears above Msdaosp.dll.

The following sections describe the main components shown in the preceding illustration and explain how these components work.

ADO and Other OLE DB Consumers

These can be any OLE DB–compliant applications or controls on a computer on which the provider is registered. If you are using ADO Recordset objects as a consumer, you code the provider identification information in the connection string and receive the data in the form of ADO Recordset objects.

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 the 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 object.
  • Makes OLE DB notifications available. This information can be passed on to consuming objects, informing them whether data currently being manipulated has changed.

The OLE DB Simple Provider 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 as well as multiple data sources and their OSP implementations, and to 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.

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 the following methods that allow the OLE DB Simple Provider DLL to request the desired OSP implementation and, therefore, the desired data source:

  • If you are writing the provider in Microsoft® Visual C++®, you will use the IDataSource interface and its getDataMember method to request an OSP.
  • If you are writing the provider in Microsoft Visual Basic® or Microsoft Visual J++®, you will use the msDataSourceObject method along with the appropriate IDispatch methods to request an OSP.

In the above implementations, 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 accomplish this in one of the following ways:

  • Complete the RegisterProvider.reg file supplied with the Toolkit.
  • Supply an established personal registration code for the OSP Data Object.

After the above step has been completed, the developer must register the OSP Data Object by using Regsvr32.exe (in Visual Basic and Visual C++ implementations) or Javareg (in Visual J++).

The OSP Data Object also sends OSP 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 are sent depends on which of the objects (Msdaosp.dll or an external data consumer) obtained the instance of the OSP data source object.


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 illustration shows how notifications are issued and received in the OLE DB Simple Provider framework.

Following are the three levels of notifications that occur in this framework:

  • OLE DB notifications
  • OSP notifications
  • Data source notifications

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 OLE DB Programmer's Reference supplied with the MDAC 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 OLE DB Simple Provider 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 OLEDBSimpleProvider::setVariant OSP method), it must also issue the OSP notifications (for example, the OLEDBSimpleProviderListener::cellChanged method).

Note The OSP should always send the appropriate pre-notification event using the OSP OLEDBSimpleProviderListener interface (aboutToChangeCell, aboutToDeleteRows, or aboutToInsertRows). If the change succeeds, the OSP also should send the appropriate post-notification using the same interface with the following methods: insertedRows, changedCells, and deletedRows.

When a failure occurs, notifications are not sent to the listener.

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 OLE DB Simple Provider 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, 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. The OLE DB Simple Provider 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 (a "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. Therefore, developers do not need to check the results of a pre-notification to validate whether the data change has actually occurred.

The OLE DB Simple Provider 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 accommodate multiple listeners by performing the following services:

  • Maintaining a list of listeners when adding or removing data source or OSP listeners.
  • 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 following services:

  • 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 can 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 having to spend time building 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 you also build a foundation for implementing data access strategies at a higher level.


ActiveX Data Objects

Microsoft® 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. The primary benefits of ADO are ease of use, high speed, low memory overhead, and a small disk footprint.

By using the OSP Toolkit with ADO, you build a foundation for implementing flexible data access strategies at a higher level. For example, you can combine the ease of application programmability of ADO with the OSP Toolkit's ease of developing providers and quickly build end-to-end, single, or multitiered applications that address your corporate, intranet, Internet, or enterprisewide 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 MDAC SDK.


Connecting to a Simple Provider Using 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 depend on whether your OSP is registered as a full OLE DB provider.

To make a connection if your OSP is registered as a full OLE DB provider

  1. Supply the ProgID of your OSP—in this case, SampleOSP_VB—as a parameter to the Provider property of the ADO Connection object. For more information about registering your provider, see "Full Registration."
  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:\Platform SDK\Samples\DataAccess\Samples\Osp\Customer.txt", con

To make a connection if your OSP is not registered as a full OLE DB provider but its data source object is registered

  1. Supply the ProgID of the OLE DB Simple Provider DLL (the mapping layer)—in this case, MSDAOSP—as the Provider property of the ADO Connection object. For more information about registering your provider, see "Registration Without a .reg File."
  2. Supply the name of your OSP's data source object—in this case, SampleOSP_VB.MyDataSource—as the DataSource property of the Properties collection on the ADO recordset 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:\Platform SDK\Samples\DataAccess\Osp\Customer.txt", con


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 OSP 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 OLE DB Simple Provider DLL.

If you require greater functionality than can be achieved with the OSP Toolkit or if you are working with complex data, consider building an OLE DB provider using Microsoft® Visual C++® rather than with the OSP interfaces. For example, providers built with the OSP Toolkit do not support command capability. If your provider must accept commands, 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

The OLE DB Simple Provider (OSP) 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 Microsoft® Visual Basic® and in Microsoft Visual C++®, and a Java class in Microsoft 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 OSP Toolkit contains a set of code samples for each visual language.
  2. Implement OSP. Most of your work will be in implementing the OSP functions described in the OSP Language Reference. You can use the OSP 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 an OSP 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 registration template (the RegisterProvider.reg file) supplied with the OSP Toolkit and import that file's settings into the 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.)

Asynchronous Programming Considerations

When implementing an OSP simple provider, it is the consumer's responsibility to marshal notification between threads. The consumer has two choices of threading models: free-threaded or apartment. Each is explained in "Consumer Threading Responsibilities," in the OLE DB Programmer's Reference. The way notifications are handled in the two threading models is described in "Threading and Notifications."

Asynchronous processing enables methods to return immediately without blocking on the calling thread. Consumers must explicitly request asynchronous processing when initializing a data source object or when opening or populating a rowset. Otherwise, the provider can perform asynchronous operations in the background but must behave synchronously, blocking if necessary, until the underlying asynchronous operation completes. For more information about asynchronous behavior in OLE DB, see the topics "Asynchronous Processing" and "Asynchronous Rowset Population. "

If an asynchronous OLE DB simple provider spawns a thread to populate rows and sends notifications from within that thread, it cannot call notification methods on the OLEDBSimpleProviderListener interface for that spawned thread. Instead, in the implementation of OLEDBSimpleProvider::addOLEDBSimpleProviderListener, the OSP should marshal IID_IOLEDBSimpleProviderListener to a stream object. Marshaling can be accomplished using CoMarshalInterThreadInterfaceInStream, or simply, CoMarshalInterface. The OSP may need a list of the listeners since multiple listeners are possible, depending on whether the threads are multicast or single cast.

Before calling any notification methods, within the asynchronous thread, the OSP must unmarshal the stream object into an interface within that thread. Because the streams were set up before the thread was started, the procedure is as follows:

  1. Call CoGetInterfaceandReleaseStream, or simply, CoUnmarsalInterface, for IID_IOLEDBSimpleProviderListener.
  2. Call the notification methods directly from the interface (now that it has been marshaled to this thread).
  3. If the threading model is apartment, which means CoInitialize was called, the consumer must have a message pump within the apartment model thread to push a copy of the same message (or notification) onto each thread. This is done by using MsgWaitForMultipleObjects to pump any waiting COM messages through each thread between the population of rows.


Using the Visual Languages

Although the process of building a provider with the Microsoft® OLE DB Simple Provider (OSP) 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.


Language Differences

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, Customer.txt (located in ..\Samples\osp), 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 OSP sample for that language.

  • Visual Basic
  • Visual C++
  • Visual J++


Microsoft Visual Basic

Information and Guidelines

Following is an outline of the basic steps needed to build an OLE DB Simple Provider in Visual Basic.

To build an OSP in Visual Basic

  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 OSP Toolkit to implement all the OSP methods. You can view the sample code in the file MyOSPObject.cls as a reference.
  5. 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 named "MyOSPClass" that implements the msDataSourceObject method. The ProgID will be "MyOSPProject.MyOSPClass". The Version-Independent ProgID will be "MyOSPProject.MyOSPClass.1".


Visual Basic: Code Sample Information

This sample uses class properties introduced in Visual Basic 6.0. It will not work with earlier versions of Visual Basic. For more information about building simple providers with Visual Basic 5.0, refer to the section Microsoft Visual Basic.

The Visual Basic Simple Provider code sample is located in ..\Samples\Dataaccess\Osp\Vb and consists of the files described in the following table.

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.


Microsoft Visual C++

Information and Guidelines

Following is an outline of the basic steps needed to build an OLE DB Simple Provider in Visual C++.

To build the OSP component using Visual C++

  1. Create an Automation object with standard registration.
  2. Implement IDataSource on the object.
  3. 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++, be sure to point to the Include directory where the OLE DB and OLEDBSimpleProvider header files are located.
  • When linking in Visual C++, be sure to add the path for the LIB files so that you can link the LIB file containing the IIDs for the OSP interfaces and classes.
  • In Visual C++ implementations, use IDataSource to expose the correct OSP implementation to the OLE DB 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 sample is set up to have MyOSPObject as the implementation of the sample provider and MyDataSource as the implementation of the OSP data source object.

The Microsoft Visual C++ simple provider code sample is located in ..\Samples\DataAccess\Osp\Vc and consists of the files described in the following table.

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 Visual C++ routines used for this sample.
Common.h | Set of common Visual C++ macros used for this sample.


Microsoft Visual J++

Information and Guidelines

Following is an outline of the basic steps needed to build an OLE DB Simple Provider in Visual J++ 6.0.

To build the OSP component using Visual J++

  1. Create a Java application.
  2. Implement IDataSource on the Java application.
  3. Implement the methods for OLEDBSimpleProvider with the object (at least once).

Registration

Visual J++, when building a COM DLL, will generate the ProgID for the OSP Data Object. This ProgID is <Project Name>.<class name of the OSP Data Object>. This is the same way that Visual Basic generates the ProgID.

Programming Notes

  • In Visual J++ implementations, use IDataSource and its methods (such as GetDataMember) to expose the correct OSP implementation to the OLE DB 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.

    Note To run the OSP Java sample, ensure that the virtual machine can locate the sample's compiled class files by adding their location to the Java class path search order. You can use the CLASSPATH environment variable for this purpose.


Visual J++: Code Sample Information

The Visual J++ simple provider code sample is located in ..\Samples\DataAccess\Osp\Vj and consists of the files described in the following table.

File name | Description
MyDataSource.java | Implementation for IDataSource.
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.
Ospsampj.dll | OSP Visual J++ sample DLL.
SampleOSP_VJ.sln | Visual J++ 6.0 solution file for this sample.
SampleOSP_VJ.reg | Copy of the RegisterProvider.reg file for use with the Visual J++ sample.
SampleOSP_VJ.vjp | Visual J++ project file for the sample.


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. For complete information about how to fill in the template and import those settings into the registry, see Using the RegisterProvider.reg file.

Following are two suggested methods for registering an OLE DB provider built with the OSP 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 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 OSP 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. When you are finished completing the template, you should rename it and double-click the file to import those settings into the registry.

Running the registry file is necessary so that the OLE DB Simple Provider DLL recognizes the OSP Data Object. You must also register the OSP Data Object separately by 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 your provider will be listed under. It is also created by you.

    Note 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 for information about 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, 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.

To fully register a provider

  1. Register the OLE DB 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 registry.

Windows Registry Entries

Following are the entries that are written to the 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 Rgsvr32.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.

To register a provider without using the RegisterProvider.reg file

  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.

Windows Registry Entries

The following entries are written to the 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.

Following is an example of the registry entries under the CLSID key that the developer needs to supply for two providers built with the OSP Toolkit, with both residing on the same computer. (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

If no registration settings have been entered or if 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

The Microsoft® OLE DB Simple Provider (OSP) Toolkit is a set of Microsoft 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 Toolkit documentation includes additional information specific to the usage of OSP.

OSP and the Toolkit

The OSP Toolkit uses the OSP API as an integral part of its OLE DB provider architecture. Think of it as 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 accomplish the following:

  • Expose a data source. You must implement a separate OSP for each data source you want to expose.
  • Build OSP Data Objects. 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:

  • 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 OSP Toolkit can be categorized as described in the following table.

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/delete | 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 | Allow data to populate a target application or control asynchronously.
Notifications | Notify, through an event handler, changes to cells, rows, and columns.


OLEDBSimpleProvider Interface and Methods

The OLEDBSimpleProvider interface implements the methods used for data access. It specifies methods for the following tasks:

  • 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 arraylike data by using row and column indices. Normally, indices begin with a value of 1. Two index values with special meaning are described in the following table.

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 OLEDBSimpleProvider::getVariant and OLEDBSimpleProvider::setVariant methods cannot use a value of -1 as an argument for the iRow and iColumn arguments.
0 | As a row value, reserved for names of columns. Is ignored (by the OSP mapping layer) as a column value. For example, a row value of 0 and a column of 1 refers to the name of column 1 returned by IColumnsInfo. Note The OLEDBSimpleProvider::getVariant and OLEDBSimpleProvider::setVariant methods ignore a value of 0 as an argument for the iColumn argument.

Method List

The following table lists the OLEDBSimpleProvider methods that the OSP Toolkit uses, a short description of each, and the category each method belongs to. Click any method name for detailed information about its implementation.

Method | Description | Category
addOLEDBSimpleProviderListener | Registers an event handler interface, making it possible to receive notifications of changes to data. | Event handler
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
getColumnCount | Returns the number of columns. | Schema
getEstimatedRows | Returns the total estimated number of rows in the data set. | Asynchronous data population
getLocale | Indicates the locale of the data. | Localization
getRowCount | Returns the number of rows. | 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 preallocated Variant. | Variant
insertRows | Inserts a specified number of empty rows, starting at a specified location. | Insertion and deletion
isAsync | Indicates whether data is being populated asynchronously. | Asynchronous data population
removeOLEDBSimpleProviderListener | Unregisters a specified event handler interface. | Event handler
setVariant | Sets the value of the cell at the row and column indicated to the value of the Variant. | Variant
stopTransfer | Requests that the OSP discontinue asynchronous transfer of data. | Asynchronous data population


addOLEDBSimpleProviderListener

OLE DB Simple Provider Toolkit

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.

Comments

If an event handler was previously specified, OLEDBSimpleProvider::addOLEDBSimpleProviderListener releases that event handler prior to registering pospIListener.

The simple provider must implement this method to maintain a list of simple provider listeners. If not implemented, OLE DB methods called by consumers may fail because the mapping layer is unable to notify listeners of events. For example, IOpenRowset::OpenRowset would return DB_E_OBJECTOPEN if OLEDBSimpleProvider::addOLEDBSimpleProviderListener is not implemented.

For multiple OLE DB rowsets over the same data set, your implementation must support multiple listeners; otherwise, only one "active" rowset will be allowed.


deleteRows

OLE DB Simple Provider Toolkit

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. Ignored when implementing the wildcard behavior (iRow value of -1).
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.

Comments

OLEDBSimpleProvider::deleteRows requires the following notifications:

  • aboutToDeleteRows
  • deletedRows

For more information and programming considerations about sending and receiving notifications, see "OSP Notifications."


find

OLE DB Simple Provider Toolkit

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 values defined in the following table.
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 the values defined in the following table.
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, OLEDBSimpleProvider::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.

Comments

Using the native type of the underlying column, OLEDBSimpleProvider::find performs compares. 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 OLEDBSimpleProvider::getVariant with OSPFORMAT_FORMATTED. Providers should not coerce values of varSval obtained from a previous call to OLEDBSimpleProvider::getVariant with OSPFORMAT_HTML.


getColumnCount

OLE DB Simple Provider Toolkit

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. For asynchronous operations unable to determine a column count (using OLEDBSimpleProvider::getEstimatedRows), a negative value may indicate that the operation is complete, which can cause unexpected results.

Return Codes

S_OK
The method succeeded.
E_FAIL
A provider-specific error occurred.

Comments

Returned column values from an asynchronous operation cannot be negative.


getEstimatedRows

OLE DB Simple Provider Toolkit

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 OLEDBSimpleProvider::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.

Comments

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 OLEDBSimpleProvider::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 OLEDBSimpleProvider::getRowCount).

Providers should not return the final row count prior to issuing OLEDBSimpleProviderListener::transferComplete.


getLocale

OLE DB Simple Provider Toolkit

OLEDBSimpleProvider::getLocale

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.

Comments

If you have Microsoft® Internet Explorer 4.0 installed, your registry key "HKEY_CLASSES_ROOT\MIME\Database\Rfc1766" contains the legal values for locale.


getRowCount

OLE DB Simple Provider Toolkit

OLEDBSimpleProvider::getRowCount

Returns the number of rows in the data set. For an asynchronous provider, returns the current 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. For asynchronous operations unable to determine a row count (using OLEDBSimpleProvider::getEstimatedRows), a negative value may indicate that the operation is complete, which can cause unexpected results.

Return Codes

S_OK
The method succeeded.
E_FAIL
A provider-specific error occurred.


getRWStatus

OLE DB Simple Provider Toolkit

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, 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
};

The preceding values are defined in the following table.

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.

Comments

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.


getVariant

OLE DB Simple Provider Toolkit

OLEDBSimpleProvider::getVariant

Retrieves the value of the cell at the indicated row and column into the preallocated Variant supplied in pVar.

getVariant uses the following enumeration:

typedef enum OSPFORMAT {
   OSPFORMAT_RAW = 0,
   OSPFORMAT_DEFAULT = 0,
   OSPFORMAT_FORMATTED = 1,
   OSPFORMAT_HTML = 2
};

The preceding values are defined in the following table.

Value | Meaning
OSPFORMAT_RAW OSPFORMAT_DEFAULT | Underlying column type should be used to get the value (the default).
OSPFORMAT_FORMATTED | Underlying column type should be converted to a string, and the string should be 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 | This value was originally intended to convert the underlying column type to an HTML string. In actual practice, the OSP mapping layer (Msdaosp.dll) does not use this value; however, for interoperability with Microsoft® Internet Explorer data binding, simple providers should support it. Provider writers should return data as Variant or BSTR; no HTML formatting is required.
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 Comments.)
pVar
[out]
Based on the value specified in the pVar parameter, pVar will contain one of the following:
  • 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.

Comments

OLEDBSimpleProvider::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 accessing column header names (row 0), or converting to OLE DB string types, for example DBTYPE_WSTR, the mapping layer uses OSPFORMAT_FORMATTED to return the cell value as a string.

When getting the value as a Variant, or when the OLE DB status value is set to DBSTATUS_S_ISNULL (meaning the value is not bound), the mapping layer calls getVariant with OSPFORMAT_RAW to return the Variant value or VT_EMPTY, respectively. Therefore simple providers should support both of these values.

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 one of the following:

  • S_OK and a Variant of type VT_NULL
  • 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 OLEDBSimpleProvider::getVariant. You must retrieve table cells individually; they cannot be retrieved a row or column at a time.

OLEDBSimpleProvider::getVariant is one of the few OSP methods that returns an object or variant. Take care to allocate a fresh variant that is not used elsewhere to return to the Microsoft Virtual Machine, which can free the object at any time.


insertRows

OLE DB Simple Provider Toolkit

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.

Comments

insertRows requires the following notifications:

  • aboutToInsertRows
  • insertedRows

For more information and programming considerations about sending and receiving notifications, see "OSP Notifications."


isAsync

OLE DB Simple Provider Toolkit

OLEDBSimpleProvider::isAsync

Indicates whether provider data will populate the consumer application or control asynchronously.

HRESULT isAsync (
   BOOL    *pbAsynch);

Parameter

*pbAsynch
[out]
Boolean value indicating whether data is returned asynchronously (1) or not (0).

Return Codes

S_OK
The method succeeded.
E_FAIL
A provider-specific error occurred.

Comments

OLEDBSimpleProvider::isAsync should always return the appropriate state; it should never block.

In Java, IOLEDBSimpleProvider::isAsync returns type int instead of Boolean because of how the interface definition is generated. Developers should return 0 for false and a nonzero integer for true.


removeOLEDBSimpleProviderListener

OLE DB Simple Provider Toolkit

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.

Comments

If OLEDBSimpleProvider::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 OSP Toolkit, the listener must be the OLE DB 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.


setVariant

OLE DB Simple Provider Toolkit

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
};

The preceding values are defined in the following table.

Value | Meaning
OSPFORMAT_RAW OSPFORMAT_DEFAULT | Underlying column type should be used to set the value (the default).
OSPFORMAT_FORMATTED | The input string is placed into the cell based on provider-specific heuristics.
OSPFORMAT_HTML | Provider-specific. The input string optionally can be in HTML format.
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 table above.)
pVar
[in/out]
Based on the value specified in the pVar parameter, pVar will contain one of the following:
  • 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.

Comments

setVariant does not support references.

setVariant requires the following notifications:

  • aboutToChangeCell
  • 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 optionally 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.

For more information and programming considerations about sending and receiving notifications, see "OSP Notifications."


stopTransfer

OLE DB Simple Provider Toolkit

OLEDBSimpleProvider::stopTransfer

Requests that the simple provider discontinue the asynchronous transfer of data.

HRESULT stopTransfer ( );

Return Codes

S_OK
The method succeeded.
E_FAIL
A provider-specific error occurred, or the OSP was unable to stop the asynchronous transfer.

Comments

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.

OLEDBSimpleProvider::stopTransfer requires the OLEDBSimpleProviderListener::transferComplete notification if provider data will populate the consumer application or control asynchronously.


OLEDBSimpleProviderListener Interface and Methods

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 OSP Toolkit, along with a short description of each. There are pre-notifications and post-notifications for each event. (Remember that the OSP notifications represent only one category of possible notifications; there are also OLE DB notifications and data source notifications.) Click any method name for a description of its implementation.

Note The simple provider must call the appropriate listener methods prior to, and after, the work is done in the insertRows, deleteRows, and setVariant methods of the OLEDBSimpleProvider interface. Failure to do so will leave the OLE DB rowset in an undetermined state, and consequently, calling IRowset::GetData would return unpredictable results.

Method | Description
aboutToChangeCell | Indicates that the specified cells are about to be modified.
aboutToDeleteRows | Indicates that one or more specified rows are about to be deleted.
aboutToInsertRows | Indicates that one or more specified rows are about to be inserted.
cellChanged | Indicates that the specified cells were changed.
deletedRows | Indicates that one or more specified rows were deleted.
insertedRows | Indicates that one or more specified rows were inserted.
rowsAvailable | Indicates that a specified sequence of rows is now available.
transferComplete | Indicates that the transfer of the entire data set exposed by OSP is complete.


aboutToChangeCell

OLE DB Simple Provider Toolkit

OLEDBSimpleProviderListener::aboutToChangeCell

Indicates that the specified cells are about to change. aboutToChangeCell is an event handler method, implemented through OLEDBSimpleProvider::addOLEDBSimpleProviderListener. This notification is issued when values are modified through OLEDBSimpleProvider::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.

Comments

Calls to OLEDBSimpleProvider::getVariant should return the original, unchanged values or the values prior to the return of control from firing the OLEDBSimpleProviderListener::aboutToChangeCell event.

You can specify a value of -1 for iRow, iColumn, or for both.


aboutToDeleteRows

OLE DB Simple Provider Toolkit

OLEDBSimpleProviderListener::aboutToDeleteRows

Indicates that the specified rows are about to be deleted. aboutToDeleteRows is an event handler method, implemented through OLEDBSimpleProvider::addOLEDBSimpleProviderListener. This notification is issued just prior to the rows being deleted, either from a call to OLEDBSimpleProvider::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.

Comments

Access to cells in the deleted rows should remain valid until control is returned from OLEDBSimpleProviderListener::aboutToDeleteRows.


aboutToInsertRows

OLE DB Simple Provider Toolkit

OLEDBSimpleProviderListener::aboutToInsertRows

Indicates that the specified rows are about to be inserted. aboutToInsertRows is an event handler method, implemented through OLEDBSimpleProvider::addOLEDBSimpleProviderListener. This notification is issued just prior to the rows being inserted, either through a call to OLEDBSimpleProvider::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.


cellChanged

OLE DB Simple Provider Toolkit

OLEDBSimpleProviderListener::cellChanged

An event handler method implemented through OLEDBSimpleProvider::addOLEDBSimpleProviderListener. It indicates that the specified cells were changed. This notification is issued when values are modified through OLEDBSimpleProvider::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.

Comments

This method should be invoked after the data values have changed. Subsequent calls to OLEDBSimpleProvider::getVariant should return the new values.

You can specify a value of -1 for iRow, iColumn, or for both.


deletedRows

OLE DB Simple Provider Toolkit

OLEDBSimpleProviderListener::deletedRows

An event handler method, implemented through OLEDBSimpleProvider::addOLEDBSimpleProviderListener. It indicates that cRows were deleted beginning at row iRow. The notification is issued when rows are deleted through OLEDBSimpleProvider::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.

Comments

OLEDBSimpleProviderListener::deletedRows should be called after the rows are deleted.


insertedRows

OLE DB Simple Provider Toolkit

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 OLEDBSimpleProvider::addOLEDBSimpleProviderListener. This notification is issued when rows are inserted through OLEDBSimpleProvider::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.


rowsAvailable

OLE DB Simple Provider Toolkit

OLEDBSimpleProviderListener::rowsAvailable

Indicates that a specified sequence of rows is now available. rowsAvailable is an event handler method, implemented through OLEDBSimpleProvider::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.

Comments

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.


transferComplete

OLE DB Simple Provider Toolkit

OLEDBSimpleProviderListener::transferComplete

Indicates that a data transfer is complete. All providers must invoke this event. transferComplete is an event handler method, implemented through OLEDBSimpleProvider::addOLEDBSimpleProviderListener. All asynchronous providers must call this function when a transfer of data is complete.

HRESULT transferComplete (
   OSPXFER   doneReason);

Parameter

doneReason
[in]
Supports the values defined in the following table.
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.

Comments

For providers that retrieve their data asynchronously, this event signifies that no additional OLEDBSimpleProviderListener::rowsAvailable events will be called for this data set.


IDataSource Interface and Methods

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 OSP Toolkit, you always use IID_OLEDBSimpleProvider. (For other data access interfaces you can use with IDataSource, see the Microsoft® 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
addDataSourceListener | Adds a listener to the notification list.
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.
removeDataSourceListener | Removes a listener from the notification list.


addDataSourceListener

OLE DB Simple Provider Toolkit

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 and 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.


getDataMember

OLE DB Simple Provider Toolkit

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.

The return value from IDataSource::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.


getDataMemberCount

OLE DB Simple Provider Toolkit

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.


getDataMemberName

OLE DB Simple Provider Toolkit

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.


removeDataSourceListener

OLE DB Simple Provider Toolkit

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, it can just return S_OK from this method.


IDataSourceListener Interface and Methods

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).

The IDataSourceListener methods used with OSP are described in the following table.

Method | Description
dataMemberAdded | Notifies the data consumer that an existing data member has become available, unavailable, or has been refreshed.
dataMemberChanged | Notifies the data consumer that an existing data member has changed.
dataMemberRemoved | Notifies the data consumer that an existing data member has been removed.


dataMemberAdded

OLE DB Simple Provider Toolkit

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.


dataMemberChanged

OLE DB Simple Provider Toolkit

IDataSourceListener::dataMemberChanged

Notifies the data consumer that an existing data member has become available, unavailable, or has been 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 to get the new data (if available).


dataMemberRemoved

OLE DB Simple Provider Toolkit

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.


Methods Using IDispatch

The IDispatch methods used with OSP are described in the following table.

Method | Description
DataSourceListener | Used to notify a consumer that the shape of a data set has changed.
msDataSourceObject | Provides the mechanism by which an OLE DB rowset maps to the correct OSP implementation.


DataSourceListener

OLE DB Simple Provider Toolkit

IDispatch::DataSourceListener

The DataSourceListener methods described below should be used only 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 names of the columns change, the type of a column changes, or the entire data set is reordered (the result of a sort operation). In such an instance, the consumer needs to be notified of the change so it can retrieve a new interface on the (changed) data by using a call to IDispatch::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);
}

Comments

When a data change occurs, the object will call the IDataSourceListener::dataMemberChanged method of the DataSourceListener method 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. IDataSourceListener::dataMemberAdded and IDataSourceListener::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 IDataSource::addDataSourceListener(NULL). This precludes multicasting event notifications but keeps requirements on controls simple.


msDataSourceObject

OLE DB Simple Provider Toolkit

IDispatch::msDataSourceObject

The msDataSourceObject method described below should be used only 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 the standard IDispatch::Invoke 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.
**ppUnk
[out]
Interface pointer to the specified OSP implementation.


Toolkit Files and Redistribution

This section contains a list of files supplied with the Microsoft® OLE DB Simple Provider (OSP) Toolkit, file name locations and descriptions, and instructions for redistributing any provider you create using the OSP Toolkit.


File Listing

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 OSP Toolkit files must be installed in the locations specified below. The following table provides a list of the OSP 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 | ..\Include | OSP Toolkit header file.
Msdaosp.idl | ..\Unsupported\idl | Defines the IDispatch DataSourceObject and GUIDs.
OspSamplesreadme.htm | ..\osp | Readme file for OSP Toolkit.
Msdatasrc.h | ..\Include | IDataSource header file.
Msdatsrc.tlb | \WinNT\System32 or \Win95\System | IDataSource type library.
Msdatsrc.idl | ..\Include | Defines IDataSource interface.
Simpdata.h | ..\Include | IOLEDBSimpleProvider header file.
Simpdata.tlb | ..\Lib | IOLEDBSimpleProvider type library.
Simpdata.idl | ..\Unsupported | Defines OLEDBSimpleProvider interface.
Osptk.lib | ..\Lib | Library containing interface GUIDs.
Customer.txt | ..\Samples\DataAccess\osp | Data file for provider examples.
RegisterProvider.reg | ..\Samples\DataAccess\osp | Registration template.
RegisterProvider.txt | ..\Samples\DataAccess\osp | Registration example.
MyDataSource.cls | ..\Samples\DataAccess\osp\VB | Visual Basic provider example.
MyOSPObject.cls | ..\Samples\DataAccess\osp\VB | Visual Basic provider example.
ospsampb.dll | ..\Samples\DataAccess\osp\VB | Visual Basic provider example.
SampleOSP_VB.reg | ..\Samples\DataAccess\osp\VB | Visual Basic provider example.
SampleOSP_VB.vbp | ..\Samples\DataAccess\osp\VB | Visual Basic provider example.
CExList.h | ..\Samples\DataAccess\osp\VC | Visual C++ provider example.
Common.cpp | ..\Samples\DataAccess\osp\VC | Visual C++ provider example.
Common.h | ..\Samples\DataAccess\osp\VC | Visual C++ provider example.
MyClassFactory.cpp | ..\Samples\DataAccess\osp\VC | Visual C++ provider example.
MyClassFactory.h | ..\Samples\DataAccess\osp\VC | Visual C++ provider example.
MyDataSource.cpp | ..\Samples\DataAccess\osp\VC | Visual C++ provider example.
MyDataSource.h | ..\Samples\DataAccess\osp\VC | Visual C++ provider example.
MyOSPObject.cpp | ..\Samples\DataAccess\osp\VC | Visual C++ provider example.
MyOSPObject.h | ..\Samples\DataAccess\osp\VC | Visual C++ provider example.
ospsampc.dll | ..\Samples\DataAccess\osp\VC | Visual C++ provider example.
SampleOSP_VC.def | ..\Samples\DataAccess\osp\VC | Visual C++ provider example.
SampleOSP_VC.dsp | ..\Samples\DataAccess\osp\VC | Visual C++ provider example.
SampleOSP_VC.reg | ..\Samples\DataAccess\osp\VC | Visual C++ provider example.
MyOSPObject.class | ..\Samples\DataAccess\osp\VJ | Visual J++ provider example.
MyOSPObject.java | ..\Samples\DataAccess\osp\VJ | Visual J++ provider example.
MyDataSource.class | ..\Samples\DataAccess\osp\VJ | Visual J++ provider example.
MyDataSource.java | ..\Samples\DataAccess\osp\VJ | Visual J++ provider example.
SampleOSP_VJ.vjp | ..\Samples\DataAccess\osp\VJ | Visual J++ provider example.
SampleOSP_VJ.reg | ..\Samples\DataAccess\osp\VJ | Visual J++ provider example.
SampleOSP_VJ.sln | ..\Samples\DataAccess\osp\VJ | Visual J++ 6.0 solution file.


Redistributing Providers

To redistribute a provider built with the Microsoft® OLE DB Simple Provider (OSP) 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 OSP Toolkit (the RegisterProvider.reg file) on each computer. Be sure to rename the RegisterProvider.reg file. You supply a ProgID, name, and GUID for your full provider, and then 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.


OLE DB Reference Information

This section contains the OLE DB interfaces and properties supported by the Microsoft® OLE DB Simple Provider (OSP) Toolkit. Because the OSP Toolkit provides support for redistributable components, you may want to include this information along with the providers you build.


Supported OLE DB Interfaces

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. (This is a COM, not an OLE DB, interface. For more information, refer to the OLE DB 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 COM and OLE DB error objects.

Session Object

Interface | Description and Notes
IGetDataSource | Mandatory interface on the session for obtaining a data source object.
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 OSP Toolkit, you can issue OpenRowset only once for each OSP. The second call will fail with a status code of E_FAIL.

If no data member is specified on IOpenRowset::OpenRowset—because no registration settings have been entered, or because no data source initialization property has been set—Msdaosp.dll returns the error DB_E_NOTABLE, which indicates that the table does not exist in the current data source.

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. (See Note below.)
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. (This is a COM, not an OLE DB, interface. For more information, refer to the OLE DB Programmer's Reference.)
IConvertType | Provides information about the availability of type conversions on a 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.

Note The OSP implementation of the IColumnsInfo::GetColumnInfo method initializes DBCOLUMNSINFO structures as follows.

For the bookmark column:

  • Column ID (DBID)—As described in the section "Bookmarks," in Chapter 4: Rowsets, of the OLE DB Programmer's Reference, where DBID.eKind is set to DBKIND_GUID_PROPID
  • wType—DBTYPE_I4 for a 32-bit OS, or DBTYPE_I8 for a 64-bit OS
  • uColumnSize—DBBKMARK, which is 4 for a 32-bit OS, or 8 for a 64-bit OS
  • bPrecision—10
  • bScale—~0 (all ones; no scale)
  • dwFlags—DBCOLUMNFLAGS_ISBOOKMARK | DBCOLUMNFLAGS_ISFIXEDLENGTH | DBCOLUMNFLAGS_MAYDEFER

For all other columns:

  • iOrdinal—A number starting from 1
  • DBID.eKind—DBKIND_PROPID
  • DBID.uName.ulPropid—A number equal to 100+iOrdinal
  • wType—DBTYPE_VARIANT
  • ulColumnSize—The size of a VARIANT (16)
  • bPrecision—~0 (all ones; no precision)
  • bScale—~0 (all ones; no scale)
  • dwFlags—DBCOLUMNFLAGS_ISFIXEDLENGTH | DBCOLUMNFLAGS_MAYDEFER | DBCOLUMNFLAGS_MAYBENULL | DBCOLUMNFLAGS_ISNULLABLE

If the OSP getRWStatus method reports the column is not OSPRW_READONLY, dwFlags is also marked DBCOLUMNFLAGS_WRITE.

If the column is a chapter column, wType is set to DBTYPE_HCHAPTER, dwFlags is marked DBCOLUMNFLAGS_ISCHAPTER, and ulColumnSize is set to the size of a pointer to IUnknown.

The column name is stored in **ppStringsBuffer.

Notification Interfaces for Rowset Object

Interface | Description
IDBAsynchNotify | The callback interface that a consumer must support to be notified of the progress of asynchronous operations such as initializing a data source object 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

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_ASYNCH | 0 Supported as READONLY because initialization of the data source object is always synchronous and immediate.
DBPROP_MAXROWSIZE | 0
DBPROP_MULTIPLERESULTS | DBPROP_MR_NOTSUPPORTED
DBPROP_PERSISTENTIDTYPE | DBPROPVAL_PT_PROPID
DBPROP_PROVIDERFILENAME | Msdaosp.dll
DBPROP_PROVIDERFRIENDLYNAME | "Microsoft OLE DB Simple Provider"
DBPROP_PROVIDEROLEDBVER | 02.00
DBPROP_PROVIDERVER | 02.00xxxx
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 OLEDBSimpleProvider::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

When an OSP contains a read-only rowset (that is, getRWStatus() returns OSP_READONLY for all rows and columns), Msdaosp.dll sets properties DBPROP_ROWRESTRICT and DBPROP_COLUMNRESTRICT to VARIANT_TRUE. This reflects the possibility that the OSP might still allow new rows to be added, and these new rows might be updatable. If a new, updatable row is added, columns might have mixed write status.
DBPROP_DEFERRED | VARIANT_TRUE
DBPROP_FINDCOMPAREOPS DBPROPVAL_CO_CONTAINS |
DBPROPVAL_CO_BEGINSWITH |
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_NOTIFYROWFETCHPOSITIONCHANGE | 0
DBPROP_NOTIFYROWINSERT | 0
DBPROP_NOTIFYROWSETRELEASE | 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_SEQUENTIALPOPULATION

Default value is

(DBPROPVAL_ASYNCH_INITIALIZE | DBPROPVAL_ASYNCH_SEQUENTIALPOPULATION | DBPROPVAL_ASYNCH_RANDOMPOPULATION)

Supported as READWRITE, but population of the rowset is determined by the underlying OSP by using the OLEDBSimpleProvider::isAsync method. Unless the underlying OSP is asynchronous, setting property DBPROP_ROWSET_ASYNCH will have no effect. Msdaosp.dll will not return until the rowset is populated.
DBPROP_ROWTHREADMODEL | DBPROPVAL_RT_FREE
DBPROP_STRONGIDENTITY | VARIANT_FALSE
DBPROP_UPDATABILITY | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_DELETE


ODBC Provider

OLE DB Provider for ODBC

OLE DB Provider for ODBC

The Microsoft® Data Access Components (MDAC) SDK contains an OLE DB provider for ODBC data. This document describes the Microsoft OLE DB Provider for ODBC, explains the procedures for installing, configuring, and testing the provider, and lists the limitations of the current implementation.

Although using this provider is a convenient way to access data through ODBC data sources, it is possible to obtain better performance and reliability by using a native provider. For more information about other OLE DB providers written to access specific data stores, see "Microsoft OLE DB Providers Overview", or visit the Products page on the Universal Data Access Web site at http://www.microsoft.com/data/partners/products.htm, or contact your software vendor.


Version Compliance

OLE DB

The ODBC Provider is compliant with version 1.5 and later of the OLE DB specification. The latest version of the OLE DB specification is included in the MDAC SDK.

ODBC

The ODBC Provider is fully compliant with ODBC versions up to 2.x, but because it also supports some 3.x functionality, the latest version of ODBC (ODBC 3.51) should be installed. For example, this provider supports different tabular data stream processing than other providers. For more information on method conformance levels, see the ODBC Programmer's Reference. For full functionality against a SQL Server data source, use the OLE DB Provider for SQL Server.


Installation Notes

The OLE DB Provider for ODBC can be installed using the MDAC Redistribution Setup program. This program (MDAC_TYP.exe) installs all the necessary data access components for the provider to function correctly. It can be obtained from Microsoft's Universal Data Access Web site at http://www.microsoft.com/data.


Supported Platforms

The ODBC Provider is supported on Microsoft® Windows NT® version 4.0, Intel and Alpha platforms. It is also supported on Microsoft Windows® 95/98 for Intel platforms. The ODBC Provider shipped with MDAC 2.5 and Microsoft Windows 2000 is supported only on Intel platforms.


Implementing the ODBC Provider

The OLE DB Provider for ODBC supports creating an initialized data source object through a file moniker. It also supports named parameters in stored procedures and can access error information using diagnostic fields.

  • Obtaining Data Source Objects
  • Initialization Properties and .dsn Files
  • Named Parameter Support
  • Error Support

For more details about implementation issues, see "General Implementation Notes" in this documentation.


Obtaining Data Source Objects

The ODBC Provider supports creating an initialized data source object through a file moniker. To do this, follow these steps:

  1. Create a file moniker (.dsn file). A .dsn file contains connection information.
  2. 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 function SQLSetConnectAtt to set various connection options and calls the ODBC function 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 IPersistFile::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 that 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.

Note The data source information property, DBPROP_SERVER_NAME, is now supported by the OLE DB Provider for ODBC. This property returns, upon initialization, the name of the server to which you are connected. In many cases, this will be the same as the DBPROP_INIT_DATASOURCE property. For example, when connecting to an ODBC data source, you might specify a DSN (friendly name) and the server name would return the name of the actual server to which you were connected.

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). Do not use the property DBPROP_INIT_LOCATION with a Microsoft Access data source. When attempting to initialize the ODBC Provider, if you set the initialization property DBPROP_INIT_LOCATION to a valid value and specify that the DATASOURCE is a Microsoft Access data source, errors will occur.
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. Setting DBPROP_INIT_MODE to read-only is advised but not mandatory for ODBC and this provider.
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.


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 earlier 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

The diagnostic fields feature was introduced in ODBC 3.0. The ODBC Provider allows you to access error information from these diagnostic fields.

The ODBC Provider includes a new interface, ISQLRequestDiagFields, to facilitate the retrieval of diagnostic information. When an OLE DB consumer needs to retrieve certain diagnostic fields after a method call, it calls ISQLRequestDiagFields::RequestDiagFields on the OLE DB object before calling the method to notify the ODBC Provider that it can 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. Following 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;

};

The parameter 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. KAGREQDIAGFLAGS_HEADER or KAGREQDIAGFLAGS_RECORD can be the value of ulDiagFlags. These two parameters tell whether the diagnostic field is a header field or record field. The type, vt, is a 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 here; 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, 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.

Definition of the tagKAGGETDIAG Structure

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 to 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.


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.


Data Types

This section describes data types in the OLE DB Provider for ODBC. For information about data type conversions, see Appendix A.

Default Data Type Mappings

The ODBC Provider binds to the ODBC data source object using the types in the following table. 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 ODBC 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 DBTYPE_U2
SQL_INTEGER | SQL_C_ULONG SQL_C_SLONG | DBTYPE_I4 DBTYPE_U4
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_DBTIMESTAMP

[1] For ODBC 3.5 drivers only.

[2] This is due to lack of support in the ODBC specification for an 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 to 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 an 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 can 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.


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, a 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 might 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, 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 as follows:

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 store 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 might 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 of the OLE DB Provider for ODBC.

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 example, 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 ODBC Provider does not support the data type SQL_VARIANT. This data type will appear to consumers as BLOB data. For SQL_VARIANT, the PROVIDER_TYPES schema rowset returns a TYPE_NAME of "sql_variant", a DATA_TYPE of DBTYPE_BYTES, and a COLUMN_SIZE of 0. For information about specific interface limitations for SQL_VARIANT, see the limitations section in the following interface notes: "IColumnsInfo: Notes," "IRowset: Notes," and "IRowsetChange: Notes."
  • The query-based update scheme implemented in the ODBC Provider might not allow for correct repositioning in the rowset after performing an update involving the key columns that 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." 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. This behavior does not signify the DB_S_ENDOFROWSET condition, and the number of rows returned by the fetching method will never be zero if any rows remain (although the number of rows fetched might 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. Because 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 a rowset is created, the OLE DB Provider for ODBC may change the value of DBPROP_CHANGEINSERTEDROWS even though the consumer has explicitly set it. The provider keeps track of its internal state, which may conflict with the consumer's. To reliably find the correct value of this property, the consumer should query the newly created rowset with IRowsetInfo::GetProperties. The specific case in which this provider changes DBPROP_CHANGEINSERTEDROWS to false is when DBPROP_IRowsetUpdate is true and KAGPROP_QUERYBASEDUPDATES is false.
  • 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 IRowsetChange::SetData for a BLOB column not bound in the accessor as DBTYPE_IUNKNOWN, you can retrieve that value as any legal binding other than DBTYPE_IUNKNOWN. After calling IRowsetChange::SetData, you can 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 or later of the OLE DB Provider for ODBC.

Microsoft SQL Server ODBC Driver

  • When using a Microsoft® SQL Server™ database as the data store, 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 store, 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 source objects, 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 store, 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 and causes a data overflow, but has not called ICommandWithParameters::SetParameterInfo, and the provider cannot determine the column type used by the data source object, 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.
  • 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.


Appendix A: Data Type Conversions

  • The ODBC Provider supports limited conversions. For more information and a listing of supported data type conversions, see "ODBC Provider Data Type Conversion."


I1 Data Type Conversions

Convert to type

I2 | R4 | BOOL
I4 | R8 | BSTR
I8 | CY | Empty
UI1 | DECIMAL | NULL
UI2 | NUMERIC | STR
UI4 | DATE | VARIANT
UI8 |  | WSTR

Unsupported data type conversions

DBDATE, DBTIME, DBTIMESTAMP, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


I2 Data Type Conversions

Convert to type

I1 | R4 | BSTR
I4 | R8 | Empty
I8 | CY | NULL
UI1 | DECIMAL | STR
UI2 | NUMERIC | VARIANT
UI4 | DATE | WSTR
UI8 | BOOL |

Unsupported data type conversions

DBDATE, DBTIME, DBTIMESTAMP, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


I4 Data Type Conversions

Convert to type

I1 | R4 | BSTR
I2 | R8 | Empty
I8 | CY | NULL
UI1 | DECIMAL | STR
UI2 | NUMERIC | VARIANT
UI4 | DATE | WSTR
UI8 | BOOL |

Unsupported data type conversions

DBDATE, DBTIME, DBTIMESTAMP, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


I8 Data Type Conversions

Convert to type

UI8
Empty
NULL

Unsupported data type conversions

I1, I2, I4, UI2, UI4, R4, R8, CY, DECIMAL, NUMERIC, DATE, DBDATE, DBTIME, DBTIMESTAMP, BOOL, BSTR, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN, STR, VARIANT, WSTR


UI1 Data Type Conversions

Convert to type

I1 | R4 | BSTR
I2 | R8 | Empty
I4 | CY | NULL
I8 | DECIMAL | STR
UI2 | NUMERIC | VARIANT
UI4 | DATE | WSTR
UI8 | BOOL |

Unsupported data type conversions

DBDATE, DBTIME, DBTIMESTAMP, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


UI2 Data Type Conversions

Convert to type

I1 | R4 | BSTR
I2 | R8 | Empty
I4 | CY | NULL
I8 | DECIMAL | STR
UI1 | NUMERIC | VARIANT
UI4 | DATE | WSTR
UI8 | BOOL |

Unsupported data type conversions

DBDATE, DBTIME, DBTIMESTAMP, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


UI4 Data Type Conversions

Convert to type

I1 | R8 | Empty
I2 | CY | NULL
I4 | DECIMAL | STR
I8 | NUMERIC | VARIANT
UI1 | DATE | WSTR
UI2 | BOOL |
UI8 | BSTR |
R4 | BYTES |

Unsupported data type conversions

DBDATE, DBTIME, DBTIMESTAMP, ERROR, GUID, IDISPATCH, IUNKNOWN


UI8 Data Type Conversions

Convert to type

I8
Empty
NULL

Unsupported data type conversions

I1, I2, I4, UI2, UI4, R4, R8, CY, NUMERIC, DECIMAL, DBTIME, DATE, DBDATE, DBTIMESTAMP, BOOL, BSTR, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN, STR, VARIANT, WSTR


R4 Data Type Conversions

Convert to type

I1 | UI8 | BSTR
I2 | R8 | Empty
I4 | CY | NULL
I8 | DECIMAL | STR
UI1 | NUMERIC | VARIANT
UI2 | DATE | WSTR
UI4 | BOOL |

Unsupported data type conversions

DBDATE, DBTIME, DBTIMESTAMP, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


R8 Data Type Conversions

Convert to type

I1 | UI8 | BSTR
I2 | R4 | Empty
I4 | CY | NULL
I8 | DECIMAL | STR
UI1 | NUMERIC | VARIANT
UI2 | DATE | WSTR
UI4 | BOOL |

Unsupported data type conversions

DBDATE, DBTIME, DBTIMESTAMP, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


CY Data Type Conversions

Convert to type

I1 | UI8 | BSTR
I2 | R4 | Empty
I4 | R8 | NULL
I8 | DECIMAL | STR
UI1 | NUMERIC | VARIANT
UI2 | DATE | WSTR
UI4 | BOOL |

Unsupported data type conversions

DBDATE, DBTIME, DBTIMESTAMP, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


DECIMAL Data Type Conversions

Convert to type

I1 | UI8 | BSTR
I2 | R4 | Empty
I4 | R8 | NULL
I8 | CY | STR
UI1 | NUMERIC | VARIANT
UI2 | DATE | WSTR
UI4 | BOOL |

Unsupported data type conversions

DBDATE, DBTIME, DBTIMESTAMP, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


NUMERIC Data Type Conversions

Convert to type

I1 | UI8 | BSTR
I2 | R4 | Empty
I4 | R8 | NULL
I8 | CY | STR
UI1 | DECIMAL | VARIANT
UI2 | DATE | WSTR
UI4 | BOOL |

Unsupported data type conversions

DBDATE, DBTIME, DBTIMESTAMP, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


DATE Data Type Conversions

Convert to type

I1 | R4 | DBTIMESTAMP
I2 | R8 | BOOL
I4 | CY | BSTR
I8 | NUMERIC | NULL
UI1 | DECIMAL | STR
UI2 | DBDATE | VARIANT
UI4 | DBTIME | WSTR
UI8 | Empty |

Unsupported data type conversions

BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


DBDATE Data Type Conversions

Convert to type

DATE | NULL
DBTIME | STR
DBTIMESTAMP | VARIANT
BSTR | WSTR
Empty |

Unsupported data type conversions

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, CY, NUMERIC, DECIMAL, BOOL, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


DBTIME Data Type Conversions

Convert to type

DATE | NULL
DBDATE | STR
DBTIMESTAMP | VARIANT
BSTR | WSTR
Empty |

Unsupported data type conversions

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, CY, NUMERIC, DECIMAL, BOOL, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


DBTIMESTAMP Data Type Conversions

Convert to type

DATE | NULL
DBDATE | STR
DBTIME | VARIANT
BSTR | WSTR
Empty |

Unsupported data type conversions

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, CY, NUMERIC, DECIMAL, BOOL, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


BOOL Data Type Conversions

Convert to type

I1 | UI8 | BSTR
I2 | R4 | Empty
I4 | R8 | NULL
I8 | CY | STR
UI1 | DECIMAL | VARIANT
UI2 | NUMERIC | WSTR
UI4 | DATE |

Unsupported data type conversions

DBDATE, DBTIME, DBTIMESTAMP, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


BSTR Data Type Conversions

Convert to type

I1 | R8 | BYTES
I2 | CY | Empty
I4 | DECIMAL | GUID
I8 | NUMERIC | NULL
UI1 | DATE | STR
UI2 | DBDATE | VARIANT
UI4 | DBTIME | WSTR
UI8 | DBTIMESTAMP |
R4 | BOOL |

Unsupported data type conversions

ERROR, IDISPATCH, IUNKNOWN


BYTES Data Type Conversions

Convert to type

UI4 | STR
BSTR | VARIANT
Empty | WSTR
NULL |

Unsupported data type conversions

I1, I2, I4, I8, UI1, UI2, UI8, R4, R8, CY, NUMERIC, DECIMAL, DBTIME, DATE, DBDATE, DBTIMESTAMP, BOOL, ERROR, GUID, IDISPATCH, IUNKNOWN


Empty Data Type Conversions

Convert to type

I1 | UI8 | BOOL
I2 | R4 | BSTR
I4 | R8 | NULL
I8 | CY | STR
UI1 | DECIMAL | VARIANT
UI2 | NUMERIC | WSTR
UI4 | DATE |

Unsupported data type conversions

DBDATE, DBTIME, DBTIMESTAMP, BYTES, ERROR, GUID, IDISPATCH, IUNKNOWN


ERROR Data Type Conversions

Convert to type

VARIANT

Unsupported data type conversions

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, CY, NUMERIC, DECIMAL, DBTIME, DATE, DBDATE, DBTIMESTAMP, BOOL, BSTR, BYTES, Empty, GUID, IDISPATCH, IUNKNOWN, NULL, STR, WSTR


GUID Data Type Conversions

Convert to type

Empty
NULL
STR
BSTR
WSTR

Unsupported data type conversions

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, CY, NUMERIC, DECIMAL, DBTIME, DATE, DBDATE, DBTIMESTAMP, BOOL, BYTES, ERROR, IDISPATCH, IUNKNOWN, VARIANT


IDISPATCH Data Type Conversions

Convert to type

Empty
IUNKNOWN
NULL
VARIANT

Unsupported data type conversions

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, CY, NUMERIC, DECIMAL, DBTIME, DATE, DBDATE, DBTIMESTAMP, BOOL, BSTR, BYTES, ERROR, GUID, STR, WSTR


IUNKNOWN Data Type Conversions

Convert to type

Empty
IDISPATCH
NULL
VARIANT

Unsupported data type conversions

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, CY, NUMERIC, DECIMAL, DBTIME, DATE, DBDATE, DBTIMESTAMP, BOOL, BSTR, BYTES, ERROR, GUID, STR, WSTR


NULL Data Type Conversions

Convert to type

VARIANT

Unsupported data type conversions

I1, I2, I4, I8, UI1, UI2, UI4, UI8, R4, R8, CY, NUMERIC, DECIMAL, DBTIME, DATE, DBDATE, DBTIMESTAMP, BOOL, BSTR, BYTES, Empty, ERROR, GUID, IDISPATCH, IUNKNOWN, STR, WSTR


STR Data Type Conversions

Convert to type

I1 | R8 | BOOL
I2 | CY | BSTR
I4 | DECIMAL | BYTES
I8 | NUMERIC | GUID
UI1 | DATE | NULL
UI2 | DBDATE | VARIANT
UI4 | DBTIME | WSTR
UI8 | DBTIMESTAMP |
R4 | Empty |

Unsupported data type conversions

ERROR, IDISPATCH, IUNKNOWN


VARIANT Data Type Conversions

Convert to type

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

Unsupported data type conversions

None


WSTR Data Type Conversions

Convert to type

I1 | R8 | BSTR
I2 | CY | BYTES
I4 | DECIMAL | Empty
I8 | NUMERIC | GUID
UI1 | DATE | NULL
UI2 | DBDATE | STR
UI4 | DBTIME | VARIANT
UI8 | DBTIMESTAMP |
R4 | BOOL |

Unsupported data type conversions

ERROR, IDISPATCH, IUNKNOWN


Appendix B: Supported Interfaces

The ODBC Provider supports the following standard OLE DB interfaces, as well as the nonstandard interfaces ISQLRequestDiagFields and ISQLGetDiagFields.

Interface Description | Limitations and Implementation Notes
IAccessor | See "IAccessor: Notes"
IColumnsInfo | See "IColumnsInfo: Notes"
IColumnsRowset | See "IColumnsRowset: Notes"
ICommand | None
ICommandPrepare | None
ICommandProperties | See "ICommandProperties: Notes"
ICommandText | See "ICommandText: Notes"
ICommandWithParameters | See "ICommandWithParameters: Notes"
IConnectionPoint | None
IConnectionPointContainer | None
IConvertType | See "IConvertType: Notes"
IDBCreateCommand | None
IDBCreateSession | None
IDBInfo | None
IDBInitialize | None
IDBProperties | None
IDBSchemaRowset | See "IDBSchemaRowset: Notes"
IErrorLookup | None
IGetDataSource | None
IMultipleResults | None
IOpenRowset | None
IPersistFile (on data source object only) | None
IRowset | See "IRowset: Notes"
IRowsetChange | See "IRowsetChange: Notes"
IRowsetIdentity | None
IRowsetInfo | None
IRowsetLocate | See "IRowsetLocate: Notes"
IRowsetResynch | See "IRowsetResynch: Notes"
IRowsetScroll | See "IRowsetScroll: Notes"
IRowsetUpdate | None
ISequentialStream | See "ISequentialStream: Notes"
ISessionProperties | None
ISourcesRowset | None
ISupportErrorInfo | None
ITransaction | See "ITransaction: Notes"
ITransactionJoin | See "ITransactionJoin: Notes"
ITransactionLocal | None
ITransactionOptions | None


Interface Limitations and Implementation Notes

The following sections discuss how the ODBC Provider implements specific interfaces and limitations, if any, on those interfaces.


IAccessor: Notes

Limitations: None

Implementation Notes

  • Accessors might 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 valid only 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: Notes

Limitations

The ODBC Provider does not support the data type SQL_VARIANT. (For more information see "Known Limitations.") IColumnsInfo::GetColumnInfo will report the following metadata for SQL_VARIANT columns:

  • Type = DBTYPE_BYTES
  • Column size = 4294967295
  • DBCOLUMNFLAGS_ISLONG = True
  • SEARACHBLE = DB_ALL_EXCEPT_LIKE (from SQLGetTypeInfo)

Implementation Notes

  • 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 can 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, and 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: Notes

Limitations: None

Implementation Notes

  • 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 the 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: Notes

Limitations: None

Implementation Notes

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: Notes

Limitations: None

Implementation Notes

  • 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: Notes

Limitations

  • 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.

Implementation Notes

  • 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.


IConvertType: Notes

Limitations

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: Notes

Limitations

  • The ODBC Provider might not support restrictions on the CATALOGS and SCHEMATA schema rowsets.
  • IDBSchemaRowset::GetSchema might 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 source objects:

    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 stores, 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 store. 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, the PROCEDURE NAMES returned in the result set might not exactly match the restriction value, but might have ";N" appended, where N is a number. Also, if the ";N" is used in the PROCEDURE NAME restriction, the provider will return an empty result set even if a procedure name is appended with that number.

Implementation Notes

  • 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: Notes

Limitations

  • 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 execute IRowset::RestartPosition before retrieving additional row handles with IRowset::GetNextRows.
  • If the columns in the underlying data store 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 store change, the consumer calls IRowset::RestartPosition, and the reposition operation causes the command to be reexecuted, 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.
  • IRowset::GetData may return incorrect data for an SQL_VARIANT column based on the value of DBPROP_IRowsetLocate. If DBPROP_IRowsetLocate is set to false and DBPROP_ACCESSORDER is set to DBPROPVAL_AO_RANDOM_REQUIRED, the ODBC Provider attempts to convert the data itself from DBTYPE_BYTES to DBTYPE_WSTR. This converts each byte to two ASCII digits.

Implementation Notes

GetNextRows may skip over rows if the underlying ODBC driver compacts data and some rows of the current rowset have been deleted.


IRowsetChange: Notes

Limitations

The ODBC Provider does not support the data type SQL_VARIANT. For more information see "Known Limitations.") IRowsetChange::DeleteRows fails when attempting to delete rows containing a column of SQL_VARIANT data type.

When using a dynamic cursor, the following limitations apply:

  • Changing the primary key for a row after inserting or updating a row will return an error when IRowsetChange::SetData is called.
  • KAGPROP_POSITIONONNEWROW cannot identify the position because the row is not necessarily at the end of the result set.
  • Bookmarks cannot be used because they are not allowed on dynamic cursors.

Implementation Notes

  • 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.


IRowsetLocate: Notes

Limitations

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 might 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: Notes

Limitations

The OLE DB specification for IRowsetResynch states that providers cannot support this interface (when DBPROP_OTHERUPDATEDELETE is VARIANT_FALSE) if the rowset does not have a different method for retrieving data from the data store. However, the ODBC Provider uses SQLSetPos or SQLExtendedFetch to refresh data for dynamic, keyset-driven, or static cursors. It does not employ another mechanism to refresh the rowset data.

Implementation Notes

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: Notes

Limitations

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.

Implementation Notes

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: Notes

Limitations: None

Implementation Notes

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 about which pointers have and have not been read. The row is in an unknown state. If the consumer attempts to update again, failure might 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: Notes

Limitations: None

Implementation Notes

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 auto-commit 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 auto-commit); there might be some cases in which the ODBC Provider cannot put the session in auto-commit mode despite the information implied in the XACT_E_CANTRETAIN error.


ITransactionJoin: Notes

Limitations: None

Implementation Notes

  • The ODBC Provider now supports Microsoft® Component Services (or Microsoft Transaction Server, if you are using Windows NT) and its coordinated transaction capabilities by implementing ITransactionJoin.
  • Enabling support for Microsoft Component Services 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.


Appendix C: 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 object 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 | Forces integrated security when you log on. Sets SQL_COPT_SS_SERV_RAC to the value of the BSTR.


OLE DB Provider for Oracle

OLE DB Provider for Oracle

The OLE DB Provider for Oracle allows high-performance, functional access to Oracle data for Microsoft® Visual Basic® applications.

The Oracle 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. It is designed to be used with Oracle 7.3x versions and later; however, it does have limited support for Oracle 8x. It is designed to be used with only one Oracle client on each computer.

What the Oracle Provider Allows You to Do

The OLE DB Provider for Oracle supports a simple OLE DB architecture by providing access to data stored in Oracle as well as limited access to Oracle8 databases. The Oracle provider requires the complete installation of Oracle's SQL*Net or Net8 protocols if the database is remote. The following illustration shows this simple configuration.

COM and OLE DB Services can be invoked as follows:

  • Connecting via ADO or IDataInitialize invokes OLE DB services. For more information about these, see the OLE DB Programmer's Reference.
  • The OLE DB Provider for Oracle supports Component Services (or Microsoft Transaction Server, if you are using Windows NT) functionality. Component Services functionality enables OLE DB session object pooling and enlistment of automatic transactions.
  • The OLE DB Provider for Oracle allows existing Oracle 7.3x applications to run without modification aside from updating the ClassID/ProgID.
  • The OLE DB Provider for Oracle, using an Oracle8.x or Oracle8i server, supports DTC/XA.

Who Can Use the Oracle Provider

Application and consumer developers, including ADO developers, can use the OLE DB Provider for Oracle to access data stored in existing Oracle and Oracle8 databases.

Note A C or Microsoft Visual C++® compiler supporting COM component construction is required to develop a consumer.


System Requirements

The Microsoft OLE DB Provider for Oracle requires a system running:

  • The Microsoft® Windows® 95/98 operating system on an Intel computer
- or -
  • The Microsoft Windows NT® (version 4.0 with Service Pack 3 or later) operating system on an Intel or Alpha computer
- or -
  • The Microsoft Windows 2000 operating system on an Intel computer.

    Note MDAC 2.6 is not supported on Alpha computers. Refer to the MDAC SDK Readme about support for later versions.

  • One version of Oracle client software (appropriately installed and configured on each computer) version 7.3.3.4.0 or later, or version 8.0.4.1.1c. For Oracle8, client software version 8.0.4.1.1c is recommended to overcome some known limitations.

    Note Oracle8 has limited support with this provider: Not all data types in Oracle8 are supported, and the Object extensions to Oracle8 are not supported. For more information about supported data types, see Appendix A: Data Types in this documentation.

  • An operational Oracle server running in your enterprise.

    Note Oracle networking components installed with an Oracle client, plus Oracle Release 7.3.4 or later (including Oracle8.x), are also required. Oracle products are available from the Oracle Corporation.


New Features

The OLE DB Provider for Oracle is updated when it can benefit from enhancements to the OLE DB specification. This section contains a description of features recently added to this provider.

Table Statistics and Histograms

The OLE DB Provider for Oracle supports the OLE DB table statistics schema, which can be used to estimate the number and distribution of values in a query result. Implementation of this schema is defined by the OLE DB TABLE_STATISTICS schema rowset, the DBPROP_TABLESTATISTICS property, a new bit in the DBPROP_OPENROWSETSUPPORT property, and the new histogram rowset returned by IOpenRowset::OpenRowset.

Multiple Parameter Sets

The OLE DB Provider for Oracle supports multiple parameter sets when using ICommand::Execute, allowing you to overload the method.

Improved Error Information

Error information now contains the Oracle error number in the dwMinor field of the ERRORINFO structure. For more information about how error objects are returned, see "How a Provider Returns an OLE DB Error Object" in the OLE DB Programmer's Reference.


Additional Resources

The following technical articles and white papers are either posted on the "Technical Materials" page of the Universal Data Access Web site at http://www.microsoft.com/data/techmat.htm or are included with the Microsoft® Data Access Components (MDAC) SDK.

Articles are listed here in order of their relevance. Their presence and location are subject to change with each release. Other articles can be found in the Microsoft Developer Network (MSDN™) Library. Detailed articles concerning known limitations or workarounds can be found in the MSDN Knowledge Base.

Documents | Date written/posted
Microsoft OLE DB Provider for Oracle: Tips, Tricks, and Traps | July 1998
Pooling in the Microsoft Data Access Components | May 1999
Optimistic Remote Updating in the Client Cursor Engine | March 1999
Using Oracle Databases with COM+/Microsoft Transaction Server (KB article) | April, 2000

References

OLE DB Programmer's Reference

ADO Programmer's Reference

Web sites

http://www.microsoft.com/data/oledb

http://www.microsoft.com/data/ado


Using the Oracle Provider: A Closer Look

This section contains detailed information about using the OLE DB Provider for Oracle. It is divided into four parts, as follows:

"Setting up the Environment" describes software considerations and configurations needed to prepare the environment for processing.

"Invoking the Oracle Provider" describes the methods for invoking the Oracle provider and the basic OLE DB objects: data source, session, command, and rowset.

"Using the Oracle Provider With Other Providers and Services" discusses ways to enable other OLE DB services in order to expand the native capability of the provider.

"Programming Considerations for the OLE DB Provider for Oracle" describes strategies for dealing with specific issues that might arise when accessing Oracle databases through this provider.


Setting Up the Environment

The following sections discuss important software installation considerations, including correctly setting registry entries and locating software sources:

  • Installing the Software
  • Registry Entries
  • Oracle Software Patches
  • Using the Oracle Provider with MTS


Installing the Software

The OLE DB Provider for Oracle is a core component of the MDAC SDK.

Network software must be installed according to its own documentation. The OLE DB Provider for Oracle requires no special installation considerations as long as the network software conforms to Oracle's specifications. For more information about network software requirements, see System Requirements.

Oracle software must be installed according to its own documentation. The OLE DB Provider for Oracle requires no special installation considerations as long as the provider supports the version. Oracle maintains a public FTP site where it posts patches to the Oracle server products and to the client component that ships with the server products. These patches are required for the proper functioning of several Microsoft products and technologies. For more information about this site, see Oracle Software Patches.

Caution Installing Oracle software over the MDAC SDK may overwrite current versions of MDAC. If problems arise using OLE DB components, reinstall MDAC.


Registry Entries

To enable the OLE DB Provider for Oracle to work with your Oracle client software, the client's registry must be modified by running a registry file from a command line. Multiple instances of the client software should not run concurrently. These files are listed in the following table and are located within the same directory structure that contains your MDAC installation.

Oracle client Windows NT or Windows 9x Windows 2000
7.x
mtxoci7x_winnt.reg
mtxoci7x_win2k.reg
8.0
mtxoci80x_winnt.reg
mtxoci80x_win2k.reg
8.1
mtxoci81x_winnt.reg
mtxoci81x_win2k.reg

The effects on the registry of executing these registry files are shown in the following table.

Oracle client Windows NT or Windows 9x Windows 2000
7.x [HKEY_LOCAL_MACHINE\SOFTWARE\ Microsoft\Transaction Server\Local Computer\My Computer]
"OracleXaLib"="xa73.dll"
"OracleSqlLib"="SQLLib18.dll"
"OracleOciLib"="ociw32.dll"
 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSDTC\MTxOCI]
"OracleXaLib"="xa73.dll"
"OracleSqlLib"="SQLLib18.dll"
"OracleOciLib"="ociw32.dll"
8.0 [HKEY_LOCAL_MACHINE\SOFTWARE\ Microsoft\Transaction Server\Local Computer\My Computer]
"OracleXaLib"="xa80.dll"
"OracleSqlLib"="sqllib80.dll"
"OracleOciLib"="oci.dll"
 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSDTC\MTxOCI]
"OracleXaLib"="xa80.dll"
"OracleSqlLib"="sqllib80.dll"
"OracleOciLib"="oci.dll"
8.1 [HKEY_LOCAL_MACHINE\SOFTWARE\ Microsoft\Transaction Server\Local Computer\My Computer]
"OracleXaLib"="oraclient8.dll"
"OracleSqlLib"="orasql8.dll"
"OracleOciLib"="oci.dll"
 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSDTC\MTxOCI]
"OracleXaLib"="oraclient8.dll"
"OracleSqlLib"="orasql8.dll"
"OracleOciLib"="oci.dll"


Oracle Software Patches

Patches for the Oracle server products and its client component are required for the proper functioning of several Microsoft products and technologies, including the Microsoft ODBC Driver for Oracle, the Microsoft OLE DB Provider for Oracle, Internet Information Services (IIS), Component Services (or Microsoft Transaction Server, if you are using Windows NT), and so forth.

Note Because the Oracle FTP site is subject to change, you may find a variation of the following instructions when you visit the site.

To download the Oracle software patches

  1. Connect to the public FTP site at oracle-ftp.oracle.com. The user ID is "anonymous", and the password is your e-mail address.
  2. Navigate to the following directory: /server/wgt_tech/server/windowsNT.
  3. To download patches most relevant for Windows 95, Windows 98 and Windows NT/Windows 2000, navigate to the subdirectory for your version of Oracle—7.3x or 8.x. The two subdirectories are /73patchsets and /8xpatchsets.
  4. To download patches for Oracle's network technology, either SQL*Net or Net8, navigate to the following directory: /network.

Accessing this FTP site from your Web browser might not work. If you experience problems, try using a "traditional" FTP client or use the DOS command prompt.

Note Because Oracle fixes bugs in current versions and then retrofits them to earlier versions using software patches, it is recommended that you download the latest patch available. This is especially true for the Oracle Server Client components. If you have questions about installing these patches, contact Oracle Support.


Using the Oracle Provider with MTS

The following software is required to access an Oracle database using MTS components when using this provider.

Software | Component | Version
Oracle7 | Oracle Server | 7.3.3.5.2 or later
 | Oracle SQL*Net | 2.3.3.0.4 or later
 | Oracle Ociw32.dll | 1.0.0.5 or later
 | Oracle Sqllib18.dll | 1.8.3.0.4 or later
 | Oracle Xa73.dll | 7.3.3.5.2 or later
Oracle8 | Oracle Server | 8.0.4.1.1c or later
 | Oracle Net8 | 8.0.5.2.4 or later
 | Oracle Ociw32.dll | 8.0.4.0.0 or later
 | Oracle Sqllib80.dll | 8.0.4.1.0 or later
 | Oracle Xa80.dll | 8.0.4.1.0 or later
Microsoft | Microsoft Transaction Server 2.0 | MTS 2.0 or later
 | Microsoft Data Access Components | MDAC 2.6 or later


Connectivity Checklist

Common configuration and connectivity issues, when setting up Oracle software with the OLE DB Provider for Oracle, can be avoided by verifying that the following details are true:

  • The correct version of the Oracle client (SQL *Net version) is installed on the computer. Msdaora.dll requires that SQL*Net version 2.3 or higher be installed on the client to enable connectivity from the client machine.
  • The folder in which the Oracle client was installed is in the computer's path. From a command line prompt, type "path".
  • The Ociw32.dll is in the <Oracle_Root>\bin folder. There should no other instance of this dll anywhere else on the client computer.
  • None of the Oracle client component DLLs (eg : core40.dll, ora*dll etc.) should exist any where outside the Oracle_Root folder or its sub-folders.
  • Only a single Oracle client version should be installed on the computer. Multiple SQL*Nets should not exist on the same client computer. Multiple instances hamper critical Oracle operations, such as TNS Alias lookups.


Invoking the Oracle Provider

The OLE DB Provider for Oracle (Msdaora.dll) can be invoked using a variety of methods, depending on your programming needs. Calling CoCreateInstance on IDBInitialize is traditionally used in OLE DB to open a data source object. However, to use resource pooling, you call CoCreateInstance on either IDataInitialize or IDBPromptInitialize. Both interfaces are part of the OLE DB service components and not your OLE DB data provider. Using IDataInitialize and IDBPromptInitialize to retrieve an instance of IDBInitialize from the OLE DB Provider for Oracle enables the service components, which include pooling, transaction enlistment, and the Client Cursor Engine. These methods are illustrated in Appendix F: Sample OLE DB Consumer Application in the OLE DB Programmer's Reference.

Connecting from ADO

The OLE DB Provider for Oracle is invoked from ADO using a typical connection string, such as the following:

"Provider=MSDAORA;Data Source=serverName;User ID=userName; Password=userPassword;"

When called from ADO, the OLE DB services are automatically enabled but the Client Cursor Engine service is not. For more information about these services, see "OLE DB Services" in the OLE DB Programmer's Reference. For more information on connection strings and keywords, see the ADO Programmer's Reference.


Data Source Objects

OLE DB uses the term data source object 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 of the OLE DB Programmer's Reference, "Data Source and Session Objects."

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 COM CoCreateInstance function to manufacture an instance of the data source object.

The Oracle provider is an in-process server. Instances of the 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 object 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 object 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 Mtxoci.dll (which in turn checks the registry keys) when the client application tries to initialize. If this file is not an Oracle version 7.3.4 or later file, or if the file is not found, the provider returns the following 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.4 (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 OLE DB Provider for Oracle interprets OLE DB initialization and authorization properties briefly described in the following table. Only the supported properties are listed. For those not supported, an error is returned. For the full description of each property, see Appendix C: OLE DB Properties, of 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. It uses DBPROMPT_NOPROMPT as its default setting for the property.

Note Oracle databases successfully authenticate users that are logged on through SQLPlus with SQL*Net Authentication services. However, when the logged on user is a service itself, such as Internet Information Server, the authentication fails. This is a known limitation of SQL*Net Authentication. Adding the following line to the Sqlnet.ora file will correct the problem:

SQLNET.AUTHENTICATION_SERVICES = (none)

Sessions

A provider session represents a single connection to an Oracle server.

OLE DB requires that sessions delimit transaction space for a data source object. 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 object 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 object.

An additional session object created on the data source object establishes its own connection to the OLE DB Provider for Oracle as specified by the data source object. The Oracle connection is dropped when the application releases all references to objects created during that session.

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. You can keep the Oracle connection of a session object active by maintaining a reference on at least one interface of the session object.

To provide advanced schema information about the kind of data in a data store, you can use the IDBSchemaRowset interface. When using the GetRowset method on this interface, a case-sensitive selection is performed only if a quoted value is passed in.


Session Properties

The OLE DB Provider for Oracle interprets OLE DB session properties as follows. For the full description, see Appendix C of the OLE DB Programmer's Reference, "OLE DB Properties."

Property ID | Short description
DBPROP_SESS_AUTO-COMMITISOLEVELS | This provider supports DBPROP_TI_READCOMMITED and DBPROP_TI_SERIALIZABLE.

Note Oracle session properties are stored in the NLS_SESSION_PARAMETERS table, which contains properties such as language, territory, sort, and time format.


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. For consumers requiring transaction control that spans multiple sessions, this provider can join transactions initiated and maintained by Microsoft Distributed Transaction Coordinator.

By default, the OLE DB Provider for Oracle uses an auto-commit transaction mode, where each discrete action on a consumer session comprises a complete transaction against an Oracle server. For this provider, auto-commit mode is local. Auto-commit 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. It also supports ITransactionJoin, which is used for enlisting a transaction in a distributed transaction. This provider does not support nested local transactions.

The Transaction Unit Of Work (UOW) as reported by ITransaction::GetTransactionInfo will always be NULL. For example, if a transaction is obtained from a session and GetTransactionInfo is called on it, the XACTUOW from the XACTTRANSINFO will always be NULL.

When using this provider with Component Services (or MTS, if you are using Windows NT) to enlist global transactions, the file Mtxoci.dll is required.

Note When accessing an Oracle8.x database and using a "select … for update" statement, you first must start a transaction. Otherwise, the following error will occur:

"Fetch out of sequence (ORA-01002)"

Nested Transactions

The OLE DB Provider for Oracle does not support nested transactions. A nested transaction occurs when a transaction is begun within the scope of another transaction. Nested transactions can be simulated, however, by using the native Oracle SQL SAVEPOINT keyword in an execute command. This sets a bookmark for uncommitted statements in an Oracle session. For more information about transactions in Oracle, search the MSDN Knowledge Base (keyword "kbOracle").


Local Transactions

A session delimits transaction scope for an OLE DB Provider for Oracle 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.

The provider's default auto-commit mode treats a single unit of work 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 ITransaction::Commit or ITransaction::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 auto-commit mode.

This provider supports ITransactionLocal::StartTransaction parameters as described in the following table.

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 transaction 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 described in the following table.

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 auto-commit 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 described in the following table.

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 auto-commit 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.

This provider 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 values:

  • The DBPROPSET_SESSION property DBPROP_SESS_AUTO-COMMITISOLEVELS for this provider's default auto-commit 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 about | Go to
Isolation levels | "Isolation Levels" in Chapter 15 of the OLE DB Programmer's Reference, "Transactions"
Transaction locking | "Transaction Locks" in Chapter 15 of the OLE DB Programmer's Reference, "Transactions"


Commands

The OLE DB Provider for Oracle exposes the ICommand interface and command objects. Command objects contain a single text command.

For more information about commands and command objects, see Chapter 3: Commands in the OLE DB Programmer's Reference.


Command Parameters

The OLE DB Provider for Oracle supports multiple parameter sets on the command object.

The Oracle provider 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.

If the name of a stored procedure parameter is specified, the Oracle provider checks the name to ensure that it is valid. The Oracle provider returns an error when it receives an erroneous parameter name from the consumer.

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.


Command Syntax

The OLE DB Provider for Oracle recognizes command syntax specified by the DBGUID_DBSQL macro. Two examples are presented below. Additionally, it allows the use of REF CURSOR data types.

ODBC SQL Syntax

{call CREDIT_TO_ACCOUNT(123456, 100.00)}

Oracle PL/SQL Syntax

BEGIN
   CREDIT_TO_ACCCOUNT(123456, 100.00);
END

Note The OLE DB Provider for Oracle does not support the {resultset} escape clause. To get this behavior, you must use the OLE DB Provider for ODBC Drivers and the Microsoft ODBC Driver for Oracle.

Stored Procedures

The provider supports two methods of calling a stored procedure or function: the canonical syntax defined in the ODBC specification; and the Oracle native syntax, PL/SQL.

You can call a stored procedure using the REF CURSOR argument as one parameter, as in the following example:

{call StoredProcedureName ({resultset 0, NameOfTheRefCursorArgument})}

Synonyms for stored procedures are not supported; however, they are supported for other Oracle database objects, such as tables.

Note When running an Oracle Stored Procedure that uses 10 or more output parameters, if proper Oracle patches have not been installed, your application may receive an Access Violation or ActiveX® Data Objects (ADO) error. This problem is restricted to some Oracle 8.0.4 and Oracle 8.0.5 database versions.


Rowsets

The OLE DB Provider for Oracle exposes the IRowset interface and rowset objects. It 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.

For information about the Rowset properties supported by this provider, see "DBPROPSET_ROWSET" in this documentation.


BLOBs and COM 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 IsequentialStream::Read method allows the consumer to retrieve large amounts of data in manageable chunks.

Storage Object Limitations

The Oracle provider's 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 about | Go to
Binary Large Objects | Chapter 7 of the OLE DB Programmer's Reference, "BLOBs and COM Objects"
The ISequentialStream interface | ISequentialStream


Notifications

The OLE DB Provider for Oracle supports consumer notification on rowset release and next fetch position change.

To receive notification, the consumer queries the rowset for a connection-point interface and then connects a consumer-implemented IRowsetNotify interface to the rowset.


Errors

COM objects report errors through the HRESULT return code of object member functions. An HRESULT is a bit-packed structure. COM provides macros that dereference structure members.

The OLE DB Provider for Oracle exposes IErrorLookup to retrieve information about an error.

For more information about | Go to
Errors in OLE DB | Chapter 16 of the OLE DB Programmer's Reference, "Errors"
Error lookup services | "Error Lookup Services" in Chapter 16 of the OLE DB Programmer's Reference, "Errors"
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 result might 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 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 consumer of this provider is ensured that member-function execution failed. When FAILED or IS_ERROR returns FALSE and the HRESULT does not equal S_OK, 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 might be available to the consumer other than the information returned in the status-value parameters, so consumers should implement application logic that retrieves status values when they are available.

Note Error information contains the Oracle error number in the dwMinor field of the ERRORINFO structure.

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 about return codes, see "Return Codes" in Chapter 16 of the OLE DB Programmer's Reference, "Errors."


Using the Oracle Provider With Other Providers and Services

Individual OLE DB services, including resource pooling, can be enabled or disabled for all consumers that access this provider by adding an OLEDB_SERVICES registry entry under the provider's CLSID in the registry. For more information about manipulating these entries, see "Setting Provider Service Defaults" in the OLE DB Programmer's Reference. ADO automatically attempts to enlist OLE DB services when invoking the provider.


Using the Oracle Provider with the OLE DB Cursor Service

The OLE DB Provider for Oracle does not natively implement fetching backwards, scrolling backwards, or the IRowsetChange, IRowsetFind, IRowsetLocate, or IRowsetScroll interfaces. Properties that support scrollability and updatability, such as DBPROP_CANFETCHBACKWARDS, DBPROP_CANSCROLLBACKWARDS, DBPROP_IRowsetScroll, DBPROP_IRowsetChange, and DBPROP_UPDATABILITY, are by default VARIANT_FALSE. Therefore, applications that require scrollable and updatable rowsets must use the Client Cursor Engine service to construct one.

Support for these properties and interfaces is provided through the invocation of the Cursor Engine by the OLE DB Service Components. If Service Components have been disabled for some reason (through a registry entry under the Provider's CLSID or by setting the OLE DB Services initialization property, DBPROP_INIT_OLEDBSERVICES, prior to connection), these properties will not be available.

When using ADO, you will get the Client Cursor Engine service only if you specify adUseClient when opening the recordset.

Considerations When Populating a Rowset Using the Client Cursor Engine

The Client Cursor Engine service provides a snapshot cursor, fully materialized, for your application. However, unless specifically requested that the cursor engine should use asynchronous fetches, it will wait until it completes its fetches before returning control to your application. In the case of a large rowset, this could be expensive. For background population, set DBPROP_ROWSET_ASYNCH to DBPROPVAL_ASYNCH_BACKGROUNDPOPULATION.

The size of the cursor can be controlled by setting DBPROP_MAX_ROWS. However, this limits the entire rowset to that number of rows, not the rows currently fetched.


Programming Considerations for the OLE DB Provider for Oracle

This section discusses the programming issues to consider when using the OLE DB Provider for Oracle, including performance issues, use of Synonyms, and inherent limitations. For additional information about programming issues, see the technical article "Microsoft OLE DB Provider for Oracle: Tips, Tricks, and Traps."


Commands and Performance

Each time a statement is executed, the provider does some work to prepare for its execution, such as determining rowset columns and preparing for parameter binding. Whenever a statement will be executed more than once, consider using ICommandPrepare to force the preparation of the command to occur only once. When you do not use ICommandPrepare, each execution will have to redo the work.


Synonyms

This provider exposes synonyms as tables. To place restrictions on them, create the synonym as private and then grant select permissions only to desired users.


Limitations of the OLE DB Provider for Oracle

Enhancements to the Microsoft OLE DB Provider for Oracle are designed to take advantage of significant technology improvements, but because the provider is dependent on a variety of release schedules, not all new features can be incorporated simultaneously. The resulting limitations follow.

Size of VARCHAR Type Column

In Oracle8, the size of the RAW and CHAR data types increased to 2000 bytes. This allowed the maximum size of a VARCHAR type column to increase from 2000 to 4000 bytes. The Oracle 7.3.x client software has no way to bind a parameter value larger than 2000 bytes.

Therefore, if you create a table with a VARCHAR type column of larger than 2000 bytes, you will be unable to perform parameterized inserts, updates, deletes, and queries against it with data that exceeds the 2000-byte limit of the client software. ORA-01026 errors will be returned. Data that is within the limits enforced by the Oracle client software will work properly.

To avoid this 2000-byte limit, you must upgrade your client software to Oracle8 (version 8.0.4.1.1c or later).

Reading System Tables

Some system tables returned by IDBSchemaRowset might not be accessible due to system access restrictions. When IDBSchemaRowset::GetRowset is called for a value of DBSCHEMA_TABLE that is a system table, an error may occur and E_FAIL is returned.

Limitations on Column-Name Size When Using DBCS

Because Oracle is based on multibyte character sets, this provider restricts the 30-byte limitation on column names to 15 when using the Double Byte Character Set.

Possibility of Unrecognized Duplicate Rowsets

This provider allows Oracle to add a ROWID column to SELECT statements, making each row uniquely identifiable in order to have an updatable rowset. When attempting to fetch rowsets (for example, by using the SELECT DISTINCT command), duplicate rowsets might not be recognized because the ROWID column makes each one unique.

Limitations of DBTYPE_VARNUMERIC

The range of numbers in Oracle exceeds the range of DBTYPE_VARNUMERIC. Numbers larger than those with an exponent of 127 or those with an exponent less than -128 cannot be represented by this provider.

Bookmarks

Bookmarks are not supported.

OLE DB Views

Views are not supported.

Errors When Using Oracle 7.x Servers

Using this provider against an Oracle 7.x server may produce an error when performing an outer join. In an outer join, all columns are potentially nullable; however, Oracle 7.x may report that a column in an outer join is non-nullable and therefore generate an error when a resulting column contains a null value. The workaround is to use Oracle 8.x servers.

Oracle Server Converts Empty Strings to Null Values

When updating an Oracle database using this provider, be aware that Oracle will convert empty strings to NULL. When using the ADO Update method, you may receive the error "The specified row could not be located for updating; Some values may have been changed since it was last read." (80040e38). This occurs because the Oracle server converts the empty string to NULL, but the ADO Recordset object stores the empty string.


Appendix A: Data Types

The OLE DB Provider for Oracle supports all Oracle 7.3 data types; however, it does not support any of the new Oracle8 data types listed here or any of the Object extensions to Oracle8.

Data type | Oracle 7.3 | Oracle8
BFILE | n/a | Not supported
BLOB | n/a | Not supported
CHAR | Supported | Supported
CLOB | n/a | Not supported
DATE | Supported | Supported
FLOAT | Supported | Supported
INTEGER | Supported | Supported
LONG | Supported | Supported
LONG RAW | Supported | Supported
NCHAR | n/a | Not supported
NCLOB | n/a | Not supported
NUMBER | Supported | Supported
NVARCHAR2 | n/a | Not supported
RAW | Supported | Supported
VARCHAR2 | Supported | Supported
MLSLABEL | Not supported | Not supported

Note For more information about the allowable size of the VARCHAR column, see "Limitations of the OLE DB Provider for Oracle" in this guide.


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 Note In several cases, numeric data will be returned as DBTYPE_VARNUMERIC. The most notable situations are for columns defined as FLOAT, NUMBER (without precision or scale), computed numeric columns, or any situation where the scale is either negative or exceeds 38 digits.
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—See Note earlier in table.)
RAW | DBTYPE_BYTES
VARCHAR2 | DBTYPE_STR

Note This provider does not support the Oracle MLSLABEL data type.


Appendix B: Supported Interfaces of the Oracle Provider

The OLE DB Provider for Oracle supports the following OLE DB interfaces.

Interface Notes
IDBCreateSession Object type: data source

Obtains a new session.
IDBInitialize Object type: data source

Initializes a data source object or enumerator.
IDBProperties Object type: data source

Sets and gets the values of properties on the data source object or enumerator and gets information about all properties supported by the provider.

IPersist* Object type: data source

Used to control exposure of data to transactions. 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 about IPersist*, see "IPersist* Objects" in the OLE DB Programmer's Reference.
IGetDataSource Object type: session

Obtains an interface pointer to the data source object.
IOpenRowset Object type: session

Enables consumers to open and work directly with individual tables or indexes in a data store.

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.
ISessionProperties Object type: session

Returns information about the properties a session supports and the current settings of those properties.

ITransactionJoin Object type: transaction

Support for distributed transactions on the session.
ITransactionLocal Object type: transaction

Starts, commits, and aborts transactions on the session.
ITransactionOptions Object type: transaction

Gets and sets various options associated with a transaction.
IAccessor Object type: command

Provides methods for accessor management.

Note This provider ignores dwFlags when calling IAccessor::CreateAccessor and will not validate or honor settings for DBBINDFLAGS_HTML or any other flag in this field. The consumer should set dwFlags to 0.
IColumnsInfo Object type: command, rowset

Exposes information about columns of a rowset or prepared command.

ICommand Object type: command

Contains methods to execute commands.
ICommandPrepare Object type: command

Encapsulates command optimization, a separation of compile time and run time.
ICommandProperties Object type: command

Specifies to the command the properties from the Rowset property group that must be supported by the rowsets returned by ICommand::Execute.

ICommandText Object type: command

Gets or sets the command text.
IConvertType Object type: command, rowset

Provides information about the availability of type conversions on a command, a rowset, or a row.
IRowset Object type: rowset

Provides methods for fetching rows sequentially, getting the data from those rows, and managing rows.
IRowsetInfo Object type: rowset

Provides information about a rowset.


Appendix C: Supported Properties of the Oracle Provider

The OLE DB properties supported by this provider are listed by PROPSETs, as follows:

  • DBPROPSET_DATASOURCE
  • DBPROPSET_DBINIT
  • DBPROPSET_DATASOURCEINFO
  • DBPROPSET_SESSION
  • DBPROPSET_ROWSET
  • DBPROPSET_MSDAORA_ROWSET

DBPROPSET_DATASOURCE

Property | Short Description
DBPROP_CURRENTCATALOG | Current catalog.

DBPROPSET_DBINIT

Property | Short Description
DBPROP_AUTH_PASSWORD | The password assigned to an Oracle user.
DBPROP_AUTH_USERID | The Oracle user ID to be used when connecting to the data source object or enumerator.
DBPROP_INIT_DATASOURCE | An existing Oracle server to connect to as the data source.
DBPROP_INIT_HWND | A window handle from the calling application.
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_OLEDBSERVICES | A bitmask specifying OLE DB services to enable or disable.
DBPROP_INIT_PROMPT | This provider supports all prompting modes for data source object initialization. It uses DBPROMPT_NOPROMPT as its default setting for the property.
DBPROP_INIT_PROVIDERSTRING | A string containing provider-specific, extended connection information.

DBPROPSET_DATASOURCEINFO

Property | Short Description
DBPROP_ACTIVESESSIONS | The maximum number of session objects that can be active at one time.
DBPROP_ASYNCTXNABORT | Indicates whether transactions can be aborted asynchronously.
DBPROP_ASYNCTXNCOMMIT | Indicates whether transactions can be committed asynchronously.
DBPROP_BYREFACCESSORS | Indicates whether the provider supports the DBACCESSOR_PASSBYREF flag in IAccessor::CreateAccessor.
DBPROP_CATALOGLOCATION | Indicates the position of the catalog name in a qualified table name in a text command.
DBPROP_CATALOGTERM | Indicates the name the data source object uses for a catalog.
DBPROP_CATALOGUSAGE | Catalog usage. A bitmask specifying how catalog names can be used in text commands.
DBPROP_COLUMNDEFINITION | A bitmask defining the valid clauses for the definition of a column.
DBPROP_CONCATNULLBEHAVIOR | Indicates how the data source object handles the concatenation of NULL-valued character data type columns with non–NULL-valued character data type columns.
DBPROP_CONNECTIONSTATUS | Indicates the status of the current connection.
DBPROP_DATASOURCENAME | Indicates the name of the data source object.
DBPROP_DATASOURCEREADONLY | Indicates a read-only data source.
DBPROP_DBMSNAME | The name of the product accessed by the provider, such as "ORACLE Server".
DBPROP_DBMSVER | Indicates the version of the product accessed by the provider.
DBPROP_DSOTHREADMODEL | A bitmask specifying the threading models supported by the data source object.
DBPROP_GROUPBY | Indicates the relationship between the columns in a GROUP BY clause and the nonaggregated columns in the select list.
DBPROP_HETEROGENEOUSTABLES | A bitmask specifying whether the provider can join tables from different catalogs or providers.
DBPROP_IDENTIFIERCASE | Indicates how identifiers treat case in data definition commands or interfaces.
DBPROP_MAXINDEXSIZE | Indicates the maximum number of bytes allowed in the combined columns of an index.
DBPROP_MAXOPENCHAPTERS | Indicates the maximum number of chapters that can be open at any time.
DBPROP_MAXROWSIZE | Indicates the maximum length of a single row in a table.
DBPROP_MAXROWSIZEINCLUDESBLOB | Indicates whether the maximum row size includes BLOB length.
DBPROP_MAXTABLESINSELECT | Indicates the maximum number of tables allowed in the FROM clause of a SELECT statement.
DBPROP_MULTIPLEPARAMSETS | Indicates support for multiple parameter sets.
DBPROP_MULTIPLERESULTS | A bitmask specifying whether the provider supports multiple results objects and what restrictions it places on these objects.
DBPROP_MULTIPLESTORAGEOBJECTS | Indicates support for multiple, open storage objects at the same time.
DBPROP_MULTITABLEUPDATE | Indicates whether the provider can update rowsets derived from multiple tables.
DBPROP_NULLCOLLATION | Indicates where NULLs are sorted in a list.
DBPROP_OLEOBJECTS | A bitmask specifying the ways in which the provider supports access to BLOBs and COM objects stored in columns.
DBPROP_OPENROWSETSUPPORT | A bitmask describing support for opening objects through IOpenRowset.
DBPROP_ORDERBYCOLUMNSINSELECT | Indicates whether or not columns in an ORDER BY clause must be in the select list.
DBPROP_OUTPUTPARAMETERAVAILABILITY | Indicates the time at which output parameter values become available.
DBPROP_PERSISTENTIDTYPE | An integer specifying the type of DBID that the provider uses when persisting DBIDs that name entities in the data store, such as tables, indexes, columns, commands, or constraints.
DBPROP_PREPAREABORTBEHAVIOR | Indicates how aborting a transaction affects prepared commands.
DBPROP_PREPARECOMMITBEHAVIOR | Indicates how committing a transaction affects prepared commands.
DBPROP_PROCEDURETERM | A character string with the data store vendor's name for a procedure.
DBPROP_PROVIDERNAME | Indicates the file name of the provider.
DBPROP_PROVIDERFRIENDLYNAME | Indicates the friendly name of the provider.
DBPROP_PROVIDEROLEDBVER | Indicates the version of OLE DB supported by the provider.
DBPROP_PROVIDERVER | Indicates the version of the provider.
DBPROP_QUOTEDIDENTIFIERCASE | Indicates how quoted identifiers treat case.
DBPROP_ROWSETCONVERSIONSONCOMMAND | Indicates how rowset conversions on command are handled.
DBPROP_SCHEMATERM | The name the data source object uses for a schema.
DBPROP_SCHEMAUSAGE | A bitmask specifying how schema names can be used in text commands.
DBPROP_SQLSUPPORT | A bitmask specifying the level of support for SQL.
DBPROP_STRUCTUREDSTORAGE | A bitmask specifying what interfaces the rowset supports on storage objects.
DBPROP_SUBQUERIES | A bitmask specifying the predicates in text commands that support subqueries.
DBPROP_SUPPORTEDTXNDDL | Indicates the relationship of transactions to table and index modification data definition language (DDL) statements.
DBPROP_SUPPORTEDTXNISOLEVELS | A bitmask specifying the supported transaction isolation levels.
DBPROP_SUPPORTEDTXNISORETAIN | A bitmask specifying the supported transaction isolation retention levels.
DBPROP_TABLESTATISTICS | Bitmask indicating statistics support.
DBPROP_TABLETERM | Indicates the name the data source object uses for a table.
DBPROP_USERNAME | A character string with the name used in a particular database, which can be different than a login name.

DBPROPSET_SESSION

Property | Short Description
DBPROP_SESS_AUTOCOMMITISOLEVELS | A bitmask specifying the transaction isolation level while in auto-commit mode. This provider supports DBPROP_TI_READCOMMITED and DBPROP_TI_SERIALIZABLE.

DBPROPSET_ROWSET

Property | Short Description
DBPROP_IAccessor | R/W: Read-only Default: VARIANT_TRUE The value of this property is read-only and is always set to VARIANT_TRUE, indicating that the rowset supports IAccessor.
DBPROP_IColumnsInfo | R/W: Read-only Default: VARIANT_TRUE The value of this property is read-only and is always set to VARIANT_TRUE, indicating that the rowset supports IColumnsInfo.
DBPROP_IColumnsRowset | R/W: Read-only Default: Indicates whether or not the rowset supports IColumnsRowset.
DBPROP_IConnectionPointContainer | R/W: Read-only Default: VARIANT_TRUE Indicates whether or not the rowset supports IConnectionPointContainer.
DBPROP_IConvertType | R/W: Read-only Default: VARIANT_TRUE The value of this property is read-only and is always set to VARIANT_TRUE, indicating that the rowset supports IConvertType.
DBPROP_IRowset | R/W: Read-only Default: VARIANT_TRUE The value of this property is read-only and is always set to VARIANT_TRUE, indicating that the rowset supports IRowset.
DBPROP_IRowsetIdentity | R/W: Read-only Default: VARIANT_TRUE Indicates whether or not the rowset supports IRowsetIdentity.
DBPROP_IRowsetInfo | R/W: Read-only Default: VARIANT_TRUE The value of this property is read-only and is always set to VARIANT_TRUE, indicating that the rowset supports IRowsetInfo.
DBPROP_ISupportErrorInfo | R/W: Read-only Default: VARIANT_TRUE Indicates whether or not the rowset supports ISupportErrorInfo.
DBPROP_ISequentialStream | R/W: Read-only Default: VARIANT_TRUE If the value of this property is set to VARIANT_TRUE, the rowset is capable of manipulating the contents of columns as a storage object supporting the specified interface.
DBPROP_ABORTPRESERVE | R/W: Read-only Default: VARIANT_FALSE After aborting a transaction, the rowset remains active.
DBPROP_ACCESSORDER | R/W: Read-only Default: Sets the order in which columns must be accessed by methods that operate on rowsets, rows, and streams.
DBPROP_BLOCKINGSTORAGEOBJECTS | R/W: Read-only Default: VARIANT_FALSE Indicates whether storage objects might prevent use of other methods on the rowset.
DBPROP_BOOKMARKS | R/W: Read-only Default: VARIANT_FALSE Indicates whether the rowset supports bookmarks.
DBPROP_CACHEDEFERRED | R/W: Read-only Default: VARIANT_FALSE Indicates whether or not the provider caches the value of a deferred column.
DBPROP_CANFETCHBACKWARDS | R/W: Read-only Default: VARIANT_FALSE Indicates whether the rowset can fetch backward.
DBPROP_CANHOLDROWS | R/W: Read-only Default: VARIANT_TRUE Indicates whether or not the rowset allows the consumer to retrieve more rows or change the next fetch position, while holding previously fetched rows or rows with pending changes.
DBPROP_CANSCROLLBACKWARDS | R/W: Read-only Default: VARIANT_FALSE Indicates whether the rowset can scroll backward.
DBPROP_COMMITPRESERVE | R/W: Read-only Default: VARIANT_TRUE Indicates whether or not the rowset remains active after committing a transaction
DBPROP_DEFERRED | R/W: Read-only Default: VARIANT_FALSE Indicates whether or not the data in the column is not fetched until an accessor is used on the column.
DBPROP_HIDDENCOLUMNS | R/W: Read-only Default: Returns the number of additional "hidden" columns added by the provider to uniquely identify rows within the rowset.
DBPROP_LITERALIDENTITY | R/W: Read-only Default: VARIANT_TRUE Indicates whether or not the consumer can perform a binary comparison of two row handles to determine whether they point to the same row.
DBPROP_MAXOPENROWS | R/W: Read-only Default: 0 Indicates the maximum number of rows that can be active at the same time.
DBPROP_MAXROWS | R/W: Read/Write Default: 0 Indicates the maximum number of rows that can be returned in a rowset.
DBPROP_NOTIFICATIONGRANULARITY | R/W: Read-only Default: NT_MULTIPLE_ROWS Provides the notification granularity.
DBPROP_NOTIFICATIONPHASES | R/W: Read-only Default: DBPROPVAL_NP_OKTODO DBPROPVAL_NP_ABOUTTODO DBPROPVAL_NP_SYNCHAFTER DBPROPVAL_NP_FAILEDTODO BPROPVAL_NP_DIDEVENT A bitmask specifying the notification phases supported by the provider.
DBPROP_NOTIFYROWSETRELEASE | R/W: Read-only Default: DBPROPVAL_NP_OKTODO DBPROPVAL_NP_ABOUTTODO A bitmask specifying whether the notification phase is cancelable.
DBPROP_NOTIFYROWSETFETCHPOSITIONCHANGE | R/W: Read-only Default: DBPROPVAL_NP_OKTODO DBPROPVAL_NP_ABOUTTODO A bitmask specifying whether the notification phase is cancelable.
DBPROP_OTHERINSERT | R/W: Read-only Default: VARIANT_FALSE Indicates whether or not rows inserted by a consumer or process other than a consumer of the rowset are visible.
DBPROP_OTHERUPDATEDELETE | R/W: Read-only Default: VARIANT_FALSE Indicates whether or not rows modified (updated or deleted) by a consumer or process other than a consumer of the rowset are visible.
DBPROP_OWNINSERT | R/W: Read-only Default: VARIANT_FALSE Indicates whether or not the inserts to the rowset are visible.
DBPROP_OWNUPDATEDELETE | R/W: Read-only Default: VARIANT_FALSE Indicates whether or not the updates and deletes made by the rowset consumer are visible.
DBPROP_QUICKRESTART | R/W: Read-only Default: VARIANT_FALSE Indicates whether IRowset::RestartPosition is relatively quick to execute.
DBPROP_REENTRANTEVENTS | R/W: Read-only Default: VARIANT_FALSE Indicates whether the provider supports reentrancy during callbacks to the IRowsetNotify interface.
DBPROP_REMOVEDELETED | R/W: Read-only Default: False Indicates whether the provider removes rows it detects as having been deleted from the rowset.
DBPROP_ROWTHREADMODEL | R/W: Read-only Default: DBPROPVAL_RT_FREETHREAD A bitmask specifying the threading models supported by the rowset.
DBPROP_SERVERCURSOR | R/W: Read-only Default: VARIANT_FALSE Works in conjunction with other cursor properties, including DBPROP_CLIENTCURSOR, to determine where a cursor, if required, is materialized.
DBPROP_STRONGIDENTITY | R/W: Read-only Default: VARIANT_TRUE Indicates whether or not the handles of newly inserted rows can be compared.
DBPROP_TRANSACTEDOBJECT | R/W: Read-only Default: VARIANT_FALSE Indicates whether or not any object created on the specified column is transacted.
DBPROP_UNIQUEROWS | R/W: Read/Write Default: VARIANT_FALSE Indicates whether or not each row is uniquely identified by its column values.

DBPROPSET_MSDAORA_ROWSET

Property | Short Description
DBPROP_MSDAORA_DETERMINEKEYCOLUMNS | R/W: Read/Write Default: VARIANT_FALSE Indicates whether or not each a row is uniquely identified by its key column so a ROWID will not be generated. DBPROP_UNIQUEROWS must be set to true.


Jet Provider

OLE DB Provider for Microsoft Jet

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). Included are descriptions of Jet-specific implementations of standard OLE DB interfaces, methods, and properties.


New Features

The OLE DB Provider for Microsoft Jet includes the following new features:

  • Data Definition Language
  • Security
  • Indexes
  • Persisting Commands/SQL Statements
  • Compacting Jet Databases
  • File Caching and Data Consistency
  • Additional Schema Rowset Support
  • IISAM Support


Data Definition Language

The OLE DB specification has been extended to encompass interfaces to better define and manage tables, indexes, relationships, and other database objects. These interfaces include IAlterTable, IAlterIndex, ITableDefinitionWithConstraints, and ITableCreate. The Jet Provider supports these interfaces as an alternative to SQL-based Data Definition Language (DDL) statements. Also, existing interfaces such as ITableDefinition and IIndexDefinition have been further clarified in the specification, and the new provider reflects these clarifications.

Although the version 3.5 provider supports some interface-based DDL capabilities, its use in conjunction with the ADO DDL components is not recommended. The capabilities of the older provider are more limited, and there has not been much integration testing between these two components, therefore increasing the possibility of encountering bugs or limitations. Now, using the Installable ISAM technology in Jet, you can perform DDL operations against older Jet file formats, including the 3.x format that the version 3.5 IISAM supports through the version 4.0 provider.


Security

OLE DB now defines security interfaces to manage users and objects in a generic fashion. These expose the functionality of the Jet engine in a way that should allow interoperability with other security models and implementations. Security in OLE DB is defined by four interfaces: ITrusteeAdmin, ITrusteeGroupAdmin, IObjectAccessControl, and ISecurityInfo. The Jet OLE DB Provider implements all of these interfaces, allowing full control of Jet security.

Jet also now supports SQL-based security commands. These are based on the SQL-92 GRANT/REVOKE syntax and enable security management in a fashion similar to traditional relational databases. Because some portions of Jet's security model are different from other database engines, this syntax will not be completely compatible with other databases. However, you should be able to access the complete spectrum of Jet security capabilities through the SQL grammar, which should provide additional flexibility to consumers who previously had to use DAO or Microsoft Access to manage security in Jet.


Indexes

While some versions of the 3.5 provider exposed the IRowsetIndex interface, actual OLE DB spec compliance was somewhat lacking. The 3.5 provider did not support setting ranges via IRowsetIndex::SetRange, and the IRowsetIndex::Seek method did not properly implement all seeking options. The final version of the 3.5 code disables this interface because of these limitations. All clients that want to use indexes to seek or set ranges should use the 4.0 code instead of the 3.5 code. The 4.0 provider fully complies with the OLE DB specification in this area.

Although the 4.0 provider supports the Seek and SetRange interfaces, it does not support these methods on file formats other than the 4.0 .mdb file format. To accommodate the OLE DB specification, significant changes and additions had to be made and it was not possible to make these changes to the other Installable ISAMs that Jet uses to access other storage formats, including the IISAMs that access previous formats of .mdb files (which includes those used in Microsoft Access 97).

SetRange is not currently exposed in ADO.


Persisting Commands/SQL Statements

Until OLE DB 2.0, there was no method-based mechanism for persisting and using persisted commands generically. Previously, the only way to create persisted commands, such as views or procedures, against OLE DB sources was to use SQL-based commands such as CREATE VIEW or CREATE PROCEDURE. Jet 3.5 did not natively support this syntax, essentially precluding clients from persisting commands against the version 3.5 provider. In Jet 4.0, clients can use either the SQL-based CREATE VIEW/CREATE PROCEDURE commands or the new ICommandPersist interface to save commands. Only the version 4.0 provider exposes this interface.


Compacting Jet Databases

Previously, there was no way through the Jet 3.5 OLE DB Provider to compact Jet databases. There is also no official mechanism in OLE DB to do maintenance work on databases at this time. The 4.0 provider now exposes a provider-specific interface called IJetCompact that can be used to compact databases and manage compaction-related properties, such as database encryption and database repair.


File Caching and Data Consistency

Jet was designed as a file-share database. Multiple clients on different machines are able to log in to the database file store and make updates and additions and perform other database operations. This differs from some other, typically client-server, database engines that have a single client (the server) interacting with the file store. Jet uses a file cache to speed access to the database. Because there are multiple clients, it is possible that they can each have file caches that are out of sync with the database file itself. Jet 4.0 exposes the interface IIdle that can be used to flush this cache both of dirty writes (writes waiting in the cache to be flushed to disk) and stale reads (reads that have since been rendered incorrect by changes to that part of the database). Multiuser databases might need to use this in cases where multiple users are making changes to the database at the same time and expecting to see the other updates immediately.


Additional Schema Rowset Support

The version 4.0 provider supports additional schema rowsets. These include both OLE DB-defined and provider-specific schemas. These schemas are described in Appendix B: Schema Rowsets, in detail.


IISAM Support

The 3.5 OLE DB Provider for Microsoft Jet did not expose the ability to connect to Installable ISAMs. This prevented clients from using all of the functionality in the Jet engine. The version 4.0 provider supports all of the IISAMs that ship with Jet 4.0. Wherever possible, metadata will reflect the limitations and capabilities of each particular ISAM. For example, the list of supported storage types that are returned in the PROVIDER_TYPES schema rowset will be different for version 3.5 format .mdb files and version 4.0 format .mdb files, because the Jet 4.0 storage engine has added new types that won't be understood by previous versions of Jet.


Functional Differences

Differences between the Access ODBC Driver and the native OLE DB Provider for Microsoft Jet

Clients converting code from the Access ODBC Driver to the native OLE DB Provider for Microsoft Jet should be aware of the following issues:

  • The native Jet Provider has no notion of a catalog as defined in OLE DB. The DBPROP_DATASOURCE property defines the database to use, and the ODBC driver treats this database as the current catalog. The DBPROP_CURRENTCATALOG property does not provide additional information to users about the current data store. Also, the native Jet Provider does not support the ability to change the current data store. Clients wanting to change the data store should uninitialize the data source object, set the new data store name, and reinitialize the data source object.
  • The native Jet Provider does not parse the same SQL syntax as parsed by the Access ODBC Driver. Clients should use the DBPROP_SQLSUPPORT property and the IDBInfo interface to query clients for their SQL support capabilities.

Differences between versions 3.5 and 4.0 of the OLE DB Provider for Microsoft Jet

Important areas of functional differences between the 3.5 OLE DB Provider for Microsoft Jet and the 4.0 Provider include the following:

  • Data Type Support
  • Database Size/Page Size
  • SQL Support


Data Type Support

Textual columns in Jet are now UNICODE instead of ANSI/DBCS. Compression is used to keep text from taking twice as much space. All IISAMs that store text columns in DBCS have implicit conversions done when read through Jet 4.0. When running against the Jet 3.5 OLE DB provider, all API calls had implicit coercions done already and it was possible to read ANSI data as UNICODE through the regular coercion process. Now, in a similar fashion, applications that need compatibility can use the coercion service in the OLE DB layer to convert back to ANSI if necessary. Not all ANSI/UNICODE conversions are lossless—converting from one to the other and back again can yield different results.

New data types have been added to Microsoft Jet 4.0. Specifically, Jet now has a numeric type for arbitrary precision data and a large, non-BLOB binary type. These are named Decimal and BigBinary, respectively. Information about these types is published in the PROVIDER_TYPES schema. These types do not work on previous .mdb file formats. The following table maps Jet data types to OLE DB data types.

4.0 Jet type name | OLE DB DBTYPE | Maximum size (4.0) | 3.5 type name
Bit | DBTYPE_BOOL |  | Bit
BigBinary | DBTYPE_BYTES | 4000 bytes | n/a
Byte | DBTYPE_UI1 |  | Byte
Currency | DBTYPE_CY |  | Currency
DateTime | DBTYPE_DATE |  | DateTime
Decimal | DBTYPE_NUMERIC | 28 digits (base 10) | n/a
Double | DBTYPE_R8 |  | Double
GUID | DBTYPE_GUID |  | GUID
Long | DBTYPE_I4 |  | Long
LongBinary | DBTYPE_BYTES | 1073741823 bytes | LongBinary
LongText | DBTYPE_WSTR | 536870910 characters | LongText
Short | DBTYPE_I2 |  | Short
Single | DBTYPE_R4 |  | Single
VarBinary | DBTYPE_BYTES | 510 bytes | Binary
VarChar | DBTYPE_WSTR | 255 characters | Text

To provide a more consistent data type naming scheme for Microsoft providers, Jet has modified the Text data type slightly. While the underlying storage has remained the same, the implications of the name text as used in SQL commands have changed. While Text in Jet referred to a short type, text in SQL Server is a BLOB field (akin to the Jet LongText/Memo type). When using commands, Text with no size now implies a LongText, while Text with a size (text(255)) still means the shorter, non-BLOB text field. The SQL data type varchar retains the original meaning of Text in Jet, so the OLE DB provider for Jet now exposes Jet's short text field as varchar instead of Text, because this behavior cannot be described in a generic fashion through OLE DB mechanisms. Applications that assume they can pass the provider type name as Text will break because the definition of Text is now ambiguous. As such, it doesn't resolve to anything in the version 4.0 provider.

To better comply with the OLE DB spec, the version 4.0 provider has also changed the name of the Binary column. The semantic meaning of Binary is unchanged, however, and it is still recognized in the version 4.0 provider as the short binary type. This change should be transparent to existing clients. New clients should use VarBinary in their applications.


Database Size/Page Size

Jet 4.0 changed its page size from 2 KB to 4 KB. Databases now increase in size in 4-KB chunks in the new version 4.0 .mdb file format. This also allows the maximum size of a database to roughly double to 2 GB. The maximum size of a BLOB column has not increased, however. It is still limited to 1 GB.


SQL Support

Significant additions have been made to the Jet SQL parser to improve compatibility with SQL-92 and SQL Server. While Jet does not fully comply with any level of the SQL-92 specification, it now understands many of the more common elements. This includes question-mark parameter markers, GRANT/REVOKE, improved referential constraint support, CHECK clauses, SQL-instantiated transactions, and CREATE VIEW/CREATE PROCEDURE support.

All of these additions are reflected through the OLE DB provider, although they are not necessarily accessible through Microsoft Access or through DAO. Consumers should be aware of the behavioral differences that exist among the various ways to access Microsoft Jet databases.

The OLE DB provider for Jet also changed the wildcards used in SQL LIKE clauses. Wildcards are used to allow more sophisticated pattern matching in queries over text columns. For example, the following query finds all rows in a customer table where the last name of the customer begins with "Smi":

SELECT * FROM CustomerTable WHERE LastName LIKE 'Smi%';

The % character is a wildcard that indicates that 0 or more characters can follow. In Jet 3.5, the equivalent wildcard character was *. The other wildcard character matches a single character of any value. This value changed from ? in Jet 3.5 to an underscore (_) in Jet 4.0. This change matches the SQL-92 and ODBC definitions for these wildcards.

It is important to note, however, that the mechanism for escaping wildcards (so that the literal % and _ can be matched in strings) has not been changed in the 4.0 release. You need to surround the character with brackets—for example, [%]—to match these characters explicitly. The following query will look for customers with a last name that is exactly "Smi%":

SELECT * FROM CustomerTable WHERE LastName LIKE 'Smi[%]';


Implementation Details by Interface

This section describes details of the Jet Provider's OLE DB behavior by interface. If an interface is not listed, this provider supports the interface as it is described in the OLE DB Programmer's Reference.


IAccessor

This section describes Jet-specific information about the IAccessor interface.

Optimization

Microsoft Jet does not have an internal row cache; therefore, it is not capable of optimization as described in the OLE DB Programmer's Reference. All accessors are equally optimized. Do not try to optimize accessors in the OLE DB Provider for Microsoft Jet.


ICommandPrepare

The ICommandPrepare interface is fully described in the OLE DB Programmer's Reference. The Microsoft Jet implementation of the interface reflects support for provider-specific properties. To see a comprehensive list of supported DBPROP_ROWSET and DBPROP_JETOLEDB_ROWSET properties, see Appendix A: Properties.


ICommandProperties

This section describes Jet-specific implementation of the ICommandProperties interfaces. This includes ICommandProperties::GetProperties and ICommandProperties::SetProperties.

ICommandProperties::GetProperties

Returns the list of properties in the Rowset property group that are currently requested for the rowset. For information about 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: Properties.

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: Properties.


ICommandWithParameters

ICommandWithParameters::SetParameterInfo

Specifies the native data type of each parameter. For information about how this interface is implemented, see the OLE DB Programmer's Reference.

If Jet can determine the base type for a parameter, it will coerce the parameter into that type. If Jet does not know the type of the parameter (indicated by a ? with no parameter clause), it makes a best guess. ICommandWithParameters::SetParameterInfo determines the type to be used. If not set, the parameter binding type is used instead.

For example:

parameters p1 long;

The first condition applies to this statement:

SELECT * FROM Tablename WHERE col1=p1;

The second condition applies to this statement:

SELECT * FROM Tablename WHERE col1=?;

The ? parameter binding is supported in the version 4.0 provider.

Long value parameter binding is supported in this provider, either through structured storage interfaces passed to ICommand::Execute or via direct memory bindings.


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 stores.

It is important to distinguish between the data source object and the data store. The data source object is the object used by the consumer code. The data store is the actual source of the data, such as a server database.

IDBDataSourceAdmin::CreateDataSource

Creates a new data store and data source object and initializes the data source object to the data store. For information about how this interface is implemented, see the OLE DB Programmer's Reference.

No data store creation properties are supported. For a complete description of the supported DBPROPSET_DBINIT and DBPROPSET_SESSION properties, see Appendix A: Properties.

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, you need to close the database and open it again using either the Initialize or CreateSession methods with the desired DBPROP_INIT_MODE setting.

IDBDataSourceAdmin::ModifyDataSource

This interface is used to change the database password.

To change the database password, a client should call ModifyDataSource with the DBPROP_JETOLEDB_DATABASEPASSWORD property set to the old password and DBPROP_JETOLEDB_NEWDATABASEPASSWORD set to the new password.


IDBInfo

IDBInfo returns information about the keywords and literals a provider supports.

IDBInfo::GetLiteralInfo

Returns information about literals used in text commands and in ITableDefinition. For information about how this interface is implemented, see the OLE DB Programmer's Reference.

Note Use the grave accent mark (`) to quote literal. Use brackets ( [ ] ) only to escape characters in a LIKE clause.


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.


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 store by using IOpenRowset::OpenRowset, which generates a rowset of all rows in the table or index.

IOpenRowset::OpenRowset

Opens and returns a rowset that includes all rows from a single base table or index.

The OLE DB Provider for Microsoft Jet allows you to open views by using this routine in addition to simple queries, where simple queries are nonparameterized, row-returning queries.


IRowset

IRowset is the base rowset interface. It provides methods for fetching rows sequentially, getting the data from those rows, and managing rows.

Cursor and Cursor Promotion

Cursor types can be promoted or demoted by the OLE DB Provider for Microsoft Jet. This cursor promotion or demotion is based on guessing the type of cursor requested by the user, with the cursor type being determined by the properties that are passed to the driver. Cursor types can be promoted without being requested by the user, because the driver itself will try to promote the cursor type if necessary. If the consumer asks for properties that don't match up with a particular cursor type, the promoted cursor that is provided to the consumer will be at the next cursor level above the minimum match. A consumer that calls ICommand::Execute 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. 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 a transaction is aborted.

Data Type Coercion

This OLE DB provider supports all the data type coercions that exist in the OLE DB Coercion Library. For better performance, bind to the native data types.

By Reference Data Binding

This OLE DB provider does not support "by reference" data binding, only binding by value.

Delayed Update Mode

In delayed update mode, this provider 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 interfaces: ISequentialStream and ILockBytes. ISequentialStream can be used for long text and long binary (memo and COM object) data types and must read the whole BLOB. ILockbytes works only on the Red ISAM 3.x and 4.x. ISequentialStream works on any BLOB data that Jet reads. You do not have to read the whole BLOB with ILockBytes; instead, it provides random access for the user.

There are restrictions on rowsets when using ILockBytes and ISequentialStream. If you have a structured storage interface open on a rowset, it might prevent you from editing a row in delayed update mode. You must release your pointers to structured interfaces to change other rows. Structured storage pointers can become invalid if you abort transactions, resulting in zombied rowsets.

IRowset::GetNextRows

Fetches rows sequentially, remembering the previous position. For information about 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 GetNextRows(NULL, 0, 1…) to traverse through the rowset in your default behavior instead of using GetRowsAtBookmark.

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 about 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 reexecution 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::DeleteRows

Deletes rows. For information about 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 pending changes include InsertRow, SetData, and opening a structured 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.


ISQLErrorInfo

ISQLErrorInfo is used to return the SQLSTATE and native error code.

ISQLErrorInfo::GetSQLInfo

Returns the SQLSTATE and native error code associated with an error.


ITableDefinition

The ITableDefinition interface exposes simple methods to create, drop, and alter tables on the data store.

Note This provider must be able to open a table exclusively. If any other rowsets on this table are open, this routine will fail.


ITransactionLocal::ITransaction

ITransactionLocal is an optional interface on sessions. It is used to start, commit, and abort transactions on the session.

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.

Jet supports only single-phase commit.


IAlterTable

The IAlterTable interface exposes methods to modify columns and tables.

Using this interface, the user can change the table's name as well as alter some table-level properties. See the Table Properties section of Appendix A for more information about these properties.


ITableCreation

ITableCreation provides information about a column or table.


Provider-Specific Interfaces

This section describes the two provider-specific interfaces of the OLE DB Provider for Microsoft Jet:

  • IJetCompact
  • IIdle


IJetCompact

The IJetCompact interface exposes maintenance operations on the OLE DB Provider for Microsoft Jet. Compact is the only supported method on this interface. It does both database compaction and repair.

This interface is defined under the DataSource cotype in Appendix D of the OLE DB Programmer's Reference, "Cotypes, Structures, and Enumerated Types."

When to Implement

IJetCompact is implemented only in version 4.0 and later.

Method | Description
IJetCompact | Compacts/repairs a database, creating a new copy

IJetCompact::Compact

Compacts or repairs a database, creating a new database. The old database is retained unchanged.

Syntax

HRESULT IJetCompact::Compact(
   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 nothing.
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, the value used is provider-specific. If cPropertySets is zero, this parameter is ignored.

Return Code

S_OK
The method succeeded.
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_OBJECTOPEN
The database cannot be opened exclusively.
DB_SEC_E_PERMISSIONDENIED
User does not have sufficient rights to compact the database.
DB_E_DUPLICATEDATASOURCE
A destination database with the same name already exists.
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. Please refer to IDBProperties::SetProperties for the complete description of this return code.
E_FAIL
An error occurred during compaction.

Comments

Compact is called to copy a database and remove extraneous space. The original database is left unchanged. This routine also will repair corruption in the database if possible. This routine will work only for Red ISAM databases—other IISAMs are not supported. This routine will not work if the database is already opened with permissions to write to the database.

Before calling Compact, the user should ensure that adequate space exists for the newly created database and that the data source object has been initialized.

The initialization properties from the data source object are used as the source for this method. Furthermore, the current values of these properties are used as a template for this routine—the caller needs to set only those properties that are important to distinguish the new database. Usually this means that only DBPROP_INIT_DATASOURCE needs to be passed to this routine to specify the new name for the compacted database. Unneeded properties are ignored by this method.

Several OLE DB Provider for Microsoft Jet–specific properties affect how this method operates:

  • DBPROP_JETOLEDB_COMPACT_DONTCOPYLOCALE (Boolean)—Use the database sort order, overriding any per-column locale settings.
  • DBPROP_JETOLEDB_COMPACT_NORELATIONSHIPS (Boolean)—Do not copy relationships to the new database.
  • DBPROP_JETOLEDB_COMPACT_NOREPAIRREPLICAS (Boolean)—Do not try to find other replicas to repair damaged replicated databases.

These properties are ignored on all other methods that take data source object properties. Therefore, you can either specify these on the data source object or pass values to this method. Values passed to this method override any previously set values.

Additionally, a database can be compacted to be encrypted or nonencrypted. This is controlled by the DBPROP_JETOLEDB_ENCRYPTDATABASE (Boolean) property.

By default, Compact will create a new database of the same version as the original database. If you want to compact to a newer file format, specify the DBPROP_JETOLEDB_ENGINETYPE (enumeration) property. Values for this property are defined in Msjetoledb.h. You cannot compact databases to a previous Jet format.


IIdle

This provider-specific interface exists under the Session cotype in the OLE DB Provider for Microsoft Jet. The Session cotype is defined in Appendix D of the OLE DB Programmer's Reference, "Cotypes, Structures, and Enumerated Types."

When to Implement

IIdle is implemented only for the OLE DB Provider for Microsoft Jet.

Method | Description
Idle | Performs idle time database operations.

IIdle::Idle

Performs database cleanup and optimizations while waiting for user input.

Syntax

HRESULT IIdle::Idle(
   ULONG   dwFlags);

Parameters

dwFlags
[in]
Option flags for idle time processing. This parameter should be passed as zero.

Return Code

S_OK
The method succeeded.
E_INVALIDARG
Invalid flags specified.
E_FAIL
An error occurred during idle processing.

Comments

When waiting for user input, Idle is called so that the engine can perform periodic cleanup and optimization tasks. Such tasks include background query execution, page timeout, memory cleanup, and so on.


Unsupported Interfaces

The following interfaces are not supported by the OLE DB Provider for Microsoft Jet:

  • IMultipleResults
  • IRowsetResynch
  • ITransactionObject
  • ITransactionOptions


Appendix A: Properties

The OLE DB specification allows data providers to implement provider-specific properties. These properties allow the programmer to have access to functionality that is outside the boundaries of the OLE DB specification. This section describes the properties that are exposed by the OLE DB Provider for Microsoft Jet.


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

Description: Description

DBPROP_COL_FIXEDLENGTH Type: VT_BOOL

Typical R/W: R/W

Description: Fixed Length

Specifies whether a column is fixed-length 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.


Provider-Specific Properties in DBPROPSET_JETOLEDB_COLUMN

The DBPROPSET_JETOLEDB_COLUMN property set contains the following properties.

Property ID Description
DBPROP_JETOLEDB_COL_ALLOWZEROLENGTH Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Allow Zero Length

Determines whether zero-length strings can be inserted into this field. Ignored for data types that are not strings. This is similar to the OLE DB standard property DBPROP_COL_NULLABLE but distinct. Zero-length strings are not NULLs in Jet.
DBPROP_JETOLEDB_COL_AUTOGENERATE Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:AutoGenerate

Whether a GUID should be automatically generated for the column. This property is ignored unless the underlying storage type is GUID.
DBPROP_JETOLEDB_COL_COMPRESSED Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Compressed UNICODE Strings

Determines whether Jet should compress UNICODE strings on the disk. This applies only to the version 4.0 .mdb file format and is ignored when running against all other storage formats.

  • VARIANT_TRUE—Compress UNICODE strings.
  • VARIANT_FALSE—Do not compress UNICODE strings.
DBPROP_JETOLEDB_COL_HYPERLINK Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Hyperlink

Whether this column is a hyperlink. This property is ignored unless the underlying storage type is LongText.
DBPROP_JETOLEDB_COL_IISAMNOTLAST Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:IISAM Not Last Column

For Installable ISAMs, this property informs the IISAM that more columns will be added to the table after this one. If you are using ITableDefinition::AddColumn or ITableDefinition::CreateTable, it is required that you set this property for every column except the last column to be added to the table.

This does not apply to the native Jet data store or any IISAM that reads previous formats of the native Jet data store.
DBPROP_JETOLEDB_COL_ONELVPERPAGE Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:One BLOB per Page

Whether BLOBs can share database pages.

  • VARIANT_TRUE—BLOBs can be stored only on their own pages.
  • VARIANT_FALSE—BLOBs can share database pages to conserve space.
DBPROP_JETOLEDB_COL_VALIDATIONRULE Type: VT_BSTR

Typical R/W: R/W

Description: Jet OLEDB:Column Validation Rule

Expression to be evaluated on a column to validate its value before allowing it to be set. This operates in a fashion similar to SQL-92 CHECK clauses.
DBPROP_JETOLEDB_COL_VALIDATIONTEXT Type: VT_BSTR

Typical R/W: R/W

Description: Jet OLEDB:Column Validation Text

Error string to display when the validation rule specified in DBPROP_JETOLEDB_COL_VALIDATIONRULE is not met.


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 the OLE DB Provider for Microsoft Jet and constitute a set of static information about the provider and data store.

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. Jet can support a maximum of 128 sessions.
DBPROP_ASYNCTXNABORT Type: VT_BOOL

Typical R/W: R only

Description: Asynchronous Abort

Specifies whether asynchronous abortion of transactions is allowed.
DBPROP_ASYNCTXNCOMMIT Type: VT_BOOL

Typical R/W: R only

Description: Asynchronous Commit

Specifies whether asynchronous commitment of transactions is allowed.
DBPROP_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 both to row and to 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 store uses for a catalog; for example, "catalog", "database", or "directory". This is used to build user interfaces.
DBPROP_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 Concatenation Behavior

Specifies how the data store handles concatenation of null-valued and non-null-valued character data type columns, that is:

  • DBPROPVAL_CB_NON_NULL, which indicates that the result is the concatenation of the non-null-valued column or columns.
DBPROP_DATASOURCENAME Type: VT_BSTR

Typical R/W: R only

Description: Data Source Name

The name of the data store. 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 store is read-only.
  • VARIANT_FALSE, which indicates that the data store is updatable.
DBPROP_DBMSNAME Type: VT_BSTR

Typical R/W: R only

Description: DBMS Name

Specifies the name of the product accessed by the provider. For this provider, the string is "MS Jet".
DBPROP_DBMSVER Type: VT_BSTR

Typical R/W: R only

Description: DBMS Version

Specifies the version of the product accessed by the provider. For this provider, the string is "4.00.0000".
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. For this provider, the value is DBPROPVAL_RT_FREETHREAD.
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.
  • For this provider, the value is DBPROPVAL_GB_CONTAINS_SELECT, which 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.
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. For this provider, the value is:

  • 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.

  • For this provider, the value is DBPROPVAL_IC_MIXED, which indicates that identifiers in SQL are case-insensitive and are stored in mixed case in the 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. For this provider, the value is 255.
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

  • For this provider, the value is always 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. For this provider, the value is always zero.
DBPROP_MULTIPLEPARAMSETS Type: VT_BOOL

Typical R/W: R only

Description: Multiple Parameter Sets

  • For this provider, the value is always VARIANT_TRUE, which indicates that the provider supports multiple parameter sets but not multiple result sets.
DBPROP_MULTIPLERESULTS Type: VT_I4

Typical R/W: R only

Description: Multiple Results

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

  • For this provider, the value is always 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. This applies to provider-owned storage objects retrieved from GetData.
DBPROP_MULTITABLEUPDATE Type: VT_BOOL

Typical R/W: R only

Description: Multiple Table Update

  • For this provider, the value is always VARIANT_TRUE, which indicates that the provider can update rowsets derived from multiple tables.
DBPROP_NULLCOLLATION Type: VT_I4

Typical R/W: R only

Description: NULL Collation Order

  • For this provider, the value is always DBPROPVAL_NC_LOW, which indicates that null values are sorted at the low end of the list.
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 COM objects stored in columns.

  • For this provider, the value is always 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.
DBPROP_ORDERBYCOLUMNSINSELECT Type: VT_I4

Typical R/W: R only

Description: Order By Columns in Select List

  • For this provider, the value is always 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

  • For this provider, the value is always DBPROPVAL_OA_NOTSUPPORTED, which indicates that output parameters are not supported.
DBPROP_PERSISTENTIDTYPE 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 the most permanent under schema changes and physical data reorganizations.

For this provider, the value is always DBPROPVAL_PT_NAME.
DBPROP_PREPAREABORTBEHAVIOR Type: VT_I4

Typical R/W: R only

Description: Prepare Abort Behavior

  • Specifies how aborting a transaction affects prepared commands. For this provider, the value is always DBPROPVAL_CB_DELETE, which indicates that aborting a transaction deletes prepared commands. The application must reprepare commands before executing them.
DBPROP_PROCEDURETERM Type: VT_BSTR

Typical R/W: R only

Description: Procedure Term

Specifies a character string with the data store vendor's name for a procedure; for example, "database procedure", "stored procedure", or "procedure". This is used to build user interfaces.
DBPROP_PROVIDERFILENAME Type: VT_BSTR

Typical R/W: R only

Description: Provider Name

Specifies the file name of the provider. (Prior to MDAC 2.5, this property was named DBPROP_PROVIDERNAME.)
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.

DBPROP_QUOTEDIDENTIFIERCASE Type: VT_I4

Typical R/W: R only

Description: Quoted Identifier Sensitivity

Specifies how quoted identifiers treat case, that is:

  • DBPROPVAL_IC_MIXED, which indicates that identifiers quoted in SQL are case-insensitive and are stored in mixed case in the system catalog.
DBPROP_ROWSETCONVERSIONCOMMAND Type: VT_BOOL

Typical R/W: R only

Description: Rowset Conversions On Command

  • For this provider, the value is always VARIANT_TRUE, which indicates that callers to IConvertType::CanConvert can inquire on a command about conversions supported on rowsets generated by the command.
DBPROP_SQLSUPPORT Type: VT_I4

Typical R/W: R only

Description: SQL Support

Specifies a bitmask that indicates the level of support for SQL.

  • For this provider, the value is always DBPROPVAL_SQL_SUBMINIMUM.
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_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.
  • For this provider, the value is always 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 or more of:

  • DBPROPVAL_TI_CHAOS
  • DBPROPVAL_TI_READUNCOMMITTED
  • DBPROPVAL_TI_BROWSE
  • DBPROPVAL_TI_CURSORSTABILITY
  • DBPROPVAL_TI_READCOMMITTED
DBPROP_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_ABORT_DC, which indicates that the transaction may either preserve or dispose of its isolation across a retaining abort.
DBPROP_TABLETERM Type: VT_BSTR

Typical R/W: R only

Description: Table Term

Specifies the name the data store 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.

For this provider, the value is always VARIANT_TRUE, which indicates that the index is automatically maintained.
DBPROP_INDEX_CLUSTERED Type: VT_BOOL

Typical R/W: R only

Description: Clustered

Specifies whether an index is clustered.

  • For this provider, the value is always 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 only

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. This value is always 100 in the OLE DB Provider for Microsoft Jet.
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.

For this provider, the value is based on the following:

  • One page = 4096 bytes
DBPROP_INDEX_NULLCOLLATION Type: VT_I4

Typical R/W: R only

Description: NULL Collation

  • Specifies how null values are collated in the index.
  • For this provider, the value is always 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.

  • For this provider, the following three values are valid:
    • DBPROPVAL_IN_ALLOWNULL— The index allows entries where the key columns are NULL and sorts according to the collation described by DBPROP_INDEX_NULLCOLLATION.
    • 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, 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, the provider ignores that entry and no error code is returned.
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.

  • For this provider, the value is always 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_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.

  • For this provider, the value is always DBPROPVAL_IT_BTREE, which indicates that the index is a B+- tree.
DBPROP_INDEX_UNIQUE Type: VT_BOOL

Typical R/W: R/W

Description: Unique

Specifies whether index keys must be unique. One of the following:

  • 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

  • For this provider, the value is always 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.
DBPROP_AUTH_ENCRYPT_PASSWORD Type: VT_BOOL

Typical R/W: R only

Description: Encrypt Password

  • For this provider, the value is always VARIANT_FALSE, which indicates that the password can be sent without encryption.
DBPROP_AUTH_MASK_PASSWORD Type: VT_BOOL

Typical R/W: R only

Description: Mask Password

Specifies values as either:

  • VARIANT_TRUE, which indicates that the consumer requires that the password be sent to the data source object 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 object or enumerator.

DBPROP_AUTH_PERSIST_ENCRYPTED Type: VT_BOOL

Typical R/W: R only

Description: Persist Encrypted

  • For this provider, the value is always 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

  • For this provider, the value is always 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 object or enumerator.
DBPROP_INIT_ASYNCH Type: VT_I4

Typical R/W: R/W

Description: Asynchronous Processing

Not supported by this provider.

DBPROP_INIT_DATASOURCE Type: VT_BSTR

Typical R/W: R/W

Description: Data Source

Specifies the name of the data store 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_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.
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_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_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 indicates 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.
  • DBPROMPT_NOPROMPT, which does not prompt the user.
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.

For this provider, this property is used to pass the Jet connect string for opening or creating databases.


Provider-Defined Properties in DBPROPSET_JETOLEDB_DBINIT

The following table lists and describes DBPROPSET_JETOLEDB_DBINIT properties that are specific to the OLE DB Provider for Microsoft Jet.

Property ID Description
DBPROP_JETOLEDB_COMPACT_DONTCOPYLOCALE Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Don't Copy Locale on Compact

Tells Jet not to copy locale information when compacting a database using IJetCompact::Compact. This property is used to make sure that the resultant database has homogeneous locale information across all tables and indexes.
DBPROP_JETOLEDB_COMPACT_NORELATIONSHIPS Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Compact Without Relationships

Determines whether IJetCompact::Compact should copy relationships into the newly compacted database:

  • VARIANT_TRUE—Compact will not copy relationships into the resultant database.
  • VARIANT_FALSE—Compact will copy relationships.

Other routines in the Jet Provider that accept properties from the Initialization property group ignore this property.
DBPROP_JETOLEDB_COMPACT_NOREPAIRREPLICAS Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Compact Without Replica Repair

Determines whether IJetCompact::Compact should attempt to repair damaged replicas by looking for other databases with which it can synchronize.
DBPROP_JETOLEDB_CREATESYSTEMDATABASE Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Create System Database

Determines whether IDBDataSourceAdmin::CreateDataSource generates a system database:

  • VARIANT_TRUE—Generate a system database.
  • VARIANT_FALSE—Generate a regular database.

In general, users will rarely want to generate system databases. This property is ignored when connecting to a preexisting database.
DBPROP_JETOLEDB_DATABASELOCKMODE Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Database Locking Mode

Scheme to use when locking the database.

Constants:

  • DBPROPVAL_DL_OLDMODE—Old mode used in previous releases of Jet's storage engine
  • DBPROPVAL_DL_ALCATRAZ—Alcatraz mode. Enables locking mode that allows for row-level locking. This does not preclude page locking.

A database can be open in only one mode at a time. The first user to open the database determines the locking mode used while the database is open.
DBPROP_JETOLEDB_DATABASEPASSWORD Type: VT_BSTR

Typical R/W: R/W

Description: Jet OLEDB:Database Password

Password used to open the database. This differs from the user password in that the database password is per file, while a user password is per user.
DBPROP_JETOLEDB_ENCRYPTDATABASE Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Encrypt Database

Determines whether IJetCompact::Compact should encrypt the resultant compacted database:

  • VARIANT_TRUE—Encrypt the resultant database.
  • VARIANT_FALSE—Do not encrypt the resultant database. If the database to be compressed is currently encrypted, this will remove that encryption.

This property defaults to the current value of the database. Not setting this property should retain the database's current setting.
DBPROP_JETOLEDB_ENGINE Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Engine Type

An enumeration defining the storage engine currently in use to access this database/data store.

Constants:

JET_ENGINETYPE_UNKNOWN = 0

JET_ENGINETYPE_JET10 = 1

JET_ENGINETYPE_JET11 = 2

JET_ENGINETYPE_JET2X = 3

JET_ENGINETYPE_JET3X = 4

JET_ENGINETYPE_JET4X = 5

JET_ENGINETYPE_DBASE3 = 10

JET_ENGINETYPE_DBASE4 = 11

JET_ENGINETYPE_DBASE5 = 12

JET_ENGINETYPE_EXCEL30 = 20

JET_ENGINETYPE_EXCEL40 = 21

JET_ENGINETYPE_EXCEL50 = 22

JET_ENGINETYPE_EXCEL80 = 23

JET_ENGINETYPE_EXCEL90 =24

JET_ENGINETYPE_EXCHANGE4 = 30

JET_ENGINETYPE_LOTUSWK1 = 40

JET_ENGINETYPE_LOTUSWK3 = 41

JET_ENGINETYPE_LOTUSWK4 = 42

JET_ENGINETYPE_PARADOX3X = 50

JET_ENGINETYPE_PARADOX4X = 51

JET_ENGINETYPE_PARADOX5X = 52

JET_ENGINETYPE_PARADOX7X = 53

JET_ENGINETYPE_TEXT1X = 60

JET_ENGINETYPE_HTML1X = 70

When calling IDBDataSourceAdmin::CreateDataSource, this can be used to specify the format for the new database.

Once a database has been opened, this property can be read to determine what file version is open.
DBPROP_JETOLEDB_GLOBALBULKNOTRANSACTIONS Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Global Bulk Transactions

Determines whether SQL bulk operations are transacted. This property determines the default for all operations in the current connection.

  • DBPROPVAL_BT_NOBULKTRANSACTIONS—Bulk operations are not allowed.
  • DBPROPVAL_BT_BULKTRANSACTION—Bulk operations are transacted.
DBPROP_JETOLEDB_GLOBALBULKPARTIAL Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Global Partial Bulk Ops

This property determines the behavior of Jet when SQL DML bulk operations fail. It can be overridden on a per-rowset basis by setting the DBPROP_JETOLEDB_BULKPARTIAL property.

Constants:

  • JET_BULKPARTIAL_NOPARTIAL—Fail the bulk operation on a single error.
  • JET_BULKPARTIAL_PARTIAL—Allow partial completion of the bulk operation. Could result in inconsistent changes since operations on some rows could succeed and others could fail.
  • JET_BULKPARTIAL_NOPARTIAL = 2— Fail the bulk operation on a single error.
DBRPOP_JETOLEDB_NEWDATABASEPASSWORD Type: VT_BSTR

Typical R/W: R/W

Description: Jet OLEDB:New Database Password

When calling IDBDataSourceAdmin::ModifyDataSource, this property allows the user to change a database password. The current password is set in DBPROP_JETOLEDB_DATABASEPASSWORD, while the new password is set in this property. If the call is successful, DBPROP_JETOLEDB_DATABASEPASSWORD will reflect the new database password.
DBPROP_JETOLEDB_REGPATH Type: VT_BSTR

Typical R/W: R/W

Description: Jet OLEDB:Registry Path

Path to the registry key to use for Jet information. This does not include the HKEY_LOCAL_MACHINE tag. This value can be changed to a secondary location to store registry values for a particular application—values that are not shared with other applications that use Jet on the computer.
DBPROP_JETOLEDB_SYSDBPATH Type: VT_BSTR

Typical R/W: R/W

Description: Jet OLEDB:System database

Location for the Jet system database to use for authenticating users. This overrides the value set in the registry or the corresponding systemdb registry key used when DBPROP_JETOLEDB_REGPATH is used. This can include the path to the file.


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

For this provider, the value is always 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.
  • For this provider, this can be used only on rowsets returned by ICommand::Execute.
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.

For this provider, the value is always VARIANT_TRUE, which indicates that, after creating an instance of a storage object, the 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.

  • In general, forward-only snapshots are the most restrictive in this regard. More full-featured cursors are generally less restrictive.
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.
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.

For this provider, the value is always 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.

For this provider, the value is always 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 the 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.

DBPROP_CACHEDEFERRED Type: VT_BOOL

Typical R/W: R only

Description: Cache Deferred Columns

  • For this provider, the value is always VARIANT_FALSE, which indicates that the provider does not cache the value of a deferred column and multiple calls to IRowset::GetData, because the column can return different values.
DBPROP_CANFETCHBACKWARDS Type: VT_BOOL

Typical R/W: R/W

Description: Fetch Backward

Specifies whether the rowset can fetch backward; 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 backward 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:

  • 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 be transmitted to the data store and all rows 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 Backward

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, because this method supports backward scrolling by definition.
DBPROP_CHANGEINSERTEDROWS Type: VT_BOOL

Typical R/W: R/W

Description: Change Inserted Rows

For this provider, the value is always VARIANT_TRUE, which indicates that the consumer can call IRowsetChange::DeleteRows or IRowsetChange::SetData for newly inserted rows.

A newly inserted row is defined as a row for which the insertion has been transmitted to the data store, as opposed to a pending insert row.

DBPROP_COLUMNRESTRICT Type: VT_BOOL

Typical R/W: R only

Description: Column Privileges

For this provider, the value is always VARIANT_TRUE, which indicates 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.
DBPROP_COMMITPRESERVE Type: VT_BOOL

Typical R/W: R only

Description: Preserve On Commit

For this provider, the value is always 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.
DBPROP_DEFERRED Type: VT_BOOL

Typical R/W: R only

Description: Defer Column

For this provider, the value is always VARIANT_TRUE, which indicates that the data in the column is not fetched until an accessor is used on the column.
DBPROP_DELAYSTORAGEOBJECTS Type: VT_BOOL

Typical R/W: R only

Description: Delay Storage Object Updates

For this provider, the value is always VARIANT_TRUE: Storage objects are also used in delayed update mode.

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

DBPROP_IRowsetIndex

 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

IRowsetIndex

IRowsetSetIndex

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

 Type: VT_BOOL

Typical R/W: R/W

Description: ILockBytes

ISequentialStream

If the value of this property is set to VARIANT_TRUE, the specified column is treated as a storage object that exposes the specified interface.
DBPROP_IMMOBILEROWS Type: VT_BOOL

Typical R/W: R only

Description: Immobile Rows

For this provider, the value is always VARIANT_FALSE, which indicates that, if the rowset is ordered, 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, 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

  • For this provider, the value is always VARIANT_FALSE, which indicates that bookmarks can be compared only with IRowsetLocate::Compare.
DBPROP_LITERALIDENTITY Type: VT_BOOL

Typical R/W: R only

Description: Literal Row Identity

  • For this provider, the value is always VARIANT_FALSE, which indicates that the consumer must call IRowsetIdentity::IsSameRow to determine whether two row handles point to the same row.
DBPROP_MAXOPENROWS Type: VT_I4

Typical R/W: R/W

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.

For this provider, the value is always 1.
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 0.

For this provider, the value is always 0.
DBPROP_MAYWRITECOLUMN Type: VT_BOOL

Typical R/W: R only

Description: Column Writable

Specifies whether a particular column is writable. 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, this property is VARIANT_TRUE for column A and VARIANT_FALSE for column B.

For this provider, the value is always V_FALSE.
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 only

Description: Notification Granularity

  • For this provider, the value is always DBPROPVAL_NT_MULTIPLEROWS, which indicates that for each phase and for methods that operate on multiple rows and generate multiphased notifications, 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.
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. This provider sets the following:

  • DBPROPVAL_NP_OKTODO
  • DBPROPVAL_NP_ABOUTTODO
  • DBPROPVAL_NP_FAILEDTODO
  • DBPROPVAL_NP_DIDEVENT
DBPROP_NOTIFYCOLUMNSET

DBPROP_NOTIFYROWDELETE

DBPROP_NOTIFYROWFIRSTCHANGE

DBPROP_NOTIFYROWINSERT

DBPROP_NOTIFYROWRESYNCH

DBPROP_NOTIFYROWSETFETCHPOSITIONCHANGE

DBPROP_NOTIFYROWSETRELEASE

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 canceled.

This provider returns all flags for the above properties:

  • DBPROPVAL_NP_OKTODO
  • DBPROPVAL_NP_ABOUTTODO
DBPROP_ORDEREDBOOKMARKS Type: VT_BOOL

Typical R/W: R only

Description: Bookmarks Ordered

  • For this provider, the value is always 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 only

Description: Others' Inserts Visible
DBPROP_OTHERUPDATEDELETE Type: VT_BOOL

Typical R/W: R/W

Description: Others' Changes Visible

Specifies values as either:

  • VARIANT_TRUE, which indicates that the rowset can see updates and deletes made by someone other than a consumer of the rowset. For example, 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 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:

  • 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 reexecuted.
DBPROP_OWNUPDATEDELETE Type: VT_BOOL

Typical R/W: R/W

Description: Own Changes Visible

Specifies values as either:

  • 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 reexecuted.
DBPROP_QUICKRESTART Type: VT_BOOL

Typical R/W: R/W

Description: Quick Restart

Specifies values as either:

  • VARIANT_TRUE, which indicates that IRowset::RestartPosition is relatively quick to execute. In particular, it does not reexecute the command that created the rowset.
  • VARIANT_FALSE, which indicates that RestartPosition is expensive to execute and requires reexecuting the command that created the rowset.
DBPROP_REENTRANTEVENTS Type: VT_BOOL

Typical R/W: R only

Description: Reentrant events

  • For this provider, the value is always VARIANT_FALSE, which indicates that the provider does not support such reentrancy. The provider returns DB_E_NOTREENTRANT on methods called during the notification.
DBPROP_REMOVEDELETED Type: VT_BOOL

Typical R/W: R/W

Description: Remove Deleted Rows

For this provider, the value is always VARIANT_TRUE.
DBPROP_REPORTMULTIPLECHANGES Type: VT_BOOL

Typical R/W: R only

Description: Report Multiple Changes

  • For this provider, the value is always VARIANT_TRUE.
DBPROP_RETURNPENDINGINSERTS Type: VT_BOOL

Typical R/W: R only

Description: Return Pending Inserts

  • For this provider, the value is always 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

  • For this provider, the value is always 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.

For this provider, 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 until the end of the rowset is reached.
DBPROP_ROWTHREADMODEL Type: VT_I4

Typical R/W: R only

Description: Row Threading Model

Specifies the threading model of the rowsets generated by the command.

  • For this provider, the value is always DBPROPVAL_RT_FREETHREAD.
DBPROP_STRONGIDENTITY Type: VT_BOOL

Typical R/W: R only

Description: Strong Row Identity

For this provider, the value is always 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.

DBPROP_TRANSACTEDOBJECT Type: VT_BOOL

Typical R/W: R only

Description: Objects Transacted

Specifies values as either:

  • VARIANT_TRUE, which indicates that any object created on the specified column is transacted. That is, data made visible to the data store through the object can be committed with ITransaction::Commit or aborted with ITransaction::Abort.

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.


Provider-Specific Properties in DBPROPSET_JETOLEDB_ROWSET

The DBPROPSET_JETOLEDB_ROWSET property set contains the following Rowset properties.

Property ID (additional properties supported in
DBPROPSET_JETOLEDB_ROWSET)
Description
DBPROP_JETOLEDB_BULKNOTRANSACTIONS Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Bulk Transactions

Determines whether SQL bulk operations are transacted. This property determines whether the current command execution is transacted.

  • DBPROPVAL_BT_DEFAULT—Defer to the value specified in DBPROP_JETOLEDB_GLOBALBULKTRANSACTIONS.
  • DBPROPVAL_BT_NOBULKTRANSACTIONS—Bulk operations are not transacted.
  • DBPROPVAL_BT_BULKTRANSACTIONS—Bulk operations are transacted.

Large bulk operations might fail when transacted due to resource limitations.

This property overrides the value set in DBPROP_JETOLEDB_GLOBALBUILDTRANSACTIONS.
DBPROP_JETOLEDB_BULKPARTIAL Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Partial Bulk Ops

This property determines the behavior of Jet when SQL DML operations fail.

  • DBPROPVAL_BP_DEFAULT—Defer to the value of DBPROP_JETOLEDB_GLOBALBULKPARTIAL.
  • DBPROPVAL_BP_PARTIAL—Allow partial completion of the bulk operation.
  • DBPROPVAL_BP_NOPARTIAL—Fail the bulk operation on a single error.
DBPROP_JETOLEDB_ENABLEFATCURSOR Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Enable Fat Cursors

This property indicates whether Jet should cache multiple rows when populating the cursor for remote row sources.
DBPROP_JETOLEDB_FATCURSORSMAXROWS Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Fat Cursor Cache Size

Number of rows that should be cached when using remote data store row caching. Used only if DBPROP_JETOLEDB_ENABLEFATCURSOR is VARIANT_TRUE.
DBPROP_JETOLEDB_LOCKGRANULARITY Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Locking Granularity

Determines whether a table is opened using Alcatraz row-level locking. This property is ignored unless DBPROP_JETOLEDB_DATABASELOCKMODE is set to DBPROPVAL_DL_ALCATRAZ.

  • DBPROPVAL_LG_PAGE—Use page locking for this result set for concurrency control.
  • DBPROVAL_LG_ALCATRAZ—Use row-level locking for concurrency control.
DBPROP_JETOLEDB_ODBCPASSTHROUGH Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:ODBC Pass-Through Statement

This property tells Jet that SQL text in a Command object should be passed to the back end unaltered.

  • VARIANT_TRUE—Pass SQL to remote connection.

VARIANT_FALSE—Interpret SQL statement locally.
DBPROP_JETOLEDB_ODBCPASSTHROUGHCONNECTSTRING Type: VT_BSTR

Typical R/W: R/W

Description: Jet OLEDB:Pass Through Query Connect String

Jet connect string to be used to connect to the remote data store. This property is used with DPROP_JETOLEDB_ODBCPASSTHROUGH and is ignored unless the value for that property is VARIANT_TRUE.
DBPROP_JETOLEDB_STOREDQUERY Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Stored Query

Whether the command text set in ICommandText::SetCommandText should be interpreted as a stored query instead of an SQL command.

  • VARIANT_TRUE—This is a stored query.
  • VARIANT_FALSE—Use normal parsing to evaluate the query.
DBPROP_JETOLEDB_VALIDATEONSET Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Validate Rules On Set

Whether Jet Validation Rules are evaluated when columns are set or when changes are being committed to the database.

  • VARIANT_TRUE—Validation Rules are evaluated when columns are set.
  • VARIANT_FALSE—Validation Rules are evaluated only when changes are committed. In Delayed Update mode, this occurs when IRowsetUpdate::Update is called.


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: Auto-commit Isolation Levels Specifies a bitmask that indicates 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.


Provider-Defined Properties in DBPROPSET_JETOLEDB_SESSION

The DBPROPSET_JETOLEDB_SESSION property set contains the following properties.

Property ID Description
DBPROP_JETOLEDB_CONNECTIONCONTROL Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Connection Control

Controls the ability to connect to the database.

  • DBPROPVAL_JCC_PASSIVESHUTDOWN—Disallow new connections to the database.
  • DBPROPVAL_JCC_NORMAL—Allow users to connect to the database.
DBPROP_JETOLEDB_EXCLUSIVEASYNCDELAY Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Exclusive Async Delay

The maximum time Jet can delay asynchronous writes to disk, in milliseconds, when the database is opened exclusively.

This property is ignored unless the DBPROP_JETOLEDB_FLUSHTRANSACTIONTIMEOUT property is 0.
DBPROP_JETOLEDB_FLUSHTRANSACTIONTIMEOUT Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Flush Transaction Timeout

The amount of time of inactivity before the asynchronous write cache is flushed to disk. This setting precludes the values for Shared Async Delay and Exclusive Async Delay.
DBPROP_JETOLEDB_IMPLICITCOMMITSYNC Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Implicit Commit Sync

Determines whether changes made in internal implicit transactions are written in synchronous or asynchronous mode.

  • VARIANT_TRUE—Changes are written synchronously.
  • VARIANT_FALSE—Changes are written asynchronously.
DBPROP_JETOLEDB_LOCKDELAY Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Lock Delay

This property determines how long to wait, in milliseconds, before attempting to acquire a lock after the previous attempt has failed.
DBPROP_JETOLEDB_LOCKRETRY Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Lock Retry

The number of times to repeat attempts to access a locked page.
DBPROP_JETOLEDB_MAXBUFFERSIZE Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Max Buffer Size

The largest amount of memory, in kilobytes, that Jet can use before it starts flushing changes to disk.
DBPROP_JETOLEDB_MAXLOCKSPERFILE Type:VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Max Locks Per File

The maximum number of locks that Jet can place on a database. The default for this property is 9500.
DBPROP_JETOLEDB_ODBCCOMMANDTIMEOUT Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:ODBC Command Timeout

Number of seconds before remote ODBC queries from Jet timeout.
DBPROP_JETOLEDB_PAGETIMEOUT Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Page Timeout

The amount of time, in milliseconds, that Jet will wait before checking to see if its cache is out of date with the database file.
DBPROP_JETOLEDB_RECYCLELONGVALUEPAGES Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Recycle Long-Valued Pages

Whether Jet should aggressively try to reclaim BLOB pages when they are freed.

  • VARIANT_TRUE—Long value pages will become available for reuse after Microsoft Jet determines that there is only one user in the database in shared mode and that new long value data has been added to the database.
  • VARIANT_FALSE—Discarded long value pages will continue to occupy space in the database and become available for reuse only after the last user closes the database.
DBPROP_JETOLEDB_RESETISAMSTATS Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Reset ISAM Stats

Determines whether the schema rowset DBSCHEMA_JETOLEDB_ISAMSTATS should reset its performance counters after returning performance information.

  • VARIANT_TRUE—Reset the ISAM performance counters to their initial values after generating the schema rowset.
  • VARIANT_FALSE—Do not reset the ISAM performance counters.
DBPROP_JETOLEDB_SHAREDASYNCDELAY Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Shared Async Delay

The maximum time Jet can delay asynchronous writes to disk, in milliseconds, when the database is opened in multiuser mode.

This property is ignored unless the DBPROP_JETOLEDB_FLUSHTRANSACTIONTIMEOUT property is 0.
DBPROP_JETOLEDB_USERCOMMITSYNC Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:User Commit Sync

Determines whether changes made in user explicit transactions are written in synchronous or asynchronous mode.

  • VARIANT_TRUE—Changes are written synchronously.
  • VARIANT_FALSE—Changes are written asynchronously.


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.
  • For this provider, the value is always VARIANT_FALSE, which indicates that the table is created permanently.


Provider-Defined Properties in DBPROPSET_JETOLEDB_TABLE

The DBPROPSET_JETOLEDB_TABLE property set contains the following Table properties.

Property ID Description
DBPROP_JETOLEDB_HIDDENINACCESS Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Table Hidden in Access

Whether the object should be displayed through the Microsoft Access User Interface.

  • VARIANT_TRUE—The object should be hidden in Microsoft Access.
  • VARIANT_FALSE—The object should not be hidden in Microsoft Access.

This property has no effect on whether the object is displayed through the OLE DB layer. ADO will also display hidden objects, even when running under Microsoft Access.
DBPROP_JETOLEDB_LINK Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Create Link

Determines whether ITableDefinition::CreateTable or ITableDefinitionWithConstraints::CreateTableWithConstraints should create a link to a remote data store instead of creating a table in the native store.

  • VARIANT_TRUE—Create a link instead of a regular table.
  • VARIANT_FALSE—Create a native table.
DBPROP_JETOLEDB_LINKCACHE_AUTHINFO Type: VT_BOOL

Typical R/W: W

Description: Jet OLEDB:Cache Link Name/Password

Whether authentication information for the link to the remote data store should be cached in the Jet database.

  • VARIANT_TRUE—Authentication information will be cached.
  • VARIANT_FALSE—Authentication information will not be cached.
DBPROP_JETOLEDB_LINKDATASOURCE Type: VT_BSTR

Typical R/W: R/W

Description: Jet OLEDB:Link Datasource

Remote data store to which to link. This property is used only when DBPROP_JETOLEDB_LINK is VARIANT_TRUE.
DBPROP_JETOLEDB_LINKEXCLUSIVE Type: VT_BOOL

Typical R/W: R/W

Description: Jet OLEDB:Exclusive Link

Determines whether a link should be created so that the remote data store is opened exclusively. This is used only when DBPROP_JETOLEDB_LINK is VARIANT_TRUE.

  • VARIANT_TRUE—The remote data store should be opened exclusively.
  • 60VARIANT_FALSE—The remote data store should be opened for multiuser access.
DBPROP_JETOLEDB_LINKPROVIDERSTRING Type: VT_BSTR

Typical R/W: R/W

Description: Jet OLEDB:Link Provider String

Jet connect string to be used when connecting to the remote data store. This is similar in function to the DBPROP_INIT_PROVIDERSTRING when used to connect to the Jet OLE DB provider, except that it applies only to a single table.
DBPROP_JETOLEDB_LINKREMOTETABLE Type: VT_BSTR

Typical R/W: R/W

Description: Jet OLEDB:Remote Table Name

Remote table to be used in the link. This might be different than the local name of the table/link. This property is used only when DBPROP_JETOLEDB_LINK is VARIANT_TRUE.
DBPROP_JETOLEDB_VALIDATIONRULE Type: VT_BSTR

Typical R/W: R/W

Description: Jet OLEDB:Table Validation Rule

Expression to be evaluated on a table to validate the values of a row before committing the row's changes. This operates in a fashion similar to SQL-92 CHECK clauses. This is very similar to DBPROP_JETOLEDB_COL_VALIDATIONRULE, but this rule can span multiple columns within the table, allowing for more complicated restrictions.
DBPROP_JETOLEDB_VALIDATIONTEXT Type: VT_BSTR

Typical R/W: R/W

Description: Jet OLEDB:Table Validation Text

Error string to display when the validation rule specified in DBPROP_JETOLEDB_VALIDATIONRULE is not met.


Appendix B: Schema Rowsets

The OLE DB specification allows providers to define their own schema rowsets to facilitate the exposure of provider-specific functionality. This appendix contains information about the schemas supported in the OLE DB Provider for Microsoft Jet.


Supported Schema Rowsets

Version 4.0 of the OLE DB Provider for Microsoft Jet added support for several new schema rowsets that were not available in version 3.5. All previously supported schema rowsets are still supported in version 4.0. Jet schema rowset support is outlined in the following table.

OLE DB schema name | ADO schema name | Description and GUID (if needed)
DBSCHEMA_CHECK_CONSTRAINTS | adSchemaCheckConstraints | The CHECK_CONSTRAINTS rowset identifies the check constraints defined in the catalog that are owned by a given user.
DBSCHEMA_COLUMNS | adSchemaColumns | The COLUMNS rowset identifies the columns of tables defined in the catalog that are accessible to a given user.
DBSCHEMA_CONSTRAINT_COLUMN_USAGE | adSchemaConstraintColumnUsage | 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.
DBSCHEMA_FOREIGN_KEYS | adSchemaForeignKeys | The FOREIGN_KEYS rowset identifies the foreign key columns defined in the catalog by a given user. This schema rowset is built upon several SQL-92 schema views as a convenience to the non-SQL programmer.
DBSCHEMA_INDEXES | adSchemaIndexes | The INDEXES rowset identifies the indexes defined in the catalog that are owned by a given user.
DBSCHEMA_JETOLEDB_ISAMSTATS | NONE | ISAM Performance Statistics GUID: {8703b612-5d43-11d1-bdbf-00c04fb92675}
DBSCHEMA_JETOLEDB_REPLCONFLICTTABLES | NONE | List of Conflict Tables in Replicas GUID: {e2082df2-54ac-11d1-bbdbb-00c04fb92675}
DBSCHEMA_JETOLEDB_REPLPARTIALFILTERLIST | NONE | List of Partial Filters in Replicas GUID: {e2082df0-54ac-11d1-bdbb-00c04fb92675}
DBSCHEMA_JETOLEDB_USERROSTER | NONE | Jet User Roster GUID: {947bb102-5d43-11d1-bdbf-00c04fb92675}
DBSCHEMA_KEY_COLUMN_USAGE | adSchemaKeyColumnUsage | The KEY_COLUMN_USAGE rowset identifies the columns defined in the catalog that are constrained as keys by a given user.
DBSCHEMA_PRIMARY_KEYS | adSchemaPrimaryKeys | 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.
DBSCHEMA_PROCEDURES | adSchemaProcedures | 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.
DBSCHEMA_PROVIDER_TYPES | adSchemaProviderTypes | The PROVIDER_TYPES rowset identifies the (base) data types supported by the data provider.
DBSCHEMA_REFERENTIAL_CONSTRAINTS | adSchemaReferentialConstraints | The REFERENTIAL_CONSTRAINTS rowset identifies the referential constraints defined in the catalog that are owned by a given user.
DBSCHEMA_STATISTICS | adSchemaStatistics | The STATISTICS rowset identifies the statistics defined in the catalog that are owned by a given user.
DBSCHEMA_TABLE_CONSTRAINTS | adSchemaTableConstraints | The TABLE_CONSTRAINTS rowset identifies the table constraints defined in the catalog that are owned by a given user.
DBSCHEMA_TABLES | adSchemaTables | The TABLES rowset identifies the tables defined in the catalog that are accessible to a given user.
DBSCHEMA_TABLES_INFO | adSchemaTablesInfo | The TABLES_INFO rowset identifies the tables defined in the catalog that are accessible to a given user.
DBSCHEMA_TRUSTEE | adSchemaTrustees | The TRUSTEE schema rowset identifies the trustees defined on the data store.
DBSCHEMA_VIEWS | adSchemaViews | The VIEWS rowset identifies the viewed tables defined in the catalog that are accessible to a given user.


Remoting Provider

OLE DB Remoting Provider

OLE DB Remoting Provider

The OLE DB Remoting Provider is an OLE DB service provider that allows client applications to retrieve and update rowsets from a remote server as if they were on the local computer. When integrated into a scenario that employs your data provider and other service components, it allows access to remote data while giving your application the same functionality as if the data were local.


Overview of the OLE DB Remoting Provider

Who can use this provider

ADO and OLE DB developers wanting to access OLE DB data providers running on separate machines or in separate processes on the same machine can use this provider.

What this provider allows you to do

The OLE DB Remoting Provider enables those applications written to consume data from OLE DB providers to work with remote OLE DB data providers. Used in conjunction with other service components, such as the Microsoft® Cursor Service for OLE DB and the Persistence Provider, the Remoting Provider allows you to retrieve rowsets from remote data stores and update those rowsets.

Remote data providers are defined as those running on the same computer as the consumer but in a different process, or they can be providers that are running on another computer. When running on another computer, the Remoting Provider communicates over the network using either DCOM or HTTP protocol.

The client application benefits from the full power of OLE DB, but all the remoting services are handled outside the application, increasing scalability. The client application need not change because the location of the data store changes.

Additional resources

References

OLE DB Programmer's Reference

ADO Programmer's Reference

Sites

http://www.microsoft.com/data/oledb

http://www.microsoft.com/data/ado

http://www.microsoft.com/data/ado/rds


New Features of the OLE DB Remoting Provider

Implementation method

When released with MDAC 2.0, the remoting functionality was implemented as a special tag used in the connection string for opening ADO recordsets. ADO treated this tag (Provider=MS Remote) in a special manner. You could not directly invoke or work with the Remoting Provider from within your code as you could with other OLE DB providers.

Now the functionality is implemented as an OLE DB service provider with its own properties and settings, making it possible to code directly to this provider from your applications. You call the Remoting Provider the same way you code to other OLE DB providers, such as the OLE DB Provider for Microsoft Indexing Service and the OLE DB Provider for SQL Server.

Updatablity

Previously, if some rows failed to update (due to update conflicts or other reasons), the whole recordset was not updated and it was not possible to determine which rows failed. Only the success or failure of the batch update operation was remoted to the client. A detailed status for each row was not available to the client.

Now, RowStatus array is returned by the Update or RefreshVisibleData methods so that the status of the failed rows is visible to the client.

See Also

IRowsetUpdate::Update | Updating Behavior


Using the OLE DB Remoting Provider: A Closer Look

The OLE DB Remoting Provider enables those applications written to consume data from OLE DB providers to work with remote OLE DB data providers.

The Remoting Provider allows you to retrieve rowsets from remote data stores and update those rowsets when used in conjunction with other service components—both on the client side and on the server side—such as the Cursor Service for OLE DB and the Persistence Provider.

The following list describes the required components on each platform when using the Remoting Provider:

  • The client Cursor Service provides a client-side manipulation of result sets both in two-tier and in three-tier scenarios. The Remoting Provider uses the Cursor Service to buffer remoted recordsets in its temporary table structures. This data is exposed as an OLE DB rowset implemented on top of a static, client-side cursor. The Cursor Service also maintains client updates in its cache. It can interact with the OLE DB Persistence Provider to marshal the rowset across process/machine boundaries. It also can submit updates to underlying OLE DB providers and obtain status information for the updates. It reconciles the status information about the individual rows submitted for update with the original rowset. The Cursor Service's update function uses an SQL query-based-update mechanism and works only against SQL-based OLE DB providers.
  • The OLE DB Persistence Provider can save a recordset into a binary stream. It can also create a recordset from a binary stream of data. The Cursor Service component uses the OLE DB Persistence Provider to marshal recordsets by value over process/machine boundaries. The Remoting Provider obtains an IStream interface from the underlying communication protocol (DCOM or HTTP), and the recordset is persisted into that stream to be received at the other end.
  • The OLE DB Remoting Provider as described in this guide.

In order for remote requests to be processed, two additional components are necessary on the server side:

  • The ADISAPI component is the Remote Data Service (RDS) server-side stub that receives HTTP requests generated by the client-side Remoting Provider, which includes marshaling and unmarshaling of rowsets into and from binary streams with the help of the OLE DB Persistence Provider. The ADISAPI component DLL is the hook into Microsoft Internet Information Services (IIS). It provides the necessary services for the server-side Remoting Provider to allow create and update functionality.
  • The DataFactory object works with the ADISAPI component in creating and updating rowsets. It provides read/write access to SQL-based OLE DB data stores but does not contain any validation or business rules logic.

The following illustration shows how these components interact.

For more information about the DataFactory object, see the topic "RDS Programming Model in Detail" in the ADO Programmer's Reference.

See Also

OLE DB Persistence Provider | The Cursor Service for OLE DB | Updating Behavior


DataFactory Version Compatibility

New functionality has been added to the server-side DataFactory object for version 2.1 and can be used only when the version 2.1 Remoting Provider is used on the client. However, the Remoting Provider will work with versions 1.5, 2.0, and 2.1 of the DataFactory, with the following exception.

When the Remoting Provider calls the DataFactory object, the DataFactory object attempts to use the new interface. If it cannot detect the new interface, the DataFactory object uses the older interface. However, if the Remoting Provider called for methods unavailable on the older interface, an error will result.

For more information about the DataFactory object and its supported methods, see "DataFactory Object (RDSServer)" in the ADO API Reference.


Using the Remoting Provider with Other Providers

The OLE DB Remoting Provider is an integral component in obtaining rowset data from another process or computer. It works only in conjunction with your data provider. To retrieve data across those boundaries, it uses two other OLE DB service providers: the OLE DB Persistence Provider and the Cursor Service for OLE DB.

The OLE DB Persistence Provider can save a recordset into a binary stream. It can also create a recordset from a binary stream of data. The Remoting Provider uses the OLE DB Persistence Provider to marshal recordsets by value over process/machine boundaries. RDS obtains an IStream interface from the underlying communication protocol (DCOM or HTTP), and the recordset is persisted into that stream to be received at the other end.

  • The client Cursor Service provides a client-side manipulation of result sets both in two-tier and in three-tier scenarios. The Cursor Service buffers remoted recordsets in its temporary table structures. This data is exposed as an OLE DB rowset implemented on top of a static, client-side cursor. The Cursor Service also maintains client updates in its cache. It can interact with the OLE DB Persistence Provider to marshal the rowset across process/machine boundaries. It also submits updates to underlying OLE DB providers and obtains status information for the updates, but only for SQL-based OLE DB data providers. The Cursor Service's update function uses an SQL query–based update mechanism. Through the synchronization functionality, it reconciles the status information about the individual rows submitted for update with the original rowset.

See Also

OLE DB Persistence Provider

Applies To: How the Synchronization Functionality Works with the Remoting Provider


How the Synchronization Functionality Works with the Remoting Provider

The two distinct components involved in buffered updating are the cursor component and the synchronization component. The cursor component is responsible for buffering the data and exposing the data as an OLE DB rowset. The synchronization functionality works within the Cursor Service component by synchronizing changes made through the rowset with the data store and by updating the rowset with values changed in the data store outside the rowset.

The goal of synchronization is to reflect changes to the original data store in the client cursor. To do this when the Remoting Provider is used, the provider returns a Cursor Service component rowset object when the consumer requests a new rowset. If changes are made to this rowset and the consumer calls IRowsetUpdate::Update, IRowsetResynch::ResynchRows, or IRowsetRefresh::RefreshVisibleData on a rowset, the Cursor Service component synchronizes the rowset with the one exposed by the Remoting Provider.

The provider then creates a wrapper rowset object, which adds calls to GetNextRows and GetPendingRows to the same Cursor Service component method calls. These two methods keep track of the order in which they return rows. The rows with pending changes are returned by GetPendingRows, whereas the rows without pending changes are returned by GetNextRows. Returning DBPENDINGSTATUS_NOCHANGE and then calling GetOriginalData for GetData hides the pending changes on these rows.

The Persistence Provider is used to pass the updated rows to the DataFactory on the server. Once the changed rows are on the server, the synchronization component creates another Cursor Service component on the server and loads it with all the rows. GetPendingRows and GetNextRows are used to fetch the rows from the Cursor Service component. Synchronization is performed when the component calls IRowsetUpdate::Update or IRowsetRefresh::RefreshVisibleData.

The RowStatus array is returned by the Update or RefreshVisibleData method. The rowset that the Remoting Provider receives will be missing successfully deleted rows but will be in the same order as the rows sent.

Using the RowStatus array, the Remoting Provider will finally make the necessary changes to the client Cursor Service rowset.

See Also

IRowsetRefresh | IRowsetResynch | IRowsetUpdate::Update | The Cursor Service for OLE DB


Invoking the OLE DB Remoting Provider

The OLE DB Remoting Provider enables those applications written to consume data from OLE DB providers to work with remote OLE DB data providers. Used in conjunction with other service components, such as the Cursor Service and the Persistence Provider, the Remoting Provider allows you to retrieve rowsets from remote data stores and update those rowsets.

Invoking the Remoting Provider is similar to invoking other OLE DB providers. The basic connection string is similar, as shown in this example:

Data Source=Pubs;User ID=eem;Password=pwd

To identify the remote location, server, and the data provider residing on the remote server, the following code is prefaced to the connection string:

Provider=MS Remote;Remote Server=http://webaddress;
Remote Provider=SQLOLEDB;Data Source=Pubs;User ID=eem;Password=pwd
Connection string keyword | Description
Provider | Value is "MS Remote".
Remote Server | Location of the remote server written as the full address.
Remote Provider | Identifies the data provider on the remote computer.
Data Source | Identifies the name of the remote data store.
User ID | Identifies the user.
Password | Identifies the password.

When called, the local copy of the Remoting Provider connects using the address given with the Remote Server keywords. The DataFactory object on the remote server then calls the identified Remoting Provider and passes the connection information to it.


Supported Interfaces of the OLE DB Remoting Provider

The OLE DB Remoting Provider supports data source, session, and command interfaces as listed in the following table. In addition, rowset interfaces are indirectly supported by the Cursor Service. Error object interfaces simply return a custom error.

For more information about the interfaces supported by the Cursor Service for OLE DB, see Appendix B: Supported Interfaces of the Cursor Service.

OLE DB interface | Short description
IAccessor | IAccessor provides methods for accessor management. The Remoting Provider uses the Cursor Service accessors.
IColumnsInfo | IColumnsInfo exposes information about columns of a rowset or about a prepared command. It provides a limited set of information in an array.
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.
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.
ICommandText | ICommandText 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.
ICommandWithParameters | This optional interface encapsulates parameters. 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.
IConvertType | IConvertType 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. Uses CLSID_OLEDB_CONVERSIONLIBRARY.
IDBCreateCommand | Consumers call IDBCreateCommand::CreateCommand on a session to obtain a new command.
IDBCreateSession | Consumers call IDBCreateSession::CreateSession on a data source object to obtain a new session.
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.
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. All read/write DBPROPSET_DBINIT properties that are not VT_EMPTY are sent to the server.
IErrorInfo | IErrorInfo is defined by Automation. When 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, GUID of the interface in which the error occurred, and the name and topic of the Help file that applies to the error.
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. This interface is exposed by a provider-specific error-lookup service that is mandatory for all providers that return OLE DB error objects.
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 the Remoting Provider, it returns only one record.
IGetDataSource | IGetDataSource is a mandatory interface on the session for obtaining an interface pointer to the data source object.
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 store by using IOpenRowset::OpenRowset, which generates a rowset of all rows in the table or index. If DFMODE is less than 21, it executes select * from <tableName>.
IPersist* Objects | See Chapter 7 of the OLE DB Programmer's Reference, "BLOBs and COM Objects."
ISessionProperties | ISessionProperties returns information about the properties a session supports and the current settings of those properties. It is a mandatory interface on sessions.
ISupportErrorInfo | ISupportErrorInfo is defined by Automation. When 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.

See Also

Automation Error Objects | Chapter 3: Commands | Data Type Conversion | IPersist* Objects | OLE DB Error Objects | Using Accessors


Supported Properties of the OLE DB Remoting Provider

The OLE DB Remoting Provider supports various properties from three property sets: TDataSource, TSession, and TCommand. Specific properties are listed in the following table, each with a default value and read/write status. In addition to the common OLE DB properties, this provider uses the DataFactory Mode property to determine the DataFactory version for compatibility and properties specific to the Cursor Service for OLE DB.

For additional information about the DataFactory object, see "DataFactory Object (RDSServer)" in the ADO API Reference. For additional information about the Cursor Service, see the "Cursor Service for OLE DB" documentation.

The CLSID and property sets of the OLE DB Remoting Provider are defined in Msremote.h within version 2.1 of the Microsoft® Data Access Components.

See Also

Chapter 3: Commands | Data Source Objects, Session Objects, and Transaction Objects | Properties (Alphabetical) | Property Sets | Property Sets and Property Groups | Session Objects


DataFactory Mode (DFMODE) Properties

To ensure compatibility with RDS version 1.5 and later, the DBPROP_MSREMOTE_CURRENT_DFMODE property determines the version of the DataFactory object instantiated. The appropriate DataFactory .dll file is shipped with each version of RDS. However, when using the object with different versions, a call to a DataFactory method when using the following interfaces can change DFMODE:

  • IOpenRowset::OpenRowset
  • ICommand::Execute
  • IColumnsInfo, any method

Invoking the correct or explicit version of the DataFactory can be accomplished by using the properties described in the following table.

Property | Description
DBPROP_MSREMOTE_DFMODE | Read/Write. User sets this property to the current version of the remote DataFactory. If an older version is detected, the Remoting Provider sets DBPROP_MSREMOTE_CURRENT_DFMODE to that version.
DBPROP_MSREMOTE_CURRENT_DFMODE | Defaults to 21 or the value of DBPROP_MSREMOTE_DFMODE if it is set to a valid value, or the returned version number of the remote DataFactory.

The property values for DBPROP_MSREMOTE_CURRENT_DFMODE are described in the following table.

Property value | Description
15 | Assumes DataFactory version 1.5.
20 | Assumes DataFactory version 2.0.
21 Assumes DataFactory version 2.1 and new DataFactory methods will be used.

If the value is 21 and a method-not-found error occurs, DBPROP_MSREMOTE_CURRENT_DFMODE is automatically changed to 20.

If 21 functionality is used and the mode is less than 21, an error will be returned stating that functionality is not available.
Any other | An error will be returned.

For additional information about DataFactory object compatibility, see the ADO documentation for the "Microsoft OLE DB Remoting Provider" in the ADO Programmer's Reference.


Supported OLE DB Properties of the Remoting Provider

Property Description
DBPROP_ADC_BATCHSIZE Property Set: DBPROPSET_ADC

Default value: 15

Typical R/W: R

Cursor Service–specific property that controls the number of rows that can be batched together when the rowset is being updated by the Cursor Service.
DBPROP_ADC_UPDATECRITERIA Property Set: DBPROPSET_ADC

Default value: 2

Typical R/W: R/W

Cursor Service–specific property that specifies the update criteria (primary keys, timestamps, modified columns, and so on) to be used for the OCC-style batch updating by the Cursor Service when updating the recordset.
DBPROP_ASYNCHFETCHSIZE Property Set: DBPROPSET_ADC

Default value: 15

Typical R/W: R/W

Cursor Service–specific property used by the consumers of remoted recordsets for asynchronous operations.
DBPROP_ASYNCHPREFETCHSIZE Property Set: DBPROPSET_ADC

Default value: 50

Typical R/W: R/W

Cursor Service–specific property used by the consumers of remoted recordsets for asynchronous operations.
DBPROP_ASYNCHTHREADPRIORITY Property Set: DBPROPSET_ADC

Default value: 3

Typical R/W: R/W

Cursor Service–specific property used by the consumers of remoted recordsets for asynchronous operations.
DBPROP_AUTH_CACHE_AUTHINFO Property Set: DBPROPSET_DBINIT

Default value: VARIANT_TRUE

Typical R/W: R

Standard OLE DB property. Specifies whether or not the data source object or enumerator is allowed to cache sensitive authentication information.
DBPROP_AUTH_ENCRYPT_PASSWORD Property Set: DBPROPSET_DBINIT

Default value: VARIANT_FALSE

Typical R/W: R

Standard OLE DB property. Specifies whether or not the consumer requires an encrypted password.
DBPROP_AUTH_INTEGRATED Property Set: DBPROPSET_DBINIT

Default value: NULL

Typical R/W: R

Standard OLE DB property. A string containing the name of the authentication server to identify the user.
DBPROP_AUTH_MASK_PASSWORD Property Set: DBPROPSET_DBINIT

Default value: VARIANT_FALSE

Typical R/W: R

Standard OLE DB property. Specifies whether or not the consumer requires a masking of the sent password.
DBPROP_AUTH_PASSWORD Property Set: DBPROPSET_DBINIT

Default value: VT_EMPTY

Typical R/W: R

Standard OLE DB property specifying the string containing the password to be used in connecting.
DBPROP_AUTH_PERSIST_ENCRYPTED Property Set: DBPROPSET_DBINIT

Default value: VARIANT_FALSE

Typical R/W: R

Standard OLE DB property. Specifies whether or not the consumer requires the data source object to persist encrypted authentication information.
DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO Property Set: DBPROPSET_DBINIT

Default value: VARIANT_TRUE

Typical R/W: R

Standard OLE DB property. Specifies whether or not the consumer should persist sensitive authentication information.
DBPROP_AUTH_USERID Property Set: DBPROPSET_DBINIT

Default value: VT_EMPTY

Typical R/W: R

Standard OLE DB property containing the user ID.
DBPROP_COMMANDTIMEOUT Property Set: DBPROPSET_ROWSET

Default value: n/a

Typical R/W: R/W

Standard OLE DB property that specifies the time after which the command should time out.
DBPROP_DSOTHREADMODEL Property Set: DBPROPSET_DATASOURCEINFO

Default value: DBPROPVAL_RT_FREETHREAD

Typical R/W: R

Standard OLE DB property. This bitmask specifies the data source object–supported threading models.
DBPROP_INIT_DATASOURCE Property Set: DBPROPSET_DBINIT

Default value: VT_EMPTY

Typical R/W: R

Standard OLE DB property with the name of the connection data store or enumerator.
DBPROP_INIT_HWND Property Set: DBPROPSET_DBINIT

Default value: 0

Typical R/W: R

Standard OLE DB property that specifies the window handle to be used.
DBPROP_INIT_IMPERSONATION_LEVEL Property Set: DBPROPSET_DBINIT

Default value: DB_IMP_LEVEL_DELEGATE

Typical R/W: R

Standard OLE DB property that holds the impersonation level.
DBPROP_INIT_LCID Property Set: DBPROPSET_DBINIT

Default value: VT_EMPTY

Typical R/W: R/W

Standard OLE DB property specifying the locale of preference for the consumer.

DBPROP_INIT_LOCATION Property Set: DBPROPSET_DBINIT

Default value: VT_EMPTY

Typical R/W: R/W

Standard OLE DB property that holds the location of the data source object or enumerator.

DBPROP_INIT_MODE Property Set: DBPROPSET_DBINIT

Default value: DB_MODE_READWRITE | DB_MODE_SHARE_DENY_NONE

Typical R/W: R/W

Standard OLE DB property. A bitmask specifying access permissions.
DBPROP_INIT_PROMPT Property Set: DBPROPSET_DBINIT

Default value: DBPROPMT_NOPROMPT

Typical R/W: R

Standard OLE DB property that specifies whether or not to prompt the user during initialization.
DBPROP_INIT_PROTECTION_LEVEL Property Set: DBPROPSET_DBINIT

Default value: DB_PROT_LEVEL_NONE

Typical R/W: R

Standard OLE DB property that specifies the level of network connection protection of sent data.
DBPROP_INIT_PROVIDERSTRING Property Set: DBPROPSET_DBINIT

Default value: VT_EMPTY

Typical R/W: R/W

Standard OLE DB property that specifies provider-specific, extended connection information.
DBPROP_INIT_TIMEOUT Property Set: DBPROPSET_DBINIT

Default value: VT_EMPTY

Typical R/W: R/W

Standard OLE DB property that specifies the number of seconds to wait for initialization to complete.
DBPROP_IRowsetChange Property Set: DBPROPSET_ROWSET

Default value: n/a

Typical R/W: R/W with exceptions

Standard OLE DB property that specifies whether or not the rowset supports this interface. Specifically, it controls whether the remoted recordset should be updatable.
DBPROP_IRowsetUpdate Property Set: DBPROPSET_ROWSET

Default value: n/a

Typical R/W: R/W with exceptions

Standard OLE DB property that specifies whether or not the rowset supports this interface. Specifically, it controls whether the remoted recordset should be batch-updatable.
DBPROP_MAXROWS Property Set: DBPROPSET_ROWSET

Default value: n/a

Typical R/W: R/W

Standard OLE DB property that specifies the maximum number of rows that can be returned in a rowset.

DBPROP_MSREMOTE_CURRENT_DFMODE Property Set: DBPROPSET_MSREMOTE_DATASOURCE

Type: VT_I4

Default value: 21

Typical R/W: R/W

Remoting Provider property that specifies which version of the DataFactory object has last been accessed.
DBPROP_MSREMOTE_DFMODE Property Set: DBPROPSET_MSREMOTE_DBINIT

Type: VT_BSTR

Default value: VT_EMPTY

Typical R/W: R/W

Remoting Provider property that, in order to use the appropriate methods, specifies which version of the DataFactory object is expected to be encountered. Example: DFMode=21,20
DBPROP_MSREMOTE_HANDLER Property Set: DBPROPSET_MSREMOTE_DBINIT

Type: VT_BSTR

Default value: VT_EMPTY

Typical R/W: R/W

Remoting Provider property that specifies the ADO Event Handler and its arguments. Example: Handler=,,MSDFMAP.Handler,arg1,arg2,…

For more information about the ADO Event Handler, see "ADO Event Handler Summary" in the ADO Programmer's Reference.
DBPROP_MSREMOTE_PROVIDER Property Set: DBPROPSET_MSREMOTE_DBINIT

Type: VT_BSTR

Default value: VT_EMPTY

Typical R/W: R/W

Remoting Provider property that specifies the name of the Remoting Provider to use.
DBPROP_MSREMOTE_SERVER Property Set: DBPROPSET_MSREMOTE_DBINIT

Type: VT_BSTR

Default value: VT_EMPTY

Typical R/W: R/W

Remoting Provider property that specifies the name or alias of the remote server.
DBPROP_MULTIPLEPARAMSETS Property Set: DBPROPSET_DATASOURCEINFO

Default value: VARIANT_FALSE

Typical R/W: R

Standard OLE DB property. Specifies whether or not the (data) provider supports multiple parameter sets.
DBPROP_MULTIPLERESULTS Property Set: DBPROPSET_DATASOURCEINFO

Default value: DBPROPVAL_MR_NOTSUPPORTED

Typical R/W: R

Standard OLE DB property. A bitmask specifying whether or not the (data) provider supports multiple results objects.
DBPROP_OUTPUTPARAMETERAVAILABILITY Property Set: DBPROPSET_DATASOURCEINFO

Default value: DBPROPVAL_OA_NOTSUPPORTED

Typical R/W: R

Standard OLE DB property that specifies the time at which output parameter values become available.
DBPROP_PROVIDERFRIENDLYNAME Property Set: DBPROPSET_DATASOURCEINFO

Default value: "MS Remote"

Typical R/W: R

Standard OLE DB property showing the provider's friendly name.
DBPROP_PROVIDERFILENAME Property Set: DBPROPSET_DATASOURCEINFO

Default value: "Msdarem.dll"

Typical R/W: R

Standard OLE DB property showing the provider's file name.
DBPROP_PROVIDEROLEDBVER Property Set: DBPROPSET_DATASOURCEINFO

Default value: "2.1"

Typical R/W: R

Standard OLE DB property showing the OLE DB version supported by the provider.
DBPROP_PROVIDERVER Property Set: DBPROPSET_DATASOURCEINFO

Default value: "2.1.xxxx" for the full version number

Typical R/W: R

Standard OLE DB property showing the version number of the provider.
DBPROP_SESS_AUTOCOMMITISOLEVELS Property Set: DBPROPSET_SESSION

Default value: DBPROPVAL_TI_CHAOS

Typical R/W: R

Standard OLE DB property. A bitmask specifying the transaction isolation level while in auto-commit mode.
DBPROP_UNIQUEROWS Property Set: DBPROPSET_ROWSET

Default value: n/a

Typical R/W: R/W

Standard OLE DB property specifying whether or not each row is uniquely identified by its column values.


Behaviors Specific to the OLE DB Remoting Provider

The remoted recordset does not exhibit exactly the same behavior as the original recordset. The consumer application on the client gets a recordset implemented by the client-side cursor engine. All the data and metadata associated with the original recordset is remoted, but only a subset of underlying rowset properties and interfaces is remoted. So if the original recordset supports a specific, nonstandard interface, the remoted recordset will not support it.

The RDS client-side recordset has the following characteristics and properties:

  • It is implemented in the client-side cursor engine with static cursors.
  • It is in disconnected mode (its ActiveConnection property is set to Nothing).
  • It has Update restrictions.
  • It supports asynchronous execution and fetching of recordsets.
  • It can support client-side sorting, filtering, and find operations.
  • It supports bookmarks. Bookmarks are not remoted across process/machine boundaries. Therefore, a bookmark that is valid on the original recordset will not be valid on the remoted one.
  • It can be bound directly to DHTML and other data-bound controls. In Microsoft® Visual Basic®, Microsoft Visual C++®, and Microsoft Visual J++™ environments, the recordset can be directly bound to supported controls by setting it to the Datasource property. In HTML pages, RDS.DataControl can be used to bind the recordset to DHTML and other ActiveX® controls on the page. The recordset is linked to RDS.DataControl by setting it to the SourceRecordset property on the data control.
  • It can be saved (persisted) into local files on the client if not running in Microsoft Internet Explorer (or if security settings in Internet Explorer have been set to Low to allow such operations).

For more information about the client-side Cursor Service for OLE DB, see the ADO documentation of the "Microsoft Cursor Service for OLE DB" in the ADO Programmer's Reference.

For more information about the RDS.DataControl object, see "Data Control Object (RDS)" in the ADO Programmer's Reference.

For more information about the ActiveConnection property, see "ActiveConnection Property" in the ADO Programmer's Reference.


Remoted Rowset Properties

All the data and metadata associated with the original rowset is remoted, but only a subset of the underlying rowset properties and interfaces is remoted. The following Rowset properties are remoted:

  • DBPROP_IRowsetUpdate
  • DBPROP_IRowsetChange
  • DBPROP_ADC_BATCHSIZE
  • DBPROP_ADC_UPDATECRITERIA
  • DBPROP_COMMANDTIMEOUT
  • DBPROP_MAXROWS
  • DBPROP_ASYNCHFETCHSIZE
  • DBPROP_ASYNCHPREFETCHSIZE
  • DBPROP_ASYNCHTHREADPRIORITY

Descriptions of these properties can be found in the "Supported OLE DB Properties of the Remoting Provider" section of this guide. Additional information can be found in the OLE DB Programmer’s Reference.


Updating Behavior

The OLE DB Remoting Provider can support batch updating if the underlying OLE DB provider is SQL-based.

Updates, if supported, are based on optimistic concurrency control semantics.

When the rowset is returned to the client, its original, underlying, and current values are all saved. At first, each of these values is identical for the fetched row. When the row is changed on the client, only the current value is modified. When an update is attempted, the underlying value is compared with that in the data store. If the values are identical, the update proceeds. If they are different, which means the data store value has changed, the update fails and RowStatus is returned. Status is given only for those rows with update conflicts. At this point, you can either resynch the row to obtain the data store value or abort the update operation. Fetching the row again, using resynch, will change the underlying value but not the original or current values.

Comments

The RowStatus array is returned by the IRowsetUpdate::Update or IRowsetRefresh::RefreshVisibleData methods. When an update of the data store fails, RowStatus identifies the rows that failed in the same order as the rows sent.

See Also

Chapter 5: Updating Data in Rowsets | IRowsetRefresh::RefreshVisibleData | IRowsetUpdate::Update | Refreshing Row Data


Appendix A: Notes

Installation Notes

Msremote.dll is shipped with Microsoft® Data Access Components 2.0 and later. This provider must be installed on both the computer where the consumer exists and on every remote server to be accessed. In addition to Msremote.dll, every remote computer must have the OLE DB Persistence Provider (Msdaprt.dll) and the Cursor Service for OLE DB (Msdace.dll) installed.

Supported Platforms

The OLE DB Remoting Provider can function on any platform where Microsoft Data Access Components is installed.


Persistence Provider

OLE DB Persistence Provider

OLE DB Persistence Provider

The OLE DB Persistence Provider makes it possible to save rowset data obtained from a data store through a data provider, its accompanying metadata describing the columns of the rowset, and the rowset's state to a local file. Persisted data can be accessed even if the connection to the data store has been broken.


Overview of the OLE DB Persistence Provider

Who can use this provider

ADO and OLE DB developers wanting to save the results of a query to a local file for later viewing or modification can use the OLE DB Persistence Provider. The functionality of this provider can be exposed using ActiveX® Data Objects (ADO).

What this provider allows you to do

The OLE DB Persistence Provider allows you to save rowset data obtained from a data store, its accompanying metadata describing the columns of the rowset, and the rowset's state to a local file. Once saved, the data can be accessed later without reconnecting to the original data store.

Currently, the two formats allowed are Advanced Data Tablegram (ADTG) and an Extensible Markup Language (XML) representation of the underlying rowset. ADTG is the binary format used to remote rowsets by Remote Data Service (RDS), where the XML representation is Microsoft's XML schema for OLE DB rowsets based on the ASCII standard.

The OLE DB Persistence Provider is a service component that interacts with your OLE DB data provider and your data consumer. When coupled with other OLE DB service components, such as the Cursor Service or Remoting Provider, the result set gains functionality otherwise unavailable, allowing you to view or modify the disconnected rowset data as a recordset and update the original data store if desired.


Persistence Provider Implementation Scenarios

Saving Rowset Data

The Persistence Provider can be used to save rowset data to a local file. After your application has opened a recordset, it calls your provider and saves the data. Saving the rowset creates an instance of the Persistence Provider so that the rowset data (plus the metadata and state information) can be written to the specified file. After the data is in this persisted state, the connection to the original data store is no longer needed.

Loading Persisted Rowset Data

When data from the original data store has been saved to a file on your computer, the Persistence Provider can be used to load that data into your application for viewing or modifying. For the load process, your application opens an unpopulated recordset and identifies the specific local file. Next, behind the scenes, the Persistence Provider is created to load the persisted data into the Cursor Service. The Cursor Service is an OLE DB service component that allows the application to manipulate the loaded rowset. After the rowset has been wrapped in a recordset, its state information and metadata are exposed.

Persistence Provider with Remoting

The Persistence Provider can be used in a remote configuration that allows you the same functionality as the Load and Save scenarios. When the Refresh method or the SubmitChanges method is invoked on the RDS.DataControl object, the Persistence Provider is created and called automatically behind the scenes. The object can be used to directly create ADO recordsets from remote OLE DB providers without writing any code. It implements methods and properties that can be manipulated directly at both design time and run time, through HTML or through scripting, to create and update disconnected ADO recordsets. RDS.DataControl always invokes the DataFactory component on the server.

Calling the Refresh method sends the requested query through the local Remoting Provider. This provider then streams the data (specifically, the request) over a DCOM or HTTP protocol to the server's Remoting Provider. The local Remoting Provider then waits for a response from the server.

The DataFactory component of the server, in essence, manages the rowset on the remote machine: It calls your provider with the requested query, loads the rowset into the server copy of the Cursor Service component (caching the rowset data), and calls the server's image of the Persistence Provider. The connection to the data store at this point is no longer necessary. The Persistence Provider then streams the data back over the network to the waiting Remoting Provider.

After the local copy of the Remoting Provider receives the response and rowset data from the server, the rowset can be loaded into the client Cursor Service and made available to the application as described in the section "Loading Persisted Rowset Data." Connection to the server can be dropped.

To submit changes made to a recordset by the application, the process is simply reversed. SubmitChanges is called to persist the rowset through the local copy of the Remoting Provider into a stream over the network. The server-side Persistence Provider receives the stream where the DataFactory passes the data as an argument to your provider in order to update the data store.


Additional Resources

References

OLE DB Programmer's Reference

ADO Programmer's Reference

XML Reference

Web sites

http://www.microsoft.com/data/oledb

http://www.microsoft.com/data/ado

See Also

IRowset | OLE DB Remoting Provider | The Cursor Service for OLE DB


Using the OLE DB Persistence Provider: A Closer Look

Rowset persistence is implemented as an OLE DB service provider (Msdaprst.dll) that can be used by consumers to save rowsets to or load data from a locally persisted file. The provider is classified as a service component because, used with other service components, it adds functionality to existing data providers, allowing a disconnected rowset to be viewed and modified. The provider is invoked from ADO but is built on top of OLE DB.

For more information about using the Persistence Provider within ADO, see the "Microsoft OLE DB Persistence Provider" documentation in the ADO Programmer's Reference.


Invoking the Persistence Provider

The OLE DB Persistence Provider is automatically invoked when the ADO Save method is called on the IADORecordset interface, with a pointer to the recordset and with the local file name specified. The following example in Microsoft® Visual C++® shows the SaveMyRowset method for rowset persistence. The example follows these steps:

  1. An empty recordset is constructed.
  2. The rowset is given to ADO, where the rowset is merged into the constructed recordset.
  3. The recordset's Save method (defined in the following code) is invoked, passing in as arguments the file name (bstrFileName) and the desired persisted file format (adPersist*), where * is either "XML" or "ADTG".

For more examples, see the "Microsoft OLE DB Persistence Provider" documentation in the ADO Programmer's Reference.

HRESULT SaveMyRowset(IRowset *pRowset, BSTR bstrFileName)
{
   // Get the recordset interface
   IADORecordset * pRs = NULL;
   IADORecordsetConstruction * pADORsCt = NULL;
   HRESULT hr;

// Get an ADO recordset, and ask for the recordset construction interface

   if (FAILED (hr = ::CoCreateInstance(
               CLSID_ADORecordset,
               NULL,
               CLSCTX_INPROC_SERVER,
               IID_IADORecordsetConstruction,
               (LPVOID *) &pADORsCt)))
      return hr;

// Give the rowset to ADO

   if (SUCCEEDED (hr= pADORsCt->put_Rowset(pRowset))
   {
      hr = pADORsCt->QueryInterface(IID_IADORecordset, (void **)&pRs);
   // Identify which format to use when persisting: XML or ADTG
      hr = pRs->Save(bstrFileName, adPersistADTG;
}
// Cleanup (or use the ATL CComPtr class):

pRs->Release();
pADORsCt->Release();

return hr;
}

Note The result of saving hierarchical rowsets is different because they are persisted as separate, flattened rowsets. For more information about persisting hierarchical rowsets, see the "Persisted Hierarchical Data" section of this guide.

Loading a persisted rowset into a recordset so that it can be viewed or modified by the application requires the persisted file name and a pointer to the IRowset interface. The following Visual C++ example shows the LoadMyFile method for loading the persisted data into a recordset. The example follows these steps:

  1. An empty recordset is constructed.
  2. The persisted file is opened (as identified by bstrFileName).
  3. The rowset is loaded into the recordset.

For more examples, see the "Microsoft OLE DB Persistence Provider" documentation in the ADO Programmer's Reference.

HRESULT LoadMyFile(BSTR bstrFileName, IRowset ** ppRs)
{
   // Get the recordset interface

   IADORecordsetConstruction * pRsCt = NULL;
   IADORecordset * pADORs = NULL;
   HRESULT hr;

   // Create the ADO Recordset

   if (SUCCEEDED (hr = ::CoCreateInstance(CLSID_ADORecordset,
                  NULL,
                  CLSCTX_INPROC_SERVER,
                  IID_IADORecordset,
                  (LPVOID *) &pADORs)))
{
   VARIANNT varEmpty;

   varEmpty.vt = VT_ERROR;
   varEmpty.scode = DISP_E_PARAMNOTFOUND;

   // Open the persisted file

   hr = pADORs->Open(bstrFileName, varEmpty, -1,-1,-1);
   hr = pADORs->QueryInterface(IID_IADORecordsetConstruction,
   (void **)&pRsCt);

   // Loads the persisted rowset into the Recordset

   hr = pRsCt->get_Rowset (ppRs);
}
   pADOrs->Release() ;
   pRsCt->Release() ;
   return hr;
}

Note The procedures for saving a rowset do not apply to hierarchical rowsets because they are persisted as separate, flattened rowsets. For more information about persisting hierarchical rowsets, see the "Persisted Hierarchical Data" section of this guide.

Programming Considerations for the C++ Programmer

For C++ programmers who want to benefit from the full ADO functionality of the Persistence Provider, use the following code sample. Call CoCreateInstance on the ADO recordset, and then call QueryInterface for the IADORecordsetConstruction interface. Next, load the underlying rowset with the ADO recordset and call the save method. ADO automatically invokes the client cursor engine and calls the Persistence Provider when saving the recordset. The following example shows this procedure:

HRESULT SaveMyRowset(IRowset *pRowset, BSTR bstrFileName)
{
   // Get the recordset interface
   IADORecordset * pRs = NULL;
   IADORecordsetConstruction * pADORsCt = NULL;
   HRESULT hr;

   // Get an ADO recordset, and ask for the recordset construction
   // interface
   if (FAILED (hr = ::CoCreateInstance(
            CLSID_ADORecordset,
            NULL,
            CLSCTX_INPROC,
            IID_IADORecordsetConstruction,
            (LPVOID *) &pADORsCt)))
      return hr;

   // Give the rowset to ADO and save to the file name while calling the    //   Persistence Provider with the desired data format
   if (SUCCEEDED (hr= pADORsCt->put_Rowset(pRowset))
   {
      hr = pADORsCt->QueryInterface(IID_IADORecordset, (void **)&pRs);
      hr = pRs->Save(bstrFileName, adPersistADTG);
      }

   // Cleanup: (or use ATL CComPtr class)
   pRs->Release();
   pADORsCt->Release();

   return hr;
}

See Also

IRowset | Persisted Hierarchical Data


Using the Persistence Provider with Other Providers

The OLE DB Persistence Provider is categorized as a service component. When your application opens a rowset object, the provider allows you to save that data to a local file apart from the data source object and the original data store. The provider must work in conjunction with other OLE DB providers. The most significant of these is your provider, which allows you to access your targeted data store. Other providers listed here can perform additional services.

Provider name | DLL | Description
Your Provider | *.dll | The data provider you implemented to retrieve the specified data
The Cursor Service for OLE DB | Msadce.dll | The service component provider that enhances cursor functionality
The OLE DB Remoting Provider | Msdarem.dll | The service provider that allows remote access to other OLE DB providers
The Data Shaping Service for OLE DB | Msdads.dll | The service provider that can shape data into hierarchical rowsets

See Also

Conceptual Programming Models in OLE DB | Microsoft OLE DB Providers Overview


Using the Persistence Provider with the Remoting Provider

The Persistence Provider can be used with a remote connection in conjunction with the Remoting Provider and your provider to load rowset data. This allows you to access data on any number of remote machines. The Remoting Provider is another OLE DB service component that interacts with your provider and your application. A copy of the Remoting Provider, your data provider, and the Microsoft Cursor Service for OLE DB must exist on each remote computer, and a copy of the Cursor Service, the Persistence Provider, and the Remoting Provider must exist locally.

The request (for refreshing or submitting changes) first goes through the local Remoting Provider. This provider then streams the data over a DCOM or HTTP protocol to the server's Remoting Provider, also called the DataFactory. The local Remoting Provider then waits for a response from the server.

The DataFactory, in essence, manages the rowset on the remote machine: It calls your provider with the request, loads the rowset into the server copy of the Cursor Service (caching the rowset data), and calls the server's image of the Persistence Provider. Depending on the action taken, the Persistence Provider streams the data or resulting message back over the network to the waiting Remoting Provider. This happens for each remote connection until the local copy of the Remoting Provider receives the response (message or rowset data) from the server or servers. Then the rowset can be loaded into the client cursor engine and made available to the application as described in the "Loading Persisted Rowset Data" section of this guide.

For more information, see the OLE DB Remoting Provider documentation.


Using the Persistence Provider with the Cursor Service

To view and modify the rowset that has been loaded into the recordset, the Cursor Service is instantiated automatically when the recordset is loaded. The Cursor Service allows forward and backward scrolling and fetching, bookmarks, inserts and deletes, and other functionality on the recordset.

For additional details about the functions of the Cursor Service, see Appendix C: Supported Properties of the Cursor Service in the Cursor Service for OLE DB documentation.

See Also

The Cursor Service for OLE DB


Using the Persistence Provider with the Data Shaping Service

The Data Shaping Service works on rowset data, allowing the application to form relationships between data elements (child and parent) or to modify or create aggregation schemes.

The following restrictions apply to the Persistence Provider when using it with the Data Shaping Service:

  • The Data Shaping Service cannot work on top of rowsets created from different providers. Thus, one cannot create a hierarchical rowset by appending a rowset obtained from the OLE DB Provider for SQL Server (by running an SQL command, for example) to another rowset obtained from the Persistence Provider.
  • Because the Persistence Provider does not support the command object, parameterized hierarchies cannot be built on rowsets created from it.

For more information about building hierarchies from unrelated rowsets, see the Data Shaping Service for OLE DB documentation.

See Also

Command Objects | Hierarchical Rowsets | Persisted Hierarchical Data | The Data Shaping Service for OLE DB


Behaviors Specific to the Persistence Provider

The OLE DB Persistence Provider exhibits unique behaviors in its treatment of data, metadata, connection information, the Cursor Service, and other providers.

For more information about | See
Persisted connection information | Persisted Connection Information
Persisted rowset data and metadata | Persisted Rowset Data and Information
Persisted hierarchical data | Persisted Hierarchical Data
Loading a Cursor Service rowset | Loading Persisted Rowset Data
Using the OLE DB Persistence Provider with the Data Shaping Service | Using the Persistence Provider with the Data Shaping Service


Saving Large Data

Some types of providers do not support the way the Persistence Provider internally uses a nonstreaming approach to reading large data (255 bytes or greater) rather than streaming it. Because persisting rowset data will fail in this case, first load the rowset into the Cursor Service and then persist that rowset.

See Also

The Cursor Service for OLE DB


Error Messages of the Persistence Provider

The Persistence Provider does not have an associated resource DLL and therefore produces no error messages. It will, however, pass error messages from other providers. Failures can be debugged using the following guidelines:

  • Verify that the path and file name are correct.
  • Verify that adequate disk space exists.

See Also

Automation Error Objects | Chapter 16: Errors | How a Consumer Retrieves an OLE DB Error Object | How a Provider Returns an OLE DB Error Object | ISupportErrorInfo | OLE DB Error Objects


Persisted Connection Information

Connection information is not saved with the rowset for two reasons: Saving login information raises security concerns, and the rowset defaults to a read-only status.

For more information about saving connection information, see "Recordset Persistence" in the ADO Programmer's Reference.


Persisted Rowset Data and Information

To persist the rowset, the OLE DB Persistence Provider combines the metadata and the data. The stored metadata varies when saving a rowset in XML or ADTG format. Although ADTG is proprietary, XML is not.

XML-formatted streams, when in a persisted state, do not contain any values for rowset properties.

ADTG-formatted rowsets store information about the persisted data, including column count, visible column count, and base table count. Base table and column information is persisted. The base table information includes the key columns defined, which the client cursor engine uses during updates. The column information includes the base column name and the friendly name (or alias), the type, length, scale, precision, and OLE DB column flags. If available, the base table name, the base catalog name, and the base schema name are also included.

Note Because the column name is identical to its base column name, it appears twice.

The OLE DB property set GUID is persisted once with a count of the properties stored for that set. Each property ID, the length of the data, and the data itself are persisted. Rowset properties are stored in this manner for version compatibility. Properties appearing in the stream that are unknown to the rowset reading the stream are ignored. Information about the state of each row is also stored. This "presence map" tracks the state and stores a bit for each nullable field in the row. The bit describes whether or not any data exists for that column. Fixed-length data is stored as-is, and variable-length data is stored preceded by the length.

A row can be in one of the four states described in the following table, depending on the action (or inaction) taken.

Row state | Description
Unchanged | The data is stored in its original form.
Deleted | For ADTG-formatted rowsets, the original data is stored, and a token follows it, marking the row as one that should be deleted. Not applicable to XML-formatted rowsets.
Inserted | A token marks the row as a new row, and the new data is stored as-is.
Changed | The original data is stored, and the modified columns are stored. An internal presence map determines which columns are modified.

See Also

Properties, Methods, and Events | Getting Information About Properties | Property Structures and Enumerated Types | Visibility of Changes


Persisted Rowset Data and Information Properties, Methods, and Events

Properties

DBPROP_COMMANDTIMEOUT

DBPROP_IRowsetChange

DBPROP_IRowsetUpdate

DBPROP_MAXROWS

Methods

Events

Applies To: Persisted Rowset Data and Information


Persisted Hierarchical Data

Hierarchical rowsets are handled differently by the OLE DB Persistence Provider. Foremost, the client must be able to support hierarchical rowsets.

In ADTG format, the Persistence Provider's hierarchy is stored rowset by rowset, not row by row. Therefore, the child rows are not visible. To pass it in correct hierarchical format to the consumer, the hierarchy should be read back into the provider that created it. XML-formatted files expose rows.

In the persisted file, for columns of type HCHAPTER, the Persistence Provider stores two additional pieces of information with the column metadata: relational information and calculated information. Data is retrieved from each rowset completely, without using the HCHAPTER value from the parent rows.

Parameterized hierarchies are not supported because the Persistence Provider does not support the command object.

See Also

Chapter Columns | Hierarchical Rowsets | Persisted Rowset Data and Information


Known Limitations of the OLE DB Persistence Provider

OLE DB Features Not Supported

A command object is used to execute provider-specific text commands. Because the persisted file has no functionality, the Persistence Provider does not support the command object. Furthermore, parameterized hierarchies cannot be built on rowsets created from this provider.

See Also

Chapter 3: Commands | Hierarchical Rowsets


Appendix A: Notes

Installation Notes

Msdaprt.dll is shipped with Microsoft® Data Access Components 2.0 and later. No installation procedures are necessary.

Supported Platforms

The OLE DB Persistence Provider can function on any platform that has Microsoft Data Access Components installed.


Supported Interfaces of the Persistence Provider

OLE DB Persistence Provider

Appendix B: Supported Interfaces of the Persistence Provider

The OLE DB Persistence Provider supports the following OLE DB interfaces.

Interface | Support notations
IAccessor | Object type: Rowset Provides methods for accessor management. For information about accessors, see "Accessors" in Chapter 6 of the OLE DB Progammer's Reference.
IChapteredRowset | Object type: Rowset Allows consumers to manage chapters.
IColumnsInfo | Object type: Rowset Exposes information about columns of a rowset or prepared command and provides a limited set of information in an array.
IColumnsRowset | Object type: Rowset Supplies complete information about columns in a rowset. Its methods can be called from a rowset or a command. Support is dependent on available stream schema information.
IConvertType | Object type: Rowset Mandatory on commands, rowsets, and index rowsets, giving information about the availability of type conversions on a command or rowset.
IDBCreateSession | Object type: Data source Creates a new session.
IDBInitialize | Object type: Data source Initializes or uninitializes a data source object or an enumerator.
IDBProperties | Object type: Data source Sets and gets values of properties on the data source object or enumerator and gets information about all properties supported by the provider.
IGetDataSource | Object type: Session Mandatory; obtains an interface pointer to the data source object.
IOpenRowset | Object type: Session Opens and returns a rowset that includes all rows from a single base table or index.
IPersist | Object type: Data source See IPersist* Objects for an overall discussion. See Getting and Setting IPersist* Objects for specific implementation notes. For additional considerations, see Lifetime of IPersist* Objects and IPersist* Objects, Rowset Update Semantics, and ITransaction. (All sources are in the OLE DB Programmer's Reference.)
IRowsetInfo | Object type: Rowset Provides information about a rowset and methods for retrieving objects associated with the rowset.
IRowset | Object type: Rowset Base rowset interface, providing methods for fetching rows sequentially, getting the data from those rows, and managing rows.
IRowsetUpdate | Object type: Rowset GetOriginalData and GetRowStatus are supported. All others return E_NOTIMPL. IRowsetUpdate::GetOriginalData gets the data most recently fetched from or transmitted to the data store; does not get values based on pending changes. IRowsetUpdate::GetRowStatus returns the status of rows.
ISessionProperties | Object type: Session Mandatory for sessions. Returns information about the properties a session supports and the current settings of those properties.
ISupportErrorInfo | Object type: Session and data source Defined by Automation and indicates whether a specific interface can return Automation error objects.


Appendix C: Supported Properties of the Persistence Provider

The Persistence Provider does not introduce any new properties; it uses existing OLE DB properties and Cursor Service properties. These are saved with the rowset and reloaded when the rowset is loaded by the Persistence Provider.

Properties used by the Persistence Provider can do the following:

  • Define whether the rowset is updatable.
  • Determine whether the rowset, which is built from the stream, should support these interfaces.
  • Set the timeout duration of a command.
  • Define the maximum number of rows that can be returned.
  • Set the number of rows that the Cursor Service should include with query-based updates.
  • Determine how the Cursor Service should perform updates.
  • Identify the asynchronous behavior that the consumer should use to fetch the rows from the stream.
Property | Description
DBPROP_ADC_AUTORECALC | Determines when the Data Shaping Service recalculates aggregate and calculated columns within a generated hierarchical rowset by using a shape command.
DBPROP_ADC_BATCHSIZE | Property Set: DBPROPSET_ADC Default value: 15 Typical R/W: R Cursor Service–specific property that controls the number of rows that can be batched together when the rowset is being updated by the Cursor Service.
DBPROP_ADC_CUSTOMRESYNCH | Cursor Service–specific property that allows you to define the command to be used when resynchronizing a row or rows in a multitable join result set.
DBPROP_ADC_RESHAPENAME | Specific to rowsets created with the Data Shaping Service. Cursor Service–specific property that allows local references in the SHAPE command for reshaping.
DBPROP_ADC_UNIQUECATALOG | The Unique Catalog property should be used for rowsets created from a join query over multiple base tables. Cursor Service–specific property.
DBPROP_ADC_UNIQUESCHEMA | The Unique Schema property should be used for rowsets created from a join query over multiple base tables. Cursor Service–specific property.
DBPROP_ADC_UNIQUETABLE | The Unique Table property should be used for rowsets created from a join query over multiple base tables. Cursor Service–specific property.
DBPROP_ADC_UPDATECRITERIA | Property Set: DBPROPSET_ADC Default value: Typical R/W: Cursor Service–specific property that specifies the update criteria (primary keys, timestamps, modified columns, and so on) to be used for the OCC-style batch updating by the Cursor Service when updating the recordset.
DBPROP_ADC_UPDATERESYNC | Defines the behavior of the BatchUpdate method, controlling whether the batch update operation is followed by an implicit resync operation.
DBPROP_ASYNCHTHREADPRIORITY | Cursor Service–specific property used by the consumers of remoted recordsets for asynchronous operations.
DBPROP_ASYNCHFETCHSIZE | Cursor Service–specific property used by the consumers of remoted recordsets for asynchronous operations.
DBPROP_ASYNCHPREFETCHSIZE | Cursor Service–specific property used by the consumers of remoted recordsets for asynchronous operations.
DBPROP_COMMANDTIMEOUT | Property Set: DBPROPSET_ROWSET Default value: n/a Typical R/W: R/W Standard OLE DB property. Specifies the length of time after which the command should be timed out.
DBPROP_IRowsetChange | Property Set: DBPROPSET_ROWSET Default value: n/a Typical R/W: R/W with exceptions Standard OLE DB property. Specifies whether or not the rowset supports this interface. Specifically, it controls whether the remoted recordset should be updatable.
DBPROP_IRowsetUpdate | Property Set: DBPROPSET_ROWSET Default value: n/a Typical R/W: R/W with exceptions Standard OLE DB property. Specifies whether or not the rowset supports this interface. Specifically, it controls whether the remoted recordset should be batch-updatable.
DBPROP_MAXROWS | Property Set: DBPROPSET_ROWSET Default value: n/a Typical R/W: R/W Standard OLE DB property. Specifies the maximum number of rows that can be returned in a rowset.

See Also

Properties, Methods, and Events | Getting Information About Properties | Getting Property Values | Property Structures and Enumerated Types | The Cursor Service for OLE DB


Data Shaping Service for OLE DB

The Data Shaping Service for OLE DB

The Data Shaping Service for OLE DB

The Data Shaping Service for OLE DB allows an application to create relationships that had not previously existed between keys, fields, or rowsets. It supports the construction of hierarchical rowset objects from your data provider.


Overview of the Data Shaping Service for OLE DB

Who can use this provider

OLE DB developers wanting to create rowset hierarchies can use the Data Shaping Service for OLE DB, also referred to as the Data Shaping Provider in this guide.

What this provider allows you to do

The Data Shaping Service for OLE DB allows an application to create relationships that had not previously existed between keys, fields, or rowsets. The provider supports the Shape language and any other command language supported by your data provider. The Shape language allows you to define rowset hierarchies built from rowset data obtained from a data provider. By connecting to the Data Shaping Service for OLE DB, you can execute various SHAPE commands to establish relationships between retrieved result sets and, if desired, group those new rowsets within a hierarchical structure.

You can also perform aggregations on newly formed hierarchical structures, summing at any level and even skipping levels if desired.

It is possible to add to existing hierarchies. The reshaping facility can be used to give an existing rowset a new parent rowset. The reshaping facility will not alter the existing rowsets or any hierarchical relationships that may exist between them.

Data can be kept current by using the ADO Resync and Requery methods or by defining the hierarchy as parameter-based.

The Data Shaping Provider uses the Cursor Service for OLE DB as its rowset implementation.

Additional Resources

The following technical articles and white papers are posted on the "Technical Materials" page of the Universal Data Access Web site at http://www.microsoft.com/data/techmat.htm or are included with the Microsoft® Data Access Components (MDAC) SDK. Articles are listed here in order of their relevance.

Their presence and location are subject to change with each release. Other articles can be found in the Microsoft Developer Network (MSDN™) Library. Detailed articles concerning known limitations or workarounds can be found in the MSDN Knowledge Base.

Documents | Date written/posted
OLE DB/ADO: Making Universal Data Access a Reality | Late 1998

References

OLE DB Programmer's Reference

ADO Programmer's Reference

Web sites

http://www.microsoft.com/data/oledb

http://www.microsoft.com/data/ado

See Also

Hierarchical Rowsets | The Cursor Service for OLE DB


New Features of the Data Shaping Service

New features of the Data Shaping Service included in version 2.1 of the Microsoft® Data Access Components include the following:

  • Hierarchy Reshaping—Reshaping a hierarchy reuses cursors created by the Data Shaping Service to create new hierarchies. These component cursors are simply tied together in whatever relationship you specify to create an entirely new relationship, much like reconfiguring a given set of building blocks. Hierarchy reshaping allows you to reuse data—an existing rowset—as a child rowset of a newly created rowset.

    For more information about hierarchy reshaping and examples using this feature, see Reshaping in this guide.

  • Grandchild Grouping—The Data Shaping Service for OLE DB provides a means to perform aggregate functions at each level or at specified levels. A new feature with the release of MDAC 2.1 is the ability to build a hierarchy structure that can aggregate specified grandchild rows, skip the parent rows, and roll the totals into a higher aggregation level. This can be beneficial, for instance, when you want to find the average of all child rowsets instead of the average of the parent rowsets.

    For more information about grouping grandchild rowsets and to view examples using this feature, see Grandchild Aggregations in this guide.

  • Parameterized Computed Child Rowsets—The Data Shaping Service for OLE DB supports Shape language syntax that allows you to insert one or more levels of computed rowsets between the parent and its parameterized child rowset. This allows you to build a hierarchical structure where the children are built from parameterized commands that are passed through their "inserted" parents but that do not affect those parents.

    For more information about using parameterized computed child rowsets and to view examples using this feature, see Parameterized Computed Child Rowsets in this guide.

See Also

Chapter 3: Commands | Group-Based Hierarchies | Using Parameters


Using the Data Shaping Service for OLE DB: A Closer Look

The Data Shaping Service for OLE DB is shipped with the Microsoft Data Access Components in the file Msadds.dll. The Data Shaping Provider allows you to build and manipulate Recordsets obtained from your data provider into hierarchies or to manipulate existing hierarchies.

Rowsets, Chapters, and Hierarchical Rowsets

A rowset object can identify a chapter—a group of rows having some sort of common characteristic within a rowset. When two rowsets have a hierarchical relationship, the parent rowset contains a chapter column. The value of this column for each parent is called a chapter handle and contains the 4-byte value that identifies the child rows. When the chapter handle for a specific parent rowset row is used with the child rowset, it scopes the child rowset to just those rows associated with the parent row, creating the hierarchy. When a new parent row is selected, ADO causes the children to be released as it releases the chapter handles, reads the new chapter handle value, and refers to the correct set of child rows.

Hierarchy Types of the Data Shaping Service

The Data Shaping Service for OLE DB allows you to build two distinct hierarchical types. A relation-based hierarchy can also be considered a filter-based hierarchy because it establishes a parent-child relationship between rowsets. The relationship can be explicitly stated in a command or use a parameterized data provider command to identify the child rows. The parameter values used to execute the data provider command are column values from the parent row.

Computed or group-based hierarchical rowsets are organized in logical groups and are members of the hierarchy structure. This allows you to establish a relationship of a fabricated parent row to a group, or a group to a group. Using the COMPUTE clause of the Shape language, the child rows in a rowset are partitioned and a parent rowset is created. The parent rowset contains one row for each partition in the child rowset.

The Shape Language

When connected to the Data Shaping Service for OLE DB, you can use the Shape language to construct or add to hierarchies. The syntax enables you to create numerous hierarchical relations between rowset objects.

You can make a one-to-one, one-to-many, many-to-one, or many-to-many relationship by appending a child rowset to a parent rowset, where each has at least one column value in common. Childless parent rows are also allowed. The following figure shows what relationships can be created.

The Shape command syntax is SHAPE <Rowset> APPEND <append list>. The APPEND clause creates four column types, as described in the following table.

Appended columns | Description
Chapter column | Adds a chapter column to the parent rowset. Defines the child rowset. Defines the relationship between the parent and the child rowset. May be a one-to-one, one-to-many, many-to-one, or many-to-many relationship. Orphans are possible.
Aggregate function | For a specific chapter, performs an aggregation function over the child rows. Available functions are MIN, MAX, AVG, SDEV (or STDEV), SUM, COUNT, and ANY.
CALC column | Produces a calculation by using an expression over the columns in the row. Uses the Microsoft® Jet Expression Service (Msjtes40.dll) to evaluate. Cannot reference other CALC columns.
New column | Adds fabricated columns to rowsets. Initial value is NULL.

You can build a parent rowset from a child rowset, but at least one chapter column in the parent rowset must reference the child rowset. Other parent columns are created by computing an aggregate operation over a column of the child rowset, calculating expressions on a row of a rowset, specifying a grouping column with the BY keyword, or appending a new, empty column. The parent rowset can have all the APPEND column types. The Shape command syntax is SHAPE <Child Rowset> COMPUTE <parent column list [BY grouping column list]>.

Shape Command References

This guide contains Data Shaping Service usage examples, but you can find a more thorough description of the command syntax in the ADO Programmer's Reference in the following topics:

  • "Shape Commands in General"
  • "Formal Shape Grammar"
  • "Shape Append Command"
  • "Shape Compute Command"

See Also

Chapter 4: Rowsets | Chapter Columns | Chapters | Chapters and Detail Groupings | Hierarchical Rowsets | Lifetime of Row Handles Within Chapters

Applies To: Building Parameterized Hierarchies | Group-Based Hierarchies | Relation-Based Hierarchical Rowsets


Relation-Based Hierarchical Rowsets

The OLE DB Data Shaping Service can be used to create a relationship between two rowsets retrieved from parent and child tables that have at least one field in common. You can have exact control in building this type of hierarchy by specifying the relationship in the SHAPE command. Performance and data freshness should be considered when constructing relation-based hierarchies.

Parent-Child Relationships

If the parent columns in the RELATE clause form a key of the parent rowset, the relationship is one-to-one or one-to-many. If the parent columns do not form a key, it is a many-to-many or many-to-one relationship.

The following table illustrates the relationship possiblities.

 | Key formed in parent rowset | No key formed in parent rowset
Key formed in child rowset | one-to-one | many-to-one
No key formed in child rowset | one-to-many | many-to-many

The RELATE clause forms the same type relationships when relating a parent field to a child field or when relating a parent field to a command parameter. The following sections explain the similarities and differences.

Building Hierarchies Using Two Unrelated Rowsets

Once the connection is made and the SHAPE command invoked, the data is retrieved in its entirety as requested before any hierarchy building is attempted. The Data Shaping Provider creates an index on the related field in the child rowset, and a chapter column is added to the parent rowset. This index acts like a filter when child rows are accessed or when the application scrolls though the parent rows. The parent rowset is tied to the child rowset by this additional column. When the child rowset is accessed using the chapter handle, it contains only rows that match the current row in the parent. When another chapter handle is selected, the subset of child rows changes to reflect the correct relationship.

Using the following example of retrieving data from the Customers and Orders tables in the Sales database, the entire Customers table is loaded into a rowset, followed by the Orders table. Once retrieval is complete, an index is built that relates the child to the parent rowset based on a common field in the two tables. The chpOrders rowset contains all selected data from the Orders table that relates to each customer row.

SHAPE {select cust_id, cust_name from Customers}
   APPEND ({select * from Orders}
   RELATE cust_id TO cust_id)
      AS ChpOrders

This results in the following hierarchy:

This type of hierarchy offers the highest performance when navigating the results; however, the initial creation of the hierarchy can be quite time-consuming because the rows for all related rowsets must be fetched before the hierarchy can be created.

See Also

Chapter 4: Rowsets | Chapter Columns | Chapters | Chapters and Detail Groupings | Hierarchical Rowsets | Invoking the Data Shaping Service for OLE DB | Lifetime of Row Handles Within Chapters | Releasing Rowsets


Building Parameterized Hierarchies

The Data Shaping Service for OLE DB can be used to create a relationship between two rowsets retrieved from parent and child tables. The relationship of the child rows to the parent is based on parameters set in the SHAPE command. The hierarchical outcome of a parameter-based hierarchy has the same structure as when relating a parent row to a child row using RELATE <parent row> TO <child row>. When the PARAMETER clause is used in a RELATE statement, the hierarchy is considered to be parameter-based. Hybrid RELATE statements, where the RELATE clause contains both parent column to child column and parent column to child provider command parameters, are supported.

Building a parameter-based hierarchy requires an active connection so that as you move to new parent rows, the child rows, as specified by the parameters, can be fetched. This type of rowset is useful when fetching only a few chapters.

After the connection is made and the SHAPE command invoked, the parent rows are fetched in their entirety. As you navigate through the fetched parent rows, the child query is executed anew to fetch the appropriate child rows. The parameter values used to execute the child query are taken from the specified columns in the parent row.

Using the following example of retrieving data from the Customers and Orders tables in the Sales database, the entire Customers table is loaded into a rowset. Each chapter in the child rowset (rsOrders) is fetched by executing the child query with the appropriate parameter. The child query for a specific chapter is not executed until the child rows are actually requested.

SHAPE {select * from Customers}
   APPEND ({select * from Orders where cust_id = ?}
      AS rsOrders
   RELATE cust_id TO PARAMETER 0)
      AS ChpOrders

This results in the following hierarchy, which is the same as a relation-based hierarchy:

See Also

Chapter 3: Commands | Chapter Columns | Chapters | Chapters and Detail Groupings | Hierarchical Rowsets | Invoking the Data Shaping Service for OLE DB | Lifetime of Row Handles Within Chapters | Releasing Rowsets | Using Parameters


Group-Based Hierarchies

The Data Shaping Service for OLE DB can be used to create a group-based hierarchy from a single rowset.

In group-based relationships, the BY clause in the SHAPE command is used to partition a rowset. A parent rowset is then constructed, in which there is one row for each partition in the child rowset.

If the BY clause is omitted, the child rowset is divided into a single partition and the parent rowset contains only a single row.

The parent rowset must contain a chapter column that relates it to its defining child rowset. The COMPUTE clause adds this column to the parent rowset. The parent rowset can also contain other columns, similar to a relation-based rowset. This includes chapter columns, so a computed parent rowset might have multiple child rowsets.

In the following example, the child rowset, rsOrders, consists of the results of the SELECT statement. The COMPUTE clause defines the parent rowset to be built from rsOrders. In the COMPUTE clause, rsOrders AS chpOrders identifies rsOrders as the defining child rowset. It also adds a chapter column, chpOrders, to the parent rowset. The BY cust_id clause identifies how the child rowset should be partitioned into chapters. There will be a single chapter (and a single parent row) for each distinct value of cust_id in rsOrders. The BY clause also adds a cust_id column to the parent rowset.

This grouped hierarchical data can now become a component of an even larger hierarchy; multiple groupings are allowed.

SHAPE {select orders.order_id, orders.cust_id, orders.order_amt
      from Orders }AS rsOrders
   COMPUTE rsOrders
      AS chpOrders
BY cust_id

This results in the following hierarchy:

See Also

Chapters


Invoking the Data Shaping Service for OLE DB

Creating a hierarchy is a two-step process, requiring connection to the Data Shaping Provider and then using the SHAPE command.

Connecting from Within OLE DB

The Data Shaping Service is an OLE DB provider and can be created by the Data Init Service like any other OLE DB provider. You can also create it using the CoCreateInstance method.

Before initializing the Data Shaping data source object, you should set the data provider property to the name (progID) of the data provider you wish to use. If a data provider is not required, the data provider can be set to none, such as "Data Provider = NONE". You should also set other properties that the Data Shaping Provider will need to connect to your data provider.

Use CoCreateInstance to create an instance of the Data Shaping Provider and pass in the appropriate connection properties. To view examples of using the Data Init Service and CoCreateInstance, see "Creating Data Source Objects" in Chapter 2 of the OLE DB Programmer's Reference (version 2.0 or later).

Connecting from within OLE DB to a remote data provider

The connection string to connect in OLE DB to a remote data store resembles the following:

Provider=MSDataShape;Data Provider=MS Remote;Remote Server=http://webaddress;Remote Provider=SQLOLEDB;Data Source=Pubs;User ID=eem;Password=pwd

Connecting from ADO

The ADO user must explicitly specify Provider=MSDataShape to use the Data Shaping Provider. It is not a semiautomatic service like session pooling and other OLE DB services. Use your provider name and its connection properties (data set name, user id, and so on) following the provider name of MsDataShape, as in this example:

Provider=MSDataShape;data provider=MSDASQL;Driver={SQL Server};Server=Lancer;Database=Sales;UID=JeffG;PWD=;

To create an empty hierarchical rowset that can later be populated with data or reshaped, it is not necessary to connect to a data provider. The connection string would be as follows:

Provider=MSDataShape;data provider=NONE

For more information about creating hierarchical rowsets, see "Fabricating Rowsets" later in this guide.

ADO Connection Example

The following example illustrates using the Shape grammar after invoking the Data Shaping Provider and connecting to a data provider:

Sub datashape()
   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset
   Dim rsChapter As Variant

   cnn.Open "Provider=MSDataShape;data provider=MSDASQL;" & _
      "Driver={SQL Server};Server=Lancer;Database=Sales;" & _
      "UID=JeffG;PWD=;"

   rst.Open "SHAPE {select * from customer} APPEND ({select * from " & _
      "orders} AS chapter RELATE cust_id TO cust_id) ", cnn

   While Not rst.EOF
      Debug.Print rst("cust_fname"), rst("cust_lname"), rst("state"), _
      rst("cust_id")

         rsChapter = rst("chapter")
         While Not rsChapter.EOF
            Debug.Print rsChapter(0), rsChapter(1), rsChapter(2), _
            rsChapter(3)
            rsChapter.MoveNext
         Wend

         rst.MoveNext
   Wend

End Sub

See Also

Creating Data Source Objects | Data Source Object States | Data Source Objects, Session Objects, and Transaction Objects


Sample Hierarchy Definitions

The Data Shaping Service for OLE DB allows you to build and manipulate hierarchies using fetched data obtained from your data provider. It applies specific commands to relate disparate rowsets in one of two distinct hierarchical groups: relation-based (including parameter-based) hierarchies and group-based hierarchies created by the COMPUTE clause.

The following examples pertain to the Sales database with a Customer table and Orders table.

Hierarchy definition | Sample
Aggregated group hierarchy | Group Hierarchy with Aggregation
Hierarchy with multiple groupings | Multiple Groupings
Hierarchy showing grand totals | Grand Totals
One parent with two child rowsets, a group detail and a parameterized hierarchy | Multifaceted Hierarchy
Nested hierarchies | Grouped Customers Related to Grouped Orders Grouped Customers Related to Grouped Orders with Aggregation
Grandchild grouping in order to skip one level when summing | Grandchild Aggregations
Computed rowsets that have a one-to-one or one-to-many relationship and are members of multiple groups | Parameterized Computed Child Rowsets

The following table schema is used for the sample hierarchy definitions:

CREATE TABLE Customers
         ( cust_id     INTEGER,
         cust_name     VARCHAR(20),
         city          VARCHAR(20),
         state         VARCHAR(20),
         PRIMARY KEY   (cust_id) )

CREATE TABLE Contacts
         ( cust_id     INTEGER,
         contact_id    INTEGER,
         city          VARCHAR(20),
         state         VARCHAR(20),
         region        INTEGER,
         PRIMARY KEY   (contact_id),
         FOREIGN KEY   (cust_id) REFERENCES customer_table )

CREATE TABLE Orders
         ( cust_id     INTEGER,
         order_id      INTEGER,
         order_date    DATE,
         order_amt     DECIMAL(9,2),
         PRIMARY KEY   (order_id),
         FOREIGN KEY   (cust_id) REFERENCES customer_table )

CREATE TABLE OrderItems
         ( order_id    INTEGER,
         item_id       INTEGER,
         description   VARCHAR(30),
         quantity      SMALLINT,
         unit_price    DECIMAL(6,2),
         PRIMARY KEY   (order_id, item_id),
         FOREIGN KEY   (orderid, REFERENCES order_table )


Group Hierarchy with Aggregation

The Data Shaping Service for OLE DB can be used to sum up a hierarchical structure to produce totals at different levels. In this example, certain columns are selected by cust_id from the Customers table, including the order amount; the order information is selected from the Orders table. All data is fetched before it is related and summed. In this example, the resulting child rowset is named rsDetail. RsDetail is summed using the ordered amount for any customer ID within the scope. The resulting Recordset is the parent of rsDetail and contains the summary information of all child rowsets. When accessing a parent row, the internally created index relates the parent to its child rowsets.

SHAPE {select customer.*, orders.order_id, orders.order_amt
         from customers, orders where customer.cust_id = orders.cust_id}
      AS rsDetail
COMPUTE rsDetail, SUM(rsDetail.order_amt)
   AS order_amt
COUNT(rsDetail)
   AS DetailCount
   BY cust_id

This results in the following aggregations within this hierarchy:


Multiple Groupings

The Data Shaping Service for OLE DB can be used to group any number of times within a hierarchical structure to produce totals at different levels. In this example, the summary of the first group, the Customer Group (rsCustGrp), is calculated by computing all customer details, and then the Customer Group is summmed by state, which forms the second grouping. Two SHAPE commands are used here: One builds the Customer Group hierarchy; the other builds the State hierarchy using the results of the Customer Group hierarchy.

Shaping the Customer Group Hierarchy

In this example, certain columns are selected by cust_id from the Customers table, including the order amount, and the order information is selected from the Orders table. All data is fetched before it is related and summed. The resulting child rowset is named rsDetail. RsDetail is then summed using the ordered amount for any customer ID within the scope. The resulting rowset is called rsCustGrp and is the parent of rsDetail. It contains the summary information of all child rowsets. When accessing a parent row, the internally created index relates the parent to its child rowsets.

Shaping the State Hierarchy

After the Customer group has been built into a relational hierarchy, with parent rows associated with their child rows through the index, the State group is built. The Customer group is the child rowset associated with its parent the State row. Again, the relation is made by creating the index or filter.

SHAPE
   (SHAPE {select customer.*, orders.order_id, name,
            orders.order_amt from Customers, Orders
            where customer.cust_id = orders.cust_id}
         AS rsDetail
      COMPUTE ANY(rsDetail.cust_name)
         AS cust_name,
      ANY(rsDetail.state)
         AS state,
      SUM(rsDetail.order_amt)
         AS ord_amt, rsDetail
      BY cust_id )
      AS rsCustGrp
   COMPUTE rsCustGrp
   BY state

This results in the following aggregations within this multiple grouped hierarchy:


Grand Totals

The Data Shaping Service for OLE DB can be used to group a hierarchical structure to produce a grand total. In this example, certain columns are selected by cust_id from the Customers table, including the order amount; the order information is selected from the Orders table. All data is fetched before it is related and totaled. The resulting child rowset is named rsDetail. RsDetail is then summed using the ordered amount for any customer ID within the scope. The resulting rowset, rsCustGrp, is the parent of rsDetail and contains the summary information of all child rowsets. By eliminating the BY clause in the outer summary, the COMPUTE SUM statement produces the grand total for order summaries of the parent rows.

SHAPE
   (SHAPE {select customer.*, orders.order_id, orders.order_amt
            from Customers, Orders where customer.cust_id = orders.cust_id}
            AS rsDetail
      COMPUTE SUM(rsDetail.order_amt)
         AS cust_ord_sum,
      ANY(rsDetail.cust_name)
         AS cust_name, rsDetail
      BY cust_id )
         AS rsCustGrp
   COMPUTE SUM(rsCustGrp.cust_ord_sum)
      AS grand_total, rsCustGrp

This results in the following hierarchy with a grand total:


Multifaceted Hierarchy

The Data Shaping Service for OLE DB can be used to create a multifaceted hierarchical structure using the SHAPE command. In this example, a hierarchy is created within a grouped hierarchy, forming the parent with two child rowsets, a group detail, and a parameterized hierarchy.

Creating a Relation-based Hierarchy with Two Children

The first SELECT in the SHAPE command defines one of the child rowsets. This child rowset is named rsDetail. The COMPUTE clause indicates that the parent rowset is to be fabricated from the rsDetail rowset. RsDetail will be partitioned on the cust_id column, and the parent rowset will contain one row for each distinct value of cust_id in rsDetail.

In addition to the rsDetail and cust_id columns, the parent rowset will contain three other columns. The first is an aggregate column produced by the ANY (rsDetail.cust_name) function. The second is a chapter column named chpContacts. The chpContacts chapter column relates the parent rowset to the rsContacts rowset defined by the second SELECT statement in the SHAPE command. Because the RELATE clause relates the parent cust_id colulmn to parameter 0, the rsContacts rowset is parameterized.

The third additional column is the result of the Count (rsContacts.contact_id) aggregate.

Note Because the Count function argument is rsContacts.contact_id (not just rsContacts), rows in the chapter where contact_id is NULL will not be included in the count.

Creating the Parameterized Hierarchy

The inner, or second, SELECT statement produces a parameterized hierarchy of contacts retrieved from the Contacts table in the Sales database. Contacts.* is the child of rsContacts. Once the parent rows have been retrieved, an index is built that establishes the relationship between parent and child. Each time action is requested on the parent row, a query based on the parameters is executed for the child or related rows. This hierarchy's only relationship to rsDetail is that of a common parent.

Shaping the Customer Group Hierarchy

After each of the parent hierarchies has been established, rsDetail and rsContacts are grouped under cust_id by the outer SHAPE … BY command. Again, the relation is made by creating the index or filter. Because the group rowset was created apart from the data store, it is not updatable.

SHAPE
   {select customer.*, orders.* from Customers, Orders
         where customer.cust_id = orders.cust_id}
      AS rsDetail
         COMPUTE rsDetail,
         ANY(rsDetail.cust_name)
      AS cust_name,
   ({select * from Contacts where cust_id = ?}
          RELATE cust_id TO PARAMETER 0)
      AS rsContacts,

         COUNT(rsContacts.contact_id)
      AS [COUNT of Contacts]
   BY cust_id

This results in the following aggregations within this multifaceted hierarchy:


Grouped Customers Related to Grouped Orders

By nesting SHAPE commands, you can use the Data Shaping Service for OLE DB to create hierarchies with as many levels as you want. The following example uses three SHAPE commands to create a four-level hierarchy.

In the following code example, data is retrieved from the Customers and Orders tables in the Sales database. The innermost SHAPE command uses select * from orders to create the lowest rowset in the hierarchy. The COMPUTE rsOrdDetail BY cust_id, month clause defines its parent.

The middle SHAPE command uses select * from Customers to create a parent rowset whose child is the parent rowset in the inner SHAPE command.

The outermost SHAPE command creates the root rowset in the hierarchy from
the parent rowset in the middle SHAPE command. It is created by partitioning
the Customers rowset by state.

SHAPE
   (SHAPE {select * from Customers}
      APPEND (
         (SHAPE {select * from Orders}
               AS rsOrdDetail
                  COMPUTE rsOrdDetail
            BY cust_id, month)
         RELATE cust_id TO cust_id)
         AS rsOrdByMonth )
      AS rsCustDetail
   COMPUTE rsCustDetail
   BY state

This results in a nested four-level hierarchy:


Grouped Customers Related to Grouped Orders with Aggregation

By nesting SHAPE commands, you can use the Data Shaping Service for OLE DB to create hierarchies with as many levels as you want and to aggregate child rows into parent rows. The grouped aggregation example uses three SHAPE commands to create a four-level hierarchy and uses the SUM function to aggregate child rows.

In the following code example, data is retrieved from the Customers and Orders tables in the Sales database. The innermost SHAPE command uses select * from orders to create the lowest rowset in the hierarchy. The clause COMPUTE rsOrdDetail BY cust_id, month defines its parent, and the SUM function aggregates the child rowsets into totalAmtPerMonth.

The middle SHAPE command uses select * from Customers to create a parent rowset whose child is the parent rowset in the inner SHAPE command. The phrase SUM(rsOrdByMonth.totalAmtPerMonth) AS totalAmtPerCustomer aggregates the previous total, totalAmtPerMonth, and sums it by rsOrdbyMonth for all orders selected into totalAmtPerCustomer.

The outermost SHAPE command creates the root rowset in the hierarchy from
the parent rowset in the middle SHAPE command. It is created by partitioning
the Customers rowset by state, and totalAmtPerState then holds the total figure of the first two aggregations.

SHAPE
   (SHAPE {select * from Customers}
      APPEND SUM(rsOrdByMonth.totalAmtPerMonth)
         AS totalAmtPerCustomer,
         ((SHAPE {select * from Orders}
               AS rsOrdDetail
                  COMPUTE SUM(rsOrdDetail.order_amt)
                     AS totalAmtPerMonth, rsOrdDetail
                  BY cust_id, month)
         RELATE cust_id TO cust_id)
            AS rsOrdByMonth )
      AS rsCustDetail
   COMPUTE SUM(rsCustDetail.totalAmtPerCust)
      AS totalAmtPerState, rsCustDetail
   BY state


Grandchild Aggregations

The Data Shaping Service for OLE DB allows you to perform grandchild aggregations. Use this command structure when you want to compute the average of the detail records, not the average of the parent rows.

In the following example, the inner SHAPE command selects from the Orders and OrderItems tables to build a relationship where rsOrders becomes the parent of rsDetails. When the outer SHAPE command is executed, it creates AvgDetailAmt and performs the average function (AVG) upon the Amount column in the grandchild (rsDetails) across all the rows in the corresponding child (rsOrders) to get the average of all the details.

SHAPE {select * from Customers}
   APPEND
((SHAPE {select * from Orders}
         APPEND({select * from OrderItems}
         RELATE order_id to order_id)
            AS rsDetails) RELATE cust_id to cust_id)
      AS rsOrders,
   AVG(rsOrders.rsDetails.Amount)
      AS AvgDetailAmt

This results in the following aggregations:


Parameterized Computed Child Rowsets

The Data Shaping Service for OLE DB allows you to insert another level of child rowsets while still executing the parameterized command for the lowest child rowset. This allows you to build parameterized hierarchies that have one or more computed rowsets between the parent rowset that supplies the parameter values and the child rowset created by the parameterized provider command. The parameters are implicitly added to the computed rowset as hidden columns and implicitly become part of the BY clause.

Consider a hierarchy built upon a parameterized compute statement:

SHAPE {select * from Customers}
   APPEND
      ((SHAPE
         {select orders.cust_id, orderItem.* from Orders, OrderItem
            where cust_id = ? and orders.order_id = orderItems.order_id}
            AS rsOrderItems
         COMPUTE rsOrderItems BY order_id)
      RELATE cust_id to PARAMETER 0)
   AS chpCustOrder

This results in the following:


Working with Hierarchies

Existing hierarchies can be manipulated to meet specific data requirements. It is also possible to navigate all levels of a hierarchy to expose all related data.

You can also build hierarchies fabricated from empty rowsets and use them as placeholders for testing or for other temporary data.

When you need to | See
Open up to manipulate a Recordset that has been created as a child of another | Accessing a Child Recordset
Keep child Recordsets current with the parent row | Controlling Freshness of Child Results
Have control over when the data in the child Recordsets matches that of the current parent | Controlling the Rows in a Chapter
Gain access to the Command object that created the initial hierarchy | Accessing the Command of a Hierarchy
Keep the child rows in synch with the parent row | Requerying and Resynching the Hierarchy
Build new relationships or aggregations within an existing hierarchy, or combine hierarchies to make a new one | Reshaping
Build a hierarchy composed of empty rowsets | Fabricating Rowsets


Accessing a Child Rowset

ADO Technique

When two Recordsets are related, an index is created on the related fields in the child Recordset and a field is added to the Fields collection of the parent Recordset, which has a reference to the child Recordset in the Value property.

To access the child Recordset, you can make a reference to the child Recordset in an empty Recordset object, in effect loading the child Recordset into another. ADO exposes the child field as type adChapter. You can write generic code to handle fields that appear as child Recordsets without having to know the field name ahead of time, as shown in this example:

Dim rChapter As ADODB.Recordset
rst.Open
   "SHAPE {select * from customer}
      APPEND ( {select * from " & _
               "custOrder} AS chapter RELATE cust_id TO cust_id)", cnn
rChapter = rst("chapter")
debug.print rChapter.fields(0)

OLE DB Technique

For more information about accessing a child rowset, see "Navigating Hierarchical Rowsets" in the "Hierarchical Rowsets" topic of Chapter 4: Rowsets.

See Also

Chapter 3: Commands | Chapter Columns | Chapters | Chapters and Detail Groupings | Hierarchical Rowsets | Lifetime of Row Handles Within Chapters | Releasing Rowsets


Controlling Freshness of Child Results

Controlling freshness of child results is not possible for group-based and relation-based hierarchies. For these two types, the data is retrieved in its entirety as requested, once the connection is made and the SHAPE command invoked. By default, the cursor engine, or specifically the Cursor Service for OLE DB, caches the fetched rows before any hierarchy building is attempted. These cached rows are used when the parent row is accessed. Executing the SHAPE command again will fetch the current data from your data provider, creating a new result set. Any existing result sets are unaffected.

For parameter-based hierarchies, the Data Shaping Service fetches each child result when a new parent is selected. To control ADO child Recordset freshness in this type of hierarchy, set the ADO dynamic property Cache Child Rows (Boolean). This property is included in the Properties collection of the Recordset object. The default is True, meaning child rows will be kept when the chapter is released.

Setting this property to False forces the cursor engine to query the server to obtain the current child row—the same as when building parameter-based hierarchies. If Cache Child Rows is False, the rows in the child Recordset are discarded when the chapter is released. If the chapter is reopened, the parameterized command is reexecuted, fetching fresh results from the data provider.

The following example shows how to force the cursor engine to retrieve child rows for each parent as needed:

Debug.Print rs.Properties("Cache Child Rows")
rs.properties("Cache Child Rows") = False


Controlling the Rows in a Chapter

Chapters are used to identify a group of rows within a rowset. When a hierarchical relationship is established using the SHAPE command, a chapter column is added to the parent row. The value of a chapter column in a parent row scopes the child rowset to just those rows that are related to the parent row. ADO expresses the value of a chapter column as a Recordset that contains only the child rowset rows that are in the chapter for the parent row.

ADO Technique

The Properties collection of the Recordset object contains a property called StayInsynch, which makes it possible to dedicate one Recordset to continually update to reflect the children of the latest parent. When StayInsynch is True (the default) and the position of the parent Recordset changes, the child Recordset is automatically closed and reopened with the value of the chapter column for the new parent row. This can be useful when you always want the child Recordset to reflect or stay in synch with the current position of the parent Recordset.

If StayInsynch is set to False, the child Recordset is not affected by changes in the position of the parent Recordset. This is useful when you want to search or otherwise change position in the parent Recordset without altering the child Recordset.

To set the property, follow this example:

Debug.Print rs.Properties("StayInsynch")
rs.properties("StayInsynch") = False

See Also

Chapters


Accessing the Command of a Hierarchy

For all Recordsets in the hierarchy, the ActiveCommand dynamic Recordset object property always points to the same Command object used to create the hierarchy.

This code example using ADO illustrates how to access the command:

con.open "Provider=MSDataShape;data provider=None"
rs.open "SHAPE APPEND NEW DBTYPE_I4 AS KeyCol",con
Set cmd = rs.ActiveCommand


Requerying and Resynching the Hierarchy

ADO provides a number of means to fetch fresh data for the child rows of a hierarchy.

Executing the Requery method on any Recordset in the hierarchy will close and reopen the Recordsets involved. This includes all associated Recordset data.

The next example shows a hierarchy of customers built to include child rows of orders. rsOrders.Requery will close that Recordset and all orders.* Recordsets. The command as recorded in the Command object will execute again, and a new hierarchy will be built. The Requery method is useful when the possibility of changes to all queried data store tables exists.

Simple Relation-Based Hierarchy Example

SHAPE {select * from Customers}
   APPEND ({select * from Orders}
      AS ChpOrders
   RELATE cust_id to cust_id)
      AS ChpOrders

This results in the following hierarchy:

Executing the Resync method on a parent row causes the child Recordset chapter to be cleared but not deleted. This causes the child rows to be reassociated when the parent row is accessed. This is useful when the "relating" field in the parent row is altered, causing the child chapter membership to change.

For example, if a hierarchy of customers is built to include child rows of orders, as in the previous code sample, and the field cust_id in the parent row is altered, the child rows would not be related to the present parent. When rsOrders.Resync is executed, the child rows are cleared, forcing the application to fetch a new set of child rows.


Reshaping

Once data has been fetched and formed into hierarchies, you can reuse those rowsets in new relationships. These reshape operations are performed only outside the data store, allowing prototyping and experimentation with the data.

Reshaping allows a SHAPE command to use an existing rowset, created under the same connection, as the child rowset of a newly created rowset. The existing rowset is unaffected by this reshaping except that it acquires a new parent rowset.

The following example, where the connection remains active, illustrates the ability to reshape hierarchies using the Shape language. First, rsOrders and rsOrderItems are related by order_id. The second SHAPE command creates a computed parent of rsOrderItems that is related by item_id. Finally, rsCustomer is created as the parent of rsOrders related by cust_id.

Rs1.open _
"SHAPE {select * from Orders} as rsOrders " & _
   "APPEND ({select * from OrderItems}" & _
      "AS rsOrderItems" & _
   "RELATE order_id TO order_id)"

RS2.open
"SHAPE rsOrderItems AS rsItems " &_
   "COMPUTE rsItems BY item_id"

RS3.open
"SHAPE {select * from Customers} AS rsCustomer " & _
   "APPEND (rsOrders RELATE cust_id TO cust_id)" _

This results in the following hierarchy:

Rowset Naming Rules When Reshaping

The rowset alias is used if it is specified in the SHAPE command that creates the rowset. Otherwise, a unique name is automatically generated by the Data Shaping Provider with the format of dsrowset<n>, where n is a unique but not necessarily sequential number beginning with 1.

In case of name collisions, the naming depends on the Unique Names property of the Data Shaping provider. If True, the user name of the cursor is appended, if necessary, with a number unique for the shape namespace. For example, a collision on a cursor named orders will generate orders_<n>. If False, the synonym name is valid and becomes the value of the Name property. This rowset will not be able to participate in reshape commands until after all other rowsets with the same name are closed.

The source of a shape component can also be a hierarchical cursor name. For each rowset, the name is set by default from the associated alias, if specified, in the SHAPE command that creates it.

Reshape Limitations

The SHAPE command always refers to Recordsets already created in the same session. That is, when reshaping, the SHAPE command explicitly refers to rowsets already created by the same MSDataShape provider session object.

The APPEND clause cannot be used to add new columns to an existing (reshaped) rowset.

Reshaping of parameterized rowsets is not supported.

When working in ADO and accessing child Recordsets in a hierarchy, the entire child Recordset is not exposed. However, the following reshape technique gives you chapterless access to the Recordset without giving it a new parent. This applies both to computed and to related hierarchies. It will not work for parameterized child rows because they are not reshapable, and parameterized rowsets are never accessible as a complete rowset.

In the following example, reshaping rsOrderItems produces a Recordset scoped to include all rows in rsOrderItems, exposing all the child rowsets.

rs1.open "SHAPE {select * from Orders} APPEND ({select * from " &_
   "OrderItems} as rsOrderItems RELATE order_id TO order_id)",con

rs2.open "shape rsOrderItems",con


Fabricating Rowsets

Using the Shape language, it is possible to construct empty rowsets that contain specific column types. These rowsets can be used to build hierarchies for prototyping or testing.

In the connection string, use the keywords "Provider=MSDataShape;data provider=None" to invoke the Shaping Provider but not a data provider.

Use the Shape keyword NEW in the APPEND clause to create new columns in the rowset, as in the following example:

SHAPE
   APPEND
      NEW DBTYPE_I4 AS Cust_LocationCode,
      NEW adLongVarChar AS Cust_Country,
      NEW adChar(60) AS Cust_Address


Supported Interfaces of the Data Shaping Service for OLE DB

Supported interface Description
IDBCreateSession Object type: Data source

Obtains a new session.
IDBInitialize Object type: Data source

Initializes and uninitializes data source objects and enumerators.
IDBProperties Object type: Data source

Used to set or get the values of properties on the data source object or enumerator and to get information about all properties supported by this provider.
IPersist Object type: Data source

Used to control exposure of data to transactions. 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.

See IPersist* Objects for an overall discussion.

See Getting and Setting IPersist* Objects for specific implementation notes.

For additional considerations, see Lifetime of IPersist* Objects and IPersist* Objects, Rowset Update Semantics, and ITransaction.

(All sources are in the OLE DB Programmer's Reference.)
ISupportErrorInfo Object types: Data source, Session and Command

Defined by Automation, indicating whether a specific interface can return Automation error objects.
IGetDataSource Object type: Session

Obtains an interface pointer to the data source object.
IOpenRowset Object type: Session

Enables consumers to open and work directly with individual tables or indexes in a data store.
ISessionProperties Object type: Session

Returns information about the properties a session supports and the current setting of those properties.

Supported only if the data provider supports this interface.
IDBCreateCommand Object type: Session

Creates a command.
IDBSchemaRowset Object type: Session

Returns schema information about the data store.

Supported only if the data provider supports this interface.
ITransaction Object type: Session

Used to commit, abort, and obtain status information about transactions.

Supported only if the data provider supports this interface.
ITransactionLocal Object type: Session

Used to start, commit, and abort transactions on the session.

Supported only if the data provider supports this interface.
IAccessor Object type: Command

Provides methods for accessor management.
IColumnsInfo Object type: Command

Exposes information about columns of a rowset or prepared command and provides a limited set of information in an array.
ICommandProperties Object type: Command

Specifies to the command the properties from the Rowset property group that must be supported by the rowset's return by ICommand::Execute.
ICommandText Object type: Command

Holds only one text command.
IConvertType Object type: Command

Provides information about the availability of type conversions on a command or on a rowset.
ICommandPrepare Object type: Command

Encapsulates command optimization, a separation of compile time and run time, as found in traditional relational database systems.

Supported for MDAC 2.0 only if the data provider supports this interface. For MDAC 2.1 and later, the interface is always supported.
ICommandWithParameters Object type: Command

Encapsulates parameters.

Supported only if the data provider supports this interface.
IColumnsRowset Object type: Command

Supplies complete information about columns in a rowset.

See Also

Chapter 15: Transactions | Chapter 6: Getting and Setting Data | IPersist* Objects | Properties (Alphabetical)


Supported Properties of the Data Shaping Service for OLE DB

The OLE DB Service Components, the OLE DB Shaping Service, and the Cursor Service for OLE DB share the same supported OLE DB properties. To view these properties, see Supported Properties of the Cursor Service in the Cursor Service for OLE DB documentation.

In addition, the OLE DB Shaping Service supports two dynamic properties of its own, as described in the following table.

Property | Description
Data Provider | Property Set: DBPROPSET_MDSDBINIT Type: VT_BSTR The name of the provider that will supply rows to be shaped. Allows a consumer to use the Shaping Service without ADO and without using connection strings. This property has a defined constant of DBPROP_MSDS_DBINIT_DATAPROVIDER.
Unique Reshape Names | Property Set: DBPROPSET_MSDSSESSION Type: VT_BOOL Default: False Indicates whether rowsets with duplicate alias values are allowed. If this dynamic property is True and a new rowset is created with the same alias as an existing rowset alias, then the new rowset alias is modified to make it unique. If this property is False and a new rowset is created with the same alias as the existing rowset, both rowsets will have the same alias. Therefore, neither rowset can be reshaped as long as both rowsets exist.

See Also

Getting Information About Properties | Getting Property Values | Properties (Alphabetical) | Setting Property Values


Behaviors Specific to the Data Shaping Service for OLE DB

Case Sensitivity

In the code examples throughout this guide, case has been matched only for clarity in explanations. The Data Shaping Provider does not require case-sensitive naming conventions.

Updatability

Hierarchies are updatable in many circumstances. The "summary" Recordset in a grouping hierarchy is not updatable because the data in those rows is mostly manufactured. Instead, changes should be made in the detail row.

Committing changes to the rowset while "disconnected" will clear pending update flags, and the row will be treated as having no pending changes. This is useful in cases where the consumer is sending updates manually and wants to mark a row as having been committed to the provider.

It is necessary for your data provider to support SQL update commands (Update, Insert, and Delete) to perform any data updates through the Data Shaping Provider.

Disconnecting and Reconnecting Recordsets

Disconnecting one rowset will disconnect all. However, the opposite is not true. To reconnect, rowsets at each hierarchical level must be connected before all data is available.


Known Limitations of the Data Shaping Service for OLE DB

Supported Remoted Hierarchical Rowsets

The Data Shaping Service for OLE DB supports only group-based or relation-based remoted rowsets and does not support remoted parameter-based rowsets.

Reshaping Limitations

For information about limitations when performing hierarchical reshaping functions, see Reshape Limitations in this guide.

See Also

Group-Based Hierarchies | OLE DB Remoting Provider | Relation-Based Hierarchical Rowsets | Reshaping


Appendix A: Installation Notes

The Data Shaping Service for OLE DB is shipped with the Microsoft Data Access Components as Msadds.dll and requires no special installation procedures. It is supported by Microsoft OLE DB version 2.0 and later. Msadds.dll contains the Data Source, Session, and Command objects.

In addition to the Data Shaping Provider, the Cursor Service for OLE DB must be installed. Msdace.dll contains the client cursor rowset object and is a core component of Microsoft Data Access Components.

Because the Data Shaping and Cursor Services are closely tied, it is recommended that you install each one from the same version of the Microsoft Data Access Components.


The Cursor Service for OLE DB

The Cursor Service for OLE DB

The Cursor Service for OLE DB consumes data from a rowset, stores the data locally, and exposes the data along with rich data-manipulation functionality through rowset interfaces. The Cursor Service can provide a local instance of virtually any rowset produced by an OLE DB provider.


Overview of the Cursor Service

Who can use the Cursor Service

Developers building applications using OLE DB technologies can define new cursors or manipulate existing cursors with the Microsoft® Cursor Service for OLE DB. This service can be used both on client-tier and on middle-tier applications, and in conjunction with other OLE DB data providers and service component providers.

What the Cursor Service allows you to do

The Cursor Service provides a client-tier and middle-tier cache for data sets (or cursors) and a service that implements smart synchronization functions. Using these services dramatically reduces network traffic while giving you full control over data synchronization. The Cursor Service can be installed on each tier of a multitier application, providing an effective solution for scalability. The OLE DB rowset becomes a convenient structured data buffer that can be marshaled by value between computers or processes. Changes to the rowset can be tracked and exposed when the rowset is returned to the server that created it. Sorting and filtering are also possible. The Cursor Service is an underlying technology that is hidden from the ADO user and accessible to the OLE DB developer through the IRowset interface.

Additional Resources

The following technical articles and white papers are posted on the "Technical Materials" page of the Universal Data Access Web site at http://www.microsoft.com/data/techmat.htm or included with the Microsoft® Data Access Components (MDAC) SDK. Articles are listed here in the order of their relevance.

Their presence and location are subject to change with each release. Other articles can be found in the Microsoft Developer Network (MSDN™) Library. Detailed articles concerning known limitations or workarounds can be found in the MSDN Knowledge Base.

Documents | Date written/posted
OLE DB/ADO: Making Universal Data Access a Reality | May 1998
Choosing and Managing Cursors Chapter 7 in the Visual Studio 6.0 documentation, "Developing for the Enterprise" | July 1998
Optimistic Remote Updating in the Client Cursor Engine | March 1999

References

OLE DB Programmer's Reference

ADO Programmer's Reference

Web sites

http://www.microsoft.com/data/oledb

http://www.microsoft.com/data/ado


Using the Cursor Service: A Closer Look

The Cursor Service consumes data from a rowset, caches it locally, and exposes the data along with data-manipulation functionality through rowset interfaces. The Cursor Service can provide a local instance of virtually any rowset produced by an application: It is responsible for providing the buffering of the original data, changed data, last read (underlying) data, and metadata, and for exposing the data as an OLE DB rowset. It provides common rowset functionality such as scrolling, buffering, and notifications on top of the buffered data.

The rowset object to be used by the Cursor Service is populated by reading data from the underlying (original) rowset. The Cursor Service determines basic column information, such as number of columns, type, and label, and then reads the data from the rowset. After the Cursor Service retrieves extended metadata information by using the IColumnsInfo and IColumnsRowset interfaces, it closes the underlying rowset. IColumnsRowset::GetColumnsRowset, if supported by the provider, returns the following column information (if not supported, the Cursor Service attempts to retrieve column information by using IColumnsInfo):

  • DBCOLUMN_BASECOLUMNNAME—Base table column name
  • DBCOLUMN_BASETABLENAME—Base table name
  • DBCOLUMN_BASESCHEMANAME—Base table schema name
  • DBCOLUMN_BASECATALOGNAME—Base table catalog name
  • DBCOLUMN_KEYCOLUMN—Key or non-key column status

For more information about these columns, see "Optional Metadata Columns" in the OLE DB Programmer's Reference.

The Cursor Service is typically invoked when the consumer requests a local copy of a remote rowset or when the consumer requests rowset functionality not supported by the provider's native rowset.

All rowsets are handled independently by the Cursor Service. Rowsets do not share common data, and no integrity constraints are enforced. The Cursor Service does not try to replicate the provider data model, its integrity restrictions, or its data. Instead, it manipulates independent data sets as defined and required by the application by providing the following functions: data fetching, data manipulation, local updates, and local rowsets.

See Also

Chapter 4: Rowsets | IColumnsRowset | Invoking the Cursor Service | IRowset


Data Fetching Using the Cursor Service

When the consumer requests functionality that requires the Cursor Service, the Cursor Service component is invoked to fetch the data and cache it locally. The data is stored in memory buffers and, if needed, written to temporary files on disk. The data fetch can be performed synchronously or asynchronously.

The following ADO code example shows how to invoke the Cursor Service by loading a new recordset with data fetched from the database:

Private Sub OpenRs_Click()
   Dim rs As New adodb.Recordset
   Dim cn As New adodb.Connection

   rs.CursorLocation = adUseClient       'invokes the Cursor Service
   rs.Open "select * from authors",
   "provider=sqloledb;server=dbsrv1;database=pubs;uid=sa;pwd=;",
   adOpenStatic, adLockBatchOptimistic
End Sub


Data Manipulation in the Cursor Service

You can access the rowset by using ADO and OLE DB interfaces. In ADO, use the Recordset object to maintain currency and manipulate the data. The Recordset object exposes methods implemented by the Cursor Service to position on a row, get field values, and filter and sort the rows based on one or more fields. A Recordset object can be cloned, which produces a second Recordset object based on the same underlying rowset data. Clones have their own currency, filter, and sort order, but they share the same set of data. The Cursor Service also generates events exposed by the associated rowset and used to bind rowset data to visual controls. Rowsets can be persisted in an internal binary format or in Extensible Markup Language (XML) format. A client rowset can be remoted between two processes or machines over DCOM or HTTP, enabling distributed data access for multitier architectures over intranets and the Internet.

See Also

Chapter 6: Getting and Setting Data


Performing Local Updates with the Cursor Service

Using the Cursor Service, you can make any changes to the local data: modify data fields, delete existing rows, and insert new rows into the rowset. The changes are immediately committed to the cache and are visible from all the cloned Recordset objects on that rowset. Local changes can be committed to the data provider row by row or once for the entire rowset. The built-in synchronization functionality handles the commit process.

Additionally, committing changes to the rowset while "disconnected" will clear pending update flags and the row will be treated as having no pending changes. This is useful in cases where the consumer is sending updates manually and wants to mark a row as having been committed to the provider.

The following code sample shows a way to make modifications to a disconnected Recordset object and then commit those changes to the database:

Private Sub ShowUpd_Click()
   Dim rs As New adodb.Recordset
   Dim cn As New adodb.Connection

   ' Open recordset with client-side cursors
   Cn.open "provider=sqloledb;server=dbsrv1;database=pubs;uid=sa;pwd=;"
   rs.CursorLocation = adUseClient
   rs.Open "select * from jobs", cn, adOpenStatic, adLockBatchOptimistic

   ' Close connection–-leave the disconnected recordset open
   cn.close
   set cn = nothing

   ' Update records
   rs.MoveFirst
rs("job_desc") = "PM"

   rs.MoveNext
   rs("job_desc") = "CEO"
   ' Commit changes locally in recordset buffer
   rs.UpdateBatch

   ' Make more changes
   rs.MoveNext
   rs.Delete

   rs.MoveLast
   rs.Delete

   rs.AddNew
   rs("job_id") = 16
   rs("job_desc") = "STE"
   rs("min_lvl") = 9
   rs.Update

   rs.MoveFirst
   rs("job_desc") = "WB"

   ' Reconnect recordset
   cn.Open "provider=sqloledb;server=dbsrv1;database=pubs;uid=sa;pwd=;"
   Set rs.ActiveConnection = cn
   ' Submit changes to datasource-–only latest changes will be saved
   rs.UpdateBatch

Set rs = Nothing
   Set cn = Nothing
End Sub

See Also

Chapter 4: Rowsets | Chapter 6: Getting and Setting Data | Updating and Refreshing Data


Row Fix-up

When static client cursors are created from multiple base tables (for instance, in a join operation), data manipulation performed by the Cursor Service can keep the cursor consistent with each base table that participated in generating the data. Explicit update control is available to ensure that updates applied to the base tables in the data store preserve referential integrity restrictions.

ADO dynamic properties exist to give explicit control to Row Fix-up functionality. When the Unique Table ADO property is set, Row Fix-up mode is implemented. The Unique Table property restricts INSERT, UPDATE, and DELETE statements affecting the Unique Table. Columns of the base tables are updatable, but those columns not in the Unique Table cannot be included in any update method.

The following table details how the Row Fix-up functionality behaves using different ADO methods. For the described behavior to occur, the Unique Table property must be set.

ADO method | Maps to OLE DB interface | Row Fix-up behavior
AddNew | IRowsetChange::InsertRow | For an inserted row, inserts new values in the fields for which the base table is the Unique Table.
Delete | IRowsetChange::DeleteRows | For a deleted row in the cursor, the corresponding row in the Unique Table will be deleted. All other base tables will be unaffected.
Update | IRowsetChange::SetData | If UpdateBatch is set to resynchronize rows in the updated batch and if the CustomResynch property is set, UpdateBatch will use this property for custom resynchronization.

For more information about ADO properties allowing Row Fix-up functionality, see "ADO Dynamic Properties" in the ADO Programmer's Reference.


Creating Local Rowsets with the Cursor Service

The Cursor Service allows you to create a stand-alone rowset by first defining the column information associated with the rowset and then opening it. Local rowsets can be used as data buffers or to hold and share local temporary data in an application. Indexes can be created on local rowsets to support sort, filter, and find operations. All data-manipulation operations are available on local rowsets.

The following code sample shows the creation of a Recordset object (and the underlying rowset), defining the columns and assigning values to each:

Private Sub CreateRs_Click()
   Dim rsNew As New ADODB.Recordset

rsNew.Fields.Append "job_id", adSmallInt
   rsNew.Fields.Append "job_desc", adVarchar
   rsNew.Fields.Append "min_lvl", adTinyInt
   rsNew.Fields.Append "max_lvl", adTinyInt
rsNew.Open

rsNew.AddNew
   rsNew.Fields("job_id").Value = 100
   rsNew.Fields("job_desc").Value = "GPM"
   rsNew.Fields("min_lvl").Value = 12
   rsNew.Fields("max_lvl").Value = 15
rsNew.Update
End Sub

See Also

Chapter 4: Rowsets | Chapter 5: Updating Data in Rowsets | Creating Rowsets


Hierarchy Reshaping

The Cursor Service is used in the background on the client side to reshape hierarchies out of existing cursors that have been created using the Shape Provider. Reshaping can occur only on hierarchical rowsets created from the Shape provider in a single session. It is also possible to create hierarchies from existing hierarchies with different properties on the component cursors by creating the building-block cursors of a hierarchy, setting the properties on each cursor, and then building the hierarchy by using the Reshape feature.

For more information about the SHAPE command, see "Shape Commands in General" or "Formal Shape Grammar" in the ADO Programmer's Reference.

For more information about the Data Shaping Service, see either the Data Shaping Service for OLE DB documentation or its complement in the ADO Programmer's Reference, "Microsoft Data Shaping Service for OLE DB."

See Also

Hierarchical Rowsets


Updating and Refreshing Data

For applications using client rowsets over SQL data, the Cursor Service provides the ability to send changes back to the provider and to refresh the data in local rowsets with current data from the provider. Both the update and the refresh operations can be applied to one or more rows simultaneously and can be coupled in a single user command. These data synchronization operations ensure consistency of multitable rowsets if the structure of the join relation between the participating tables is known.

See Also

IRowsetRefresh::RefreshVisibleData | IRowsetUpdate::Update


Update Functionality

To send updates and retrieve updated information, the Cursor Service keeps track of synchronization information. Upon rowset creation, the Cursor Service will attempt to obtain a pointer to the session object from which the rowset was created. Subsequently, the cursor's session can be set to a new value or set to null to disconnect the rowset.

The Cursor Service attempts to retrieve necessary metadata information from the cursor's IColumnsRowset interface. If necessary, the provider obtains additional extended metadata information through implementation-specific means and saves it to the rowset's IColumnsRowset interface for future use—in this case, for the update request.

Requested updates are handled using the IRowsetUpdate interface. When the Cursor Service's IRowsetUpdate::Update method is called, the session object is identified and row handles are passed to IRowsetUpdate::Update. The provider determines the changes to be made to each row and the data to be used for optimistic-searched updates.

The provider submits updates to the data store. For rows successfully updated, the underlying values are set, as is the status to DBROWSTATUS_S_OK, or to DBROWSTATUS_E_DELETED for deleted rows. For rows not successfully updated, the service passes DBROWSTATUS_S_NOCHANGE to prevent a change of the row status but still sets the underlying value.

The Cursor Service updates its internal values. For any rows whose status is DBROWSTATUS_S_OK, the rowset discards any pending changes and updates its original values. For any rows whose status is DBROWSTATUS_E_DELETED, it deletes the row from its caches.

See Also

IColumnsRowset | IRowsetUpdate::Update | Session Objects


Refreshing Data

The Refresh function updates the rowset with the most recent data available from the provider. The scope of the Refresh command could be a large data field, a row, a set of rows, or the entire rowset. To perform a refresh, the update service requires information about the base tables used to produce the rowset and about the base tables' primary keys.

See Also

Refreshing Row Data


Optimistic Conflict Resolution

For each row that has changed in a rowset, the Cursor Service maintains a copy of the original row fetched from the provider. When the updates are sent to the server, the synchronization service compares original values with the current values on the server to verify whether or not the row has changed since the data was fetched. The synchronization service compares the values using a combination of key columns, row timestamps, changed fields, and sometimes the entire row.

For rows successfully updated, the synchronization service sets the underlying value and sets the status to DBROWSTATUS_S_OK, or to DBROWSTATUS_E_DELETED for deleted rows. For rows not successfully updated, the synchronization service passes DBROWSTATUS_S_NOCHANGE to prevent a change of the row status but still sets the underlying value.

If a conflict is detected, the status for that row changes to indicate an update conflict. You have the option to get the current values for the conflicting row and compare them with the original row values and the changed values in the rowset. After you resolve the conflict, the update can be submitted again, this time using the current values to detect new update conflicts. The update service has the option to get current data for all conflicting rows in a batch. It also allows you to control how the local data is updated when conflicts are detected, to match the transaction model implemented by the application.

The Cursor Service updates its internal values. For any rows whose status is DBROWSTATUS_S_OK, the rowset discards any pending changes and updates its original values. For any rows whose status is DBROWSTATUS_E_DELETED, it deletes the row from its caches.

For additional information about updating and refreshing data, see the technical article "Optimistic Remote Updating in the Client Cursor Engine."

See Also

Updating Behavior


Invoking the Cursor Service

The Cursor Service is an underlying technology that is hidden from the ADO user. Each occurrence of a data fetch operation will automatically load the data as a recordset into the Cursor Service. For the OLE DB developer, the Cursor Service is invoked using the IRowset interface. Invoking the Cursor Service is illustrated in the Data Fetch example.

See Also

IRowset


Using the Cursor Service with Other Providers

As an OLE DB service component, the Cursor Service is frequently used in conjunction with other service components. Indeed, unless you want to create an empty recordset, other providers are necessary to supply the rowset data to load into the Cursor Service.

OLE DB Remoting Provider

The OLE DB Remoting Provider is an OLE DB service provider that allows client applications to retrieve and update rowsets from a remote server as if they were on the local computer. When integrated into a scenario that employs your data provider and other service components, it allows access to remote data while giving your application the same functionality as if the data were local. The Cursor Service provides this functionality. It buffers a remoted rowset in its temporary table structures, giving the same appearance as a rowset obtained locally.

For detailed information about OLE DB remoting services, see the OLE DB Remoting Provider, or for more information about using the Remoting Provider within ADO, see the "Microsoft OLE DB Remoting Provider" topic in the ADO Programmer's Reference.

Data Shaping Service for OLE DB

The Data Shaping Service for OLE DB allows an application to create relationships that had not previously existed among keys, fields, or recordsets. Using the Cursor Service, the Data Shaping Service shapes the data retrieved from your provider in a hierarchical format you can specify. The Cursor Service, invoked using the IRowset interface, can then modify the data by creating a relationship between the child and parent, inserting or deleting recordsets as needed or creating new aggregations.

For detailed information about the data shaping (hierarchy) function, see the Data Shaping Service for OLE DB documentation. For more information about data shaping within ADO, see the "Microsoft Data Shaping Service for OLE DB" topicin the ADO Programmer's Reference.

OLE DB Persistence Provider

Once data from the original data store is saved to a file on your computer, you can use the Persistence Provider to load that data into your application for viewing or modifying. For the load process, your application opens an unpopulated recordset and identifies the specific local file. The Persistence Provider is created to load the locally saved data into the Cursor Service. After the rowset has been wrapped in a recordset, its state information and metadata are exposed.

The following illustration shows the Cursor Service loading process from a persisted file.

For detailed information about the OLE DB Persistence Provider, see the OLE DB Persistence Provider documentation. For more information about using the Persistence Provider within ADO, see the "Microsoft OLE DB Persistence Provider" topic in the ADO Programmer's Reference.


Behaviors Specific to the Cursor Service

Updating in the Cursor Service

The Cursor Service can customize updating by using the UpdateCriteria property as exposed through ADO. Update options include KeyField only, KeyField and modified fields, KeyField and all other fields, and KeyField and Timestamp. Custom resync commands can be issued. Properties reflecting these update options can be found in Appendix C: Supported Properties of the Cursor Service.

See Also

IRowsetResynch


Limitations of the Cursor Service

Updating

Updating is limited to SQL-based data, such as SQL Server, Microsoft® Access, or Oracle data.

Row Locking

Because updating capabilities of the Cursor Service are optimistic-concurrency–based, row locking is not supported.

See Also

Chapter 5: Updating Data in Rowsets | Performing Local Updates with the Cursor Service


Appendix A: Installation Notes

The Microsoft Cursor Service for OLE DB is shipped with the Microsoft Data Access Components and requires no special installation procedures. It is supported by Microsoft OLE DB version 2.0 and later.


Appendix B: Supported Interfaces of the Cursor Service

The Cursor Service for OLE DB supports the interfaces listed in the following table.

Interfaces | Description
IAccessor | Used to manage accessors.
IChapteredRowset | Used to manage chapter rowsets.
IColumnsInfo | Determines basic column information such as number of columns, type, and labels.
IColumnsRowset | Used to retrieve extended metadata information such as BASETABLENAME, BASECOLUMNNAME, and KEYCOLUMN.
IConvertType | Provides information about the availability of type conversions on a command, rowset, or row.
IDBAsynchStatus | Consumers can ask for status or cancel operations when asynchronous data source object initialization or asynchronous rowset generation or population has been requested.
IParentRowset | Used to retrieve child rowsets from a hierarchical rowset.
IRowset | Base rowset interface, providing methods for fetching rows sequentially, getting the data from those rows, and managing rows.
IRowsetChange | Used to update the values of columns in existing rows, delete existing rows, and insert new rows.
IRowsetChapterMember | Detects whether or not a row is a member of a chapter.
IRowsetFind | Finds a row within a rowset that matches a specified value.
IRowsetIdentity | Indicates whether row instance identity is implemented on the rowset and enables testing for row identity. This means that any two row handles representing the same underlying row will always reflect the same data and state.
IRowsetInfo | Provides information about a rowset.
IRowsetLocate | Fetches rows at approximate positions in the rowset.
IRowsetRefresh | Retrieves values for rows that are currently visible to a transaction.
IRowsetResynch | Discards the entire row and any changes to that row.
IRowsetScroll | Enables consumers to fetch rows at approximate positions in the rowset.
IRowsetUpdate | Enables consumers to delay the transmission of changes made with IRowsetChange to the data store. For specific information about the available methods, see: IRowsetUpdate::Update IRowsetUpdate::Undo IRowsetUpdate::GetOriginalData IRowsetUpdate::GetRowStatus
IRowsetView | Creates or applies a view to an existing rowset.
IViewChapter | Creates or applies a view to an existing chapter rowset.
IViewFilter | Restricts the contents of a rowset to rows matching a set of conditions.
IViewSort | Applies a sort order to a view.


Appendix C: Supported Properties of the Cursor Service

The Cursor Service for OLE DB supports the following properties, in addition to what your provider supports. To find out more about uncovering your provider's properties, see "Getting Property Values" in the OLE DB Programmer's Reference.

Property Description
DBPROP_ADC_AUTORECALC Property Set: DBPROPSET_ADC
Type: Long
Default: 1
Typical R/W: R/W

Specific to rowsets created with the Data Shaping Service.

Determines when the Data Shaping Service recalculates aggregate and calculated columns within a generated hierarchical rowset by using a shape command.

Options: recalculate values (default), or calculate only upon initial fetch of the rows into the hierarchy.

The enumeration values for this property are as follows:

adRecalcUpFront
Value=0
Calculates when referencing the value for the first time.

adRecalcAlways
Value=1
Recalculates values when the Data Shaping Service determines that values have changed, if those values are dependent on the calculated column.
DBPROP_ADC_BATCHSIZE Property Set: DBPROPSET_ADC
Type: Long
Default: 15
Read/Write: R/W

When possible, allows update requests sent to the data store to be batched, reducing the number of round-trips.

This is done when using IRowsetUpdate::Update with more than one HROW. Exposed in ADO using adLockBatchOptimistic locktype.

The BatchSize property defaults to 15 updates and can be set to optimize performance. Increasing the value of BatchSize increases the number of statements per batch.

Only Microsoft® SQL Server™ will support BatchSize > 1. For all other data sources, the BatchSize property is ignored.

Setting BatchSize to a higher number will cause a larger number of updates to be sent in each batch, which in turn will reduce round-trips.

Limitations
The batch must not exceed the command-line length of the data source object.

If the length of the SQL string being submitted becomes too large for the provider to manage or if the number of column-value parameters used exceeds the maximum defined for the provider, an error may be returned.
DBPROP_ADC_CACHECHILDROWS Property Set: DBPROPSET_ADC
Type: VT_BOOL
Default: True
Read/Write: R/W

Specific to rowsets created with the Data Shaping Service.

When using a parameterized hierarchy, the child recordsets are fetched on demand when the user attempts to access the particular child chapter. Once a chapter has been fetched, the Shaping provider (by default) will not refetch it if it is accessed a second time.

This property allows you to control this behavior. You can choose to requery for the child rows each time the chapter is accessed. Not only can updated values be returned from this requery, but new rows can be added and other rows deleted.

When this property is set to True, the Cursor Service reads rows for the child rowset only once; thereafter, it rereads from the cache of rows.

When this property is set to False, the Cursor Service reexecutes the child command to read rows from the data store and uses the new rows for each.

Limitations
Bookmarks and absolute positions are negated by the requery action.

Works only with Cursor Service version 2.1.
DBPROP_ADC_CEVER Property Set: DBPROPSET_ADC
Type: Long
Default: n/a
Read/Write: R

States the version of the Cursor Service being used.
DBPROP_ADC_CUSTOMRESYNCH Property Set: DBPROPSET_ADC
Type: VT_BSTR
Default: Empty
Read/Write: R/W

Allows you to define the command to be used when resynchronizing a row or rows in a multitable join result set.

This property must follow these rules:

  • Cursor Service rowset properties Update Table, Update Catalog, and Update Schema must be defined for the table that will have only unique rows in the result set (usually the Many table of a one-to-many join).
  • The command being executed must have exactly the number of parameters as there are key columns in the primary table (specified by the Update properties), and they must be in the same order as they exist in the result set. Key columns are visible when examining the ADO extended field properties.
  • The result of the command execution must be one row, and it must have the same "shape" as the original recordset.

Failure to follow any one of these rules will generate a run-time error. The Cursor Service will not actually parse this command, so it must exist in a format that is directly recognized by the underlying provider or data store.

Limitations
Works only with Cursor Service version 2.1.
DBPROP_ADC_MAINTAINCHANGESTATUS Property Set: DBPROPSET_ADC
Type: VT_BOOL
Default: False
Read/Write: R/W

Specific to rowsets created with the Persistence Provider. Introduced with MDAC 2.1.

Determines whether or not to get the changed rows that might be pending an update. If True, then for every row the Cursor Service reads from the original rowset, it checks the row status (IRowsetUpdate::GetRowStatus) and stores any changes. If False or if the property does not exist, the Cursor Service will not read the row status for the row.

Note When the Cursor Service reads the Persistence Provider's rowset, it asks for row changes and will maintain the change status for each row. Because it is not supported by other rowsets, changes that are not committed are not read into the Cursor Service's rowset.
DBPROP_ADC_RESHAPENAME Property Set: DBPROPSET_ADC
Type: VT_BSTR
Default: Empty
Read/Write: R

Specific to rowsets created with the Data Shaping Service.

Allows local references in the SHAPE command for reshaping.

The Name property is used to identify each rowset in the namespace implemented by the Shaping Service provider. If the SHAPE command that creates the rowset associates it with an alias, the Data Shaping Service sets the value of the Name property to that alias. If no alias is defined or if the alias conflicts with existing names in the Shape namespace, the Name value is set to a default name.

Rowset naming rules:

  • Use the rowset alias if it is specified in the SHAPE command that creates the rowset.
  • If no alias is specified, use the name of the chapter column associated with the rowset.
  • If no name is specified, a unique name is auto-generated by the Data Shaping Service. The name is dsrowset_<n> where <n> is an appended number, unique for each Shape namespace. The initial name is dsrowset_1.

The appended number is unique for each Shape namespace. For example, if the first collision occurs on orders, the generated name would be orders_1. A subsequent auto-generated name would be dsrowset_2. Next, a collision on customers would generate the name customers_3. If an auto-generated name already exists, the number is appended repeatedly until it ensures uniqueness. For example, if customer_3 already exists, the auto-generated name will be customer_3_3.

The rowset name can be used as a caption for the rowset. If unique, it is intended for use in Reshape commands.

Limitations
Works only with Cursor Service version 2.1.
DBPROP_ADC_SAVEMODE Property Set: DBPROPSET_ADC
Type: Long
Default: version-specific
Read/Write: R/W

Determines the format of the data stream when saving a recordset to disk, providing backward compatibility when saving a recordset.

The default for each version corresponds to the version number:

  • MDAC 2.1 uses Save Mode = 21
  • MDAC 2.0 uses Save Mode = 20

Only files saved as 20 can be used from MDAC 2.0.
DBPROP_ADC_UNIQUECATALOG

DBPROP_ADC_UNIQUESCHEMA

DBPROP_ADC_UNIQUETABLE

 Property Set: DBPROPSET_ADC
Type: VT_BSTR
Default: Empty
Read/Write: R/W

The Unique Table, Unique Schema, and Unique Catalog properties (Unique*) should be used for rowsets created from a join query over multiple base tables. The value of the Unique* properties is the qualified name of a base table that has at most one copy of each row in each row of the rowset (also called the most-many table).

These properties can be set only on updatable recordsets that have successfully retrieved the update metadata, including base table names. The Unique Table, Unique Schema, and Unique Catalog values should match the Base Table, Base Schema, and Base Catalog values of one of the base tables associated with the recordset.

The Unique Table property is required in order to build a qualified name. Follow these rules to identify the Unique Table:

  • If the Unique Schema and Unique Catalog names exist, they are matched, in this order, against the same properties of qualified base table names.
  • If a unique match is found, the unique table is identified. Otherwise, a run-time error is generated.

When the Unique Table property is set, the recordset switches to Row Fix-up mode and implements row fix-up semantics for data-manipulation operations. The primary key of this table is promoted as primary key of the entire cursor.

In Row Fix-up mode, the columns holding the primary keys of all base tables are read-only. Updates and inserts are restricted to columns of the unique table. A delete operation deletes the corresponding row only in the base unique table. If the ResyncCommand property is set, resync operations use this command to perform resynchronization.

Limitations
Works only with Cursor Service version 2.1.
DBPROP_ADC_UPDATECRITERIA Property Set: DBPROPSET_ADC
Type: Long
Default: 2
Read/Write: R/W

Sets update criteria to define which fields are used in the WHERE clause to handle collisions occurring during the update process. Can be one of the following four values:

adCriteriaKey
Value=0
Update criteria use just the key fields to check for changes.

adCriteriaAllCols
Value=1
Update criteria use all columns in the result set to check for changes.

adCriteriaUpdCols
Value=2 (default)
Update criteria use just the modified columns in the result set to check for collisions.

adCriteriaTimeStamp
Value=3
Update criteria use the key fields and the timestamp field in the result set to check for collisions.

Comments
If you set this property to adCriteriaTimeStamp and there is no qualifying timestamp column, the Cursor Service behaves as if you had set adCriteriaCols, but the property remains unchanged.

Once a collision has been detected, the application must control the transaction semantics on the server to resolve the problem.
DBPROP_ADC_UPDATERESYNC Property Set: DBPROPSET_ADC
Type: Long
Default: 1
Read/Write: R/W

Defines the behavior of the BatchUpdate method, controlling whether the batch update operation is followed by an implicit resync operation. The scope of the implicit resync can be the rows that have conflicts or the entire batch.

If a provider cannot support the request update type, the provider should return an error.

The following are valid values for this property. Providers can extend this list as appropriate, and not all providers have to support all update-resync modes.

adResyncNone
Value=0
No auto-resynchronization performed.

adResyncAutoIncrement
Value=1
Auto-resynchronization autoincrement columns for inserted rows.

adResyncConflicts
Value=2
Auto-resynchronization performed on rows with conflicts.

adResynchUpdates
Value=4
Auto-resynchronization performed on any successfully updated row.

adResyncInserts
Value=8
Auto-resynchronization performed on any newly inserted rows.

adResyncAll
Value=15
Auto-resynchronization performed on all columns for all changes; acts as a shortcut for all the others.

Limitations
Works only with Cursor Service version 2.1.

Comments
Because timestamps are usually server-generated and change with every update, adResyncUpdates is crucial for timestamps. If your first successful update makes the local timestamp stale, the next update attempt on the same row will fail because it violates the concurrency standard.

The adRsyncAutoIncrement, adResyncUpdates, adResyncInserts, and adResyncConflicts values can be used simultaneously.

The adUpdateResyncInsert and adUpdateResyncConflicts values can be used simultaneously as values of Update Resync.

The resynchronization operation performed in adResyncConflicts is very different from the other two because it stores the resync values as underlying values but does not override pending changes.
DBPROP_ASYNCHFETCHSIZE Property Set: DBPROPSET_ADC
Type: Long
Default: 15
Typical R/W: R/W

When asynchronously fetching data, the Cursor Service needs to know how many rows to include in each fetch. The default for this is 15 rows. Larger values can reduce round-trips but might cause the packet to get corrupted, depending on network stability.
DBPROP_ASYNCHPREFETCHSIZE Property Set: DBPROPSET_ADC
Type: Long
Default: 50
Typical R/W: R/W

The purpose of asynchronous fetching is largely to enable quick user-interface response time for a client application. The Initial Fetch Size property is used to determine the number of rows to be fetched initially, before creating the background thread to fetch on. If the result set size is smaller than the initial fetch size, the Cursor Service never creates the background thread.
DBPROP_ASYNCHTHREADPRIORITY Property Set: DBPROPSET_ADC
Type: Long
Default: 3
Typical R/W: R/W

When asynchronously fetching data, depending on the environment in which the background fetch is being used, the user might want the background thread to use more or less of the processor time in order to fetch data. A higher priority background thread can mean that the full result set is available sooner, at a potential cost of user-interface responsiveness. Using less processor time may take longer but will have a lower impact on the user interface.

The following values are available for background thread priorities:

adPriorityLowest
Value=1
Two points lower than the default.

adPriorityBelowNormal
Value=2
Slightly lower priority than the default.

adPriorityNormal
Value=3
Standard priority at which a thread operates.

adPriorityAboveNormal
Value=4
Slightly higher than default thread priority.

adPriorityHighest
Value=5
Thread priority above typical.
DBPROP_BOOKMARKSKIPPED Property set: DBPROPSET_ROWSET
Type: VT_BOOL
Typical R/W: R/W

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.

DBPROP_BOOKMARKS Property set: DBPROPSET_ROWSET
Type: VT_BOOL
Typical R/W: R/W

Determines whether or not the rowset supports bookmarks.
DBPROP_CANFETCHBACKWARDS Property set: DBPROPSET_ROWSET
Type: VT_BOOL
Typical R/W: R/W

Whether or not the rowset can fetch backward.
DBPROP_CANSCROLLBACKWARDS Property set: DBPROPSET_ROWSET
Type: VT_BOOL
Typical R/W: R/W

Whether or not the rowset can scroll backward.
DBPROP_CHANGEINSERTEDROWS Property set: DBPROPSET_ROWSET
Type: VT_BOOL
Typical R/W: R/W

Whether or not an inserted row can be changed. A newly inserted row is defined to be a row for which the insertion has been transmitted to the data source object, as opposed to a pending insert row.

DBPROP_IMMOBILEROWS Property set: DBPROPSET_ROWSET
Type: VT_BOOL
Typical R/W: R/W

Whether or not the inserted or updated rows obey the ordering criteria of the rowset, if the rowset is ordered. If the rowset is not ordered, 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_ORDEREDBOOKMARKS Property set: DBPROPSET_ROWSET
Type: VT_BOOL
Typical R/W: R/W

Whether bookmarks can be compared to determine the relative position of their associated rows in the rowset, or only for equality.

DBPROP_OWNINSERT Property set: DBPROPSET_ROWSET
Type: VT_BOOL
Typical R/W: R/W

Whether or not 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.
DBPROP_OWNUPDATEDELETE Property set: DBPROPSET_ROWSET
Type: VT_BOOL
Typical R/W: R/W

Whether or not the rowset can see its own updates and deletes.

DBPROP_REMOVEDDELETED Property set: DBPROPSET_ROWSET
Type: VT_BOOL
Typical R/W: R/W

Whether or not this provider removes the rows the rowset detects as having been deleted. This is determined by the DBPROP_OWNUPDATEDELETE and DBPROP_OTHERUPDATEDELETE properties.
DBPROP_RETURNPENDINGINSERTS Property set: DBPROPSET_ROWSET
Type: VT_BOOL
Typical R/W: R

Whether or not the methods that fetch rows, such as IRowset::GetNextRows, can return rows that have been inserted in delayed update mode but for which IRowsetUpdate::Update has not yet been called.
DBPROP_STRONGIDENTITY Property set: DBPROPSET_ROWSET
Type: VT_BOOL
Typical R/W: R

Whether the handles of newly inserted rows can be compared as specified by DBPROP_LITERALIDENTITY, or if the handles of newly inserted rows can be compared successfully. A newly inserted row is defined as a row for which an insertion has been transmitted to the data source object, as opposed to a pending insert row.


SQL Server Provider

OLE DB Provider for SQL Server

OLE DB Provider for SQL Server

SQLOLEDB, the Microsoft® OLE DB Provider for SQL Server, exposes interfaces to consumers wanting access to data on one or more computers running Microsoft SQL Server™.

When developing an OLE DB consumer, select a provider developed for the data source to consume. Use SQLOLEDB to develop an optimized OLE DB consumer for SQL Server databases.

SQLOLEDB is an OLE DB version 2.6–compliant provider. The provider passes the command statements (such as SQL-92 and Transact SQL) through to the server. Invalid commands are rejected by the server.

For complete documentation of the OLE DB Provider for SQL Server, see the SQL Server OLE DB Programmer’s Reference in the SQL Server Books Online (Search for “SQL Server OLE DB Programmer’s Reference”).

To view the SQL Server Books Online, click this icon


OLE DB Test Tools

OLE DB Test Tools Overview

OLE DB 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.

The Microsoft® Local Test Manager (LTM) is an easy-to-use tool that provides a single point of control for automated testing of OLE DB providers. It accompanies the Microsoft Data Access Components (MDAC) SDK and includes an online help file. LTM can now be used with Microsoft Visual Studio®, Microsoft Visual Basic®, and Microsoft Visual J++® development environments.

Conformance testing strategies and details are discussed in the MDAC SDK topic "Conformance Testing in OLE DB."

The QuikTest Test Module now has its own topic in this section of the MDAC SDK.

Rowset Viewer is offered as a sample for experimentation and as a tool for immediate use. For more information about the sample, see "Rowset Viewer Sample." To run the tool, click Start, point to Programs, point to Platform SDK, point to Data Access, and click RowsetViewer, or run RowsetViewer.exe from Windows Explorer. For more information about its features, see "Rowset Viewer Menus and Toolbar" in the OLE DB Samples section.

What's New

Document type | Title | Status/Location
MDAC SDK | Trace Provider | Removed pending further work.


Conformance Testing in OLE DB

The OLE DB conformance tests help provider writers confirm that their implementations match the OLE DB specification so that they can develop high-quality, interoperable, compliant OLE DB providers. Using a common set of tests allows developers to implement consistent behavior across providers.


What's New

Starting in MDAC 2.5, the Local Test Manager online help also has been updated and enhanced to include procedural help and a user's guide.

A number of features have been added to the Local Test Manager (LTM) tool, including:

  • Saving the test run output to a file
  • Setting a breakpoint using LTM
  • Searching within the Output pane
  • Refreshing the Test Selection pane
  • Automatic coupling with development tools' debuggers
  • A simpler architecture
  • Test suites

The LTM online help file explains these features in depth. To view this help file, start LTM.exe, open the Help menu, and click Help Topics.


Additional Resources

The following technical articles and white papers are posted on the "Technical Materials" page of the Universal Data Access Web site at http://www.microsoft.com/data/techmat.htm or included with the MDAC SDK.

The presence and location of these articles are subject to change with each MDAC release. Other articles can be found in the Microsoft Developer Network (MSDN™) Library. Detailed articles concerning known limitations or workarounds can be found in the MSDN Knowledge Base.

Documents

OLE DB Minimum Levels of Consumer and Provider Functionality (Date written/posted: 1997; updated: April, 2000)

References

OLE DB Programmer's Reference

ADO Programmer's Reference

Web sites

http://www.microsoft.com/data/oledb

http://www.microsoft.com/data/ado


Introduction to Conformance Testing

The conformance tests for OLE DB are meant to supplement a provider writer's own testing, not to replace it. They form just a part of the complete testing suite. Comprehensive testing of any OLE DB provider includes the following:

  • Interface tests
  • Tests for provider-specific behavior
  • Stress tests
  • Performance tests
  • Multiuser testing
  • Application tests (using the provider to build applications)
  • Testing with a variety of consumers

The conformance tests contribute to the first task (OLE DB interface testing) and to the last task (consumer testing, through ADO), but not the others. The conformance tests include test variations for normal usage as well as some error and boundary cases. They do not check provider-specific behavior, stress scenarios, performance, multiuser cases, application scenarios, or low-resource conditions.


Conformance Test Life Cycle

The following table describes the three stages in the life cycle of conformance tests: unsupported, proposed, and approved.

Stage | Description
Unsupported | Intended as examples. Designed to work with the OLE DB Provider for ODBC Drivers, but might not work correctly with other providers.
Proposed | Updated tests that work with a wide range of providers—for example, read-write, read-only, SQL, and non-SQL. After broader exposure, they will move to the "Approved" category.
Approved | Available for accurate conformance testing.


Conformance Test Sets

Two sets of conformance tests are included in this version of the MDAC SDK: OLE DB interface tests and ADO minimal tests. They are located in the MDAC installation directory under \Conformance\Tests. For complete file and path information, see Files Used for Conformance Testing in this guide.

You can use the TableDump utility to build your own tests. The files generated from this utility identify provider-specific information.

Sample test modules are available but only as examples of test code. They are not supported.

OLE DB Interface Tests

The OLE DB interface tests check providers' compliance to standards in the OLE DB Programmer's Reference.

OLE DB leveling identifies the critical interfaces and behaviors (properties) required for providers and expected by consumers. Conformance testing of these interfaces identifies base consumer functionality and minimum provider functionality, which helps you write applications that support the natural functionality of native data stores and that extend built-in services. (For more information about OLE DB leveling, see the technical article OLE DB Minimum Levels of Consumer and Provider Functionality.)

The OLE DB interface tests were originally written to test the Microsoft OLE DB Provider for ODBC Drivers, which is also included in the MDAC SDK. The Level 0 (minimum) provider interfaces have been modified to handle a wide range of providers and are located in the \Proposed subdirectory.

Only the Level 0 (minimum) interface tests are supported. Tests for the other interfaces are available in the \Unsupported subdirectory.

ADO Minimal Tests

The ADO minimal tests are a small set of tests, at least one per ADO method, which check whether ADO statements work correctly against a provider. There are tests for each ADO method, including methods used for updates and for transactions. The tests are grouped so that you can run just the Level 0 tests, which do not require updatability or transactions; or you can run the full set of ADO tests.

TableDump Utility

The OLE DB and ADO tests use TableDump-generated files to identify provider-specific information and to handle provider variations (such as read-only versus read-write, SQL versus non-SQL, and so forth). The TableDump utility generates initialization files needed by the tests. This utility can be found in the \Conformance\Tools subdirectory. For more information about working with the TableDump utility, see Building a Tool (TableDump or Privlib) or Test in this guide.

Unsupported Sample Test Modules

The MDAC SDK includes some sample test modules in the \Conformance\Tests subdirectory. Each test module is named with an abbreviated form of the interface it tests. These tests 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, you must first build the Private Library (Privlib.lib), which is used by these tests.


Building OLE DB Conformance Tests

You can use your preferred Microsoft development environment—Visual Basic®, Visual Studio®, or Visual J++®—to build conformance tests. The MDAC SDK contains a set of Level 0 conformance tests to test your providers with broad test coverage support. The QuikTest test module is also included. You can generate initialization files using the TableDump utility to create and obtain the provider-required information necessary to run your tests for the desired level of conformance. Use the Local Test Manager (LTM) tool to run your conformance tests.

To build an OLE DB conformance test

  1. Build the PrivLib and the TableDump.
  2. Generate the initialization files.
  3. Establish your provider requirements.
  4. Establish your data requirements.
  5. Use the TableDump Utility with LTM to generate provider information.
  6. Use LTM to modify, run, and debug the test.


OLE DB Level 0 Conformance Tests

There is at least one Level 0 test module for each Level 0 interface, in addition to one test module for data coercion and one test module for simple threading cases. There are a total of 20 Level 0 tests. The names of these tests are abbreviated forms of the interface names.

Test name | Tested interface
IACCESSR | IAccessor
ICLSFACT | IClassFactory and CoCreateInstance
ICOLINFO | IColumnsInfo
ICNVTTYP | IConvertType
IDBCRSES | IDBCreateSession
IDBINIT | IDBInitialize
IDBPRPTS | IDBProperties
IGETDSO | IGetDataSource
IOPENRW | IOpenRowset
IPERSIST | IPersistFile
IROWSET | IRowset
IROWCHNG | IRowsetChange::SetData
IROWDEL | IRowsetChange::DeleteRows
IROWIDEN | IRowsetIdentity
IROWINFO | IRowsetInfo
IROWNEW | IRowsetChange::InsertRow
ISESPRPT | ISessionProperties
THREADS | Threading test (tests free-threaded only, not apartment model)
DATALITE | Data Coercion test


Test Coverage

The Level 0 test modules cover each interface listed in the "Minimum Provider Functionality" section of the technical article OLE DB Minimum Levels of Consumer and Provider Functionality. The modules also cover data coercion and threading.

The tests are organized into test modules (one per interface). Each test module contains test cases, and each test case contains test variations.

All OLE DB test modules use Privlib.lib, which is a set of common Microsoft® Visual C++® functions such as table creation, data comparison, and accessor creation.


Files Used for Conformance Testing

The Local Test Manager tool and the conformance tests are installed in the MDAC SDK installation directory.

File | Location
Proposed OLE DB test sources, include files, and resource files | InstallDirectory\Conformance\Tests\Proposed\OLEDBTests
Unsupported OLE DB test sources, include files, and resource files | InstallDirectory\Conformance\Tests\Unsupported\OLEDBTests
Additional include files | InstallDirectory\Conformance\Include
Libs | InstallDirectory\Conformance\Lib
Source for the test tools (ADOPriv, PrivLib, and TableDump) | InstallDirectory\Conformance\Tools
Local Test Manager | InstallDirectory\bin


Building a Tool (TableDump or Privlib) or Test

It is important to build PrivLib and TableDump before trying to build any OLE DB tests.

To build a tool or test

  1. Using Microsoft® Visual C++®, use Open Workspace on the .dsp. For PrivLib, the .dsp is located in <install directory>\Conformance\Tools\PrivLib.
  2. Add all the include directories and all the lib directories for this version of the SDK to your Visual C++ directory. (To do this in Visual C++, on the Tools menu, click Options and then click Directories.)
  3. Update your system path to include the following directories at the beginning of the path list:

    InstallDirectory\Conformance\Tests\Proposed\OLEDBTests\Include

    InstallDirectory\Conformance\Include

    InstallDirectory\Include

    InstallDirectory\Conformance\Lib

    For Visual C++ 5.0 users, build PrivLib using the default target.

    For Visual C++ 6.x users, change the default target to "privlib - Win32 x86 Debug" and then build.

    To build an OLE DB test on an Intel® machine, build the test using the Debug MBCS target. For Visual C++ 5.0 users, this target should be the default target. For Visual C++ 6.x users, change the default target and then build.

  4. After you have built Privlib.lib, copy it to a directory in your \lib path (set as the default target).
  5. Create an initialization file by using the TableDump program found in the \Tools subdirectory. For more information about the TableDump utility, see "Generating Initialization Files Using the TableDump Utility" in this guide.


Generating Initialization Files Using the TableDump Utility

The TableDump utility creates and obtains all information from the provider needed to run your test for the level of conformance of that provider.

Many differences, limitations, and issues are unique to each provider. It is difficult to develop complete OLE DB conformance tests that are able to handle the broad range of OLE DB providers.

Following are some of the major issues:

  • A provider might have its own provider-specific initialization properties. Standard initialization properties from the Initialization property group can be specified on the connection string.
  • Some providers are read-only.
  • Some providers do not allow table creation.
  • A provider might have its own unique CommandText (if you are testing commands).

To ensure that data is sent and received correctly, the data format must be identified. The TableDump utility provides an initialization file that can be used by all conformance tests. This file contains all necessary information to initialize the provider, understand the provider's command syntax if available, confirm that the data retrieved is correct, and verify all other aspects of the provider meeting a particular conformance level.


Provider Requirements

The following requirements have been imposed to solve the stated problems and still work with the broad range of providers:

  • To connect to your provider, you must first know the initialization properties and values.
  • If you are testing commands, you must know the command syntax for all statements.

    Note Because commands are not in Level 0, you will not use them if you use CONFLEVEL=CONF_LEVEL_0 | CONF_STRICT options. These options specify that all Level 0 interfaces are supported or that only Level 0 interfaces are used when running the tests, respectively.

  • If you are testing commands, you should use the DEFAULTQUERY= statement or require that DEFAULTQUERY be used. When the test attempts to generate a statement for SELECT_ORDERBYNUMERIC and there is no SQL support, the query specified will be used. This query is used by the TableDump utility for getting the data, and all other queries will have the same value as specified with the DEFAULTQUERY= statement.
  • To verify data correctly for all providers, you must obtain all columns and rows of data and place them into the initialization file.


Data Requirements

For complete conformance testing, the following requirements are placed upon the table or data provided to the conformance tests:

  • The table must contain a separate column for every possible supported DBTYPE. This ensures that all types of data are tested and obtained.
  • The table must contain at least 10 rows of data and should be between 60 and 100 rows.
  • The data provided in the table should stress the data type, which can be ColumnSize, Precision, or Scale. The data should have at least one MIN value for every data type and at least one entry with maximum precision for that data type.


Using the TableDump Utility with LTM

OLE DB Test Tools

Using the TableDump Utility with Local Test Manager

TableDump is a simple command-line utility that takes arguments for all its required information. The utility also can be used with Local Test Manager (LTM). When creating a provider alias, specify FILE=filename.ini in the LTM initialization string. When the test runs, it checks for the existence of this keyword and processes the .ini file before beginning any test variations.

Note Arguments must be followed by an equal sign (=) and end in a semicolon (;) to distinguish them from other arguments. Arguments containing a semicolon or equal sign can be enclosed in single quotes, as in the following example:

ProviderString='Data Source=Pubs;User ID=SA;Password=;'

The following table lists the required and optional keywords, values, and descriptions for the TableDump utility.

Keyword/Value | Description
PROVIDER=ProviderName; | Required—the Provider ProgID of the provider to use. TableDump will use ProgIDToCLSID to convert this to a CLSID to obtain an instance of the provider in CoCreateInstance.
TABLENAME=TableName; | Required—the TableName to pass to IOpenRowset. Note Although the argument is called TableName, this does not imply that TableDump works only with relational providers. Here, TableName just indicates the DBID passed to IOpenRowset.
DEFAULTQUERY=DefaultQuery; | Optional—usually not needed. If you want to test command support, this is the default command for all queries used by the tests and PrivLib. All entries in the QUERY section will have this command as the default. You can then modify that section based on your provider's required grammar or command syntax.
OUTPUT=OutputFileName; | Optional—the output file name for the generated .ini file. You can include the entire path, including file name and extension. TableDump will default to "TableDump.out" if this option is not specified.
CREATETABLE=<number of rows in table>; | Optional—the number of rows in the table you are creating. For providers that support table creation and ANSI SQL. After creation, you can populate the table on every test run with data of your choosing.
CREATEINDEX=<column ordinal for the index>; | Optional—the column ordinal for the index in the table you have created. For providers that support table creation and ANSI SQL.


Format of the .ini File

This topic provides a brief description of the .ini file's sections and meanings, along with a sample .ini file. For most providers, the .ini file will not have to be modified because it is generated by the TableDump utility.

[INFO:]

This section contains all provider initialization and other initialization information, including:
  • The provider's name
  • The table name to use in IOpenRowset and/or the default query
In the following sample from the Microsoft® OLE DB Provider for ODBC, the table is Customers and the initialization properties are DBPROP_INIT_DATASOURCE, DBPROP_AUTH_PASSWORD, and DBPROP_AUTH_USERID:
   [INFO] PROVIDER=; TABLENAME=; DEFAULTQUERY=;
   VERSION=1,50,3518,0;
   TABLE=Privlib;  PROVIDER=TestPWProv_VC; TABLENAME=Privlib;
   OUTPUT=c:\bin\TestPWProv_VC.ini; BINDINGTYPE=DBTYPE_VARIANT;

[URL]

This section specifies URL information that the test will use when testing your provider. Options include:
  • DSO
  • Session
  • Rowset
  • Row
  • Stream
Modify this section by inserting the correct information between the quotes. For an example, see the .ini File Example.

[QUERY]

This section contains a list of all the command statements that the test will try to use in the process of testing the provider. TableDump will generate these statements based on the specified default query when the tool is used. This section seldom needs modification.

[COLUMN]

This section lists all columns found on the specified table or a result from the default query. It contains all the information commonly reported from IColumnsInfo, such as type information, precision, scale, and other informational flags to be used when creating and verifying data:
[COLUMN] ColName iOrdinal, TYPE, ulColumnSize, bPrecision,
bScale, dwFlags, ColIDKind, ColIDGuid, ColIDName

[DATA]

This section lists all data found on the specified table or a result from the default query. The format is a 1:1 correspondence to the COLUMN section. The first row of data is for the first row, and the first column refers to the first column in the COLUMN section. All data is represented in the file in {"String"} format, and NULL is represented as {(null)}. When the testing framework reads this file back in, all types will get converted back into the correct type indicated in the COLUMN section. When the column is of type VARIANT, the data value is also prefixed with the VT_TYPE indicator for the specified subtype. To represent a VARIANT with subtype VT_I4 and Value 3, TableDump will produce VT_I4("3") and DBTYPE_VARIANT will be indicated in the COLUMN section. This is for providers that support VARIANT types or those that support the possibility of every VARIANT in different rows being of a different subtype.


.ini File Example

Suppose you want to test a provider with the OLE DB conformance tests and need to generate an .ini file that will be used by all the tests. The following example shows sample provider information and lists the requirements of the .ini file.

Provider information

ProviderName: MSDASQL

DataSource: OLE_DB_NWind_Jet

UserID: admin

Password:

Table: Customers

DefaultQuery: select * from Customers

Information necessary to generate the .ini file

TableDump.exe PROVIDER=MSDASQL; data source=OLE_DB_NWind_Jet;
user id =admin; password=; TABLENAME=Customers;
DEFAULTQUERY="select * from Customers"; OUTPUT=Customers.ini;
ROOT_URL=MSDASQL://dso/session/%s;

The preceding code will create the file Customers.ini.

[INFO] {Provider (Table, <DefaultQuery>)}
{ PROVIDER=MSDASQL (TABLE=Customers, DEFAULTQUERY="select * from Customers")}

[URL] URLInfo
URL_DSO{MSDASQL://dso}
URL_SESSION{MSDASQL://dso/session}
URL_ROWSET{MSDASQL://dso/session/DEFAULT_280_19374}
URL_ROW{MSDASQL://dso/session/DEFAULT_280_19374/1.0000}
URL_STREAM{MSDASQL://dso/session/DEFAULT_280_19374/1.0000}
URL_COMMAND{MSDASQL://dso/session/select * from DEFAULT_280_19374}

[QUERY] {SQLEnumIdentifier("Query")}
{SELECT_ALLFROMTBL("select * from Customers")}
{SELECT_SEARCHABLE("select * from Customers")}
{SELECT_UPDATEABLE("select * from Customers")}
{SELECT_ABCANDCOLLIST("select * from Customers")}
{SELECT_DISTINCTCOLLISTORDERBY("select * from Customers")}
{SELECT_REVCOLLIST("select * from Customers")}
{SELECT_COLLISTGROUPBY("select * from Customers")}
{SELECT_COLLISTWHERELASTCOLINSELECT("select * from Customers")}
{SELECT_REVCOLLISTFROMVIEW("select * from Customers")}
{SELECT_COUNT("select * from Customers")}
{SELECT_COLLISTSELECTREVCOLLIST("select * from Customers")}
{SELECT_EMPTYROWSET("select * from Customers")}
{SELECT_COLLISTFROMTBL("select * from Customers")}
{SELECT_COLLISTTBLUNIONTBL("select * from Customers")}
{SELECT_COLLISTORDERBYCOLONECOMPUTE("select * from Customers")}
{SELECT_CROSSPRODUCT("select * from Customers")}
{SELECT_LEFTOUTERJOIN("select * from Customers")}
{SELECT_RIGHTOUTERJOIN("select * from Customers")}
{SELECT_FROMTBLWITHPARAMS("select * from Customers")}
{SELECT_CHANGECOLNAME("select * from Customers")}
{SELECT_DUPLICATECOLUMNS("select * from Customers")}
{SELECT_REVERSEDUPLICATECOLUMNS("select * from Customers")}
{SELECT_MAXCOLINQUERY("select * from Customers")}
{SELECT_COMPUTEDCOLLIST("select * from Customers")}
{SELECT_UPDATEABLEALLROWS("select * from Customers")}
{SELECT_ORDERBYNUMERIC("select * from Customers")}
{INSERT_1ROW("select * from Customers")}
{INSERT_ALLWITHPARAMS("select * from Customers")}
{SELECT_ALL_WITH_SEARCHABLE_AND_UPDATEABLE("select * from Customers")}
{SELECT_ALL_WITH_BLOB_AT_END("select * from Customers")}
{NO_QUERY("select * from Customers")}
{SELECT_ALL_WITH_FOR_BROWSE("select * from Customers")}

[COLUMN] {ColName(iOrdinal, TYPE, ulColumnSize, bPrecision, bScale, dwFlags)}
{CustomerID(1,DBTYPE_STR ,5,255,255,DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_MAYBENULL)}
{CompanyName(2,DBTYPE_STR ,40,255,255,DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_MAYBENULL)}
{ContactName(3,DBTYPE_STR ,30,255,255,DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_MAYBENULL)}
{ContactTitle(4,DBTYPE_STR ,30,255,255,DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_MAYBENULL)}
{Address(5,DBTYPE_STR ,60,255,255,DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_MAYBENULL)}
{City(6,DBTYPE_STR ,15,255,255,DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_MAYBENULL)}
{Region(7,DBTYPE_STR ,15,255,255,DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_MAYBENULL)}
{PostalCode(8,DBTYPE_STR ,10,255,255,DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_MAYBENULL)}
{Country(9,DBTYPE_STR ,15,255,255,DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_MAYBENULL)}
{Phone(10,DBTYPE_STR ,24,255,255,DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_MAYBENULL)}
{Fax(11,DBTYPE_STR ,24,255,255,DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_MAYBENULL)}

[DATA] {<Type>("data")}
{("ALFKI")},{("Alfreds Futterkiste")},{("Maria Anders")},{("Sales Representative")},{("Obere Str. 57")},{("Berlin")},{(null)},{("12209")},{("Germany")},{("030-0074321")},{("030-0076545")},
{("ANATR")},{("Ana Trujillo Emparedados y helados")},{("Ana Trujillo")},{("Owner")},{("Avda. de la Constitución 2222")},{("México D.F.")},{(null)},{("05021")},{("Mexico")},{("(5) 555-4729")},{("(5) 555-3745")},
{("ANTON")},{("Antonio Moreno Taquería")},{("Antonio Moreno")},{("Owner")},{("Mataderos 2312")},{("México D.F.")},{(null)},{("05023")},{("Mexico")},{("(5) 555-3932")},{(null)},
{("AROUT")},{("Around the Horn")},{("Thomas Hardy")},{("Sales Representative")},{("120 Hanover Sq.")},{("London")},{(null)},{("WA1 1DP")},{("UK")},{("(171) 555-7788")},{("(171) 555-6750")},
{("BERGS")},{("Berglunds snabbköp")},{("Christina Berglund")},{("Order Administrator")},{("Berguvsvägen 8")},{("Luleå")},{(null)},{("S-958 22")},{("Sweden")},{("0921-12 34 65")},{("0921-12 34 67")},
{("BLAUS")},{("Blauer See Delikatessen")},{("Hanna Moos")},{("Sales Representative")},{("Forsterstr. 57")},{("Mannheim")},{(null)},{("68306")},{("Germany")},{("0621-08460")},{("0621-08924")},
{("BLONP")},{("Blondel père et fils")},{("Frédérique Citeaux")},{("Marketing Manager")},{("24, place Kléber")},{("Strasbourg")},{(null)},{("67000")},{("France")},{("88.60.15.31")},{("88.60.15.32")},
{("BOLID")},{("Bólido Comidas preparadas")},{("Martín Sommer")},{("Owner")},{("C/ Araquil, 67")},{("Madrid")},{(null)},{("28023")},{("Spain")},{("(91) 555 22 82")},{("(91) 555 91 99")},
{("BONAP")},{("Bon app'")},{("Laurence Lebihan")},{("Owner")},{("12, rue des Bouchers")},{("Marseille")},{(null)},{("13008")},{("France")},{("91.24.45.40")},{("91.24.45.41")},
{("BOTTM")},{("Bottom-Dollar Markets")},{("Elizabeth Lincoln")},{("Accounting Manager")},{("23 Tsawassen Blvd.")},{("Tsawassen")},{("BC")},{("T2F 8M4")},{("Canada")},{("(604) 555-4729")},{("(604) 555-3745")},
{("BSBEV")},{("B's Beverages")},{("Victoria Ashworth")},{("Sales Representative")},{("Fauntleroy Circus")},{("London")},{(null)},{("EC2 5NT")},{("UK")},{("(171) 555-1212")},{(null)},

*For simplicity, the rest of this code has been omitted.


Building and Running the ADO Minimal Tests

The three tests described in the following table are available for testing your consumers and providers in ADO.

ADO test | Description
Level 0 Conformance Tests Mapping ADO properties and methods to OLE DB interfaces provides the basis for grouping ADO properties and methods into the conformance levels.

Before running ADO Level 0 conformance tests, you must manually modify the .ini file generated by TableDump as shown in the .ini file example. Modify the SELECT_EMPTYROWSET line under [QUERY] by adding a query that will yield no results. For example:

{SELECT_EMPTYROWSET("select * from mytable where 1=2")}

If you do not modify this line, end-of-file and beginning-of-file tests will fail.

For more information, see the technical article Mapping ADO Methods to OLE DB Interfaces.
Update Conformance Tests | These tests are provided to confirm that ADO Update statements will work with compliant updatable providers.
Transaction Conformance Tests | These tests are provided to confirm that ADO Transaction statements will work with compliant providers that support transactions.


Building ADOPriv.dll

Before running the ADO tests, you must build ADOPriv.dll.

To build ADOPriv.dll

  1. Using Microsoft® Visual C++®, use Open Workspace on ADOPriv.dsp in <install directory>\Conformance\Tools\ADOPriv.
  2. Add all the include directories and all the lib directories for this version of the SDK to your Visual C++ directory. (To do this in Visual C++, on the Tools menu, click Options and then click Directories.)
  3. Update your system path to include the following directories:

    InstallDirectory\Conformance\Tests\Proposed\OLEDBTests\Include

    InstallDirectory\Conformance\Include

    InstallDirectory\Conformance\Lib

  4. Build ADOPriv using the default target.
  5. Copy ADOPriv.dll to InstallDirectory\Conformance\Lib.
  6. Register ADOPriv.dll in that directory by using Regsrv32.exe.


Conformance Testing ADO Applications

The ADO tests are located in InstallDirectory\Conformance\Tests\Proposed\ADOTests.

The test .dll and .reg files are in the \ADOTests subdirectory. The source is in the \SRC subdirectory, in case you need to rebuild the tests.

ADOPriv.dll is in the \Tools subdirectory. (This is the wrapper for the PrivLib used by the ADO tests for common functionality.)

To run ADO conformance tests

  1. Register the ADO tests by executing regsvr32 on each of the ADO test .dll files. For example: regsvr32 adolvl0.dll
  2. Execute each of the .reg files. For example: adolvl0.reg.
  3. If you haven't already done so, create an .ini file using the TableDump utility found in the \Tools subdirectory.
  4. Start Local Test Manager (LTM) by opening LTM.exe. When LTM is running, set up a provider alias by adding a new provider (specifying the initialization string.)
  5. Click the test module, click the provider you added, and then click Run.

Test modules and LTM are set up so that you can run a test module, a test case, or a test variation.

Click the plus sign next to the test module to expose the cases in that module. Click the plus sign next to a test case to expose the variations in that case.

Each variation tests a single test scenario. Each variation will return pass or failure. In many cases, the variation also will return the expected HRESULT and the actual HRESULT received.


Provider Connection String for ADO

ADO connection strings for providers are documented in the ADO Programmer's Reference in the MDAC SDK. The following table shows examples of connection strings that can be used in running the ADO conformance tests.

Provider Connection string example
ODBC
data source=ADOT7;

user id=adolab;
password=sometext;
FILE=C:\testdrop\ wayl7.ini;
DefaultQuery=select * from wayl7;
CURSORLOC=client;CONNECTIONTIMEOUT=500;
Oracle
data source=goliath.world;

user id=ADOLAB1;
password=sometext;
FILE=C:\testdrop\ wayl7can.ini;
DefaultQuery=select * from wayl7;
CURSORLOC=server;


Debugging ADO Conformance Tests

Your provider should get zero failures when running the Level 0 tests. If your provider supports updates and transactions, it should also get zero failures when running the Updatability and Transaction tests.

In the case of a failure, you can view and step through the ADO tests by using Microsoft® Visual Basic® 6.0. In the ADO test project, open the Project menu, click Properties, click Debugging, and then specify LTM.exe. When you press F8 to begin debugging, the Local Test Manager application will open and pass control back to Visual Basic when the tests are run.


Local Test Manager

Microsoft® Local Test Manager (LTM) is a stand-alone conformance testing tool that can be used with different applications, using a variety of APIs, written in numerous programming languages. Examples of testing configurations might include a Microsoft Visual Basic® ADO application connecting to Microsoft SQL Server™ through the native provider, or a Microsoft Visual J++® application invoking the OLE DB Provider for Microsoft Jet and the OLE DB Persistence Provider.

Using LTM, you can test OLE DB interface specification conformance for a particular OLE DB provider. Standard tests accompany LTM, but you can also run your own complete test suites and modules and any specific test cases within those modules. You can customize tests to identify and isolate specific interfaces and providers and their interaction with a variety of consumers. You can then analyze the results to see whether your implementations match the OLE DB specification. Test suites can be saved and reused to provide consistent conformance test runs.

Conformance tests performed using LTM are meant to be only one part of the entire test battery of a provider. The tests do not allow checking for performance, stress scenarios, low resource conditions, multiuser cases, or provider-specific behaviors.

Additional Resources

For detailed information about how to use the Local Test Manager testing tool, see the LTM online help. (Open LTM.exe, open the Help menu, and then click Help Topics.)

For information about | See
Conformance testing issues | "Introduction to Conformance Testing"
Building conformance tests | "Building OLE DB Conformance Tests"
QuikTest test module | "QuikTest Test Module"


QuikTest Test Module

The QuikTest Test Module is a simple conformance test of minimum requirements for all OLE DB providers. It is shipped with the Microsoft® Data Access Components (MDAC) SDK and can be used with the Local Test Manager (LTM) testing tool.

LTM is installed with the DefaultSuite test suite, which includes QuikTest. You can run QuikTest from LTM as you would any conformance test.

QuikTest produces a useful overview of OLE DB provider functionality but does not test providers thoroughly. The QuikTest Test Module tests specific levels of provider functionality, the least of which is named Level 0.

For example, run QuikTest against a provider that supports only Level 0 functionality. QuikTest will pass the provider on the Level 0 tests and ignore the results of the higher level tests.

For more information about conformance testing, see "Introduction to Conformance Testing" in the OLE DB Testing section of the MDAC SDK.

For more information about LTM, see the Local Test Manager documentation.


OLE DB Samples

OLE DB Samples

The following OLE DB samples are available for this release.

Rowset Viewer Sample

The Rowset Viewer sample provides a simple way to view and manipulate rowsets. Because Rowset Viewer has been expanded to include much of the functionality of the Query Demo sample, Query Demo has been removed. To run the Rowset Viewer sample, click Start, point to Programs, point to Platform SDK, point to Data Access, and click RowsetViewer.

File name | Description
RowsetViewer.exe | Rowset Viewer sample

For information about building and running the Rowset Viewer sample, see Rowset Viewer Sample.

Table Copy Sample

The Table Copy sample demonstrates a simple consumer that reads data from one OLE DB rowset and saves it as a new table. To run the Table Copy sample, click Start, point to Programs, point to Platform SDK, point to Data Access, and click Table Copy.

File name | Description
TableCopy.exe | Table Copy sample

For information about building and running the Table Copy sample, see Table Copy Sample.

Sample Consumer

The sample consumer demonstrates how to build a simple consumer. This consumer uses the sample provider. To run the sample consumer, click Start, point to Programs, point to Platform SDK, point to Data Access, and click Sample Client For Text Provider.

File name | Description
SampClnt.exe | Sample consumer

For information about building and running the sample consumer, see OLE DB Sample Consumer.

OLE DB Sample Provider

The OLE DB Sample Provider demonstrates the construction of a very simple OLE DB provider. This sample is intended as a learning tool for developers new to writing OLE DB providers. The sample provider exposes data in a single, comma-separated value file named CUSTOMER.CSV.

The source files are located in the SAMPLES\DATAACCESS\OLEDB\SAMPPROV folder of the Microsoft Platform SDK installation folder. Only the source code and project files are included for the sample provider. To build the executable provider, either run NMAKE on the SampProv.mak file or load the project into the Microsoft® Visual C++® development environment and compile it.

ATL OLE DB Provider

OmniProv 1.0 is an ATL OLE DB Provider sample that works with the Microsoft DataGrid Control 6.0 (OLE DB) and the Microsoft ADO Data Control 6.0 (OLE DB) using server-side cursors. It has the capability of supporting nondeferred updates, inserts, and deletes. Additionally, it implements a storage class, which interfaces with a custom database. The ATL OLE DB Provider sample is part of the Data Access MSDN Technical Articles and can be located at http://msdn.microsoft.com/library/techart/omniprov.htm.


Rowset Viewer Sample

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 datasource, session, command, rowset, transaction, and notification objects supported by any OLE DB provider.

Rowset Viewer Features

The Rowset Viewer sample has a separate multi-document interface (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 datasource 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.

File name | Description
CBase.cpp | Base classes project file.
CBase.h | Base classes header file.
CBinder.cpp | Required and optional binder interfaces.
CBinder.h | Header file.
CCommand.cpp | Required command interfaces.
CCommand.h | Header file.
CDataLinks.cpp | Initialization interfaces.
CDataLinks.h | Header file.
CDataset.cpp | Dataset (OLAP) interfaces.
CDataSet.h | Header file.
CDataSource.cpp | Datasource (OLE DB) interfaces.
CDataSource.h | Header file.
CDialog.cpp | Handles all Connection, Properties, and other common dialogs.
CDialog.h | Header file.
CDialogLite.cpp | Handles minimum of Connection, Properties, and other common dialog boxes.
CDialogLite.h | Header file.
CEnum.cpp | Handles all Root Enumerator operations.
CEnum.h | Header file.
CError.cpp | Error interfaces.
CError.h | Header file.
CListener.cpp | Connection listener.
CListener.h | Header file.
CMainWindow.cpp | Main Rowset Viewer window.
CMainWindow.h | Header file.
CMDIChild.cpp | MDI Rowset Viewer windows.
CMDIChild.h | Header file.
CMultipleResults.h | Multiple results interfaces.
CMutipleResults.cpp | Header file.
CObjTree.cpp | Object trees for window.
CObjTree.h | Header file.
Common.cpp | Common support functions.
Common.h | Header file.
COptions.cpp | Load options routines.
COptions.h | Header file.
CRow.cpp | Row interfaces.
CRow.h | Header file.
CRowPosition.cpp | RowPosition interfaces.
CRowPosition.h | Header file.
CRowset.cpp | Rowset interfaces.
CRowset.h | Header file.
CSession.cpp | Session interfaces.
CSession.h | Header file.
CStream.cpp | IStream interfaces.
CStream.h | Header file.
CTrace.cpp | Tracing routines.
CTrace.h | Header file.
CTransaction.cpp | Transaction interfaces.
CTransaction.h | Header file.
CWinApp.cpp | Coinitializes application, sets Locale, and loads user options.
CWinApp.h | Header file.
Error.cpp | ErrorInfo routines.
Error.h | Header file.
Headers.cpp | Include Headers.h
Headers.h | Header file.
List.h | Linked list implementation.
Makefile | Make file for Rowset Viewer.
Menu.bmp | Bitmap file.
Property.cpp | Property routines.
Property.h | Header file.
Readonly.bmp | Bitmap file.
RowsetViewer.dsp | Project file.
RowsetViewer.ico | Icon file.
RowsetViewer.rc | Resource file.
RowsetViewer.rc2 | Resource file containing version information.
Resource.h | Header file.
Spy.cpp | IMallocSpy memory leak checking.
Spy.h | Header file.
Toolbar.bmp | Bitmap file.
Version.h | Version file for Rowset Viewer sample.


Building Rowset Viewer

The file Makefile 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 Rowset Viewer on the Intel x86 platform based on the Win32 (x86) application configuration.

NMAKE /f "makefile" CFG="RowsetViewer - Win32 x86 Debug"

To build a release version of Rowset Viewer on the Intel x86 platform based on the Win32 (x86) application configuration, use this syntax:

NMAKE /f "makefile" CFG="RowsetViewer - Win32 x86 Release"


Running Rowset Viewer

After building the Rowset Viewer sample, open RowsetViewer.exe from the directory in which you saved it.


Rowset Viewer Menus and Toolbar

Using Rowset Viewer menu commands, you can invoke methods on a variety of provider-supported interfaces. The toolbar contains buttons for frequently used operations, and presents tool tips for easier identification.

Menu | Commands
File | Connection and disconnection capabilities: Full Connect and Full Disconnect Service Components, Root Binder and Root Enumerator, and Data Links Recent Configurations and Recent Files Exit
DataSource | COM and OLE DB interfaces to create Data Source objects Release Object
Session | COM (IUnknown) and OLE DB interfaces to create Session objects Release Object
Command | COM (IUnknown) and OLE DB interfaces to create Command objects Clear Command and Release Object
Rowset | COM (IUnknown and IConnectionPointContainer) and OLE DB interfaces to create Rowset objects Invoke services, including the Cursor Engine, RowPosition object, and MSPersist Release Object
Row | COM (IUnknown and IConnectionPointContainer) and OLE DB interfaces to create Row objects Release Object
Error | COM (IUnknown) and OLE DB interfaces to create Error objects GetErrorInfo, SetErrorInfo, Display All Errors Release Object
Tools | Options—Use to set Data Source, Command, Accessor, Rowset, Notification, and Error options for Rowset Viewer Trace Window—Activates the Output window, allowing text editing Objects Window—Activates the Objects window
Window | Standard window-manipulation functions
Help | Displays copyright and version information


Rowset Viewer Example

This example describes how to connect to a provider, execute a command, and use IOpenRowset. It may be necessary to configure the ODBC data source before connecting to the provider. Use the ODBC Data Source Administrator to create or select a File DSN and supply the UserID and Password as appropriate. (OLE_DB_NWind_Jet is no longer available from this installation as a sample database.)

To connect to a provider

  1. Start RowsetViewer.exe.
  2. From the File menu, choose Full Connect.
  3. Select MSDASQL as the provider and select your ODBC data source for the DataSource parameter.
  4. Click OK.

To execute a command

  1. In the top pane of the MDI child window, type a query—for example:
    SELECT * FROM CUSTOMERS
  2. Click the SQL button on the toolbar to run ICommand::Execute, or open the Command menu, select ICommand, and then click Execute. The ICommandExecute::Execute dialog box appears.
  3. Click OK.

To use IOpenRowset

  1. In the top pane of the MDI child window, type the name of a table in your data source—for example, CUSTOMERS.
  2. Make sure you are connected to a provider and click the ! button, or open the Session menu, select IOpenRowset, and then click IOpenRowset::Open Rowset.


Table Copy Sample

This section contains the following information:

  • Table Copy Overview
  • Table Copy Files
  • Building Table Copy
  • Running Table Copy
  • Table Copy Example


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.

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 Files

The following source files are included with the Table Copy sample.

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 Table Copy

The file 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 Table Copy on the Intel x86 platform based on the Win32 (x86) Application configuration.

NMAKE /f "tablecopy.mak" CFG="TableCopy - Win32 x86 Debug"

To build a release version of Table Copy on the Intel x86 platform based on the Win32 (x86) application configuration, use this syntax:

NMAKE /f "tablecopy.mak" CFG="TableCopy - Win32 x86 Release"

For additional information about using NMAKE, see the Microsoft Visual Studio documentation.


Running Table Copy

To run the Table Copy sample, click Start, point to Programs, point to Microsoft Data Access SDK 2.5, and click Table Copy, or run TableCopy.exe from Windows Explorer.


Table Copy Example

This example describes the steps involved in connecting to a data source, selecting the data to be copied, and copying the data using the Table Copy sample. Complete each of these procedures by performing 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 your ODBC data source and click OK.

    Note Use the ODBC Data Source Administrator to configure your data source. (OLE_DB_NWind_Jet is no longer available from this installation as a sample database.)

  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 your ODBC data source 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. Under Insert, select the InsertRow (Immediate) button.
  12. Click Next again to move to step 5 of the wizard.
  13. 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

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.

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

The following are 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.

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 SampClnt.mak is set up for compilation with NMAKE, the Microsoft Program Maintenance Utility provided with Microsoft Visual Studio. Sample Consumer 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 Sample Consumer on the Intel x86 platform based on the Win32 (x86) Console Application configuration.

NMAKE /f "sampclnt.mak" CFG="sampclnt - Win32 x86 Debug"

To build a release version of Sample Consumer on the Intel x86 platform based on the Win32 (x86) application configuration, use this syntax:

NMAKE /f "sampclnt.mak" CFG="sampclnt - Win32 x86 Release"

For additional information about using NMAKE, see the Microsoft Visual Studio documentation.


Running the Sample Consumer

To run the sample consumer you have built, run SampClnt.exe from the command line or double-click the file name from Windows Explorer. The data file, Customer.csv, must be in the same directory as the .exe file. The sample consumer logs the results of its run to SampClnt.out; view this file to see the results of the run.


OLE DB Sample Provider

The OLE DB sample provider, SAMPPROV.DLL, is a learning tool for developers new to OLE DB. Users can display the source code in the debugger to learn about building OLE DB providers. Although this provider uses a simple interface to access data in the underlying data source, it demonstrates the general procedures that developers need to follow when using IOpenRowset to expose rowsets over a file-based data source.

Sample Provider Features

The sample provider exposes data in a single, comma-separated value file named CUSTOMER.CSV. If you alter the code to use a different file, you must follow the formatting conventions of the sample file, which follow.

The first line of a .csv file that can be read by SAMPPROV must contain the column names in quotation marks. The second line must list the column types and lengths. All additional lines contain data in comma-separated format. Every line of data in the file must contain the correct number of values. The following example illustrates the file format:

"Column1","Column2","Column3"
Char(20),SLONG,SLONG
"Name1",10,-10
@@@@@@@@@@@@@@@@@@@@@     // Deleted Row
"Name2",110,-110

In addition, the sample provider replaces deleted rows in the file with the at sign (@). These rows are ignored by the sample provider when the file is initialized. If a user updates a row, the new data is appended to the end of the file and the original row is overwritten with at signs, indicating that it is deleted.

Sample Provider Limitations

The following are limitations in SAMPPROV.DLL:

  • This code example is for illustrative purposes; the sample provider has not been rigorously tested.
  • IDBInitialize::Initialize requires the path to the .csv file. If the path is not supplied by the calling application, SAMPPROV prompts the user for the path.
  • The data source object can create only one session; the session can create only one rowset. This is because the underlying CfileIO object opens the .csv text files in exclusive mode and has no mechanism to share access to the file.
  • The use of properties is very simple: only a small set of properties are illustrated, and properties are read-only.

Sample Provider Files

The following source files are included with the sample provider. The source files are located in the <install directory>\SAMPLES\SAMPPROV directory. The sample provider executable file SAMPPROV.DLL is located in the <install directory>\BIN directory.

File name | Description
ACCESSOR.cpp | CImpIAccessor object implementation.
ASSERTS.cpp | Simple assertion routines.
ASSERTS.h | Simple assertion routine header file.
BITARRAY.cpp | An implementation of a bit array class used by the internal buffer to mark released or unreleased rows.
BITARRAY.h | Class definitions for the bit array class.
CLASSFAC.cpp | DLL entry and exit points and the OLE ClassFactory class.
CLASSFAC.h | Class definitions for CClassFactory and DLL entry points.
COLINFO.cpp | IColumnsInfo interface implementation.
CRTSESS.cpp | IDBCreateSession interface implementation.
CVTTYPE.cpp | IConvertType interface implementation.
DATASRC.cpp | CDataSource object implementation.
DATASRC.h | CDataSource base object and contained interface definitions.
DBINIT.cpp | IDBInitialize interface implementation.
DBPROP.cpp | IDBProperties and IDBInfo interface implementations.
DBSESS.cpp | CDBSession object implementation.
DBSESS.h | CDBSession base object and contained interface definitions.
EXTBUFF.cpp | The class that provides array-based access to a big block of memory.
EXTBUFF.h | Class definitions for CExtBuffer class.
FILEIDX.cpp | The index array code for a comma-separated value (.csv) provider.
FILEIDX.h | Class definitions for CFileIO class.
FILEIO.cpp | The file manipulation code for a comma-separated value (.csv) provider.
FILEIO.h | Class definitions for CFileIO class.
GLOBALS.cpp | Global initialization object.
GUIDS.h | Internal GUIDs.
HASHTBL.cpp | Hashing routines for row manipulation.
HASHTBL.h | Class definitions for CHashTbl class and miscellaneous bookmark functions.
HEADERS.cpp | Precompiled headers module.
HEADERS.h | Precompiled headers.
IROWSET.cpp | IRowset interface implementation.
LOCAL.RC | Localizable resource.
OPNROWST.cpp | IOpenRowset interface implementation.
PERSIST.cpp | IPersist interface implementation.
RC.h | Resource IDs.
ROWCHNG.cpp | IRowsetChange interface implementation.
ROWINFO.cpp | IRowsetInfo interface implementation.
ROWSET.cpp | CRowset object implementation.
ROWSET.h | CRowset base object and contained interface definitions.
SAMPPROV.DEF | Project definitions.
SAMPPROV.h | Main include file.
SAMPPROV.MAK | Microsoft Developer Studio–generated NMAKE File.
SAMPPROV.RC | Main resource file.
SAMPVER.h | Version include file.
UTILPROP.cpp | Properties utility object implementation.
UTILPROP.h | CUtilProp object definitions.

Building the Sample Provider

The file <install directory>\SAMPLES\SAMPPROV\SAMPPROV.MAK is set up for compilation with NMAKE, the Microsoft® Program Maintenance Utility provided with Microsoft® Developer Studio®. SAMPPROV 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 SAMPPROV on the Intel® x86 platform based on the Win32® (x86) Dynamic-Link Library configuration.

NMAKE /f "sampprov.mak" CFG="sampprov - Win32 x86 Debug"

To build a release version of SAMPPROV on the Intel x86 platform based on the Win32 (x86) Dynamic-Link Library configuration, use this syntax:

NMAKE /f "sampprov.mak" CFG="sampprov - Win32 x86 Release"

Note Neither Microsoft Windows® 2000 nor MDAC 2.5 is supported on Alpha platforms.

For additional information on using NMAKE, see the Developer Studio documentation.

Writing an OLE DB Provider: An Introduction

The purpose of this section is to offer some general guidelines on how to construct an OLE DB data provider.

Information Needed to Write an OLE DB Provider

OLE DB is based on the Component Object Model (COM). Developers must be familiar with this model as well as with basic object-oriented coding practices to create an OLE DB provider. Although many different implementations are possible, the sample provider implements the TDataSource, TDBSession, and TRowset CoTypes. The sample provider uses the IOpenRowset interface, which is a required interface on the session for all providers. IOpenRowset is used by consumers that need access to the full data set and do not need to specify selection criteria.

Developers should become familiar with data source objects, sessions, and rowsets by reading the OLE DB Programmer's Reference. In addition, developers may want to read the "Overview" in the OLE DB Programmer's Reference to get a general introduction to OLE DB conventions, design considerations, and a complete introduction to the objects that make up OLE DB.

The Sample Provider

The sample provider offers access to a fixed-length text data file using OLE DB interfaces. The purpose of this sample provider is purely illustrative. The capabilities of the sample provider are limited; these limitations are discussed in "Sample Provider Overview," earlier in this paper.

The sample provider opens the sample data file and enables the consumer to read the data, using the IRowset interface, in a forward-only fashion. The consumer can update or delete the current row but cannot add new rows. The format of the data file is discussed in "Sample Provider Overview," previously in this paper.

Objects and Interfaces Implemented by the Sample Provider

The sample provider implements three OLE DB objects.

  • The data source object, which enables consumers to connect and initialize the interaction with the data file.
  • The session, which enables consumers to create a rowset for the data set in the data file.
  • The rowset, which exposes a data set to the consumer.

These objects, along with the interfaces implemented in the sample, are described in detail in the following topics, which also contain some of the implementation code for specific interfaces and methods.

The following table lists the interfaces and methods implemented in the sample provider along with the name of the source code file in which each can be found.

Interface name | Method name | Source code file
IDBInitialize | Initialize Uninitialize | DBInit.cpp DBInit.cpp
IDBCreateSession | CreateSession | CrtSess.cpp
IDBProperties | GetProperties GetPropertyInfo SetProperties | UtilProp.cpp UtilProp.cpp UtilProp.cpp
IPersist | GetClassID | Persist.cpp
IGetDataSource | GetDataSource | DBSess.cpp
IOpenRowset | OpenRowset | OpnRowst.cpp
IColumnsInfo | GetColumnInfo MapColumnIDs | ColInfo.cpp ColInfo.cpp
IConvertType | CanConvert | CvtType.cpp
IAccessor | AddRefAccessor CreateAccessor GetBindings ReleaseAccessor | Accessor.cpp Accessor.cpp Accessor.cpp Accessor.cpp
IRowset | AddRefRows GetData GetNextRows ReleaseRows RestartPosition | IRowset.cpp IRowset.cpp IRowset.cpp IRowset.cpp IRowset.cpp
IRowsetInfo | GetProperties GetReferencedRowset GetSpecification | RowInfo.cpp RowInfo.cpp RowInfo.cpp
IRowsetChange | DeleteRows InsertRow SetData | RowChng.cpp RowChng.cpp RowChng.cpp
ISessionProperties | GetProperties SetProperties | DBSess.cpp DBSess.cpp

Setting and Getting Provider Properties

The data source object is the first object created when a consumer instantiates an OLE DB data provider by calling CoCreateInstance or through some other technique.

The data source object provides the starting point for communications between the provider and consumer. For example, a consumer can call CoCreateInstance and request an IDBInitialize interface pointer to instantiate a data source object. The provider's developer must generate a CLSID (class ID) and store the ID in the Microsoft® Windows® Registry. The consumer can use this CLSID with CoCreateInstance to instantiate the data source object. To facilitate this operation, providers should add their application or library information to the Windows Registry upon installation on a new system. The sample provider setup program registers the sample provider in the Windows Registry.

The data source object is also responsible for setting and returning information about the properties supported by the provider and exposing the list of supported keywords and literals. This functionality is supported through the mandatory IDBProperties interface and the optional IDBInfo interface. The IDBProperties interface contains three methods:

  • GetProperties returns the list of properties currently set on the data source object.
  • GetPropertyInfo returns information about supported rowset and data source properties.
  • SetProperties sets the properties on the data source object.

The IDBInfo interface contains two methods:

  • GetKeywords returns a list of supported keywords.
  • GetLiteralInfo returns information about literals used in text commands.

In the sample provider implementation, no command interfaces are implemented, so GetKeywords returns NULL. GetLiteralInfo is not implemented and returns E_NOTIMPL. GetProperties, GetPropertyInfo, and SetProperties are handled by passing the calls to the utility object that manages properties, which is found in UtilProp.cpp. The structure containing the properties known to the sample provider, which is also found in UtilProp.cpp, follows.

// Struct containing the properties we know about. The GUID and string fields are
// initialized in the constructor, because C++ makes it awkward to do so at declaration
// time. So, if you change this table, be sure to make parallel changes in CUtilProp::CUtilProp.
PROPSTRUCT s_rgprop[] =
 {
/* 0 */ {DBPROP_IAccessor,   FLAGS_ROWSETRO, VT_BOOL, TRUE,  0, NULL,   L"IAccessor"},
/* 1 */ {DBPROP_IColumnsInfo,  FLAGS_ROWSETRO, VT_BOOL, TRUE,  0, NULL,   L"IColumnsInfo"},
/* 2 */ {DBPROP_IConvertType,  FLAGS_ROWSETRO, VT_BOOL, TRUE,  0, NULL,   L"IConvertType"},
/* 3 */ {DBPROP_IRowset,   FLAGS_ROWSETRO, VT_BOOL, TRUE,  0, NULL,   L"IRowset"},
/* 4 */ {DBPROP_IRowsetChange, FLAGS_ROWSETRW, VT_BOOL, TRUE,  0, NULL,   L"IRowsetChange"},
/* 5 */ {DBPROP_IRowsetInfo,   FLAGS_ROWSETRO, VT_BOOL, TRUE,  0, NULL,   L"IRowsetInfo"},
/* 6 */ {DBPROP_ACTIVESESSIONS,  FLAGS_DATASOURCE, VT_I4, FALSE, 1, NULL,   L"Active Sessions"},
/* 7 */ {DBPROP_PROVIDERNAME,  FLAGS_DATASOURCE, VT_BSTR, FALSE, 0, L"SAMPPROV.DLL",L"Provider Name"}, /* For OLE DB 2.5 and later, replace DBPROP_PROVIDERNAME with DBPROP_PROVIDERFILENAME */
/* 8 */ {DBPROP_PROVIDEROLEDBVER,FLAGS_DATASOURCE, VT_BSTR, FALSE, 0, L"01.10",   L"OLE DB Version"},
/* 9 */ {DBPROP_PROVIDERVER,   FLAGS_DATASOURCE, VT_BSTR, FALSE, 0, _TEXT(VER_PRODUCTVERSION_STR), L"Provider Version"},
/* 10*/ {DBPROP_INIT_DATASOURCE, FLAGS_DBINITRW, VT_BSTR, FALSE, 0, L"",    L"Data Source"},
/* 11*/ {DBPROP_INIT_HWND,   FLAGS_DBINITRW, VT_I4, FALSE, 0, NULL,   L"Window Handle"},
/* 12*/ {DBPROP_INIT_PROMPT,   FLAGS_DBINITRW, VT_I2, FALSE, 4, NULL,   L"Prompt"}
  };

The source code for GetPropertyInfo, which passes property information back to the provider, follows.

// CUtilProp::GetPropertyInfo  -----------------------------------------
//
// @mfunc Returns information about rowset and data source properties
//   supported by the provider
//
// @rdesc HRESULT
//  @flag S_OK    | The method succeeded
//  @flag E_INVALIDARG  | pcPropertyIDSets or prgPropertyInfo was NULL
//  @flag E_OUTOFMEMORY | Out of memory
//

STDMETHODIMP CUtilProp::GetPropertyInfo
  (
  BOOL      fDSOInitialized,   //@parm IN  | if Initialized
  ULONG     cPropertyIDSets,   //@parm IN  | # properties
  const DBPROPIDSET rgPropertyIDSets[],  //@parm IN  | Array of property sets
  ULONG*    pcPropertyInfoSets,  //@parm OUT | # DBPROPSET structures
  DBPROPINFOSET** prgPropertyInfoSets, //@parm OUT | DBPROPSET structures property
               //    | information returned
  WCHAR**     ppDescBuffer   //@parm OUT | Property descriptions
  )
{
 BOOL     fRet     = TRUE;
 BOOL     fPropsinError  = FALSE;
 BOOL     fPropsSucceed  = FALSE;
 BOOL     fIsSpecialGUID   = FALSE;
 BOOL     fIsNotSpecialGUID  = FALSE;
 ULONG    cProps     = 0;
 ULONG    cCount     = 0;
 ULONG    ulPropertySets   = 0;
 WCHAR*   pDescBuffer    = NULL;
 DBPROPINFO*  pPropInfo;
 DBPROPINFOSET* pPropInfoSet;

 // init out params
 if (pcPropertyInfoSets)
  *pcPropertyInfoSets  = 0;
 if (prgPropertyInfoSets)
  *prgPropertyInfoSets = NULL;
 if (ppDescBuffer)
  *ppDescBuffer = NULL;

 // Check Arguments, on failure post HRESULT to error queue
 if( ((cPropertyIDSets > 0) && !rgPropertyIDSets) ||
  !pcPropertyInfoSets || !prgPropertyInfoSets )
  return ResultFromScode( E_INVALIDARG );

 // New argument check for > 1 cPropertyIDs and NULL pointer for
 // array of property ids.
 for(ULONG ul=0; ul<cPropertyIDSets; ul++)
 {
  if( rgPropertyIDSets[ul].cPropertyIDs && !(rgPropertyIDSets[ul].rgPropertyIDs) )
   return ResultFromScode( E_INVALIDARG );

  if (DBPROPSET_ROWSETALL == rgPropertyIDSets[ul].guidPropertySet    ||
   DBPROPSET_DATASOURCEALL == rgPropertyIDSets[ul].guidPropertySet   ||
   DBPROPSET_DATASOURCEINFOALL == rgPropertyIDSets[ul].guidPropertySet ||
   DBPROPSET_SESSIONALL == rgPropertyIDSets[ul].guidPropertySet    ||
   DBPROPSET_DBINITALL == rgPropertyIDSets[ul].guidPropertySet)
   fIsSpecialGUID = TRUE;
  else
   fIsNotSpecialGUID = TRUE;

  if(fIsSpecialGUID && fIsNotSpecialGUID)
   return ResultFromScode( E_INVALIDARG );
 }

 // save the count of PropertyIDSets
 cProps = cPropertyIDSets;

 // If the consumer does not restrict the property sets
 // by specify an array of property sets and a cPropertySets
 // greater than 0, then we need to make sure we
 // have some to return
 if ( (cPropertyIDSets == 0) )
 {
  if( fDSOInitialized )
   cProps = NUMBER_OF_SUPPORTED_PROPERTY_SETS;
  else
   cProps = 1;
 }

 // use task memory allocater to alloc a DBPROPINFOSET struct
 pPropInfoSet = (DBPROPINFOSET*) g_pIMalloc->Alloc(cProps *
               sizeof( DBPROPINFOSET ));
 if ( !pPropInfoSet )
  return ResultFromScode( E_OUTOFMEMORY );

 memset( pPropInfoSet, 0, (cProps * sizeof( DBPROPINFOSET )));

 // Alloc memory for ppDescBuffer
 if ( ppDescBuffer )
 {
  pDescBuffer = (WCHAR*)g_pIMalloc->Alloc(NUMBER_OF_SUPPORTED_PROPERTIES *
   CCH_GETPROPERTYINFO_DESCRIP_BUFFER_SIZE * sizeof(WCHAR) );

  if( pDescBuffer )
  {
   memset(pDescBuffer, 0, (NUMBER_OF_SUPPORTED_PROPERTIES *
    CCH_GETPROPERTYINFO_DESCRIP_BUFFER_SIZE * sizeof(WCHAR)));

   *ppDescBuffer = pDescBuffer;
  }
  else
  {
   g_pIMalloc->Free( pPropInfoSet );
   return ResultFromScode( E_OUTOFMEMORY );
  }
 }

 // For each supported Property Set
 for (ulPropertySets=0; ulPropertySets < cProps; ulPropertySets++)
 {
  BOOL fGetAllProps = FALSE;

  // If no restrictions return all properties from the three supported property sets
  if( cPropertyIDSets == 0 )
  {
   fGetAllProps = TRUE;

   // only do this once
   if (ulPropertySets == 0)
   {
    pPropInfoSet[0].guidPropertySet = DBPROPSET_DBINIT;
    pPropInfoSet[0].cPropertyInfos  = NUMBER_OF_SUPPORTED_DBINIT_PROPERTIES;

    if ( fDSOInitialized )
    {
     pPropInfoSet[1].guidPropertySet = DBPROPSET_DATASOURCEINFO;
     pPropInfoSet[1].cPropertyInfos  = NUMBER_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES;
     pPropInfoSet[2].guidPropertySet = DBPROPSET_ROWSET;
     pPropInfoSet[2].cPropertyInfos  = NUMBER_OF_SUPPORTED_ROWSET_PROPERTIES;
    }
   }
  }
  else
  {
   pPropInfoSet[ulPropertySets].guidPropertySet = rgPropertyIDSets[ulPropertySets].guidPropertySet;
   pPropInfoSet[ulPropertySets].cPropertyInfos  = rgPropertyIDSets[ulPropertySets].cPropertyIDs;

   if ((rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DBINITALL) ||
    (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DBINIT   &&
     rgPropertyIDSets[ulPropertySets].cPropertyIDs == 0))
   {
    fGetAllProps = TRUE;
    pPropInfoSet[ulPropertySets].guidPropertySet = DBPROPSET_DBINIT;
    pPropInfoSet[ulPropertySets].cPropertyInfos  = NUMBER_OF_SUPPORTED_DBINIT_PROPERTIES;
   }
   else if (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCEALL)
   {
    // Since we do not support it should return DB_E_ERRORSOCCURRED with 0 & NULL
    fPropsinError = TRUE;
    pPropInfoSet[ulPropertySets].guidPropertySet = DBPROPSET_DATASOURCE;
    pPropInfoSet[ulPropertySets].cPropertyInfos  = 0;
   }
   else if( fDSOInitialized )
   {
    if( (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCEINFOALL) ||
     ((rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCEINFO) &&
      (rgPropertyIDSets[ulPropertySets].cPropertyIDs == 0)) )
    {
     fGetAllProps = TRUE;
     pPropInfoSet[ulPropertySets].guidPropertySet = DBPROPSET_DATASOURCEINFO;
     pPropInfoSet[ulPropertySets].cPropertyInfos  = NUMBER_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES;
    }
    else if (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_SESSIONALL)
    {
     // Since we do not support it should return a error with 0 & NULL
     fPropsinError = TRUE;
     pPropInfoSet[ulPropertySets].guidPropertySet = DBPROPSET_SESSION;
     pPropInfoSet[ulPropertySets].cPropertyInfos  = 0;
    }
    else if( (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_ROWSETALL) ||
       ((rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_ROWSET) &&
        (rgPropertyIDSets[ulPropertySets].cPropertyIDs == 0)) )
    {
     fGetAllProps = TRUE;
     pPropInfoSet[ulPropertySets].guidPropertySet = DBPROPSET_ROWSET;
     pPropInfoSet[ulPropertySets].cPropertyInfos  = NUMBER_OF_SUPPORTED_ROWSET_PROPERTIES;
    }
    else if (rgPropertyIDSets[ulPropertySets].cPropertyIDs == 0)
     fPropsinError = TRUE;
   }
   else if (rgPropertyIDSets[ulPropertySets].cPropertyIDs == 0)
   {
    // Since we do not support it should return a error with 0 & NULL
    fPropsinError = TRUE;
   }
  }

  if (pPropInfoSet[ulPropertySets].cPropertyInfos)
  {
    // use task memory allocater to alloc array of DBPROPINFO structs
   pPropInfo = (DBPROPINFO*) g_pIMalloc->Alloc(sizeof( DBPROPINFO ) *
         pPropInfoSet[ulPropertySets].cPropertyInfos);

   if (!pPropInfo)
   {
    if ( ppDescBuffer )
     *ppDescBuffer = NULL;
    g_pIMalloc->Free( pPropInfoSet );
    g_pIMalloc->Free( pDescBuffer );

    return ResultFromScode( E_OUTOFMEMORY );
   }

   pPropInfoSet[ulPropertySets].rgPropertyInfos = &pPropInfo[0];

   memset( pPropInfo, 0,
    (pPropInfoSet[ulPropertySets].cPropertyInfos * sizeof( DBPROPINFO )));
  }

   // for each prop in our table
  for (cCount=0; cCount < pPropInfoSet[ulPropertySets].cPropertyInfos; cCount++)
  {
   // init the Variant right up front
   // that way we can VariantClear with no worried (if we need to)
   VariantInit( &pPropInfo[cCount].vValues );

   // set the description pointer
   pPropInfo[cCount].pwszDescription = pDescBuffer;

   // Check supported property sets
   if ( (pPropInfoSet[ulPropertySets].guidPropertySet == DBPROPSET_DBINIT) &&
     (fGetAllProps) )
   {
    // load up their DBPROPINFO from our table
    fPropsSucceed = TRUE;
    fRet = LoadDBPROPINFO( &m_rgproperties[START_OF_SUPPORTED_DBINIT_PROPERTIES + cCount],
       &pPropInfo[cCount] );
   }
   else if ( (pPropInfoSet[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCEINFO) &&
      fGetAllProps && fDSOInitialized)
   {
    // load up their DBPROPINFO from our table
    fPropsSucceed = TRUE;
    fRet = LoadDBPROPINFO( &m_rgproperties[START_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES + cCount],
       &pPropInfo[cCount] );
   }

   else if ( (pPropInfoSet[ulPropertySets].guidPropertySet == DBPROPSET_ROWSET) &&
       fGetAllProps && fDSOInitialized)
   {
    // load up their DBPROPINFO from our table
    fPropsSucceed = TRUE;
    fRet = LoadDBPROPINFO( &m_rgproperties[START_OF_SUPPORTED_ROWSET_PROPERTIES + cCount],
       &pPropInfo[cCount] );
   }
   else
   {
    ULONG ulIndex;

    PPropInfo[cCount].dwPropertyID = rgPropertyIDSets[ulPropertySets].rgPropertyIDs[cCount];
    PPropInfo[cCount].dwFlags  = DBPROPFLAGS_NOTSUPPORTED;

    If ( (GetPropIndex(rgPropertyIDSets[ulPropertySets].rgPropertyIDs[cCount], &ulIndex)) &&
      (fDSOInitialized || (pPropInfoSet[ulPropertySets].guidPropertySet == DBPROPSET_DBINIT)) )
    {
     fPropsSucceed = TRUE;
     fRet = LoadDBPROPINFO( &m_rgproperties[ulIndex], &pPropInfo[cCount] );
    }
    else
    {
     fPropsinError = TRUE;
     pPropInfo[cCount].pwszDescription = NULL;
    }
   }

   if (!fRet)
   {
    ULONG ulFor;

    // something went wrong
    // clear all variants used so far..
    for (ulFor = 0; ulFor < cCount; ulFor++)
    VariantClear( &pPropInfo[ulFor].vValues );

    // .. delete the pPropInfo array, return failure
    if ( ppDescBuffer ) *ppDescBuffer = NULL;
    g_pIMalloc->Free( pPropInfo );
    g_pIMalloc->Free( pPropInfoSet );
    g_pIMalloc->Free( pDescBuffer );
    return ResultFromScode( E_FAIL );
   }

   // move the description pointer to the next
   if ( pPropInfo[cCount].pwszDescription )
    pDescBuffer += (wcslen(pPropInfo[cCount].pwszDescription) + sizeof(CHAR));
  }
  // Set local back to FALSE
  fGetAllProps = FALSE;
 }

 // set count of properties and property information
 *pcPropertyInfoSets  = cProps;
 *prgPropertyInfoSets = pPropInfoSet;

 if ( !fPropsSucceed && fPropsinError )
 {
  if ( ppDescBuffer ) *ppDescBuffer = NULL;
  g_pIMalloc->Free( pDescBuffer );
  return ResultFromScode( DB_E_ERRORSOCCURRED );
 }
 else if ( fPropsSucceed && fPropsinError )
  return ResultFromScode( DB_S_ERRORSOCCURRED );
 else
  return ResultFromScode( S_OK );
}

The source code for GetProperties, which returns the current settings of all supported properties, follows. Immediately following the creation of the data source object, none of the properties are set. Properties are set by the SetProperties method.

// CUtilProp::GetProperties ----------------------------------------------------
//
// @mfunc Returns current settings of all properties supported by the DSO/rowset
//
// @rdesc HRESULT
//  @flag S_OK    | The method succeeded
//  @flag E_INVALIDARG  | pcProperties or prgPropertyInfo was NULL
//  @flag E_OUTOFMEMORY | Out of memory
//
STDMETHODIMP CUtilProp::GetProperties
  (
  DWORD     dwBitMask,     //@parm IN  | Mask if Initialized
  ULONG     cPropertyIDSets,   //@parm IN  | # of restiction property IDs
  const DBPROPIDSET rgPropertyIDSets[],  //@parm IN  | restriction guids
  ULONG*      pcPropertySets,  //@parm OUT | count of properties returned
  DBPROPSET**   prgPropertySets  //@parm OUT | property information returned
  )
{
 BOOL   fRet     = TRUE;
 BOOL   fPropsinError  = FALSE;
 BOOL   fPropsSucceed  = FALSE;
 ULONG    cProps   = 0;
 ULONG    cCount   = 0;
 ULONG    ulPropertySets = 0;
 DBPROP*  pProp;
 DBPROPSET* pPropSet;

 // save the count of PropertyIDSets
 cProps = cPropertyIDSets;

 // If the consumer does not restrict the property sets
 // by specify an array of property sets and a cPropertySets
 // greater than 0, then we need to make sure we
 // have some to return
 if( cPropertyIDSets == 0 )
 {
  // only allow the DBINIT and DATASOURCE if Initialized
  if ( (dwBitMask & PROPSET_DSOINIT) == PROPSET_DSOINIT )
   cProps = 2;
  else
   cProps = 1;
 }

 // use task memory allocater to alloc a DBPROPINFOSET struct
 pPropSet = (DBPROPSET*) g_pIMalloc->Alloc(cProps *
               sizeof( DBPROPSET ));
 if ( !pPropSet )
  return ResultFromScode( E_OUTOFMEMORY );

 memset( pPropSet, 0, (cProps * sizeof( DBPROPSET )));

 // For each supported Property Set
 for (ulPropertySets=0; ulPropertySets < cProps; ulPropertySets++)
 {
  BOOL fGetAllProps = FALSE;

  // If no restrictions return all properties from the three supported property sets
  if ( cPropertyIDSets == 0 )
  {
    fGetAllProps = TRUE;

    // only do this once
    if ( ulPropertySets == 0 )
    {
     if( !(dwBitMask & PROPSET_SESSION) )
     {
      if ( !(dwBitMask & PROPSET_ROWSET) )
      {
        pPropSet[0].guidPropertySet = DBPROPSET_DBINIT;
        pPropSet[0].cProperties  = NUMBER_OF_SUPPORTED_DBINIT_PROPERTIES;

       if( dwBitMask & PROPSET_INIT )
       {
        pPropSet[1].guidPropertySet = DBPROPSET_DATASOURCEINFO;
        pPropSet[1].cProperties  = NUMBER_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES;
       }
      }
      else
      {
       pPropSet[0].guidPropertySet = DBPROPSET_ROWSET;
       pPropSet[0].cProperties  = NUMBER_OF_SUPPORTED_ROWSET_PROPERTIES;
      }
     }
    }
  }
  else
  {
   pPropSet[ulPropertySets].guidPropertySet = rgPropertyIDSets[ulPropertySets].guidPropertySet;
   pPropSet[ulPropertySets].cProperties  = rgPropertyIDSets[ulPropertySets].cPropertyIDs;

   if( rgPropertyIDSets[ulPropertySets].cPropertyIDs == 0 )
   {
    fGetAllProps = TRUE;

    if( rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DBINIT )
    {
     pPropSet[ulPropertySets].cProperties  = NUMBER_OF_SUPPORTED_DBINIT_PROPERTIES;
    }
    else if( (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCEINFO) &&
       ((dwBitMask & PROPSET_DSOINIT) == PROPSET_DSOINIT) )
    {
     pPropSet[ulPropertySets].cProperties  = NUMBER_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES;
    }
    else if( (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_ROWSET) &&
       (dwBitMask & PROPSET_ROWSET))
    {
     pPropSet[ulPropertySets].cProperties  = NUMBER_OF_SUPPORTED_ROWSET_PROPERTIES;
    }
    else
    {
     fGetAllProps = FALSE;
    }
   }
  }

  if( pPropSet[ulPropertySets].cProperties )
  {
    // use task memory allocater to alloc array of DBPROPINFO structs
   pProp = (DBPROP*) g_pIMalloc->Alloc(sizeof( DBPROP ) *
           pPropSet[ulPropertySets].cProperties);

   if (!pProp)
   {
    for(ULONG ul=0; ul<ulPropertySets; ul++)
    {
     for(ULONG ul2=0; ul2<pPropSet[ul].cProperties; ul2++)
      VariantClear( &pPropSet[ul].rgProperties[ul2].vValue );

     g_pIMalloc->Free( pPropSet[ul].rgProperties );
    }
    g_pIMalloc->Free( pPropSet );

    return ResultFromScode( E_OUTOFMEMORY );
   }

   pPropSet[ulPropertySets].rgProperties = &pProp[0];

   memset( pProp, 0,
    (pPropSet[ulPropertySets].cProperties * sizeof( DBPROP )));
  }

   // for each prop in our table..
  for (cCount=0; cCount < pPropSet[ulPropertySets].cProperties; cCount++)
  {
   // init the Variant right up front
   // that way we can VariantClear with no worried (if we need to)
   VariantInit( &pProp[cCount].vValue );

   // Check supported property sets
   if ( pPropSet[ulPropertySets].guidPropertySet == DBPROPSET_DBINIT &&
     fGetAllProps )
   {
    fPropsSucceed = TRUE;
    // load up their DBPROP from our table
    fRet = LoadDBPROP( &m_rgproperties[START_OF_SUPPORTED_DBINIT_PROPERTIES + cCount],
          &pProp[cCount] );
   }
   else if ( pPropSet[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCEINFO &&
      fGetAllProps )
   {
    fPropsSucceed = TRUE;
    // load up their DBPROPINFO from our table
    fRet = LoadDBPROP( &m_rgproperties[START_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES + cCount],
          &pProp[cCount] );
   }
   else if ( pPropSet[ulPropertySets].guidPropertySet == DBPROPSET_ROWSET &&
      fGetAllProps )
   {
    fPropsSucceed = TRUE;
    // load up their DBPROPINFO from our table
    fRet = LoadDBPROP( &m_rgproperties[START_OF_SUPPORTED_ROWSET_PROPERTIES + cCount],
          &pProp[cCount] );
   }
   else
   {
    ULONG ulIndex;

    PProp[cCount].dwPropertyID = rgPropertyIDSets[ulPropertySets].rgPropertyIDs[cCount];
    PProp[cCount].dwStatus  = DBPROPSTATUS_NOTSUPPORTED;

    If(  (GetPropIndex(rgPropertyIDSets[ulPropertySets].rgPropertyIDs[cCount], &ulIndex)) &&
      (((dwBitMask & PROPSET_DSO) &&
      (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DBINIT)) ||
       (((dwBitMask & PROPSET_DSOINIT) == PROPSET_DSOINIT) &&
      ((rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCE) ||
      (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCEINFO))) ||
       ((dwBitMask & PROPSET_SESSION) &&
      (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_SESSION)) ||
       ((dwBitMask & PROPSET_ROWSET) &&
      (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_ROWSET))) )
    {
     fPropsSucceed = TRUE;
     fRet = LoadDBPROP( &m_rgproperties[ulIndex], &pProp[cCount] );
    }
    else
     fPropsinError = TRUE;
   }

   if (!fRet)
   {
    ULONG ulFor;

    // something went wrong
    // clear all variants used so far..
    for (ulFor = 0; ulFor < cCount; ulFor++)
     VariantClear( &pProp[ulFor].vValue );

    // .. delete the pPropInfo array, return failure
    g_pIMalloc->Free( pProp );
    g_pIMalloc->Free( pPropSet );
    return ResultFromScode( E_FAIL );
   }
  }
  // Set local back to FALSE
  fGetAllProps = FALSE;
 }

 // set count of properties and property information
 *pcPropertySets  = cProps;
 *prgPropertySets = pPropSet;

 if ( !fPropsSucceed && fPropsinError )
  return ResultFromScode( DB_E_ERRORSOCCURRED );
 else if ( fPropsSucceed && fPropsinError )
  return ResultFromScode( DB_S_ERRORSOCCURRED );
 else
  return ResultFromScode( S_OK );
}

The source code for SetProperties, which sets the values of all supported properties, follows. The consumer must set properties on the provider before initializing the data source object. In most providers, these properties provide information such as a database location, database name, user ID, and password. The sample provider requires only one value: a valid directory path to the data file.

// CUtilProp::SetProperties ----------------------------------------------------
//
// @mfunc Set current settings of properties supported by the DSO/rowset
//
// @rdesc HRESULT
//  @flag S_OK    | The method succeeded
//  @flag E_INVALIDARG  | pcProperties or prgPropertyInfo was NULL
//  @flag E_OUTOFMEMORY | Out of memory
//
STDMETHODIMP CUtilProp::SetProperties
  (
  DWORD    dwBitMask,   //@parm IN  Type of PropSet
  ULONG    cPropertyIDSets, //@parm IN  # of DBPROPSET
  DBPROPSET    rgPropertyIDSets[] //@parm INOUT Array of property sets
 )
{
 ULONG ulPropertySets  = 0;
 ULONG cCount    = 0;
 BOOL  fSetAllProps  = TRUE;
 BOOL  fOnePropSet   = FALSE;

 // For each supported Property Set
 for (ulPropertySets=0; ulPropertySets < cPropertyIDSets; ulPropertySets++)
 {
  ULONG ulIndex = 0;

   // for each prop in the propset
  for (cCount=0; cCount < rgPropertyIDSets[ulPropertySets].cProperties; cCount++)
  {
   rgPropertyIDSets[ulPropertySets].rgProperties[cCount].dwStatus = DBPROPSTATUS_OK;

   // only allow the DBINIT and DATASOURCE
   if ( (dwBitMask & PROPSET_DSO) &&
     ((rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DBINIT) &&
      (dwBitMask & PROPSET_INIT)) )
   {
     rgPropertyIDSets[ulPropertySets].rgProperties[cCount].dwStatus = DBPROPSTATUS_NOTSETTABLE;
     fSetAllProps = FALSE;
   }
   else if ( ((dwBitMask & PROPSET_DSO) &&
      (rgPropertyIDSets[ulPropertySets].guidPropertySet != DBPROPSET_DATASOURCE &&
       rgPropertyIDSets[ulPropertySets].guidPropertySet != DBPROPSET_DBINIT)) ||
       (rgPropertyIDSets[ulPropertySets].guidPropertySet == DBPROPSET_DATASOURCE &&
      !(dwBitMask & PROPSET_INIT)) )
   {
     rgPropertyIDSets[ulPropertySets].rgProperties[cCount].dwStatus = DBPROPSTATUS_NOTSUPPORTED;
     fSetAllProps = FALSE;
   }
   // only allow the SESSION
   else if ( (dwBitMask & PROPSET_SESSION) &&
       (rgPropertyIDSets[ulPropertySets].guidPropertySet != DBPROPSET_SESSION) )
   {
     rgPropertyIDSets[ulPropertySets].rgProperties[cCount].dwStatus = DBPROPSTATUS_NOTSUPPORTED;
     fSetAllProps = FALSE;
   }
   // only allow the ROWSET
   else if ( (dwBitMask & PROPSET_ROWSET) &&
       (rgPropertyIDSets[ulPropertySets].guidPropertySet != DBPROPSET_ROWSET) )
   {
     rgPropertyIDSets[ulPropertySets].rgProperties[cCount].dwStatus = DBPROPSTATUS_NOTSUPPORTED;
     fSetAllProps = FALSE;
   }
   else
   {
    // get the index in the array
    if ( GetPropIndex(rgPropertyIDSets[ulPropertySets].rgProperties[cCount].dwPropertyID, &ulIndex) )
    {
     // arg checking for the prop
     if ( (DBPROPOPTIONS_SETIFCHEAP != rgPropertyIDSets[ulPropertySets].rgProperties[cCount].dwOptions) &&
       (DBPROPOPTIONS_REQUIRED != rgPropertyIDSets[ulPropertySets].rgProperties[cCount].dwOptions) )
     {
      rgPropertyIDSets[ulPropertySets].rgProperties[cCount].dwStatus = DBPROPSTATUS_BADOPTION;
       fSetAllProps = FALSE;
     }
     else if ( ((m_rgproperties[ulIndex].vtType != rgPropertyIDSets[ulPropertySets].rgProperties[cCount].vValue.vt) &&
        (VT_EMPTY != rgPropertyIDSets[ulPropertySets].rgProperties[cCount].vValue.vt)) ||
         (IsValidValue(&rgPropertyIDSets[ulPropertySets].rgProperties[cCount]) == S_FALSE))
     {
      rgPropertyIDSets[ulPropertySets].rgProperties[cCount].dwStatus = DBPROPSTATUS_BADVALUE;
       fSetAllProps = FALSE;
     }
     else if ( !(m_rgproperties[ulIndex].dwFlags & DBPROPFLAGS_WRITE) )
     {
      if ( ((rgPropertyIDSets[ulPropertySets].rgProperties[cCount].vValue.vt == VT_BOOL) &&
         (rgPropertyIDSets[ulPropertySets].rgProperties[cCount].vValue.boolVal != m_rgproperties[ulIndex].boolVal)) ||
        ((rgPropertyIDSets[ulPropertySets].rgProperties[cCount].vValue.vt == VT_I4) &&
         (rgPropertyIDSets[ulPropertySets].rgProperties[cCount].vValue.lVal != m_rgproperties[ulIndex].longVal)) ||
        ((rgPropertyIDSets[ulPropertySets].rgProperties[cCount].vValue.vt == VT_BSTR) &&
         (wcscmp(rgPropertyIDSets[ulPropertySets].rgProperties[cCount].vValue.bstrVal,m_rgproperties[ulIndex].pwstrVal))) )
      {
       rgPropertyIDSets[ulPropertySets].rgProperties[cCount].dwStatus = DBPROPSTATUS_NOTSETTABLE;
        fSetAllProps = FALSE;
      }
      else
       fOnePropSet = TRUE;

     }
     else
     {
      fOnePropSet = TRUE;

      if( rgPropertyIDSets[ulPropertySets].rgProperties->vValue.vt == VT_EMPTY )
      {
       memcpy( &m_rgproperties[ulIndex], &s_rgprop[ulIndex], sizeof(PROPSTRUCT));
      }
      else
      {
       // switch on the propid
       switch( m_rgproperties[ulIndex].dwPropertyID )
       {
        case DBPROP_INIT_DATASOURCE:
         wcscpy(m_wszFilePath, V_BSTR(&rgPropertyIDSets[ulPropertySets].rgProperties->vValue));
         m_rgproperties[ulIndex].pwstrVal = m_wszFilePath;
         break;

        case DBPROP_INIT_HWND:
        case DBPROP_INIT_PROMPT:
         m_rgproperties[ulIndex].longVal = V_I4(&rgPropertyIDSets[ulPropertySets].rgProperties[cCount].vValue);
         break;

        case DBPROP_IRowsetChange:
         m_rgproperties[ulIndex].boolVal = V_BOOL(&rgPropertyIDSets[ulPropertySets].rgProperties[cCount].vValue);
         break;
       }
      }
     }
    }
    else
    {
     rgPropertyIDSets[ulPropertySets].rgProperties[cCount].dwStatus = DBPROPSTATUS_NOTSUPPORTED;
     fSetAllProps = FALSE;
    }
   }
  }
 }

 // Figure out the retcode
 if ( fSetAllProps )
  return ResultFromScode( S_OK );
 else if ( fOnePropSet && !fSetAllProps )
  return ResultFromScode( DB_S_ERRORSOCCURRED );
 else
  return ResultFromScode( DB_E_ERRORSOCCURRED );
}

Initializing and Uninitializing the Data Source Object

The IDBInitialize interface contains two methods: Initialize and Uninitialize. The following sections discuss the implementation of these methods in the sample provider.

Initializing the Data Source Object

Initialize enables consumers to explicitly initialize a data source object. Consumers must set properties on the data source object before attempting to initialize it; and consumers must supply a valid directory path to the data file in IDBProperties::SetProperties. If the directory path is invalid, the sample provider returns an E_FAIL error on initialization. The source code for IDBInitialize::Initialize follows; you can find the complete source code for IDBInitialize in DBInit.cpp.

// CImpIDBInitialize::Initialize ---------------------------------------------
//
// @mfunc Initializes the DataSource object.. For this provider it requires
// that a valid path is given to where the file will be located.
//
// @rdesc HRESULT
//  @flag S_OK    | Path exists
//  @flag E_FAIL    | Invalid path
//  @flag E_INVALIDARG  | Invalid Parameters passed in
//  @flag DB_E_ALREADYINITIALIZED | Datasource Object already initialized
//
STDMETHODIMP CImpIDBInitialize::Initialize
  (
  )
{
 HRESULT   hr;
 DBPROPIDSET rgPropertyIDSets[1];
 ULONG   cPropertySets;
 DBPROPSET*  prgPropertySets;
 DBPROPID  rgPropId[2];

 char szNewVal[MAX_PATH ];
 int  nPrompt = DBPROMPT_NOPROMPT;

 assert( m_pObj );

 if (m_pObj->m_fDSOInitialized)
  return ResultFromScode( DB_E_ALREADYINITIALIZED );

 rgPropId[0]        = DBPROP_INIT_DATASOURCE;
 rgPropId[1]        = DBPROP_INIT_PROMPT;

 rgPropertyIDSets[0].guidPropertySet = DBPROPSET_DBINIT;
 rgPropertyIDSets[0].rgPropertyIDs   = rgPropId;
 rgPropertyIDSets[0].cPropertyIDs  = 2;

 // Get the value of the DBPROP_INIT_DATASOURCE property
 hr = m_pObj->m_pUtilProp->GetProperties(
         PROPSET_DSO,
         1,
         rgPropertyIDSets,
         &cPropertySets,
         &prgPropertySets );

 // On failure treat it as if we were opening with prompt..
 if( SUCCEEDED(hr) && (prgPropertySets[0].rgProperties[0].vValue.vt) )
  WideCharToMultiByte( CP_ACP, 0, V_BSTR(&prgPropertySets[0].rgProperties->vValue), -1,
          szNewVal, sizeof( szNewVal ), NULL, NULL );
 else
  *szNewVal = '\0';

 // Get the Prompt value
 nPrompt = V_I2(&prgPropertySets[0].rgProperties[1].vValue);

 // Free the memory
 if (prgPropertySets)
 {
  for(ULONG ulIndex=0; ulIndex<prgPropertySets[0].cProperties; ulIndex++)
   VariantClear(&prgPropertySets[0].rgProperties[ulIndex].vValue);

  g_pIMalloc->Free(prgPropertySets[0].rgProperties);
  g_pIMalloc->Free(prgPropertySets);
 }

 // if caller didn't supply a directory path, ask the user
 if (nPrompt != DBPROMPT_NOPROMPT)
 {
  if (!BrowseDirs( GetDesktopWindow(), g_hInstance, NULL, szNewVal ))
   {
    return ResultFromScode( E_FAIL );
   }
   else
   {
    lstrcpyn( m_pObj->m_szPath, szNewVal, MAX_PATH );
    m_pObj->m_fDSOInitialized = TRUE;
    return ResultFromScode( S_OK );
   }
 }
 else  // caller did supply a directory path
 {
  // Get the current Directory
  char szCurrentDir[MAX_PATH];
  GetCurrentDirectory( MAX_PATH, szCurrentDir );

   // Check if the directory is a valid directory
  if (SetCurrentDirectory( szNewVal ))
  {
   // Restore to the original directory.
   SetCurrentDirectory( szCurrentDir );

   lstrcpyn( m_pObj->m_szPath, szNewVal, MAX_PATH );
   m_pObj->m_fDSOInitialized = TRUE;
   return ResultFromScode( S_OK );
  }
  else
  {
   return ResultFromScode( E_FAIL );
  }
 }
}

Uninitializing the Data Source Object

Uninitialize enables consumers to return the data source object to an uninitialized state. It is an error to call IDBInitialize::Uninitialize when there are open sessions or rowsets on the data source object. The source code for IDBInitialize::Uninitialize follows.

// CImpIDBInitialize::Uninitialize ---------------------------------------------
//
// @mfunc Returns the Data Source Object to an uninitialized state
//
// @rdesc HRESULT
//  @flag S_OK    | The method succeeded
//  @flag DB_E_OBJECTOPEN | A DBSession object was already created
//
STDMETHODIMP CImpIDBInitialize::Uninitialize
  (
  void
  )
{
 assert( m_pObj );

 if (!m_pObj->m_fDSOInitialized)
 {
  // data source object is not initialized; do nothing
  return ResultFromScode( S_OK );
 }
 else
 {
  if (!m_pObj->m_fDBSessionCreated)
  {
   // DSO initialized, but no DBSession has been created.
   // So, reset DSO to uninitialized state
   m_pObj->m_fDSOInitialized = FALSE;
   return ResultFromScode( S_OK );
  }
  else
  {
   // DBSession has already been created; trying to uninit
   // the DSO now is an error
   return ResultFromScode( DB_E_OBJECTOPEN );
  }
 }
}

Getting the Class ID of the Data Source Object

Consumers can retrieve the class ID of the data source object by calling IPersist::GetClassID. Generally, the consumer does this only when the provider does not support IPersistFile and the consumer wants to persist the class ID and initialization property values for later use.

The source code for IPersist::GetClassID follows; you can find the complete source code for the IPersist interface in Persist.cpp.

// CImpIPersist::GetClassID --------------------------------------------------
//
// @mfunc Get the CLSID of the DSO.
//
// @rdesc HRESULT
//  @flag S_OK      | The method succeeded.
//  @flag E_FAIL      | Provider-specific error.
//
STDMETHODIMP CImpIPersist::GetClassID( CLSID *pClassID )
{
 // Check the pointer
 if (pClassID)
 {
  memcpy( pClassID, &CLSID_SampProv, sizeof(CLSID) );
  return ResultFromScode(S_OK);
 }
 return ResultFromScode(E_FAIL);
}

Creating a Session

After you initialize the data source object, you must create a session object to manage the session and provide the framework needed to create a rowset with IOpenRowset::OpenRowset. The IDBCreateSession::CreateSession interface on the data source object enables you to create a new session object and returns an interface pointer to the session.

The source code for IDBCreateSession::CreateSession follows; you can find the complete source code for the IDBCreateSession interface in CrtSess.cpp.

// CImpIDBCreateSession::CreateSession ------------------------------------------------
//
// @mfunc Creates a new DB Session object from the DSO, and returns the
// requested interface on the newly created object.
//
// @rdesc HRESULT
//  @flag S_OK      | The method succeeded.
//  @flag E_INVALIDARG    | ppDBSession was NULL
//  @flag DB_E_NOAGGREGATION  | pUnkOuter was not NULL (this object does not support
//            being aggregated)
//  @flag E_FAIL      | Provider-specific error. This provider can only create
//            one DBSession
//  @flag E_OUTOFMEMORY   | Out of memory
//  @flag E_NOINTERFACE   | Could not obtain requested interface on DBSession object
//
STDMETHODIMP CImpIDBCreateSession::CreateSession
  (
  IUnknown* pUnkOuter,  //@parm IN | Controlling IUnknown if being aggregated
  REFIID  riid,   //@parm IN | The ID of the interface
  IUnknown**  ppDBSession //@parm OUT | A pointer to memory in which to return the interface pointer
  )
{
 CDBSession* pDBSession = NULL;
 HRESULT hr;

 // check in-params and NULL out-params in case of error
 if (ppDBSession)
  *ppDBSession = NULL;
 else
  return ResultFromScode( E_INVALIDARG );

 if (pUnkOuter)
  return ResultFromScode( DB_E_NOAGGREGATION );

 assert( m_pObj );

 // Check to see if the DSO is Uninitialized
 if (!m_pObj->m_fDSOInitialized)
  return ResultFromScode( E_UNEXPECTED );

 // this Data Source object can only create 1 DBSession...
 if (m_pObj->m_fDBSessionCreated)
  return ResultFromScode( E_FAIL );

 // open a DBSession object
 pDBSession = new CDBSession( NULL );
 if (!pDBSession)
  return ResultFromScode( E_OUTOFMEMORY );

 // initialize the object
 if (!pDBSession->FInit( m_pObj->m_szPath, m_pObj ))
 {
  delete pDBSession;
  return ResultFromScode( E_FAIL );
 }

 // get requested interface pointer on DBSession
 hr = pDBSession->QueryInterface( riid, (void **) ppDBSession );
 if (FAILED( hr ))
 {
  delete pDBSession;
  return ResultFromScode( hr );
 }

 // all went well
 m_pObj->m_fDBSessionCreated = TRUE;
 return ResultFromScode( S_OK );
}

Once the session has been created, the provider must expose the interface pointer to the data source object that created the session. This interface pointer is exposed through the mandatory interface IGetDataSource. The code for IGetDataSource::GetDataSource follows; you can find the complete source code for the IGetDataSource interface in DBSess.cpp.

//-----------------------------------------------------------------------------
// CImpIGetDataSource::GetDataSource
//
// @mfunc Retrieve an interface pointer on the session object
//
// @rdesc
//  @flag S_OK | Session Object Interface returned
//  @flag E_INVALIDARG | ppDataSource was NULL
//  @flag E_NOINTERFACE | IID not supported
//
STDMETHODIMP CImpIGetDataSource::GetDataSource
 (
 REFIID  riid,   // @parm IN  | IID desired
 IUnknown** ppDataSource // @parm OUT | ptr to interface
 )
{
 // Check Function Arguments
 if( ppDataSource == NULL )
  return ResultFromScode(E_INVALIDARG);

 assert( m_pObj->m_pCDataSource );
 return ((m_pObj->m_pCDataSource)->QueryInterface(riid, (LPVOID*)ppDataSource));
}

Creating a Rowset

The session contains the interface that enables consumers to open a data file and create a rowset object containing all rows in the data file. The sample provider implements IOpenRowset on the session to create this rowset. The following section describes this interface.

Instantiating and Exposing a Rowset

The IOpenRowset interface contains a single method: OpenRowset. IOpenRowset is a required interface on the session. IOpenRowset::OpenRowset can be used by providers that do not support command objects to generate a rowset of all rows in a table or index. This provides a way for consumers to open and manipulate individual tables or indexes in a database with relatively low resource overhead. In the sample provider implementation, IOpenRowset accepts a file name as the name of the table and concatenates that with the directory name provided through IDBProperties::SetProperties. IOpenRowset uses the directory name and file name to instantiate a rowset and expose the rowset to the consumer.

The source code for the IOpenRowset::OpenRowset method follows; you can find the complete source code for the IOpenRowset interface in OpnRowst.cpp.

// CImpIOpenRowset::OpenRowset ------------------------------------------------
//
// @mfunc Opens and returns a rowset that includes all rows from a single base table
//
// @rdesc HRESULT
//  @flag S_OK      | The method succeeded
//  @flag E_INVALIDARG    | pTableID was NULL
//  @flag E_FAIL      | Provider-specific error
//  @flag DB_E_NOTABLE    | Specified table does not exist in current Data
//            | Data Source object
//  @flag E_OUTOFMEMORY   | Out of memory
//  @flag E_NOINTERFACE   | The requested interface was not available

STDMETHODIMP CImpIOpenRowset::OpenRowset
 (
 IUnknown*       pUnkOuter,    //@parm IN  | Controlling unknown, if any
 DBID*         pTableID,     //@parm IN  | table to open
 DBID*         pIndexID,     //@parm IN  | DBID of the index
 REFIID        riid,     //@parm IN  | interface to return
 ULONG         cProperties,    //@parm IN  | count of properties
 DBPROPSET       rgProperties[],   //@parm INOUT | array of property values
 IUnknown**      ppRowset    //@parm OUT | where to return interface
 )
{
 int  cCharsCopied;
 char szFileName[MAX_PATH ];
 char szFile[_MAX_PATH ] = "";

 CFileIO*  pFileio = NULL;
 CRowset*  pRowset = NULL;
 HRESULT   hr;
 HRESULT   hrProp  = ResultFromScode( S_OK );

 // NULL out-params in case of error
 if( ppRowset )
  *ppRowset = NULL;

 // Validate in-params
 if (pUnkOuter)
  return ResultFromScode( DB_E_NOAGGREGATION );

 // Check Arguments
 if ( !pTableID && !pIndexID )
  return ResultFromScode( E_INVALIDARG );

 if ( riid == IID_NULL)
  return ResultFromScode( E_NOINTERFACE );

 assert( m_pObj->m_pUtilProp );

 // Check Arguments for use by properties
 hr = m_pObj->m_pUtilProp->SetPropertiesArgChk(cProperties, rgProperties);
 if( FAILED(hr) )
  return hr;

 // If the eKind is not known to use, basically it
 // means we have no table identifier
 if ( (!pTableID ) || ( pTableID->eKind != DBKIND_NAME ) ||
  ( (pTableID->eKind == DBKIND_NAME) && (!(pTableID->uName.pwszName)) ) ||
  ( wcslen(pTableID->uName.pwszName)==0 ) )
  return ResultFromScode(DB_E_NOTABLE);

 // We only accept NULL for pIndexID
 if( pIndexID )
  return ResultFromScode(DB_E_NOINDEX);

 assert( m_pObj );

 // this DBSession object can only create 1 Rowset object
 if (m_pObj->m_fRowsetCreated)
  return ResultFromScode( E_FAIL );

 // set the properties
 if ( cProperties )
  hr = m_pObj->m_pUtilProp->SetProperties(PROPSET_ROWSET, cProperties, rgProperties);

 if( (hr == DB_E_ERRORSOCCURRED) ||
  (hr == DB_S_ERRORSOCCURRED) )
 {
  // If all the properties set were SETIFCHEAP then we can set
  // our status to DB_S_ERRORSOCCURRED and continue.
  for(ULONG ul=0;ul<cProperties; ul++)
  {
   for(ULONG ul2=0;ul2<rgProperties[ul].cProperties; ul2++)
   {
    // Check for a required property that failed, if found, we must return
    // DB_E_ERRORSOCCURRED
    if( (rgProperties[ul].rgProperties[ul2].dwStatus != DBPROPSTATUS_OK) &&
     (rgProperties[ul].rgProperties[ul2].dwOptions != DBPROPOPTIONS_SETIFCHEAP) )
     return ResultFromScode(DB_E_ERRORSOCCURRED);
   }
  }
  hrProp = ResultFromScode(DB_S_ERRORSOCCURRED);
 }

 if( FAILED(hr) && (hrProp==S_OK) )
  return hr;

 // if ppRowset NULL return
 if ( !ppRowset )
  return ResultFromScode( S_OK );

 // get file name
 cCharsCopied = WideCharToMultiByte( CP_ACP, 0, pTableID->uName.pwszName, -1,
              szFileName, sizeof( szFileName ), NULL, NULL );
 if (!cCharsCopied)
  return ResultFromScode( E_FAIL );

 // Concatenate the path and filename
 lstrcat( szFile, m_pObj->m_szPath );
 lstrcat( szFile, "\\" );
 lstrcat( szFile, szFileName );

 // open and initialize a file object
 pFileio = new CFileIO();
 if (!pFileio)
  return ResultFromScode( E_OUTOFMEMORY );

 hr = pFileio->fInit( szFile );
 if (FAILED( hr ))
 {
  delete pFileio;
  return hr;
 }

 // open and initialize a rowset\cursor object
 pRowset = new CRowset( NULL );
 if (!pRowset)
 {
  delete pFileio;
  return ResultFromScode( E_OUTOFMEMORY );
 }
 // Initialize the rowset\cursor.
 // For now, since don't yet support "setable" properties, so no properties to pass.
 // The rowset will always create all of its interfaces.
 // This is all-or-nothing.
 if (!pRowset->FInit( pFileio ))
 {
  delete pFileio;
  delete pRowset;
  return ResultFromScode( DB_E_NOTABLE );
 }
 //At this point we have handed off the pFileio pointer to the sample
 //provider so null it out.
 PFileio = NULL;

 // get requested interface pointer on rowset\cursor
 hr = pRowset->QueryInterface( riid, (void **) ppRowset );
 if (FAILED( hr ))
 {
  delete pRowset;
  delete pFileio;
  return ResultFromScode( hr );
 }

 //Assign creator pointer. Used for IRowsetInfo::GetSpecification
 pRowset->m_pCreator = m_pObj;
 pRowset->m_pCreator->AddRef();
 m_pObj->m_fRowsetCreated = TRUE;

 return (hr == S_OK) ? hrProp : hr;
}

Using the Rowset

Rowsets are the unifying abstraction that enables all OLE DB providers to expose base data, command results, or schema information, in tabular format. Conceptually, a rowset is a collection of rows in which each row contains columns of data.

The IOpenRowset interface exposed by the session generates a rowset object, which encapsulates the provider's data. A rowset typically has an internal buffer, or row cache, which enables shared access to the provider's data by multiple consumers. The following section describes how providers and consumers interact with the rowset.

Consumer and Provider Interactions with the Rowset

After receiving the rowset interface pointer, the consumer can request rowset metadata from the provider through IColumnsInfo, if it does not have prior knowledge of the data representation. The consumer uses the metadata to specify bindings, which describe how the consumer wants to receive the data. The consumer creates bindings by requesting IAccessor from the provider and specifying the bindings through IAccessor::CreateAccessor. The provider returns a handle to the accessor to the consumer.

The consumer then requests a number of rows from the provider using IRowset::GetNextRows. The provider retrieves the data for these rows and stores it in the data cache. The provider then returns an array of row handles to the consumer. Each row handle returned by the provider has an initial reference count of one. The consumer is then free to get the data for any rows from the provider using GetData. The consumer supplies GetData with the row handle, the handle of an accessor, and the buffer location into which to return the data; the provider copies the data to the location specified by the consumer.

Consumers using the sample provider can delete or change data in the rowset, but they cannot add new rows. To update rows, consumers call IRowsetChange::SetData, which sets the data in the data cache to the values specified by the consumer. To delete rows from the data cache, the consumer calls IRowsetChange::DeleteRows. A third method on IRowsetChange, IRowsetChange::InsertRow, is not implemented in the sample provider.

When the consumer makes any change to data in the data cache, the effects of the change are written to the data source immediately. OLE DB specifies a change-buffering model, which enables the consumer to make changes that are not realized until the consumer calls IRowsetUpdate::Update; this model is not supported by the sample provider.

When the consumer has finished working with a row, it can release the row by calling IRowset::ReleaseRows. ReleaseRows simply decrements the reference count on the row in the data cache. If the reference count for that row reaches zero, the row data is released from the data cache.

Exposing Metadata

Providers expose information about the columns of a rowset through IColumnsInfo. The information for each column is returned in a DBCOLUMNINFO structure. For more details on the information contained in the DBCOLUMNINFO structure, see IColumnsInfo::GetColumnInfo in the OLE DB Programmer's Reference. OLE DB also enables sophisticated providers to expose a richer set of metadata through IColumnsRowset; the sample provider does not implement this interface.

Exposing Metadata to Consumers

The GetColumnInfo method returns metadata that is most commonly used by consumers: column ID, column name, the ordinal number of the column in the rowset, the column's data type, and so on. This information helps the consumer determine the binding type.

The provider returns the information in an array of DBCOLUMNINFO structures, one DBCOLUMNINFO structure per column in the rowset. The order of the structures returned in the array is the order in which the columns appear in the rowset.

The source code for IColumnsInfo::GetColumnInfo follows; you can find the complete source code for IColumnsInfo in ColInfo.cpp.

// CImpIColumnsInfo::GetColumnInfo -------------------------------------------
//
// @mfunc Returns the column metadata needed by most consumers.
//
// @rdesc HRESULT
//  @flag S_OK      | The method succeeded
//  @flag E_OUTOFMEMORY   | Out of memory
//  @flag E_INVALIDARG  | pcColumns or prginfo or ppStringsbuffer was NULL
//
STDMETHODIMP CImpIColumnsInfo::GetColumnInfo
  (
  ULONG*    pcColumns,  //@parm OUT | Number of columns in rowset
  DBCOLUMNINFO**  prgInfo,    //@parm OUT | Array of DBCOLUMNINFO Structures
  WCHAR**   ppStringsBuffer //@parm OUT | Storage for all string values
  )
{
 ULONG     icol;
 ULONG     icolStart;
 DBCOLUMNINFO* rgdbcolinfo;
 WCHAR*    pstrBuffer;

 // Initialize
 if (pcColumns)
  *pcColumns = 0;
 if (prgInfo)
  *prgInfo = NULL;
 if (ppStringsBuffer)
  *ppStringsBuffer = NULL;

 // Usual argument checking, prescribed by the spec.
 if (pcColumns == NULL || prgInfo == NULL || ppStringsBuffer == NULL)
  return ResultFromScode( E_INVALIDARG );

 icolStart = 1;
 rgdbcolinfo = (DBCOLUMNINFO *) g_pIMalloc->Alloc( m_pObj->m_cCols*sizeof( DBCOLUMNINFO ));
 if (rgdbcolinfo == NULL)
  return ResultFromScode( E_OUTOFMEMORY );

 memcpy( rgdbcolinfo, &(m_pObj->m_rgdbcolinfo[icolStart]), m_pObj->m_cCols*sizeof( DBCOLUMNINFO ));

 // Copy the heap for column names.
 if (m_pObj->m_cbHeapUsed)
 {
  ptrdiff_t dp;

  pstrBuffer = (WCHAR *) g_pIMalloc->Alloc( m_pObj->m_cbHeapUsed );
  if (pstrBuffer == NULL)
  {
   g_pIMalloc->Free( rgdbcolinfo );
   return ResultFromScode( E_OUTOFMEMORY );
  }
  memcpy( pstrBuffer, m_pObj->m_pbHeap, m_pObj->m_cbHeapUsed );
  dp = (LONG) pstrBuffer - (LONG) (m_pObj->m_pbHeap);
  dp >>= 1;

  // Loop through columns and adjust pointers to column names.
  for (icol =0; icol < m_pObj->m_cCols; icol++)
  {
   if (rgdbcolinfo[icol].pwszName)
     rgdbcolinfo[icol].pwszName += dp;
  }
 }

 *prgInfo   = rgdbcolinfo;
 *ppStringsBuffer = pstrBuffer;
 *pcColumns = m_pObj->m_cCols;

 return ResultFromScode( S_OK );
}

Returning Column Ordinals

For purposes of identification, columns in a rowset are identified by a column ID, which is a value of type DBID in the DBCOLUMNINFO structure. Although this is sufficient to uniquely identify the column, some providers might need to refer to the column by its position, or ordinal value.

The MapColumnIDs method returns column ordinals for all column IDs provided in the rgColumnIDs array. Column ordinals do not change during the life of the rowset, but might change between different instances of the rowset. The source code for IColumnsInfo::MapColumnIDs follows.

// CimpIColumnsInfo::MapColumnIDs --------------------------------------------
//
// @mfunc Returns an array of ordinals of the columns in a rowset that are
// identified by the specified column IDs.
//
// @rdesc HRESULT
//  @flag S_OK        | The method succeeded
//  @flag E_INVALIDARG      | cColumnIDs was not 0 and rgColumnIDs was NULL,
//              rgColumns was NULL
//  @flag DB_E_COLUMNUNAVAILABLE  | An element of rgColumnIDs was invalid
//
STDMETHODIMP CimpIColumnsInfo::MapColumnIDs
  (
  ULONG     cColumnIDs,   //@parm IN | Number of Column IDs to map
  const DBID  rgColumnIDs[],  //@parm IN | Column IDs to map
  ULONG     rgColumns[]   //@parm OUT | Ordinal values
  )
{
 ULONG ulError = 0;
 ULONG i;

 // If cColumnIDs is 0 return
 if (0 == cColumnIDs)
  return ResultFromScode( S_OK );

 // Check arguments
 if ((cColumnIDs != 0) && (NULL == rgColumnIDs))
  return ResultFromScode( E_INVALIDARG );

 if (NULL == rgColumns)
  return ResultFromScode( E_INVALIDARG );

 // Walk the Column ID structs and determine
 // the ordinal value
 for (i=0; I < cColumnIDs; i++)
 {
  if ((rgColumnIDs[i].uName.ulPropid < 1)     ||
    (rgColumnIDs[i].uName.ulPropid > m_pObj->m_cCols) ||
    (rgColumnIDs[i].eKind != DBKIND_GUID_PROPID)  ||
    (rgColumnIDs[i].uGuid.guid != GUID_NULL))
  {
   rgColumns[i] = DB_INVALIDCOLUMN;
   ulError++;
  }
  else
   rgColumns[i] = rgColumnIDs[i].uName.ulPropid;
 }

 if (!ulError)
  return ResultFromScode( S_OK );
 else if (ulError < cColumnIDs)
  return ResultFromScode( DB_S_ERRORSOCCURRED );
 else
  return ResultFromScode( DB_E_ERRORSOCCURRED );
}

Creating and Using Accessors

Consumers describe the memory structure for their buffers through a process called binding. An accessor is a group of bindings. The sample provider supports a very basic type of accessor and cannot support reference accessors, which allow the consumer direct access to the rowset's data cache.

Accessors are implemented through IAccessor. You can create accessors with IAccessor::CreateAccessor and release accessors with IAccessor::ReleaseAccessor. You can use IAccessor::GetBindings to determine the bindings in an existing accessor. IAccessor::AddRefAccessor enables the consumer to add a reference count to an existing accessor. The following sections describe these methods.

Determining Supported Conversions

Before the consumer creates an accessor, it can call IConvertType::CanConvert to determine if the provider supports a particular conversion.

The source code for IConvertType::CanConvert follows; you can find the complete source code for IConvertType in CvtType.cpp.

//----------------------------------------------------------------------------
// CImpIConvertType::CanConvert
//
// @mfunc Used by consumer to determine provider support for a conversion
//
// @rdesc HRESULT indicating the status of the method
//  @flag S_OK      | Conversion supported
//  @flag S_FALSE     | Conversion unsupported
//  @flag DB_E_BADCONVERTFLAG | dwConvertFlags was invalid
//  @flag DB_E_BADCONVERTFLAG | called on rowset for DBCONVERTFLAG_PARAMETER
//  @flag OTHER       | HRESULTS returned from support functions
//
STDMETHODIMP CImpIConvertType::CanConvert
 (
 DBTYPE     wFromType,   //@parm IN | src type
 DBTYPE     wToType,   //@parm IN | dst type
 DBCONVERTFLAGS dwConvertFlags //@parm IN | conversion flags
 )
{
 // Check Arguments
 if( dwConvertFlags != DBCONVERTFLAGS_COLUMN &&
  dwConvertFlags != DBCONVERTFLAGS_PARAMETER )
  return (ResultFromScode(DB_E_BADCONVERTFLAG));

 // Called on rowset, but asking about parameters?
 // Since we only support conversions on a Rowset we always
 // return error for DBCONVERTFLAGS_PARAMETER
 if( dwConvertFlags == DBCONVERTFLAGS_PARAMETER)
  return (ResultFromScode(DB_E_BADCONVERTFLAG));

 // Ask the conversion library for the answer
 return g_pIDataConvert->CanConvert(wFromType, wToType);
}

Creating an Accessor

CreateAccessor associates a set of bindings with an accessor handle that is used to send data to or fetch data from the rowset's data cache. The sample provider supports only the DBACCESSOR_ROWDATA accessor flag, which specifies that the accessor is to be used for rowset data.

The source code for IAccessor::CreateAccessor follows; you can find the complete source code for IAccessor in Accessor.cpp.

// CImpIAccessor::CreateAccessor -----------------------------------------
//
// @mfunc Creates a set of bindings that can be used to send data
// to or retrieve data from the data cache.
// NOTE:  Currently does not support returning rgStatus[].
//
// @rdesc HRESULT
//  @flag S_OK        | Method Succeeded
//  @flag E_FAIL        | Provider specific Error
//  @flag E_INVALIDARG      | pHAccessor was NULL, dwAccessorFlags was
//              invalid, or cBindings was not 0 and
//              rgBindings was NULL
//  @flag E_OUTOFMEMORY     | Out of Memory
//  @flag DB_E_ERRORSOCCURRED   | dwBindPart in an rgBindings element was invalid, OR
//              | Column number specified was out of range, OR
//              | Requested coercion is not supported.
//  @flag OTHER       | Other HRESULTs returned by called functions
//
STDMETHODIMP CimpIAccessor::CreateAccessor
  (
  DBACCESSORFLAGS dwAccessorFlags,
  ULONG     cBindings,  //@parm IN | Number of Bindings
  const DBBINDING rgBindings[], //@parm IN | Array of DBBINDINGS
  ULONG     cbRowSize,  //@parm IN | Number of bytes in consumer's buffer
  HACCESSOR*  phAccessor,   //@parm OUT | Accessor Handle
  DBBINDSTATUS  rgStatus[]  //@parm OUT | Binding status
  )
{
 PACCESSOR pAccessor;
 ULONG   hAccessor;
 ULONG   ibind;
 ULONG   icol;
 HRESULT   hr;

 // Check Parameters
 if( (cBindings && !rgBindings) ||
  (phAccessor == NULL) )
  return ResultFromScode( E_INVALIDARG );

 // init out params
 *phAccessor = (HACCESSOR) 0;

 // Check for the binding types we don't accept..
 if ((dwAccessorFlags & DBACCESSOR_PASSBYREF)    ||
   (dwAccessorFlags & DBACCESSOR_PARAMETERDATA))
  return ResultFromScode( E_INVALIDARG );

 // .. then check for the binding type that is required
 if (!(dwAccessorFlags & DBACCESSOR_ROWDATA))
  return ResultFromScode( E_INVALIDARG );

 // Check on the bindings the user gave us.
 for (ibind =0, hr =NOERROR; ibind < cBindings; ibind++)
 {
  icol = rgBindings[ibind].iOrdinal;

  // make sure column number is in range
  if (!(0 < icol && icol <= m_pObj->m_cCols))
  {
   TRACE( "CreateAccessor failure: binding %d, bad column number %d\n", ibind, icol );
   hr = ResultFromScode( DB_E_ERRORSOCCURRED );
   break;
  }

  // At least one of these valid parts has to be set. In SetData I assume it is the case.
  if ((rgBindings[ibind].dwPart & DBPART_VALUE)  == 0
   && (rgBindings[ibind].dwPart & DBPART_LENGTH) == 0
   && (rgBindings[ibind].dwPart & DBPART_STATUS) == 0)
  {
   TRACE( "CreateAccessor failure: binding %d, column %d, _VALUE, _LENGTH, _STATUS not specified", ibind, icol );
   hr = ResultFromScode( DB_E_ERRORSOCCURRED );

   if ( rgStatus )
   {
    //  Set Bind status to DBBINDSTATUS_BADBINDINFO
   }
   break;
  }

  // Make sure we can do the coercion that is requested
  if( NOERROR != g_pIDataConvert->CanConvert(rgBindings[ibind].wType, m_pObj->m_rgdbcolinfo[icol].wType) ||
   NOERROR != g_pIDataConvert->CanConvert(m_pObj->m_rgdbcolinfo[icol].wType, rgBindings[ibind].wType) )
  {
   TRACE( "CreateAccessor failure: binding %d, column %d, cannot coerce types\n", ibind, icol );
   hr = ResultFromScode( DB_E_ERRORSOCCURRED );
   if ( rgStatus )
   {
    //  Set Bind status to DBBINDSTATUS_UNSUPPORTEDCONVERSION
   }
   break;
  }
 }

 // Any errors amongst those checks?
 if (hr != NOERROR)
 {
  return hr;
 }

 // Make a copy of the client's binding array, and the type of binding.
 // Note: accessors with no bindings (cBindings=0) are legal.
 pAccessor = (ACCESSOR *) new BYTE[sizeof( ACCESSOR ) + (cBindings - 1) *sizeof( DBBINDING )];
 if (pAccessor == NULL)
  return ResultFromScode( E_OUTOFMEMORY );

 // We store a ptr to the newly created variable-sized ACCESSOR.
 // We have an array of ptrs (to ACCESSOR's).
 // The handle is the index into the array of ptrs.
 // The InsertIntoExtBuffer function appends to the end of the array.
 assert( m_pObj->m_pextbufferAccessor );
 hr = m_pObj->m_pextbufferAccessor->InsertIntoExtBuffer( &pAccessor, hAccessor );
 if (FAILED( hr ))
 {
  delete [] pAccessor;
  return ResultFromScode( E_OUTOFMEMORY );
 }
 assert( hAccessor );

 // Copy the client's bindings into the ACCESSOR.
 pAccessor->dwAccessorFlags = dwAccessorFlags;
 pAccessor->cBindings   = cBindings;
 pAccessor->cRef      = 1;  // Establish Reference count.
 memcpy( &(pAccessor->rgBindings[0]), &rgBindings[0], cBindings*sizeof( DBBINDING ));

 // fill out-param and return
 *phAccessor = (HACCESSOR) hAccessor;
 return ResultFromScode( S_OK );
}

Returning Accessor Bindings

GetBindings returns the bindings in an existing accessor. The source code for IAccessor::GetBindings follows; you can find the complete source code for IAccessor in Accessor.cpp.

// CImpIAccessor::GetBindings --------------------------------------------------
//
// @mfunc Returns the bindings in an accessor
//
// @rdesc HRESULT
//  @flag S_OK        | Method Succeeded
//  @flag E_INVALIDARG      | pdwAccessorFlags/pcBinding/prgBinding were NULL
//  @flag E_OUTOFMEMORY     | Out of Memory
//  @flag DB_E_BADACCESSORHANDLE  | Invalid Accessor given
//
STDMETHODIMP CImpIAccessor::GetBindings
  (
  HACCESSOR    hAccessor,   //@parm IN | Accessor Handle
  DBACCESSORFLAGS* pdwAccessorFlags,  //@parm OUT | Binding Type flag
  ULONG*     pcBindings,    //@parm OUT | Number of Bindings returned
  DBBINDING**  prgBindings    //@parm OUT | Bindings
  )
{
 // Retrieve our accessor structure from the client's hAccessor,
 // make a copy of the bindings for the user, then done.
 PACCESSOR pAccessor;
 ULONG   cBindingSize;
 HRESULT   hr;

 // check parameters
 if (!pdwAccessorFlags || !pcBindings || !prgBindings)
  return ResultFromScode( E_INVALIDARG );

 // init out-params
 *pdwAccessorFlags = 0;
 *pcBindings   = 0;
 *prgBindings  = NULL;

 // Validate Accessor Handle
 hr = m_pObj->m_pextbufferAccessor->GetItemOfExtBuffer((ULONG) hAccessor, &pAccessor );
 if (FAILED( hr ) || pAccessor == NULL)
  return ResultFromScode( DB_E_BADACCESSORHANDLE );

 // Allocate and return Array of bindings
 cBindingSize = pAccessor->cBindings * sizeof( DBBINDING );
 *prgBindings = (DBBINDING *) g_pIMalloc->Alloc( cBindingSize );
 if (*prgBindings)
 {
  *pdwAccessorFlags = pAccessor->dwAccessorFlags;
  *pcBindings = pAccessor->cBindings;
  memcpy( *prgBindings, pAccessor->rgBindings, cBindingSize );
 }
 else
 {
  return ResultFromScode( E_OUTOFMEMORY );
 }

 // all went well..
 return ResultFromScode( S_OK );
}

Adding a Reference Count to an Existing Accessor

AddRefAccessor adds a reference count to an existing accessor. The source code for IAccessor::AddRefAccessor follows.

// CImpIAccessor::AddRefAccessor -----------------------------------------
//
// @mfunc Adds a reference count to an existing accessor
//
// @rdesc HRESULT
//  @flag S_OK        | Method Succeeded
//  @flag E_FAIL        | Provider specific Error
//
STDMETHODIMP CimpIAccessor::AddRefAccessor
 (
 HACCESSOR hAccessor,  //@parm IN  | Accessor Handle
 ULONG*  pcRefCounts   //@parm OUT | Reference Count
 )
{
 // Retrieve our accessor structure from the client's hAccessor,
 // free it, then mark accessor ptr as unused.
 // We do not re-use accessor handles.  This way, we hope
 // to catch more client errors.  (Also, ExtBuffer doesn't
 // maintain a free list, so it doesn't know how to.)

 PACCESSOR pAccessor;
 HRESULT   hr;

 if( pcRefCounts )
  *pcRefCounts = 0;

 hr = m_pObj->m_pextbufferAccessor->GetItemOfExtBuffer((ULONG) hAccessor, &pAccessor );
 if (FAILED( hr ) || pAccessor == NULL)
  return ResultFromScode( DB_E_BADACCESSORHANDLE );

 InterlockedIncrement(&(pAccessor->cRef));

 if( pcRefCounts )
  *pcRefCounts = (ULONG)(pAccessor->cRef);

 return ResultFromScode(S_OK);
}

Releasing an Accessor

ReleaseAccessor decrements the reference count on an accessor; when the reference count reaches zero, the accessor is released. The source code for IAccessor::ReleaseAccessor follows; you can find the complete source code for IAccessor in Accessor.cpp.

// CImpIAccessor::ReleaseAccessor ---------------------------------------
//
// @mfunc Releases an Accessor
//
// @rdesc HRESULT
//  @flag S_OK        | Method Succeeded
//  @flag DB_E_BADACCESSORHANDLE  | hAccessor was invalid
//
STDMETHODIMP CImpIAccessor::ReleaseAccessor
  (
  HACCESSOR hAccessor,   //@parm IN  | Accessor handle to release
  ULONG*  pcRefCounts  //@parm OUT | Reference Count
  )
{
 // Retrieve our accessor structure from the client's hAccessor,
 // free it, then mark accessor ptr as unused.
 // We do not re-use accessor handles.  This way, we hope
 // to catch more client errors.  (Also, ExtBuffer doesn't
 // maintain a free list, so it doesn't know how to.)

 PACCESSOR pAccessor;
 HRESULT   hr;

 if( pcRefCounts )
  *pcRefCounts = 0;

 hr = m_pObj->m_pextbufferAccessor->GetItemOfExtBuffer((ULONG) hAccessor, &pAccessor );
 if (FAILED( hr ) || pAccessor == NULL)
  return ResultFromScode( DB_E_BADACCESSORHANDLE );

 // Free the actual structure.
 InterlockedDecrement(&(pAccessor->cRef));
 assert( pAccessor->cRef >= 0 );
 if( pAccessor->cRef <= 0 )
 {
  delete [] pAccessor;
  if( pcRefCounts )
   *pcRefCounts = 0;
 }
 else
 {
  if( pcRefCounts )
   *pcRefCounts = (ULONG)(pAccessor->cRef);
 }

 // Store a null in our array-of-ptrs,
 // so we know next time that it is invalid.
 // (operator[] returns a ptr to the space where the ptr is stored.)
 *(PACCESSOR*) ((*m_pObj->m_pextbufferAccessor)[ (ULONG) hAccessor]) = NULL;

 return ResultFromScode( S_OK );
}

Exposing Rowset Information

IRowsetInfo enables consumers to learn about the properties of a rowset through IRowsetInfo::GetProperties. In providers that implement bookmarks, consumers can get an interface pointer to a rowset referenced by a bookmark column with IRowsetInfo::GetReferencedRowset. Consumers can get an interface pointer to the object that created the rowset by calling IRowsetInfo::GetSpecification.

In the sample provider implementation, calls to IRowsetInfo::GetProperties are handled by passing the call to the utility object that manages properties, which is found in UtilProp.cpp. The sample provider doesn't support bookmarks, so all calls to IRowsetInfo::GetReferencedRowset return the error DB_E_NOTAREFERENCECOLUMN. The sample provider doesn't store an interface pointer to the object that created the rowset, so calls to IRowsetInfo::GetSpecification are returned with the success code S_OK and the pointer to the memory in which to return the interface pointer is set to NULL.

Managing Rowsets

IRowset provides methods for fetching rows sequentially, exposing data from those rows to consumers, and managing the rows in the rowset. IRowset contains five methods: AddRefRows, GetNextRows, GetData, ReleaseRows, and RestartPosition. The source code for IRowset::AddRefRows follows; you can find the complete source code for the IRowset interface in IRowset.cpp.

Incrementing the Reference Count on Row Handles

AddRefRows increments the reference count on the row handles supplied by the caller. AddRefRows enables consumers to make multiple references to a row in the data cache.

// CImpIRowset::AddRefRows --------------------------------------------------
//
// @mfunc Adds a reference count to an existing row handle
//
// @rdesc HResult
//  @flag S_OK      | Method Succeeded
//  @flag E_INVALIDARG  | rghRows was NULL and cRows was not 0
//  @flag DB_E_BADROWHANDLE | An element of rghRows was invalid

STDMETHODIMP  CImpIRowset::AddRefRows
  (
  ULONG     cRows,    // @parm IN   | Number of rows to refcount
  const HROW  rghRows[],  // @parm IN   | Array of row handles to refcount
  ULONG*    pcRefCounted, // @parm OUT  | Number successfully refcounted
  ULONG     rgRefCounts[] // @parm OUT  | Array of refcounts
  )
{
 ULONG ihRow = 0;
 ROWBUFF *pRowBuff;
 ULONG cAddRef;

 // init out param
 if (pcRefCounted)
  *pcRefCounted = 0;

 // check params
 if (cRows && !rghRows)
  return ResultFromScode( E_INVALIDARG );

 cAddRef = 0;

 // for each of the HROWs the caller provided...
 for (ihRow = 0; ihRow < cRows; ihRow++)
 {
  // check the row handle
  if (S_OK != (m_pObj->m_prowbitsIBuffer)->IsSlotSet((ULONG) rghRows[ihRow] ))
   return ResultFromScode( DB_E_BADROWHANDLE );

  // bump refcount
  pRowBuff = m_pObj->GetRowBuff((ULONG) rghRows[ihRow] );
  assert( pRowBuff->ulRefCount != 0 );
  assert( m_pObj->m_ulRowRefCount != 0 );
  ++pRowBuff->ulRefCount;
  ++m_pObj->m_ulRowRefCount;

  // bump total refcounted
  cAddRef++;

  // stuff new refcount into caller's array
  if (rgRefCounts)
   rgRefCounts[ihRow] = pRowBuff->ulRefCount;
 }

 // fill out-param
 if (pcRefCounted)
  (*pcRefCounted) = cAddRef;

 return ResultFromScode( S_OK );
}

Populating the Data Cache

IRowset::GetNextRows gets the next sequence of rows from the file and places them in the rowset's data cache. When GetNextRows is first called, it starts at the beginning of the file. After that, GetNextRows maintains information about its current position so that it can proceed forward from that position. The sample provider does not support rowsets with reversible direction. The sample provider does, however, support IRowset::RestartPosition, which repositions the next fetch position to the beginning of the file. The source code for IRowset::GetNextRows follows.

// CimpIRowset::GetNextRows --------------------------------------------------
//
// @mfunc Fetches rows in a sequential style, remembering the previous position
//
// @rdesc HRESULT
//  @flag S_OK        | Method Succeeded
//  @flag DB_S_ENDOFROWSET    | Reached end of rowset
//  @flag DB_E_CANTFETCHBACKWARDS | cRows was negative and we can't fetch backwards
//  @flag DB_E_ROWSNOTRELEASED  | Must release all HROWs before calling GetNextRows
//  @flag E_FAIL        | Provider-specific error
//  @flag E_INVALIDARG      | pcRowsObtained or prghRows was NULL
//  @flag E_OUTOFMEMORY     | Out of Memory
//  @flag OTHER       | Other HRESULTs returned by called functions
//
STDMETHODIMP CImpIRowset::GetNextRows
  (
  HCHAPTER hReserved,   //@parm IN | Reserved for future use. Ingored.
  LONG   cRowsToSkip,   //@parm IN | Rows to skip before reading
  LONG   cRows,     //@parm IN | Number of rows to fetch
  ULONG  *pcRowsObtained, //@parm OUT | Number of rows obtained
  HROW   **prghRows   //@parm OUT | Array of Hrows obtained
  )
{
 ULONG  cRowsTmp;
 BOOL   fExtraRow;
 ULONG  cSlotAlloc =0;
 ULONG  di;
 LONG   irow, ih;
 ULONG  irowFirst, irowLast;
 PROWBUFF prowbuff;
 HRESULT  hr;
 BYTE   *pbRowFirst;

 // Check validity of arguments.
 if (pcRowsObtained == NULL || prghRows == NULL)
  return ResultFromScode( E_INVALIDARG );

 // init out-params
 *pcRowsObtained = 0;

 // No-op case always succeeds.
 if (cRows == 0 && cRowsToSkip == 0)
  return ResultFromScode( S_OK );

 // This implementation doesn't support scrolling backward.
 if (cRows < 0 || cRowsToSkip < 0)
  return ResultFromScode( DB_E_CANTFETCHBACKWARDS );

 // Are there any unreleased rows?
 if ((m_pObj->m_prowbitsIBuffer)->ArrayEmpty() != S_OK)
  return ResultFromScode( DB_E_ROWSNOTRELEASED );
 assert( m_pObj->m_ulRowRefCount == 0 ); // should be true since array was empty

 // Is the cursor fully materialized (end-of-cursor condition)?
 if (m_pObj->m_dwStatus & STAT_ENDOFCURSOR)
  return ResultFromScode( DB_S_ENDOFROWSET );

 assert( m_pObj->m_rgbRowData );
 if (FAILED( m_pObj->Rebind((BYTE *) m_pObj->GetRowBuff( m_pObj->m_irowMin ))))
  return ResultFromScode( E_FAIL );

 // Sometimes we need an extra row to fetch data and copy it to appropriate place.
 fExtraRow = (cRows > 1);
 cRowsTmp = cRows + (fExtraRow ? 1 : 0);

 if (FAILED( hr = GetNextSlots( m_pObj->m_pIBuffer, cRowsTmp, &irowFirst )))
  return hr;
 cSlotAlloc = cRowsTmp;

 //
 // Fetch Data
 //
 if (cRowsToSkip)
 {
  // Calculate the new position
  m_pObj->m_irowFilePos += cRowsToSkip;

  // Check if skip causes END_OF_ROWSET
  if (m_pObj->m_irowFilePos > m_pObj->m_pFileio->GetRowCnt() ||
    m_pObj->m_irowFilePos <= 0)
  {
   m_pObj->m_dwStatus |= STAT_ENDOFCURSOR;
   return ResultFromScode( DB_S_ENDOFROWSET );
  }
 }

 pbRowFirst = (BYTE *) m_pObj->GetRowBuff( irowFirst );
 for (irow =1; irow <= cRows; irow++)
 {
  // Get the Data from the File into the row buffer
  if (S_FALSE == (hr =  m_pObj->m_pFileio->Fetch( m_pObj->m_irowFilePos + irow )))
  {
   m_pObj->m_dwStatus |= STAT_ENDOFCURSOR;
   break;
  }
  else
  {
   if (FAILED( hr ))
    return ResultFromScode( E_FAIL );
  }

  // Got a row, so copy it from bound row to the destination.
  // Very first row is Fetch buffer, we give out rows [2...cRows+1].
  if (fExtraRow)
   memcpy( pbRowFirst + (m_pObj->m_cbRowSize * irow), pbRowFirst, m_pObj->m_cbRowSize );
 }

 cRowsTmp = irow - 1; //Irow will be +1 because of For Loop
 m_pObj->m_irowLastFilePos = m_pObj->m_irowFilePos;
 m_pObj->m_irowFilePos += cRowsTmp;

 //
 // Through fetching many rows of data
 //
 // Allocate row handles for client.
 // Note that we need to use IMalloc for this.
 //
 // Should only malloc cRowsTmp, instead of cRows.
 //
 // Modified to use IMalloc.
 // Should malloc cRows, since client will assume it's that big.
 //

 *pcRowsObtained = cRowsTmp;
 if (*prghRows == NULL)
 {
  *prghRows = (HROW *) g_pIMalloc->Alloc( cRows*sizeof( HROW ));
 }

 if (NULL == *prghRows)
  return ResultFromScode( E_OUTOFMEMORY );

 //
 // Fill in the status information: Length, IsNull
 // May be able to wait until first call to GetData,
 // but have to do it sometime.
 //
 // Suggest keeping an array of structs of accessor info.
 // One element is whether accessor requires any status info or length info.
 // Then we could skip this whole section.
 //
 // Added check for cRowsTmp to MarkRows call.
 // Don't want to call if cRowsTmp==0.
 // (Range passed to MarkRows is inclusive, so can't specify marking 0 rows.)
 //
 // Note that SetSlots is a CBitArray member function -- not an IBuffer function.
 //
 // Bits are [0...n-1], row handles are [1...n].
 //
 // Cleanup. Row handles, bits, indices are the same [m....(m+n)], where m is some # >0,
 //
 // Added row-wise reference counts and cursor-wise reference counts.
 //

 // Set row handles, fix data length field and compute data status field.//
 di = fExtraRow ? 1 : 0;
 m_pObj->m_cRows = cRowsTmp;
 irowLast = irowFirst + di + cRowsTmp - 1;

 // Cleanup extra slots where no hRow actually was put..
 //  ** Because of less rows than asked for
 //  ** Because of temporary for for data transfer.
 if (fExtraRow)
  if (FAILED( hr = ReleaseSlots( m_pObj->m_pIBuffer, irowFirst, 1 )))
   return hr;
 if (cSlotAlloc > (cRowsTmp + di))
  if (FAILED( hr = ReleaseSlots( m_pObj->m_pIBuffer, irowFirst + cRowsTmp + di, (cSlotAlloc - cRowsTmp - di))))
   return hr;

 for (irow = (LONG) (irowFirst + di), ih =0; irow <= (LONG) irowLast; irow++, ih++)
 {
  // Increment the rows-read count,
  // then store it as the bookmark in the very first DWORD of the row.

  prowbuff = m_pObj->GetRowBuff( irow );

  // Insert the bookmark and its row number (from 1...n) into a hash table.
  // This allows us to quickly determine the presence of a row in mem, given the bookmark.
  // The bookmark is contained in the row buffer, at the very beginning.
  // Bookmark is the row number within the entire result set [1...num_rows_read].

  // This was a new Bookmark, not in memory,
  // so return to user (in *prghRows) the hRow we stored.
  prowbuff->ulRefCount++;
  prowbuff->pbBmk = (BYTE*) m_pObj->m_irowLastFilePos + ih + 1;
  m_pObj->m_ulRowRefCount++;

  (*prghRows)[ih] = (HROW) (irow);
 }

 if (m_pObj->m_dwStatus & STAT_ENDOFCURSOR)
  return ResultFromScode( DB_S_ENDOFROWSET );
 else
  return ResultFromScode( S_OK );
}

Retrieving Data from the Data Cache

IRowset::GetData enables consumers to retrieve data from the data cache. GetData uses the bindings in the accessor to determine how the data should be returned and what data should be returned to the consumer's buffer. Then, GetData converts the data in the cache to the type specified in the binding and transfers the converted data to the consumer. The source code for IRowset::GetData follows.

// CImpIRowset::GetData --------------------------------------------------
//
// @mfunc Retrieves data from the rowset's cache
//
// @rdesc HRESULT
//  @flag S_OK       | Method Succeeded
//  @flag DB_S_ERRORSOCCURRED   | Could not coerce a column value
//  @flag DB_E_BADACCESSORHANDLE | Invalid Accessor given
//  @flag DB_E_BADROWHANDLE  | Invalid row handle given
//  @flag E_INVALIDARG     | pData was NULL
//  @flag OTHER      | Other HRESULTs returned by called functions
//
STDMETHODIMP CImpIRowset::GetData
  (
  HROW    hRow,   //@parm IN | Row Handle
  HACCESSOR hAccessor,  //@parm IN | Accessor to use
  void   *pData   //@parm OUT | Pointer to buffer where data should go.
  )
{
 PACCESSOR pAccessor;
 ULONG   icol, ibind;
 ROWBUFF   *pRowBuff;
 COLUMNDATA  *pColumnData;
 DBBINDING *pBinding;
 ULONG   cBindings;
 ULONG   ulErrorCount;
 DBTYPE  dwSrcType;
 DBTYPE  dwDstType;
 void    *pSrc;
 void    *pDst;
 ULONG   ulSrcLength;
 ULONG   *pulDstLength;
 ULONG   ulDstMaxLength;
 DWORD   dwSrcStatus;
 DWORD   *pdwDstStatus;
 DWORD   dwPart;
 HRESULT   hr;

 // Coerce data for row 'hRow', according to hAccessor.
 // Put in location 'pData'.  Offsets and types are in hAccessor's bindings.
 //
 // Return S_OK if all lossless conversions,
 // return DB_S_ERRORSOCCURRED if lossy conversion (truncation, rounding, etc.)
 // Return E_FAIL, etc., if horrible errors.

 // GetItemOfExtBuffer is basically operator[].
 // It takes an index (or handle) (referenced from 1...n),
 // and a ptr for where to write the data.
 //
 // It holds ptrs to a variable-length ACCESSOR struct.
 // So we get the ACCESSOR ptr for the client's accessor handle.

 assert( m_pObj->m_pextbufferAccessor );
 hr = m_pObj->m_pextbufferAccessor->GetItemOfExtBuffer((ULONG) hAccessor, &pAccessor );
 if (FAILED( hr ) || pAccessor == NULL)
  return ResultFromScode( DB_E_BADACCESSORHANDLE );

 assert( pAccessor );
 cBindings = pAccessor->cBindings;
 pBinding  = pAccessor->rgBindings;

 // IsSlotSet returns S_OK  if row is marked.
 //       S_FALSE if row is not marked.
 // The "mark" means that there is data present in the row.
 // Rows are [1...n], slot marks are [0...n-1].
 if (m_pObj->m_prowbitsIBuffer->IsSlotSet((ULONG) hRow ) != S_OK)
  return ResultFromScode( DB_E_BADROWHANDLE );

 // Ensure a place to put data.
 if (pData == NULL)
  return ResultFromScode( E_INVALIDARG );

 pRowBuff = m_pObj->GetRowBuff((ULONG) hRow );

 // Internal error for a 0 reference count on this row,
 // since we depend on the slot-set stuff.
 assert( pRowBuff->ulRefCount );

 ulErrorCount = 0;
 for (ibind = 0; ibind < cBindings; ibind++)
 {
  icol = pBinding[ibind].iOrdinal;
  pColumnData = (COLUMNDATA *) ((BYTE *) pRowBuff + m_pObj->m_rgdwDataOffsets[icol]);

  dwSrcType  = m_pObj->m_rgdbcolinfo[icol].wType;
  pSrc     = &(pColumnData->bData);
  ulSrcLength  = pColumnData->dwLength;
  dwSrcStatus  = pColumnData->dwStatus;
  ulDstMaxLength = pBinding[ibind].cbMaxLen;
  dwDstType  = pBinding[ibind].wType;
  dwPart   = pBinding[ibind].dwPart;

  pDst     = dwPart & DBPART_VALUE ? ((BYTE*) pData + pBinding[ibind].obValue) : NULL;
  pulDstLength = dwPart & DBPART_LENGTH ? (ULONG *) ((BYTE*) pData + pBinding[ibind].obLength) : NULL;
  pdwDstStatus = dwPart & DBPART_STATUS ? (ULONG *) ((BYTE*) pData + pBinding[ibind].obStatus) : NULL;

  hr = g_pIDataConvert->DataConvert(
      dwSrcType,
      dwDstType,
      ulSrcLength,
      pulDstLength,
      pSrc,
      pDst,
      ulDstMaxLength,
      dwSrcStatus,
      pdwDstStatus,
      0, // bPrecision for conversion to DBNUMERIC
      0, // bScale for conversion to DBNUMERIC
      DBDATACONVERT_DEFAULT);
  if (FAILED( hr ))
   return hr;  // fatal error
  if (hr != ResultFromScode( S_OK ))
   ulErrorCount++; // can't coerce
 }

 // We report any lossy conversions with a special status.
 // Note that DB_S_ERRORSOCCURRED is a success, rather than failure.
 return ResultFromScode( ulErrorCount ? DB_S_ERRORSOCCURRED : S_OK );
}

Decrementing the Reference Count on Row Handles

IRowset::ReleaseRows decrements the reference count on the rows specified in the rghRows array. A consumer must call ReleaseRows once for each time a row was fetched or each time the row had its reference count incremented by AddRefRow. When the reference count reaches zero, the row is released if the rowset is in immediate update mode.

In providers that implement IRowsetUpdate, rows are released unless there are pending changes on the row; the sample provider always performs rowset updates in immediate mode, which means that changes are immediately applied to the underlying data source. Therefore, the sample provider does not recognize any changes as pending. The source code for IRowset::ReleaseRows follows.

// CImpIRowset::ReleaseRows ---------------------------------------
//
// @mfunc Releases row handles
//
// @rdesc HRESULT
//  @flag S_OK      | Method Succeeded
//  @flag E_INVALIDARG  | Invalid parameters were specified
//  @flag DB_E_BADROWHANDLE | Row handle was invalid
//
STDMETHODIMP CimpIRowset::ReleaseRows
  (
  ULONG   cRow,     //@parm IN  | Number of rows to release
  const HROW  rghRow[],   //@parm IN  | Array of handles of rows to be released
  DBROWOPTIONS  rgRowOptions[], //@parm IN  | Additional Options
  ULONG   *pcRowReleased, //@parm OUT | Count of rows actually released
  ULONG   rgRefCount[]  //@parm OUT | Array of refcnts for the rows
  )
{
 ULONG ihRow;
 BOOL  fEncounteredError;
 ROWBUFF *pRowBuff;
 ULONG cRowReleased = 0;

 // check params
 if (cRow && !rghRow)
  return ResultFromScode( E_INVALIDARG );

 // init out param
 if (pcRowReleased)
  *pcRowReleased = 0;

 fEncounteredError = FALSE;
 ihRow = 0;

 while (ihRow < cRow)
 {
  if ((m_pObj->m_prowbitsIBuffer)->IsSlotSet((ULONG) rghRow[ihRow] ) == S_OK)
  {
   // Found valid row, so decrement reference counts.
   // (Internal error for refcount to be 0 here, since slot set.)
   pRowBuff = m_pObj->GetRowBuff((ULONG) rghRow[ihRow] );
   assert( pRowBuff->ulRefCount != 0 );
   assert( m_pObj->m_ulRowRefCount != 0 );
   --pRowBuff->ulRefCount;
   --m_pObj->m_ulRowRefCount;

   if (rgRefCount)
    rgRefCount[ihRow] = pRowBuff->ulRefCount;

   if (pRowBuff->ulRefCount == 0)
   {
    ReleaseSlots( m_pObj->m_pIBuffer, (ULONG) rghRow[ihRow], 1 );
    cRowReleased++;
   }
   ihRow++;
  }
  else
  {
   // It is an error for client to try to release a row
   // for which "IsSetSlot" is false.  Client gave us an invalid handle.
   // Ignore it (we can't release it...) and report error when done.
   FEncounteredError = TRUE;
   if (rgRefCount)
    rgRefCount[ihRow] = 0;
   // stop looping if we hit an error
   break;
  }
 }

 if (pcRowReleased)
  *pcRowReleased = cRowReleased;

 if (fEncounteredError)
  return ResultFromScode( DB_E_BADROWHANDLE );
 else
  return ResultFromScode( S_OK );
}

Returning to the First Row of the Rowset

IRowset::RestartPosition moves the next fetch position used by GetNextRows to the first row of the rowset. The source code for IRowset::RestartPosition follows.

// CImpIRowset::ResartPosition ---------------------------------------
//
// @mfunc Repositions the next fetch position to the start of the rowset
//
// - all rows must be released before calling this method
// - it is not expensive to Restart us, because we are from a single table
//
//
// @rdesc HRESULT
//  @flag S_OK      | Method Succeeded
//  @flag DB_E_ROWSNOTRELEASED  | All HROWs must be released before calling
//
STDMETHODIMP CImpIRowset::RestartPosition
  (
  HCHAPTER  hReserved    //@parm IN | Reserved for future use.  Ignored.
  )
{
 // make sure all rows have been released
 if (S_OK != (m_pObj->m_prowbitsIBuffer)->ArrayEmpty())
  return DB_E_ROWSNOTRELEASED;
 assert( m_pObj->m_ulRowRefCount == 0 ); // should be true since array was empty

 // set "next fetch" position to the start of the rowset
 m_pObj->m_irowFilePos = 0;

 // clear "end of cursor" flag
 m_pObj->m_dwStatus &= ~STAT_ENDOFCURSOR;

 return ResultFromScode( S_OK );
}

Updating Rows

IRowsetChange enables consumers to change the values of columns in a row of data. If the consumer wants to change the data, it must first construct an accessor for the columns to be changed. IRowsetChange contains three methods: DeleteRows, InsertRow, and SetData. The sample provider does not implement the InsertRow method.

Setting the New Data Values

IRowsetChange::SetData sets new data values for columns in a row. SetData cannot be used to change values in a deleted row. When SetData updates a row, it overwrites the row data in the provider's data cache and in the underlying data source with at signs (@), causing the row to appear deleted. Then it appends the new row to the end of the file. Changes made through SetData are applied to the data source immediately. The source code for IRowsetChange::SetData follows; you can find the complete source code for IRowsetChange in RowChng.cpp.

// CImpIRowsetChange::SetData ------------------------------------------------
//
// @mfunc Sets new data values into fields of a row.
//
// @rdesc HRESULT
//  @flag S_OK       | The method succeeded
//  @flag E_OUTOFMEMORY    | Out of memory
//  @flag DB_E_BADACCESSORHANDLE | Bad accessor handle
//  @flag DB_E_READONLYACCESSOR  | Tried to write through a read-only accessor
//  @flag DB_E_BADROWHANDLE  | Invalid row handle
//  @flag E_INVALIDARG     | pData was NULL
//  @flag E_FAIL       | Provider-specific error
//  @flag OTHER      | Other HRESULTs returned by called functions
//
STDMETHODIMP CimpIRowsetChange::SetData
  (
  HROW    hRow,   //@parm IN | Handle of the row in which to set the data
  HACCESSOR hAccessor,  //@parm IN | Handle to the accessor to use
  void*  pData   //@parm IN | Pointer to the data
  )
{
 PACCESSOR   paccessor;
 ULONG     icol, ibind;
 BYTE*     pbProvRow;
 HRESULT   hr;
 ULONG     cBindings;
 DBBINDING*  pBinding;
 ULONG     dwErrorCount;
 DBTYPE    dwSrcType;
 DBTYPE    dwDstType;
 void*     pSrc;
 void*     pDst;
 ULONG     dwSrcLength;
 ULONG*    pdwDstLength;
 ULONG     dwDstMaxLength;
 DWORD     dwSrcStatus;
 DWORD*    pdwDstStatus;
 DWORD     dwPart;
 PCOLUMNDATA   pColumnData;
 BYTE    b;

 BYTE*     rgbRowDataSave = NULL;

 RgbRowDataSave = (BYTE *) malloc( m_pObj->m_cbRowSize );
 if (NULL == rgbRowDataSave)
  return ResultFromScode( E_OUTOFMEMORY );

 if ( m_pObj->m_pextbufferAccessor == NULL
   || FAILED( m_pObj->m_pextbufferAccessor->GetItemOfExtBuffer((ULONG) hAccessor, &paccessor))
   || paccessor == NULL)
 {
  return ResultFromScode( DB_E_BADACCESSORHANDLE );
 }

 assert( paccessor );

 cBindings = paccessor->cBindings;
 pBinding  = paccessor->rgBindings;

 // Is row handle right?
 if ((m_pObj->m_prowbitsIBuffer)->IsSlotSet((ULONG) hRow ) != S_OK)
  return ResultFromScode( DB_E_BADROWHANDLE );

 // Ensure a source of data.
 if (pData == NULL)
  return ResultFromScode( E_INVALIDARG );

 pbProvRow = (BYTE *) (m_pObj->GetRowBuff((ULONG) hRow ));

 // Save the row.
 memcpy( rgbRowDataSave, pbProvRow, m_pObj->m_cbRowSize );

 // Apply accessor to data.
 for (ibind = 0, dwErrorCount = 0; ibind < cBindings; ibind++)
 {
  icol = pBinding[ibind].iOrdinal;
  pColumnData = (COLUMNDATA *) (pbProvRow + m_pObj->m_rgdwDataOffsets[icol]);

  dwDstType  = m_pObj->m_rgdbcolinfo[icol].wType;
  pDst     = &(pColumnData->bData);
  pdwDstLength = (ULONG *) & (pColumnData->dwLength);
  pdwDstStatus = &(pColumnData->dwStatus);
  dwDstMaxLength = m_pObj->m_rgdbcolinfo[icol].ulColumnSize;

  dwPart   = pBinding[ibind].dwPart;
  dwSrcType  = pBinding[ibind].wType;

  if ((dwPart & DBPART_VALUE) == 0)
  {
   if (((dwPart & DBPART_STATUS)
     && (*(ULONG *) ((BYTE*) pData + pBinding[ibind].obStatus) & DBSTATUS_S_ISNULL))
      || ((dwPart & DBPART_LENGTH) && *(ULONG *) ((BYTE*) pData + pBinding[ibind].obLength) == 0))
   {
    pSrc = &b;
    b = 0x00;
   }
   else
    return ResultFromScode( E_FAIL );
   }
  else
  {
   pSrc = (void *) ((BYTE*) pData + pBinding[ibind].obValue);
  }

  dwSrcLength = (dwPart & DBPART_LENGTH) ? *(ULONG *) ((BYTE*) pData + pBinding[ibind].obLength) : 0;

  dwSrcStatus = (dwPart & DBPART_STATUS) ? *(ULONG *) ((BYTE*) pData + pBinding[ibind].obStatus)
    : DBSTATUS_S_OK;

  hr = g_pIDataConvert->DataConvert(
      dwSrcType,
      dwDstType,
      dwSrcLength,
      pdwDstLength,
      pSrc,
      pDst,
      dwDstMaxLength,
      dwSrcStatus,
      pdwDstStatus,
      0, // bPrecision for conversion to DBNUMERIC
      0, // bScale for conversion to DBNUMERIC
      DBDATACONVERT_SETDATABEHAVIOR);
  if (FAILED( hr ))
   return hr;  // fatal error
  if (hr != S_OK)
   dwErrorCount++; // rounding or truncation or can't coerce
 }

 // Carry out the update.
 if (FAILED( m_pObj->m_pFileio->UpdateRow((ULONG) ((PROWBUFF) pbProvRow)->pbBmk, m_pObj->m_rgdwDataOffsets, pbProvRow )))
 {
  // Restore the row to its previous state */
  memcpy( pbProvRow, rgbRowDataSave, m_pObj->m_cbRowSize );
  return ResultFromScode( E_FAIL );
 }

 free( rgbRowDataSave );

 // We report any lossy conversions with a special status.
 // Note that DB_S_ERRORSOCCURRED is a success, rather than failure.
 Return ResultFromScode( dwErrorCount ? DB_S_ERRORSOCCURRED : S_OK );
}

Deleting Rows

IRowsetChange also enables consumers to delete rows from the rowset. IRowsetChange::DeleteRows deletes rows from the rowset by overwriting the entire row with at signs (@). Deletions made through DeleteRows are applied to the data source immediately. The source code for IRowsetChange::DeleteRows follows; you can find the complete source code for IRowsetChange in RowChg.cpp.

// CImpIRowsetChange::DeleteRows ---------------------------------------
//
// @mfunc Deletes rows from the provider.  If Errors on individual rows
// occur, the DBERRORINFO array is updated to reflect the error and S_FALSE
// is returned instead of S_OK.
//
// @rdesc HRESULT indicating the status of the method
//  @Flag S_OK      | All row handles deleted
//  @Flag DB_S_ERRORSOCCURRED | Some, but not all, row handles deleted
//  @Flag E_INVALIDARG    | Arguments did not match spec.
//  @Flag E_OUTOFMEMORY   | Could not allocated error array
//
STDMETHODIMP CImpIRowsetChange::DeleteRows
  (
  HCHAPTER  hReserved,  //@parm IN | Reserved for future use
  ULONG   cRows,    //@parm IN | Number of rows to delete
  Const HROW  rghRows[],  //@parm IN | Array of handles to delete
  DBROWSTATUS rgRowStatus[] //@parm OUT  | Error information
  )
{
 ULONG     ihRow    = 0L;
 ULONG     cErrors  = 0L;
 ULONG     cRowReleased = 0L;
 BYTE*     pbProvRow;

 // If No Row handle, just return.
 If (0 == cRows)
  Return ResultFromScode( S_OK );

 // Check for Invalid Arguments
 if ((cRows >= 1) && (NULL == rghRows))
  return ResultFromScode( E_INVALIDARG );

 // Process row handles
 while (ihRow < cRows)
 {
  if (rgRowStatus)
   rgRowStatus[ihRow] = DBROWSTATUS_S_OK;

  // Is row handle valid
  if (S_OK != (m_pObj->m_prowbitsIBuffer)->IsSlotSet((ULONG) rghRows[ihRow] ))
  {
   // Log Error
   if (rgRowStatus)
    rgRowStatus[ihRow]= DBROWSTATUS_E_INVALID;

   cErrors++;
   ihRow++;
   continue;
  }

  // Get RowBuffer to look at which row this applies to
  pbProvRow = (BYTE *) (m_pObj->GetRowBuff((ULONG) rghRows[ihRow] ));

  // Has row already been deleted
  //  S_OK means deleted
  if (S_OK == m_pObj->m_pFileio->IsDeleted((ULONG) ((PROWBUFF) pbProvRow)->pbBmk ))
  {
   if (rgRowStatus)
    rgRowStatus[ihRow] = DBROWSTATUS_E_DELETED;
   cErrors++;
   ihRow++;
   continue;
  }

  // Delete the Row,
  if (S_OK != m_pObj->m_pFileio->DeleteRow((ULONG) ((PROWBUFF) pbProvRow)->pbBmk ))
  {
   // Some better decision as to what rowstatus to set could be done here..
   if (rgRowStatus)
    rgRowStatus[ihRow] = DBROWSTATUS_E_PERMISSIONDENIED;
   cErrors++;
   ihRow++;
   continue;
  }
 } //while

 // If everything went OK except errors in rows use DB_S_ERRORSOCCURRED.
 Return cErrors ? (cErrors < cRows) ?
   ResultFromScode(DB_S_ERRORSOCCURRED) :
   ResultFromScode(DB_E_ERRORSOCCURRED) :
   ResultFromScode(S_OK);
}


Microsoft Open Database Connectivity (ODBC) Start Page

ODBC Start Page

ODBC
Version 3.51

Purpose

The Microsoft® Open Database Connectivity (ODBC) interface is a C programming language interface that makes it possible for applications to access data from a variety of database management systems (DBMSs). The ODBC interface permits maximum interoperability—an application can access data in diverse DBMSs through a single interface. Furthermore, that application will be independent of any DBMS from which it accesses data. Users of the application can add software components called drivers, which interface between an application and a specific DBMS.

For more information about changes and enhancements for MDAC 2.6, see What's New in the MDAC SDK.

For information about support for Microsoft Data Access Components, see Getting Help from Microsoft Product Support Services.

Main Components of ODBC 3.51

ODBC Programmer's Reference

Documentation of ODBC interfaces and methods.

ODBC Data Source Administrator

The Microsoft ODBC Data Source Administrator manages database drivers and data sources.

ODBC Drivers

Documentation for the ODBC Desktop Database Drivers, the ODBC Driver for Oracle, and the Visual FoxPro® ODBC Driver.

ODBC Test

Microsoft ODBC Test is an ODBC-enabled application that you can use to test ODBC drivers.

ODBC Samples

Written in C++, these four code samples enable you to connect, execute queries, and convert user and system data set names to file names.

Feedback

Send feedback about the ODBC documentation or samples directly to the ODBC documentation team.

See Also

  • ODBC Technical Articles


What's New in ODBC

The ODBC documentation released with Microsoft® Data Access Components (MDAC) 2.6 contains no new enhancements.

Microsoft ODBC Desktop Database Drivers

The Desktop Database Drivers for ODBC, commonly known as the Jet Driver, has been removed from the MDAC Redist. The Jet Database Engine is not included in the MDAC configuration. However, for the 2.6 release, documentation for the Desktop Database Drivers is available in the SDK under ODBC Drivers.


ODBC Programmer's Reference

The ODBC Programmer's Reference

The ODBC Programmer's Reference is divided into the following parts:

  • Part 1, Introduction to ODBC , provides a brief history of Structured Query Language and ODBC, and conceptual information about the ODBC interface.
  • Part 2, Developing Applications and Drivers , contains information about developing applications that use the ODBC interface and drivers that implement it.
  • Part 3, Installing and Configuring ODBC Software, provides information about installation and a setup DLL function reference.
  • Part 4, API Reference, contains syntax and semantic information for all ODBC functions.
  • Appendixes contain technical details and reference tables for ODBC error codes, data types, SQL grammar, and so forth.

Working with the ODBC Documentation

The ODBC interface is designed for use with the C programming language. Use of the ODBC interface spans three areas: SQL statements, ODBC function calls, and C programming. This documentation assumes the following:

  • A working knowledge of the C programming language.
  • General DBMS knowledge and a familiarity with SQL.

The following typographic conventions are used.

Format | Used for
SELECT * FROM | Uppercase letters indicate SQL statements, macro names, and terms used at the operating-system command level.
RETCODE SQLFetch(hdbc) | The monospace font is used for sample command lines and program code.
argument | Italicized words indicate programmatic arguments, information that the user or the application must provide, or word emphasis.
SQLEndTran | Bold type indicates that syntax must be typed exactly as shown, including function names.
| | A vertical bar separates two mutually exclusive choices in a syntax line.
... | An ellipsis indicates that arguments can be repeated several times.
. . . | A column of three dots indicates continuation of previous lines of code.


Part 1 Introduction to ODBC

ODBC Programmer's Reference

Part 1: Introduction to ODBC

Part 1 of the ODBC Programmer's Reference offers a brief history of Structured Query Language and ODBC, and includes conceptual information about the ODBC interface. This section contains the following chapters:

  • Chapter 1: Introduction to ODBC
  • Chapter 2: An Introduction to SQL and ODBC
  • Chapter 3: ODBC Architecture


Chapter 1: Introduction to ODBC

Open Database Connectivity (ODBC) is a widely accepted application programming interface (API) for database access. It is based on the Call-Level Interface (CLI) specifications from X/Open and ISO/IEC for database APIs and uses Structured Query Language (SQL) as its database access language.

ODBC is designed for maximum interoperability—that is, the ability of a single application to access different database management systems (DBMSs) with the same source code. Database applications call functions in the ODBC interface, which are implemented in database-specific modules called drivers. The use of drivers isolates applications from database-specific calls in the same way that printer drivers isolate word processing programs from printer-specific commands. Because drivers are loaded at run time, a user only has to add a new driver to access a new DBMS; it is not necessary to recompile or relink the application.


Why Was ODBC Created?

Historically, companies used a single DBMS. All database access was done either through the front end of that system or through applications written to work exclusively with that system. However, as the use of computers grew and more computer hardware and software became available, companies started to acquire different DBMSs. The reasons were many: People bought what was cheapest, what was fastest, what they already knew, what was latest on the market, what worked best for a single application. Other reasons were reorganizations and mergers, where departments that previously had a single DBMS now had several.

The issue grew even more complex with the advent of personal computers. These computers brought in a host of tools for querying, analyzing, and displaying data, along with a number of inexpensive, easy-to-use databases. From then on, a single corporation often had data scattered across a myriad of desktops, servers, and minicomputers, stored in a variety of incompatible databases, and accessed by a vast number of different tools, few of which could get at all of the data.

The final challenge came with the advent of client/server computing, which seeks to make the most efficient use of computer resources. Inexpensive personal computers (the clients) sit on the desktop and provide both a graphical front end to the data and a number of inexpensive tools, such as spreadsheets, charting programs, and report builders. Minicomputers and mainframe computers (the servers) host the DBMSs, where they can use their computing power and central location to provide quick, coordinated data access. How then was the front-end software to be connected to the back-end databases?

A similar problem faced independent software vendors (ISVs). Vendors writing database software for minicomputers and mainframes were usually forced to write one version of an application for each DBMS or write DBMS-specific code for each DBMS they wanted to access. Vendors writing software for personal computers had to write data access routines for each different DBMS with which they wanted to work. This often meant a huge amount of resources were spent writing and maintaining data access routines rather than applications, and applications were often sold not on their quality but on whether they could access data in a given DBMS.

What both sets of developers needed was a way to access data in different DBMSs. The mainframe and minicomputer group needed a way to merge data from different DBMSs in a single application, while the personal computer group needed this ability as well as a way to write a single application that was independent of any one DBMS. In short, both groups needed an interoperable way to access data; they needed open database connectivity.


What Is ODBC?

Many misconceptions about ODBC exist in the computing world. To the end user, it is an icon in the Microsoft® Windows® Control Panel. To the application programmer, it is a library containing data access routines. To many others, it is the answer to all database access problems ever imagined.

First and foremost, ODBC is a specification for a database API. This API is independent of any one DBMS or operating system; although this manual uses C, the ODBC API is language-independent. The ODBC API is based on the CLI specifications from X/Open and ISO/IEC. ODBC 3.x fully implements both of these specifications—earlier versions of ODBC were based on preliminary versions of these specifications but did not fully implement them—and adds features commonly needed by developers of screen-based database applications, such as scrollable cursors.

The functions in the ODBC API are implemented by developers of DBMS-specific drivers. Applications call the functions in these drivers to access data in a DBMS-independent manner. A Driver Manager manages communication between applications and drivers.

Although Microsoft provides a Driver Manager for computers running Microsoft Windows NT® Server/Windows 2000 Server, Microsoft Windows NT Workstation/Windows 2000 Professional, and Microsoft Windows® 95/98, has written several ODBC drivers, and calls ODBC functions from some of its applications, anybody can write ODBC applications and drivers. In fact, the vast majority of ODBC applications and drivers available for computers running Windows NT Server/Windows 2000 Server, Windows NT Workstation/Windows 2000 Professional, and Windows 95/98 are produced by companies other than Microsoft. Furthermore, ODBC drivers and applications exist on the Macintosh® and a variety of UNIX platforms.

To help application and driver developers, Microsoft offers an ODBC Software Development Kit (SDK) for computers running Windows NT Server/Windows 2000 Server, Windows NT Workstation/Windows 2000 Professional, and Windows 95/98 that provides the Driver Manager, installer DLL, test tools, and sample applications. Microsoft has teamed with Visigenic Software to port these SDKs to the Macintosh and a variety of UNIX platforms.

It is important to understand that ODBC is designed to expose database capabilities, not supplement them. Thus, application writers should not expect that using ODBC will suddenly transform a simple database into a fully featured relational database engine. Nor are driver writers expected to implement functionality not found in the underlying database. An exception to this is that developers who write drivers that directly access file data (such as data in an Xbase file) are required to write a database engine that supports at least minimal SQL functionality. Another exception is that the ODBC component of the Microsoft® Data Access Components (MDAC) SDK provides a cursor library that simulates scrollable cursors for drivers that implement a certain level of functionality.

Applications that use ODBC are responsible for any cross-database functionality. For example, ODBC is not a heterogeneous join engine, nor is it a distributed transaction processor. However, because it is DBMS-independent, it can be used to build such cross-database tools.


ODBC and the Standard CLI

ODBC aligns with the following specifications and standards that deal with the Call-Level Interface (CLI). (The ODBC features are a superset of each of these standards.)

  • The X/Open CAE Specification "Data Management: SQL Call-Level Interface (CLI)"
  • ISO/IEC 9075-3:1995 (E) Call-Level Interface (SQL/CLI)

As a result of this alignment, the following are true:

  • An application written to the X/Open and ISO CLI specifications will work with an ODBC 3.x driver or a standards-compliant driver when it is compiled with the ODBC 3.x header files and linked with ODBC 3.x libraries, and when it gains access to the driver through the ODBC 3.x Driver Manager.
  • A driver written to the X/Open and ISO CLI specifications will work with an ODBC 3.x application or a standards-compliant application when it is compiled with the ODBC 3.x header files and linked with ODBC 3.x libraries, and when the application gains access to the driver through the ODBC 3.x Driver Manager. (For more information, see "Standards-Compliant Applications and Drivers" in Chapter 17: Programming Considerations.)

The Core interface conformance level encompasses all the features in the ISO CLI and all the nonoptional features in the X/Open CLI. Optional features of the X/Open CLI appear in higher interface conformance levels. Because all ODBC 3.x drivers are required to support the features in the Core interface conformance level, the following are true:

  • An ODBC 3.x driver will support all the features used by a standards-compliant application.
  • An ODBC 3.x application using only the features in ISO CLI and the nonoptional features of the X/Open CLI will work with any standards-compliant driver.

In addition to the call-level interface specifications contained in the ISO/IEC and X/Open CLI standards, ODBC implements the following features. (Some of these features existed in versions of ODBC prior to ODBC 3.x.)

  • Multirow fetches by a single function call
  • Binding to an array of parameters
  • Bookmark support including fetching by bookmark, variable-length bookmarks, and bulk update and delete by bookmark operations on discontiguous rows
  • Row-wise binding
  • Binding offsets
  • Support for batches of SQL statements, either in a stored procedure or as a sequence of SQL statements executed through SQLExecute or SQLExecDirect
  • Exact or approximate cursor row counts
  • Positioned update and delete operations and batched updates and deletes by function call (SQLSetPos)
  • Catalog functions that extract information from the information schema without the need for supporting information schema views
  • Escape sequences for outer joins, scalar functions, datetime literals, interval literals, and stored procedures
  • Code-page translation libraries
  • Reporting of a driver's ANSI-conformance level and SQL support
  • On-demand automatic population of implementation parameter descriptor
  • Enhanced diagnostics and row and parameter status arrays
  • Datetime, interval, numeric/decimal, and 64-bit integer application buffer types
  • Asynchronous execution
  • Stored procedure support, including escape sequences, output parameter binding mechanisms, and catalog functions
  • Connection enhancements including support for connection attributes and attribute browsing


Chapter 2: An Introduction to SQL and ODBC

ODBC was created to provide a uniform method of access to different, or heterogeneous, database management systems. This chapter discusses some of the concepts and history behind the development of ODBC, including:

  • SQL and the various ways that applications use it.
  • How network database access is done in the real world and what parts of this process can easily be standardized.
  • Strategies used by ODBC and why those strategies were chosen.


Structured Query Language (SQL)

A typical DBMS allows users to store, access, and modify data in an organized, efficient way. Originally, the users of DBMSs were programmers. Accessing the stored data required writing a program in a programming language such as COBOL. While these programs were often written to present a friendly interface to a nontechnical user, access to the data itself required the services of a knowledgeable programmer. Casual access to the data was not practical.

Users were not entirely happy with this situation. While they could access data, it often required convincing a DBMS programmer to write special software. For example, if a sales department wanted to see the total sales in the previous month by each of its salespeople and wanted this information ranked in order by each salesperson's length of service in the company, it had two choices: Either a program already existed that allowed the information to be accessed in exactly this way, or the department had to ask a programmer to write such a program. In many cases, this was more work than it was worth, and it was always an expensive solution for one-time, or ad hoc, inquiries. As more and more users wanted easy access, this problem grew larger and larger.

Allowing users to access data on an ad hoc basis required giving them a language in which to express their requests. A single request to a database is defined as a query; such a language is called a query language. Many query languages were developed for this purpose, but one of these became the most popular: Structured Query Language, invented at IBM in the 1970s. It is more commonly known by its acronym, SQL, and is pronounced both as "ess-cue-ell" and as "sequel"; this manual uses the former pronunciation. SQL became an ANSI standard in 1986 and an ISO standard in 1987; it is used today in a great many database management systems.

Although SQL solved the ad hoc needs of users, the need for data access by computer programs did not go away. In fact, most database access still was (and is) programmatic, in the form of regularly scheduled reports and statistical analyses, data entry programs such as those used for order entry, and data manipulation programs, such as those used to reconcile accounts and generate work orders.

These programs also use SQL, using one of the following three techniques:

  • Embedded SQL, in which SQL statements are embedded in a host language such as C or COBOL.
  • SQL modules, in which SQL statements are compiled on the DBMS and called from a host language.
  • Call-level interface, or CLI, which consists of functions called to pass SQL statements to the DBMS and to retrieve results from the DBMS.

Note It is a historical accident that the term call-level interface is used instead of application programming interface (API), another term for the same thing. In the database world, API is used to describe SQL itself: SQL is the API to a DBMS.

Of these choices, embedded SQL is the most commonly used, although most major DBMSs support proprietary CLIs.


Processing an SQL Statement

Before discussing the techniques for using SQL programmatically, it is necessary to discuss how an SQL statement is processed. The steps involved are common to all three techniques, although each technique performs them at different times. The following illustration shows the steps involved in processing an SQL statement, which are discussed throughout the rest of this section.

To process an SQL statement, a DBMS performs the following five steps:

  1. The DBMS first parses the SQL statement. It breaks the statement up into individual words, called tokens, makes sure that the statement has a valid verb and valid clauses, and so on. Syntax errors and misspellings can be detected in this step.
  2. The DBMS validates the statement. It checks the statement against the system catalog. Do all the tables named in the statement exist in the database? Do all of the columns exist and are the column names unambiguous? Does the user have the required privileges to execute the statement? Certain semantic errors can be detected in this step.
  3. The DBMS generates an access plan for the statement. The access plan is a binary representation of the steps that are required to carry out the statement; it is the DBMS equivalent of executable code.
  4. The DBMS optimizes the access plan. It explores various ways to carry out the access plan. Can an index be used to speed a search? Should the DBMS first apply a search condition to Table A and then join it to Table B, or should it begin with the join and use the search condition afterward? Can a sequential search through a table be avoided or reduced to a subset of the table? After exploring the alternatives, the DBMS chooses one of them.
  5. The DBMS executes the statement by running the access plan.

The steps used to process an SQL statement vary in the amount of database access they require and the amount of time they take. Parsing an SQL statement does not require access to the database and can be done very quickly. Optimization, on the other hand, is a very CPU-intensive process and requires access to the system catalog. For a complex, multitable query, the optimizer may explore thousands of different ways of carrying out the same query. However, the cost of executing the query inefficiently is usually so high that the time spent in optimization is more than regained in increased query execution speed. This is even more significant if the same optimized access plan can be used over and over to perform repetitive queries.


Embedded SQL

The first technique for sending SQL statements to the DBMS is embedded SQL. Because SQL does not use variables and control-of-flow statements, it is often used as a database sublanguage that can be added to a program written in a conventional programming language, such as C or COBOL. This is a central idea of embedded SQL: placing SQL statements in a program written in a host programming language. Briefly, the following techniques are used to embed SQL statements in a host language:

  • Embedded SQL statements are processed by a special SQL precompiler. All SQL statements begin with an introducer and end with a terminator, both of which flag the SQL statement for the precompiler. The introducer and terminator vary with the host language. For example, the introducer is "EXEC SQL" in C and "&SQL(" in MUMPS, and the terminator is a semicolon (;) in C and a right parenthesis in MUMPS.
  • Variables from the application program, called host variables, can be used in embedded SQL statements wherever constants are allowed. These can be used on input to tailor an SQL statement to a particular situation and on output to receive the results of a query.
  • Queries that return a single row of data are handled with a singleton SELECT statement; this statement specifies both the query and the host variables in which to return data.
  • Queries that return multiple rows of data are handled with cursors. A cursor keeps track of the current row within a result set. The DECLARE CURSOR statement defines the query, the OPEN statement begins the query processing, the FETCH statement retrieves successive rows of data, and the CLOSE statement ends query processing.
  • While a cursor is open, positioned update and positioned delete statements can be used to update or delete the row currently selected by the cursor.


Embedded SQL Example

The following code is a simple embedded SQL program, written in C. The program illustrates many, but not all, of the embedded SQL techniques. The program prompts the user for an order number, retrieves the customer number, salesperson, and status of the order, and displays the retrieved information on the screen.

main()
{
   EXEC SQL INCLUDE SQLCA;
   EXEC SQL BEGIN DECLARE SECTION;
      int OrderID;         /* Employee ID (from user)         */
      int CustID;            /* Retrieved customer ID         */
      char SalesPerson[10]   /* Retrieved salesperson name      */
      char Status[6]         /* Retrieved order status        */
   EXEC SQL END DECLARE SECTION;

   /* Set up error processing */
   EXEC SQL WHENEVER SQLERROR GOTO query_error;
   EXEC SQL WHENEVER NOT FOUND GOTO bad_number;

   /* Prompt the user for order number */
   printf ("Enter order number: ");
   scanf ("%d", &OrderID);

   /* Execute the SQL query */
   EXEC SQL SELECT CustID, SalesPerson, Status
      FROM Orders
      WHERE OrderID = :OrderID
      INTO :CustID, :SalesPerson, :Status;

   /* Display the results */
   printf ("Customer number:  %d\n", CustID);
   printf ("Salesperson: %s\n", SalesPerson);
   printf ("Status: %s\n", Status);
   exit();

query_error:
   printf ("SQL error: %ld\n", sqlca->sqlcode);
   exit();

bad_number:
   printf ("Invalid order number.\n");
   exit();
}

Note the following about this program:

  • Host Variables. The host variables are declared in a section enclosed by the BEGIN DECLARE SECTION and END DECLARE SECTION keywords. Each host variable name is prefixed by a colon (:) when it appears in an embedded SQL statement. The colon allows the precompiler to distinguish between host variables and database objects, such as tables and columns, that have the same name.
  • Data Types. The data types supported by a DBMS and a host language can be quite different. This affects host variables because they play a dual role. On one hand, host variables are program variables, declared and manipulated by host language statements. On the other hand, they are used in embedded SQL statements to retrieve database data. If there is no host language type that corresponds to a DBMS data type, the DBMS automatically converts the data. However, because each DBMS has its own rules and idiosyncrasies associated with the conversion process, the host variable types must be chosen carefully.
  • Error Handling. The DBMS reports run-time errors to the applications program through an SQL Communications Area, or SQLCA. In the preceding code example, the first embedded SQL statement is INCLUDE SQLCA. This tells the precompiler to include the SQLCA structure in the program. This is required whenever the program will process errors returned by the DBMS. The WHENEVER...GOTO statement tells the precompiler to generate error-handling code that branches to a specific label when an error occurs.
  • Singleton SELECT. The statement used to return the data is a singleton SELECT statement; that is, it returns only a single row of data. Therefore, the code example does not declare or use cursors.


Compiling an Embedded SQL Program

Because an embedded SQL program contains a mix of SQL and host language statements, it cannot be submitted directly to a compiler for the host language. Instead, it is compiled through a multistep process. Although this process differs from product to product, the steps are roughly the same for all products.

This illustration shows the steps necessary to compile an embedded SQL program.

Five steps are involved in compiling an embedded SQL program:

  1. The embedded SQL program is submitted to the SQL precompiler, a programming tool. The precompiler scans the program, finds the embedded SQL statements, and processes them. A different precompiler is required for each programming language supported by the DBMS. DBMS products typically offer precompilers for one or more languages, including C, Pascal, COBOL, Fortran, Ada, PL/I, and various assembly languages.
  2. The precompiler produces two output files. The first file is the source file, stripped of its embedded SQL statements. In their place, the precompiler substitutes calls to proprietary DBMS routines that provide the run-time link between the program and the DBMS. Typically, the names and the calling sequences of these routines are known only to the precompiler and the DBMS; they are not a public interface to the DBMS. The second file is a copy of all the embedded SQL statements used in the program. This file is sometimes called a database request module, or DBRM.
  3. The source file output from the precompiler is submitted to the standard compiler for the host programming language (such as a C or COBOL compiler). The compiler processes the source code and produces object code as its output. Note that this step has nothing to do with the DBMS or with SQL.
  4. The linker accepts the object modules generated by the compiler, links them with various library routines, and produces an executable program. The library routines linked into the executable program include the proprietary DBMS routines described in step 2.
  5. The database request module generated by the precompiler is submitted to a special binding utility. This utility examines the SQL statements, parses, validates, and optimizes them, and then produces an access plan for each statement. The result is a combined access plan for the entire program, representing an executable version of the embedded SQL statements. The binding utility stores the plan in the database, usually assigning it the name of the application program that will use it. Whether this step takes place at compile time or run time depends on the DBMS.

Notice that the steps used to compile an embedded SQL program correlate very closely with the steps described earlier in "Processing an SQL Statement." In particular, notice that the precompiler separates the SQL statements from the host language code, and the binding utility parses and validates the SQL statements and creates the access plans. In DBMSs where step 5 takes place at compile time, the first four steps of processing an SQL statement take place at compile time, while the last step (execution) takes place at run time. This has the effect of making query execution in such DBMSs very fast.


Static SQL

The embedded SQL shown in the "Embedded SQL Example" section is known as static SQL. It is called static SQL because the SQL statements in the program are static; that is, they do not change each time the program is run. As described in the previous section, these statements are compiled when the rest of the program is compiled.

Static SQL works well in many situations and can be used in any application for which the data access can be determined at program design time. For example, an order-entry program always uses the same statement to insert a new order, and an airline reservation system always uses the same statement to change the status of a seat from available to reserved. Each of these statements would be generalized through the use of host variables; different values can be inserted in a sales order, and different seats can be reserved. Because such statements can be hard-coded in the program, such programs have the advantage that the statements need to be parsed, validated, and optimized only once, at compile time. This results in relatively fast code.


Dynamic SQL

Although static SQL works well in many situations, there is a class of applications in which the data access cannot be determined in advance. For example, suppose a spreadsheet allows a user to enter a query, which the spreadsheet then sends to the DBMS to retrieve data. The contents of this query obviously cannot be known to the programmer when the spreadsheet program is written.

To solve this problem, the spreadsheet uses a form of embedded SQL called dynamic SQL. Unlike static SQL statements, which are hard-coded in the program, dynamic SQL statements can be built at run time and placed in a string host variable. They are then sent to the DBMS for processing. Because the DBMS must generate an access plan at run time for dynamic SQL statements, dynamic SQL is generally slower than static SQL. When a program containing dynamic SQL statements is compiled, the dynamic SQL statements are not stripped from the program, as in static SQL. Instead, they are replaced by a function call that passes the statement to the DBMS; static SQL statements in the same program are treated normally.

The simplest way to execute a dynamic SQL statement is with an EXECUTE IMMEDIATE statement. This statement passes the SQL statement to the DBMS for compilation and execution.

One disadvantage of the EXECUTE IMMEDIATE statement is that the DBMS must go through each of the five steps of processing an SQL statement each time the statement is executed. The overhead involved in this process can be significant if many statements are executed dynamically, and it is wasteful if those statements are similar. To address this situation, dynamic SQL offers an optimized form of execution called prepared execution, which uses the following steps:

  1. The program constructs an SQL statement in a buffer, just as it does for the EXECUTE IMMEDIATE statement. Instead of host variables, a question mark (?) can be substituted for a constant anywhere in the statement text to indicate that a value for the constant will be supplied later. The question mark is called as a parameter marker.
  2. The program passes the SQL statement to the DBMS with a PREPARE statement, which requests that the DBMS parse, validate, and optimize the statement and generate an execution plan for it. The program then uses an EXECUTE statement (not an EXECUTE IMMEDIATE statement) to execute the PREPARE statement at a later time. It passes parameter values for the statement through a special data structure called the SQL Data Area or SQLDA.
  3. The program can use the EXECUTE statement repeatedly, supplying different parameter values each time the dynamic statement is executed.

Prepared execution is still not the same as static SQL. In static SQL, the first four steps of processing an SQL statement take place at compile time. In prepared execution, these steps still take place at run time, but they are performed only once; execution of the plan takes place only when EXECUTE is called. This helps eliminate some of the performance disadvantages inherent in the architecture of dynamic SQL. The next illustration shows the differences between static SQL, dynamic SQL with immediate execution, and dynamic SQL with prepared execution.


SQL Modules

The second technique for sending SQL statements to the DBMS is through modules. Briefly, a module consists of a group of procedures, which are called from the host programming language. Each procedure contains a single SQL statement, and data is passed to and from the procedure through parameters.

A module can be thought of as an object library that is linked to the application code. However, exactly how the procedures and the rest of the application are linked is implementation-dependent. For example, the procedures could be compiled into object code and linked directly to the application code, they could be compiled and stored on the DBMS and calls to access plan identifiers placed in the application code, or they could be interpreted at run time.

The main advantage of modules is that they cleanly separate SQL statements from the programming language. In theory, it should be possible to change one without changing the other and simply relink them.


Call-Level Interfaces

The final technique for sending SQL statements to the DBMS is through a call-level interface (CLI). A call-level interface provides a library of DBMS functions that can be called by the application program. Thus, instead of trying to blend SQL with another programming language, a call-level interface is similar to the routine libraries most programmers are accustomed to using, such as the string, I/O, or math libraries in C. Note that DBMSs that support embedded SQL already have a call-level interface, the calls to which are generated by the precompiler. However, these calls are undocumented and subject to change without notice.

Call-level interfaces are commonly used in client/server architectures, in which the application program (the client) resides on one computer and the DBMS (the server) resides on a different computer. The application calls CLI functions on the local system, and those calls are sent across the network to the DBMS for processing.

A call-level interface is similar to dynamic SQL, in that SQL statements are passed to the DBMS for processing at run time, but it differs from embedded SQL as a whole in that there are no embedded SQL statements and no precompiler is required.

Using a call-level interface typically involves the following steps:

  1. The application calls a CLI function to connect to the DBMS.
  2. The application builds an SQL statement and places it in a buffer. It then calls one or more CLI functions to send the statement to the DBMS for preparation and execution.
  3. If the statement is a SELECT statement, the application calls a CLI function to return the results in application buffers. Typically, this function returns one row or one column of data at a time.
  4. The application calls a CLI function to disconnect from the DBMS.


Database Access Architecture

One of the questions in the development of ODBC was which part of the database access architecture to standardize. The SQL programming interfaces described in the previous section—embedded SQL, SQL modules, and CLIs—are only one part of this architecture. In fact, because ODBC was primarily intended to connect personal computer–based applications to minicomputer and mainframe DBMSs, there were also a number of network components, some of which could be standardized.


Network Database Access

Accessing a database across a network requires a number of components, each of which is independent of, and resides beneath, the programming interface. These components are shown in the following illustration.

A further description of each component follows:

  • Programming Interface. As described earlier in this chapter, the programming interface contains the calls made by the application. These interfaces (embedded SQL, SQL modules, and call-level interfaces) are generally specific to each DBMS, although they are usually based on an ANSI or ISO standard.
  • Data Stream Protocol. The data stream protocol describes the stream of data transferred between the DBMS and its client. For example, the protocol might require the first byte to describe what the rest of the stream contains: an SQL statement to be executed, a returned error value, or returned data. The format of the rest of the data in the stream would then depend on this flag. For example, an error stream might contain the flag, a 2-byte integer error code, a 2-byte integer error message length, and an error message.

    The data stream protocol is a logical protocol and is independent of the protocols used by the underlying network. Thus, a single data stream protocol can generally be used on a number of different networks. Data stream protocols are typically proprietary and have been optimized to work with a particular DBMS.

  • Interprocess Communication Mechanism. The interprocess communication (IPC) mechanism is the process by which one process communicates with another. Examples include named pipes, TCP/IP sockets, and DECnet sockets. The choice of IPC mechanism is constrained by the operating system and network being used.
  • Network Protocol. The network protocol is used to transport the data stream over a network. It can be considered the plumbing that supports the IPC mechanisms used to implement the data stream protocol, as well as supporting basic network operations such as file transfers and print sharing. Network protocols include NetBEUI, TCP/IP, DECnet, and SPX/IPX and are specific to each network.


Standard Database Access Architectures

In looking at the database access components described in the preceding section, it turns out that two of them—programming interfaces and data stream protocols—are good candidates for standardization. The other two components—IPC mechanism and network protocols—not only reside at too low a level but they are both highly dependent on the network and operating system. There is also a third approach—gateways—that provides possibilities for standardization.


Standard Programming Interface

The programming interface is perhaps the most obvious candidate for standardization. In fact, when ODBC was being developed, ANSI and ISO already provided standards for embedded SQL and SQL modules. Although no standards existed for a database CLI, the SQL Access Group—an industry consortium of database vendors—was considering whether to create one; parts of ODBC later became the basis for their work.

One of the requirements for ODBC was that a single application binary had to work with multiple DBMSs. It is for this reason that ODBC does not use embedded SQL or module languages. Although the language in embedded SQL and module languages is standardized, each is tied to DBMS-specific precompilers. Thus, applications must be recompiled for each DBMS and the resulting binaries work only with a single DBMS. While this is acceptable for the low-volume applications found in the minicomputer and mainframe worlds, it is unacceptable in the personal computer world. First, it is a logistical nightmare to deliver multiple versions of high-volume, shrink-wrapped software to customers; second, personal computer applications often need to access multiple DBMSs simultaneously.

On the other hand, a call-level interface can be implemented through libraries, or database drivers, that reside on each local machine; a different driver is required for each DBMS. Because modern operating systems can load such libraries (such as dynamic-link libraries on the Microsoft® Windows® operating system) at run time, a single application can access data from different DBMSs without recompilation and can also access data from multiple databases simultaneously. As new database drivers become available, users can just install these on their computers without having to modify, recompile, or relink their database applications. Furthermore, a call-level interface was a good candidate for ODBC because Windows—the platform for which ODBC was originally developed—already made extensive use of such libraries.


Standard Data Stream Protocol

A standard data stream protocol is one way to access data in heterogeneous DBMSs. In fact, standard data stream protocols already exist: the proposed ANSI/ISO Relational Data Access (RDA) standard and the IBM Distributed Relational Database Architecture (DRDA). Although both systems show promise, neither is widely implemented today.


Standard Gateway

A gateway is a piece of software that causes one DBMS to look like another. That is, the gateway accepts the programming interface, SQL grammar, and data stream protocol of a single DBMS and translates it to the programming interface, SQL grammar, and data stream protocol of the hidden DBMS. For example, applications written to use Microsoft® SQL Server™ can also access DB2 data through the Micro Decisionware DB2 Gateway; this product causes DB2 to look like SQL Server. When gateways are used, a different gateway must be written for each target database.

Although gateways are limited by architectural differences among DBMSs, they are a good candidate for standardization. However, if all DBMSs are to standardize on the programming interface, SQL grammar, and data stream protocol of a single DBMS, whose DBMS is to be chosen as the standard? Certainly no commercial DBMS vendor is likely to agree to standardize on a competitor's product. And if a standard programming interface, SQL grammar, and data stream protocol are developed, no gateway is needed.


The ODBC Solution

The question, then, is how does ODBC standardize database access? There are two architectural requirements:

  • Applications must be able to access multiple DBMSs using the same source code without recompiling or relinking.
  • Applications must be able to access multiple DBMSs simultaneously.

And there is one more question, due to marketplace reality:

  • Which DBMS features should ODBC expose? Only features that are common to all DBMSs or any feature that is available in any DBMS?

ODBC solves these problems in the following manner:

  • ODBC is a call-level interface. To solve the problem of how applications access multiple DBMSs using the same source code, ODBC defines a standard CLI. This contains all of the functions in the CLI specifications from X/Open and ISO/IEC and provides additional functions commonly required by applications.

    A different library, or driver, is required for each DBMS that supports ODBC. The driver implements the functions in the ODBC API. To use a different driver, the application does not need to be recompiled or relinked. Instead, the application simply loads the new driver and calls the functions in it. To access multiple DBMSs simultaneously, the application loads multiple drivers. How drivers are supported is operating system–specific. For example, on the Microsoft® Windows® operating system, drivers are dynamic-link libraries (DLLs).

  • ODBC defines a standard SQL grammar. In addition to a standard call-level interface, ODBC defines a standard SQL grammar. This grammar is based on the X/Open SQL CAE specification. Differences between the two grammars are minor and primarily due to the differences between the SQL grammar required by embedded SQL (X/Open) and a CLI (ODBC). There are also some extensions to the grammar to expose commonly available language features not covered by the X/Open grammar.

    Applications can submit statements using ODBC or DBMS-specific grammar. If a statement uses ODBC grammar that is different from DBMS-specific grammar, the driver converts it before sending it to the data source. However, such conversions are rare because most DBMSs already use standard SQL grammar.

  • ODBC provides a Driver Manager to manage simultaneous access to multiple DBMSs. Although the use of drivers solves the problem of accessing multiple DBMSs simultaneously, the code to do this can be complex. Applications that are designed to work with all drivers cannot be statically linked to any drivers. Instead, they must load drivers at run time and call the functions in them through a table of function pointers. The situation becomes more complex if the application uses multiple drivers simultaneously.

    Rather than forcing each application to do this, ODBC provides a Driver Manager. The Driver Manager implements all of the ODBC functions—mostly as pass-through calls to ODBC functions in drivers—and is statically linked to the application or loaded by the application at run time. Thus, the application calls ODBC functions by name in the Driver Manager, rather than by pointer in each driver.

    When an application needs a particular driver, it first requests a connection handle with which to identify the driver and then requests that the Driver Manager load the driver. The Driver Manager loads the driver and stores the address of each function in the driver. To call an ODBC function in the driver, the application calls that function in the Driver Manager and passes the connection handle for the driver. The Driver Manager then calls the function by using the address it stored earlier.

  • ODBC exposes a significant number of DBMS features but does not require drivers to support all of them. If ODBC exposed only features that are common to all DBMSs, it would be of little use; after all, the reason so many different DBMSs exist today is that they have different features. If ODBC exposed every feature that is available in any DBMS, it would be impossible for drivers to implement.

    Instead, ODBC exposes a significant number of features—more than are supported by most DBMSs—but requires drivers to implement only a subset of those features. Drivers implement the remaining features only if they are supported by the underlying DBMS or if they choose to emulate them. Thus, applications can be written to exploit the features of a single DBMS as exposed by the driver for that DBMS, to use only those features used by all DBMSs, or to check for support of a particular feature and react accordingly.

    So that an application can determine what features a driver and DBMS support, ODBC provides two functions (SQLGetInfo and SQLGetFunctions) that return general information about the driver and DBMS capabilities and a list of functions the driver supports. ODBC also defines API and SQL grammar conformance levels, which specify broad ranges of features supported by the driver. For more information, see "Conformance Levels" in Chapter 4: ODBC Fundamentals.

    It is important to remember that ODBC defines a common interface for all of the features it exposes. Because of this, applications contain feature-specific code, not DBMS-specific code, and can use any drivers that expose those features. One advantage of this is that applications do not need to be updated when the features supported by a DBMS are enhanced; instead, when an updated driver is installed, the application automatically uses the features because its code is feature-specific, not driver-specific or DBMS-specific.


Chapter 3: ODBC Architecture

The ODBC architecture has four components:

  • Application. Performs processing and calls ODBC functions to submit SQL statements and retrieve results.
  • Driver Manager. Loads and unloads drivers on behalf of an application. Processes ODBC function calls or passes them to a driver.
  • Driver. Processes ODBC function calls, submits SQL requests to a specific data source, and returns results to the application. If necessary, the driver modifies an application's request so that the request conforms to syntax supported by the associated DBMS.
  • Data source. Consists of the data the user wants to access and its associated operating system, DBMS, and network platform (if any) used to access the DBMS.

The following illustration shows the relationship between these four components.

Note the following about this diagram. First, multiple drivers and data sources can exist, which allows the application to simultaneously access data from more than one data source. Second, the ODBC API is used in two places: between the application and the Driver Manager, and between the Driver Manager and each driver. The interface between the Driver Manager and the drivers is sometimes referred to as the service provider interface, or SPI. For ODBC, the application programming interface (API) and the service provider interface (SPI) are the same; that is, the Driver Manager and each driver have the same interface to the same functions.


Applications

An application is a program that calls the ODBC API to access data. Although many types of applications are possible, most fall into three categories, which are used as examples throughout this guide.

  • Generic Applications. These are also referred to as shrink-wrapped applications or off-the-shelf applications. Generic applications are designed to work with a variety of different DBMSs. Examples include a spreadsheet or statistics package that uses ODBC to import data for further analysis and a word processor that uses ODBC to get a mailing list from a database.

    An important subcategory of generic applications is application development environments, such as PowerBuilder or Microsoft® Visual Basic®. Although the applications constructed with these environments will probably work only with a single DBMS, the environment itself needs to work with multiple DBMSs.

    What all generic applications have in common is that they are highly interoperable among DBMSs and they need to use ODBC in a relatively generic manner. For more information about interoperability, see "Choosing a Level of Interoperability" in Chapter 16: Interoperability.

  • Vertical Applications. Vertical applications perform a single type of task, such as order entry or tracking manufacturing data, and work with a database schema that is controlled by the developer of the application. For a particular customer, the application works with a single DBMS. For example, a small business might use the application with dBase, while a large business might use it with Oracle.

    The application uses ODBC in such a manner that the application is not tied to any one DBMS, although it might be tied to a limited number of DBMSs that provide similar functionality. Thus, the application developer can sell the application independently from the DBMS. Vertical applications are interoperable when they are developed but are sometimes modified to include noninteroperable code once the customer has chosen a DBMS.

  • Custom Applications. Custom applications are used to perform a specific task in a single company. For example, an application in a large company might gather sales data from several divisions (each of which uses a different DBMS) and create a single report. ODBC is used because it is a common interface and saves programmers from having to learn multiple interfaces. Such applications are generally not interoperable and are written to specific DBMSs and drivers.

A number of tasks are common to all applications, no matter how they use ODBC. Taken together, they largely define the flow of any ODBC application. The tasks are:

  • Selecting a data source and connecting to it.
  • Submitting an SQL statement for execution.
  • Retrieving results (if any).
  • Processing errors.
  • 70Committing or rolling back the transaction enclosing the SQL statement.
  • Disconnecting from the data source.

Because most data access work is done with SQL, the primary task for which applications use ODBC is to submit SQL statements and retrieve the results (if any) generated by those statements. Other tasks for which applications use ODBC include determining and adjusting to driver capabilities and browsing the database catalog.


The Driver Manager

The Driver Manager is a library that manages communication between applications and drivers. For example, on Microsoft® Windows® platforms, the Driver Manager is a dynamic-link library (DLL) that is written by Microsoft and can be redistributed by users of the Microsoft Data Access Software Development Kit (SDK).

The Driver Manager exists mainly as a convenience to application writers and solves a number of problems common to all applications. These include determining which driver to load based on a data source name, loading and unloading drivers, and calling functions in drivers.

To see why the latter is a problem, consider what would happen if the application called functions in the driver directly. Unless the application was linked directly to a particular driver, it would have to build a table of pointers to the functions in that driver and call those functions by pointer. Using the same code for more than one driver at a time would add yet another level of complexity. The application would first have to set a function pointer to point to the correct function in the correct driver, and then call the function through that pointer.

The Driver Manager solves this problem by providing a single place to call each function. The application is linked to the Driver Manager and calls ODBC functions in the Driver Manager, not the driver. The application identifies the target driver and data source with a connection handle. When it loads a driver, the Driver Manager builds a table of pointers to the functions in that driver. It uses the connection handle passed by the application to find the address of the function in the target driver and calls that function by address.

For the most part, the Driver Manager just passes function calls from the application to the correct driver. However, it also implements some functions (SQLDataSources, SQLDrivers, and SQLGetFunctions) and performs basic error checking. For example, the Driver Manager checks that handles are not null pointers, that functions are called in the correct order, and that certain function arguments are valid. For a complete description of the errors checked by the Driver Manager, see the reference section for each function and Appendix B: ODBC State Transition Tables.

The final major role of the Driver Manager is loading and unloading drivers. The application loads and unloads only the Driver Manager. When it wants to use a particular driver, it calls a connection function (SQLConnect, SQLDriverConnect, or SQLBrowseConnect) in the Driver Manager and specifies the name of a particular data source or driver, such as "Accounting" or "SQL Server." Using this name, the Driver Manager searches the data source information for the driver's file name, such as Sqlsrvr.dll. It then loads the driver (assuming it is not already loaded), stores the address of each function in the driver, and calls the connection function in the driver, which then initializes itself and connects to the data source.

When the application is done using the driver, it calls SQLDisconnect in the Driver Manager. The Driver Manager calls this function in the driver, which disconnects from the data source. However, the Driver Manager keeps the driver in memory in case the application reconnects to it. It unloads the driver only when the application frees the connection used by the driver or uses the connection for a different driver, and no other connections use the driver. For a complete description of the Driver Manager's role in loading and unloading drivers, see "Driver Manager's Role in the Connection Process" in Chapter 6: Connecting to a Data Source or Driver.


Drivers

Drivers are libraries that implement the functions in the ODBC API. Each is specific to a particular DBMS; for example, a driver for Oracle cannot directly access data in an Informix DBMS. Drivers expose the capabilities of the underlying DBMSs; they are not required to implement capabilities not supported by the DBMS. For example, if the underlying DBMS does not support outer joins, then neither should the driver. The only major exception to this is that drivers for DBMSs that do not have stand-alone database engines, such as Xbase, must implement a database engine that at least supports a minimal amount of SQL.


Driver Tasks

Specific tasks performed by drivers include:

  • Connecting to and disconnecting from the data source.
  • Checking for function errors not checked by the Driver Manager.
  • Initiating transactions; this is transparent to the application.
  • Submitting SQL statements to the data source for execution. The driver must modify ODBC SQL to DBMS-specific SQL; this is often limited to replacing escape clauses defined by ODBC with DBMS-specific SQL.
  • Sending data to and retrieving data from the data source, including converting data types as specified by the application.
  • Mapping DBMS-specific errors to ODBC SQLSTATEs.


Driver Architecture

Driver architecture falls into two categories, depending on which software processes SQL statements:

  • File-based drivers. The driver accesses the physical data directly. In this case, the driver acts as both driver and data source; that is, it processes ODBC calls and SQL statements. For example, dBASE drivers are file-based drivers because dBASE does not provide a stand-alone database engine the driver can use. It is important to note that developers of file-based drivers must write their own database engines.
  • DBMS-based drivers. The driver accesses the physical data through a separate database engine. In this case the driver processes only ODBC calls; it passes SQL statements to the database engine for processing. For example, Oracle drivers are DBMS-based drivers because Oracle has a stand-alone database engine the driver uses. Where the database engine resides is immaterial. It can reside on the same machine as the driver or a different machine on the network; it might even be accessed through a gateway.

Driver architecture is generally interesting only to driver writers; that is, driver architecture generally makes no difference to the application. However, the architecture can affect whether an application can use DBMS-specific SQL. For example, Microsoft Access provides a stand-alone database engine. If a Microsoft Access driver is DBMS-based—it accesses the data through this engine—the application can pass Microsoft Access–SQL statements to the engine for processing.

However, if the driver is file-based—that is, it contains a proprietary engine that accesses the Microsoft® Access .mdb file directly—any attempts to pass Microsoft Access–specific SQL statements to the engine are likely to result in syntax errors. The reason is that the proprietary engine is likely to implement only ODBC SQL.


File-Based Drivers

File-based drivers are used with data sources such as dBASE that do not provide a stand-alone database engine for the driver to use. These drivers access the physical data directly and must implement a database engine to process SQL statements. As a standard practice, the database engines in file-based drivers implement the subset of ODBC SQL defined by the minimum SQL conformance level; for a list of the SQL statements in this conformance level, see Appendix C: SQL Grammar.

In comparing file-based and DBMS-based drivers, file-based drivers are harder to write because of the database engine component, less complicated to configure because there are no network pieces, and less powerful because few people have the time to write database engines as powerful as those produced by database companies.

The following illustration shows two different configurations of file-based drivers, one in which the data resides locally and the other in which it resides on a network file server.


DBMS-Based Drivers

DBMS-based drivers are used with data sources such as Oracle or SQL Server that provide a stand-alone database engine for the driver to use. These drivers access the physical data through the stand-alone engine; that is, they submit SQL statements to and retrieve results from the engine.

Because DBMS-based drivers use an existing database engine, they are usually easier to write than file-based drivers. Although a DBMS-based driver can be easily implemented by translating ODBC calls to native API calls, this results in a slower driver. A better way to implement a DBMS-based driver is to use the underlying data stream protocol, which is usually what the native API does. For example, a SQL Server driver should use TDS (the data stream protocol for SQL Server) rather than DB Library (the native API for SQL Server). An exception to this rule is when ODBC is the native API. For example, Watcom SQL is a stand-alone engine that resides on the same machine as the application and is loaded directly as the driver.

DBMS-based drivers act as the client in a client/server configuration where the data source acts as the server. In most cases, the client (driver) and server (data source) reside on different machines, although both could reside on the same machine running a multitasking operating system. A third possibility is a gateway, which sits between the driver and data source. A gateway is a piece of software that causes one DBMS to look like another. For example, applications written to use SQL Server can also access DB2 data through the Micro Decisionware DB2 Gateway; this product causes DB2 to look like SQL Server.

The following illustration shows three different configurations of DBMS-based drivers. In the first configuration, the driver and data source reside on the same machine. In the second, the driver and data source reside on different machines. In the third, the driver and data source reside on different machines and a gateway sits between them, residing on yet another machine.


Network Example

This illustration shows how each of the preceding configurations could appear in a single network.


Other Driver Architectures

Some ODBC drivers do not strictly conform to the architecture described previously. This might be because the drivers perform duties other than those of a traditional ODBC driver, or are not drivers in the normal sense.

The Driver as a Middle Component

The ODBC driver may reside between the Driver Manager and one or more other ODBC drivers. When the driver in the middle is capable of working with multiple data sources, it acts as a dispatcher of ODBC calls (or appropriately translated calls) to other modules that actually access the data sources. In this architecture, the driver in the middle is taking on some of the role of a Driver Manager.

Another example of this sort of driver is a spy program for ODBC, which intercepts and copies ODBC functions being sent between the Driver Manager and the driver. This layer can be used to emulate either a driver or an application. To the Driver Manager, the layer appears to be the driver; to the driver, the layer appears to be the Driver Manager.

Heterogeneous Join Engines

Some ODBC drivers are built upon a query engine for performing heterogeneous joins. In one architecture of a heterogeneous join engine (see the following illustration), the driver appears to the application as a driver but appears to another instance of the Driver Manager as an application. This driver processes a heterogeneous join from the application by calling separate SQL statements in drivers for each joined database.

This architecture provides a common interface for the application to access data from different databases. It can use a common way to retrieve metadata, such as information about special columns (row identifiers), and it can call common catalog functions to retrieve data dictionary information. By calling the ODBC function SQLStatistics, for instance, the application can retrieve information about the indexes on the tables to be joined, even if the tables are on two separate databases. The query processor does not have to worry about how the databases store metadata.

The application also has standard access to data types. ODBC defines common SQL data types that DBMS-specific data types are mapped to. An application can call SQLGetTypeInfo to retrieve information about data types on different databases.

When the application generates a heterogeneous join statement, the query processor in this architecture parses the SQL statement and then generates separate SQL statements for each database to be joined. By using metadata about each driver, the query processor can determine the most efficient, intelligent join. For example, if the statement joins two tables on one database with one table on another database, the query processor can join the two tables on the one database before joining the result with the table from the other database.

ODBC on the Server

ODBC drivers can be installed on a server so that they can be used by applications on any of a series of client machines. In this architecture (see the following illustration), a Driver Manager and a single ODBC driver are installed on each client, and another Driver Manager and a series of ODBC drivers are installed on the server. This allows each client access to a variety of drivers used and maintained on the server.

One advantage of this architecture is efficient software maintenance and configuration. Drivers need only be updated in one place: on the server. By using system data sources, data sources can be defined on the server for use by all clients. The data sources need not be defined on the client. Connection pooling can be used to streamline the process by which clients connect to data sources.

The driver on the client is usually a very small driver that transfers the Driver Manager call to the server. Its footprint can be significantly smaller than the fully functional ODBC drivers on the server. In this architecture, client resources can be freed if the server has more computing power. In addition, the efficiency and security of the entire system can be enhanced by installing backup servers and performing load balancing to optimize server use.


Data Sources

A data source is simply the source of the data. It can be a file, a particular database on a DBMS, or even a live data feed. For example, a data source might be an Oracle DBMS running on an OS/2® operating system, accessed by Novell® Netware; an IBM DB2 DBMS accessed through a gateway; a collection of Xbase files in a server directory; or a local Microsoft® Access database file.

The purpose of a data source is to gather all of the technical information needed to access the data—the driver name, network address, network software, and so on—into a single place and hide it from the user. The user should be able to look at a list that includes Payroll, Inventory, and Personnel, choose Payroll from the list, and have the application connect to the payroll data, all without knowing where the payroll data resides or how the application got to it.

The term data source should not be confused with similar terms. In this manual, DBMS or database refers to a database program or engine. A further distinction is made between desktop databases, designed to run on personal computers and often lacking in full SQL and transaction support, and server databases, designed to run in a client/server situation and characterized by a stand-alone database engine and rich SQL and transaction support. Database also refers to a particular collection of data, such as a collection of Xbase files in a directory or a database on SQL Server. It is generally equivalent to the term catalog, used elsewhere in this manual, or the term qualifier in earlier versions of ODBC.


Types of Data Sources

There are two types of data sources: machine data sources and file data sources. Although both contain similar information about the source of the data, they differ in the way this information is stored. Because of these differences, they are used in somewhat different manners.


Machine Data Sources

Machine data sources are stored on the system with a user-defined name. Associated with the data source name is all of the information the Driver Manager and driver need to connect to the data source. For an Xbase data source, this might be the name of the Xbase driver, the full path of the directory containing the Xbase files, and some options that tell the driver how to use those files, such as single-user mode or read-only. For an Oracle data source, this might be the name of the Oracle driver, the server where the Oracle DBMS resides, the SQL*Net connection string that identifies the SQL*Net driver to use, and the system ID of the database on the server.


File Data Sources

File data sources are stored in a file and allow connection information to be used repeatedly by a single user or shared among several users. When a file data source is used, the Driver Manager makes the connection to the data source using the information in a .dsn file. This file can be manipulated like any other file. A file data source does not have a data source name, as does a machine data source, and is not registered to any one user or machine.

A file data source streamlines the connection process, because the .dsn file contains the connection string that would otherwise have to be built for a call to the SQLDriverConnect function. Another advantage of the .dsn file is that it can be copied to any machine, so identical data sources can be used by many machines as long as they have the appropriate driver installed. A file data source can also be shared by applications. A shareable file data source can be placed on a network and used simultaneously by multiple applications.

A .dsn file can also be unshareable. An unshareable .dsn file resides on a single machine and points to a machine data source. Unshareable file data sources exist mainly to allow the easy conversion of machine data sources to file data sources so that an application can be designed to work solely with file data sources. When the Driver Manager is sent the information in an unshareable file data source, it connects as necessary to the machine data source that the .dsn file points to.

For more information about file data sources, see "Connecting Using File Data Sources" in Chapter 6: Connecting to a Data Source or Driver, or the SQLDriverConnect function description.


Using Data Sources

Data sources usually are created by the end user or a technician with a program called the ODBC Administrator. The ODBC Administrator prompts the user for the driver to use and then calls that driver. The driver displays a dialog box that requests the information it needs to connect to the data source. After the user enters the information, the driver stores it on the system.

Later, the application calls the Driver Manager and passes it the name of a machine data source or the path of a file containing a file data source. When passed a machine data source name, the Driver Manager searches the system to find the driver used by the data source. It then loads the driver and passes the data source name to it. The driver uses the data source name to find the information it needs to connect to the data source. Finally, it connects to the data source, typically prompting the user for a user ID and password, which generally are not stored.

When passed a file data source, the Driver Manager opens the file and loads the specified driver. If the file also contains a connection string, it passes this to the driver. Using the information in the connection string, the driver connects to the data source. If no connection string was passed, the driver generally prompts the user for the necessary information.


Data Source Example

On computers running Microsoft® Windows NT® Server/Windows 2000 Server, Microsoft Windows NT Workstation/Windows 2000 Professional, or Microsoft Windows® 95/98, machine data source information is stored in the registry. Depending on which registry key the information is stored under, the data source is known as a user data source or a system data source. User data sources are stored under the HKEY_CURRENT_USER key and are available only to the current user. System data sources are stored under the HKEY_LOCAL_MACHINE key and can be used by more than one user on one machine. They can also be used by systemwide services, which can then gain access to the data source even if no user is logged on to the machine. For more information about user and system data sources, see SQLManageDataSources in Chapter 23: Installer DLL API Reference.

Suppose a user has three user data sources: Personnel and Inventory, which use an Oracle DBMS; and Payroll, which uses a Microsoft SQL Server DBMS. The registry values for data sources might be:

HKEY_CURRENT_USER
SOFTWARE
ODBC
Odbc.ini
ODBC Data Sources
Personnel : REG_SZ : Oracle
Inventory : REG_SZ : Oracle
Payroll : REG_SZ : SQL Server

and the registry values for the Payroll data source might be:

HKEY_CURRENT_USER
SOFTWARE
ODBC
Odbc.ini
Payroll
Driver : REG_SZ : C:\WINDOWS\SYSTEM\Sqlsrvr.dll
Description : REG_SZ : Payroll database
Server : REG_SZ : PYRLL1
FastConnectOption : REG_SZ : No
UseProcForPrepare : REG_SZ : Yes
OEMTOANSI : REG_SZ : No
LastUser : REG_SZ : smithjo
Database : REG_SZ : Payroll
Language : REG_SZ :


Part 2 Developing Applications and Drivers

ODBC Programmer's Reference

Part 2: Developing Applications and Drivers

Part 2 of the ODBC Programmer's Reference contains information about developing applications that use the ODBC interface and drivers that implement it. This section contains the following chapters:

  • Chapter 4: ODBC Fundamentals
  • Chapter 5: Basic ODBC Application Steps
  • Chapter 6: Connecting to a Data Source or Driver
  • Chapter 7: Catalog Functions
  • Chapter 8: SQL Statements
  • Chapter 9: Executing Statements
  • Chapter 10: Retrieving Results (Basic)
  • Chapter 11: Retrieving Results (Advanced)
  • Chapter 12: Updating Data
  • Chapter 13: Descriptors
  • Chapter 14: Transactions
  • Chapter 15: Diagnostics
  • Chapter 16: Interoperability
  • Chapter 17: Programming Considerations


Chapter 4: ODBC Fundamentals

This chapter covers a number of concepts fundamental to writing ODBC applications and drivers:

  • Handles
  • Buffers
  • Data types
  • Conformance levels
  • Environment, connection, and statement attributes


Handles

Handles are opaque, 32-bit values that identify a particular item; in ODBC, this item can be an environment, connection, statement, or descriptor. When the application calls SQLAllocHandle, the Driver Manager or driver creates a new item of the specified type and returns its handle to the application. The application later uses the handle to identify that item when calling ODBC functions. The Driver Manager and driver use the handle to locate information about the item.

For example, the following code uses two statement handles (hstmtOrder and hstmtLine) to identify the statements on which to create result sets of sales orders and sales order line numbers. It later uses these handles to identify which result set to fetch data from.

SQLHSTMT      hstmtOrder, hstmtLine; // Statement handles.
SQLUINTEGER   OrderID;
SQLINTEGER    OrderIDInd = 0;
SQLRETURN     rc;

// Prepare the statement that retrieves line number information.
SQLPrepare(hstmtLine, "SELECT * FROM Lines WHERE OrderID = ?", SQL_NTS);

// Bind OrderID to the parameter in the preceding statement.
SQLBindParameter(hstmtLine, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 5, 0,
               &OrderID, 0, &OrderIDInd);

// Bind the result sets for the Order table and the Lines table. Bind
// OrderID to the OrderID column in the Orders table. When each row is
// fetched, OrderID will contain the current order ID, which will then be
// passed as a parameter to the statement tofetch line number
// information. Code not shown.

// Create a result set of sales orders.
SQLExecDirect(hstmtOrder, "SELECT * FROM Orders", SQL_NTS);

// Fetch and display the sales order data. Code to check if rc equals
// SQL_ERROR or SQL_SUCCESS_WITH_INFO not shown.
while ((rc = SQLFetch(hstmtOrder)) != SQL_NO_DATA) {
   // Display the sales order data. Code not shown.

   // Create a result set of line numbers for the current sales order.
   SQLExecute(hstmtLine);

   // Fetch and display the sales order line number data. Code to check
   // if rc equals SQL_ERROR or SQL_SUCCESS_WITH_INFO not shown.
   while ((rc = SQLFetch(hstmtLine)) != SQL_NO_DATA) {
      // Display the sales order line number data. Code not shown.
   }

   // Close the sales order line number result set.
   SQLCloseCursor(hstmtLine);
}

// Close the sales order result set.
SQLCloseCursor(hstmtOrder);

Handles are meaningful only to the ODBC component that created them; that is, only the Driver Manager can interpret Driver Manager handles and only a driver can interpret its own handles.

For example, suppose the driver in the preceding example allocates a structure to store information about a statement and returns the pointer to this structure as the statement handle. When the application calls SQLPrepare, it passes an SQL statement and the handle of the statement used for sales order line numbers. The driver sends the SQL statement to the data source, which prepares it and returns an access plan identifier. The driver uses the handle to find the structure in which to store this identifier.

Later, when the application calls SQLExecute to generate the result set of line numbers for a particular sales order, it passes the same handle. The driver uses the handle to retrieve the access plan identifier from the structure. It sends the identifier to the data source to tell it which plan to execute.

ODBC has two levels of handles: Driver Manager handles and driver handles. The application uses Driver Manager handles when calling ODBC functions because it calls those functions in the Driver Manager. The Driver Manager uses this handle to find the corresponding driver handle and uses the driver handle when calling the function in the driver. For an example of how driver and Driver Manager handles are used, see "Driver Manager's Role in the Connection Process" in Chapter 6: Connecting to a Data Source or Driver.

That there are two levels of handles is an artifact of the ODBC architecture; in most cases, it is not relevant to either the application or driver. Although there is usually no reason to do so, it is possible for the application to determine the driver handles by calling SQLGetInfo.


Environment Handles

An environment is a global context in which to access data; associated with an environment is any information that is global in nature, such as:

  • The environment's state
  • The current environment-level diagnostics
  • The handles of connections currently allocated on the environment
  • The current settings of each environment attribute

Within a piece of code that implements ODBC (the Driver Manager or a driver), an environment handle identifies a structure to contain this information.

Environment handles are not frequently used in ODBC applications. They are always used in calls to SQLDataSources and SQLDrivers and sometimes used in calls to SQLAllocHandle, SQLEndTran, SQLFreeHandle, SQLGetDiagField, and SQLGetDiagRec.

Each piece of code that implements ODBC (the Driver Manager or a driver) contains one or more environment handles. For example, the Driver Manager maintains a separate environment handle for each application that is connected to it. Environment handles are allocated with SQLAllocHandle and freed with SQLFreeHandle.


Connection Handles

A connection consists of a driver and a data source. A connection handle identifies each connection. The connection handle defines not only which driver to use but which data source to use with that driver. Within a piece of code that implements ODBC (the Driver Manager or a driver), the connection handle identifies a structure that contains connection information, such as:

  • The connection's state
  • The current connection-level diagnostics
  • The handles of statements and descriptors currently allocated on the connection
  • The current settings of each connection attribute

ODBC does not prevent multiple simultaneous connections, if the driver supports them. Thus, in a particular ODBC environment, multiple connection handles might point to a variety of drivers and data sources, to the same driver and a variety of data sources, or even to multiple connections to the same driver and data source. Some drivers limit the number of active connections they support; the SQL_MAX_DRIVER_CONNECTIONS option in SQLGetInfo specifies how many active connections a particular driver supports.

Connection handles are used primarily when connecting to the data source (SQLConnect, SQLDriverConnect, or SQLBrowseConnect), disconnecting from the data source (SQLDisconnect), getting information about the driver and data source (SQLGetInfo), retrieving diagnostics (SQLGetDiagField and SQLGetDiagRec), and performing transactions (SQLEndTran). They are also used when setting and getting connection attributes (SQLSetConnectAttr and SQLGetConnectAttr) and when getting the native format of an SQL statement (SQLNativeSql).

Connection handles are allocated with SQLAllocHandle and freed with SQLFreeHandle.


Statement Handles

A statement is most easily thought of as an SQL statement, such as SELECT * FROM Employee. However, a statement is more than just an SQL statement—it consists of all of the information associated with that SQL statement, such as any result sets created by the statement and parameters used in the execution of the statement. A statement does not even need to have an application-defined SQL statement. For example, when a catalog function such as SQLTables is executed on a statement, it executes a predefined SQL statement that returns a list of table names.

Each statement is identified by a statement handle. A statement is associated with a single connection, and there can be multiple statements on that connection. Some drivers limit the number of active statements they support; the SQL_MAX_CONCURRENT_ACTIVITIES option in SQLGetInfo specifies how many active statements a driver supports on a single connection. A statement is defined to be active if it has results pending, where results are either a result set or the count of rows affected by an INSERT, UPDATE, or DELETE statement, or data is being sent with multiple calls to SQLPutData.

Within a piece of code that implements ODBC (the Driver Manager or a driver), the statement handle identifies a structure that contains statement information, such as:

  • The statement's state
  • The current statement-level diagnostics
  • The addresses of the application variables bound to the statement's parameters and result set columns
  • The current settings of each statement attribute

Statement handles are used in most ODBC functions. Notably, they are used in the functions to bind parameters and result set columns (SQLBindParameter and SQLBindCol), prepare and execute statements (SQLPrepare, SQLExecute, and SQLExecDirect), retrieve metadata (SQLColAttribute and SQLDescribeCol), fetch results (SQLFetch), and retrieve diagnostics (SQLGetDiagField and SQLGetDiagRec). They are also used in catalog functions (SQLColumns, SQLTables, and so on) and a number of other functions.

Statement handles are allocated with SQLAllocHandle and freed with SQLFreeHandle.


Descriptor Handles

A descriptor is a collection of metadata that describes the parameters of an SQL statement or the columns of a result set, as seen by the application or driver (also known as the implementation). Thus, a descriptor can fill any of four roles:

  • Application Parameter Descriptor (APD). Contains information about the application buffers bound to the parameters in an SQL statement, such as their addresses, lengths, and C data types.
  • Implementation Parameter Descriptor (IPD). Contains information about the parameters in an SQL statement, such as their SQL data types, lengths, and nullability.
  • Application Row Descriptor (ARD). Contains information about the application buffers bound to the columns in a result set, such as their addresses, lengths, and C data types.
  • Implementation Row Descriptor (IRD). Contains information about the columns in a result set, such as their SQL data types, lengths, and nullability.

Four descriptors (one filling each role) are allocated automatically when a statement is allocated. These are known as automatically allocated descriptors and are always associated with that statement. Applications can also allocate descriptors with SQLAllocHandle. These are known as explicitly allocated descriptors. They are allocated on a connection and can be associated with one or more statements on that connection to fulfill the role of an APD or ARD on those statements.

Most operations in ODBC can be performed without explicit use of descriptors by the application. However, descriptors provide a convenient shortcut for some operations. For example, suppose an application wants to insert data from two different sets of buffers. To use the first set of buffers, it would repeatedly call SQLBindParameter to bind them to the parameters in an INSERT statement and then execute the statement. To use the second set of buffers, it would repeat this process. Alternatively, it could set up bindings to the first set of buffers in one descriptor and to the second set of buffers in another descriptor. To switch between the sets of bindings, the application would simply call SQLSetStmtAttr and associate the correct descriptor with the statement as the APD.

For more information about descriptors, see "Types of Descriptors" in Chapter 13: Descriptors.


State Transitions

ODBC defines discrete states for each environment, each connection, and each statement. For example, the environment has three possible states: Unallocated (in which no environment is allocated), Allocated (in which an environment is allocated but no connections are allocated), and Connection (in which an environment and one or more connections are allocated). Connections have seven possible states; statements have 13 possible states.

A particular item, as identified by its handle, moves from one state to another when the application calls a certain function or functions and passes the handle to that item. Such movement is called a state transition. For example, allocating an environment handle with SQLAllocHandle moves the environment from Unallocated to Allocated, and freeing that handle with SQLFreeHandle returns it from Allocated to Unallocated. ODBC defines a limited number of legal state transitions, which is another way of saying that functions must be called in a certain order.

Some functions, such as SQLGetConnectAttr, do not affect state at all. Other functions affect the state of a single item. For example, SQLDisconnect moves a connection from a Connection state to an Allocated state. Finally, some functions affect the state of more than one item. For example, allocating a connection handle with SQLAllocHandle moves a connection from an Unallocated to an Allocated state and moves the environment from an Allocated to a Connection state.

If an application calls a function out of order, the function returns a state transition error. For example, if an environment is in a Connection state and the application calls SQLFreeHandle with that environment handle, SQLFreeHandle returns SQLSTATE HY010 (Function sequence error), because it can be called only when the environment is in an Allocated state. By defining this as an invalid state transition, ODBC prevents the application from freeing the environment while there are active connections.

Some state transitions are inherent in the design of ODBC. For example, it is not possible to allocate a connection handle without first allocating an environment handle, because the function that allocates a connection handle requires an environment handle. Other state transitions are enforced by the Driver Manager and the drivers. For example, SQLExecute executes a prepared statement. If the statement handle passed to it is not in a Prepared state, SQLExecute returns SQLSTATE HY010 (Function sequence error).

From the application's point of view, state transitions are usually straightforward: Legal state transitions tend to go hand-in-hand with the flow of a well-written application. State transitions are more complex for the Driver Manager and the drivers because they must track the state of the environment, each connection, and each statement. Most of this work is done by the Driver Manager; most of the work that must be done by drivers occurs with statements with pending results.

Parts 1 and 2 of this manual ("Introduction to ODBC" and "Developing Applications and Drivers") tend not to explicitly mention state transitions. Instead, they describe the order in which functions must be called. For example, Chapter 9: Executing Statements, states that a statement must be prepared with SQLPrepare before it can be executed with SQLExecute. For a complete description of states and state transitions, including which transitions are checked by the Driver Manager and which must be checked by drivers, see Appendix B: ODBC State Transition Tables.


Buffers

A buffer is any piece of application memory used to pass data between the application and the driver. For example, application buffers can be associated with, or bound to, result set columns with SQLBindCol. As each row is fetched, the data is returned for each column in these buffers. Input buffers are used to pass data from the application to the driver; output buffers are used to return data from the driver to the application.

Note If an ODBC function returns SQL_ERROR, the contents of any output arguments to that function are undefined.

This discussion concerns itself primarily with buffers of indeterminate type. The addresses of these buffers appear as arguments of type SQLPOINTER, such as the TargetValuePtr argument in SQLBindCol. However, some of the items discussed here, such as the arguments used with buffers, also apply to arguments used to pass strings to the driver, such as the TableName argument in SQLTables.

These buffers usually come in pairs. Data buffers are used to pass the data itself, while length/indicator buffers are used to pass the length of the data in the data buffer or a special value such as SQL_NULL_DATA, which indicates that the data is NULL. The length of the data in a data buffer is different from the length of the data buffer itself. The following illustration shows the relationship between the data buffer and the length/indicator buffer.

A length/indicator buffer is required whenever the data buffer contains variable-length data, such as character or binary data. If the data buffer contains fixed-length data, such as an integer or date structure, a length/indicator buffer is needed only to pass indicator values because the length of the data is already known. If an application uses a length/indicator buffer with fixed-length data, the driver ignores any lengths passed in it.

The length of both the data buffer and the data it contains is measured in bytes, as opposed to characters. This distinction is unimportant for programs that use ANSI strings because lengths in bytes and characters are the same.

When the data buffer represents a driver-defined descriptor field, diagnostic field, or attribute, the application should indicate to the Driver Manager the nature of the function argument that indicates the value for the field or attribute. The application does this by setting the length argument in any function call that sets the field or attribute to one of the following values. (The same is true for functions that retrieve the values of the field or attribute, with the exception that the argument points to the values that for the setting function are in the argument itself.)

  • If the function argument that indicates the value for the field or attribute is a pointer to a character string, the length argument is the length of the string or SQL_NTS.
  • If the function argument that indicates the value for the field or attribute is a pointer to a binary buffer, the application places the result of the SQL_LEN_BINARY_ATTR(length) macro in the length argument. This places a negative value in the length argument.
  • If the function argument that indicates the value for the field or attribute is a pointer to a value other than a character string or a binary string, the length argument should have the value SQL_IS_POINTER.
  • If the function argument that indicates the value for the field or attribute contains a fixed-length value, the length argument is SQL_IS_INTEGER, SQL_IS_UINTEGER, SQL_SMALLINT, or SQL_USMALLINT, as appropriate.


Deferred Buffers

A deferred buffer is one whose value is used at some time after it is specified in a function call. For example, SQLBindParameter is used to associate, or bind, a data buffer with a parameter in an SQL statement. The application specifies the number of the parameter and passes the address, byte length, and type of the buffer. The driver saves this information but does not examine the contents of the buffer. Later, when the application executes the statement, the driver retrieves the information and uses it to retrieve the parameter data and send it to the data source. Therefore, the input of data in the buffer is deferred. Because deferred buffers are specified in one function and used in another, it is an application programming error to free a deferred buffer while the driver still expects it to exist; for more information, see "Allocating and Freeing Buffers," later in this chapter.

Both input and output buffers can be deferred. The following table summarizes the uses of deferred buffers. Note that deferred buffers bound to result set columns are specified with SQLBindCol, and deferred buffers bound to SQL statement parameters are specified with SQLBindParameter.

Buffer use | Type | Specified with | Used by
Sending data for input parameters | Deferred input | SQLBindParameter | SQLExecute SQLExecDirect
Sending data to update or insert a row in a result set | Deferred input | SQLBindCol | SQLSetPos
Returning data for output and input/output parameters | Deferred output | SQLBindParameter | SQLExecute SQLExecDirect
Returning result set data | Deferred output | SQLBindCol | SQLFetch SQLFetchScroll SQLSetPos


Allocating and Freeing Buffers

All buffers are allocated and freed by the application. If a buffer is not deferred, it need only exist for the duration of the call to a function. For example, SQLGetInfo returns the value associated with a particular option in the buffer pointed to by the InfoValuePtr argument. This buffer can be freed immediately after the call to SQLGetInfo, as shown in the following code example:

SQLSMALLINT   InfoValueLen;
SQLCHAR *     InfoValuePtr = malloc(50);   // Allocate InfoValuePtr.

SQLGetInfo(hdbc, SQL_DBMS_NAME, (SQLPOINTER)InfoValuePtr, 50,
            &InfoValueLen);

free(InfoValuePtr);                        // OK to free InfoValuePtr.

Because deferred buffers are specified in one function and used in another, it is an application programming error to free a deferred buffer while the driver still expects it to exist. For example, the address of the *ValuePtr buffer is passed to SQLBindCol for later use by SQLFetch. This buffer cannot be freed until the column is unbound, such as with a call to SQLBindCol or SQLFreeStmt as shown in the following code example:

SQLRETURN    rc;
SQLINTEGER   ValueLenOrInd;
SQLHSTMT     hstmt;

// Allocate ValuePtr
SQLCHAR * ValuePtr = malloc(50);

// Bind ValuePtr to column 1. It is an error to free ValuePtr here.
SQLBindCol(hstmt, 1, SQL_C_CHAR, ValuePtr, 50, &ValueLenOrInd);

// Fetch each row of data and place the value for column 1 in *ValuePtr.
// Code to check if rc equals SQL_ERROR or SQL_SUCCESS_WITH_INFO
// not shown.
while ((rc = SQLFetch(hstmt)) != SQL_NO_DATA) {
   // It is an error to free ValuePtr here.
}

// Unbind ValuePtr from column 1.  It is now OK to free ValuePtr.
SQLFreeStmt(hstmt, SQL_UNBIND);
free(ValuePtr);

Such an error is easily made by declaring the buffer locally in a function; the buffer is freed when the application leaves the function. For example, the following code causes undefined and probably fatal behavior in the driver:

SQLRETURN   rc;
SQLHSTMT    hstmt;

BindAColumn(hstmt);

// Fetch each row of data and try to place the value for column 1 in
// *ValuePtr. Because ValuePtr has been freed, the behavior is undefined
// and probably fatal. Code to check if rc equals SQL_ERROR or
// SQL_SUCCESS_WITH_INFO not shown.
while ((rc = SQLFetch(hstmt)) != SQL_NO_DATA) {}

   .
   .
   .

void BindAColumn(SQLHSTMT hstmt)  // WARNING! This function won't work!
{
   // Declare ValuePtr locally.
   SQLCHAR      ValuePtr[50];
   SQLINTEGER   ValueLenOrInd;

   // Bind rgbValue to column.
   SQLBindCol(hstmt, 1, SQL_C_CHAR, ValuePtr, sizeof(ValuePtr),
               &ValueLenOrInd);

   // ValuePtr is freed when BindAColumn exits.
}


Using Data Buffers

Data buffers are described by three pieces of information: their type, address, and byte length. Whenever a function needs one of these pieces of information and does not already know it, it has an argument with which the application passes it.


Data Buffer Type

The C data type of a buffer is specified by the application. In the case of a single variable, this occurs when the application allocates the variable. In the case of generic memory—that is, memory pointed to by a pointer of type void—this occurs when the application casts the memory to a particular type. The driver discovers this type in two ways:

  • Data buffer type argument. Buffers used to transfer parameter values and result set data, such as the buffer bound with TargetValuePtr in SQLBindCol, usually have an associated type argument, such as the TargetType argument in SQLBindCol. In this argument, the application passes the C type identifier corresponding to the type of the buffer. For example, in the following call to SQLBindCol, the value SQL_C_TYPE_DATE tells the driver that the Date buffer is an SQL_DATE_STRUCT:
    SQL_DATE_STRUCT Date;
SQLINTEGER  DateInd;
SQLBindCol(hstmt, 1, SQL_C_TYPE_DATE, &Date, 0, &DateInd);

    For more information on type identifiers, see the "Data Types in ODBC" section, later in this chapter.

  • Predefined type. Buffers used to send and retrieve options or attributes, such as the buffer pointed to by the InfoValuePtr argument in SQLGetInfo, have a fixed type that depends on the option specified. The driver assumes that the data buffer is of this type; it is the application's responsibility to allocate a buffer of this type. For example, in the following call to SQLGetInfo, the driver assumes the buffer is a 32-bit integer because this is what the SQL_STRING_FUNCTIONS option requires:
    SQLUINTEGER StringFuncs;
SQLGetInfo(hdbc, SQL_STRING_FUNCTIONS, (SQLPOINTER) &StringFuncs, 0,
            NULL);

The driver uses the C data type to interpret the data in the buffer.


Data Buffer Address

The application passes the address of the data buffer to the driver in an argument, often named ValuePtr or a similar name. For example, in the following call to SQLBindCol, the application specifies the address of the Date variable:

SQL_DATE_STRUCT Date;
SQLINTEGER DateInd;
SQLBindCol(hstmt, 1, SQL_C_TYPE_DATE, &dsDate, 0, &DateInd);

As mentioned in the "Allocating and Freeing Buffers" section in this chapter, the address of a deferred buffer must remain valid until the buffer is unbound.

Unless it is specifically prohibited, the address of a data buffer can be a null pointer. For buffers used to send data to the driver, this causes the driver to ignore the information normally contained in the buffer. For buffers used to retrieve data from the driver, this causes the driver to not return a value. In both cases, the driver ignores the corresponding data buffer length argument.


Data Buffer Length

The application passes the byte length of the data buffer to the driver in an argument, named BufferLength or a similar name. For example, in the following call to SQLBindCol, the application specifies the length of the ValuePtr buffer (sizeof(ValuePtr)):

SQLCHAR      ValuePtr[50];
SQLINTEGER   ValueLenOrInd;
SQLBindCol(hstmt, 1, SQL_C_CHAR, ValuePtr, sizeof(ValuePtr), &ValueLenOrInd);

A driver will always return the number of bytes, not the number of characters, in the buffer length argument of any function that has an output string argument.

Data buffer lengths are required only for output buffers; the driver uses them to avoid writing past the end of the buffer. However, the driver checks the data buffer length only when the buffer contains variable-length data, such as character or binary data. If the buffer contains fixed-length data, such as an integer or date structure, the driver ignores the data buffer length and assumes the buffer is large enough to hold the data; that is, it never truncates fixed-length data. It is therefore important for the application to allocate a large enough buffer for fixed-length data.

When a truncation of non-data output strings occurs (such as the cursor name returned for SQLGetCursorName), the returned length in the buffer length argument is the maximum byte length possible. That means that it is twice the number of characters, because the string could contain DBCS characters. For example, if the cursor name is 10 ANSI characters long (10 bytes) and an application calls SQLGetCursorName to try to retrieve the cursor name into a buffer that is 6 bytes long, the value returned in *NameLengthPtr will be 20.

Data buffer lengths are not required for input buffers because the driver does not write to these buffers.


Using Length/Indicator Values

The length/indicator buffer is used to pass the byte length of the data in the data buffer or a special indicator such as SQL_NULL_DATA, which indicates that the data is NULL. Depending on the function in which it is used, a length/indicator buffer is defined to be an SQLINTEGER or an SQLSMALLINT. Therefore, a single argument is needed to describe it. If the data buffer is a nondeferred input buffer, this argument contains the byte length of the data itself or an indicator value. It is often named StrLen_or_Ind or a similar name. For example, the following code calls SQLPutData to pass a buffer full of data; the byte length (ValueLen) is passed directly because the data buffer (ValuePtr) is an input buffer.

SQLCHAR      ValuePtr[50];
SQLINTEGER   ValueLen;

// Call local function to place data in ValuePtr. In ValueLen, return the
// number of bytes of data placed in ValuePtr. If there is not enough
// data, this will be less than 50.
FillBuffer(ValuePtr, sizeof(ValuePtr), &ValueLen);

// Call SQLPutData to send the data to the driver.
SQLPutData(hstmt, ValuePtr, ValueLen);

If the data buffer is a deferred input buffer, a nondeferred output buffer, or an output buffer, the argument contains the address of the length/indicator buffer. It is often named StrLen_or_IndPtr or a similar name. For example, the following code calls SQLGetData to retrieve a buffer full of data; the byte length is returned to the application in the length/indicator buffer (ValueLenOrInd), whose address is passed to SQLGetData because the corresponding data buffer (ValuePtr) is a nondeferred output buffer.

SQLCHAR      ValuePtr[50];
SQLINTEGER   ValueLenOrInd;
SQLGetData(hstmt, 1, SQL_C_CHAR, ValuePtr, sizeof(ValuePtr), &ValueLenOrInd);

Unless it is specifically prohibited, a length/indicator buffer argument can be 0 (if nondeferred input) or a null pointer (if output or deferred input). For input buffers, this causes the driver to ignore the byte length of the data. This returns an error when passing variable-length data but is common when passing non-null, fixed-length data, because neither a length nor an indicator value is needed. For output buffers, this causes the driver to not return the byte length of the data or an indicator value. This is an error if the data returned by the driver is NULL but is common when retrieving fixed-length, non-nullable data, because neither a length nor an indicator value is needed.

As when the address of a deferred data buffer is passed to the driver, the address of a deferred length/indicator buffer must remain valid until the buffer is unbound.

The following lengths are valid as length/indicator values:

  • n, where n > 0.
  • 0.
  • SQL_NTS. A string sent to the driver in the corresponding data buffer is null-terminated; this is a convenient way for C programmers to pass strings without having to calculate their byte length. This value is legal only when the application sends data to the driver. When the driver returns data to the application, it always returns the actual byte length of the data.

The following values are valid as length/indicator values. SQL_NULL_DATA is stored in the SQL_DESC_INDICATOR_PTR descriptor field; all other values are stored in the SQL_DESC_OCTET_LENGTH_PTR descriptor field.

  • SQL_NULL_DATA. The data is a NULL data value, and the value in the corresponding data buffer is ignored. This value is legal only for SQL data sent to or retrieved from the driver.
  • SQL_DATA_AT_EXEC. The data buffer does not contain any data. Instead, the data will be sent with SQLPutData when the statement is executed or when SQLBulkOperations or SQLSetPos is called. This value is legal only for SQL data sent to the driver. For more information, see SQLBindParameter, SQLBulkOperations, and SQLSetPos.
  • Result of the SQL_LEN_DATA_AT_EXEC(length) macro. This value is similar to SQL_DATA_AT_EXEC. For more information, see "Sending Long Data" in Chapter 9: Executing Statements.
  • SQL_NO_TOTAL. The driver cannot determine the number of bytes of long data still available to return in an output buffer. This value is legal only for SQL data retrieved from the driver.
  • SQL_DEFAULT_PARAM. A procedure is to use the default value of an input parameter in a procedure instead of the value in the corresponding data buffer.
  • SQL_COLUMN_IGNORE. SQLBulkOperations or SQLSetPos is to ignore the value in the data buffer. When updating a row of data by a call to SQLBulkOperations or SQLSetPos, the column value is not changed. When inserting a new row of data by a call to SQLBulkOperations, the column value is set to its default or, if the column does not have a default, to NULL.


Data Length, Buffer Length, and Truncation

The data length is the byte length of the data as it would be stored in the application's data buffer, not as it is stored in the data source. This distinction is important because the data is often stored in different types in the data buffer than in the data source. So for data being sent to the data source, this is the byte length of the data before conversion to the data source's type. For data being retrieved from the data source, this is the byte length of the data after conversion to the data buffer's type and before any truncation is done.

For fixed-length data, such as an integer or a date structure, the byte length of the data is always the size of the data type. In general, applications allocate a data buffer that is the size of the data type. If the application allocates a smaller buffer, the consequences are undefined because the driver assumes the data buffer is the size of the data type and does not truncate the data to fit into a smaller buffer. If the application allocates a larger buffer, the extra space is never used.

For variable-length data, such as character or binary data, it is important to recognize that the byte length of the data is separate from and often different than the byte length of the buffer. The relation of these two lengths is described in the "Buffers" section, earlier in this chapter. If the byte length of the data is greater than the byte length of the buffer, the driver truncates fetched data to the byte length of the buffer and returns SQL_SUCCESS_WITH_INFO with SQLSTATE 01004 (Data truncated). However, the returned byte length is the length of the untruncated data.

For example, suppose an application allocates 50 bytes for a binary data buffer. If the driver has 10 bytes of binary data to return, it returns those 10 bytes in the buffer. The byte length of the data is 10, and the byte length of the buffer is 50. If the driver has 60 bytes of binary data to return, it truncates the data to 50 bytes, returns those bytes in the buffer, and returns SQL_SUCCESS_WITH_INFO. The byte length of the data is 60 (the length before truncation), and the byte length of the buffer is still 50.

A diagnostic record is created for each column that is truncated. Because it takes time for the driver to create these records and for the application to process them, truncation can degrade performance. Usually, an application can avoid this problem by allocating large enough buffers, although this might not be possible when working with long data. When data truncation occurs, the application can sometimes allocate a larger buffer and refetch the data; this is not true in all cases. If truncation occurs while getting data with calls to SQLGetData, the application need not call SQLGetData for data that has already been returned; for more information, see "Getting Long Data" in Chapter 10: Retrieving Results (Basic).


Character Data and C Strings

Input parameters that refer to variable-length character data (such as column names, dynamic parameters, and string attribute values) have an associated length parameter. If the application terminates strings with the null character, as is typical in C, it provides as an argument either the length in bytes of the string (not including the null-terminator) or SQL_NTS (Null-Terminated String). A non-negative length argument specifies the actual length of the associated string. The length argument may be 0 to specify a zero-length string, which is distinct from a NULL value. The negative value SQL_NTS directs the driver to determine the length of the string by locating the null-termination character.

When character data is returned from the driver to the application, the driver must always null-terminate it. This gives the application the choice of whether to handle the data as a string or a character array. If the application buffer is not large enough to return all of the character data, the driver truncates it to the byte length of the buffer less the number of bytes required by the null-termination character, null-terminates the truncated data, and stores it in the buffer. Therefore, applications must always allocate extra space for the null-termination character in buffers used to retrieve character data. For example, a 51-byte buffer is needed to retrieve 50 characters of data.

Special care must be taken by both the application and the driver when sending or retrieving long character data in parts with SQLPutData or SQLGetData. If the data is passed as a series of null-terminated strings, the null-termination characters on these strings must be stripped before the data can be reassembled.

A number of ODBC programmers have confused character data and C strings. That this has occurred is an artifact of using the C language when defining ODBC functions. If an ODBC driver or application uses another language—remember that ODBC is language-independent—this confusion is less likely to arise.

When C strings are used to hold character data, the null-termination character is not considered to be part of the data and is not counted as part of its byte length. For example, the character data "ABC" can be held as the C string "ABC\0" or the character array {'A', 'B', 'C'}. The byte length of the data is 3, whether or not it is treated as a string or a character array.

Although applications and drivers commonly use C strings (null-terminated arrays of characters) to hold character data, there is no requirement to do this. In C, character data can also be treated as an array of characters (without null-termination) and its byte length passed separately in the length/indicator buffer.

Because character data can be held in a non–null-terminated array and its byte length passed separately, it is possible to embed null characters in character data. However, the behavior of ODBC functions in this case is undefined and it is driver-specific whether a driver handles this correctly. Thus, interoperable applications should always handle character data that can contain embedded null characters as binary data.


Data Types in ODBC

ODBC uses two sets of data types: SQL data types and C data types. SQL data types are used in the data source, and C data types are used in C code in the application.


Type Identifiers

To describe SQL and C data types, ODBC defines two sets of type identifiers. A type identifier describes the type of an SQL column or a C buffer. It is a #define value and is generally passed as a function argument or returned in metadata. For example, the following call to SQLBindParameter binds a variable of type SQL_DATE_STRUCT to a date parameter in an SQL statement. The C type identifier SQL_C_TYPE_DATE specifies the type of the Date variable, and the SQL type identifier SQL_TYPE_DATE specifies the type of the dynamic parameter.

SQL_DATE_STRUCT Date;
SQLINTEGER  DateInd = 0;
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_TYPE_DATE, SQL_TYPE_DATE, 0, 0,
                  &Date, 0, &DateInd);


SQL Data Types in ODBC

SQL data types are the types in which data is stored in the data source.


SQL Type Identifiers

Each data source defines its own SQL data types. ODBC defines type identifiers and describes the general characteristics of the SQL data types that might be mapped to each type identifier. It is driver-specific how each data type in the underlying data source is mapped to an SQL type identifier of ODBC.

For example, SQL_CHAR is the type identifier for a character column with a fixed length, typically between 1 and 254 characters. These characteristics correspond to the CHAR data type found in many SQL data sources. Thus, when an application discovers that the type identifier for a column is SQL_CHAR, it can assume it is probably dealing with a CHAR column. However, it should still check the byte length of the column before assuming it is between 1 and 254 characters; the driver for a non-SQL data source, for example, might map a fixed-length character column of 500 characters to SQL_CHAR or SQL_LONGVARCHAR, because neither is an exact match.

ODBC defines a wide variety of SQL type identifiers. However, the driver is not required to use all of these identifiers. Instead, it uses only those identifiers it needs to expose the SQL data types supported by the underlying data source. If the underlying data source supports SQL data types to which no type identifier corresponds, the driver can define additional type identifiers. For more information, see "Driver-Specific Data Types, Descriptor Types, Information Types, Diagnostic Types, and Attributes," in Chapter 17: Programming Considerations.

For a complete description of SQL type identifiers, see C Data Types in Appendix D: Data Types.


Retrieving Data Type Information with SQLGetTypeInfo

Because the mappings from underlying SQL data types to ODBC type identifiers are approximate, ODBC provides a function (SQLGetTypeInfo) through which a driver can completely describe each SQL data type in the data source. This function returns a result set, each row of which describes the characteristics of a single data type, such as name, type identifier, precision, scale, and nullability.

This information generally is used by generic applications that allow the user to create and alter tables. Such applications call SQLGetTypeInfo to retrieve the data type information and then present some or all of it to the user. Such applications need to be aware of two things:

  • More than one SQL data type can map to a single type identifier, which can make it difficult to determine which data type to use. To solve this, the result set is ordered first by type identifier and second by closeness to the type identifier's definition. In addition, data source–defined data types take precedence over user-defined data types. For example, suppose that a data source defines the INTEGER and COUNTER data types to be the same except that COUNTER is auto-incrementing. Suppose also that the user-defined type WHOLENUM is a synonym of INTEGER. Each of these types maps to SQL_INTEGER. In the SQLGetTypeInfo result set, INTEGER appears first, followed by WHOLENUM and then COUNTER. WHOLENUM appears after INTEGER because it is user-defined, but before COUNTER because it more closely matches the definition of the SQL_INTEGER type identifier.
  • ODBC does not define data type names for use in CREATE TABLE and ALTER TABLE statements. Instead, the application should use the name returned in the TYPE_NAME column of the result set returned by SQLGetTypeInfo. The reason for this is that although most of SQL does not vary much across DBMSs, data type names vary tremendously. Rather than forcing drivers to parse SQL statements and replace standard data type names with DBMS-specific data type names, ODBC requires applications to use the DBMS-specific names in the first place.

Note that SQLGetTypeInfo does not necessarily describe all of the data types an application can encounter. In particular, result sets might contain data types not directly supported by the data source. For example, the data types of the columns in result sets returned by catalog functions are defined by ODBC and these data types might not be supported by the data source. To determine the characteristics of the data types in a result set, an application calls SQLColAttribute.


C Data Types in ODBC

ODBC defines the C data types that are used by application variables and their corresponding type identifiers. These are used by the buffers that are bound to result set columns and statement parameters. For example, suppose an application wants to retrieve data from a result set column in character format. It declares a variable with the SQLCHAR * data type and binds this variable to the result set column with a type identifier of SQL_C_CHAR. For a complete list of C data types and type identifiers, see Appendix D: Data Types.

ODBC also defines a default mapping from each SQL data type to a C data type. For example, a 2-byte integer in the data source is mapped to a 2-byte integer in the application. To use the default mapping, an application specifies the SQL_C_DEFAULT type identifier. However, use of this identifier is discouraged for interoperability reasons.

All integer C data types defined in ODBC 1.x were signed. Unsigned C data types and their corresponding type identifiers were added in ODBC 2.0. Because of this, applications and drivers need to be particularly careful when dealing with 1.x versions.


Data Type Conversions

Data can be converted from one type to another at one of four times: when data is transferred from one application variable to another (C to C), when data in an application variable is sent to a statement parameter (C to SQL), when data in a result set column is returned in an application variable (SQL to C), and when data is transferred from one data source column to another (SQL to SQL).

Any conversion that occurs when data is transferred from one application variable to another is outside the scope of this document.

When an application binds a variable to a result set column or statement parameter, the application implicitly specifies a data type conversion in its choice of the data type of the application variable. For example, suppose a column contains integer data. If the application binds an integer variable to the column, it specifies that no conversion be done; if the application binds a character variable to the column, it specifies that the data be converted from integer to character.

ODBC defines how data is converted between each SQL and C data type. Basically, ODBC supports all reasonable conversions, such as character to integer and integer to float, and does not support ill-defined conversions, such as float to date. Drivers are required to support all conversions for each SQL data type they support. For a complete list of conversions between SQL and C data types, see "Converting Data from SQL to C Data Types" and "Converting Data from C to SQL Data Types" in Appendix D: Data Types.

ODBC also defines a scalar function for converting data from one SQL data type to another. The CONVERT scalar function is mapped by the driver to the underlying scalar function or functions defined to perform conversions in the data source. Because this function is mapped to DBMS-specific functions, ODBC does not define how these conversions work or what conversions must be supported. An application discovers what conversions are supported by a particular driver and data source through the SQL_CONVERT options in SQLGetInfo. For more information about the CONVERT scalar function, see "Escape Sequences in ODBC" in Chapter 8: SQL Statements and Explicit Data Type Conversion Function in Appendix E: Scalar Functions.


Conformance Levels

ODBC drivers give the application access to diverse data sources. Each driver lets the application determine at run time what ODBC capabilities and what SQL grammar the driver and each data source supports. This is not a requirement of applications designed to work with a single driver or a small, known set of drivers, because these applications can simply be written to the capabilities of that driver or drivers. To help applications discover driver and data source capabilities, two areas of conformance are available: the ODBC interface and SQL grammar.


Interface Conformance Levels

The purpose of leveling is to inform the application what features are available to it from the driver. A leveling scheme based on functions does not sufficiently achieve this goal. In ODBC 3.x, drivers are classified based on the features they possess. Supporting the feature can include supporting the function; it can also include supporting a descriptor field, a statement attribute, a "Y" value for an information type returned by SQLGetInfo, and so on.

To simplify specification of interface conformance, ODBC defines three conformance levels. To meet a particular conformance level, a driver must satisfy all of the requirements of that conformance level. Conformance with a given level implies complete conformance with all lower levels.

Conformance levels do not always divide neatly into support for a specific list of ODBC functions, but specify supported features as listed in the following sections. To provide support for a feature, a driver must support some or all forms of calls to certain ODBC functions (for more information, see "Function Conformance"), setting certain attributes (see "Attribute Conformance"), and certain descriptor fields (see "Descriptor Field Conformance").

The application discovers a driver's interface conformance level by connecting to a data source and calling SQLGetInfo with the SQL_ODBC_INTERFACE_CONFORMANCE option.

Drivers are free to implement features beyond the level to which they claim complete conformance. Applications discover any such additional capabilities by calling SQLGetFunctions (to determine which ODBC functions are present) and SQLGetInfo (to query various other ODBC capabilities).

There are three ODBC interface conformance levels: Core, Level 1, and Level 2.

Note These conformance levels have different requirements than the ODBC API conformance levels of the same name in ODBC 2.x. In particular, all the features implied by ODBC 2.x API conformance Level 1 are now part of the Core interface conformance level. As a result, many ODBC drivers may report Core-level interface conformance.


Core Interface Conformance

All ODBC drivers must exhibit at least Core-level interface conformance. Because the features in the Core level are those required by most generic interoperable applications, the driver can work with such applications. The features in the Core level also correspond to the features defined in the ISO CLI specification and to the nonoptional features defined in the X/Open CLI specification. A Core-level interface–conformant ODBC driver allows the application to do all of the following:

  • Allocate and free all types of handles, by calling SQLAllocHandle and SQLFreeHandle.
  • Use all forms of the SQLFreeStmt function.
  • Bind result set columns, by calling SQLBindCol.
  • Handle dynamic parameters, including arrays of parameters, in the input direction only, by calling SQLBindParameter and SQLNumParams. (Parameters in the output direction are feature 203 in "Level 2 Interface Conformance.")
  • Specify a bind offset.
  • Use the data-at-execution dialog, involving calls to SQLParamData and SQLPutData.
  • Manage cursors and cursor names, by calling SQLCloseCursor, SQLGetCursorName, and SQLSetCursorName.
  • Gain access to the description (metadata) of result sets, by calling SQLColAttribute, SQLDescribeCol, SQLNumResultCols, and SQLRowCount. (Use of these functions on column number 0 to retrieve bookmark metadata is feature 204 in "Level 2 Interface Conformance.")
  • Query the data dictionary, by calling the catalog functions SQLColumns, SQLGetTypeInfo, SQLStatistics, and SQLTables.

    The driver is not required to support multipart names of database tables and views. (For more information, see feature 101 in "Level 1 Interface Conformance" and feature 201 in "Level 2 Interface Conformance.") However, certain features of the SQL-92 specification, such as column qualification and names of indexes, are syntactically comparable to multipart naming. The present list of ODBC features is not intended to introduce new options into these aspects of SQL-92.

  • Manage data sources and connections, by calling SQLConnect, SQLDataSources, SQLDisconnect, and SQLDriverConnect. Obtain information on drivers, no matter which ODBC level they support, by calling SQLDrivers.
  • Prepare and execute SQL statements, by calling SQLExecDirect, SQLExecute, and SQLPrepare.
  • Fetch one row of a result set or multiple rows, in the forward direction only, by calling SQLFetch or by calling SQLFetchScroll with the FetchOrientation argument set to SQL_FETCH_NEXT.
  • Obtain an unbound column in parts, by calling SQLGetData.
  • Obtain current values of all attributes, by calling SQLGetConnectAttr, SQLGetEnvAttr, and SQLGetStmtAttr, and set all attributes to their default values and set certain attributes to nondefault values by calling SQLSetConnectAttr, SQLSetEnvAttr, and SQLSetStmtAttr.
  • Manipulate certain fields of descriptors, by calling SQLCopyDesc, SQLGetDescField, SQLGetDescRec, SQLSetDescField, and SQLSetDescRec.
  • Obtain diagnostic information, by calling SQLGetDiagField and SQLGetDiagRec.
  • Detect driver capabilities, by calling SQLGetFunctions and SQLGetInfo. Also, detect the result of any text substitutions made to an SQL statement before it is sent to the data source, by calling SQLNativeSql.
  • Use the syntax of SQLEndTran to commit a transaction. A Core-level driver need not support true transactions; therefore, the application cannot specify SQL_ROLLBACK nor SQL_AUTOCOMMIT_OFF for the SQL_ATTR_AUTOCOMMIT connection attribute. (For more information, see feature 109 in "Level 2 Interface Conformance.")
  • Call SQLCancel to cancel the data-at-execution dialog and, in multithread environments, to cancel an ODBC function executing in another thread. Core-level interface conformance does not mandate support for asynchronous execution of functions, nor the use of SQLCancel to cancel an ODBC function executing asynchronously. Neither the platform nor the ODBC driver need be multithread for the driver to conduct independent activities at the same time. However, in multithread environments, the ODBC driver must be thread-safe. Serialization of requests from the application is a conformant way to implement this specification, even though it might create serious performance problems.
  • Obtain the SQL_BEST_ROWID row-identifying column of tables, by calling SQLSpecialColumns. (Support for SQL_ROWVER is feature 208 in "Level 2 Interface Conformance.")

    Important ODBC Drivers must implement the functions in the Core interface conformance level.


Level 1 Interface Conformance

The Level 1 interface conformance level includes the Core interface conformance level functionality plus additional features, such as transactions, that are usually available in an OLTP relational DBMS. A Level 1 interface–conformant driver lets the application do the following, in addition to the features in the Core interface conformance level:

101 | Specify the schema of database tables and views (using two-part naming). (For more information, see the three-part naming feature 201 in "Level 2 Interface Conformance.")
102 | Invoke true asynchronous execution of ODBC functions, where applicable ODBC functions are all synchronous or all asynchronous on a given connection.
103 | Use scrollable cursors, and thereby achieve access to a result set in methods other than forward-only, by calling SQLFetchScroll with the FetchOrientation argument other than SQL_FETCH_NEXT. (The SQL_FETCH_BOOKMARK FetchOrientation is in feature 204 in "Level 2 Interface Conformance.")
104 | Obtain primary keys of tables, by calling SQLPrimaryKeys.
105 | Use stored procedures, through the ODBC escape sequence for procedure calls, and query the data dictionary regarding stored procedures, by calling SQLProcedureColumns and SQLProcedures. (The process by which procedures are created and stored on the data source is outside the scope of this document.)
106 | Connect to a data source by interactively browsing the available servers, by calling SQLBrowseConnect.
107 | Use ODBC functions instead of SQL statements to perform certain database operations: SQLSetPos with SQL_POSITION and SQL_REFRESH.
108 | Gain access to the contents of multiple result sets generated by batches and stored procedures, by calling SQLMoreResults.
109 | Delimit transactions spanning several ODBC functions, with true atomicity and the ability to specify SQL_ROLLBACK in SQLEndTran.


Level 2 Interface Conformance

The Level 2 interface conformance level includes the Level 1 interface conformance–level functionality plus the following features:

201 | Use three-part names of database tables and views. (For more information, see the two-part naming support feature 101 in "Level 1 Interface Conformance.")
202 | Describe dynamic parameters, by calling SQLDescribeParam.
203 | Use not only input parameters but also output and input/output parameters, and result values of stored procedures.
204 | Use bookmarks, including retrieving bookmarks, by calling SQLDescribeCol and SQLColAttribute on column number 0; fetching based on a bookmark, by calling SQLFetchScroll with the FetchOrientation argument set to SQL_FETCH_BOOKMARK; and update, delete, and fetch by bookmark operations, by calling SQLBulkOperations with the Operation argument set to SQL_UPDATE_BY_BOOKMARK, SQL_DELETE_BY_BOOKMARK, or SQL_FETCH_BY_BOOKMARK.
205 | Retrieve advanced information on the data dictionary, by calling SQLColumnPrivileges, SQLForeignKeys, and SQLTablePrivileges.
206 | Use ODBC functions instead of SQL statements to perform additional database operations, by calling SQLBulkOperations with SQL_ADD, or SQLSetPos with SQL_DELETE or SQL_UPDATE. (Support for calls to SQLSetPos with the LockType argument set to SQL_LOCK_EXCLUSIVE or SQL_LOCK_UNLOCK is not a part of the conformance levels but is an optional feature.)
207 | Enable asynchronous execution of ODBC functions for specified individual statements.
208 | Obtain the SQL_ROWVER row-identifying column of tables, by calling SQLSpecialColumns. (For more information, see the support for SQLSpecialColumns with the IdentifierType argument set to SQL_BEST_ROWID as feature 20 in "Core Interface Conformance.")
209 | Set the SQL_ATTR_CONCURRENCY statement attribute to at least one value other than SQL_CONCUR_READ_ONLY.
210 | The ability to time out login request and SQL queries (SQL_ATTR_LOGIN_TIMEOUT and SQL_ATTR_QUERY_TIMEOUT).
211 | The ability to change the default isolation level; the ability to execute transactions with the "serializable" level of isolation.


Function Conformance

The following table indicates the conformance level of each ODBC function, where this is well defined.

Function | Conformance level
SQLAllocHandle | Core
SQLBindCol | Core
SQLBindParameter | Core[1]
SQLBrowseConnect | Level 1
SQLBulkOperations | Level 1
SQLCancel | Core[1]
SQLCloseCursor | Core
SQLColAttribute | Core[1]
SQLColumnPrivileges | Level 2
SQLColumns | Core
SQLConnect | Core
SQLCopyDesc | Core
SQLDataSources | Core
SQLDescribeCol | Core[1]
SQLDescribeParam | Level 2
SQLDisconnect | Core
SQLDriverConnect | Core
SQLDrivers | Core
SQLEndTran | Core[1]
SQLExecDirect | Core
SQLExecute | Core
SQLFetch | Core
SQLFetchScroll | Core[1]
SQLForeignKeys | Level 2
SQLFreeHandle | Core
SQLFreeStmt | Core
SQLGetConnectAttr | Core
SQLGetCursorName | Core
SQLGetData | Core
SQLGetDescField | Core
SQLGetDescRec | Core
SQLGetDiagField | Core
SQLGetDiagRec | Core
SQLGetEnvAttr | Core
SQLGetFunctions | Core
SQLGetInfo | Core
SQLGetStmtAttr | Core
SQLGetTypeInfo | Core
SQLMoreResults | Level 1
SQLNativeSql | Core
SQLNumParams | Core
SQLNumResultCols | Core
SQLParamData | Core
SQLPrepare | Core
SQLPrimaryKeys | Level 1
SQLProcedureColumns | Level 1
SQLProcedures | Level 1
SQLPutData | Core
SQLRowCount | Core
SQLSetConnectAttr | Core[2]
SQLSetCursorName | Core
SQLSetDescField | Core[1]
SQLSetDescRec | Core
SQLSetEnvAttr | Core[2]
SQLSetPos | Level 1[1]
SQLSetStmtAttr | Core[2]
SQLSpecialColumns | Core[1]
SQLStatistics | Core
SQLTablePrivileges | Level 2
SQLTables | Core

[1] Significant features of this function are available only at higher conformance levels.

[2] Setting certain attributes to nondefault values depends on the conformance level. For more information, see the next section, "Attribute Conformance."


Attribute Conformance

The following table indicates the conformance level of each ODBC environment attribute, where this is well defined.

Function | Conformance level
SQL_ATTR_CONNECTION_POOLING | --[1]
SQL_ATTR_CP_MATCH | --[1]
SQL_ATTR_ODBC_VER | Core
SQL_ATTR_OUTPUT_NTS | --[1]

[1] This is an optional feature and as such is not part of the conformance levels.

The following table indicates the conformance level of each ODBC connection attribute, where this is well defined.

Function | Conformance level
SQL_ATTR_ACCESS_MODE | Core
SQL_ATTR_ASYNC_ENABLE | Level 1/Level 2[1]
SQL_ATTR_AUTO_IPD | Level 2
SQL_ATTR_AUTOCOMMIT | Level 1
SQL_ATTR_CONNECTION_DEAD | Level 1
SQL_ATTR_CONNECTION_TIMEOUT | Level 2
SQL_ATTR_CURRENT_CATALOG | Level 2
SQL_ATTR_LOGIN_TIMEOUT | Level 2
SQL_ATTR_ODBC_CURSORS | Core
SQL_ATTR_PACKET_SIZE | Level 2
SQL_ATTR_QUIET_MODE | Core
SQL_ATTR_TRACE | Core
SQL_ATTR_TRACEFILE | Core
SQL_ATTR_TRANSLATE_LIB | Core
SQL_ATTR_TRANSLATE_OPTION | Core
SQL_ATTR_TXN_ISOLATION | Level 1/Level 2[2]

[1] Applications that support connection-level asynchrony (required for Level 1) must support setting this attribute to SQL_TRUE by calling SQLSetConnectAttr; the attribute need not be settable to a value other than its default value through SQLSetStmtAttr. Applications that support statement-level asynchrony (required for Level 2) must support setting this attribute to SQL_TRUE using either function.

[2] For Level 1 interface conformance, the driver must support one value in addition to the driver-defined default value (available by calling SQLGetInfo with the SQL_DEFAULT_TXN_ISOLATION option). For Level 2 interface conformance, the driver must also support SQL_TXN_SERIALIZABLE.

The following table indicates the conformance level of each ODBC statement attribute, where this is well defined.

Function | Conformance level
SQL_ATTR_APP_PARAM_DESC | Core
SQL_ATTR_APP_ROW_DESC | Core
SQL_ATTR_ASYNC_ENABLE | Level 1/Level 2[1]
SQL_ATTR_CONCURRENCY | Level 1/Level 2[2]
SQL_ATTR_CURSOR_SCROLLABLE | Level 1
SQL_ATTR_CURSOR_SENSITIVITY | Level 2
SQL_ATTR_CURSOR_TYPE | Core/Level 2[3]
SQL_ATTR_ENABLE_AUTO_IPD | Level 2
SQL_ATTR_FETCH_BOOKMARK_PTR | Level 2
SQL_ATTR_IMP_PARAM_DESC | Core
SQL_ATTR_IMP_ROW_DESC | Core
SQL_ATTR_KEYSET_SIZE | Level 2
SQL_ATTR_MAX_LENGTH | Level 1
SQL_ATTR_MAX_ROWS | Level 1
SQL_ATTR_METADATA_ID | Core
SQL_ATTR_NOSCAN | Core
SQL_ATTR_PARAM_BIND_OFFSET_PTR | Core
SQL_ATTR_PARAM_BIND_TYPE | Core
SQL_ATTR_PARAM_OPERATION_PTR | Core
SQL_ATTR_PARAM_STATUS_PTR | Core
SQL_ATTR_PARAMS_PROCESSED_PTR | Core
SQL_ATTR_PARAMSET_SIZE | Core
SQL_ATTR_QUERY_TIMEOUT | Level 2
SQL_ATTR_RETRIEVE_DATA | Level 1
SQL_ATTR_ROW_ARRAY_SIZE | Core
SQL_ATTR_ROW_BIND_OFFSET_PTR | Core
SQL_ATTR_ROW_BIND_TYPE | Core
SQL_ATTR_ROW_NUMBER | Level 1
SQL_ATTR_ROW_OPERATION_PTR | Level 1
SQL_ATTR_ROW_STATUS_PTR | Core
SQL_ATTR_ROWS_FETCHED_PTR | Core
SQL_ATTR_SIMULATE_CURSOR | Level 2
SQL_ATTR_USE_BOOKMARKS | Level 2

[1] Applications that support connection-level asynchrony (required for Level 1) must support setting this attribute to SQL_TRUE by calling SQLSetConnectAttr; the attribute need not be settable to a value other than its default value through SQLSetStmtAttr. Applications that support statement-level asynchrony (required for Level 2) must support setting this attribute to SQL_TRUE using either function.

[2] For Level 2 interface conformance, the driver must support SQL_CONCUR_READ_ONLY and at least one other value.

[3] For Level 1 interface conformance, the driver must support SQL_CURSOR_FORWARD_ONLY and at least one other value. For Level 2 interface conformance, the driver must support all values defined in this document.


Descriptor Field Conformance

The following table indicates the conformance level of each ODBC descriptor header field, where this is well defined.

Function | Conformance level
SQL_DESC_ALLOC_TYPE | Core
SQL_DESC_ARRAY_SIZE | Core
SQL_DESC_ARRAY_STATUS_PTR | Core (for APD, IPR, and IRD); Level 1 (for ARD)
SQL_DESC_BIND_OFFSET_PTR | Core
SQL_DESC_BIND_TYPE | Core
SQL_DESC_COUNT | Core
SQL_DESC_ROWS_PROCESSED_PTR | Core

The following table indicates the conformance level of each ODBC descriptor record field, where this is well defined.

Function | Conformance level
SQL_DESC_AUTO_UNIQUE_VALUE | Level 2
SQL_DESC_BASE_COLUMN_NAME | Core
SQL_DESC_BASE_TABLE_NAME | Level 1
SQL_DESC_CASE_SENSITIVE | Core
SQL_DESC_CATALOG_NAME | Level 2
SQL_DESC_CONCISE_TYPE | Core
SQL_DESC_DATA_PTR | Core
SQL_DESC_DATETIME_INTERVAL_ CODE | Core[1]
SQL_DESC_DATETIME_INTERVAL_ PRECISION | Core[1]
SQL_DESC_DISPLAY_SIZE | Core
SQL_DESC_FIXED_PREC_SCALE | Core
SQL_DESC_INDICATOR_PTR | Core
SQL_DESC_LABEL | Level 2
SQL_DESC_LENGTH | Core
SQL_DESC_LITERAL_PREFIX | Core
SQL_DESC_LITERAL_SUFFIX | Core
SQL_DESC_LOCAL_TYPE_NAME | Core
SQL_DESC_NAME | Core
SQL_DESC_NULLABLE | Core
SQL_DESC_OCTET_LENGTH | Core
SQL_DESC_OCTET_LENGTH_PTR | Core
SQL_DESC_PARAMETER_TYPE | Core/Level 2[2]
SQL_DESC_PRECISION | Core
SQL_DESC_ROWVER | Level 1
SQL_DESC_SCALE | Core
SQL_DESC_SCHEMA_NAME | Level 1
SQL_DESC_SEARCHABLE | Core
SQL_DESC_TABLE_NAME | Level 1
SQL_DESC_TYPE | Core
SQL_DESC_TYPE_NAME | Core
SQL_DESC_UNNAMED | Core
SQL_DESC_UNSIGNED | Core
SQL_DESC_UPDATABLE | Core

[1] Support for these record fields is required only if the driver supports the applicable data types.

[2] For Core-level conformance, the driver must support SQL_PARAM_INPUT. For Level 2 interface conformance, the driver must also support SQL_PARAM_INPUT_OUTPUT and SQL_PARAM_OUTPUT.


SQL Conformance Levels

The level of SQL-92 grammar supported by a driver is indicated by the value returned by a call to SQLGetInfo with the SQL_SQL_CONFORMANCE information type. This indicates whether the driver conforms to the Entry, FIPS Transitional, Intermediate, or Full levels defined in SQL-92.

All ODBC drivers must support the minimum SQL grammar described in "SQL Minimum Grammar" in Appendix C: SQL Grammar. This grammar is a subset of the Entry level of SQL-92. Drivers may support additional SQL and be conformant to the SQL-92 Entry, Intermediate, or Full level, or to the FIPS 127-2 Transitional level. Drivers that comply to a given level of SQL-92 or FIPS 127-2 can support additional features in any of the higher levels yet not be fully conformant to that level. To determine whether a feature is supported, an application should call SQLGetInfo with the appropriate information type. The conformance level of an SQL feature is described in the corresponding information type. (See the SQLGetInfo function description.)


Environment, Connection, and Statement Attributes

ODBC defines a number of attributes that are associated with environments, connections, or statements.

Environment attributes affect the entire environment, such as whether connection pooling is enabled. Environment attributes are set with SQLSetEnvAttr and retrieved with SQLGetEnvAttr.

Connection attributes affect each connection individually, such as how long a driver should wait while attempting to connect to a data source before timing out. Connection attributes are set with SQLSetConnectAttr and retrieved with SQLGetConnectAttr. For more information about connection attributes, see "Connection Attributes" in Chapter 6: Connecting to a Data Source or Driver.

Statement attributes affect each statement individually, such as whether a statement should be executed asynchronously. Statement attributes are set with SQLSetStmtAttr and retrieved with SQLGetStmtAttr. A few statement attributes are read-only attributes and cannot be set. For example, the SQL_ATTR_ROW_NUMBER statement attribute, which is used to retrieve the number of the current row in the cursor, is read-only. For more information about statement attributes, see "Statement Attributes" in Chapter 9: Executing Statements.

In addition to attributes defined by ODBC, a driver can define its own connection and statement attributes. Driver-defined attributes must be registered with X/Open to ensure that two driver vendors do not assign the same integer value to different, proprietary attributes. For more information, see "Driver-Specific Data Types, Descriptor Types, Information Types, Diagnostic Types, and Attributes" in Chapter 17: Programming Considerations.

For a complete list of attributes, see SQLSetEnvAttr, SQLSetConnectAttr, and SQLSetStmtAttr. Most attributes are also described in the description of the ODBC function that they affect.


Tables and Views

In ODBC functions, tables and views are interchangeable. The term table is used both for tables and for views, except where the term view is used explicitly.


Chapter 5: Basic ODBC Application Steps

This chapter describes the general flow of ODBC applications. It is unlikely that any application calls all of these functions in exactly this order. However, most applications use some variation of these steps. The basic application steps are shown in the following illustration.


Step 1: Connect to the Data Source

The first step in any application is to connect to the data source. This phase, including the functions it requires, is shown in the following illustration.

The first step in connecting to the data source is to load the Driver Manager and allocate the environment handle with SQLAllocHandle. For more information, see "Allocating the Environment Handle" in Chapter 6: Connecting to a Data Source or Driver.

The application then registers the version of ODBC to which it conforms by calling SQLSetEnvAttr with the SQL_ATTR_APP_ODBC_VER environment attribute. For more information, see the "Declaring the Application's ODBC Version" section in Chapter 6: Connecting to a Data Source or Driver, and the "Backward Compatibility and Standards Compliance" section in Chapter 17: Programming Considerations.

Next, the application allocates a connection handle with SQLAllocHandle and connects to the data source with SQLConnect, SQLDriverConnect, or SQLBrowseConnect. For more information, see "Allocating a Connection Handle" and "Establishing a Connection" in Chapter 6: Connecting to a Data Source or Driver.

The application then sets any connection attributes, such as whether to manually commit transactions. For more information, see "Connection Attributes" in Chapter 6: Connecting to a Data Source or Driver.


Step 2: Initialize the Application

The second step is to initialize the application, as shown in the following illustration. Exactly what is done here varies with the application.

At this point, it is common to use SQLGetInfo to discover the capabilities of the driver. For more information, see "Considering Database Features to Use" in Chapter 16: Interoperability.

All applications need to allocate a statement handle with SQLAllocHandle, and many applications set statement attributes, such as the cursor type, with SQLSetStmtAttr. For more information, see "Allocating a Statement Handle" and "Statement Attributes" in Chapter 9: Executing Statements.


Step 3: Build and Execute an SQL Statement

The third step is to build and execute an SQL statement, as shown in the following illustration. The methods used to perform this step are likely to vary tremendously. The application might prompt the user to enter an SQL statement, build an SQL statement based on user input, or use a hard-coded SQL statement. For more information, see "Constructing SQL Statements" in Chapter 8: SQL Statements.

If the SQL statement contains parameters, the application binds them to application variables by calling SQLBindParameter for each parameter. For more information, see "Statement Parameters" in Chapter 9: Executing Statements.

After the SQL statement is built and any parameters are bound, the statement is executed with SQLExecDirect. If the statement will be executed multiple times, it can be prepared with SQLPrepare and executed with SQLExecute. For more information, see "Executing a Statement" in Chapter 9: Executing Statements.

The application might also forgo executing an SQL statement altogether and instead call a function to return a result set containing catalog information, such as the available columns or tables. For more information, see "Uses of Catalog Data" in Chapter 7: Catalog Functions.

The application's next action depends on the type of SQL statement executed.

Type of SQL statement | Proceed to
SELECT or catalog function | "Step 4a: Fetch the Results"
UPDATE, DELETE, or INSERT | "Step 4b: Fetch the Row Count"
All other SQL statements | "Step 3: Build and Execute an SQL Statement" (this topic) or "Step 5: Commit the Transaction"


Step 4a: Fetch the Results

The next step is to fetch the results, as shown in the following illustration.

If the statement executed in "Step 3: Build and Execute an SQL Statement" was a SELECT statement or a catalog function, the application first calls SQLNumResultCols to determine the number of columns in the result set. This step is not necessary if the application already knows the number of result set columns, such as when the SQL statement is hard-coded in a vertical or custom application.

Next, the application retrieves the name, data type, precision, and scale of each result set column with SQLDescribeCol. Again, this is not necessary for applications such as vertical and custom applications that already know this information. The application passes this information to SQLBindCol, which binds an application variable to a column in the result set.

The application now calls SQLFetch to retrieve the first row of data and place the data from that row in the variables bound with SQLBindCol. If there is any long data in the row, it then calls SQLGetData to retrieve that data. The application continues to call SQLFetch and SQLGetData to retrieve additional data. After it has finished fetching data, it calls SQLCloseCursor to close the cursor.

For a complete description of retrieving results, see Chapter 10: Retrieving Results (Basic) and Chapter 11: Retrieving Results (Advanced).

The application now returns to "Step 3: Build and Execute an SQL Statement" to execute another statement in the same transaction; or proceeds to "Step 5: Commit the Transaction" to commit or roll back the transaction.


Step 4b: Fetch the Row Count

The next step is to fetch the row count, as shown in the following illustration.

If the statement executed in Step 3 was an UPDATE, DELETE, or INSERT statement, the application retrieves the count of affected rows with SQLRowCount. For more information, see "Determining the Number of Affected Rows" in Chapter 12: Updating Data.

The application now returns to step 3 to execute another statement in the same transaction or proceeds to step 5 to commit or roll back the transaction.


Step 5: Commit the Transaction

The next step is to commit the transaction, as shown in the following illustration.

The fifth step is to call SQLEndTran to commit or roll back the transaction. The application performs this step only if it set the transaction commit mode to manual-commit; if the transaction commit mode is auto-commit, which is the default, the transaction is automatically committed when the statement is executed. For more information, see Chapter 14: Transactions.

To execute a statement in a new transaction, the application returns to step 3. To disconnect from the data source, the application proceeds to step 6.


Step 6: Disconnect from the Data Source

The final step is to disconnect from the data source, as shown in the following illustration. First, the application frees any statement handles by calling SQLFreeHandle. For more information, see "Freeing a Statement Handle" in Chapter 9: Executing Statements.

Next, the application disconnects from the data source with SQLDisconnect and frees the connection handle with SQLFreeHandle. For more information, see "Disconnecting from a Data Source or Driver" in Chapter 6: Connecting to a Data Source or Driver.

Finally, the application frees the environment handle with SQLFreeHandle and unloads the Driver Manager. For more information, see "Allocating the Environment Handle" in Chapter 6: Connecting to a Data Source or Driver.


Chapter 6: Connecting to a Data Source or Driver

An application can be connected to any number of drivers and data sources. These can be a variety of drivers and data sources, the same driver and a variety of data sources, or even multiple connections to the same driver and data source.


Allocating the Environment Handle

The first task for any ODBC application is to load the Driver Manager; how this is done is operating-system dependent. For example, on a computer running Microsoft® Windows NT® Server/Windows 2000 Server, Windows NT Workstation/Windows 2000 Professional, or Microsoft Windows® 95/98, the application either links to the Driver Manager library or calls LoadLibrary to load the Driver Manager DLL.

The next task, which must be done before an application can call any other ODBC function, is to initialize the ODBC environment and allocate an environment handle, as follows:

  1. The application declares a variable of type SQLHENV. It then calls SQLAllocHandle and passes the address of this variable and the SQL_HANDLE_ENV option. For example:
    SQLHENV henv1;

SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv1);
  2. The Driver Manager allocates a structure in which to store information about the environment, and returns the environment handle in the variable.

The Driver Manager does not call SQLAllocHandle in the driver at this time because it does not know which driver to call. It delays calling SQLAllocHandle in the driver until the application calls a function to connect to a data source. For more information, see "Driver Manager's Role in the Connection Process," later in this chapter.

When the application has finished using ODBC, it frees the environment handle with SQLFreeHandle. After freeing the environment, it is an application programming error to use the environment's handle in a call to an ODBC function; doing so has undefined but probably fatal consequences.

When SQLFreeHandle is called, the driver releases the structure used to store information about the environment. Note that SQLFreeHandle cannot be called for an environment handle until after all connection handles on that environment handle have been freed.

For more information about the environment handle, see "Environment Handles" in Chapter 4: ODBC Fundamentals.


Declaring the Application's ODBC Version

Before an application allocates a connection, it must set the SQL_ATTR_ODBC_VERSION environment attribute. This attribute states that the application follows the ODBC 2.x or ODBC 3.x specification when using the following items:

  • SQLSTATEs. Many SQLSTATE values are different in ODBC 2.x and ODBC 3.x.
  • Date, Time, and Timestamp Type Identifiers. The following table shows the type identifiers for date, time, and timestamp data in ODBC 2.x and ODBC 3.x.
    ODBC 2.x | ODBC 3.x
    SQL Type Identifiers
    SQL_DATE | SQL_TYPE_DATE
    SQL_TIME | SQL_TYPE_TIME
    SQL_TIMESTAMP | SQL_TYPE_TIMESTAMP
    C Type Identifiers
    SQL_C_DATE | SQL_C_TYPE_DATE
    SQL_C_TIME | SQL_C_TYPE_TIME
    SQL_C_TIMESTAMP | SQL_C_TYPE_TIMESTAMP
  • CatalogName Argument in SQLTables. In ODBC 2.x, the wildcard characters ("%" and "_") in the CatalogName argument are treated literally. In ODBC 3.x, they are treated as wildcard characters. Thus, an application that follows the ODBC 2.x specification cannot use these as wildcard characters and does not escape them when using them as literals. An application that follows the ODBC 3.x specification can use these as wildcard characters or escape them and use them as literals. For more information, see "Arguments in Catalog Functions" in Chapter 7: Catalog Functions.

The ODBC 3.x Driver Manager and ODBC 3.x drivers check the version of the ODBC specification to which an application is written and respond accordingly. For example, if the application follows the ODBC 2.x specification and calls SQLExecute before calling SQLPrepare, the ODBC 3.x Driver Manager returns SQLSTATE S1010 (Function sequence error). If the application follows the ODBC 3.x specification, the Driver Manager returns SQLSTATE HY010 (Function sequence error). For more information, see "Backward Compatibility and Standards Compliance" in Chapter 17: Programming Considerations.

Important Applications that follow the ODBC 3.x specification must use conditional code to avoid using functionality new to ODBC 3.x when working with ODBC 2.x drivers. ODBC 2.x drivers do not support functionality new to ODBC 3.x just because the application declares that it follows the ODBC 3.x specification. Furthermore, ODBC 3.x drivers do not cease to support functionality new to ODBC 3.x just because the application declares that it follows the ODBC 2.x specification.


Choosing a Data Source or Driver

The data source or driver used by an application is sometimes hard-coded in the application. For example, a custom application written by an MIS department to transfer data from one data source to another would contain the names of those data sources—the application simply would not work with any other data sources. Another example is a vertical application, such as one used for order entry. Such an application always uses the same data source, which has a predefined schema known by the application.

Other applications choose the data source or driver at run time. Usually, these are generic applications that do ad hoc queries, such as a spreadsheet that uses ODBC to import data. Such applications usually list the available data sources or drivers and let users choose the ones they want to work with. Whether a generic application lists data sources, drivers, or both often depends on whether the application uses DBMS-based or file-based drivers.

DBMS-based drivers usually require a complex set of connection information, such as the network address, network protocol, database name, and so on. The purpose of a data source is to hide all of this information. Hence, the data source paradigm lends itself to use with DBMS-based drivers. An application can display a list of data sources to the user in one of two ways. It can call SQLDriverConnect with the DSN (Data Source Name) keyword and no associated value; the Driver Manager will display a list of data source names. If the application wants control over the appearance of the list, it calls SQLDataSources to retrieve a list of available data sources and constructs its own dialog box. This function is implemented by the Driver Manager and can be called before any drivers are loaded. The application then calls a connection function and passes it the name of the chosen data source.

If a data source is not specified, the default data source indicated by the system information is used. (For more information, see "Default Subkey" in Chapter 19: Configuring Data Sources.) If SQLConnect is called with a ServerName argument that cannot be found, is a null pointer, or is "DEFAULT", the Driver Manager connects to the default data source. The default data source is also used if the connection string used in a call to SQLDriverConnect or SQLBrowseConnect contains the DSN keyword set to "DEFAULT" or if the specified data source is not found. Additionally, the default data source is used if the connection string used in a call to SQLDriverConnect does not contain the DSN keyword.

With file-based drivers, it is possible to use a file paradigm. For data stored on the local machine, users often know that their data is in a particular file, such as Employee.dbf. Rather than choosing an unknown data source, it is easier for such users to choose the file they know. To implement this, the application first calls SQLDrivers. This function is implemented by the Driver Manager and can be called before any drivers are loaded. SQLDrivers returns a list of available drivers; it also returns values for the FileUsage and FileExtns keywords. The FileUsage keyword explains whether file-based drivers treat files as tables, as does Xbase, or as databases, as does Microsoft® Access. The FileExtns keyword lists the file extensions the driver recognizes, such as .dbf for an Xbase driver. Using this information, the application constructs a dialog box through which the user chooses a file. Based on the extension of the chosen file, the application then connects directly to the driver by calling SQLDriverConnect with the DRIVER keyword.

There is nothing to stop an application from using a data source with a file-based driver or calling SQLDriverConnect with the DRIVER keyword to connect directly to a DBMS-based driver. Here are several common uses of the DRIVER keyword for DBMS-based drivers:

  • Not creating data sources. For example, a custom application might use a particular driver and database. If the driver name and all information needed to connect to the database is hard-coded in the application, users do not need to create a data source on their computer to run the application—all they need to do is install the application and driver.

    A disadvantage of this method is that the application must be recompiled and redistributed if the connection information changes. If a data source name is hard-coded in the application instead of complete connection information, each user needs to change only the information in the data source.

  • Accessing a particular DBMS a single time. For example, a spreadsheet that retrieves data by calling ODBC functions might contain the DRIVER keyword to identify a particular driver. Because the driver name is meaningful to any users that have that driver, the spreadsheet could be passed among those users. If the spreadsheet contained a data source name, each user would have to create the same data source to use the spreadsheet.
  • Browsing the system for all databases accessible to a particular driver. For more information, see "Connecting with SQLBrowseConnect," later in this chapter.


Allocating a Connection Handle

Before the application can connect to a data source or driver, it must allocate a connection handle, as follows:

  1. The application declares a variable of type SQLHDBC. It then calls SQLAllocHandle and passes the address of this variable, the handle of the environment in which to allocate the connection, and the SQL_HANDLE_DBC option. For example:
    SQLHDBC hdbc1;

SQLAllocHandle(SQL_HANDLE_DBC, henv1, &hdbc1);
  2. The Driver Manager allocates a structure in which to store information about the statement and returns the connection handle in the variable.

The Driver Manager does not call SQLAllocHandle in the driver at this time because it does not know which driver to call. It delays calling SQLAllocHandle in the driver until the application calls a function to connect to a data source. For more information, see "Driver Manager's Role in the Connection Process," later in this chapter.

It is important to note that allocating a connection handle is not the same as loading a driver. The driver is not loaded until a connection function is called. Thus, after allocating a connection handle and before connecting to the driver or data source, the only functions the application can call with the connection handle are SQLSetConnectAttr, SQLGetConnectAttr, or SQLGetInfo with the SQL_ODBC_VER option. Calling other functions with the connection handle, such as SQLEndTran, returns SQLSTATE 08003 (Connection not open). For complete details, see Appendix B: ODBC State Transition Tables.

For more information about connection handles, see "Connection Handles" in Chapter 4: ODBC Fundamentals.


Connection Attributes

Connection attributes are characteristics of the connection. For example, because transactions occur at the connection level, the transaction isolation level is a connection attribute. Similarly, the login timeout, or number of seconds to wait while trying to connect before timing out, is a connection attribute.

Connection attributes are set with SQLSetConnectAttr and their current settings retrieved with SQLGetConnectAttr. If SQLSetConnectAttr is called before the driver is loaded, the Driver Manager stores the attributes in its connection structure and sets them in the driver as part of the connection process. There is no requirement that an application set any connection attributes; all connection attributes have defaults, some of which are driver-specific.

A connection attribute can be set before or after connection, or either, depending on the attribute and the driver. The login timeout (SQL_ATTR_LOGIN_TIMEOUT) applies to the connection process and is effective only if set before connecting. The attributes that specify whether to use the ODBC cursor library (SQL_ATTR_ODBC_CURSORS) and the network packet size (SQL_ATTR_PACKET_SIZE) must be set before connecting, because the ODBC cursor library resides between the Driver Manager and the driver and therefore must be loaded before the driver.

The attributes to specify whether a data source is read-only or read-write (SQL_ATTR_ACCESS_MODE) and the current catalog (SQL_ATTR_CURRENT_CATALOG) can be set before or after connecting, depending on the driver. However, interoperable applications set them before connecting because some drivers do not support changing these after connecting.

Some connection attributes have a default before the connection is made, while others do not. Those that do are SQL_ATTR_ACCESS_MODE, SQL_ATTR_AUTOCOMMIT, SQL_ATTR_LOGIN_TIMEOUT, SQL_ATTR_ODBC_CURSORS, SQL_ATTR_TRACE, and SQL_ATTR_TRACEFILE.

The translation connection attributes (SQL_ATTR_TRANSLATE_DLL and SQL_ATTR_TRANSLATE_OPTION) must be set after connecting.

All other connection attributes can be set at any time. For more information, see the SQLSetConnectAttr function description. (Connection attributes cannot be set on the environment level by a call to SQLSetEnvAttr.)


Establishing a Connection

After allocating environment and connection handles and setting any connection attributes, the application is ready to connect to the data source or driver. There are three different functions the application can use to do this: SQLConnect (Core interface conformance level), SQLDriverConnect (Core), and SQLBrowseConnect (Level 1). Each of the three is designed to be used in a different scenario. Before connecting, the application can determine which of these functions is supported with the ConnectFunctions keyword returned by SQLDrivers.

Note Some drivers limit the number of active connections they support. An application calls SQLGetInfo with the SQL_MAX_DRIVER_CONNECTIONS option to determine how many active connections a particular driver supports.


Default Data Source

The driver may select a data source, called the default data source, in certain cases where the application does not explicitly specify one:

  • In a call to SQLConnect where the ServerName argument is a zero-length string, a null pointer, or DEFAULT.
  • In a call to SQLDriverConnect where InConnectionString either specifies DSN=DEFAULT or specifies with the DSN keyword a data source that is not contained in the system information.

It is driver-defined how the default data source is specified. This may involve administrative action and may depend on the user.


Connecting with SQLConnect

SQLConnect is the simplest connection function. It requires a data source name and accepts an optional user ID and password. It works well for applications that hard-code a data source name and do not require a user ID or password. It also works well for applications that want to control their own "look and feel" or that have no user interface. Such applications can build a list of data sources using SQLDataSources, prompt the user for data source, user ID, and password, and then call SQLConnect.


Connection Strings

A connection string contains information used for establishing a connection. A complete connection string contains all the information needed to establish a connection. The connection string is a series of keyword/value pairs separated by semicolons. (For the complete syntax of a connection string, see the SQLDriverConnect function description.) The connection string is used by:

  • SQLDriverConnect, which completes the connection string by interaction with the user.
  • SQLBrowseConnect, which completes the connection string iteratively with the data source.

SQLConnect does not use a connection string; using SQLConnect is analogous to connecting using a connection string with exactly three keyword/value pairs (for data source name and, optionally, user ID and password).


Connecting with SQLDriverConnect

SQLDriverConnect is used to connect to a data source using a connection string. SQLDriverConnect is used instead of SQLConnect for the following reasons:

  • To let the application use driver-specific connection information.
  • To request that the driver prompt the user for connection information.
  • To connect without specifying a data source.


Driver-Specific Connection Information

SQLConnect assumes that a data source name, user ID, and password are sufficient to connect to a data source and that all other connection information can be stored on the system. This is often not the case. For example, a driver might need one user ID and password to log on to a server and a different user ID and password to log on to a DBMS. Because SQLConnect accepts a single user ID and password, this means that the other user ID and password must be stored with the data source information on the system if SQLConnect is to be used. This is a potential breach of security and should be avoided unless the password is encrypted.

SQLDriverConnect allows the driver to define an arbitrary amount of connection information in the keyword-value pairs of the connection string. For example, suppose a driver requires a data source name, a user ID and password for the server, and a user ID and password for the DBMS. A custom program that always uses the XYZ Corp data source might prompt the user for IDs and passwords and build the following set of keyword-value pairs, or connection string, to pass to SQLDriverConnect:

DSN=XYZ Corp;UID=Gomez;PWD=Sesame;UIDDBMS=JGomez;PWDDBMS=Shazam;

The DSN (Data Source Name) keyword names the data source, the UID and PWD keywords specify the user ID and password for the server, and the UIDDBMS and PWDDBMS keywords specify the user ID and password for the DBMS. Note that the final semicolon is optional. SQLDriverConnect parses this string; uses the XYZ Corp data source name to retrieve additional connection information from the system, such as the server address; and logs on to the server and DBMS using the specified user IDs and passwords.

Keyword-value pairs in SQLDriverConnect must follow certain syntax rules. The keywords and their values should not contain the []{}(),;?*=!@ characters. The value of the DSN keyword cannot consist only of blanks and should not contain leading blanks. Because of the registry grammar, keywords and data source names cannot contain the backslash (\) character. Spaces are not allowed around the equal sign in the keyword-value pair.

The FILEDSN keyword can be used in a call to SQLDriverConnect to specify the name of a file containing data source information (see "Connecting Using File Data Sources," later in this section). The SAVEFILE keyword can be used to specify the name of a .dsn file in which the keyword-value pairs of a successful connection made by the call to SQLDriverConnect will be saved. For more information on file data sources, see the SQLDriverConnect function description.


Prompting the User for Connection Information

If the application uses SQLConnect and needs to prompt the user for any connection information, such as a user name and password, it must do so itself. While this allows the application to control its "look and feel," it might force the application to contain driver-specific code. This occurs when the application needs to prompt the user for driver-specific connection information. This presents an impossible situation for generic applications, which are designed to work with any and all drivers, including drivers that do not exist when the application is written.

SQLDriverConnect can prompt the user for connection information. For example, the custom program mentioned earlier could pass the following connection string to SQLDriverConnect:

DSN=XYZ Corp;

The driver might then display a dialog box that prompts for user IDs and passwords, similar to the following illustration.

That the driver can prompt for connection information is particularly useful to generic and vertical applications. These applications should not contain driver-specific information, and having the driver prompt for the information it needs keeps that information out of the application. This is shown by the previous two examples. When the application passed only the data source name to the driver, the application did not contain any driver-specific information and was therefore not tied to a particular driver. When the application passed a complete connection string to the driver, it was tied to the driver that could interpret that string.

A generic application might take this one step further and not even specify a data source. When SQLDriverConnect receives an empty connection string, the Driver Manager displays the following dialog box.

After the user selects a data source, the Driver Manager constructs a connection string specifying that data source and passes it to the driver. The driver can then prompt the user for any additional information it needs.

The conditions under which the driver prompts the user are controlled by the DriverCompletion flag; there are options to always prompt, prompt if necessary, or never prompt. For a complete description of this flag, see the SQLDriverConnect function description.


Connecting Using File Data Sources

The connection information for a file data source is stored in a .dsn file. As a result, the connection string can be used repeatedly by a single user or shared among several users if they have the appropriate driver installed. The file contains a driver name (or another data source name in the case of an unshareable file data source) and optionally, a connection string that can be used by SQLDriverConnect. The Driver Manager builds the connection string for the call to SQLDriverConnect from the keywords in the .dsn file.

A file data source allows an application to specify connection options without having to build a connection string for use with SQLDriverConnect. The file data source usually is created by specifying the SAVEFILE keyword, which causes the Driver Manager to save the output connection string created by a call to SQLDriverConnect to the .dsn file. That connection string can be used repeatedly by calling SQLDriverConnect with the FILEDSN keyword. This streamlines the connection process and provides a persistent source of the connection string.

File data sources also can be created by calling SQLCreateDataSource in the installer DLL. Information can be written into the .dsn file by calling SQLWriteFileDSN, and read from the .dsn file by calling SQLReadFileDSN; both of these functions are also in the installer DLL. For information about the installer DLL, see Chapter 19: Configuring Data Sources.

The keywords used for connection information are in the [ODBC] section of a .dsn file. The minimum information that a shareable .dsn file would have in the [ODBC] section is the DRIVER keyword:

DRIVER = SQL Server

The shareable .dsn file usually contains a connection string, as follows:

DRIVER = SQL Server
UID = Larry
DATABASE = MyDB

When the file data source is unshareable, the .dsn file contains only a DSN keyword. When the Driver Manager is sent the information in an unshareable file data source, it connects as necessary to the data source indicated by the DSN keyword. An unshareable .dsn file would contain the following keyword:

DSN = MyDataSource

The connection string used for a file data source is the union of the keywords specified in the .dsn file and the keywords specified in the connection string in the call to SQLDriverConnect. If any of the keywords in the .dsn file conflict with keywords in the connection string, the Driver Manager decides which keyword value should be used. For more information, see SQLDriverConnect.


Connecting Directly to Drivers

As was discussed in "Choosing a Data Source or Driver," earlier in this chapter, some applications do not want to use a data source at all. Instead, they want to connect directly to a driver. SQLDriverConnect provides a way for the application to connect directly to a driver without specifying a data source. Conceptually, a temporary data source is created at run time.

To connect directly to a driver, the application specifies the DRIVER keyword in the connection string instead of the DSN keyword. The value of the DRIVER keyword is the description of the driver as returned by SQLDrivers. For example, suppose a driver has the description Paradox Driver and requires the name of a directory containing the data files. To connect to this driver, the application might use either of the following connection strings:

DRIVER={Paradox Driver};Directory=C:\PARADOX;
DRIVER={Paradox Driver};

With the first string, the driver would not need any additional information. With the second string, the driver would need to prompt for the name of the directory containing the data files.


Connecting with SQLBrowseConnect

SQLBrowseConnect, like SQLDriverConnect, uses a connection string. However, by using SQLBrowseConnect, an application can construct a complete connection string at run time. This allows the application to do two things:

  • Build its own dialog boxes to prompt for this information, thereby retaining control over its "look and feel."
  • Browse the system for data sources that can be used by a particular driver, possibly in several steps. For example, the user might first browse the network for servers and, after choosing a server, browse the server for databases accessible by the driver.

The application calls SQLBrowseConnect and passes a connection string, known as the browse request connection string, that specifies a driver or data source. The driver returns a connection string, known as the browse result connection string, that contains keywords, possible values (if the keyword accepts a discrete set of values), and user-friendly names. The application builds a dialog box with the user-friendly names and prompts the user for values. It then builds a new browse request connection string from these values and returns this to the driver with another call to SQLBrowseConnect.

Because connection strings are passed back and forth, the driver can provide several levels of browsing by returning a new connection string when the application returns the old one. For example, the first time an application calls SQLBrowseConnect, the driver might return keywords to prompt the user for a server name. When the application returns the server name, the driver might return keywords to prompt the user for a database. The browsing process would be complete after the application returned the database name.

Each time SQLBrowseConnect returns a new browse result connection string, it returns SQL_NEED_DATA as its return code. This tells the application that the connection process is not complete. Until SQLBrowseConnect returns SQL_SUCCESS, the connection is in a Need Data state and cannot be used for other purposes, such as to set a connection attribute. The application can terminate the connection browsing process by calling SQLDisconnect.


SQL Server Browsing Example

The following example shows how SQLBrowseConnect might be used to browse the connections available with a driver for SQL Server. First, the application requests a connection handle:

SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);

Next, the application calls SQLBrowseConnect and specifies the SQL Server driver, using the driver description returned by SQLDrivers:

SQLBrowseConnect(hdbc, "DRIVER={SQL Server};", SQL_NTS, BrowseResult,
                  sizeof(BrowseResult), &BrowseResultLen);

Because this is the first call to SQLBrowseConnect, the Driver Manager loads the SQL Server driver and calls the driver's SQLBrowseConnect function with the same arguments it received from the application.

The driver determines that this is the first call to SQLBrowseConnect and returns the second level of connection attributes: server, user name, password, application name, and workstation ID. For the server attribute, it returns a list of valid server names. The return code from SQLBrowseConnect is SQL_NEED_DATA. Here is the browse result string:

"SERVER:Server={red,blue,green,yellow};UID:Login ID=?;PWD:Password=?;
   *APP:AppName=?;*WSID:WorkStation ID=?;"

Each keyword in the browse result string is followed by a colon and one or more words before the equal sign. These words are the user-friendly name that an application can use to build a dialog box. The APP and WSID keywords are prefixed by an asterisk, which means they are optional. The SERVER, UID, and PWD keywords are not prefixed by an asterisk; values must be supplied for them in the next browse request string. The value for the SERVER keyword may be one of the servers returned by SQLBrowseConnect or a user-supplied name.

The application calls SQLBrowseConnect again, specifying the green server and omitting the APP and WSID keywords and the user-friendly names after each keyword:

SQLBrowseConnect(hdbc, "SERVER=green;UID=Smith;PWD=Sesame;", SQL_NTS,
                  BrowseResult, sizeof(BrowseResult), &BrowseResultLen);

The driver attempts to connect to the green server. If there are any nonfatal errors, such as a missing keyword-value pair, SQLBrowseConnect returns SQL_NEED_DATA and remains in the same state as it was prior to the error. The application can call SQLGetDiagField or SQLGetDiagRec to determine the error. If the connection is successful, the driver returns SQL_NEED_DATA and returns the browse result string:

"*DATABASE:Database={master,model,pubs,tempdb};
   *LANGUAGE:Language={us_english,Français};"

Because the attributes in this string are optional, the application can omit them. However, the application must call SQLBrowseConnect again. If the application chooses to omit the database name and language, it specifies an empty browse request string. In this example, the application chooses the pubs database and calls SQLBrowseConnect a final time, omitting the LANGUAGE keyword and the asterisk before the DATABASE keyword:

SQLBrowseConnect(hdbc, "DATABASE=pubs;", SQL_NTS, BrowseResult,
                  sizeof(BrowseResult), &BrowseResultLen);

Because the DATABASE attribute is the final connection attribute required by the driver, the browsing process is complete, the application is connected to the data source, and SQLBrowseConnect returns SQL_SUCCESS. SQLBrowseConnect also returns the complete connection string as the browse result string:

"DSN=MySQLServer;SERVER=green;UID=Smith;PWD=Sesame;DATABASE=pubs;"

The final connection string returned by the driver does not contain the user-friendly names after each keyword, nor does it contain optional keywords not specified by the application. The application can use this string with SQLDriverConnect to reconnect to the data source on the current connection handle (after disconnecting) or to connect to the data source on a different connection handle. For example:

SQLDriverConnect(hdbc, hwnd, BrowseResult, SQL_NTS, ConnStrOut,
                  sizeof(ConnStrOut), &ConnStrOutLen, SQL_DRIVER_NOPROMPT);


Connection Pooling

ODBC Programmer's Reference

ODBC Connection Pooling

Connection pooling enables an application to use a connection from a pool of connections that do not need to be reestablished for each use. Once a connection has been created and placed in a pool, an application can reuse that connection without performing the complete connection process.

Using a pooled connection can result in significant performance gains, because applications can save the overhead involved in making a connection. This can be particularly significant for middle-tier applications that connect over a network or for applications that repeatedly connect and disconnect, such as Internet applications.

In addition to performance gains, the connection pooling architecture enables an environment and its associated connections to be used by multiple components in a single process. This means that stand-alone components in the same process can interact with each other without being aware of each other. A connection in a connection pool can be used repeatedly by multiple components.

Note Connection pooling can be used by an ODBC application exhibiting ODBC 2.x behavior, as long as the application can call SQLSetEnvAttr. When using connection pooling, the application must not execute SQL statements that change the database or the context of the database, such as changing the <database name>, which changes the catalog used by a data source.

An ODBC driver must be fully thread-safe, and connections must not have thread affinity to support connection pooling. This means the driver is able to handle a call on any thread at any time and is able to connect on one thread, to use the connection on another thread, and to disconnect on a third thread.

The connection pool is maintained by the Driver Manager. Connections are drawn from the pool when the application calls SQLConnect or SQLDriverConnect and are returned to the pool when the application calls SQLDisconnect. The size of the pool grows dynamically, based on the requested resource allocations. It shrinks based on the inactivity timeout: If a connection is inactive for a period of time (it has not been used in a connection), it is removed from the pool. The size of the pool is limited only by memory constraints and limits on the server.

The Driver Manager determines whether a specific connection in a pool should be used according to the arguments passed in SQLConnect or SQLDriverConnect, and according to the connection attributes set after the connection was allocated.

When the Driver Manager is pooling connections, it needs to be able to determine if a connection is still working before handing out the connection. Otherwise, the Driver Manager keeps on handing out the dead connection to the application whenever a transient network failure occurs. A new connection attribute has been defined in ODBC 3.x: SQL_ATTR_CONNECTION_DEAD. This is a read-only connection attribute that returns either SQL_CD_TRUE or SQL_CD_FALSE. The value SQL_CD_TRUE means that the connection has been lost, while the value SQL_CD_FALSE means that the connection is still active. (Drivers conforming to earlier versions of ODBC can also support this attribute.)

A driver must implement this option efficiently or it will impair the connection pooling performance. Specifically, a call to get this connection attribute should not cause a round trip to the server. Instead, a driver should just return the last known state of the connection. The connection is dead if the last trip to the server failed, and not dead if the last trip succeeded.

In order to prevent unwanted repeated attempts by the Driver Manager to reestablish a connection when connection pooling is enabled, you can set ODBCGetTryWaitValue. ODBCSetTryWaitValue saves the information in the registry at the following location:

HKEY_LOCAL_MACHINE\Software\Odbc\Odbcinst.ini\ODBC Connection Pooling\Retry Wait

If there is a bad connection in the pool, the ODBC Driver Manager will attempt to connect before the connection can be reused for the first time. If the connection still fails, the ODBC Driver Manager returns the error and marks the connection with the time. From that point until the RetryWait value expires, the ODBC Driver Manager returns a failure without testing the connection.

To use a connection pool, an application performs the following steps:

  1. Enables connection pooling by calling SQLSetEnvAttr to set the SQL_ATTR_CONNECTION_POOLING environment attribute to SQL_CP_ONE_PER_DRIVER or SQL_CP_ONE_PER_HENV. This call must be made before the application allocates the shared environment for which connection pooling is to be enabled. The environment handle in the call to SQLSetEnvAttr should be set to null, which makes SQL_ATTR_CONNECTION_POOLING a process-level attribute. If the attribute is set to SQL_CP_ONE_PER_DRIVER, a single connection pool is supported for each driver. If an application works with many drivers and few environments, this might be more efficient because fewer comparisons may be required. If set to SQL_CP_ONE_PER_HENV, a single connection pool is supported for each environment. If an application works with many environments and few drivers, this might be more efficient because fewer comparisons may be required. Connection pooling is disabled by setting SQL_ATTR_CONNECTION_POOLING to SQL_CP_OFF.
  2. Allocates an environment by calling SQLAllocHandle with the HandleType argument set to SQL_HANDLE_ENV. The environment allocated by this call will be an implicit shared environment because connection pooling has been enabled. The environment to be used is not determined, however, until SQLAllocHandle with a HandleType of SQL_HANDLE_DBC is called on this environment.
  3. Allocates a connection by calling SQLAllocHandle with InputHandle set to SQL_HANDLE_DBC, and the InputHandle set to the environment handle allocated for connection pooling. The Driver Manager attempts to find an existing environment that matches the environment attributes set by the application. If no such environment exists, one is created, with a reference count (maintained by the Driver Manager) of 1. If a matching shared environment is found, the environment is returned to the application and its reference count is incremented. (The actual connection to be used is not determined by the Driver Manager until SQLConnect or SQLDriverConnect is called.)
  4. Calls SQLConnect or SQLDriverConnect to make the connection. The Driver Manager uses the connection options in the call to SQLConnect (or the connection keywords in the call to SQLDriverConnect) and the connection attributes set after connection allocation to determine which connection in the pool should be used.

    Note How a requested connection is matched to a pooled connection is determined by the SQL_ATTR_CP_MATCH environment attribute. For more information, see SQLSetEnvAttr.

  5. Calls SQLDisconnect when done with the connection. The connection is returned to the connection pool and becomes available for reuse.

    Note For more information about connection pooling, see "Connection Pooling Considerations" in Chapter 17: Programming Considerations" or for an in-depth discussion, see the technical article "Pooling in the Microsoft Data Access Components."


Disconnecting from a Data Source or Driver

When an application has finished using a data source, it calls SQLDisconnect. SQLDisconnect frees any statements that are allocated on the connection and disconnects the driver from the data source. It returns an error if a transaction is in process.

After disconnecting, the application can call SQLFreeHandle to free the connection. After freeing the connection, it is an application programming error to use the connection's handle in a call to an ODBC function; doing so has undefined but probably fatal consequences. When SQLFreeHandle is called, the driver releases the structure used to store information about the connection.

The application also can reuse the connection, either to connect to a different data source or reconnect to the same data source. The decision to remain connected, as opposed to disconnecting and reconnecting later, requires that the application writer consider the relative costs of each option; both connecting to a data source and remaining connected can be relatively costly depending on the connection medium. In making a correct tradeoff, the application must also make assumptions about the likelihood and timing of further operations on the same data source.


Driver Manager's Role in the Connection Process

ODBC Programmer's Reference

The Driver Manager's Role in the Connection Process

Remember that applications do not call driver functions directly. Instead, they call Driver Manager functions with the same name and the Driver Manager calls the driver functions. Usually, this happens almost immediately. For example, the application calls SQLExecute in the Driver Manager and after a few error checks, the Driver Manager calls SQLExecute in the driver.

The connection process is different. When the application calls SQLAllocHandle with the SQL_HANDLE_ENV and SQL_HANDLE_DBC options, the function allocates handles only in the Driver Manager. The Driver Manager does not call this function in the driver because it does not know which driver to call. Similarly, if the application passes the handle of an unconnected connection to SQLSetConnectAttr or SQLGetConnectAttr, only the Driver Manager executes the function. It stores or gets the attribute value from its connection handle and returns SQLSTATE 08003 (Connection not open) when getting a value for an attribute that has not been set and for which ODBC does not define a default value.

When the application calls SQLConnect, SQLDriverConnect, or SQLBrowseConnect, the Driver Manager first determines which driver to use. It then checks to determine whether a driver is currently loaded on the connection:

  • If no driver is loaded on the connection, the Driver Manager checks whether the specified driver is loaded on another connection in the same environment. If not, the Driver Manager loads the driver on the connection and calls SQLAllocHandle in the driver with the SQL_HANDLE_ENV option.

    The Driver Manager then calls SQLAllocHandle in the driver with the SQL_HANDLE_DBC option, whether or not it was just loaded. If the application set any connection attributes, the Driver Manager calls SQLSetConnectAttr in the driver; if an error occurs, the Driver Manager's connection function returns SQLSTATE IM006 (Driver's SQLSetConnectAttr failed). Finally, the Driver Manager calls the connection function in the driver.

  • If the specified driver is loaded on the connection, the Driver Manager calls only the connection function in the driver. In this case, the driver must make sure that all connection attributes on the connection maintain their current settings.
  • If a different driver is loaded on the connection, the Driver Manager calls SQLFreeHandle in the driver to free the connection. If there are no other connections that use the driver, the Driver Manager calls SQLFreeHandle in the driver to free the environment and unloads the driver. The Driver Manager then performs the same operations as when a driver is not loaded on the connection.

When the application calls SQLDisconnect, the Driver Manager calls SQLDisconnect in the driver. However, it leaves the driver loaded in case the application reconnects to the driver. When the application calls SQLFreeHandle with the SQL_HANDLE_DBC option, the Driver Manager calls SQLFreeHandle in the driver. If the driver is not used by any other connections, the Driver Manager then calls SQLFreeHandle in the driver with the SQL_HANDLE_ENV option and unloads the driver.


Chapter 7: Catalog Functions

All databases have a structure that outlines how data will be stored in the database. For example, a simple sales order database might have the structure shown in the following illustration, in which the ID columns are used to link the tables.

This structure, along with other information such as privileges, is stored in a set of system tables called the database's catalog, which is also known as a data dictionary.

An application can discover this structure through calls to the catalog functions. The catalog functions return information in result sets and are usually implemented through SELECT statements against the tables in the catalog. For example, an application might request a result set containing information about all the tables on the system or all the columns in a particular table.


Uses of Catalog Data

Applications use catalog data in a variety of ways. Here are some common uses:

  • Constructing SQL statements at run time. Vertical applications, such as an order entry application, contain hard-coded SQL statements. The tables and columns that are used by the application are fixed ahead of time, as are the statements that access these tables. For example, an order entry application usually contains a single, parameterized INSERT statement for adding new orders to the system.

    Generic applications, such as a spreadsheet program that uses ODBC to retrieve data, often construct SQL statements at run time based on input from the user. Such an application could require the user to type the names of the tables and columns to use. However, it would be easier for the user if the application displayed lists of tables and columns from which the user could make selections. To build these lists, the application would call the SQLTables and SQLColumns catalog functions.

  • Constructing SQL statements during development. Application development environments typically allow the programmer to create database queries while developing a program. The queries are then hard-coded in the application being built.

    Such environments could also use SQLTables and SQLColumns to create lists from which the programmer could make selections. These environments might also use SQLPrimaryKeys and SQLForeignKeys to automatically determine and show relationships between selected tables, and use SQLStatistics to determine and highlight indexed fields so the programmer can create efficient queries.

  • Constructing cursors. An application, driver, or middleware that provides a scrollable cursor engine could use SQLSpecialColumns to determine which column or columns uniquely identify a row. The program could build a keyset containing the values of these columns for each row that has been fetched. When the application scrolls back to the row, it would then use these values to fetch the most recent data for the row. For more information about scrollable cursors and keysets, see "Scrollable Cursors" in Chapter 11: Retrieving Results (Advanced).


Catalog Functions in ODBC

ODBC contains the following catalog functions:

Function | Description
SQLTables | Returns a list of catalogs, schemas, tables, or table types in the data source.
SQLColumns | Returns a list of columns in one or more tables.
SQLStatistics | Returns a list of statistics about a single table. Also returns a list of indexes associated with that table.
SQLSpecialColumns | Returns a list of columns that uniquely identifies a row in a single table. Also returns a list of columns in that table that are automatically updated.
SQLPrimaryKeys | Returns a list of columns that compose the primary key of a single table.
SQLForeignKeys | Returns a list of foreign keys in a single table or a list of foreign keys in other tables that refer to a single table.
SQLTablePrivileges | Returns a list of privileges associated with one or more tables.
SQLColumnPrivileges | Returns a list of privileges associated with one or more columns in a single table.
SQLProcedures | Returns a list of procedures in the data source.
SQLProcedureColumns | Returns a list of input and output parameters, the return value, and the columns in the result set of a single procedure.
SQLGetTypeInfo | Returns a list of the SQL data types supported by the data source. These data types are generally used in CREATE TABLE and ALTER TABLE statements.

Because SQLTables, SQLColumns, SQLStatistics, and SQLSpecialColumns conform to the X/Open CLI, and SQLGetTypeInfo conforms to the ISO 92 CLI, they are implemented by most drivers. The remaining catalog functions are in the ODBC conformance level.


Data Returned by Catalog Functions

Each catalog function returns data as a result set. This result set is no different from any other result set. It is usually generated by a predefined, parameterized SELECT statement that is hard-coded in the driver or stored in a procedure in the data source. For information on how to retrieve data from a result set, see "Was a Result Set Created?" in Chapter 10, "Retrieving Results (Basic)."

The result set for each catalog function is described in the reference entry for that function. In addition to the listed columns, the result set can contain driver-specific columns after the last predefined column. These columns (if any) are described in the driver documentation.

Applications should bind driver-specific columns relative to the end of the result set. That is, they should calculate the number of a driver-specific column as the number of the last column—retrieved with SQLNumResultCols—less the number of columns that occur after the required column. This saves having to change the application when new columns are added to the result set in future versions of ODBC or the driver. For this scheme to work, drivers must add new driver-specific columns before old driver-specific columns so that column numbers do not change relative to the end of the result set.

Identifiers that are returned in the result set are not quoted, even if they contain special characters. For example, suppose the identifier quote character (which is driver-specific and returned through SQLGetInfo) is a double quotation mark (") and the Accounts Payable table contains a column named Customer Name. In the row returned by SQLColumns for this column, the value of the TABLE_NAME column is Accounts Payable, not "Accounts Payable", and the value of the COLUMN_NAME column is Customer Name, not "Customer Name". To retrieve the names of customers in the Accounts Payable table, the application would quote these names:

SELECT "Customer Name" FROM "Accounts Payable"

For more information, see "Quoted Identifiers" in Chapter 8, "SQL Statements."

The catalog functions are based on an SQL-like authorization model in which a connection is made based on a user name and password, and only data for which the user has a privilege is returned. Password protection of individual files, which does not fit into this model, is driver-defined.

The result sets returned by the catalog functions are almost never updatable, and applications should not expect to be able to change the structure of the database by changing the data in these result sets.


Arguments in Catalog Functions

All catalog functions accept arguments with which an application can restrict the scope of the data returned. For example, the first and second calls to SQLTables in the following code return a result set containing information about all tables, while the third call returns information about the Orders table:

SQLTables(hstmt1, NULL, 0, NULL, 0, NULL, 0, NULL, 0);
SQLTables(hstmt2, NULL, 0, NULL, 0, "%", SQL_NTS, NULL, 0);
SQLTables(hstmt3, NULL, 0, NULL, 0, "Orders", SQL_NTS, NULL, 0);

Catalog function string arguments fall into four different types: ordinary argument (OA), pattern value argument (PV), identifier argument (ID), and value list argument (VL). Most string arguments can be of one of two different types, depending on the value of the SQL_ATTR_METADATA_ID statement attribute. The following table lists the arguments for each catalog function and describes the type of the argument for an SQL_TRUE or SQL_FALSE value of SQL_ATTR_METADATA_ID.

Function | Argument | Type when SQL_ ATTR_METADATA_ ID = SQL_FALSE | Type when SQL_ ATTR_METADATA_ ID = SQL_TRUE
SQLColumnPrivileges | CatalogName SchemaName TableName ColumnName | OA OA OA PV | ID ID ID ID
SQLColumns | CatalogName SchemaName TableName ColumnName | OA PV PV PV | ID ID ID ID
SQLForeignKeys | PKCatalogName PKSchemaName PKTableName FKCatalogName FKSchemaName FKTableName | OA OA OA OA OA OA | ID ID ID ID ID ID
SQLPrimaryKeys | CatalogName SchemaName TableName | OA OA OA | ID ID ID
SQLProcedureColumns | CatalogName SchemaName ProcName ColumnName | OA PV PV PV | ID ID ID ID
SQLProcedures | CatalogName SchemaName ProcName | OA PV PV | ID ID ID
SQLSpecialColumns | CatalogName SchemaName TableName | OA OA OA | ID ID ID
SQLStatistics | CatalogName SchemaName TableName | OA OA OA | ID ID ID
SQLTablePrivileges | CatalogName SchemaName TableName | OA PV PV | ID ID ID
SQLTables | CatalogName SchemaName TableName TableType | PV PV PV VL | ID ID ID VL


Ordinary Arguments

When a catalog function string argument is an ordinary argument, it is treated as a literal string. An ordinary argument accepts neither a string search pattern nor a list of values. The case of an ordinary argument is significant, and quote characters in the string are taken literally. These arguments are treated as ordinary arguments if the SQL_ATTR_METADATA_ID statement attribute is set to SQL_FALSE; they are treated as identifier arguments instead if this attribute is set to SQL_TRUE.

If an ordinary argument is set to a null pointer and the argument is a required argument, the function returns SQL_ERROR and SQLSTATE HY009 (Invalid use of null pointer). If an ordinary argument is set to a null pointer and the argument is not a required argument, the argument's behavior is driver-dependent. The required arguments are listed in the following table.

Function | Required arguments
SQLColumnPrivileges | TableName
SQLForeignKeys | PKTableName, FKTableName
SQLPrimaryKeys | TableName
SQLSpecialColumns | TableName
SQLStatistics | TableName


Pattern Value Arguments

Some arguments in the catalog functions, such as the TableName argument in SQLTables, accept search patterns. These arguments accept search patterns if the SQL_ATTR_METADATA_ID statement attribute is set to SQL_FALSE; they are identifier arguments that do not accept a search pattern if this attribute is set to SQL_TRUE.

The search pattern characters are:

  • An underscore (_), which represents any single character.
  • A percent sign (%), which represents any sequence of zero or more characters.
  • An escape character, which is driver-specific and is used to include underscores, percent signs, and the escape character as literals. If the escape character precedes a non-special character, the escape character has no special meaning. If the escape character precedes a special character, it escapes the special character. For example, "\a" would be treated as two characters, "\" and "a", but "\%" would be treated as the non-special single character "%".

The escape character is retrieved with the SQL_SEARCH_PATTERN_ESCAPE option in SQLGetInfo. It must precede any underscore, percent sign, or escape character in an argument that accepts search patterns to include that character as a literal. Examples are shown in the following table.

Search pattern | Description
%A% | All identifiers containing the letter A
ABC_ | All four character identifiers starting with ABC
ABC\_ | The identifier ABC_, assuming the escape character is a backslash (\)
\\% | All identifiers starting with a backslash (\), assuming the escape character is a backslash

Special care must be taken to escape search pattern characters in arguments that accept search patterns. This is particularly true for the underscore character, which is commonly used in identifiers. A common mistake in applications is to retrieve a value from one catalog function and pass that value to a search pattern argument in another catalog function. For example, suppose an application retrieves the table name MY_TABLE from the result set for SQLTables and passes this to SQLColumns to retrieve a list of columns in MY_TABLE. Instead of getting the columns for MY_TABLE, the application will get the columns for all the tables that match the search pattern MY_TABLE, such as MY_TABLE, MY1TABLE, MY2TABLE, and so on.

Note ODBC 2.x drivers do not support search patterns in the CatalogName argument in SQLTables. ODBC 3.x drivers accept search patterns in this argument if the SQL_ATTR_ ODBC_VERSION environment attribute is set to SQL_OV_ODBC3; they do not accept search patterns in this argument if it is set to SQL_OV_ODBC2.

Passing a null pointer to a search pattern argument does not constrain the search for that argument; that is, a null pointer and the search pattern % (any characters) are equivalent. However, a zero-length search pattern—that is, a valid pointer to a string of length zero—matches only the empty string ("").


Identifier Arguments

If a string in an identifier argument is quoted, the driver removes leading and trailing blanks and treats literally the string within the quotation marks. If the string is not quoted, the driver removes trailing blanks and folds the string to uppercase. Setting an identifier argument to a null pointer returns SQL_ERROR and SQLSTATE HY009 (Invalid use of null pointer), unless the argument is a catalog name and catalogs are not supported.

These arguments are treated as identifier arguments if the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE. In this case, the underscore (_) and the percent sign (%) will be treated as the actual character, not as a search pattern character. These arguments are treated as either an ordinary argument or a pattern argument, depending on the argument, if this attribute is set to SQL_FALSE.

Although identifiers containing special characters must be quoted in SQL statements, they must not be quoted when passed as catalog function arguments, because quote characters passed to catalog functions are interpreted literally. For example, suppose the identifier quote character (which is driver-specific and returned through SQLGetInfo) is a double quotation mark ("). The first call to SQLTables returns a result set containing information about the Accounts Payable table, while the second call returns information about the "Accounts Payable" table, which is probably not what was intended.

SQLTables(hstmt1, NULL, 0, NULL, 0, "Accounts Payable", SQL_NTS, NULL, 0);
SQLTables(hstmt2, NULL, 0, NULL, 0, "\"Accounts Payable\"", SQL_NTS, NULL, 0);

Quoted identifiers are used to distinguish a true column name from a pseudo-column of the same name, such as ROWID in Oracle. If "ROWID" is passed in an argument of a catalog function, the function will work with the ROWID pseudo-column if it exists. If the pseudo-column does not exist, the function will work with the "ROWID" column. If ROWID is passed in an argument of a catalog function, the function will work with the ROWID column.

For more information about quoted identifiers, see "Quoted Identifiers" in Chapter 8, "SQL Statements."


Value List Arguments

A value list argument consists of a list of comma-separated values to be used for matching. There is only one value list argument in the ODBC catalog functions: the TableType argument in SQLTables. Setting TableType to a null pointer is the same as if it is set to SQL_ALL_TABLE_TYPES, which enumerates all possible members of the value list. This argument is not affected by the SQL_ATTR_METADATA_ID statement attribute. For more information, see the SQLTables function description.


Schema Views

An application can retrieve metadata information from the DBMS either by calling ODBC catalog functions or by using INFORMATION_SCHEMA views. The views are defined by the ANSI SQL-92 standard.

If supported by the DBMS and the driver, the INFORMATION_SCHEMA views provide a more powerful and comprehensive means of retrieving metadata than the ODBC catalog functions provide. An application can execute its own custom SELECT statement against one of these views, can join views, or can perform a union on views. While offering greater utility and a wider range of metadata, INFORMATION_SCHEMA views are not often supported by the DBMS. This might change as more DBMSs and drivers achieve compliance with SQL-92.

To determine which views are supported, an application calls SQLGetInfo with the SQL_INFO_SCHEMA_VIEWS option. To retrieve metadata from a supported view, the application executes a SELECT statement that specifies the schema information required.


Chapter 8: SQL Statements

ODBC applications perform almost all database access by executing SQL statements. The form of these statements—hard-coded or constructed at run time, interoperable or data source–specific, and so on—depends on the needs of the application.


Constructing SQL Statements

SQL statements can be constructed in one of three ways: hard-coded during development, constructed at run time, or entered directly by the user.


Hard-Coded SQL Statements

Applications that perform a fixed task usually contain hard-coded SQL statements. For example, an order entry system might use the following call to list open sales orders:

SQLExecDirect(hstmt, "SELECT OrderID FROM Orders WHERE Status = 'OPEN'", SQL_NTS);

There are several advantages to hard-coded SQL statements: They can be tested when the application is written; they are simpler to implement than statements constructed at run time; and they simplify the application.

Using statement parameters and preparing statements provide even better ways to use hard-coded SQL statements. For example, suppose the Parts table contains the PartID, Description, and Price columns. One way to insert a new row into this table would be to construct and execute an INSERT statement:

#define DESC_LEN 51
#define STATEMENT_LEN 51

SQLUINTEGER   PartID;
SQLCHAR       Desc[DESC_LEN], Statement[STATEMENT_LEN];
SQLREAL       Price;

// Set part ID, description, and price.
GetNewValues(&PartID, Desc, &Price);

// Build INSERT statement.
sprintf(Statement, "INSERT INTO Parts (PartID, Description,  Price) "
         "VALUES (%d, '%s', %f)", PartID, Desc, Price);

// Execute the statement.
SQLExecDirect(hstmt, Statement, SQL_NTS);

An even better way is to use a hard-coded, parameterized statement. This has two advantages over a statement with hard-coded data values. First, it is easier to construct a parameterized statement because the data values can be sent in their native types, such as integers and floating-point numbers, rather than converting them to strings. Second, such a statement can be used more than once simply by changing the parameter values and reexecuting it; there is no need to rebuild it.

#define DESC_LEN 51

SQLCHAR * Statement = "INSERT INTO Parts (PartID, Description,  Price) "
         "VALUES (?, ?, ?)";
SQLUINTEGER   PartID;
SQLCHAR       Desc[DESC_LEN];
SQLREAL       Price;
SQLINTEGER    PartIDInd = 0, DescLenOrInd = SQL_NTS, PriceInd = 0;

// Bind the parameters.
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 5, 0,
                  &PartID, 0, &PartIDInd);
SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, DESC_LEN - 1, 0,
                  Desc, sizeof(Desc), &DescLenOrInd);
SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_REAL, 7, 0,
                  &Price, 0, &PriceInd);

// Set part ID, description, and price.
GetNewValues(&PartID, Desc, &Price);

// Execute the statement.
SQLExecDirect(hstmt, Statement, SQL_NTS);

Assuming this statement is to be executed more than once, it can be prepared for even greater efficiency:

#define DESC_LEN 51

SQLCHAR *Statement = "INSERT INTO Parts (PartID, Description,  Price) "
         "VALUES (?, ?, ?)";
SQLUINTEGER   PartID;
SQLCHAR       Desc[DESC_LEN];
SQLREAL       Price;
SQLINTEGER    PartIDInd = 0, DescLenOrInd = SQL_NTS, PriceInd = 0;

// Prepare the INSERT statement.
SQLPrepare(hstmt, Statement, SQL_NTS);

// Bind the parameters.
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 5, 0,
                  &PartID, 0, &PartIDInd);
SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, DESC_LEN - 1, 0,
                  Desc, sizeof(Desc), &DescLenOrInd);
SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_REAL, 7, 0,
                  &Price, 0, &PriceInd);

// Loop to continually get new values and insert them.
while (GetNewValues(&PartID, Desc, &Price))
   SQLExecute(hstmt);

Perhaps the most efficient way to use the statement is to construct a procedure containing the statement, as shown in the following code example. Because the procedure is constructed at development time and stored on the data source, it does not need to be prepared at run time. A drawback of this method is that the syntax for creating procedures is DBMS-specific and procedures must be constructed separately for each DBMS on which the application is to run.

#define DESC_LEN 51

SQLUINTEGER   PartID;
SQLCHAR       Desc[DESC_LEN];
SQLREAL       Price;
SQLINTEGER    PartIDInd = 0, DescLenOrInd = SQL_NTS, PriceInd = 0;

// Bind the parameters.
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 5, 0,
                  &PartID, 0, &PartIDInd);
SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, DESC_LEN - 1, 0,
                  Desc, sizeof(Desc), &DescLenOrInd);
SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_REAL, 7, 0,
                  &Price, 0, &PriceInd);

// Loop to continually get new values and insert them.
while (GetNewValues(&PartID, Desc, &Price))
   SQLExecDirect(hstmt, "{call InsertPart(?, ?, ?)}", SQL_NTS);

For more information about parameters, prepared statements, and procedures, see "Executing a Statement" in Chapter 9: Executing Statements.


SQL Statements Constructed at Run Time

Applications that perform ad hoc analysis commonly build SQL statements at run time. For example, a spreadsheet might allow a user to select columns from which to retrieve data:

SQLCHAR *     Statement, *TableName;
SQLCHAR **    TableNamesArray, **ColumnNamesArray;
BOOL *        ColumnSelectedArray;
BOOL          CommaNeeded;
SQLSMALLINT   i, NumColumns;

// Use SQLTables to build a list of tables (TableNamesArray[]). Let the
// user select a table and store the selected table in TableName.

// Use SQLColumns to build a list of the columns in the selected table
// (ColumnNamesArray). Set NumColumns to the number of columns in the
// table. Let the user select one or more columns and flag these columns
// in ColumnSelectedArray[].

// Build a SELECT statement from the selected columns.
CommaNeeded = FALSE;
strcpy(Statement, "SELECT ");
for (i = 0; i = NumColumns; i++) {
   if (ColumnSelectedArray[i]) {
      if (CommaNeeded)
            strcat(Statement, ",");
      else
            CommaNeeded = TRUE;
      strcat(Statement, ColumnNamesArray[i]);
   }
}

strcat(Statement, " FROM ");
strcat(Statement, TableName);

// Execute the statement directly. Because it will be executed only once,
// do not prepare it.
SQLExecDirect(hstmt, Statement, SQL_NTS);

Another class of applications that commonly constructs SQL statements at run time are application development environments. However, the statements they construct are hard-coded in the application they are building, where they can usually be optimized and tested.

Applications that construct SQL statements at run time can provide tremendous flexibility to the user. As can be seen from the preceding example, which did not even support such common operations as WHERE clauses, ORDER BY clauses, or joins, constructing SQL statements at run time is vastly more complex than hard-coding statements. Furthermore, testing such applications is problematic because they can construct an arbitrary number of SQL statements.

A potential disadvantage of constructing SQL statements at run time is that it takes far more time to construct a statement than use a hard-coded statement. Fortunately, this is rarely a concern. Such applications tend to be user-interface intensive, and the time the application spends constructing SQL statements is generally small compared to the time the user spends entering criteria.


SQL Statements Entered by the User

Applications that perform ad hoc analysis also commonly allow the user to enter SQL statements directly. For example:

SQLCHAR *     Statement, SqlState[6], Msg[SQL_MAX_MESSAGE_LENGTH];
SQLSMALLINT   i, MsgLen;
SQLINTEGER    NativeError;
SQLRETURN     rc1, rc2;

// Prompt user for SQL statement.
GetSQLStatement(Statement);

// Execute the statement directly. Because it will be executed only once,
// do not prepare it.
rc1 = SQLExecDirect(hstmt, Statement, SQL_NTS);

// Process any errors or returned information.
if ((rc1 == SQL_ERROR) || rc1 == SQL_SUCCESS_WITH_INFO) {
   i = 1;
   while ((rc2 = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError,
         Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA) {
      DisplayError(SqlState, NativeError, Msg, MsgLen);
      i++;
   }
}

This approach simplifies application coding; the application relies on the user to build the SQL statement and on the data source to check the statement's validity. Because it's difficult to write a graphical user interface that adequately exposes the intricacies of SQL, simply asking the user to enter the SQL statement text may be a preferable alternative. However, this requires the user to know not only SQL but also the schema of the data source being queried. Some applications provide a graphical user interface by which the user can create a basic SQL statement and also provide a text interface with which the user can modify it.


Interoperability of SQL Statements

Like the rest of an application, SQL statements can be interoperable or DBMS-specific. And like the rest of the application, the choice of how interoperable SQL statements need to be depends on the type of application. Custom applications are less likely to use interoperable SQL statements because they are usually designed to exploit the capabilities of one or possibly two DBMSs. Generic applications use interoperable SQL statements because they are designed to work with a variety of DBMSs. And vertical applications usually fall somewhere in between, demanding a certain level of functionality but otherwise using interoperable SQL statements.


Choosing an SQL Grammar

The first decision to make when constructing SQL statements is which grammar to use. In addition to the grammars available from the various standards bodies, such as X/Open, ANSI, and ISO, virtually every DBMS vendor defines its own grammar, each of which varies slightly from the standard.

Appendix C: SQL Grammar, describes the minimum SQL grammar that all ODBC drivers must support. This grammar is a subset of the Entry level of SQL-92. Drivers may support additional grammar to conform to the Intermediate, Full, or FIPS 127-2 Transitional levels defined by SQL-92. For more information, see "SQL Minimum Grammar" in Appendix C: SQL Grammar, and SQL-92.

Appendix C also defines escape sequences containing standard grammar for commonly available language features, such as outer joins, that are not covered by the SQL-92 grammar. For more information, see "ODBC Escape Sequences" Appendix C: SQL Grammar, and "Escape Sequences," later in this chapter.

The grammar that is chosen affects how the driver processes the statement. Drivers must modify SQL-92 SQL and the ODBC-defined escape sequences to DBMS-specific SQL. Because most SQL grammars are based on one or more of the various standards, most drivers do little or no work to meet this requirement. It often consists only of searching for the escape sequences defined by ODBC and replacing them with DBMS-specific grammar. When a driver encounters grammar it does not recognize, it assumes the grammar is DBMS-specific and passes the SQL statement without modification to the data source for execution.

Therefore, there are really two choices of grammar to use: the SQL-92 grammar (and the ODBC escape sequences) and a DBMS-specific grammar. Of the two, only the SQL-92 grammar is interoperable, so all interoperable applications should use it. Applications that are not interoperable can use the SQL-92 grammar or a DBMS-specific grammar. DBMS-specific grammars have two advantages: They can exploit any features not covered by SQL-92, and they are marginally faster because the driver does not have to modify them. The latter feature can be partially enforced by setting the SQL_ATTR_NOSCAN statement attribute, which stops the driver from searching for and replacing escape sequences.

If the SQL-92 grammar is used, the application can discover how it is modified by the driver by calling SQLNativeSql. This is often useful when debugging applications. SQLNativeSql accepts an SQL statement and returns it after the driver has modified it. Because this function is in the Core interface conformance level, it is supported by all drivers.


Constructing Interoperable SQL Statements

As mentioned in the previous sections, interoperable applications should use the ODBC SQL grammar. Beyond using this grammar, however, number of additional problems are faced by interoperable applications. For example, what does an application do if it wants to use a feature, such as outer joins, that is not supported by all data sources?

At this point, the application writer must make some decisions about which language features are required and which are optional. In most cases, if a particular driver does not support a feature required by the application, the application simply refuses to run with that driver. However, if the feature is optional, the application can work around the feature. For example, it might disable those parts of the interface that allow the user to use the feature.

To determine which features are supported, applications start by calling SQLGetInfo with the SQL_SQL_CONFORMANCE option. The SQL conformance level gives the application a broad view of which SQL is supported. To refine this view, the application calls SQLGetInfo with any of a number of other options. For a complete list of these options, see the SQLGetInfo function description. Finally, SQLGetTypeInfo returns information about the data types supported by the data source. The following sections list a number of possible factors that applications should watch for when constructing interoperable SQL statements.


Catalog and Schema Usage

Data sources do not necessarily support catalog and schema names as object name identifiers in all SQL statements. Data sources might support catalog and schema names in one or more of the following classes of SQL statements: Data Manipulation Language (DML) statements, procedure calls, table definition statements, index definition statements, and privilege definition statements. To determine the classes of SQL statements in which catalog and schema names can be used, an application calls SQLGetInfo with the SQL_CATALOG_USAGE and SQL_SCHEMA_USAGE options.


Catalog Position

The position of a catalog name in an identifier and how it is separated from the rest of the identifier varies from data source to data source. For example, in an Xbase data source, the catalog name is a directory and, in Microsoft® Windows®, is separated from the table name (which is a file name) by a backslash (\). The following illustration demonstrates this condition.

In an SQL Server data source, the catalog is a database and is separated from the schema and table names by a period (.).

In an Oracle data source, the catalog is also the database but follows the table name and is separated from the schema and table names by an at sign (@).

To determine the catalog separator and the location of the catalog name, an application calls SQLGetInfo with the SQL_CATALOG_NAME_SEPARATOR and SQL_CATALOG_LOCATION options. Interoperable applications should construct identifiers according to these values.

When quoting identifiers that contain more than one part, applications must be careful to quote each part separately and not quote the character that separates the identifiers. For example, the following statement to select all of the rows and columns of an Xbase table quotes the catalog (\XBASE\SALES\CORP) and table (Parts.dbf) names, but not the catalog separator (\):

SELECT * FROM "\XBASE\SALES\CORP"\"PARTS.DBF"

The following statement to select all of the rows and columns of an Oracle table quotes the catalog (Sales), schema (Corporate), and table (Parts) names, but not the catalog (@) or schema (.) separators:

SELECT * FROM "Corporate"."Parts"@"Sales"

For information about quoting identifiers, see the next section, "Quoted Identifiers."


Quoted Identifiers

In an SQL statement, identifiers containing special characters or match keywords must be enclosed in identifier quote characters; identifiers enclosed in such characters are known as quoted identifiers (also known as delimited identifiers in SQL-92). For example, the Accounts Payable identifier is quoted in the following SELECT statement:

SELECT * FROM "Accounts Payable"

The reason for quoting identifiers is to make the statement parseable. For example, if Accounts Payable was not quoted in the previous statement, the parser would assume there were two tables, Accounts and Payable, and return a syntax error that they were not separated by a comma. The identifier quote character is driver-specific and is retrieved with the SQL_IDENTIFIER_QUOTE_CHAR option in SQLGetInfo. The lists of special characters and of keywords are retrieved with the SQL_SPECIAL_CHARACTERS and SQL_KEYWORDS options in SQLGetInfo.

To be safe, interoperable applications often quote all identifiers except those for pseudo-columns, such as the ROWID column in Oracle. SQLSpecialColumns returns a list of pseudo-columns. Also, if there are application-specific restrictions on where special characters can appear in an object name, it is best for interoperable applications not to use special characters in those positions.


Identifier Case

In SQL statements and catalog function arguments, identifiers and quoted identifiers can be either case-sensitive or not, which an application can determine by calling SQLGetInfo with the SQL_IDENTIFIER_CASE and SQL_QUOTED_IDENTIFIER_CASE options.

Each of these options has four possible return values: one stating that the identifier or quoted identifier case is sensitive and three stating that it is not sensitive. The three values that are not case-sensitive further describe the case in which identifiers are stored in the system catalog. How identifiers are stored in the system catalog is relevant only for display purposes, such as when an application displays the results of a catalog function; it does not change the case-sensitivity of identifiers.


Escape Sequences

ODBC defines escape sequences containing standard grammar for date, time, timestamp, and datetime interval literals, scalar function calls, LIKE predicate escape characters, outer joins, and procedure calls. Interoperable applications should use these sequences whenever possible.

To determine if a driver supports the escape sequences for date, time, timestamp, or datetime interval literals, an application calls SQLGetTypeInfo. If the data source supports a date, time, timestamp, or datetime interval data type, it must also support the corresponding escape sequence. To determine whether the other escape sequences are supported, an application calls SQLGetInfo.

For more information, see "Escape Sequences in ODBC," later in this chapter.


Literal Prefixes and Suffixes

In an SQL statement, a literal is a character representation of an actual data value. For example, in the following statement, ABC, FFFF, and 10 are literals:

SELECT CharCol, BinaryCol, IntegerCol FROM MyTable
   WHERE CharCol = 'ABC' AND BinaryCol = 0xFFFF AND IntegerCol = 10

Literals for some data types require special prefixes and suffixes. In the preceding example, the character literal (ABC) requires a single quotation mark (') as both a prefix and a suffix, the binary literal (FFFF) requires the characters 0x as a prefix, and the integer literal (10) does not require a prefix or suffix.

For all data types except date, time, and timestamps, interoperable applications should use the values returned in the LITERAL_PREFIX and LITERAL_SUFFIX columns in the result set created by SQLGetTypeInfo. For date, time, timestamp, and datetime interval literals, interoperable applications should use the escape sequences discussed in the preceding section.


Parameter Markers in Procedure Calls

When calling procedures that accept parameters, interoperable applications should use parameter markers instead of literal parameter values. Some data sources do not support the use of literal parameter values in procedure calls. For more information about parameters, see "Statement Parameters" in Chapter 9: Executing Statements. For more information about calling procedures, see "Procedure Calls," later in this chapter.


DDL Statements

Data Definition Language (DDL) statements vary tremendously among DBMSs. ODBC SQL defines statements for the most common data definition operations: creating and dropping tables, indexes, and views; altering tables; and granting and revoking privileges. All other DDL statements are data source–specific. Therefore, interoperable applications cannot perform some data definition operations. In general, this is not a problem, because such operations tend to be highly DBMS-specific and are best left to the proprietary database administration software shipped with most DBMSs or the setup program shipped with the driver.

Another problem in data definition is that data type names vary tremendously among DBMSs. Rather than defining standard data type names and forcing drivers to convert them to DBMS-specific names, SQLGetTypeInfo provides a way for applications to discover DBMS-specific data type names. Interoperable applications should use these names in SQL statements to create and alter tables; the names listed in Appendix C: SQL Grammar, and Appendix D: Data Types, are examples only.


Escape Sequences in ODBC

A number of language features, such as outer joins and scalar function calls, are commonly implemented by DBMSs. However, the syntaxes for these features tend to be DBMS-specific, even when standard syntaxes are defined by the various standards bodies. Because of this, ODBC defines escape sequences that contain standard syntaxes for the following language features:

  • Date, time, timestamp, and datetime interval literals
  • Scalar functions such as numeric, string, and data type conversion functions
  • LIKE predicate escape character
  • Outer joins
  • Procedure calls

The escape sequence used by ODBC is as follows:

{extension}

The escape sequence is recognized and parsed by drivers, which replace the escape sequences with DBMS-specific grammar. For more information about escape sequence syntax, see "ODBC Escape Sequences" in Appendix C: SQL Grammar.

Note In ODBC 2.x, this was the standard syntax of the escape sequence:

--(*vendor(vendor-name), product(product-name) extension *)--
In addition to this syntax, a shorthand syntax was defined of the form:
{extension}
In ODBC 3.x, the long form of the escape sequence has been deprecated, and the shorthand form is used exclusively.

Because the escape sequences are mapped by the driver to DBMS-specific syntaxes, an application can use either the escape sequence or DBMS-specific syntax. However, applications that use the DBMS-specific syntax will not be interoperable. When using the escape sequence, applications should make sure that the SQL_ATTR_NOSCAN statement attribute is turned off, which it is by default. Otherwise, the escape sequence will be sent directly to the data source, where it will generally cause a syntax error.

Drivers support only those escape sequences that they can map to underlying language features. For example, if the data source does not support outer joins, neither will the driver. To determine which escape sequences are supported, an application calls SQLGetTypeInfo and SQLGetInfo. For more information, see the next section, "Date, Time, and Timestamp Literals."


Date, Time, and Timestamp Literals

The escape sequence for date, time, and timestamp literals is

{literal-type 'value'}

where literal-type is one of the values listed in the following table.

literal-type | Meaning | Format of value
d | Date | yyyy-mm-dd
t | Time* | hh:mm:ss[1]
ts | Timestamp | yyyy-mm-dd hh:mm:ss[.f...][1]

[1] The number of digits to the right of the decimal point in a time or timestamp interval literal containing a seconds component is dependent on the seconds precision, as contained in the SQL_DESC_PRECISION descriptor field. (For more information, see SQLSetDescField.)

For more information about the date, time, and timestamp escape sequences, see "Date, Time, and Timestamp Escape Sequences" in Appendix C: SQL Grammar.

For example, both of the following SQL statements update the open date of sales order 1023 in the Orders table. The first statement uses the escape sequence syntax. The second statement uses the Oracle Rdb native syntax for the DATE column and is not interoperable.

UPDATE Orders SET OpenDate={d '1995-01-15'} WHERE OrderID=1023

UPDATE Orders SET OpenDate='15-Jan-1995' WHERE OrderID=1023

The escape sequence for a date, time, or timestamp literal also can be placed in a character variable bound to a date, time, or timestamp parameter. For example, the following code uses a date parameter bound to a character variable to update the open date of sales order 1023 in the Orders table:

SQLCHAR      OpenDate[56]; // The size of a date literal is 55.
SQLINTEGER   OpenDateLenOrInd = SQL_NTS;

// Bind the parameter.
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_TYPE_DATE, 0, 0,
                  OpenDate, sizeof(OpenDate), &OpenDateLenOrInd);

// Place the date in the OpenDate variable. In addition to the escape
// sequence shown, it would also be possible to use either of the
// strings "{d '1995-01-15'}" and "15-Jan-1995", although the latter
// is data source-specific.
strcpy(OpenDate, "{d '1995-01-15'}");

// Execute the statement.
SQLExecDirect(hstmt, "UPDATE Orders SET OpenDate=? WHERE OrderID = 1023", SQL_NTS);

However, it is usually more efficient to bind the parameter directly to a date structure:

SQL_DATE_STRUCT   OpenDate;
SQLINTEGER        OpenDateInd = 0;

// Bind the parameter.
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_TYPE_DATE, SQL_TYPE_DATE, 0, 0,
                  &OpenDate, 0, &OpenDateLen);

// Place the date in the dsOpenDate structure.
OpenDate.year = 1995;
OpenDate.month = 1;
OpenDate.day = 15;

// Execute the statement.
SQLExecDirect(hstmt, "UPDATE Employee SET OpenDate=? WHERE OrderID = 1023", SQL_NTS);

To determine whether a driver supports the ODBC escape sequences for date, time, or timestamp literals, an application calls SQLGetTypeInfo. If the data source supports a date, time, or timestamp data type, it must also support the corresponding escape sequence.

Data sources can also support the datetime literals defined in the ANSI SQL-92 specification, which are different from the ODBC escape sequences for date, time, or timestamp literals. To determine whether a data source supports the ANSI literals, an application calls SQLGetInfo with the SQL_ANSI_SQL_DATETIME_LITERALS option.

To determine whether a driver supports the ODBC escape sequences for interval literals, an application calls SQLGetTypeInfo. If the data source supports a datetime interval data type, it must also support the corresponding escape sequence.

Data sources can also support the datetime literals defined in the ANSI SQL-92 specification, which are different from the ODBC escape sequences for datetime interval literals. To determine whether a data source supports the ANSI literals, an application calls SQLGetInfo with the SQL_ANSI_SQL_DATETIME_LITERALS option.


Scalar Function Calls

Scalar functions return a value for each row. For example, the absolute value scalar function takes a numeric column as an argument and returns the absolute value of each value in the column. The escape sequence for calling a scalar function is

{fn scalar-function}

where scalar-function is one of the functions listed in Appendix E: Scalar Functions. For more information on the scalar function escape sequence, see "Scalar Function Escape Sequence" in Appendix C: SQL Grammar.

For example, the following SQL statements create the same result set of uppercase customer names. The first statement uses the escape-sequence syntax. The second statement uses the native syntax for Ingres for OS/2 and is not interoperable.

SELECT {fn UCASE(Name)} FROM Customers

SELECT uppercase(Name) FROM Customers

An application can mix calls to scalar functions that use native syntax and calls to scalar functions that use ODBC syntax. For example, assume that names in the Employee table are stored as a last name, a comma, and a first name. The following SQL statement creates a result set of last names of employees in the Employee table. The statement uses the ODBC scalar function SUBSTRING and the SQL Server scalar function CHARINDEX and will execute correctly only on SQL Server.

SELECT {fn SUBSTRING(Name, 1, CHARINDEX(',', Name) – 1)} FROM Customers

For maximum interoperability, applications should use the CONVERT scalar function to make sure the output of a scalar function is the required type. The CONVERT function converts data from one SQL data type to the specified SQL data type. The syntax of the CONVERT function is

CONVERT(value_exp, data_type)

where value_exp is a column name, the result of another scalar function, or a literal value, and data_type is a keyword that matches the #define name used by an SQL data type identifier as defined in Appendix D: Data Types. For example, the following SQL statement uses the CONVERT function to make sure that the output of the CURDATE function is a date, rather than a timestamp or character data:

INSERT INTO Orders (OrderID, CustID, OpenDate, SalesPerson, Status)
   VALUES (?, ?, {fn CONVERT({fn CURDATE()}, SQL_DATE)}, ?, ?)

To determine which scalar functions are supported by a data source, an application calls SQLGetInfo with the SQL_CONVERT_FUNCTIONS, SQL_NUMERIC_FUNCTIONS, SQL_STRING_FUNCTIONS, SQL_SYSTEM_FUNCTIONS, and SQL_TIMEDATE_FUNCTIONS options. To determine which conversion operations are supported by the CONVERT function, an application calls SQLGetInfo with any of the options that start with SQL_CONVERT.


LIKE Predicate Escape Character

In a LIKE predicate, the percent sign (%) matches zero or more of any character and the underscore (_) matches any one character. To match an actual percent sign or underscore in a LIKE predicate, an escape character must precede the percent sign or underscore. The escape sequence that defines the LIKE predicate escape character is

{escape 'escape-character'}

where escape-character is any character supported by the data source.

For more information on the LIKE escape sequence, see "LIKE Escape Sequence" in Appendix C: SQL Grammar.

For example, the following SQL statements create the same result set of customer names that start with the characters "%AAA". The first statement uses the escape-sequence syntax. The second statement uses the native syntax for Microsoft® Access and is not interoperable. Note that the second percent character in each LIKE predicate is a wildcard character that matches zero or more of any character.

SELECT Name FROM Customers WHERE Name LIKE '\%AAA%' {escape '\'}

SELECT Name FROM Customers WHERE Name LIKE '[%]AAA%'

To determine whether the LIKE predicate escape character is supported by a data source, an application calls SQLGetInfo with the SQL_LIKE_ESCAPE_CLAUSE option.


Outer Joins

ODBC supports the SQL-92 left, right, and full outer join syntax. The escape sequence for outer joins is

{oj outer-join}

where outer-join is

table-reference {LEFT | RIGHT | FULL} OUTER JOIN
{table-reference | outer-join} ON search-condition

table-reference specifies a table name, and search-condition specifies the join condition between the table-references.

An outer join request must appear after the FROM keyword and before the WHERE clause (if one exists). For complete syntax information, see "Outer Join Escape Sequence" in Appendix C: SQL Grammar.

For example, the following SQL statements create the same result set that lists all customers and shows which has open orders. The first statement uses the escape-sequence syntax. The second statement uses the native syntax for Oracle and is not interoperable.

SELECT Customers.CustID, Customers.Name, Orders.OrderID, Orders.Status
   FROM {oj Customers LEFT OUTER JOIN Orders ON Customers.CustID=Orders.CustID}
   WHERE Orders.Status='OPEN'

SELECT Customers.CustID, Customers.Name, Orders.OrderID, Orders.Status
   FROM Customers, Orders
   WHERE (Orders.Status='OPEN') AND (Customers.CustID= Orders.CustID(+))

To determine the types of outer joins that a data source and driver support, an application calls SQLGetInfo with the SQL_OJ_CAPABILITIES flag. The types of outer joins that might be supported are left, right, full, or nested outer joins; outer joins in which the column names in the ON clause do not have the same order as their respective table names in the OUTER JOIN clause; inner joins in conjunction with outer joins; and outer joins using any ODBC comparison operator. If the SQL_OJ_CAPABILITIES information type returns 0, no outer join clause is supported.


Procedure Calls

A procedure is an executable object stored on the data source. Generally, it is one or more SQL statements that have been precompiled. The escape sequence for calling a procedure is

{[?=]call procedure-name[([parameter][,[parameter]]...)]}

where procedure-name specifies the name of a procedure and parameter specifies a procedure parameter.

For more information on the procedure call escape sequence, see "Procedure Call Escape Sequence" in Appendix C: SQL Grammar.

A procedure can have zero or more parameters. It can also return a value, as indicated by the optional parameter marker ?= at the start of the syntax. If parameter is an input or an input/output parameter, it can be a literal or a parameter marker. However, interoperable applications should always use parameter markers because some data sources do not accept literal parameter values. If parameter is an output parameter, it must be a parameter marker. Parameter markers must be bound with SQLBindParameter before the procedure call statement is executed.

Input and input/output parameters can be omitted from procedure calls. If a procedure is called with parentheses but without any parameters, such as {call procedure-name()}, the driver instructs the data source to use the default value for the first parameter. If the procedure does not have any parameters, this might cause the procedure to fail. If a procedure is called without parentheses, such as {call procedure-name}, the driver does not send any parameter values.

Literals can be specified for input and input/output parameters in procedure calls. For example, suppose the procedure InsertOrder has five input parameters. The following call to InsertOrder omits the first parameter, provides a literal for the second parameter, and uses a parameter marker for the third, fourth, and fifth parameters:

{call InsertOrder(, 10, ?, ?, ?)}   // Not interoperable!

Note that if a parameter is omitted, the comma delimiting it from other parameters must still appear. If an input or input/output parameter is omitted, the procedure uses the default value of the parameter. Another way to specify the default value of an input or input/output parameter is to set the value of the length/indicator buffer bound to the parameter to SQL_DEFAULT_PARAM.

If an input/output parameter is omitted or if a literal is supplied for the parameter, the driver discards the output value. Similarly, if the parameter marker for the return value of a procedure is omitted, the driver discards the return value. Finally, if an application specifies a return value parameter for a procedure that does not return a value, the driver sets the value of the length/indicator buffer bound to the parameter to SQL_NULL_DATA.

Suppose the procedure PARTS_IN_ORDERS creates a result set containing a list of orders that contain a particular part number. The following code calls this procedure for part number 544:

SQLUINTEGER   PartID;
SQLINTEGER    PartIDInd = 0;

// Bind the parameter.
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0,
                  &PartID, 0, PartIDInd);

// Place the department number in PartID.
PartID = 544;

// Execute the statement.
SQLExecDirect(hstmt, "{call PARTS_IN_ORDERS(?)}", SQL_NTS);

To determine if a data source supports procedures, an application calls SQLGetInfo with the SQL_PROCEDURES option.

For more information about procedures, see "Procedures" in Chapter 9: Executing Statements.


Chapter 9: Executing Statements

ODBC applications perform almost all database access by executing SQL statements. The general sequence of events is to allocate a statement handle, set any statement attributes, execute the statement, retrieve any results, and free the statement handle.


Allocating a Statement Handle

Before the application can execute a statement, it must allocate a statement handle as follows:

  1. The application declares a variable of type HSTMT. It then calls SQLAllocHandle and passes the address of this variable, the handle of the connection in which to allocate the statement, and the SQL_HANDLE_STMT option. For example:
    SQLHSTMT hstmt1;

SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt1);
  2. The Driver Manager allocates a structure in which to store information about the statement and calls SQLAllocHandle in the driver with the SQL_HANDLE_STMT option.
  3. The driver allocates its own structure in which to store information about the statement and returns the driver statement handle to the Driver Manager.
  4. The Driver Manager returns the Driver Manager statement handle to the application in the application variable.

The statement handle identifies which statement to use when calling ODBC functions. For more information about statement handles, see "Statement Handles" in Chapter 4: ODBC Fundamentals.


Statement Attributes

Statement attributes are characteristics of the statement. For example, whether to use bookmarks and what kind of cursor to use with the statement's result set are statement attributes.

Statement attributes are set with SQLSetStmtAttr and their current settings retrieved with SQLGetStmtAttr. There is no requirement that an application set any statement attributes; all statement attributes have defaults, some of which are driver-specific.

When a statement attribute can be set depends on the attribute itself. The SQL_ATTR_CONCURRENCY, SQL_ATTR_CURSOR_TYPE, SQL_ATTR_SIMULATE_CURSOR, and SQL_ATTR_USE_BOOKMARKS statement attributes must be set before the statement is executed. The SQL_ATTR_ASYNC_ENABLE and SQL_ATTR_NOSCAN statement attributes can be set at any time but are not applied until the statement is used again. SQL_ATTR_MAX_LENGTH, SQL_ATTR_MAX_ROWS, and SQL_ATTR_QUERY_TIMEOUT statement attributes can be set at any time, but it is driver-specific whether they are applied before the statement is used again. The remaining statement attributes can be set at any time.

Note The ability to set statement attributes at the connection level by calling SQLSetConnectAttr has been deprecated in ODBC 3.x. ODBC 3.x applications should never set statement attributes at the connection level. ODBC 3.x drivers need only support this functionality if they should work with ODBC 2.x applications. For more information, see "SQLSetConnectOption Mapping" in Appendix G: Driver Guidelines for Backward Compatibility.

An exception to this is the SQL_ATTR_METADATA_ID and SQL_ATTR_ASYNC_ENABLE attributes, which are both connection attributes and statement attributes and can be set either at the connection level or the statement level.

None of the statement attributes introduced in ODBC 3.x (except for SQL_ATTR_METADATA_ID) can be set at the connection level.

For more information, see the SQLSetStmtAttr function description.


Executing a Statement

There are four ways to execute a statement, depending on when they are compiled (prepared) by the database engine and who defines them:

  • Direct execution. The application defines the SQL statement. It is prepared and executed at run time in a single step.
  • Prepared execution. The application defines the SQL statement. It is prepared and executed at run time in separate steps. The statement can be prepared once and executed multiple times.
  • Procedures. The application can define and compile one or more SQL statements at development time and store these statements on the data source as a procedure. The procedure is executed one or more times at run time. The application can enumerate available stored procedures using catalog functions.
  • Catalog functions. The driver writer creates a function that returns a predefined result set. Usually, this function submits a predefined SQL statement or calls a procedure created for this purpose. The function is executed one or more times at run time.

A particular statement (as identified by its statement handle) can be executed any number of times. The statement can be executed with a variety of different SQL statements, or it can be executed repeatedly with the same SQL statement. For example, the following code uses the same statement handle (hstmt1) to retrieve and display the tables in the Sales database. It then reuses this handle to retrieve the columns in a table selected by the user.

SQLHSTMT    hstmt1;
SQLCHAR *   Table;

// Create a result set of all tables in the Sales database.
SQLTables(hstmt1, "Sales", SQL_NTS, "sysadmin", SQL_NTS, NULL, 0, NULL, 0);

// Fetch and display the table names; then close the cursor.
// Code not shown.

// Have the user select a particular table.
SelectTable(Table);

// Reuse hstmt1 to create a result set of all columns in Table.
SQLColumns(hstmt1, "Sales", SQL_NTS, "sysadmin", SQL_NTS, Table, SQL_NTS, NULL, 0);

// Fetch and display the column names in Table; then close the cursor.
// Code not shown.

And the following code shows how a single handle is used to repeatedly execute the same statement to delete rows from a table.

SQLHSTMT      hstmt1;
SQLUINTEGER   OrderID;
SQLINTEGER    OrderIDInd = 0;

// Prepare a statement to delete orders from the Orders table.
SQLPrepare(hstmt1, "DELETE FROM Orders WHERE OrderID = ?", SQL_NTS);

// Bind OrderID to the parameter for the OrderID column.
SQLBindParameter(hstmt1, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 5, 0,
                  &OrderID, 0, &OrderIDInd);

// Repeatedly execute hstmt1 with different values of OrderID.
while ((OrderID = GetOrderID()) != 0) {
   SQLExecute(hstmt1);
}

For many drivers, allocating statements is an expensive task, so reusing the same statement in this manner is usually more efficient than freeing existing statements and allocating new ones. Applications that create result sets on a statement must be careful to close the cursor over the result set before reexecuting the statement; for more information, see "Closing the Cursor" in Chapter 10: Retrieving Results (Basic).

Reusing statements also forces the application to avoid a limitation in some drivers of the number of statements that can be active at one time. The exact definition of "active" is driver-specific, but it often refers to any statement that has been prepared or executed and still has results available. For example, after an INSERT statement has been prepared, it is generally considered to be active; after a SELECT statement has been executed and the cursor is still open, it is generally considered to be active; after a CREATE TABLE statement has been executed, it is not generally considered to be active.

An application determines how many statements can be active on a single connection at one time by calling SQLGetInfo with the SQL_MAX_CONCURRENT_ACTIVITIES option. An application can use more active statements than this limit by opening multiple connections to the data source; because connections can be expensive, however, the effect on performance should be considered.

Applications can limit the amount of time allotted for a statement to execute with the SQL_ATTR_QUERY_TIMEOUT statement attribute. If the timeout period expires before the data source returns the result set, the function executing the SQL statement returns SQLSTATE HYT00 (Timeout expired). By default, there is no timeout.


Direct Execution

Direct execution is the simplest way to execute a statement. When the statement is submitted for execution, the data source compiles it into an access plan and then executes that access plan.

Direct execution is commonly used by generic applications that build and execute statements at run time. For example, the following code builds an SQL statement and executes it a single time:

SQLCHAR *SQLStatement;

// Build an SQL statement.
BuildStatement(SQLStatement);

// Execute the statement.
SQLExecDirect(hstmt, SQLStatement, SQL_NTS);

Direct execution works best for statements that will be executed a single time. Its major drawback is that the SQL statement is parsed every time it is executed. In addition, the application cannot retrieve information about the result set created by the statement (if any) until after the statement is executed; this is possible if the statement is prepared and executed in two separate steps.

To execute a statement directly, the application performs the following actions:

  1. Sets the values of any parameters. For more information, see "Statement Parameters," later in this chapter.
  2. Calls SQLExecDirect and passes it a string containing the SQL statement.
  3. When SQLExecDirect is called, the driver:
    • Modifies the SQL statement to use the data source's SQL grammar without parsing the statement; this includes replacing the escape sequences discussed in "Escape Sequences in ODBC" in Chapter 8: SQL Statements. The application can retrieve the modified form of an SQL statement by calling SQLNativeSql. Escape sequences are not replaced if the SQL_ATTR_NOSCAN statement attribute is set.
    • Retrieves the current parameter values and converts them as necessary. For more information, see "Statement Parameters," later in this chapter.
    • Sends the statement and converted parameter values to the data source for execution.
    • Returns any errors. These include sequencing or state diagnostics such as SQLSTATE 24000 (Invalid cursor state), syntactic errors such as SQLSTATE 42000 (Syntax error or access violation), and semantic errors such as SQLSTATE 42S02 (Base table or view not found).


Prepared Execution

Prepared execution is an efficient way to execute a statement more than once. The statement is first compiled, or prepared, into an access plan. The access plan is then executed one or more times at a later time. For more information about access plans, see "Processing an SQL Statement" in Chapter 2: An Introduction to SQL and ODBC.

Prepared execution is commonly used by vertical and custom applications to repeatedly execute the same, parameterized SQL statement. For example, the following code prepares a statement to update the prices of different parts. It then executes the statement multiple times with different parameter values each time.

SQLREAL       Price;
SQLUINTEGER   PartID;
SQLINTEGER    PartIDInd = 0, PriceInd = 0;

// Prepare a statement to update salaries in the Employees table.
SQLPrepare(hstmt, "UPDATE Parts SET Price = ? WHERE PartID = ?", SQL_NTS);

// Bind Price to the parameter for the Price column and PartID to
// the parameter for the PartID column.
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_REAL, 7, 0,
                  &Price, 0, &PriceInd);
SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 10, 0,
                  &PartID, 0, &PartIDInd);

// Repeatedly execute the statement.
while (GetPrice(&PartID, &Price)) {
   SQLExecute(hstmt);
}

Prepared execution is faster than direct execution for statements executed more than once, primarily because the statement is compiled only once; statements executed directly are compiled each time they are executed. Prepared execution also can provide a reduction in network traffic because the driver can send an access plan identifier to the data source each time the statement is executed, rather than an entire SQL statement, if the data source supports access plan identifiers.

The application can retrieve the metadata for the result set after the statement is prepared and before it is executed. However, returning metadata for prepared, unexecuted statements is expensive for some drivers and should be avoided by interoperable applications if possible. For more information, see "Result Set Metadata" in Chapter 10: Retrieving Results (Basic).

Prepared execution should not be used for statements executed a single time. For such statements, it is slightly slower than direct execution because it requires an additional ODBC function call.

Important Committing or rolling back a transaction, either by explicitly calling SQLEndTran or by working in auto-commit mode, causes some data sources to delete the access plans for all statements on a connection. For more information, see the SQL_CURSOR_COMMIT_BEHAVIOR and SQL_CURSOR_ROLLBACK_BEHAVIOR options in the SQLGetInfo function description.

To prepare and execute a statement, the application:

  1. Calls SQLPrepare and passes it a string containing the SQL statement.
  2. Sets the values of any parameters. Parameters can actually be set before or after preparing the statement. For more information, see "Statement Parameters," later in this chapter.
  3. Calls SQLExecute and does any additional processing that is necessary, such as fetching data.
  4. Repeats steps 2 and 3 as necessary.
  5. When SQLPrepare is called, the driver:
    • Modifies the SQL statement to use the data source's SQL grammar without parsing the statement. This includes replacing the escape sequences discussed in "Escape Sequences in ODBC" in Chapter 8: SQL Statements. The application can retrieve the modified form of an SQL statement by calling SQLNativeSql. Escape sequences are not replaced if the SQL_ATTR_NOSCAN statement attribute is set.
    • Sends the statement to the data source for preparation.
    • Stores the returned access plan identifier for later execution (if the preparation succeeded) or returns any errors (if the preparation failed). Errors include syntactic errors such as SQLSTATE 42000 (Syntax error or access violation) and semantic errors such as SQLSTATE 42S02 (Base table or view not found).

      Note Some drivers do not return errors at this point but instead return them when the statement is executed or when catalog functions are called. Thus, SQLPrepare might appear to have succeeded when in fact it has failed.

  6. When SQLExecute is called, the driver:
    • Retrieves the current parameter values and converts them as necessary. For more information, see "Statement Parameters," later in this chapter.
    • Sends the access plan identifier and converted parameter values to the data source.
    • Returns any errors. These are generally run-time errors such as SQLSTATE 24000 (Invalid cursor state). However, some drivers return syntactic and semantic errors at this point.

If the data source does not support statement preparation, the driver must emulate it to the extent possible. For example, the driver might do nothing when SQLPrepare is called and then perform direct execution of the statement when SQLExecute is called.

If the data source supports syntax checking without execution, the driver might submit the statement for checking when SQLPrepare is called and submit the statement for execution when SQLExecute is called.

If the driver cannot emulate statement preparation, it stores the statement when SQLPrepare is called and submits it for execution when SQLExecute is called.

Because emulated statement preparation is not perfect, SQLExecute can return any errors normally returned by SQLPrepare.


Procedures

A procedure is an executable object stored on the data source. Generally, it is one or more SQL statements that have been precompiled.


When to Use Procedures

There are a number of advantages to using procedures, all based on the fact that using procedures moves SQL statements from the application to the data source. What is left in the application is an interoperable procedure call. These advantages include:

  • Performance. Procedures are usually the fastest way to execute SQL statements. Like prepared execution, the statement is compiled and executed in two separate steps. Unlike prepared execution, procedures are executed only at run time. They are compiled at a different time.
  • Business rules. A business rule is a rule about the way in which a company does business. For example, only someone with the title Sales Person might be allowed to add new sales orders. Placing these rules in procedures allows individual companies to customize vertical applications by rewriting the procedures called by the application without having to modify the application code. For example, an order entry application might call the procedure InsertOrder with a fixed number of parameters; exactly how InsertOrder is implemented can vary from company to company.
  • Replaceability. Closely related to placing business rules in procedures is the fact that procedures can be replaced without recompiling the application. If a business rule changes after a company has bought and installed an application, the company can change the procedure containing that rule. From the application's standpoint, nothing has changed; it still calls a particular procedure to accomplish a particular task.
  • DBMS-specific SQL. Procedures provide a way for applications to exploit DBMS-specific SQL and still remain interoperable. For example, a procedure on a DBMS that supports control-of-flow statements in SQL might trap and recover from errors, while a procedure on a DBMS that does not support control-of-flow statements might simply return an error.
  • Procedures survive transactions. On some data sources, the access plans for all prepared statements on a connection are deleted when a transaction is committed or rolled back. By placing SQL statements in procedures, which are permanently stored in the data source, the statements survive the transaction. Whether the procedures survive in a prepared, partially prepared, or unprepared state is DBMS-specific.
  • Separate development. Procedures can be developed separately from the rest of the application. In large corporations, this might provide a way to further exploit the skills of highly specialized programmers. In other words, application programmers can write user-interface code and database programmers can write procedures.

Procedures are generally used by vertical and custom applications. These applications tend to perform fixed tasks, and it is possible to hard-code procedure calls in them. For example, an order entry application might call the procedures InsertOrder, DeleteOrder, UpdateOrder, and GetOrders.

There is little reason to call procedures from generic applications. Procedures are usually written to perform a task in the context of a particular application and so have no use to generic applications. For example, a spreadsheet has no reason to call the InsertOrder procedure just mentioned. Furthermore, generic applications should not construct procedures at run time in hopes of providing faster statement execution; not only is this likely to be slower than prepared or direct execution, it also requires DBMS-specific SQL statements.

An exception to this is application-development environments, which often provide a way for programmers to build SQL statements that execute procedures and may provide a way for programmers to test procedures. Such environments call SQLProcedures to list available procedures and SQLProcedureColumns to list the input, input/output, and output parameters, the procedure return value, and the columns of any result sets created by a procedure. However, such procedures must be developed beforehand on each data source; doing so requires DBMS-specific SQL statements.

There are three major disadvantages to using procedures. The first is that procedures must be written and compiled for each DBMS with which the application is to run. While this is not a problem for custom applications, it can significantly increase development and maintenance time for vertical applications designed to run with a number of DBMSs.

The second disadvantage is that many DBMSs do not support procedures. Again, this is most likely to be a problem for vertical applications designed to run with a number of DBMSs. To determine whether procedures are supported, an application calls SQLGetInfo with the SQL_PROCEDURES option.

The third disadvantage, which is particularly applicable to application development environments, is that ODBC does not define a standard grammar for creating procedures. That is, although applications can call procedures interoperably, they cannot create them interoperably.


Executing Procedures

ODBC defines a standard escape sequence for executing procedures. For the syntax of this sequence and a code example that uses it, see "Procedure Calls" in Chapter 8: SQL Statements.

To execute a procedure, an application performs the following actions:

  1. Sets the values of any parameters. For more information, see "Statement Parameters," later in this chapter.
  2. Calls SQLExecDirect and passes it a string containing the SQL statement that executes the procedure. This statement can use the escape sequence defined by ODBC or DBMS-specific syntax; statements that use DBMS-specific syntax are not interoperable.
  3. When SQLExecDirect is called, the driver:
    • Retrieves the current parameter values and converts them as necessary. For more information, see "Statement Parameters," later in this chapter.
    • Calls the procedure in the data source and sends it the converted parameter values. How the driver calls the procedure is driver-specific. For example, it might modify the SQL statement to use the data source's SQL grammar and submit this statement for execution, or it might call the procedure directly using a Remote Procedure Call (RPC) mechanism that is defined in the data stream protocol of the DBMS.
    • Returns the values of any input/output or output parameters or the procedure return value, assuming the procedure succeeds. These values might not be available until after all other results (row counts and result sets) generated by the procedure have been processed. If the procedure fails, the driver returns any errors.


Batches of SQL Statements

A batch of SQL statements is a group of two or more SQL statements or a single SQL statement that has the same effect as a group of two or more SQL statements. In some implementations, the entire batch statement is executed before any results are available. This is often more efficient than submitting statements separately, because network traffic can often be reduced and the data source can sometimes optimize execution of a batch of SQL statements. In other implementations, calling SQLMoreResults triggers the execution of the next statement in the batch. ODBC supports the following types of batches:

  • Explicit batches. An explicit batch is two or more SQL statements separated by semicolons (;). For example, the following batch of SQL statements opens a new sales order. This requires inserting rows into both the Orders and Lines tables. Note that there is no semicolon after the last statement.
    INSERT INTO Orders (OrderID, CustID, OpenDate, SalesPerson, Status)
   VALUES (2002, 1001, {fn CURDATE()}, 'Garcia', 'OPEN');
INSERT INTO Lines (OrderID, Line, PartID, Quantity)
   VALUES (2002, 1, 1234, 10);
INSERT INTO Lines (OrderID, Line, PartID, Quantity)
   VALUES (2002, 2, 987, 8);
INSERT INTO Lines (OrderID, Line, PartID, Quantity)
   VALUES (2002, 3, 566, 17);
INSERT INTO Lines (OrderID, Line, PartID, Quantity)
   VALUES (2002, 4, 412, 500)
  • Procedures. If a procedure contains more than one SQL statement, it is considered to be a batch of SQL statements. For example, the following SQL Server–specific statement creates a procedure that returns a result set containing information about a customer and a result set listing all the open sales orders for that customer:
    CREATE PROCEDURE GetCustInfo (@CustomerID INT) AS
   SELECT * FROM Customers WHERE CustID = @CustomerID
   SELECT OrderID FROM Orders
      WHERE CustID = @CustomerID AND Status = 'OPEN'

    The CREATE PROCEDURE statement itself is not a batch of SQL statements. However, the procedure it creates is a batch of SQL statements. No semicolons separate the two SELECT statements because the CREATE PROCEDURE statement is specific to SQL Server, and SQL Server does not require semicolons to separate multiple statements in a CREATE PROCEDURE statement.

  • Arrays of parameters. Arrays of parameters can be used with a parameterized SQL statement as an effective way to perform bulk operations. For example, arrays of parameters can be used with the following INSERT statement to insert multiple rows into the Lines table while executing only a single SQL statement:
    INSERT INTO Lines (OrderID, Line, PartID, Quantity)
   VALUES (?, ?, ?, ?)

    If a data source does not support arrays of parameters, the driver can emulate them by executing the SQL statement once for each set of parameters. For more information, see "Statement Parameters" and "Arrays of Parameter Values," later in this chapter.

The different types of batches cannot be mixed in an interoperable manner. That is, how an application determines the result of executing an explicit batch that includes procedure calls, an explicit batch that uses arrays of parameters, and a procedure call that uses arrays of parameters is driver-specific.


Result-Generating and Result-Free Statements

SQL statements can be loosely divided into the following five categories:

  • Result set–generating statements. These are SQL statements that generate a result set. For example, a SELECT statement.
  • Row count–generating statements. These are SQL statements that generate a count of affected rows. For example, an UPDATE or DELETE statement.
  • Data Definition Language (DDL) statements. These are SQL statements that modify the structure of the database. For example, CREATE TABLE or DROP INDEX.
  • Context-changing statements. These are SQL statements that change the context of a database. For example, the USE and SET statements in SQL Server.
  • Administrative statements. These are SQL statements used for administrative purposes in a database. For example, GRANT and REVOKE.

SQL statements in the first two categories are collectively known as result-generating statements. SQL statements in the latter three categories are collectively known as result-free statements. ODBC defines the semantics of batches that include only result-generating statements. These semantics vary widely and are therefore data source–specific. For example, the SQL Server driver does not support dropping an object and then referring to or re-creating the same object in the same batch. Therefore, the term batch as used in this manual refers only to batches of result-generating statements.


Executing Batches

Before an application executes a batch of statements, it should first check whether they are supported. To do this, the application calls SQLGetInfo with the SQL_BATCH_SUPPORT, SQL_PARAM_ARRAY_ROW_COUNTS, and SQL_PARAM_ARRAY_SELECTS options. The first option returns whether row count–generating and result set–generating statements are supported in explicit batches and procedures, while the latter two options return information about the availability of row counts and result sets in parameterized execution.

Batches of statements are executed through SQLExecute or SQLExecDirect. For example, the following call executes an explicit batch of statements to open a new sales order.

SQLCHAR *BatchStmt =
   "INSERT INTO Orders (OrderID, CustID, OpenDate, SalesPerson, Status)"
      "VALUES (2002, 1001, {fn CURDATE()}, 'Garcia', 'OPEN');"
   "INSERT INTO Lines (OrderID, Line, PartID, Quantity) VALUES (2002, 1, 1234, 10);"
   "INSERT INTO Lines (OrderID, Line, PartID, Quantity) VALUES (2002, 2, 987, 8);"
   "INSERT INTO Lines (OrderID, Line, PartID, Quantity) VALUES (2002, 3, 566, 17);"
   "INSERT INTO Lines (OrderID, Line, PartID, Quantity) VALUES (2002, 4, 412, 500)";

SQLExecDirect(hstmt, BatchStmt, SQL_NTS);

When a batch of result-generating statements is executed, it returns one or more row counts or result sets. For information about how to retrieve these, see "Multiple Results" in Chapter 11: Retrieving Results (Advanced).

If a batch of statements includes parameter markers, these are numbered in increasing parameter order as they are in any other statement. For example, the following batch of statements has parameters numbered from 1 through 21; those in the first INSERT statement are numbered 1 through 5 and those in the last INSERT statement are numbered 18 through 21.

INSERT INTO Orders (OrderID, CustID, OpenDate, SalesPerson, Status)
   VALUES (?, ?, ?, ?, ?);
INSERT INTO Lines (OrderID, Line, PartID, Quantity) VALUES (?, ?, ?, ?);
INSERT INTO Lines (OrderID, Line, PartID, Quantity) VALUES (?, ?, ?, ?);
INSERT INTO Lines (OrderID, Line, PartID, Quantity) VALUES (?, ?, ?, ?);
INSERT INTO Lines (OrderID, Line, PartID, Quantity) VALUES (?, ?, ?, ?);

For more information about parameters, see "Statement Parameters," later in this chapter.


Errors and Batches

When an error occurs while executing a batch of SQL statements, one of the following four outcomes are possible. (Each possible outcome is data source–specific and might even depend on the statements included in the batch.)

  • No statements in the batch are executed.
  • No statements in the batch are executed and the transaction is rolled back.
  • All of the statements before the error statement are executed.
  • All of the statements except the error statement are executed.

In the first two cases, SQLExecute and SQLExecDirect return SQL_ERROR. In the latter two cases, they may return SQL_SUCCESS_WITH_INFO or SQL_SUCCESS, depending on the implementation. In all cases, further error information can be retrieved with SQLGetDiagField, SQLGetDiagRec, or SQLError. However, the nature and depth of this information is data source–specific. Furthermore, this information is unlikely to exactly identify the statement in error.


Executing Catalog Functions

Because a catalog function creates a result set, it is equivalent to executing any result set–generating SQL statement. In fact, catalog functions are often implemented by executing predefined SQL statements or calling predefined procedures that are shipped with the driver or DBMS. Almost anything that applies to SQL statements that create result sets also applies to catalog functions. For example, the SQL_ATTR_MAX_ROWS statement attribute limits the number of rows returned by the catalog function, just as it limits the number of rows returned by a SELECT statement.

To execute a catalog function, an application just calls the function.

For more information about catalog functions, see Chapter 7: Catalog Functions.


Statement Parameters

A parameter is a variable in an SQL statement. For example, suppose a Parts table has columns named PartID, Description, and Price. To add a part without parameters would require constructing an SQL statement such as:

INSERT INTO Parts (PartID, Description, Price) VALUES (2100, 'Drive shaft', 50.00)

Although this statement inserts a new order, it is not a good solution for an order entry application because the values to insert cannot be hard-coded in the application. An alternative is to construct the SQL statement at run time, using the values to be inserted. This also is not a good solution, due to the complexity of constructing statements at run time. The best solution is to replace the elements of the VALUES clause with question marks (?), or parameter markers:

INSERT INTO Parts (PartID, Description, Price) VALUES (?, ?, ?)

The parameter markers are then bound to application variables. To add a new row, the application has only to set the values of the variables and execute the statement. The driver then retrieves the current values of the variables and sends them to the data source. If the statement will be executed multiple times, the application can make the process even more efficient by preparing the statement.

The statement just shown might be hard-coded in an order entry application to insert a new row. However, parameter markers are not limited to vertical applications. For any application, they ease the difficulty of constructing SQL statements at run time by avoiding conversions to and from text. For example, the part ID just shown is most likely stored in the application as an integer. If the SQL statement is constructed without parameter markers, the application must convert the part ID to text and the data source must convert it back to an integer. By using a parameter marker, the application can send the part ID to the driver as an integer, which usually can send it to the data source as an integer, thereby saving two conversions. For long data values this is critical, because the text forms of such values often exceed the allowable length of an SQL statement.

Parameters are legal only in certain places in SQL statements. For example, they are not allowed in the select list (the list of columns to be returned by a SELECT statement), nor are they allowed as both operands of a binary operator such as the equal sign (=), because it would be impossible to determine the parameter type. In general, parameters are legal only in Data Manipulation Language (DML) statements, and not in Data Definition Language (DDL) statements. For details, see "Parameter Markers" in Appendix C: SQL Grammar.

When the SQL statement invokes a procedure, named parameters can be used. Named parameters are identified by their names, not by their position in the SQL statement. They can be bound by a call to SQLBindParameter, but the parameter is identified by the SQL_DESC_NAME field of the IPD (implementation parameter descriptor), not by the ParameterNumber argument of SQLBindParameter. They can also be bound by calling SQLSetDescField or SQLSetDescRec. For more information on named parameters, see "Binding Parameters by Name (Named Parameters)," later in this chapter. For more information on descriptors, see Chapter 13: Descriptors.


Binding Parameters

Each parameter in an SQL statement must be associated, or bound, to a variable in the application before the statement is executed. When the application binds a variable to a parameter, it describes that variable—address, C data type, and so on—to the driver. It also describes the parameter itself—SQL data type, precision, and so on. The driver stores this information in the structure it maintains for that statement and uses the information to retrieve the value from the variable when the statement is executed.

Parameters can be bound or rebound at any time before a statement is executed. If a parameter is rebound after a statement is executed, the binding does not apply until the statement is executed again. To bind a parameter to a different variable, an application simply rebinds the parameter with the new variable; the previous binding is automatically released.

A variable remains bound to a parameter until a different variable is bound to the parameter, until all parameters are unbound by calling SQLFreeStmt with the SQL_RESET_PARAMS option, or until the statement is released. For this reason, the application must be sure that variables are not freed until after they are unbound. For more information, see "Allocating and Freeing Buffers" in Chapter 4: ODBC Fundamentals.

Because parameter bindings are just information stored in the structure maintained by the driver for the statement, they can be set in any order. They are also independent of the SQL statement that is executed. For example, suppose an application binds three parameters and then executes the following SQL statement:

INSERT INTO Parts (PartID, Description, Price) VALUES (?, ?, ?)

If the application then immediately executes the SQL statement

SELECT * FROM Orders WHERE OrderID = ?, OpenDate = ?, Status = ?

on the same statement handle, the parameter bindings for the INSERT statement are used because those are the bindings stored in the statement structure. In most cases, this is a poor programming practice and should be avoided. Instead, the application should call SQLFreeStmt with the SQL_RESET_PARAMS option to unbind all the old parameters and then bind new ones.


Binding Parameter Markers

The application binds parameters by calling SQLBindParameter. SQLBindParameter binds one parameter at a time. With it, the application specifies the following:

  • The parameter number. Parameters are numbered in increasing parameter order in the SQL statement, starting with the number 1. While it is legal to specify a parameter number that is higher than the number of parameters in the SQL statement, the parameter value will be ignored when the statement is executed.
  • The parameter type (input, input/output, or output). Except for parameters in procedure calls, all parameters are input parameters. For more information, see "Procedure Parameters," later in this chapter.
  • The C data type, address, and byte length of the variable bound to the parameter. The driver must be able to convert the data from the C data type to the SQL data type or an error is returned. For a list of supported conversions, see Converting Data from C to SQL Data Types in Appendix D: Data Types.
  • The SQL data type, precision, and scale of the parameter itself.
  • The address of a length/indicator buffer. It provides the byte length of binary or character data, specifies that the data is NULL, or specifies that the data will be sent with SQLPutData. For more information, see "Using Length/Indicator Values" in Chapter 4: ODBC Fundamentals.

For example, the following code binds SalesPerson and CustID to parameters for the SalesPerson and CustID columns. Because SalesPerson contains character data, which is variable length, the code specifies the byte length of SalesPerson (11) and binds SalesPersonLenOrInd to contain the byte length of the data in SalesPerson. This information is not necessary for CustID because it contains integer data, which is of fixed length.

SQLCHAR       SalesPerson[11];
SQLINTEGER    SalesPersonLenOrInd, CustIDInd;
SQLUINTEGER   CustID;

// Bind SalesPerson to the parameter for the SalesPerson column and
// CustID to the parameter for the CustID column.
SQLBindParameter(hstmt1, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0,
                  SalesPerson, sizeof(SalesPerson), &SalesPersonLenOrInd);
SQLBindParameter(hstmt1, 2, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 10, 0,
                  &CustID, 0, &CustIDInd);

// Set the values of the salesperson and customer ID variables and length/indicators.
strcpy(SalesPerson, "Garcia");
SalesPersonLenOrInd = SQL_NTS;
CustID = 1331;
CustIDInd = 0;

// Execute a statement to get data for all orders made to the specified
// customer by the specified salesperson.
SQLExecDirect(hstmt1,"SELECT * FROM Orders WHERE SalesPerson=? AND CustID=?",SQL_NTS);

When SQLBindParameter is called, the driver stores this information in the structure for the statement. When the statement is executed, it uses the information to retrieve the parameter data and send it to the data source.

Note In ODBC 1.0, parameters were bound with SQLSetParam. The Driver Manager maps calls between SQLSetParam and SQLBindParameter, depending on the versions of ODBC used by the application and driver.


Binding Parameters by Name (Named Parameters)

Certain DBMSs allow an application to specify the parameters to a stored procedure by name instead of by position in the procedure call. Such parameters are called named parameters. ODBC supports the use of named parameters. In ODBC, named parameters are used only in calls to stored procedures and cannot be used in other SQL statements.

The driver checks the value of the SQL_DESC_UNNAMED field of the IPD to determine whether named parameters are used. If SQL_DESC_UNNAMED is not set to SQL_UNNAMED, the driver uses the name in the SQL_DESC_NAME field of the IPD to identify the parameter. To bind the parameter, an application can call SQLBindParameter to specify the parameter information and then can call SQLSetDescField to set the SQL_DESC_NAME field of the IPD. When named parameters are used, the order of the parameter in the procedure call is not important and the parameter's record number is ignored.

The difference between unnamed parameters and named parameters is in the relationship between the record number of the descriptor and the parameter number in the procedure. When unnamed parameters are used, the first parameter marker is related to the first record in the parameter descriptor, which in turn is related to the first parameter (in creation order) in the procedure call. When named parameters are used, the first parameter marker is still related to the first record of the parameter descriptor, but the relationship between the record number of the descriptor and the parameter number in the procedure does not exist anymore. Named parameters do not use the mapping of the descriptor record number to the procedure parameter position; instead, the descriptor record name is mapped to the procedure parameter name.

Note If automatic population of the IPD is enabled, the driver will populate the descriptor such that the order of the descriptor records will match the order of the parameters in the procedure definition, even if named parameters are used.

If a named parameter is used, all parameters must be named parameters. If any parameter is not a named parameter, then none of the parameters ca be named parameters. If there were a mixture of named parameters and unnamed parameters, the behavior would be driver-dependent.

As an example of named parameters, suppose a SQL Server stored procedure has been defined as follows:

CREATE PROCEDURE test @title_id int = 1, @quote char(30) AS <blah>

In this procedure, the first parameter, @title_id, has a default value of 1. An application can use the following code to invoke this procedure such that it specifies only one dynamic parameter. This parameter is a named parameter with the name "@quote".

// Prepare the procedure invocation statement.
SQLPrepare(hstmt, "{call test(?)}", SQL_NTS);

// Populate record 1 of ipd.
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
                  30, 0, szQuote, 0, &cbValue);

// Get ipd handle and set the SQL_DESC_NAMED and SQL_DESC_UNNAMED fields
// for record #1.
SQLGetStmtAttr(hstmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0);
SQLSetDescField(hIpd, 1, SQL_DESC_NAME, "@quote", SQL_NTS);
SQLSetDescField(hIpd, 1, SQL_DESC_UNNAMED, SQL_NAMED, 0);

// Assuming that szQuote has been appropriately initialized,
// execute.
SQLExecute(hstmt);


Parameter Binding Offsets

An application can specify that an offset is added to bound parameter buffer addresses and the corresponding length/indicator buffer addresses when SQLExecDirect or SQLExecute is called. The result of these additions determines the addresses used in these operations.

Bind offsets allow an application to change bindings without calling SQLBindParameter for previously bound parameters. A call to SQLBindParameter to rebind a parameter changes the buffer address and the length/indicator pointer. Rebinding with an offset, on the other hand, simply adds an offset to the existing bound parameter buffer address and length/indicator buffer address. When offsets are used, the bindings are a "template" of how the application buffers are laid out and the application can move this "template" to different areas of memory by changing the offset. A new offset can be specified at any time and is always added to the originally bound values.

To specify a bind offset, the application sets the SQL_ATTR_PARAM_BIND_OFFSET_PTR statement attribute to the address of an SQLINTEGER buffer. Before the application calls a function that uses the bindings, it places an offset in bytes in this buffer, as long as neither the parameter buffer address nor the length/indicator buffer address is 0, and the bound parameter is in the SQL statement. The sum of the address and the offset must be a valid address. (This means that either or both the offset and the address to which the offset is added can be invalid, as long as their sum is a valid address.)

Note Binding offsets are not supported by ODBC 2.x drivers.


Describing Parameters

SQLBindParameter has arguments that describe the parameter: its SQL type, precision, and scale. The driver uses this information, or metadata, to convert the parameter value to the type needed by the data source. At first glance, it might seem that the driver is in a better position to know the parameter metadata than the application; after all, the driver can easily discover the metadata for a result set column. As it turns out, this is not the case. First, most data sources do not provide a way for the driver to discover parameter metadata. Second, most applications already know the metadata.

If an SQL statement is hard-coded in the application, the application writer already knows the type of each parameter. If an SQL statement is constructed by the application at run time, the application can determine the metadata as it builds the statement. For example, when the application constructs the clause

WHERE OrderID = ?

it can call SQLColumns for the OrderID column.

The only situation in which the application cannot easily determine the parameter metadata is when the user enters a parameterized statement. In this case, the application calls SQLPrepare to prepare the statement, SQLNumParams to determine the number of parameters, and SQLDescribeParam to describe each parameter. However, as was noted earlier, most data sources do not provide a way for the driver to discover parameter metadata, so SQLDescribeParam is not widely supported.


Setting Parameter Values

To set the value of a parameter, the application simply sets the value of the variable bound to the parameter. It is not important when this value is set, as long as it is set before the statement is executed. The application can set the value before or after binding the variable, and it can change the value as many times as it wants. When the statement is executed, the driver simply retrieves the current value of the variable. This is particularly useful when a prepared statement is executed more than once; the application sets new values for some or all of the variables each time the statement is executed. For an example of this, see "Prepared Execution," earlier in this chapter.

If a length/indicator buffer was bound in the call to SQLBindParameter, it must be set to one of the following values before the statement is executed:

  • The byte length of the data in the bound variable. The driver checks this length only if the variable is character or binary (ValueType is SQL_C_CHAR or SQL_C_BINARY).
  • SQL_NTS. The data is a null-terminated string.
  • SQL_NULL_DATA. The data value is NULL, and the driver ignores the value of the bound variable.
  • SQL_DATA_AT_EXEC or the result of the SQL_LEN_DATA_AT_EXEC macro. The value of the parameter is to be sent with SQLPutData. For more information, see "Sending Long Data," later in this chapter.

The following table shows the values of the bound variable and the length/indicator buffer that the application sets for a variety of parameter values.

Parameter value | Parameter (SQL) data type | Variable (C) data type | Value in bound variable | Value in length/indicator buffer[d]
"ABC" | SQL_CHAR | SQL_C_CHAR | ABC\0[a] | SQL_NTS or 3
10 | SQL_INTEGER | SQL_C_SLONG | 10 | --
10 | SQL_INTEGER | SQL_C_CHAR | 10\0[a] | SQL_NTS or 2
1 P.M. | SQL_TYPE_TIME | SQL_C_TYPE_TIME | 13,0,0[b] | --
1 P.M. | SQL_TYPE_TIME | SQL_C_CHAR | {t '13:00:00'}\0 [a], [c] | SQL_NTS or 14
NULL | SQL_SMALLINT | SQL_C_SSHORT | -- | SQL_NULL_DATA

[a] "\0" represents a null-termination character. The null-termination character is required only if the value in the length/indicator buffer is SQL_NTS.

[b] The numbers in this list are the numbers stored in the fields of the TIME_STRUCT structure.

[c] The string uses the ODBC date escape clause. For more information, see "Date, Time, and Timestamp Literals" in Chapter 8: SQL Statements.

[d] Drivers must always check this value to see whether it is a special value, such as SQL_NULL_DATA.

What a driver does with a parameter value at execution time is driver-dependent. If necessary, the driver converts the value from the C data type and byte length of the bound variable to the SQL data type, precision, and scale of the parameter. In most cases, the driver then sends the value to the data source. In some cases, it formats the value as text and inserts it into the SQL statement before sending the statement to the data source.


Sending Long Data

DBMSs define long data as any character or binary data over a certain size, such as 254 characters. It might not be possible to store an entire item of long data in memory, such as when the item represents a long text document or a bitmap. Because such data cannot be stored in a single buffer, the data source sends it to the driver in parts with SQLPutData when the statement is executed. Parameters for which data is sent at execution time are known as data-at-execution parameters.

Note An application can actually send any type of data at execution time with SQLPutData, although only character and binary data can be sent in parts. However, if the data is small enough to fit in a single buffer, there is generally no reason to use SQLPutData. It is much easier to bind the buffer and let the driver retrieve the data from the buffer.

To send data at execution time, the application performs the following actions:

  1. Passes a 32-bit value that identifies the parameter in the ParameterValuePtr argument in SQLBindParameter rather than passing the address of a buffer. This value is not analyzed by the driver. It will be returned to the application later, so it should mean something to the application. For example, it might be the number of the parameter or the handle of a file containing data.
  2. Passes the address of a length/indicator buffer in the StrLen_or_IndPtr argument of SQLBindParameter.
  3. Stores SQL_DATA_AT_EXEC or the result of the SQL_LEN_DATA_AT_EXEC(length) macro in the length/indicator buffer. Both of these values indicate to the driver that the data for the parameter will be sent with SQLPutData. SQL_LEN_DATA_AT_EXEC(length) is used when sending long data to a data source that needs to know how many bytes of long data will be sent so that it can preallocate space. To determine if a data source requires this value, the application calls SQLGetInfo with the SQL_NEED_LONG_DATA_LEN option. All drivers must support this macro; if the data source does not require the byte length, the driver can ignore it.
  4. Calls SQLExecute or SQLExecDirect. The driver discovers that a length/indicator buffer contains the value SQL_DATA_AT_EXEC or the result of the SQL_LEN_DATA_AT_EXEC(length) macro and returns SQL_NEED_DATA as the return value of the function.
  5. Calls SQLParamData in response to the SQL_NEED_DATA return value. If long data needs to be sent, SQLParamData returns SQL_NEED_DATA. In the buffer pointed to by the ValuePtrPtr argument, the driver returns the value that identifies the data-at-execution parameter. If there is more than one data-at-execution parameter, the application must use this value to determine which parameter to send data for; the driver is not required to request data for data-at-execution parameters in any particular order.
  6. Calls SQLPutData to send the parameter data to the driver. If the parameter data does not fit into a single buffer, as is often the case with long data, the application calls SQLPutData repeatedly to send the data in parts; it is up to the driver and data source to reassemble the data. If the application passes null-terminated string data, the driver or data source must remove the null-termination character as part of the reassembly process.
  7. Calls SQLParamData again to indicate that it has sent all of the data for the parameter. If there are any data-at-execution parameters for which data has not been sent, the driver returns SQL_NEED_DATA and the value that identifies the next parameter; the application returns to step 6. If data has been sent for all data-at-execution parameters, the statement is executed. SQLParamData returns SQL_SUCCESS or SQL_SUCCESS_WITH_INFO and can return any return value or diagnostic that SQLExecute or SQLExecDirect can return.

After SQLExecute or SQLExecDirect returns SQL_NEED_DATA and before data has been completely sent for the last data-at-execution parameter, the statement is in a Need Data state. While a statement is in a Need Data state, the application can call only SQLPutData, SQLParamData, SQLCancel, SQLGetDiagField, or SQLGetDiagRec; all other functions return SQLSTATE HY010 (Function sequence error). Calling SQLCancel cancels execution of the statement and returns it to its previous state. For more information, see Appendix B: ODBC State Transition Tables.

For an example of sending data at execution time, see the SQLPutData function description.


Retrieving Output Parameters by SQLGetData

An application can retrieve bound output parameters by calling SQLBindParameter, in which case output values are present in the application variables to which the respective parameters are bound. If the output parameters are unbound, the application can read argument values by calling SQLGetData. The application can use both of these techniques if some parameters are bound and some are unbound.

For unbound output parameters following the highest-numbered bound parameter, portable applications obtain the parameter data by calling SQLGetData in ascending order of parameter number. It is driver-defined whether an application can obtain parameter data in a different sequence. It is driver-defined whether parameter data for lower-numbered unbound parameters is available.

The application can achieve type conversion of the parameter data by specifying in the call to SQLGetData either the desired target type or the value SQL_APD_TYPE, which means that the APD indicates the desired target type even though the parameter is unbound.

If the length of the unbound output parameter value exceeds the length of the application's buffer, SQLGetData can be called multiple times to obtain the value of a single parameter of SQL_CHAR or SQL_VARCHAR type in pieces of manageable size.


Procedure Parameters

Parameters in procedure calls can be input, input/output, or output parameters. This is different from parameters in all other SQL statements, which are always input parameters.

Input parameters are used to send values to the procedure. For example, suppose the Parts table has PartID, Description, and Price columns. The InsertPart procedure might have an input parameter for each column in the table. For example:

{call InsertPart(?, ?, ?)}

A driver should not modify the contents of an input buffer until SQLExecDirect or SQLExecute returns SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, SQL_INVALID_HANDLE, or SQL_NO_DATA. The contents of the input buffer should not be modified while SQLExecDirect or SQLExecute returns SQL_NEED_DATA or SQL_STILL_EXECUTING.

Input/output parameters are used both to send values to procedures and retrieve values from procedures. Using the same parameter as both an input and an output parameter tends to be confusing and should be avoided. For example, suppose a procedure accepts an order ID and returns the ID of the customer. This can be defined with a single input/output parameter:

{call GetCustID(?)}

It might be better to use two parameters: an input parameter for the order ID and an output or input/output parameter for the customer ID:

{call GetCustID(?, ?)}

Output parameters are used to retrieve the procedure return value and to retrieve values from procedure arguments; procedures that return values are sometimes known as functions. For example, suppose the GetCustID procedure just mentioned returns a value that indicates whether it was able to find the order. In the following call, the first parameter is an output parameter used to retrieve the procedure return value, the second parameter is an input parameter used to specify the order ID, and the third parameter is an output parameter used to retrieve the customer ID:

{? = call GetCustID(?, ?)}

Drivers handle values for input and input/output parameters in procedures no differently than input parameters in other SQL statements. When the statement is executed, they retrieve the values of the variables bound to these parameters and send them to the data source.

After the statement has been executed, drivers store the returned values of input/output and output parameters in the variables bound to those parameters. These returned values are not guaranteed to be set until after all results returned by the procedure have been fetched and SQLMoreResults has returned SQL_NO_DATA. If executing the statement results in an error, the contents of the input/output parameter buffer or output parameter buffer are undefined.

An application calls SQLProcedure to determine whether a procedure has a return value. It calls SQLProcedureColumns to determine the type (return value, input, input/output, or output) of each procedure parameter.


Arrays of Parameter Values

It is often useful for applications to pass arrays of parameters. For example, using arrays of parameters and a parameterized INSERT statement, an application can insert a number of rows at once. There are several advantages to using arrays. First, network traffic is reduced because the data for many statements is sent in a single packet (if the data source supports parameter arrays natively). Second, some data sources can execute SQL statements using arrays faster than executing the same number of separate SQL statements. Finally, when the data is stored in an array, as is often the case for screen data, the application can bind all of the rows in a particular column with a single call to SQLBindParameter and update them by executing a single statement.

Unfortunately, not many data sources support parameter arrays. However, a driver can emulate parameter arrays by executing an SQL statement once for each set of parameter values. This can lead to increases in speed because the driver can then prepare the statement that it plans to execute once for each parameter set. It might also lead to simpler application code.


Binding Arrays of Parameters

Applications that use arrays of parameters bind the arrays to the parameters in the SQL statement. There are two binding styles:

  • Bind an array to each parameter. Each data structure (array) contains all the data for a single parameter. This is called column-wise binding because it binds a column of values for a single parameter.
  • Define a structure to hold the parameter data for an entire set of parameters and bind an array of these structures. Each data structure contains the data for a single SQL statement. This is called row-wise binding because it binds a row of parameters.

As when the application binds single variables to parameters, it calls SQLBindParameter to bind arrays to parameters. The only difference is that the addresses passed are array addresses, not single-variable addresses. The application sets the SQL_ATTR_PARAM_BIND_TYPE statement attribute to specify whether it is using column-wise (the default) or row-wise binding. Whether to use column-wise or row-wise binding is largely a matter of application preference. Depending on how the processor accesses memory, row-wise binding might be faster. However, the difference is likely to be negligible except for very large numbers of rows of parameters.

Column-Wise Binding

When using column-wise binding, an application binds one or two arrays to each parameter for which data is to be provided. The first array holds the data values, and the second array holds length/indicator buffers. Each array contains as many elements as there are values for the parameter.

Column-wise binding is the default. The application also can change from row-wise binding to column-wise binding by setting the SQL_ATTR_PARAM_BIND_TYPE statement attribute. The following illustration shows how column-wise binding works.

Column-wise Binding of Parameters

For example, the following code binds 10-element arrays to parameters for the PartID, Description, and Price columns, and executes a statement to insert 10 rows. It uses column-wise binding.

#define DESC_LEN 51
#define ARRAY_SIZE 10

SQLCHAR *      Statement = "INSERT INTO Parts (PartID, Description,  Price) "
                                                "VALUES (?, ?, ?)";
SQLUINTEGER    PartIDArray[ARRAY_SIZE];
SQLCHAR        DescArray[ARRAY_SIZE][DESC_LEN];
SQLREAL        PriceArray[ARRAY_SIZE];
SQLINTEGER     PartIDIndArray[ARRAY_SIZE], DescLenOrIndArray[ARRAY_SIZE],
               PriceIndArray[ARRAY_SIZE];
SQLUSMALLINT   i, ParamsProcessed, ParamStatusArray[ARRAY_SIZE];

// Set the SQL_ATTR_PARAM_BIND_TYPE statement attribute to use
// column-wise binding.
SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAMETER_BIND_BY_COLUMN, 0);

// Specify the number of elements in each parameter array.
SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, ARRAY_SIZE, 0);

// Specify an array in which to return the status of each set of
// parameters.
SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_STATUS_PTR, ParamStatusArray, 0);

// Specify an SQLUINTEGER value in which to return the number of sets of
// parameters processed.
SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &ParamsProcessed, 0);

// Bind the parameters in column-wise fashion.
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 5, 0,
                  PartIDArray, 0, PartIDIndArray);
SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, DESC_LEN - 1, 0,
                  DescArray, DESC_LEN, DescLenOrIndArray);
SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_REAL, 7, 0,
                  PriceArray, 0, PriceIndArray);

// Set part ID, description, and price.
for (i = 0; i < ARRAY_SIZE; i++) {
   GetNewValues(&PartIDArray[i], DescArray[i], &PriceArray[i]);
   PartIDIndArray[i] = 0;
   DescLenOrIndArray[i] = SQL_NTS;
   PriceIndArray[i] = 0;
}

// Execute the statement.
SQLExecDirect(hstmt, Statement, SQL_NTS);

// Check to see which sets of parameters were processed successfully.
for (i = 0; i < ParamsProcessed; i++) {
   printf("Parameter Set  Status\n");
   printf("-------------  -------------\n");
   switch (ParamStatusArray[i]) {
      case SQL_PARAM_SUCCESS:
      case SQL_PARAM_SUCCESS_WITH_INFO:
         printf("%13d  Success\n", i);
         break;

      case SQL_PARAM_ERROR:
         printf("%13d  Error\n", i);
         break;

      case SQL_PARAM_UNUSED:
         printf("%13d  Not processed\n", i);
         break;

      case SQL_PARAM_DIAG_UNAVAILABLE:
         printf("%13d  Unknown\n", i);
         break;

   }
}

Row-Wise Binding

When using row-wise binding, an application defines a structure for each set of parameters. The structure contains one or two elements for each parameter. The first element holds the parameter value, and the second element holds the length/indicator buffer. The application then allocates an array of these structures, which contains as many elements as there are values for each parameter.

The application declares the size of the structure to the driver with the SQL_ATTR_PARAM_BIND_TYPE statement attribute. The application binds the addresses of the parameters in the first structure of the array. Thus, the driver can calculate the address of the data for a particular row and column as

Address = Bound Address + ((Row Number - 1) * Structure Size) + Offset

where rows are numbered from 1 to the size of the parameter set. The offset, if defined, is the value pointed to by the SQL_ATTR_PARAM_BIND_OFFSET_PTR statement attribute. The following illustration shows how row-wise binding works. The parameters can be placed in the structure in any order but are shown in sequential order for clarity.

Row-wise Binding of Parameters

The following code creates a structure with elements for the values to store in the PartID, Description, and Price columns. It then allocates a 10-element array of these structures and binds it to parameters for the PartID, Description, and Price columns, using row-wise binding. It then executes a statement to insert 10 rows.

#define DESC_LEN 51
#define ARRAY_SIZE 10

typedef tagPartStruct {
   SQLREAL       Price;
   SQLUINTEGER   PartID;
   SQLCHAR       Desc[DESC_LEN];
   SQLINTEGER    PriceInd;
   SQLINTEGER    PartIDInd;
   SQLINTEGER    DescLenOrInd;
} PartStruct;

PartStruct PartArray[ARRAY_SIZE];
SQLCHAR *      Statement = "INSERT INTO Parts (PartID, Description,
                Price) "
               "VALUES (?, ?, ?)";
SQLUSMALLINT   i, ParamsProcessed, ParamStatusArray[ARRAY_SIZE];

// Set the SQL_ATTR_PARAM_BIND_TYPE statement attribute to use
// column-wise binding.
SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_BIND_TYPE, sizeof(PartStruct), 0);

// Specify the number of elements in each parameter array.
SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, ARRAY_SIZE, 0);

// Specify an array in which to return the status of each set of
// parameters.
SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_STATUS_PTR, ParamStatusArray, 0);

// Specify an SQLUINTEGER value in which to return the number of sets of
// parameters processed.
SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &ParamsProcessed, 0);

// Bind the parameters in row-wise fashion.
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 5, 0,
                  &PartArray[0].PartID, 0, &PartArray[0].PartIDInd);
SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, DESC_LEN - 1, 0,
                  PartArray[0].Desc, DESC_LEN, &PartArray[0].DescLenOrInd);
SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_REAL, 7, 0,
                  &PartArray[0].Price, 0, &PartArray[0].PriceInd);

// Set part ID, description, and price.
for (i = 0; i < ARRAY_SIZE; i++) {
   GetNewValues(&PartArray[i].PartID, PartArray[i].Desc, &PartArray[i].Price);
   PartArray[0].PartIDInd = 0;
   PartArray[0].DescLenOrInd = SQL_NTS;
   PartArray[0].PriceInd = 0;
}

// Execute the statement.
SQLExecDirect(hstmt, Statement, SQL_NTS);

// Check to see which sets of parameters were processed successfully.
for (i = 0; i < ParamsProcessed; i++) {
   printf("Parameter Set  Status\n");
   printf("-------------  -------------\n");
   switch (ParamStatusArray[i]) {
      case SQL_PARAM_SUCCESS:
      case SQL_PARAM_SUCCESS_WITH_INFO:
         printf("%13d  Success\n", i);
         break;

      case SQL_PARAM_ERROR:
         printf("%13d  Error\n", i);
         break;

      case SQL_PARAM_UNUSED:
         printf("%13d  Not processed\n", i);
         break;

      case SQL_PARAM_DIAG_UNAVAILABLE:
         printf("%13d  Unknown\n", i);
         break;

   }


Using Arrays of Parameters

To use arrays of parameters, the application calls SQLSetStmtAttr with an Attribute argument of SQL_ATTR_PARAMSET_SIZE to specify the number of sets of parameters. It calls SQLSetStmtAttr with an Attribute argument of SQL_ATTR_PARAMS_PROCESSED_PTR to specify the address of a variable in which the driver can return the number of sets of parameters processed, including error sets. It calls SQLSetStmtAttr with an Attribute argument of SQL_ATTR_PARAM_STATUS_PTR to point to an array in which to return status information for each row of parameter values. The driver stores these addresses in the structure it maintains for the statement.

Note In ODBC 2.x, SQLParamOptions was called to specify multiple values for a parameter. In ODBC 3.x, the call to SQLParamOptions has been replaced by calls to SQLSetStmtAttr to set the SQL_ATTR_PARAMSET_SIZE and SQL_ATTR_PARAMS_PROCESSED_ARRAY attributes.

Before executing the statement, the application sets the value of each element of each bound array. When the statement is executed, the driver uses the information it stored to retrieve the parameter values and send them to the data source; if possible, the driver should send these values as arrays. Although the use of arrays of parameters is best implemented by executing the SQL statement with all of the parameters in the array with a single call to the data source, this capability is not widely available in DBMSs today. However, drivers can simulate it by executing an SQL statement multiple times, each with a single set of parameters.

Before an application uses arrays of parameters, it must be sure that they are supported by the drivers used by the application. There are two ways to do this:

  • Use only drivers known to support arrays of parameters. The application can hard-code the names of these drivers, or the user can be instructed to use only these drivers. Custom applications and vertical applications commonly use a limited set of drivers.
  • Check for support of arrays of parameters at run time. A driver supports arrays of parameters if it is possible to set the SQL_ATTR_PARAMSET_SIZE statement attribute to a value greater than 1. Generic applications and vertical applications commonly check for support of arrays of parameters at run time.

The availability of row counts and result sets in parameterized execution can be determined by calling SQLGetInfo with the SQL_PARAM_ARRAY_ROW_COUNTS and SQL_PARAM_ARRAY_SELECTS options. For INSERT, UPDATE, and DELETE statements, the SQL_PARAM_ARRAY_ROW_COUNTS option indicates whether individual row counts (one for each parameter set) are available (SQL_PARC_BATCH) or whether row counts are rolled up into one (SQL_PARC_NO_BATCH). For SELECT statements, the SQL_PARAM_ARRAY_SELECTS option indicates whether a result set is available for each set of parameters (SQL_PAS_BATCH) or whether only one result set is available (SQL_PAS_NO_BATCH). If the driver does not allow result set–generating statements to be executed with an array of parameters, SQL_PARAM_ARRAY_SELECTS returns SQL_PAS_NO_SELECT. It is data source–specific whether arrays of parameters can be used with other types of statements, especially because the use of parameters in these statements would be data source–specific and would not follow ODBC SQL grammar.

The array pointed to by the SQL_ATTR_PARAM_OPERATION_PTR statement attribute can be used to ignore rows of parameters. If an element of the array is set to SQL_PARAM_IGNORE, the set of parameters corresponding to that element is excluded from the SQLExecute or SQLExecDirect call. The array pointed to by the SQL_ATTR_PARAM_OPERATION_PTR attribute is allocated and filled in by the application and read by the driver. If fetched rows are used as input parameters, the values of the row status array can be used in the parameter operation array.

Error Processing

If an error occurs while executing the statement, the execution function returns an error and sets the row number variable to the number of the row containing the error. It is data source–specific whether all rows except the error set are executed or whether all rows before (but not after) the error set are executed. Because it processes sets of parameters, the driver sets the buffer specified by the SQL_ATTR_PARAMS_PROCESSED_PTR statement attribute to the number of the row currently being processed. If all sets except the error set are executed, the driver sets this buffer to SQL_ATTR_PARAMSET_SIZE after all rows are processed.

If the SQL_ATTR_PARAM_STATUS_PTR statement attribute has been set, SQLExecute or SQLExecDirect returns the parameter status array, which provides the status of each set of parameters. The parameter status array is allocated by the application and filled in by the driver. Its elements indicate whether the SQL statement was executed successfully for the row of parameters or whether an error occurred while processing the set of parameters. If an error occurred, the driver sets the corresponding value in the parameter status array to SQL_PARAM_ERROR and returns SQL_SUCCESS_WITH_INFO. The application can check the status array to determine which rows were processed. Using the row number, the application can often correct the error and resume processing.

How the parameter status array is used is determined by the SQL_PARAM_ARRAY_ROW_COUNTS and SQL_PARAM_ARRAY_SELECTS options returned by a call to SQLGetInfo. For INSERT, UPDATE, and DELETE statements, the parameter status array is filled in with status information if SQL_PARC_BATCH is returned for SQL_PARAM_ARRAY_ROW_COUNTS, but not if SQL_PARC_NO_BATCH is returned. For SELECT statements, the parameter status array is filled in if SQL_PAS_BATCH is returned for SQL_PARAM_ARRAY_SELECT, but not if SQL_PAS_NO_BATCH or SQL_PAS_NO_SELECT is returned.

Data-at-Execution Parameters

If any of the values in the length/indicator array are SQL_DATA_AT_EXEC or the result of the SQL_LEN_DATA_AT_EXEC(length) macro, the data for those values is sent with SQLPutData in the usual way. The following aspects of this process bear special comment because they are not readily obvious:

  • When the driver returns SQL_NEED_DATA, it must set the address of the row number variable to the row for which it needs data. As in the single-valued case, the application cannot make any assumptions about the order in which the driver will request parameter values within a single set of parameters. If an error occurs in the execution of a data-at-execution parameter, the buffer specified by the SQL_ATTR_PARAMS_PROCESSED_PTR statement attribute is set to the number of the row on which the error occurred, the status for the row in the row status array specified by the SQL_ATTR_PARAM_STATUS_PTR statement attribute is set to SQL_PARAM_ERROR, and the call to SQLExecute, SQLExecDirect, SQLParamData, or SQLPutData returns SQL_ERROR. The contents of this buffer are undefined if SQLExecute, SQLExecDirect, or SQLParamData return SQL_STILL_EXECUTING.
  • Because the driver does not interpret the value in the ParameterValuePtr argument of SQLBindParameter for data-at-execution parameters, if the application provides a pointer to an array, SQLParamData does not extract and return an element of this array to the application. Instead, it returns the scalar value the application had supplied. This means the value returned by SQLParamData is not sufficient to specify the parameter for which the application needs to send data; the application also needs to consider the current row number.

    When only some of the elements of an array of parameters are data-at-execution parameters, the application must pass the address of an array in ParameterValuePtr that contains elements for all the parameters. This array is interpreted normally for the parameters that are not data-at-execution parameters. For the data-at-execution parameters, the value that SQLParamData provides to the application, which normally could be used to identify the data that the driver is requesting on this occasion, is always the address of the array.


Asynchronous Execution

By default, drivers execute ODBC functions synchronously; that is, the application calls a function and the driver does not return control to the application until it has finished executing the function. However, some functions can be executed asynchronously; that is, the application calls the function, and the driver, after minimal processing, returns control to the application. The application can then call other functions while the first function is still executing.

Asynchronous execution is supported for most functions that are largely executed on the data source, such as the functions to prepare and execute SQL statements, retrieve metadata, and fetch data. It is most useful when the task being executed on the data source takes a long time, such as a complex query against a large database.

Note In general, applications should execute functions asynchronously only on single-threaded operating systems. On multithread operating systems, applications should execute functions on separate threads rather than executing them asynchronously on the same thread. No functionality is lost if drivers that operate only on multithread operating systems do not support asynchronous execution.

Asynchronous execution is controlled on either a per-statement or a per-connection basis, depending on the data source. That is, the application specifies not that a particular function is to be executed asynchronously, but that any function executed on a particular statement or a particular connection is to be executed asynchronously. To find out which one is supported, an application calls SQLGetInfo with an option of SQL_ASYNC_MODE. SQL_AM_CONNECTION is returned if connection-level asynchronous execution is supported; SQL_AM_STATEMENT if statement-level asynchronous execution is supported.

To specify that functions executed with a particular statement are to be executed asynchronously, the application calls SQLSetStmtAttr with the SQL_ATTR_ASYNC_ENABLE attribute and sets it to SQL_ASYNC_ENABLE_ON. If connection-level asynchronous processing is supported, the SQL_ATTR_ASYNC_ENABLE statement attribute is read-only and its value is the same as the connection attribute of the connection on which the statement was allocated. It is driver-specific whether the value of the statement attribute is set at statement allocation time or later. Attempting to set it will return SQL_ERROR and SQLSTATE HYC00 (Optional feature not implemented).

To determine the maximum number of active concurrent statements in asynchronous mode that the driver can support on a given connection, the application calls SQLGetInfo with the SQL_MAX_ASYNC_CONCURRENT_STATEMENTS option.

To specify that functions executed with a particular connection are to be executed asynchronously, the application calls SQLSetConnectAttr with the SQL_ATTR_ASYNC_ENABLE attribute and sets it to SQL_ASYNC_ENABLE_ON. All future statement handles allocated on the connection will be enabled for asynchronous execution; it is driver-defined whether existing statement handles will be enabled by this action. If SQL_ATTR_ASYNC_ENABLE is set to SQL_ASYNC_ENABLE_OFF, all statements on the connection are in synchronous mode. An error is returned if asynchronous execution is enabled while there is an active statement on the connection.

When the application executes a function with a statement that is enabled for asynchronous processing, the driver performs a minimal amount of processing (such as checking arguments for errors), hands processing to the data source, and returns control to the application with the SQL_STILL_EXECUTING return code. The application then performs other tasks. To determine when the asynchronous function has finished, the application polls the driver at regular intervals by calling the function with the same arguments as it originally used. If the function is still executing, it returns SQL_STILL_EXECUTING; if it has finished executing, it returns the code it would have returned had it executed synchronously, such as SQL_SUCCESS, SQL_ERROR, or SQL_NEED_DATA. For example:

SQLHSTMT  hstmt1;
SQLRETURN rc;

// Specify that the statement is to be executed asynchronously.
SQLSetStmtAttr(hstmt1, SQL_ATTR_ASYNC_ENABLE, SQL_ASYNC_ENABLE_ON, 0);

// Execute a SELECT statement asynchronously.
while ((rc=SQLExecDirect(hstmt1,"SELECT * FROM Orders",SQL_NTS))==SQL_STILL_EXECUTING) {
   // While the statement is still executing, do something else.
   // Do not use hstmt1, because it is being used asynchronously.
}

// When the statement has finished executing, retrieve the results.

Whether the driver executes the function synchronously or asynchronously is up to the driver. For example, suppose the result set metadata is cached in the driver. In this case, it takes very little time to execute SQLDescribeCol and the driver should simply execute the function rather than artificially delay execution. On the other hand, if the driver needs to retrieve the metadata from the data source, it should return control to the application while it is doing this. Therefore, the application must be able to handle a return code other than SQL_STILL_EXECUTING when it first executes a function asynchronously.

While the function is being executed asynchronously, the application can call functions on any other statements. It can also call functions on any connection except the one associated with the asynchronous statement. On the asynchronous statement, the application can call only the asynchronously executing function, or SQLCancel, SQLGetDiagField, or SQLGetDiagRec. SQLGetDiagField or SQLGetDiagRec can be called on an asynchronously executing statement handle to return a header diagnostic field but not a record diagnostic field. On the connection associated with the asynchronous statement, the application can call only SQLAllocHandle (to allocate a statement handle), SQLGetDiagField, SQLGetDiagRec, or SQLGetFunctions. If the application calls any other function with the asynchronous statement or with the connection associated with that statement, the function returns SQLSTATE HY010 (Function sequence error). The application can call any function using handles other than the original statement handle or the original connection handle. For example:

SQLHDBC       hdbc1, hdbc2;
SQLHSTMT      hstmt1, hstmt2, hstmt3;
SQLCHAR *     SQLStatement = "SELECT * FROM Orders";
SQLUINTEGER   InfoValue;
SQLRETURN     rc;

SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt1);
SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt2);
SQLAllocHandle(SQL_HANDLE_STMT, hdbc2, &hstmt3);

// Specify that hstmt1 is to be executed asynchronously.
SQLSetStmtAttr(hstmt1, SQL_ATTR_ASYNC_ENABLE, SQL_ASYNC_ENABLE_ON, 0);

// Execute hstmt1 asynchronously.
while ((rc = SQLExecDirect(hstmt1, SQLStatement, SQL_NTS)) == SQL_STILL_EXECUTING) {
   // The following calls return HY010 because the previous call to
   // SQLExecDirect is still executing asynchronously on hstmt1. The
   // first call uses hstmt1 and the second call uses hdbc1, on which
   // hstmt1 is allocated.
   SQLExecDirect(hstmt1, SQLStatement, SQL_NTS);                     // Error!
   SQLGetInfo(hdbc1, SQL_UNION, (SQLPOINTER) &InfoValue, 0, NULL);   // Error!

   // The following calls do not return errors. They use a statement
   // handle other than hstmt1 or a connection handle other than hdbc1.
   SQLExecDirect(hstmt2, SQLStatement, SQL_NTS);                     // OK
   SQLTables(hstmt3, NULL, 0, NULL, 0, NULL, 0, NULL, 0);            // OK
   SQLGetInfo(hdbc2, SQL_UNION, (SQLPOINTER) &InfoValue, 0, NULL);   // OK
}

When an application calls a function to determine whether it is still executing asynchronously, it must use the original statement handle. This is because asynchronous execution is tracked on a per-statement basis. The application must also supply valid values for the other arguments—the original arguments will do—to get past error checking in the Driver Manager. However, after the driver checks the statement handle and determines that the statement is executing asynchronously, it ignores all other arguments.

While a function is executing asynchronously—that is, after it has returned SQL_STILL_EXECUTING and before it returns a different code—the application can cancel it by calling SQLCancel with the same statement handle. This is not guaranteed to cancel function execution. For example, the function might have already finished. Furthermore, the code returned by SQLCancel indicates whether SQLCancel successfully attempted to cancel the function, not whether it actually did cancel the function. To determine whether the function was canceled, the application calls the function again. If the function was canceled, it returns SQL_ERROR and SQLSTATE HY008 (Operation canceled). If the function was not canceled, it returns another code, such as SQL_SUCCESS, SQL_STILL_EXECUTING, or SQL_ERROR with a different SQLSTATE.

To disable asynchronous execution of a particular statement when the driver supports statement-level asynchronous processing, the application calls SQLSetStmtAttr with the SQL_ATTR_ASYNC_ENABLE attribute and sets it to SQL_ASYNC_ENABLE_OFF. If the driver supports connection-level asynchronous processing, the application calls SQLSetConnectAttr to set SQL_ATTR_ASYNC_ENABLE to SQL_ASYNC_ENABLE_OFF, which disables asynchronous execution of all statements on the connection.

When SQLGetDiagField is called on a statement handle on which a function is currently executing asynchronously, the following values are returned:

  • What the SQL_DIAG_CURSOR_ROW_COUNT, SQL_DIAG_DYNAMIC_FUNCTION, SQL_DIAG_DYNAMIC_FUNCTION_CODE, and SQL_DIAG_ROW_COUNT header fields return are undefined.
  • The SQL_DIAG_NUMBER header field returns 0.
  • The SQL_DIAG_RETURNCODE header field returns SQL_STILL_EXECUTING.
  • All record fields return SQL_NO_DATA.

SQLGetDiagRec always returns SQL_NO_DATA when it is called on a statement handle on which a function is currently executing asynchronously.

For a list of functions that can be executed asynchronously, see the SQL_ATTR_ASYNC_ENABLE attribute in the SQLSetStmtAttr function description.


Freeing a Statement Handle

As mentioned earlier, it is more efficient to reuse statements than to drop them and allocate new ones. Before executing a new SQL statement on a statement, applications should be sure that the current statement settings are appropriate. These include statement attributes, parameter bindings, and result set bindings. Generally, parameters and result sets for the old SQL statement need to be unbound (by calling SQLFreeStmt with the SQL_RESET_PARAMS and SQL_UNBIND options) and rebound for the new SQL statement.

When the application has finished using the statement, it calls SQLFreeHandle to free the statement. After freeing the statement, it is an application programming error to use the statement's handle in a call to an ODBC function; doing so has undefined but probably fatal consequences.

When SQLFreeHandle is called, the driver releases the structure used to store information about the statement.

SQLDisconnect automatically frees all statements on a connection.


Chapter 10: Retrieving Results (Basic)

A result set is a set of rows on the data source that matches certain criteria. It is a conceptual table that results from a query and that is available to an application in tabular form. SELECT statements, catalog functions, and some procedures create result sets. In the following example, the first SQL statement creates a result set containing all the rows and all the columns in the Orders table, and the second SQL statement creates a result set containing OrderID, SalesPerson, and Status columns for the rows in the Orders table in which the Status is OPEN:

SELECT * FROM Orders
SELECT OrderID, SalesPerson, Status FROM Orders WHERE Status = 'OPEN'

A result set can be empty, which is different from no result set at all. For example, the following SQL statement creates an empty result set:

SELECT * FROM Orders WHERE 1 = 2

An empty result set is no different from any other result set except that it has no rows. For example, the application can retrieve metadata for the result set, can attempt to fetch rows, and must close the cursor over the result set.

The process of retrieving rows from the data source and returning them to the application is called fetching. This chapter explains the basic parts of that process. For information about more advanced topics, such as block and scrollable cursors, see "Block Cursors" and "Scrollable Cursors" in Chapter 11: Retrieving Results (Advanced). For information about updating, deleting, and inserting rows, see Chapter 12: Updating Data.


Was a Result Set Created?

In most situations, application programmers know whether the statements their application executes will create a result set. This is the case if the application uses hard-coded SQL statements written by the programmer. It is usually the case when the application constructs SQL statements at run time: The programmer can easily include code that flags whether a SELECT statement or an INSERT statement is being constructed. In a few situations, the programmer cannot possibly know whether a statement will create a result set. This is true if the application provides a way for the user to enter and execute an SQL statement. It is also true when the application constructs a statement at run time to execute a procedure.

In such cases, the application calls SQLNumResultCols to determine the number of columns in the result set. If this is 0, the statement did not create a result set; if it is any other number, the statement did create a result set.

The application can call SQLNumResultCols at any time after the statement is prepared or executed. However, because some data sources cannot easily describe the result sets that will be created by prepared statements, performance will suffer if SQLNumResultCols is called after a statement is prepared but before it is executed.

Some data sources also support determining the number of rows that an SQL statement returns in a result set. To do so, the application calls SQLRowCount. Exactly what the row count represents is indicated by the setting of the SQL_DYNAMIC_CURSOR_ATTRIBUTES2, SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2, SQL_KEYSET_CURSOR_ATTRIBUTES2, or SQL_STATIC_CURSOR_ATTRIBUTES2 option (depending on the type of the cursor) returned by a call to SQLGetInfo. This bitmask indicates for each cursor type whether the row count returned is exact, approximate, or is not available at all. Whether row counts for static or keyset-driven cursors are affected by changes made through SQLBulkOperations or SQLSetPos, or by positioned update or delete statements, depends on other bits returned by the same option arguments listed previously. For more information, see the SQLGetInfo function description.


Result Set Metadata

Metadata is data that describes other data. For example, result set metadata describes the result set, such as the number of columns in the result set, the data types of those columns, their names, precision, nullability, and so on.

Interoperable applications should always check the metadata of result set columns. The metadata for a column in a result set might differ from the metadata for the column as returned by a catalog function. For example, suppose that an updatable column is included in a result set created by joining two tables. While SQLColumnPrivileges might indicate that a user can update the column, the result set metadata might not if the column is on the "many" side of the join; many data sources can update columns on the "one" side of a join but not on the "many" side. Even data types cannot be assumed to be the same, because the data source might promote the data type while creating the result set.


How Is Metadata Used?

Applications require metadata for most result set operations. For example, the application uses the data type of a column to determine what kind of variable to bind to that column. It uses the byte length of a character column to determine how much space it needs to display data from that column. How an application determines the metadata for a column depends on the type of the application.

Vertical applications work with predefined tables and perform predefined operations on those tables. Because the result set metadata for such applications is defined before the application is even written and is controlled by the application developer, it can be hard-coded into the application. For example, if an order ID column is defined as a 4-byte integer in the data source, the application can always bind a 4-byte integer to that column. When metadata is hard-coded in the application, a change to the tables used by the application generally implies a change to the application code. This is rarely a problem, because such changes are usually made as part of a new release of the application.

Like vertical applications, custom applications generally work with predefined tables and perform predefined operations on those tables. For example, an application might be written to transfer data among three different data sources; the data to be transferred is usually known when the application is written. Thus, custom applications also tend to have hard-coded metadata.

Generic applications, especially those that support ad hoc queries, almost never know the metadata of the result sets they create. Therefore, they must discover the metadata at run time using the functions SQLNumResultCols, SQLDescribeCol, and SQLColAttribute, which are described in the next section, "SQLDescribeCol and SQLColAttribute."

All applications, regardless of their type, can hard-code metadata for the result sets returned by the catalog functions. These result sets are defined in the reference section of this manual.


SQLDescribeCol and SQLColAttribute

SQLDescribeCol and SQLColAttribute are used to retrieve result set metadata. The difference between these two functions is that SQLDescribeCol always returns the same five pieces of information (a column's name, data type, precision, scale, and nullability), while SQLColAttribute returns a single piece of information requested by the application. However, SQLColAttribute can return a much richer selection of metadata, including a column's case-sensitivity, display size, updatability, and searchability.

Many applications, especially ones that only display data, require only the metadata returned by SQLDescribeCol. For these applications, it is faster to use SQLDescribeCol than SQLColAttribute because the information is returned in a single call. Other applications, especially ones that update data, require the additional metadata returned by SQLColAttribute and therefore use both functions. In addition, SQLColAttribute supports driver-specific metadata; for more information, see "Driver-Specific Data Types, Descriptor Types, Information Types, Diagnostic Types, and Attributes" in Chapter 17: Programming Considerations.

An application can retrieve result set metadata at any time after a statement has been prepared or executed and before the cursor over the result set is closed. Very few applications require result set metadata after the statement is prepared and before it is executed. If possible, applications should wait to retrieve metadata until after the statement is executed, because some data sources cannot return metadata for prepared statements and emulating this capability in the driver is often a slow process. For example, the driver might generate a zero-row result set by replacing the WHERE clause of a SELECT statement with the clause WHERE 1 = 2 and executing the resulting statement.

Metadata is often expensive to retrieve from the data source. Because of this, drivers should cache any metadata they retrieve from the server and hold it for as long as the cursor over the result set is open. Also, applications should request only the metadata they absolutely need.


Binding Columns

Data fetched from the data source is returned to the application in variables that the application has allocated for this purpose. Before this can be done, the application must associate, or bind, these variables to the columns of the result set; conceptually, this process is the same as binding application variables to statement parameters. When the application binds a variable to a result set column, it describes that variable—address, data type, and so on—to the driver. The driver stores this information in the structure it maintains for that statement and uses the information to return the value from the column when the row is fetched.


Binding Result Set Columns

Applications can bind as many or as few columns of the result set as they choose, including binding no columns at all. When a row of data is fetched, the driver returns the data for the bound columns to the application. Whether the application binds all of the columns in the result set depends on the application. For example, applications that generate reports usually have a fixed format; such applications create a result set containing all of the columns used in the report and then bind and retrieve the data for all of these columns. Applications that display screens full of data sometimes allow the user to decide which columns to display; such applications create a result set containing all columns the user might want, but bind and retrieve the data only for those columns chosen by the user.

Data can be retrieved from unbound columns by calling SQLGetData. This is commonly called to retrieve long data, which often exceeds the length of a single buffer and must be retrieved in parts.

Columns can be bound at any time, even after rows have been fetched. However, the new bindings do not take effect until the next time a row is fetched; they are not applied to data from rows already fetched.

A variable remains bound to a column until a different variable is bound to the column, until the column is unbound by calling SQLBindCol with a null pointer as the variable's address, until all columns are unbound by calling SQLFreeStmt with the SQL_UNBIND option, or until the statement is released. For this reason, the application must be sure that all bound variables remain valid as long as they are bound. For more information, see "Allocating and Freeing Buffers" in Chapter 4: ODBC Fundamentals.

Because column bindings are just information associated with the statement structure, they can be set in any order. They are also independent of the result set. For example, suppose an application binds the columns of the result set generated by the following SQL statement:

SELECT * FROM Orders

If the application then executes the SQL statement

SELECT * FROM Lines

on the same statement handle, the column bindings for the first result set are still in effect because those are the bindings stored in the statement structure. In most cases, this is a poor programming practice and should be avoided. Instead, the application should call SQLFreeStmt with the SQL_UNBIND option to unbind all the old columns and then bind new ones.


Using SQLBindCol

The application binds columns by calling SQLBindCol. This function binds one column at a time. With it, the application specifies the following:

  • The column number. Column 0 is the bookmark column; this column is not included in some result sets. All other columns are numbered starting with the number 1. It is an error to bind a higher-numbered column than there are columns in the result set; this error cannot be detected until the result set has been created, so it is returned by SQLFetch, not SQLBindCol.
  • The C data type, address, and byte length of the variable bound to the column. It is an error to specify a C data type to which the SQL data type of the column cannot be converted; this error might not be detected until the result set has been created, so it is returned by SQLFetch, not SQLBindCol. For a list of supported conversions, see Converting Data from SQL to C Data Types in Appendix D: Data Types. For information about the byte length, see "Data Buffer Length" in Chapter 4: ODBC Fundamentals.
  • The address of a length/indicator buffer. The length/indicator buffer is optional. It is used to return the byte length of binary or character data or return SQL_NULL_DATA if the data is NULL. For more information, see "Using Length/Indicator Values" in Chapter 4: ODBC Fundamentals.

When SQLBindCol is called, the driver associates this information with the statement. When each row of data is fetched, it uses the information to place the data for each column in the bound application variables.

For example, the following code binds variables to the SalesPerson and CustID columns. Data for the columns will be returned in SalesPerson and CustID. Because SalesPerson is a character buffer, the application specifies its byte length (11) so that the driver can determine whether to truncate the data. The byte length of the returned title, or whether it is NULL, will be returned in SalesPersonLenOrInd.

Because CustID is an integer variable and has fixed length, there is no need to specify its byte length; the driver assumes it is sizeof(SQLUINTEGER). The byte length of the returned customer ID data, or whether it is NULL, will be returned in CustIDInd. Note that the application is interested only in whether the salary is NULL, because the byte length is always sizeof(SQLUINTEGER).

SQLCHAR       SalesPerson[11];
SQLUINTEGER   CustID;
SQLINTEGER    SalesPersonLenOrInd, CustIDInd;
SQLRETURN     rc;
SQLHSTMT      hstmt;

// Bind SalesPerson to the SalesPerson column and CustID to the
// CustID column.
SQLBindCol(hstmt, 1, SQL_C_CHAR, SalesPerson, sizeof(SalesPerson),
            &SalesPersonLenOrInd);
SQLBindCol(hstmt, 2, SQL_C_ULONG, &CustID, 0, &CustIDInd);

// Execute a statement to get the sales person/customer of all orders.
SQLExecDirect(hstmt, "SELECT SalesPerson, CustID FROM Orders ORDER BY SalesPerson",
               SQL_NTS);

// Fetch and print the data. Print "NULL" if the data is NULL. Code to
// check if rc equals SQL_ERROR or SQL_SUCCESS_WITH_INFO not shown.
while ((rc = SQLFetch(hstmt)) != SQL_NO_DATA) {
   if (SalesPersonLenOrInd == SQL_NULL_DATA)
            printf("NULL                     ");
   else
            printf("%10s   ", SalesPerson);
   if (CustIDInd == SQL_NULL_DATA)
         printf("NULL\n");
   else
            printf("%d\n", CustID);
}

// Close the cursor.
SQLCloseCursor(hstmt);

The following code executes a SELECT statement entered by the user and prints each row of data in the result set. Because the application cannot predict the shape of the result set created by the SELECT statement, it cannot bind hard-coded variables to the result set as in the preceding example. Instead, the application allocates a buffer that holds the data and a length/indicator buffer for each column in that row. For each column, it calculates the offset to the start of the memory for the column and adjusts this offset so that the data and length/indicator buffers for the column start on alignment boundaries. It then binds the memory starting at the offset to the column. From the driver's point of view, the address of this memory is indistinguishable from the address of a variable bound in the preceding example. For more information about alignment, see "Alignment" in Chapter 17: Programming Considerations.

// This application allocates a buffer at run time. For each column, this
// buffer contains memory for the column's data and length/indicator.
// For example:
//      column 1         column 2      column 3      column 4
// <------------><---------------><-----><------------>
//      db1   li1   db2   li2   db3   li3   db4   li4
//      |      |      |      |      |      |      |         |
//      _____V_____V________V_______V___V___V______V_____V_
// |__________|__|_____________|__|___|__|__________|__|
//
// dbn = data buffer for column n
// lin = length/indicator buffer for column n

// Define a macro to increase the size of a buffer so that it is a
// multiple of the alignment size. Thus, if a buffer starts on an
// alignment boundary, it will end just before the next alignment
// boundary. In this example, an alignment size of 4 is used because
// this is the size of the largest data type used in the application's
// buffer--the size of an SDWORD and of the largest default C data type
// are both 4. If a larger data type (such as _int64) was used, it would
// be necessary to align for that size.
#define ALIGNSIZE 4
#define ALIGNBUF(Length) Length % ALIGNSIZE ? \
                  Length + ALIGNSIZE - (Length % ALIGNSIZE) : Length

SQLCHAR        SelectStmt[100];
SQLSMALLINT    NumCols, *CTypeArray, i;
SQLINTEGER *   ColLenArray, *OffsetArray, SQLType, *DataPtr;
SQLRETURN      rc;
SQLHSTMT       hstmt;

// Get a SELECT statement from the user and execute it.
GetSelectStmt(SelectStmt, 100);
SQLExecDirect(hstmt, SelectStmt, SQL_NTS);

// Determine the number of result set columns. Allocate arrays to hold
// the C type, byte length, and buffer offset to the data.
SQLNumResultCols(hstmt, &NumCols);
CTypeArray = (SQLSMALLINT *) malloc(NumCols * sizeof(SQLSMALLINT));
ColLenArray = (SQLINTEGER *) malloc(NumCols * sizeof(SQLINTEGER));
OffsetArray = (SQLINTEGER *) malloc(NumCols * sizeof(SQLINTEGER));

OffsetArray[0] = 0;
for (i = 0; i < NumCols; i++) {
   // Determine the column's SQL type. GetDefaultCType contains a switch
   // statement that returns the default C type for each SQL type.
   SQLColAttribute(hstmt, ((SQLUSMALLINT) i) + 1, SQL_DESC_TYPE, NULL, 0, NULL, (SQLPOINTER) &SQLType);
   CTypeArray[i] = GetDefaultCType(SQLType);

   // Determine the column's byte length. Calculate the offset in the
   // buffer to the data as the offset to the previous column, plus the
   // byte length of the previous column, plus the byte length of the
   // previous column's length/indicator buffer. Note that the byte
   // length of the column and the length/indicator buffer are increased
   // so that, assuming they start on an alignment boundary, they will
   // end on the byte before the next alignment boundary. Although this
   // might leave some holes in the buffer, it is a relatively
   // inexpensive way to guarantee alignment.
   SQLColAttribute(hstmt, ((SQLUSMALLINT) i)+1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &ColLenArray[i]);
   ColLenArray[i] = ALIGNBUF(ColLenArray[i]);
   if (i)
      OffsetArray[i] = OffsetArray[i-1]+ColLenArray[i-1]+ALIGNBUF(sizeof(SQLINTEGER));
}

// Allocate the data buffer. The size of the buffer is equal to the
// offset to the data buffer for the final column, plus the byte length
// of the data buffer and length/indicator buffer for the last column.
void *DataPtr = malloc(OffsetArray[NumCols - 1] +
               ColLenArray[NumCols - 1] + ALIGNBUF(sizeof(SQLINTEGER)));

// For each column, bind the address in the buffer at the start of the
// memory allocated for that column's data and the address at the start
// of the memory allocated for that column's length/indicator buffer.
for (i = 0; i < NumCols; i++)
   SQLBindCol(hstmt,
            ((SQLUSMALLINT) i) + 1,
            CTypeArray[i],
            (SQLPOINTER)((SQLCHAR *)DataPtr + OffsetArray[i]),
            ColLenArray[i],
            (SQLINTEGER *)((SQLCHAR *)DataPtr + OffsetArray[i] + ColLenArray[i]));

// Retrieve and print each row. PrintData accepts a pointer to the data,
// its C type, and its byte length/indicator. It contains a switch
// statement that casts and prints the data according to its type. Code
// to check if rc equals SQL_ERROR or SQL_SUCCESS_WITH_INFO not shown.
while ((rc = SQLFetch(hstmt)) != SQL_NO_DATA) {
   for (i = 0; i < NumCols; i++) {
      PrintData((SQLCHAR *)DataPtr[OffsetArray[i]], CTypeArray[i],
               (SQLINTEGER *)((SQLCHAR *)DataPtr[OffsetArray[i] + ColLenArray[i]]));
   }
}

// Close the cursor.
SQLCloseCursor(hstmt);


Fetching Data

The process of retrieving rows from the result set and returning them to the application is called fetching. This section describes how to fetch data.


Cursors

An application fetches data with a cursor. A cursor is different from a result set: A result set is the set of rows that matches a particular search criteria, while a cursor is the software that returns those rows to the application. The name cursor, as it applies to databases, probably originated from the blinking cursor on a computer terminal. Just as that cursor indicates the current position on the screen and where the typed words will appear next, a cursor on a result set indicates the current position in the result set and what row will be returned next.

The cursor model in ODBC is based on the cursor model in embedded SQL. One notable difference between these models is the way cursors are opened. In embedded SQL, a cursor must be explicitly declared and opened before it can be used. In ODBC, a cursor is implicitly opened when a statement that creates a result set is executed. When the cursor is opened, it is positioned before the first row of the result set. In both embedded SQL and ODBC, a cursor must be closed after the application has finished using it.

Different cursors have different characteristics. The most common type of cursor, which is called a forward-only cursor, can only move forward through the result set. To return to a previous row, the application must close and reopen the cursor and then read rows from the beginning of the result set until it reaches the required row. Forward-only cursors provide a fast mechanism for making a single pass through a result set.

Forward-only cursors are less useful for screen-based applications, in which the user scrolls backward and forward through the data. Such applications can use a forward-only cursor by reading the result set once, caching the data locally, and performing scrolling themselves. However, this works well only with small amounts of data. A better solution is to use a scrollable cursor, which provides random access to the result set. Such applications can also increase performance by fetching more than one row of data at a time, using what is called a block cursor. For more information on block cursors, see "Using Block Cursors," in Chapter 11: Retrieving Results (Advanced).

The forward-only cursor is the default cursor type in ODBC and is discussed in the following sections. For more information about block cursors and scrollable cursors, see "Block Cursors" and "Scrollable Cursors" in Chapter 11: Retrieving Results (Advanced).

Important Committing or rolling back a transaction, either by explicitly calling SQLEndTran or by operating in auto-commit mode, causes some data sources to close all the cursors on all statements on a connection. For more information, see the SQL_CURSOR_COMMIT_BEHAVIOR and SQL_CURSOR_ROLLBACK_BEHAVIOR attributes in the SQLGetInfo function description.


Fetching a Row of Data

To fetch a row of data, an application calls SQLFetch. SQLFetch can be called with any kind of cursor, but it only moves the rowset cursor in a forward-only direction. SQLFetch advances the cursor to the next row and returns the data for any columns that were bound with calls to SQLBindCol. When the cursor reaches the end of the result set, SQLFetch returns SQL_NO_DATA. For examples of calling SQLFetch, see "Using SQLBindCol," earlier in this chapter.

Exactly how SQLFetch is implemented is driver-specific, but the general pattern is for the driver to retrieve the data for any bound columns from the data source, convert it according to the types of the bound variables, and place the converted data in those variables. If the driver cannot convert any data, SQLFetch returns an error. The application can continue fetching rows, but the data for the current row is lost. What happens to the data for unbound columns depends on the driver, but most drivers either retrieve and discard it or never retrieve it at all.

The driver also sets the values of any length/indicator buffers that have been bound. If the data value for a column is NULL, the driver sets the corresponding length/indicator buffer to SQL_NULL_DATA. If the data value is not NULL, the driver sets the length/indicator buffer to the byte length of the data after conversion. If this length cannot be determined, as is sometimes the case with long data that is retrieved by more than one function call, the driver sets the length/indicator buffer to SQL_NO_TOTAL. For fixed-length data types, such as integers and date structures, the byte length is the size of the data type.

For variable-length data, such as character and binary data, the driver checks the byte length of the converted data against the byte length of the buffer bound to the column; the buffer's length is specified in the BufferLength argument in SQLBindCol. If the byte length of the converted data is greater than the byte length of the buffer, the driver truncates the data to fit in the buffer, returns the untruncated length in the length/indicator buffer, returns SQL_SUCCESS_WITH_INFO, and places SQLSTATE 01004 (Data truncated) in the diagnostics. The only exception to this is if a variable-length bookmark is truncated when returned by SQLFetch, which returns SQLSTATE 22001 (String data, right truncated).

Fixed-length data is never truncated, because the driver assumes that the size of the bound buffer is the size of the data type. Data truncation tends to be rare, because the application usually binds a buffer large enough to hold the entire data value; it determines the necessary size from the metadata. However, the application might explicitly bind a buffer it knows to be too small. For example, it might retrieve and display the first 20 characters of a part description or the first 100 characters of a long text column.

Character data must be null-terminated by the driver before it is returned to the application, even if it has been truncated. The null-termination character is not included in the returned byte length but does require space in the bound buffer. For example, suppose an application uses strings composed of character data in the ASCII character set, a driver has 50 characters of data to return, and the application's buffer is 25 bytes long. In the application's buffer, the driver returns the first 24 characters followed by a null-termination character. In the length/indicator buffer, it returns a byte length of 50.

The application can restrict the number of rows in the result set by setting the SQL_ATTR_MAX_ROWS statement attribute before executing the statement that creates the result set. For example, the preview mode in an application used to format reports needs only enough data to display the first page of the report. By restricting the size of the result set, such a feature would run faster. This statement attribute is intended to reduce network traffic and might not be supported by all drivers.


Getting Long Data

DBMSs define long data as any character or binary data over a certain size, such as 255 characters. This data may be small enough to be stored in a single buffer, such as a part description of several thousand characters. However, it might be too long to store in memory, such as long text documents or bitmaps. Because such data cannot be stored in a single buffer, it is retrieved from the driver in parts with SQLGetData after the other data in the row has been fetched.

Note An application can actually retrieve any type of data with SQLGetData, not just long data, although only character and binary data can be retrieved in parts. However, if the data is small enough to fit in a single buffer, there is generally no reason to use SQLGetData. It is much easier to bind a buffer to the column and let the driver return the data in the buffer.

To retrieve long data from a column, an application first calls SQLFetchScroll or SQLFetch to move to a row and fetch the data for bound columns. The application then calls SQLGetData. SQLGetData has the same arguments as SQLBindCol: a statement handle; a column number; the C data type, address, and byte length of an application variable; and the address of a length/indicator buffer. Both functions have the same arguments because they perform essentially the same task: They both describe an application variable to the driver and specify that the data for a particular column should be returned in that variable. The major differences are that SQLGetData is called after a row is fetched (and is sometimes called late binding for this reason) and that the binding specified by SQLGetData lasts only for the duration of the call.

With respect to a single column, SQLGetData behaves in the same manner as SQLFetch: It retrieves the data for the column, converts it to the type of the application variable, and returns it in that variable. It also returns the byte length of the data in the length/indicator buffer. For more information on how SQLFetch returns data, see the immediately preceding section, "Fetching a Row of Data."

SQLGetData differs from SQLFetch in one important respect. If it is called more than once in succession for the same column, each call returns a successive part of the data. Each call except the last call returns SQL_SUCCESS_WITH_INFO and SQLSTATE 01004 (String data, right truncated); the last call returns SQL_SUCCESS. This is how SQLGetData is used to retrieve long data in parts. When there is no more data to return, SQLGetData returns SQL_NO_DATA. The application is responsible for putting the long data together, which might mean concatenating the parts of the data. Each part is null-terminated; the application must remove the null-termination character if concatenating the parts. Retrieving data in parts can be done for variable-length bookmarks as well as for other long data. The value returned in the length/indicator buffer decreases in each call by the number of bytes returned in the previous call, although it is common for the driver to be unable to discover the amount of available data and return a byte length of SQL_NO_TOTAL. For example:

// Declare a binary buffer to retrieve 5000 bytes of data at a time.
SQLCHAR       BinaryPtr[5000];
SQLUINTEGER   PartID;
SQLINTEGER    PartIDInd, BinaryLenOrInd, NumBytes;
SQLRETURN     rc;
SQLHSTMT      hstmt;

// Create a result set containing the ID and picture of each part.
SQLExecDirect(hstmt, "SELECT PartID, Picture FROM Pictures", SQL_NTS);

// Bind PartID to the PartID column.
SQLBindCol(hstmt, 1, SQL_C_ULONG, &PartID, 0, &PartIDInd);

// Retrieve and display each row of data.
while ((rc = SQLFetch(hstmt)) != SQL_NO_DATA) {
   // Display the part ID and initialize the picture.
   DisplayID(PartID, PartIDInd);
   InitPicture();

   // Retrieve the picture data in parts. Send each part and the number
   // of bytes in each part to a function that displays it. The number
   // of bytes is always 5000 if there were more than 5000 bytes
   // available to return (cbBinaryBuffer > 5000). Code to check if
   // rc equals SQL_ERROR or SQL_SUCCESS_WITH_INFO not shown.
   while ((rc = SQLGetData(hstmt, 2, SQL_C_BINARY, BinaryPtr, sizeof(BinaryPtr),
                           &BinaryLenOrInd)) != SQL_NO_DATA) {
      NumBytes = (BinaryLenOrInd > 5000) || (BinaryLenOrInd == SQL_NO_TOTAL) ?
                  5000 : BinaryLenOrInd;
      DisplayNextPictPart(BinaryPtr, NumBytes);
   }
}

// Close the cursor.
SQLCloseCursor(hstmt);

There are a number of restrictions on using SQLGetData. In general, columns accessed with SQLGetData:

  • Must be accessed in order of increasing column number (because of the way the columns of a result set are read from the data source). For example, it is an error to call SQLGetData for column 5 and then call it for column 4.
  • Cannot be bound.
  • Must have a higher column number than the last bound column. For example, if the last bound column is column 3, it is an error to call SQLGetData for column 2. For this reason, applications should be careful to place long data columns at the end of the select list.
  • Cannot be used if SQLFetch or SQLFetchScroll was called to retrieve more than one row. For more information, see "Using Block Cursors" in Chapter 11: Retrieving Results (Advanced).

Some drivers do not enforce these restrictions. Interoperable applications should either assume they exist or determine which restrictions are not enforced by calling SQLGetInfo with the SQL_GETDATA_EXTENSIONS option.

If the application does not need all of the data in a character or binary data column, it can reduce network traffic in DBMS-based drivers by setting the SQL_ATTR_MAX_LENGTH statement attribute before executing the statement. This restricts the number of bytes of data that will be returned for any character or binary column. For example, suppose a column contains long text documents. An application that browses the table containing this column might need to display only the first page of each document. Although this statement attribute can be simulated in the driver, there is no reason to do so. In particular, if an application wants to truncate character or binary data, it should bind a small buffer to the column with SQLBindCol and let the driver truncate the data.


Closing the Cursor

When an application has finished using a cursor, it calls SQLCloseCursor to close the cursor. For example:

SQLCloseCursor(hstmt);

Until the application closes the cursor, the statement on which the cursor is opened cannot be used for most other operations, such as executing another SQL statement. For a complete list of functions that can be called while a cursor is open, see Appendix B: ODBC State Transition Tables.

Note To close a cursor, an application should call SQLCloseCursor, not SQLCancel.

Cursors remain open until they are explicitly closed, except when a transaction is committed or rolled back, in which case some data sources close the cursor. In particular, reaching the end of the result set, when SQLFetch returns SQL_NO_DATA, does not close a cursor. Even cursors on empty result sets (result sets created when a statement executed successfully but which returned no rows) must be explicitly closed.


Chapter 11: Retrieving Results (Advanced)

An application can specify that an offset is added to bound data buffer addresses and the corresponding length/indicator buffer addresses when SQLBulkOperations, SQLFetch, SQLFetchScroll, or SQLSetPos is called. The results of these additions determine the addresses used in these operations.

Bind offsets allow an application to change bindings without calling SQLBindCol for previously bound columns. A call to SQLBindCol to rebind data changes the buffer address and the length/indicator pointer. Rebinding with an offset, on the other hand, simply adds an offset to the existing bound data buffer address and length/indicator buffer address. When offsets are used, the bindings are a "template" of how the application buffers are laid out and the application can move this "template" to different areas of memory by changing the offset. A new offset can be specified at any time and is always added to the originally bound values.

To specify a bind offset, the application sets the SQL_ATTR_ROW_BIND_OFFSET_PTR statement attribute to the address of an SQLINTEGER buffer. Before the application calls a function that uses the bindings, such as SQLBulkOperations, SQLFetch, SQLFetchScroll, or SQLSetPos, it places an offset in bytes in this buffer, as long as neither the data buffer address nor the length/indicator buffer address is 0, and as long as the bound column is in the result set. The sum of the address and the offset must be a valid address. (This means that either or both the offset and the address to which the offset is added can be invalid, as long as their sum is a valid address.) The SQL_ATTR_ROW_BIND_OFFSET_PTR statement attribute is a pointer so that the offset value can be applied to more than one set of binding data, all of which can be changed by changing one offset value. An application must make sure that the pointer remains valid until the cursor is closed.

Note Binding offsets are not supported by ODBC 2.x drivers.


Block Cursors

Many applications spend a significant amount of time bringing data across the network. Part of this time is spent actually bringing the data across the network, and part of it is spent on network overhead, such as the call made by the driver to request a row of data. The latter time can be reduced if the application makes efficient use of block, or fat, cursors, which can return more than one row at a time.

An application always has the option of using a block cursor. On data sources from which only one row at a time can be fetched, block cursors must be simulated in the driver. This can be done by performing multiple single-row fetches. While this is unlikely to provide any performance gains, it opens opportunities for applications. Such applications will then experience performance increases as DBMSs implement block cursors natively and the drivers associated with those DBMSs expose them.

The rows returned in a single fetch with a block cursor are called the rowset. It is important not to confuse the rowset with the result set. The result set is maintained at the data source, while the rowset is maintained in application buffers. While the result set is fixed, the rowset is not—it changes position and contents each time a new set of rows is fetched. Just as a single-row cursor such as the traditional SQL forward-only cursor points to a current row, a block cursor points to the rowset, which can be thought of as current rows.

To perform operations that operate on a single row when multiple rows have been fetched, the application must first indicate which row is the current row. The current row is required by calls to SQLGetData and positioned update and delete statements. When a block cursor first returns a rowset, the current row is the first row of the rowset. To change the current row, the application calls SQLSetPos or SQLBulkOperations (to update by bookmark). The following illustration shows the relationship of the result set, rowset, current row, rowset cursor, and block cursor. For more information, see "Using Block Cursors," later in this chapter, and "Positioned Update and Delete Statements" and "Updating Data with SQLSetPos" in Chapter 12: Updating Data.

Whether a cursor is a block cursor is independent of whether it is scrollable. For example, most of the work in a report application is spent retrieving and printing rows. Because of this, it will work fastest with a forward-only, block cursor. It uses a forward-only cursor to avoid the expense of a scrollable cursor, and a block cursor to reduce the network traffic.


Binding Columns for Use with Block Cursors

Because block cursors return multiple rows, applications that use them must bind an array of variables to each column instead of a single variable. These arrays are collectively known as the rowset buffers. Following are the two styles of binding:

  • Bind an array to each column. This is called column-wise binding because each data structure (array) contains data for a single column.
  • Define a structure to hold the data for an entire row and bind an array of these structures. This is called row-wise binding because each data structure contains the data for a single row.

As when the application binds single variables to columns, it calls SQLBindCol to bind arrays to columns. The only difference is that the addresses passed are array addresses, not single variable addresses. The application sets the SQL_ATTR_ROW_BIND_TYPE statement attribute to specify whether it is using column-wise or row-wise binding. Whether to use column-wise or row-wise binding is largely a matter of application preference. Row-wise binding might correspond more closely to the application's layout of data, in which case it would provide better performance.


Column-Wise Binding

When using column-wise binding, an application binds one or two, or in some cases three, arrays to each column for which data is to be returned. The first array holds the data values, and the second array holds length/indicator buffers. Indicators and length values can be stored in separate buffers by setting the SQL_DESC_INDICATOR_PTR and SQL_DESC_OCTET_LENGTH_PTR descriptor fields to different values; if this is done, a third array is bound. Each array contains as many elements as there are rows in the rowset.

The application declares that it is using column-wise binding with the SQL_ATTR_ROW_BIND_TYPE statement attribute, which determines the bind type for rowset buffers as opposed to parameter set buffers. The driver returns the data for each row in successive elements of each array. The following illustration shows how column-wise binding works.

For example, the following code binds 10-element arrays to the OrderID, SalesPerson, and Status columns:

#define ROW_ARRAY_SIZE 10

SQLUINTEGER    OrderIDArray[ROW_ARRAY_SIZE], NumRowsFetched;
SQLCHAR        SalesPersonArray[ROW_ARRAY_SIZE][11],
               StatusArray[ROW_ARRAY_SIZE][7];
SQLINTEGER     OrderIDIndArray[ROW_ARRAY_SIZE],
               SalesPersonLenOrIndArray[ROW_ARRAY_SIZE],
               StatusLenOrIndArray[ROW_ARRAY_SIZE];
SQLUSMALLINT   RowStatusArray[ROW_ARRAY_SIZE], i;
SQLRETURN      rc;
SQLHSTMT       hstmt;

// Set the SQL_ATTR_ROW_BIND_TYPE statement attribute to use
// column-wise binding. Declare the rowset size with the
// SQL_ATTR_ROW_ARRAY_SIZE statement attribute. Set the
// SQL_ATTR_ROW_STATUS_PTR statement attribute to point to the
// row status array. Set the SQL_ATTR_ROWS_FETCHED_PTR statement
// attribute to point to cRowsFetched.
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0);
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, ROW_ARRAY_SIZE, 0);
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, RowStatusArray, 0);
SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &NumRowsFetched, 0);

// Bind arrays to the OrderID, SalesPerson, and Status columns.
SQLBindCol(hstmt, 1, SQL_C_ULONG, OrderIDArray, 0, OrderIDIndArray);
SQLBindCol(hstmt, 2, SQL_C_CHAR, SalesPersonArray, sizeof(SalesPersonArray[0]),
            SalesPersonLenOrIndArray);
SQLBindCol(hstmt, 3, SQL_C_CHAR, StatusArray, sizeof(StatusArray[0]),
            StatusLenOrIndArray);

// Execute a statement to retrieve rows from the Orders table.
SQLExecDirect(hstmt, "SELECT OrderID, SalesPerson, Status FROM Orders", SQL_NTS);

// Fetch up to the rowset size number of rows at a time. Print the actual
// number of rows fetched; this number is returned in NumRowsFetched.
// Check the row status array to print only those rows successfully
// fetched. Code to check if rc equals SQL_SUCCESS_WITH_INFO or
// SQL_ERROR not shown.
while ((rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0)) != SQL_NO_DATA) {
   for (i = 0; i < NumRowsFetched; i++) {
      if ((RowStatusArray[i] == SQL_ROW_SUCCESS) ||
            (RowStatusArray[i] == SQL_ROW_SUCCESS_WITH_INFO)) {
         if (OrderIDIndArray[i] == SQL_NULL_DATA)
            printf(" NULL      ");
         else
            printf("%d\t", OrderIDArray[i]);
         if (SalesPersonLenOrIndArray[i] == SQL_NULL_DATA)
            printf(" NULL      ");
         else
            printf("%s\t", SalesPersonArray[i]);
         if (StatusLenOrIndArray[i] == SQL_NULL_DATA)
            printf(" NULL\n");
         else
            printf("%s\n", StatusArray[i]);
      }
   }
}

// Close the cursor.
SQLCloseCursor(hstmt);


Row-Wise Binding

When using row-wise binding, an application defines a structure containing one or two, or in some cases three, elements for each column for which data is to be returned. The first element holds the data value, and the second element holds the length/indicator buffer. Indicators and length values can be stored in separate buffers by setting the SQL_DESC_INDICATOR_PTR and SQL_DESC_OCTET_LENGTH_PTR descriptor fields to different values; if this is done, the structure contains a third element. The application then allocates an array of these structures, which contains as many elements as there are rows in the rowset.

The application declares the size of the structure to the driver with the SQL_ATTR_ROW_BIND_TYPE statement attribute and binds the address of each member in the first element of the array. Thus, the driver can calculate the address of the data for a particular row and column as

Address = Bound Address + ((Row Number - 1) * Structure Size)

where rows are numbered from 1 to the size of the rowset. (One is subtracted from the row number because array indexing in C is zero-based.) The following illustration shows how row-wise binding works. Generally, only columns that will be bound are included in the structure. The structure can contain fields that are unrelated to result set columns. The columns can be placed in the structure in any order but are shown in sequential order for clarity.

For example, the following code creates a structure with elements in which to return data for the OrderID, SalesPerson, and Status columns, and length/indicators for the SalesPerson and Status columns. It allocates 10 of these structures and binds them to the OrderID, SalesPerson, and Status columns.

#define ROW_ARRAY_SIZE 10

// Define the ORDERINFO struct and allocate an array of 10 structs.
typedef struct {
   SQLUINTEGER   OrderID;
   SQLINTEGER    OrderIDInd;
   SQLCHAR       SalesPerson[11];
   SQLINTEGER    SalesPersonLenOrInd;
   SQLCHAR       Status[7];
   SQLINTEGER    StatusLenOrInd;
} ORDERINFO;
ORDERINFO OrderInfoArray[ROW_ARRAY_SIZE];

SQLUINTEGER    NumRowsFetched;
SQLUSMALLINT   RowStatusArray[ROW_ARRAY_SIZE], i;
SQLRETURN      rc;
SQLHSTMT       hstmt;

// Specify the size of the structure with the SQL_ATTR_ROW_BIND_TYPE
// statement attribute. This also declares that row-wise binding will
// be used. Declare the rowset size with the SQL_ATTR_ROW_ARRAY_SIZE
// statement attribute. Set the SQL_ATTR_ROW_STATUS_PTR statement
// attribute to point to the row status array. Set the
// SQL_ATTR_ROWS_FETCHED_PTR statement attribute to point to
// NumRowsFetched.
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_TYPE, sizeof(ORDERINFO), 0);
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, ROW_ARRAY_SIZE, 0);
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, RowStatusArray, 0);
SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &NumRowsFetched, 0);

// Bind elements of the first structure in the array to the OrderID,
// SalesPerson, and Status columns.
SQLBindCol(hstmt, 1, SQL_C_ULONG, &OrderInfoArray[0].OrderID, 0, &OrderInfoArray[0].OrderIDInd);
SQLBindCol(hstmt, 2, SQL_C_CHAR, OrderInfoArray[0].SalesPerson,
            sizeof(OrderInfoArray[0].SalesPerson),
            &OrderInfoArray[0].SalesPersonLenOrInd);
SQLBindCol(hstmt, 3, SQL_C_CHAR, OrderInfoArray[0].Status,
            sizeof(OrderInfoArray[0].Status), &OrderInfoArray[0].StatusLenOrInd);

// Execute a statement to retrieve rows from the Orders table.
SQLExecDirect(hstmt, "SELECT OrderID, SalesPerson, Status FROM Orders", SQL_NTS);

// Fetch up to the rowset size number of rows at a time. Print the actual
// number of rows fetched; this number is returned in NumRowsFetched.
// Check the row status array to print only those rows successfully
// fetched. Code to check if rc equals SQL_SUCCESS_WITH_INFO or
// SQL_ERRORnot shown.
while ((rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0)) != SQL_NO_DATA) {
   for (i = 0; i < NumRowsFetched; i++) {
      if (RowStatusArray[i] == SQL_ROW_SUCCESS|| RowStatusArray[i] ==
         SQL_ROW_SUCCESS_WITH_INFO) {
         if (OrderInfoArray[i].OrderIDInd == SQL_NULL_DATA)
            printf(" NULL      ");
         else
            printf("%d\t", OrderInfoArray[i].OrderID);
         if (OrderInfoArray[i].SalesPersonLenOrInd == SQL_NULL_DATA)
            printf(" NULL      ");
         else
            printf("%s\t", OrderInfoArray[i].SalesPerson);
         if (OrderInfoArray[i].StatusLenOrInd == SQL_NULL_DATA)
            printf(" NULL\n");
         else
            printf("%s\n", OrderInfoArray[i].Status);
      }
   }
}

// Close the cursor.
SQLCloseCursor(hstmt);


Using Block Cursors

Support for block cursors is built into ODBC 3.x. SQLFetch can be used only for multirow fetches when called in ODBC 3.x; if an ODBC 2.x application calls SQLFetch, it will open only a single-row, forward-only cursor. When an ODBC 3.x application calls SQLFetch in an ODBC 2.x driver, it returns a single row unless the driver supports SQLExtendedFetch. For more information, see "Block Cursors, Scrollable Cursors, and Backward Compatibility" in Appendix G: Driver Guidelines for Backward Compatibility.

To use block cursors, the application sets the rowset size, binds the rowset buffers (as described in the previous section), optionally sets the SQL_ATTR_ROWS_FETCHED_PTR and SQL_ATTR_ROW_STATUS_PTR statement attributes, and calls SQLFetch or SQLFetchScroll to fetch a block of rows. The application can change the rowset size and bind new rowset buffers (by calling SQLBindCol or specifying a bind offset) even after rows have been fetched.


Rowset Size

Which rowset size to use depends on the application. Screen-based applications commonly follow one of two strategies. The first is to set the rowset size to the number of rows displayed on the screen; if the user resizes the screen, the application changes the rowset size accordingly. The second is to set the rowset size to a larger number, such as 100, which reduces the number of calls to the data source. The application scrolls locally within the rowset when possible and fetches new rows only when it scrolls outside the rowset.

Other applications, such as reports, tend to set the rowset size to the largest number of rows the application can reasonably handle—with a larger rowset, the network overhead per row is sometimes reduced. Exactly how large a rowset can be depends on the size of each row and the amount of memory available.

Rowset size is set by a call to SQLSetStmtAttr with an Attribute argument of SQL_ATTR_ROW_ARRAY_SIZE. The application can change the rowset size, bind new rowset buffers (by calling SQLBindCol or specifying a binding offset) even after rows have been fetched, or both. The implications of changing the rowset size depend on the function:

  • SQLFetch and SQLFetchScroll use the rowset size at the time of the call to determine how many rows to fetch. However, SQLFetchScroll with a FetchOrientation of SQL_FETCH_NEXT increments the cursor based on the rowset of the previous fetch and then fetches a rowset based on the current rowset size.
  • SQLSetPos uses the rowset size that is in effect as of the preceding call to SQLFetch or SQLFetchScroll, because SQLSetPos operates on a rowset that has already been set. SQLSetPos also will pick up the new rowset size if SQLBulkOperations has been called after the rowset size was changed.
  • SQLBulkOperations uses the rowset size in effect at the time of the call, because it performs operations on a table independent of any fetched rowset.


Number of Rows Fetched and Status

If the SQL_ATTR_ROWS_FETCHED_PTR statement attribute has been set, it specifies a buffer that returns the number of rows fetched by the call to SQLFetch or SQLFetchScroll, and error rows. (This number is a count of all rows that do not have the status SQL_ROW_NO_ROWS.) After a call to SQLBulkOperations or SQLSetPos, the buffer contains the number of rows that were affected by a bulk operation performed by the function. If the SQL_ATTR_ROW_STATUS_PTR statement attribute has been set, SQLFetch or SQLFetchScroll returns the row status array, which provides the status of each returned row. Both of the buffers pointed to by these fields are allocated by the application and populated by the driver. An application must make sure that these pointers remain valid until the cursor is closed.

Entries in the row status array state whether each row was fetched successfully, whether it was updated, added, or deleted since it was last fetched, and whether an error occurred while fetching the row. If SQLFetch or SQLFetchScroll encounters an error while retrieving one row of a multirow rowset, or if SQLBulkOperations with an Operation argument of SQL_FETCH_BY_BOOKMARK encounters an error while performing a bulk fetch, it sets the corresponding value in the row status array to SQL_ROW_ERROR, continues fetching rows, and returns SQL_SUCCESS_WITH_INFO. For more information about error handling and the row status array, see the SQLFetch and SQLFetchScroll function descriptions.


SQLGetData and Block Cursors

SQLGetData operates on a single column of a single row and cannot fetch an array containing data from multiple rows. This is because the primary use of SQLGetData is to fetch long data in parts, and there is little or no reason to do this for more than one row at a time.

To use SQLGetData with a block cursor, an application first calls SQLSetPos to position the cursor on a single row. It then calls SQLGetData for a column in that row. However, this behavior is optional. To determine if a driver supports the use of SQLGetData with block cursors, an application calls SQLGetInfo with the SQL_GETDATA_EXTENSIONS option.


Row Status Array

In addition to data, SQLFetch and SQLFetchScroll can return an array that gives the status of each row in the rowset. This array is specified through the SQL_ATTR_ROW_STATUS_PTR statement attribute. This array is allocated by the application and must have as many elements as are specified by the SQL_ATTR_ROW_ARRAY_SIZE statement attribute. The values in the array are set by SQLBulkOperations, SQLFetch, SQLFetchScroll, and SQLSetPos. The values describe the status of the row and whether that status has changed since it was last fetched.

Row status array value | Description
SQL_ROW_SUCCESS | The row was successfully fetched and has not changed since it was last fetched.
SQL_ROW_SUCCESS_WITH_INFO | The row was successfully fetched and has not changed since it was last fetched. However, a warning was returned about the row.
SQL_ROW_ERROR | An error occurred while fetching the row.
SQL_ROW_UPDATED | The row was successfully fetched and has been updated since it was last fetched. If the row is fetched again or refreshed by SQLSetPos, its status is changed to the new status. Some drivers cannot detect changes to data and therefore cannot return this value. To determine whether a driver can detect updates to refetched rows, an application calls SQLGetInfo with the SQL_ROW_UPDATES option.
SQL_ROW_DELETED | The row has been deleted since it was last fetched.
SQL_ROW_ADDED | The row was inserted by SQLBulkOperations. If the row is fetched again or is refreshed by SQLSetPos, its status is SQL_ROW_SUCCESS. This value is not set by SQLFetch or SQLFetchScroll.
SQL_ROW_NOROW | The rowset overlapped the end of the result set, and no row was returned that corresponded to this element of the row status array.


Scrollable Cursors

In modern screen-based applications, the user scrolls backward and forward through the data. For such applications, returning to a previously fetched row is a problem. One possibility is to close and reopen the cursor and then fetch rows until the cursor reaches the required row. Another possibility is to read the result set, cache it locally, and implement scrolling in the application. Both possibilities work well only with small result sets, and the latter possibility is difficult to implement. A better solution is to use a scrollable cursor, which can move backward and forward in the result set.

A scrollable cursor is commonly used in modern screen-based applications in which the user scrolls back and forth through the data. However, applications should use scrollable cursors only when forward-only cursors will not do the job, as scrollable cursors are generally more expensive than forward-only cursors.

The ability to move backward raises a question not applicable to forward-only cursors: Should a scrollable cursor detect changes made to rows previously fetched? That is, should it detect updated, deleted, and newly inserted rows?

This question arises because the definition of a result set—the set of rows that matches certain criteria—does not state when rows are checked to see whether they match that criteria, nor does it state whether rows must contain the same data each time they are fetched. The former omission makes it possible for scrollable cursors to detect whether rows have been inserted or deleted, while the latter makes it possible for them to detect updated data.

The ability to detect changes is sometimes useful, sometimes not. For example, an accounting application needs a cursor that ignores all changes; balancing books is impossible if the cursor shows the latest changes. On the other hand, an airline reservation system needs a cursor that shows the latest changes to the data; without such a cursor, it must continually requery the database to show the most up-to-date flight availability.

To cover the needs of different applications, ODBC defines four different types of scrollable cursors. These cursors vary both in expense and in their ability to detect changes to the result set. Note that if a scrollable cursor can detect changes to rows, it can only detect them when it attempts to refetch those rows; there is no way for the data source to notify the cursor of changes to the currently fetched rows. Note as well that visibility of changes is also controlled by the transaction isolation level; for more information, see "Transaction Isolation" in Chapter 14: Transactions.


Scrollable Cursor Types

The four types of scrollable cursors are static, dynamic, keyset-driven, and mixed. Static cursors detect few or no changes but are relatively cheap to implement. Dynamic cursors detect all changes but are expensive to implement. Keyset-driven and mixed cursors lie in between, detecting most changes but at less expense than dynamic cursors.

The following terms are used to define the characteristics of each type of scrollable cursor:

  • Own updates, deletes, and inserts. Updates, deletes, and inserts made through the cursor, either with a call to SQLBulkOperations or SQLSetPos or with a positioned update or delete statement.
  • Other updates, deletes, and inserts. Updates, deletes, and inserts not made by the cursor, including those made by other operations in the same transaction, those made through other transactions, and those made by other applications.
  • Membership. The set of rows in the result set.
  • Order. The order in which rows are returned by the cursor.
  • Values. The values in each row in the result set.

For information about how to update, delete, and insert data, see Chapter 12: Updating Data.


Static Cursors

A static cursor is one in which the result set appears to be static. It does not usually detect changes made to the membership, order, or values of the result set after the cursor is opened. For example, suppose a static cursor fetches a row and another application then updates that row. If the static cursor refetches the row, the values it sees are unchanged, despite the changes made by the other application.

Static cursors may detect their own updates, deletes, and inserts, although they are not required to do so. Whether a particular static cursor detects these changes is reported through the SQL_STATIC_SENSITIVITY option in SQLGetInfo. Static cursors never detect other updates, deletes, and inserts.

The row status array specified by the SQL_ATTR_ROW_STATUS_PTR statement attribute can contain SQL_ROW_SUCCESS, SQL_ROW_SUCCESS_WITH_INFO, or SQL_ROW_ERROR for any row. It returns SQL_ROW_UPDATED, SQL_ROW_DELETED, or SQL_ROW_ADDED for rows updated, deleted, or inserted by the cursor, assuming that the cursor is capable of detecting such changes.

Static cursors are commonly implemented by locking the rows in the result set or making a copy, or snapshot, of the result set. While locking rows is relatively easy to do, it has the drawback of significantly reducing concurrency. Making a copy allows greater concurrency and allows the cursor to keep track of its own updates, deletes, and inserts by modifying the copy. However, a copy is more expensive to make and can diverge from the underlying data as that data is changed by others.


Dynamic Cursors

A dynamic cursor is just that: dynamic. It can detect any changes made to the membership, order, and values of the result set after the cursor is opened. For example, suppose a dynamic cursor fetches two rows and another application then updates one of those rows and deletes the other. If the dynamic cursor then attempts to refetch those rows, it will not find the deleted row but will return the new values for the updated row.

Dynamic cursors detect all updates, deletes, and inserts, both their own and those made by others. (This is subject to the isolation level of the transaction, as set by the SQL_ATTR_TXN_ISOLATION connection attribute.) The row status array specified by the SQL_ATTR_ROW_STATUS_PTR statement attribute reflects these changes and can contain SQL_ROW_SUCCESS, SQL_ROW_SUCCESS_WITH_INFO, SQL_ROW_ERROR, SQL_ROW_UPDATED, and SQL_ROW_ADDED. It cannot return SQL_ROW_DELETED because a dynamic cursor does not return deleted rows outside the rowset and therefore no longer recognizes the existence of the deleted row in the result set or its corresponding element in the row status array. SQL_ROW_ADDED is returned only when a row is updated by a call to SQLSetPos, not when it is updated by another cursor.

One way of implementing dynamic cursors in the database is by creating a selective index that defines the membership and ordering of the result set. Because the index is updated when others make changes, a cursor based on such an index is sensitive to all changes. Additional selection within the result set defined by this index is possible by processing along the index.

Dynamic cursors can be simulated by requiring the result set to be ordered by a unique key. With such a restriction, fetches are made by executing a SELECT statement each time the cursor fetches rows. For example, suppose the result set is defined by this statement:

SELECT * FROM Customers ORDER BY Name, CustID

To fetch the next rowset in this result set, the simulated cursor sets the parameters in the following SELECT statement to the values in the last row of the current rowset, and then executes it:

SELECT * FROM Customers WHERE (Name > ?) AND (CustID > ?)
   ORDER BY Name, CustID

This statement creates a second result set, the first rowset of which is the next rowset in the original result set—in this case, the set of rows in the Customers table. The cursor returns this rowset to the application.

It is interesting to note that a dynamic cursor implemented in this manner actually creates many result sets, which allows it to detect changes to the original result set. The application never learns of the existence of these auxiliary result sets; it simply appears as if the cursor is able to detect changes to the original result set.


Keyset-Driven Cursors

A keyset-driven cursor lies between a static and a dynamic cursor in its ability to detect changes. Like a static cursor, it does not always detect changes to the membership and order of the result set. Like a dynamic cursor, it does detect changes to the values of rows in the result set (subject to the isolation level of the transaction, as set by the SQL_ATTR_TXN_ISOLATION connection attribute).

When a keyset-driven cursor is opened, it saves the keys for the entire result set; this fixes the apparent membership and order of the result set. As the cursor scrolls through the result set, it uses the keys in this keyset to retrieve the current data values for each row. For example, suppose a keyset-driven cursor fetches a row and another application then updates that row. If the cursor refetches the row, the values it sees are the new ones because it refetched the row using its key. Because of this, the keyset-driven cursors always detect changes made by themselves and others.

When the cursor attempts to retrieve a row that has been deleted, this row appears as a "hole" in the result set: The key for the row exists in the keyset, but the row no longer exists in the result set. If the key values in a row are updated, the row is considered to have been deleted and then inserted, so such rows also appear as holes in the result set. While a keyset-driven cursor can always detect rows deleted by others, it can optionally remove the keys for rows it deletes itself from the keyset. Keyset-driven cursors that do this cannot detect their own deletes. Whether a particular keyset-driven cursor detects its own deletes is reported through the SQL_STATIC_SENSITIVITY option in SQLGetInfo.

Rows inserted by others are never visible to a keyset-driven cursor because no keys for these rows exist in the keyset. However, a keyset-driven cursor can optionally add the keys for rows it inserts itself to the keyset. Keyset-driven cursors that do this can detect their own inserts. Whether a particular keyset-driven cursor detects its own inserts is reported through the SQL_STATIC_SENSITIVITY option in SQLGetInfo.

The row status array specified by the SQL_ATTR_ROW_STATUS_PTR statement attribute can contain SQL_ROW_SUCCESS, SQL_ROW_SUCCESS_WITH_INFO, or SQL_ROW_ERROR for any row. It returns SQL_ROW_UPDATED, SQL_ROW_DELETED, or SQL_ROW_ADDED for rows it detects as updated, deleted, or inserted.

Keyset-driven cursors are commonly implemented by creating a temporary table that contains the keys for each row in the result set. Because the cursor must also determine whether rows have been updated, this table also commonly contains a column with row versioning information.

To scroll over the original result set, the keyset-driven cursor opens a static cursor over the temporary table. To retrieve a row in the original result set, the cursor first retrieves the appropriate key from the temporary table and then retrieves the current values for the row. If block cursors are used, the cursor must retrieve multiple keys and rows.


Mixed Cursors

A mixed cursor is a combination of a keyset-driven cursor and a dynamic cursor. It is used when the result set is too large to reasonably save keys for the entire result set. Mixed cursors are implemented by creating a keyset that is smaller than the entire result set but larger than the rowset.

As long as the application scrolls within the keyset, the behavior is keyset-driven. When the application scrolls outside the keyset, the behavior is dynamic: The cursor fetches the requested rows and creates a new keyset. After the new keyset is created, the behavior reverts to keyset-driven within that keyset.

For example, suppose a result set has 1,000 rows and uses a mixed cursor with a keyset size of 100 and a rowset size of 10. When the first rowset is fetched, the cursor creates a keyset consisting of the keys for the first 100 rows. It then returns the first 10 rows, as requested.

Now suppose another application deletes rows 11 and 101. If the cursor attempts to retrieve row 11, it will encounter a hole because it has a key for this row but no row exists; this is keyset-driven behavior. If the cursor attempts to retrieve row 101, the cursor will not detect that the row is missing because it does not have a key for the row. Instead, it will retrieve what was previously row 102. This is dynamic cursor behavior.

A mixed cursor is equivalent to a keyset-driven cursor when the keyset size is equal to the result set size. A mixed cursor is equivalent to a dynamic cursor when the keyset size is equal to 1.


Using Scrollable Cursors

Using a scrollable cursor requires these three steps:

  1. Determine the cursor capabilities.
  2. Set up the cursor.
  3. Scroll and fetch rows.


Determining Cursor Capabilities

The following four options in SQLGetInfo describe what types of cursors are supported and what their capabilities are:

  • SQL_CURSOR_SENSITIVITY. Indicates whether a cursor is sensitive to changes made by another cursor.
  • SQL_SCROLL_OPTIONS. Lists the supported cursor types (forward-only, static, keyset-driven, dynamic, or mixed). All data sources must support forward-only cursors.
  • SQL_DYNAMIC_CURSOR_ATTRIBUTES1, SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1, SQL_KEYSET_CURSOR_ATTRIBUTES1, or SQL_STATIC_CURSOR_ATTRIBUTES1 (depending on the type of the cursor). Lists the fetch types supported by scrollable cursors. The bits in the return value correspond to the fetch types in SQLFetchScroll.
  • SQL_KEYSET_CURSOR_ATTRIBUTES2 or SQL_STATIC_CURSOR_ATTRIBUTES2 (depending on the type of the cursor). Lists whether static and keyset-driven cursors can detect their own updates, deletes, and inserts.

An application can determine cursor capabilities at run time by calling SQLGetInfo with these options. This is commonly done by generic applications. Cursor capabilities also can be determined during application development and their use hard-coded into the application. This is commonly done by vertical and custom applications but can also be done by generic applications that use a client-side cursor implementation such as the ODBC cursor library.


Setting Up the Cursor

The application can specify the cursor type before executing a statement that creates a result set. It does this with the SQL_ATTR_CURSOR_TYPE statement attribute. If the application does not explicitly specify a type, a forward-only cursor will be used. To get a mixed cursor, an application specifies a keyset-driven cursor but declares a keyset size less than the result set size.

For keyset-driven and mixed cursors, the application can also specify the keyset size. It does this with the SQL_ATTR_KEYSET_SIZE statement attribute. If the keyset size is set to 0, which is the default, the keyset size is set to the result set size and a keyset-driven cursor is used. The keyset size can be changed after the cursor has been opened.

The application can also set the rowset size; for more information, see "Using Block Cursors," earlier in this chapter.


Cursor Characteristics and Cursor Type

An application can specify the characteristics of a cursor rather than specifying the cursor type (forward-only, static, keyset-driven, or dynamic). To do so, the application selects the cursor's scrollability (by setting the SQL_ATTR_CURSOR_SCROLLABLE statement attribute) and sensitivity (by setting the SQL_ATTR_CURSOR_SENSITIVITY statement attribute) before opening the cursor on the statement handle. The driver then chooses the cursor type that most efficiently provides the characteristics that the application requested.

Whenever an application sets any of the statement attributes SQL_ATTR_CONCURRENCY, SQL_ATTR_CURSOR_SCROLLABLE, SQL_ATTR_CURSOR_SENSITIVITY, or SQL_ATTR_CURSOR_TYPE, the driver makes any required change to the other statement attributes in this set of four attributes so that their values remain consistent. As a result, when the application specifies a cursor characteristic, the driver can change the attribute that indicates cursor type based on this implicit selection; when the application specifies a type, the driver can change any of the other attributes to be consistent with the characteristics of the selected type. For more information on these statement attributes, see the SQLSetStmtAttr function description.

An application that sets statement attributes to specify both a cursor type and cursor characteristics runs the risk of obtaining a cursor that is not the most efficient method available on that driver of meeting the application's requirements.

The implicit setting of statement attributes is driver-defined except that it must follow these rules:

  • Forward-only cursors are never scrollable; see the definition of SQL_ATTR_CURSOR_SCROLLABLE in SQLSetStmtAttr.
  • Insensitive cursors are never updatable (and thus their concurrency is read-only); this is based on their definition of insensitive cursors in the ISO SQL standard.

Consequently, the implicit setting of statement attributes occurs in the cases described in the following table.

Application sets attribute to | Other attributes set implicitly
SQL_ATTR_CONCURRENCY to SQL_CONCUR_READ_ONLY | SQL_ATTR_CURSOR_SENSITIVITY to SQL_INSENSITIVE.
SQL_ATTR_CONCURRENCY to SQL_CONCUR_LOCK, SQL_CONCUR_ROWVER, or SQL_CONCUR_VALUES | SQL_ATTR_CURSOR_SENSITIVITY to SQL_UNSPECIFIED or SQL_SENSITIVE, as defined by the driver. It can never be set to SQL_INSENSITIVE, because insensitive cursors are always read-only.
SQL_ATTR_CURSOR_SCROLLABLE to SQL_NONSCROLLABLE | SQL_ATTR_CURSOR_TYPE to SQL_CURSOR_FORWARD_ONLY
SQL_ATTR_CURSOR_SCROLLABLE to SQL_SCROLLABLE | SQL_ATTR_CURSOR_TYPE to SQL_CURSOR_STATIC, SQL_CURSOR_KEYSET_DRIVEN, or SQL_CURSOR_DYNAMIC, as specified by the driver. It is never set to SQL_CURSOR_FORWARD_ONLY.
SQL_ATTR_CURSOR_SENSITIVITY to SQL_INSENSITIVE | SQL_ATTR_CONCURRENCY to SQL_CONCUR_READ_ONLY. SQL_ATTR_CURSOR_TYPE to SQL_CURSOR_STATIC.
SQL_ATTR_CURSOR_SENSITIVITY to SQL_SENSITIVE | SQL_ATTR_CONCURRENCY to SQL_CONCUR_LOCK, SQL_CONCUR_ROWVER, or SQL_CONCUR_VALUES, as specified by the driver. It is never set to SQL_CONCUR_READ_ONLY. SQL_ATTR_CURSOR_TYPE to SQL_CURSOR_FORWARD_ONLY, SQL_CURSOR_STATIC, SQL_CURSOR_KEYSET_DRIVEN, or SQL_CURSOR_DYNAMIC, as specified by the driver.
SQL_ATTR_CURSOR_SENSITIVITY to SQL_UNSPECIFIED | SQL_ATTR_CONCURRENCY to SQL_CONCUR_READ_ONLY, SQL_CONCUR_LOCK, SQL_CONCUR_ROWVER, or SQL_CONCUR_VALUES, as specified by the driver. SQL_ATTR_CURSOR_TYPE to SQL_CURSOR_FORWARD_ONLY, SQL_CURSOR_STATIC, SQL_CURSOR_KEYSET_DRIVEN, or SQL_CURSOR_DYNAMIC, as specified by the driver.
SQL_ATTR_CURSOR_TYPE to SQL_CURSOR_DYNAMIC | SQL_ATTR_SCROLLABLE to SQL_SCROLLABLE. SQL_ATTR_CURSOR_SENSITIVITY to SQL_SENSITIVE. (But only if SQL_ATTR_CONCURRENCY is not equal to SQL_CONCUR_READ_ONLY. Updatable dynamic cursors are always sensitive to changes made in their own transaction.)
SQL_ATTR_CURSOR_TYPE to SQL_CURSOR_FORWARD_ONLY | SQL_ATTR_CURSOR_SCROLLABLE to SQL_NONSCROLLABLE.
SQL_ATTR_CURSOR_TYPE to SQL_CURSOR_KEYSET_DRIVEN | SQL_ATTR_SCROLLABLE to SQL_SCROLLABLE. SQL_ATTR_SENSITIVITY to SQL_UNSPECIFIED or SQL_SENSITIVE (according to driver-defined criteria, if SQL_ATTR_CONCURRENCY is not SQL_CONCUR_READ_ONLY).
SQL_ATTR_CURSOR_TYPE to SQL_CURSOR_STATIC | SQL_ATTR_SCROLLABLE to SQL_SCROLLABLE. SQL_ATTR_SENSITIVITY to SQL_INSENSITIVE (if SQL_ATTR_CONCURRENCY is SQL_CONCUR_READ_ONLY). SQL_ATTR_SENSITIVITY to SQL_UNSPECIFIED or SQL_SENSITIVE (if SQL_ATTR_CONCURRENCY is not SQL_CONCUR_READ_ONLY).


Scrolling and Fetching Rows

When using a scrollable cursor, applications call SQLFetchScroll to position the cursor and fetch rows. SQLFetchScroll supports relative scrolling (next, prior, and relative n rows), absolute scrolling (first, last, and row n), and positioning by bookmark. The FetchOrientation and FetchOffset arguments in SQLFetchScroll specify which rowset to fetch, as shown in the following diagrams.

Fetching Next, Prior, First, and Last Rowsets

Fetching Absolute, Relative, and Bookmarked Rowsets

SQLFetchScroll positions the cursor to the specified row and returns the rows in the rowset starting with that row. If the specified rowset overlaps the end of the result set, a partial rowset is returned. If the specified rowset overlaps the start of the result set, the first rowset in the result set is usually returned; for complete details, see the SQLFetchScroll function description.

In some cases, the application might want to position the cursor without retrieving any data. For example, it might want to test whether a row exists or just get the bookmark for the row without bringing other data across the network. To do this, it sets the SQL_ATTR_RETRIEVE_DATA statement attribute to SQL_RD_OFF. The variable bound to the bookmark column (if any) is always updated, regardless of the setting of this statement attribute.

After the rowset has been retrieved, the application can call SQLSetPos to position to a particular row in the rowset or refresh rows in the rowset. For more information on using SQLSetPos, see "Updating Data with SQLSetPos" in Chapter 12: Updating Data.

Note Scrolling is supported in ODBC 2.x drivers by SQLExtendedFetch. For more information, see "Block Cursors, Scrollable Cursors, and Backward Compatibility" in Appendix G: Driver Guidelines for Backward Compatibility.


Relative and Absolute Scrolling

Most of the scrolling options in SQLFetchScroll position the cursor relative to the current position or to an absolute position. SQLFetchScroll supports fetching the next, prior, first, and last rowsets, as well as relative fetching (fetch the rowset n rows from the start of the current rowset) and absolute fetching (fetch the rowset starting at row n). If n is negative in an absolute fetch, rows are counted from the end of the result set. Thus, an absolute fetch of row –1 means to fetch the rowset that starts with the last row in the result set.

Dynamic cursors detect rows inserted into and deleted from the result set, so there is no easy way for dynamic cursors to retrieve the row at a particular number other than reading from the start of the result set, which is likely to be slow. Furthermore, absolute fetching is not very useful in dynamic cursors because row numbers change as rows are inserted and deleted; therefore, successively fetching the same row number can yield different rows.

Applications that use SQLFetchScroll only for its block cursor capabilities, such as reports, are likely to pass through the result set a single time, using only the option to fetch the next rowset. Screen-based applications, on the other hand, can take advantage of all the capabilities of SQLFetchScroll. If the application sets the rowset size to the number of rows displayed on the screen and binds the screen buffers to the result set, it can translate scroll bar operations directly to calls to SQLFetchScroll.

Scroll bar operation | SQLFetchScroll scrolling option
Page up | SQL_FETCH_PRIOR
Page down | SQL_FETCH_NEXT
Line up | SQL_FETCH_RELATIVE with FetchOffset equal to –1
Line down | SQL_FETCH_RELATIVE with FetchOffset equal to 1
Scroll box at top | SQL_FETCH_FIRST
Scroll box at bottom | SQL_FETCH_LAST
Random scroll box position | SQL_FETCH_ABSOLUTE

Such applications also need to position the scroll box after a scrolling operation, which requires the current row number and the number of rows. For the current row number, applications can either keep track of the current row number or call SQLGetStmtAttr with the SQL_ATTR_ROW_NUMBER attribute to retrieve it.

The number of rows in the cursor, which is the size of the result set, is available as the SQL_DIAG_CURSOR_ROW_COUNT field of the diagnostic header. The value in this field is defined only after SQLExecute, SQLExecDirect, or SQLMoreResult has been called. This count can be either an approximate count or an exact count, depending on the capabilities of the driver. The driver's support can be determined by calling SQLGetInfo with the cursor attributes information types and checking whether the SQL_CA2_CRC_APPROXIMATE or SQL_CA2_CRC_EXACT bit is returned for the type of cursor.

An exact row count is never supported for a dynamic cursor. For other types of cursors, the driver can support either exact or approximate row counts, but not both. If the driver supports neither exact nor approximate row counts for a specific cursor type, the SQL_DIAG_CURSOR_ROW_COUNT field contains the number of rows that have been fetched so far. Regardless of what the driver supports, SQLFetchScroll with an Operation of SQL_FETCH_LAST will cause the SQL_DIAG_CURSOR_ROW_COUNT field to contain the exact row count.


Bookmarks

A bookmark is a value used to identify a row of data. The meaning of the bookmark value is known only to the driver or data source. For example, it might be as simple as a row number or as complex as a disk address. Bookmarks in ODBC are a bit different from bookmarks in real books. In a real book, the reader places a bookmark at a specific page and then looks for that bookmark to return to the page. In ODBC, the application requests a bookmark for a particular row, stores it, and passes it back to the cursor to return to the row. Thus, bookmarks in ODBC are similar to a reader writing down a page number, remembering it, and then looking up the page again.

To determine a driver's support of bookmarks, an application calls SQLGetInfo with the SQL_BOOKMARK_PERSISTENCE option. The bits in this value describe what operations bookmarks survive, such as whether bookmarks are still valid after the cursor is closed.


Bookmark Types

All bookmarks in ODBC 3.x are variable-length bookmarks. This allows a primary key or a unique index associated with a table to be used as a bookmark. The bookmark also can be a 32-bit value, as was used in ODBC 2.x. To specify that a bookmark is used with a cursor, an ODBC 3.x application sets the SQL_ATTR_USE_BOOKMARK statement attribute to SQL_UB_VARIABLE. A variable-length bookmark is automatically used.

An application can call SQLColAttribute with the FieldIdentifier argument set to SQL_DESC_OCTET_LENGTH to obtain the length of the bookmark. Because a variable-length bookmark can be a long value, an application should not bind to column 0 unless it will use the bookmark for many of the rows in the rowset.

Fixed-length bookmarks are supported only for backward compatibility. If an ODBC 2.x application working with an ODBC 3.x driver calls SQLSetStmtOption to set SQL_USE_BOOKMARKS to SQL_UB_ON, it is mapped in the Driver Manager to SQL_UB_VARIABLE. A variable-length bookmark is used, even if only 32 bits of it are populated. If a driver supports fixed-length bookmarks, it will support variable-length bookmarks. If an ODBC 3.x application working with an ODBC 2.x driver calls SQLSetStmtAttr to set SQL_ATTR_USE_BOOKMARKS to SQL_UB_VARIABLE, it is mapped in the Driver Manager to SQL_UB_ON and a 32-bit fixed-length bookmark is used. The SQL_ATTR_FETCH_BOOKMARK_PTR statement attribute must then point to a 32-bit bookmark. If the bookmarks used are longer than 32 bits, such as when primary keys are used as bookmarks, the cursor must map the actual values to 32-bit values. It could, for example, build a hash table of them. When an ODBC 3.x application working with an ODBC 2.x driver binds a bookmark, the buffer length must be 4.


Retrieving Bookmarks

If the application will use bookmarks, it must set the SQL_ATTR_USE_BOOKMARKS statement attribute to SQL_UB_VARIABLE before preparing or executing the statement. This is necessary because building and maintaining bookmarks can be an expensive operation, so bookmarks should be enabled only when an application can make good use of them.

Bookmarks are returned as column 0 of the result set. There are three ways an application can retrieve them:

  • Bind column 0 of the result set. SQLFetch or SQLFetchScroll returns the bookmarks for each row in the rowset along with the data for other bound columns.
  • Call SQLSetPos to position to a row in the rowset and then call SQLGetData for column 0. If a driver supports bookmarks, it must always support the ability to call SQLGetData for column 0, even if it does not allow applications to call SQLGetData for other columns before the last bound column.
  • Call SQLBulkOperations with the Operation argument set to SQL_ADD, and column 0 bound. The cursor inserts the row and returns the bookmark for the row in the bound buffer.


Scrolling by Bookmark

When fetching rows with SQLFetchScroll, an application can use a bookmark as a basis for selecting the starting row. This is a form of absolute addressing because it does not depend on the current cursor position. To scroll to a bookmarked row, the application calls SQLFetchScroll with a FetchOrientation of SQL_FETCH_BOOKMARK. This operation uses the bookmark pointed to by the SQL_ATTR_FETCH_BOOKMARK_PTR statement attribute. It returns the rowset starting with the row identified by that bookmark. An application can specify an offset for this operation in the FetchOffset argument of the call to SQLFetchScroll. When an offset is specified, the first row of the returned rowset is determined by adding the number in the FetchOffset argument to the number of the row identified by the bookmark. This use of the FetchOffset argument is not supported when used with ODBC 2.x drivers; when an application calls SQLFetchScroll in an ODBC 2.x driver with FetchOrientation set to SQL_FETCH_BOOKMARK, the FetchOffset argument must be set to 0.


Updating, Deleting, or Fetching by Bookmark

Bookmarks can be used to identify data to be updated in the result set, deleted from the result set, or fetched from the result set to the rowset buffers. These operations are performed by a call to SQLBulkOperations with an Option argument of SQL_UPDATE_BY_BOOKMARK, SQL_DELETE_BY_BOOKMARK, or SQL_FETCH_BY_BOOKMARK. The bookmarks used in these operations are stored in column 0 of the rowset buffers. When updating by bookmark, the data that result set columns are updated to is retrieved from the rowset buffers. For more information, see "Updating Data with SQLBulkOperations" in Chapter 12: Updating Data.


Comparing Bookmarks

Because bookmarks are byte-comparable, they can be compared for equality or inequality. To do so, an application treats each bookmark as an array of bytes and compares two bookmarks byte-by-byte. Because bookmarks are guaranteed to be distinct only within a result set, it makes no sense to compare bookmarks that were obtained from different result sets.


The ODBC Cursor Library

Block and scrollable cursors are very useful additions to many applications. However, not all drivers support block and scrollable cursors. The same is true of positioned update and delete statements and SQLSetPos, which are discussed in Chapter 12: Updating Data. Because of this, the ODBC component of the Microsoft® Data Access Components (MDAC) SDK includes a cursor library. The cursor library implements block, static cursors, positioned update and delete statements, and SQLSetPos for any driver that meets the X/Open Standard CLI conformance level. The cursor library may be redistributed with ODBC applications; see the licensing agreement in the SDK for details.

To use the cursor library, an application sets the SQL_ATTR_ODBC_CURSORS connection attribute before connecting to the data source. For more information about the cursor library, see Appendix F: ODBC Cursor Library.


Multiple Results

A result is something returned by the data source after a statement is executed. ODBC has two types of results: result sets and row counts. Row counts are the number of rows affected by an update, delete, or insert statement. Batches, described in "Batches of SQL Statements" in Chapter 9: Executing Statements, can generate multiple results.

The following table lists the SQLGetInfo options an application uses to determine whether a data source returns multiple results for each different type of batch. In particular, a data source can return a single row count for the entire batch of statements or individual row counts for each statement in the batch. In the case of a result set–generating statement executed with an array of parameters, the data source can return a single result set for all sets of parameters or individual result sets for each set of parameters.

Batch type | Row counts | Result sets
Explicit batch | SQL_BATCH_ROW_COUNT[a] | --[b]
Procedure | SQL_BATCH_ROW_COUNT[a] | --[b]
Arrays of parameters | SQL_PARAM_ARRAYS_ROW_COUNTS | SQL_PARAM_ARRAYS_SELECTS

[a] Row count–generating statements in a batch may be supported, yet the return of the row counts not supported. The SQL_BATCH_SUPPORT option in SQLGetInfo indicates whether row count–generating statements are allowed in batches; the SQL_BATCH_ROW_COUNTS option indicates whether these row counts are returned to the application.

[b] Explicit batches and procedures always return multiple result sets when they include multiple result set–generating statements.

Note The SQL_MULT_RESULT_SETS option introduced in ODBC 1.0 provides only general information about whether multiple result sets can be returned. In particular, it is set to "Y" if the SQL_BS_SELECT_EXPLICIT or SQL_BS_SELECT_PROC bits are returned for SQL_BATCH_SUPPORT or if SQL_PAS_BATCH is returned for SQL_PARAM_ARRAYS_SELECT.

To process multiple results, an application calls SQLMoreResults. This function discards the current result and makes the next result available. It returns SQL_NO_DATA when no more results are available. For example, suppose the following statements are executed as a batch:

SELECT * FROM Parts WHERE Price > 100.00;
UPDATE Parts SET Price = 0.9 * Price WHERE Price > 100.00

After these statements are executed, the application fetches rows from the result set created by the SELECT statement. When it is done fetching rows, it calls SQLMoreResults to make available the number of parts that were repriced. If necessary, SQLMoreResults discards unfetched rows and closes the cursor. The application then calls SQLRowCount to determine how many parts were repriced by the UPDATE statement.

It is driver-specific whether the entire batch statement is executed before any results are available. In some implementations, this is the case; in others, calling SQLMoreResults triggers the execution of the next statement in the batch.

If one of the statements in a batch fails, SQLMoreResults will return either SQL_ERROR or SQL_SUCCESS_WITH_INFO. If the batch was aborted when the statement failed or the failed statement was the last statement in the batch, SQLMoreResults will return SQL_ERROR. If the batch was not aborted when the statement failed and the failed statement was not the last statement in the batch, SQLMoreResults will return SQL_SUCCESS_WITH_INFO. SQL_SUCCESS_WITH_INFO indicates that at least one result set or count was generated and that the batch was not aborted.


Chapter 12: Updating Data

Applications can update data either by executing SQL statements or by calling SQLSetPos or SQLBulkOperations. UPDATE, DELETE, and INSERT statements act directly on the data source and are usually supported by drivers. Searched update and delete statements contain a specification of the rows to change. Positioned update and delete statements and SQLSetPos act on the data source through a cursor and are less widely supported.

Whether cursors can detect changes made to the result set with the methods described in this chapter depends on the type of the cursor and how it is implemented. Forward-only cursors do not revisit rows and therefore will not detect any changes. For information about whether scrollable cursors can detect changes, see "Scrollable Cursors" in Chapter 11: Retrieving Results (Advanced).


UPDATE, DELETE, and INSERT Statements

SQL-based applications make changes to tables by executing the UPDATE, DELETE, and INSERT statements. These statements are part of the Minimum SQL grammar conformance level and must be supported by all drivers and data sources.

The syntax of these statements is:

UPDATE table-name
SET column-identifier = {expression | NULL}
[, column-identifier = {expression | NULL}]...
 [WHERE search-condition]
DELETE FROM table-name [WHERE search-condition]
INSERT INTO table-name [(column-identifier [, column-identifier]...)]
{query-specification | VALUES (insert-value [, insert-value]...)}

Note that the query-specification element is valid only in the Core and Extended SQL grammars, and that the expression and search-condition elements become more complex in the Core and Extended SQL grammars.

Like other SQL statements, UPDATE, DELETE, and INSERT statements are often more efficient when they use parameters. For example, the following statement can be prepared and repeatedly executed to insert multiple rows in the Orders table:

INSERT INTO Orders (PartID, Description, Price) VALUES (?, ?, ?)

This efficiency can be increased by passing arrays of parameter values. For more information about statement parameters and arrays of parameter values, see "Statement Parameters" in Chapter 9: Executing Statements.


Positioned Update and Delete Statements

Applications can update or delete the current row in a result set with a positioned update or delete statement. Positioned update and delete statements are supported by some data sources, but not all of them. To determine whether a data source supports positioned update and delete statements, an application calls SQLGetInfo with the SQL_DYNAMIC_CURSOR_ATTRIBUTES1, SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1, SQL_KEYSET_CURSOR_ATTRIBUTES1, or SQL_STATIC_CURSOR_ATTRIBUTES1 InfoType (depending on the type of the cursor). Note that the ODBC cursor library simulates positioned update and delete statements.

To use a positioned update or delete statement, the application must create a result set with a SELECT FOR UPDATE statement. The syntax of this statement is:

SELECT [ALL | DISTINCT] select-list
FROM table-reference-list
[WHERE search-condition]
FOR UPDATE OF [column-name [, column-name]...]

The application then positions the cursor on the row to be updated or deleted. It can do this by calling SQLFetchScroll to retrieve a rowset containing the required row and calling SQLSetPos to position the rowset cursor on that row. The application then executes the positioned update or delete statement on a different statement than the statement being used by the result set. The syntax of these statements is:

UPDATE table-name
SET column-identifier = {expression | NULL}
[, column-identifier = {expression | NULL}]...
WHERE CURRENT OF cursor-name
DELETE FROM table-name WHERE CURRENT OF cursor-name

Notice that these statements require a cursor name. The application either can specify a cursor name with SQLSetCursorName before executing the statement that creates the result set or can let the data source automatically generate a cursor name when the cursor is created. In the latter case, the application retrieves this cursor name for use in positioned update and delete statements by calling SQLGetCursorName.

For example, the following code allows a user to scroll through the Customers table and delete customer records or update their addresses and phone numbers. It calls SQLSetCursorName to specify a cursor name before it creates the result set of customers and uses three statement handles: hstmtCust for the result set, hstmtUpdate for a positioned update statement, and hstmtDelete for a positioned delete statement. Although the code could bind separate variables to the parameters in the positioned update statement, it updates the rowset buffers and binds the elements of these buffers. This keeps the rowset buffers synchronized with the updated data.

#define POSITIONED_UPDATE 100
#define POSITIONED_DELETE 101

SQLUINTEGER    CustIDArray[10];
SQLCHAR        NameArray[10][51], AddressArray[10][51],
               PhoneArray[10][11];
SQLINTEGER     CustIDIndArray[10], NameLenOrIndArray[10],
               AddressLenOrIndArray[10],
               PhoneLenOrIndArray[10];
SQLUSMALLINT   RowStatusArray[10], Action, RowNum;
SQLHSTMT       hstmtCust, hstmtUpdate, hstmtDelete;

// Set the SQL_ATTR_BIND_TYPE statement attribute to use column-wise
// binding. Declare the rowset size with the SQL_ATTR_ROW_ARRAY_SIZE
// statement attribute. Set the SQL_ATTR_ROW_STATUS_PTR statement
// attribute to point to the row status array.
SQLSetStmtAttr(hstmtCust, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0);
SQLSetStmtAttr(hstmtCust, SQL_ATTR_ROW_ARRAY_SIZE, 10, 0);
SQLSetStmtAttr(hstmtCust, SQL_ATTR_ROW_STATUS_PTR, RowStatusArray, 0);

// Bind arrays to the CustID, Name, Address, and Phone columns.
SQLBindCol(hstmtCust, 1, SQL_C_ULONG, CustIDArray, 0, CustIDIndArray);
SQLBindCol(hstmtCust, 2, SQL_C_CHAR, NameArray, sizeof(NameArray[0]),
            NameLenOrIndArray);
SQLBindCol(hstmtCust, 3, SQL_C_CHAR, AddressArray, sizeof(AddressArray[0]),
         AddressLenOrIndArray);
SQLBindCol(hstmtCust, 4, SQL_C_CHAR, PhoneArray, sizeof(PhoneArray[0]),
            PhoneLenOrIndArray);

// Set the cursor name to Cust.
SQLSetCursorName(hstmtCust, "Cust", SQL_NTS);

// Prepare positioned update and delete statements.
SQLPrepare(hstmtUpdate,
   "UPDATE Customers SET Address = ?, Phone = ? WHERE CURRENT OF Cust",
   SQL_NTS);
SQLPrepare(hstmtDelete, "DELETE FROM Customers WHERE CURRENT OF Cust", SQL_NTS);

// Execute a statement to retrieve rows from the Customers table.
SQLExecDirect(hstmtCust,
   "SELECT CustID, Name, Address, Phone FROM Customers FOR UPDATE OF Address, Phone",
   SQL_NTS);

// Fetch and display the first 10 rows.
SQLFetchScroll(hstmtCust, SQL_FETCH_NEXT, 0);
DisplayData(CustIDArray, CustIDIndArray, NameArray, NameLenOrIndArray, AddressArray,
            AddressLenOrIndArray, PhoneArray, PhoneLenOrIndArray, RowStatusArray);

// Call GetAction to get an action and a row number from the user.
while (GetAction(&Action, &RowNum)) {
   switch (Action) {

      case SQL_FETCH_NEXT:
      case SQL_FETCH_PRIOR:
      case SQL_FETCH_FIRST:
      case SQL_FETCH_LAST:
      case SQL_FETCH_ABSOLUTE:
      case SQL_FETCH_RELATIVE:
         // Fetch and display the requested data.
         SQLFetchScroll(hstmtCust, Action, RowNum);
         DisplayData(CustIDArray, CustIDIndArray, NameArray, NameLenOrIndArray,
                     AddressArray, AddressLenOrIndArray, PhoneArray,
                     PhoneLenOrIndArray, RowStatusArray);
         break;

      case POSITIONED_UPDATE:
         // Get the new data and place it in the rowset buffers.
         GetNewData(AddressArray[RowNum - 1], &AddressLenOrIndArray[RowNum - 1],
                     PhoneArray[RowNum - 1], &PhoneLenOrIndArray[RowNum - 1]);

         // Bind the elements of the arrays at position RowNum-1 to the
         // parameters of the positioned update statement.
         SQLBindParameter(hstmtUpdate, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
                           50, 0, AddressArray[RowNum - 1], sizeof(AddressArray[0]),
                           &AddressLenOrIndArray[RowNum - 1]);
         SQLBindParameter(hstmtUpdate, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
                           10, 0, PhoneArray[RowNum - 1], sizeof(PhoneArray[0]),
                           &PhoneLenOrIndArray[RowNum - 1]);

         // Position the rowset cursor. The rowset is 1-based.
         SQLSetPos(hstmtCust, RowNum, SQL_POSITION, SQL_LOCK_NO_CHANGE);

         // Execute the positioned update statement to update the row.
         SQLExecute(hstmtUpdate);
         break;

      case POSITIONED_DELETE:
         // Position the rowset cursor. The rowset is 1-based.
         SQLSetPos(hstmtCust, RowNum, SQL_POSITION, SQL_LOCK_NO_CHANGE);

         // Execute the positioned delete statement to delete the row.
         SQLExecute(hstmtDelete);
         break;
   }
}

// Close the cursor.
SQLCloseCursor(hstmtCust);


Simulating Positioned Update and Delete Statements

If the data source does not support positioned update and delete statements, the driver can simulate these. For example, the ODBC cursor library simulates positioned update and delete statements. The general strategy for simulating positioned update and delete statements is to convert positioned statements to searched ones. This is done by replacing the WHERE CURRENT OF clause with a searched WHERE clause that identifies the current row.

For example, because the CustID column uniquely identifies each row in the Customers table, the positioned delete statement

DELETE FROM Customers WHERE CURRENT OF CustCursor

might be converted to

DELETE FROM Customers WHERE (CustID = ?)

The driver may use one of the following row identifiers in the WHERE clause:

  • Columns whose values serve to identify uniquely every row in the table. For example, calling SQLSpecialColumns with SQL_BEST_ROWID returns the optimal column or set of columns that serve this purpose.
  • Pseudo-columns, provided by some data sources, for the purpose of uniquely identifying every row. These may also be retrievable by calling SQLSpecialColumns.
  • A unique index, if available.
  • 0All the columns in the result set.

Exactly which columns a driver should use in the WHERE clause it constructs depends on the driver. On some data sources, determining a row identifier can be costly. However, it is faster to execute and guarantees that a simulated statement updates or deletes at most one row. Depending on the capabilities of the underlying DBMS, using a row identifier can be expensive to set up. However, it is faster to execute and guarantees that a simulated statement will update or delete only one row. The option of using all the columns in the result set is usually much easier to set up. However, it is slower to execute and, if the columns do not uniquely identify a row, can result in rows being unintentionally updated or deleted, especially when the select list for the result set does not contain all the columns that exist in the underlying table.

Depending upon which of the preceding strategies the driver supports, an application can choose which strategy it wants the driver to use with the SQL_ATTR_SIMULATE_CURSOR statement attribute. Although it might seem odd for an application to risk unintentionally updating or deleting a row, the application can remove this risk by ensuring that the columns in the result set uniquely identify each row in the result set. This saves the driver the effort of having to do this.

If the driver chooses to use a row identifier, it intercepts the SELECT FOR UPDATE statement that creates the result set. If the columns in the select list do not effectively identify a row, the driver adds the necessary columns to the end of the select list. Some data sources have a single column that always uniquely identifies a row, such as the ROWID column in Oracle; if such a column is available, the driver uses this. Otherwise, the driver calls SQLSpecialColumns for each table in the FROM clause to retrieve a list of the columns that uniquely identify each row. A common restriction that results from this technique is that cursor simulation fails if there is more than one table in the FROM clause.

No matter how the driver identifies rows, it usually strips the FOR UPDATE OF clause off the SELECT FOR UPDATE statement before sending it to the data source. The FOR UPDATE OF clause is used only with positioned update and delete statements. Data sources that do not support positioned update and delete statements generally do not support it.

When the application submits a positioned update or delete statement for execution, the driver replaces the WHERE CURRENT OF clause with a WHERE clause containing the row identifier. The values of these columns are retrieved from a cache maintained by the driver for each column it uses in the WHERE clause. After the driver has replaced the WHERE clause, it sends the statement to the data source for execution.

For example, suppose that the application submits the following statement to create a result set:

SELECT Name, Address, Phone FROM Customers FOR UPDATE OF Phone, Address

If the application has set SQL_ATTR_SIMULATE_CURSOR to request a guarantee of uniqueness and if the data source does not provide a pseudo-column that always uniquely identifies a row, the driver calls SQLSpecialColumns for the Customers table, discovers that CustID is the key to the Customers table and adds this to the select list, and strips the FOR UPDATE OF clause:

SELECT Name, Address, Phone, CustID FROM Customers

If the application has not requested a guarantee of uniqueness, the driver strips only the FOR UPDATE OF clause:

SELECT Name, Address, Phone FROM Customers

Suppose the application scrolls through the result set and submits the following positioned update statement for execution, where Cust is the name of the cursor over the result set:

UPDATE Customers SET Address = ?, Phone = ? WHERE CURRENT OF Cust

If the application has not requested a guarantee of uniqueness, the driver replaces the WHERE clause and binds the CustID parameter to the variable in its cache:

UPDATE Customers SET Address = ?, Phone = ? WHERE (CustID = ?)

If the application has not requested a guarantee of uniqueness, the driver replaces the WHERE clause and binds the Name, Address, and Phone parameters in this clause to the variables in its cache:

UPDATE Customers SET Address = ?, Phone = ?
   WHERE (Name = ?) AND (Address = ?) AND (Phone = ?)


Determining the Number of Affected Rows

After an application updates, deletes, or inserts rows, it can call SQLRowCount to determine how many rows were affected. SQLRowCount returns this value whether or not the rows were updated, deleted, or inserted by executing an UPDATE, DELETE, or INSERT statement, by executing a positioned update or delete statement, or by calling SQLSetPos.

If a batch of SQL statements is executed, the count of affected rows might be a total count for all statements in the batch or individual counts for each statement in the batch. For more information, see "Batches of SQL Statements" in Chapter 9: Executing Statements, and "Multiple Results" in Chapter 11: Retrieving Results (Advanced).

The number of affected rows is also returned in the SQL_DIAG_ROW_COUNT diagnostic header field in the diagnostic area associated with the statement handle. However, the data in this field is reset after every function call on the same statement handle, whereas the value returned by SQLRowCount remains the same until a call to SQLBulkOperations, SQLExecute, SQLExecDirect, SQLPrepare, or SQLSetPos.


Updating Data with SQLSetPos

Applications can update or delete any row in the rowset with SQLSetPos. Calling SQLSetPos is a convenient alternative to constructing and executing an SQL statement. It lets an ODBC driver support positioned updates even when the data source does not support positioned SQL statements. It is part of the paradigm of achieving complete database access by means of function calls.

SQLSetPos operates on the current rowset and can be used only after a call to SQLFetchScroll. The application specifies the number of the row to update, delete, or insert, and the driver retrieves the new data for that row from the rowset buffers. SQLSetPos can also be used to designate a specified row as the current row, or to refresh a particular row in the rowset from the data source.

Rowset size is set by a call to SQLSetStmtAttr with an Attribute argument of SQL_ATTR_ROW_ARRAY_SIZE. SQLSetPos uses a new rowset size, however, only after a call to SQLFetch or SQLFetchScroll. For example, if the rowset size is changed, SQLSetPos is called and then SQLFetch or SQLFetchScroll is called, and the call to SQLSetPos uses the old rowset size while SQLFetch or SQLFetchScroll uses the new rowset size.

The first row in the rowset is row number 1. The RowNumber argument in SQLSetPos must identify a row in the rowset; that is, its value must be in the range between 1 and the number of rows that were most recently fetched (which may be less than the rowset size). If RowNumber is 0, the operation applies to every row in the rowset.

Because most interaction with relational databases is done through SQL, SQLSetPos is not widely supported. However, a driver can easily emulate it by constructing and executing an UPDATE or DELETE statement.

To determine what operations SQLSetPos supports, an application calls SQLGetInfo with the SQL_DYNAMIC_CURSOR_ATTRIBUTES1, SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1, SQL_KEYSET_CURSOR_ATTRIBUTES1, or SQL_STATIC_CURSOR_ATTRIBUTES1 information option (depending on the type of the cursor).


Updating Rows in the Rowset with SQLSetPos

The update operation of SQLSetPos makes the data source update one or more selected rows of a table, using data in the application buffers for each bound column (unless the value in the length/indicator buffer is SQL_COLUMN_IGNORE). Columns that are not bound will not be updated.

To update rows with SQLSetPos, the application does the following:

  1. Places the new data values in the rowset buffers. For information on how to send long data with SQLSetPos, see "Long Data and SQLSetPos and SQLBulkOperations," later in this chapter.
  2. Sets the value in the length/indicator buffer of each column as necessary. This is the byte length of the data or SQL_NTS for columns bound to string buffers, the byte length of the data for columns bound to binary buffers, and SQL_NULL_DATA for any columns to be set to NULL.
  3. Sets the value in the length/indicator buffer of those columns which are not to be updated to SQL_COLUMN_IGNORE. Although the application can skip this step and resend existing data, this is inefficient and risks sending values to the data source that were truncated when they were read.
  4. Calls SQLSetPos with Operation set to SQL_UPDATE and RowNumber set to the number of the row to update. If RowNumber is 0, all rows in the rowset are updated.

After SQLSetPos returns, the current row is set to the updated row.

When updating all rows of the rowset (RowNumber is equal to 0), an application can disable the update of certain rows by setting the corresponding elements of the row operation array (pointed to by the SQL_ATTR_ROW_OPERATION_PTR statement attribute) to SQL_ROW_IGNORE. The row operation array corresponds in size and number of elements to the row status array (pointed to by the SQL_ATTR_ROW_STATUS_PTR statement attribute). To update only those rows in the result set that were successfully fetched and have not been deleted from the rowset, the application uses the row status array from the function that fetched the rowset as the row operation array to SQLSetPos.

For every row that is sent to the data source as an update, the application buffers should have valid row data. If the application buffers were filled by fetching and if a row status array has been maintained, its values at each of these row positions should not be SQL_ROW_DELETED, SQL_ROW_ERROR, or SQL_ROW_NOROW.

For example, the following code allows a user to scroll through the Customers table and update, delete, or add new rows. It places the new data in the rowset buffers before calling SQLSetPos to update or add new rows. An extra row is allocated at the end of the rowset buffers to hold new rows; this prevents existing data from being overwritten when data for a new row is placed in the buffers.

#define UPDATE_ROW   100
#define DELETE_ROW   101
#define ADD_ROW      102

SQLUINTEGER    CustIDArray[11];
SQLCHAR        NameArray[11][51], AddressArray[11][51],
               PhoneArray[11][11];
SQLINTEGER     CustIDIndArray[11], NameLenOrIndArray[11],
               AddressLenOrIndArray[11],
               PhoneLenOrIndArray[11];
SQLUSMALLINT   RowStatusArray[10], Action, RowNum;
SQLRETURN      rc;
SQLHSTMT       hstmt;

// Set the SQL_ATTR_ROW_BIND_TYPE statement attribute to use column-wise
// binding. Declare the rowset size with the SQL_ATTR_ROW_ARRAY_SIZE
// statement attribute. Set the SQL_ATTR_ROW_STATUS_PTR statement
// attribute to point to the row status array.
SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, SQL_CURSOR_KEYSET_DRIVEN, 0);
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0);
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, 10, 0);
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, RowStatusArray, 0);

// Bind arrays to the CustID, Name, Address, and Phone columns.
SQLBindCol(hstmt, 1, SQL_C_ULONG, CustIDArray, 0, CustIDIndArray);
SQLBindCol(hstmt, 2, SQL_C_CHAR, NameArray, sizeof(NameArray[0]), NameLenOrIndArray);
SQLBindCol(hstmt, 3, SQL_C_CHAR, AddressArray, sizeof(AddressArray[0]),
            AddressLenOrIndArray);
SQLBindCol(hstmt, 4, SQL_C_CHAR, PhoneArray, sizeof(PhoneArray[0]),
            PhoneLenOrIndArray);

// Execute a statement to retrieve rows from the Customers table.
SQLExecDirect(hstmt, "SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);

// Fetch and display the first 10 rows.
rc = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0);
DisplayData(CustIDArray, CustIDIndArray, NameArray, NameLenOrIndArray, AddressArray,
            AddressLenOrIndArray, PhoneArray, PhoneLenOrIndArray, RowStatusArray);

// Call GetAction to get an action and a row number from the user.
while (GetAction(&Action, &RowNum)) {
   switch (Action) {

      case SQL_FETCH_NEXT:
      case SQL_FETCH_PRIOR:
      case SQL_FETCH_FIRST:
      case SQL_FETCH_LAST:
      case SQL_FETCH_ABSOLUTE:
      case SQL_FETCH_RELATIVE:
         // Fetch and display the requested data.
         SQLFetchScroll(hstmt, Action, RowNum);
         DisplayData(CustIDArray, CustIDIndArray,
                     NameArray, NameLenOrIndArray,
                     AddressArray, AddressLenOrIndArray,
                     PhoneArray, PhoneLenOrIndArray, RowStatusArray);
         break;

      case UPDATE_ROW:
         // Place the new data in the rowset buffers and update the
         // specified row.
         GetNewData(&CustIDArray[RowNum - 1], &CustIDIndArray[RowNum - 1],
                  NameArray[RowNum - 1], &NameLenOrIndArray[RowNum - 1],
                  AddressArray[RowNum - 1], &AddressLenOrIndArray[RowNum - 1],
                  PhoneArray[RowNum - 1], &PhoneLenOrIndArray[RowNum - 1]);
         SQLSetPos(hstmt, RowNum, SQL_UPDATE, SQL_LOCK_NO_CHANGE);
         break;

      case DELETE_ROW:
         // Delete the specified row.
         SQLSetPos(hstmt, RowNum, SQL_DELETE, SQL_LOCK_NO_CHANGE);
         break;

      case ADD_ROW:
         // Place the new data in the rowset buffers at index 10.
         // This is an extra element for new rows so rowset data is
         // not overwritten. Insert the new row. Row 11 corresponds
         // to index 10.
         GetNewData(&CustIDArray[10], &CustIDIndArray[10],
                     NameArray[10], &NameLenOrIndArray[10],
                     AddressArray[10], &AddressLenOrIndArray[10],
                     PhoneArray[10], &PhoneLenOrIndArray[10]);
         SQLSetPos(hstmt, 11, SQL_ADD, SQL_LOCK_NO_CHANGE);
         break;
   }
}

// Close the cursor.
SQLCloseCursor(hstmt);


Deleting Rows in the Rowset with SQLSetPos

The delete operation of SQLSetPos makes the data source delete one or more selected rows of a table. To delete rows with SQLSetPos, the application calls SQLSetPos with Operation set to SQL_DELETE and RowNumber set to the number of the row to delete. If RowNumber is 0, all rows in the rowset are deleted.

After SQLSetPos returns, the deleted row is the current row and its status is SQL_ROW_DELETED. The row cannot be used in any further positioned operations, such as calls to SQLGetData or SQLSetPos.

When deleting all rows of the rowset (RowNumber is equal to 0), the application can prevent the driver from deleting certain rows by using the row operation array, in the same way as for the update operation of SQLSetPos. (See "Updating Rows in the Rowset with SQLSetPos," earlier in this chapter.)

Every row that is deleted should be a row that exists in the result set. If the application buffers were filled by fetching and if a row status array has been maintained, its values at each of these row positions should not be SQL_ROW_DELETED, SQL_ROW_ERROR, or SQL_ROW_NOROW.


Updating Data with SQLBulkOperations

Applications can perform bulk update, delete, fetch, or insertion operations on the underlying table at the data source with a call to SQLBulkOperations. Calling SQLBulkOperations is a convenient alternative to constructing and executing an SQL statement. It lets an ODBC driver support positioned updates even when the data source does not support positioned SQL statements. It is part of the paradigm of achieving complete database access by means of function calls.

SQLBulkOperations operates on the current rowset and can be used only after a call to SQLFetch or SQLFetchScroll. The application specifies the rows to update, delete, or refresh by caching their bookmarks. The driver retrieves the new data for rows to be updated, or the new data to be inserted into the underlying table, from the rowset buffers.

The rowset size to be used by SQLBulkOperations is set by a call to SQLSetStmtAttr with an Attribute argument of SQL_ATTR_ROW_ARRAY_SIZE. Unlike SQLSetPos, which uses a new rowset size only after a call to SQLFetch or SQLFetchScroll, SQLBulkOperations uses the new rowset size after the call to SQLSetStmtAttr.

Because most interaction with relational databases is done through SQL, SQLBulkOperations is not widely supported. However, a driver can easily emulate it by constructing and executing an UPDATE, DELETE, or INSERT statement.

To determine what operations SQLBulkOperation supports, an application calls SQLGetInfo with the SQL_DYNAMIC_CURSOR_ATTRIBUTES1, SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1, SQL_KEYSET_CURSOR_ATTRIBUTES1, or SQL_STATIC_CURSOR_ATTRIBUTES1 information option (depending on the type of the cursor).


Updating Rows by Bookmark with SQLBulkOperations

When updating a row by bookmark, SQLBulkOperations makes the data source update one or more rows of the table. The rows are identified by the bookmark in a bound bookmark column. The row is updated using data in the application buffers for each bound column (except when the value in the length/indicator buffer for a column is SQL_COLUMN_IGNORE). Unbound columns will not be updated.

To update rows by bookmark with SQLBulkOperations, the application:

  1. Retrieves and caches the bookmarks of all rows to be updated. If there is more than one bookmark and column-wise binding is used, the bookmarks are stored in an array; if there is more than one bookmark and row-wise binding is used, the bookmarks are stored in an array of row structures.
  2. Sets the SQL_ATTR_ROW_ARRAY_SIZE statement attribute to the number of bookmarks and binds the buffer containing the bookmark value, or the array of bookmarks, to column 0.
  3. Places the new data values in the rowset buffers. For information on how to send long data with SQLBulkOperations, see "Long Data and SQLSetPos and SQLBulkOperations," later in this chapter.
  4. Sets the value in the length/indicator buffer of each column as necessary. This is the byte length of the data or SQL_NTS for columns bound to string buffers, the byte length of the data for columns bound to binary buffers, and SQL_NULL_DATA for any columns to be set to NULL.
  5. Sets the value in the length/indicator buffer of those columns that are not to be updated to SQL_COLUMN_IGNORE. Although the application can skip this step and resend existing data, this is inefficient and risks sending values to the data source that were truncated when they were read.
  6. Calls SQLBulkOperations with the Operation argument set to SQL_UPDATE_BY_BOOKMARK.

For every row that is sent to the data source as an update, the application buffers should have valid row data. If the application buffers were filled by fetching, if a row status array has been maintained, and if the status value for a row is SQL_ROW_DELETED, SQL_ROW_ERROR, or SQL_ROW_NOROW, invalid data could inadvertently be sent to the data source.


Deleting Rows by Bookmark with SQLBulkOperations

When deleting a row by bookmark, SQLBulkOperations makes the data source delete one or more selected rows of the table. The rows are identified by the bookmark in a bound bookmark column.

To delete rows by bookmark with SQLBulkOperations, the application does the following:

  1. Retrieves and caches the bookmarks of all rows to be deleted. If there is more than one bookmark and column-wise binding is used, the bookmarks are stored in an array; if there is more than one bookmark and row-wise binding is used, the bookmarks are stored in an array of row structures.
  2. Sets the SQL_ATTR_ROW_ARRAY_SIZE statement attribute to the number of bookmarks and binds the buffer containing the bookmark value, or the array of bookmarks, to column 0.
  3. Calls SQLBulkOperations with Operation set to SQL_DELETE_BY_BOOKMARK.


Inserting Rows with SQLBulkOperations

Inserting data with SQLBulkOperations is similar to updating data with SQLBulkOperations because it uses data from the bound application buffers.

So that each column in the new row has a value, all bound columns with a length/indicator value of SQL_COLUMN_IGNORE and all unbound columns must either accept NULL values or have a default.

To insert rows with SQLBulkOperations, the application does the following:

  1. Sets the SQL_ATTR_ROW_ARRAY_SIZE statement attribute to the number of rows to insert and places the new data values in the bound application buffers. For information on how to send long data with SQLBulkOperations, see "Long Data and SQLSetPos and SQLBulkOperations," later in this chapter.
  2. Sets the value in the length/indicator buffer of each column as necessary. This is the byte length of the data or SQL_NTS for columns bound to string buffers, the byte length of the data for columns bound to binary buffers, and SQL_NULL_DATA for any columns to be set to NULL. The application sets the value in the length/indicator buffer of those columns that are to be set to their default (if one exists) or NULL (if one does not) to SQL_COLUMN_IGNORE.
  3. Calls SQLBulkOperations with the Operation argument set to SQL_ADD.

After SQLBulkOperations returns, the current row is unchanged. If the bookmark column (column 0) is bound, SQLBulkOperations returns the bookmarks of the inserted rows in the rowset buffer bound to that column.


Fetching Rows with SQLBulkOperations

Data can be refetched into a rowset using bookmarks by a call to SQLBulkOperations. The rows to be fetched are identified by the bookmarks in a bound bookmark column. Columns with a value of SQL_COLUMN_IGNORE are not fetched.

To perform bulk fetches with SQLBulkOperations, the application does the following:

  1. Retrieves and caches the bookmarks of all rows to be updated. If there is more than one bookmark and column-wise binding is used, the bookmarks are stored in an array; if there is more than one bookmark and row-wise binding is used, the bookmarks are stored in an array of row structures.
  2. Sets the SQL_ATTR_ROW_ARRAY_SIZE statement attribute to the number of rows to fetch and binds the buffer containing the bookmark value, or the array of bookmarks, to column 0.
  3. Sets the value in the length/indicator buffer of each column as necessary. This is the byte length of the data or SQL_NTS for columns bound to string buffers, the byte length of the data for columns bound to binary buffers, and SQL_NULL_DATA for any columns to be set to NULL. The application sets the value in the length/indicator buffer of those columns that are to be set to their default (if one exists) or NULL (if one does not) to SQL_COLUMN_IGNORE.
  4. Calls SQLBulkOperations with the Operation argument set to SQL_FETCH_BY_BOOKMARK.

There is no need for the application to use the row operation array to prevent the operation to be performed on certain columns. The application selects the rows it wants to fetch by copying only the bookmarks for those rows into the bound bookmark array.


Long Data and SQLSetPos and SQLBulkOperations

As is the case with parameters in SQL statements, long data can be sent when updating rows with SQLBulkOperations or SQLSetPos or when inserting rows with SQLBulkOperations. The data is sent in parts, with multiple calls to SQLPutData. Columns for which data is sent at execution time are known as data-at-execution columns.

Note An application actually can send any type of data at execution time with SQLPutData, although only character and binary data can be sent in parts. However, if the data is small enough to fit in a single buffer, there is generally no reason to use SQLPutData. It is much easier to bind the buffer and let the driver retrieve the data from the buffer.

Because long data columns typically are not bound, the application must bind the column before calling SQLBulkOperations or SQLSetPos and unbind it after calling SQLBulkOperations or SQLSetPos. The column must be bound because SQLBulkOperations or SQLSetPos operates only on bound columns and must be unbound so that SQLGetData can be used to retrieve data from the column.

To send data at execution time, the application does the following:

  1. Places a 32-bit value in the rowset buffer instead of a data value. This value will be returned to the application later, so the application should set it to a meaningful value, such as the number of the column or the handle of a file containing data.
  2. Sets the value in the length/indicator buffer to the result of the SQL_LEN_DATA_AT_EXEC(length) macro. This value indicates to the driver that the data for the parameter will be sent with SQLPutData. The length value is used when sending long data to a data source that needs to know how many bytes of long data will be sent so that it can preallocate space. To determine whether a data source requires this value, the application calls SQLGetInfo with the SQL_NEED_LONG_DATA_LEN option. All drivers must support this macro; if the data source does not require the byte length, the driver can ignore it.
  3. Calls SQLBulkOperations or SQLSetPos. The driver discovers that a length/indicator buffer contains the result of the SQL_LEN_DATA_AT_EXEC(length) macro and returns SQL_NEED_DATA as the return value of the function.
  4. Calls SQLParamData in response to the SQL_NEED_DATA return value. If long data needs to be sent, SQLParamData returns SQL_NEED_DATA. In the buffer pointed to by the ValuePtrPtr argument, the driver returns the unique value that the application placed in the rowset buffer. If there is more than one data-at-execution column, the application uses this value to determine which column to send data for; the driver is not required to request data for data-at-execution columns in any particular order.
  5. Calls SQLPutData to send the column data to the driver. If the column data does not fit in a single buffer, as is often the case with long data, the application calls SQLPutData repeatedly to send the data in parts; it is up to the driver and data source to reassemble the data. If the application passes null-terminated string data, the driver or data source must remove the null-termination character as part of the reassembly process.
  6. Calls SQLParamData again to indicate that it has sent all of the data for the column. If there are any data-at-execution columns for which data has not been sent, the driver returns SQL_NEED_DATA and the unique value for the next data-at-execution column; the application returns to step 5. If data has been sent for all data-at-execution columns, the data for the row is sent to the data source. SQLParamData then returns SQL_SUCCESS or SQL_SUCCESS_WITH_INFO and can return any SQLSTATE that SQLBulkOperations or SQLSetPos can return.

After SQLBulkOperations or SQLSetPos returns SQL_NEED_DATA and before data has been completely sent for the last data-at-execution column, the statement is in a Need Data state. In this state, the application can call only SQLPutData, SQLParamData, SQLCancel, SQLGetDiagField, or SQLGetDiagRec; all other functions return SQLSTATE HY010 (Function sequence error). Calling SQLCancel cancels execution of the statement and returns it to its previous state. For more information, see Appendix B: ODBC State Transition Tables.


Chapter 13: Descriptors

A descriptor handle refers to a data structure that holds information about either columns or dynamic parameters.

ODBC functions that operate on column and parameter data implicitly set and retrieve descriptor fields. For instance, when SQLBindCol is called to bind column data, it sets descriptor fields that completely describe the binding. When SQLColAttribute is called to describe column data, it returns data stored in descriptor fields.

An application calling ODBC functions need not concern itself with descriptors. No database operation requires that the application gain direct access to descriptors. However, for some applications, gaining direct access to descriptors streamlines many operations. For example, direct access to descriptors provides a way to rebind column data, which can be more efficient than calling SQLBindCol again.

Note The physical representation of the descriptor is not defined. Applications gain direct access to a descriptor only by manipulating its fields by calling ODBC functions with the descriptor handle.


Types of Descriptors

A descriptor is used to describe one of the following:

  • A set of zero or more parameters. A parameter descriptor can be used to describe:
    • The application parameter buffer, which contains either the input dynamic arguments as set by the application or the output dynamic arguments following the execution of a CALL statement of SQL.
    • The implementation parameter buffer. For input dynamic arguments, this contains the same arguments as the application parameter buffer, after any data conversion the application may specify. For output dynamic arguments, this contains the returned arguments, before any data conversion that the application may specify.

    For input dynamic arguments, the application must operate on an application parameter descriptor before executing any SQL statement that contains dynamic parameter markers. For both input and output dynamic arguments, the application can specify different data types from those in the implementation parameter descriptor to achieve data conversion.

  • A single row of database data. A row descriptor can be used to describe:
    • The implementation row buffer, which contains the row from the database. (These buffers conceptually contain data as written to or read from the database. However, the stored form of database data is not specified. A database could perform additional conversion on the data from its form in the implementation buffer.)
    • The application row buffer, which contains the row of data as presented to the application, following any data conversion that the application may specify.

    The application operates on the application row descriptor in any case where column data from the database must appear in application variables. To achieve data conversion of column data, the application can specify different data types from those in the implementation row descriptor.

The descriptor types are summarized in the following table.

Buffer type | Rows | Dynamic parameters
Application buffer | Application row descriptor (ARD) | Application parameter descriptor (APD)
Implementation buffer | Implementation row descriptor (IRD) | Implementation parameter descriptor (IPD)

For either the parameter or the row buffers, if the application specifies different data types in corresponding records of the implementation and application descriptors, the driver performs data conversion when it uses the descriptors. For example, it may convert numeric and datetime values to character-string format. (For valid conversions, see Appendix D: Data Types.)

A descriptor can perform different roles. Different statements can share any descriptor that the application explicitly allocates. A row descriptor in one statement can serve as a parameter descriptor in another statement.

It is always known whether a given descriptor is an application descriptor or an implementation descriptor, even if the descriptor has not yet been used in a database operation. For the descriptors that the implementation implicitly allocates, the implementation records the predefined row relative to the statement handle. Any descriptor that the application allocates by calling SQLAllocHandle is an application descriptor.


Descriptor Fields

Descriptors contain header and record fields that completely describe columns or parameters.

A descriptor contains a single copy of the following header fields. Changing a header field affects all columns or parameters.

SQL_DESC_ALLOC_TYPE | SQL_DESC_BIND_TYPE
SQL_DESC_ARRAY_SIZE | SQL_DESC_COUNT
SQL_DESC_ARRAY_STATUS_PTR | SQL_DESC_ROWS_PROCESSED_PTR
SQL_DESC_BIND_OFFSET_PTR |

A descriptor contains zero or more descriptor records. Each record describes a column or parameter, depending on the type of descriptor. When a new column or parameter is bound, a new record is added to the descriptor. When a column or parameter is unbound, a record is removed from the descriptor. Each record contains a single copy of the following fields:

SQL_DESC_AUTO_UNIQUE_VALUE | SQL_DESC_LOCAL_TYPE_NAME
SQL_DESC_BASE_COLUMN_NAME | SQL_DESC_NAME
SQL_DESC_BASE_TABLE_NAME | SQL_DESC_NULLABLE
SQL_DESC_CASE_SENSITIVE | SQL_DESC_OCTET_LENGTH
SQL_DESC_CATALOG_NAME | SQL_DESC_OCTET_LENGTH_PTR
SQL_DESC_CONCISE_TYPE | SQL_DESC_PARAMETER_TYPE
SQL_DESC_DATA_PTR | SQL_DESC_PRECISION
SQL_DESC_DATETIME_INTERVAL_CODE | SQL_DESC_SCALE
SQL_DESC_DATETIME_INTERVAL_PRECISION | SQL_DESC_SCHEMA_NAME
SQL_DESC_DISPLAY_SIZE | SQL_DESC_SEARCHABLE
SQL_DESC_FIXED_PREC_SCALE | SQL_DESC_TABLE_NAME
SQL_DESC_INDICATOR_PTR | SQL_DESC_TYPE
SQL_DESC_LABEL | SQL_DESC_TYPE_NAME
SQL_DESC_LENGTH | SQL_DESC_UNNAMED
SQL_DESC_LITERAL_PREFIX | SQL_DESC_UNSIGNED
SQL_DESC_LITERAL_SUFFIX | SQL_DESC_UPDATABLE

Many statement attributes correspond to the header field of a descriptor. Setting these attributes through a call to SQLSetStmtAttr and setting the corresponding descriptor header field by calling SQLSetDescField have the same effect. The same is true for SQLGetStmtAttr and SQLGetDescField, both of which retrieve the same information. Calling the statement functions instead of the descriptor functions has the advantage that a descriptor handle does not have to be retrieved.

The following header fields can be set by setting statement attributes:

SQL_DESC_ARRAY_SIZE | SQL_DESC_BIND_TYPE
SQL_DESC_ARRAY_STATUS_PTR | SQL_DESC_ROWS_PROCESSED_PTR
SQL_DESC_BIND_OFFSET_PTR |


Record Count

The SQL_DESC_COUNT header field of a descriptor is the one-based index of the highest-numbered record that contains data. This field is not a count of all columns or parameters that are bound. When a descriptor is allocated, the initial value of SQL_DESC_COUNT is 0.

The driver takes any action necessary to allocate and maintain whatever storage it requires to hold descriptor information. The application does not explicitly specify the size of a descriptor nor allocate new records. When the application provides information for a descriptor record whose number is higher than the value of SQL_DESC_COUNT, the driver automatically increases SQL_DESC_COUNT. When the application unbinds the highest-numbered descriptor record, the driver automatically decreases SQL_DESC_COUNT to contain the number of the highest remaining bound record.


Bound Descriptor Records

When the application sets the SQL_DESC_DATA_PTR field of a descriptor record so that it no longer contains a null value, the record is said to be bound.

If the descriptor is an APD, each bound record constitutes a bound parameter. For input parameters, the application must bind a parameter for each dynamic parameter marker in the SQL statement before executing the statement. For output parameters, the application need not bind the parameter.

If the descriptor is an ARD, which describes a row of database data, each bound record constitutes a bound column.


Deferred Fields

The values of deferred fields are not used when they are set, but the driver saves the addresses of the variables for a deferred effect. For an application parameter descriptor, the driver uses the contents of the variables at the time of the call to SQLExecDirect or SQLExecute. For an application row descriptor, the driver uses the contents of the variables at the time of the fetch.

The following are deferred fields:

  • The SQL_DESC_DATA_PTR and SQL_DESC_INDICATOR_PTR fields of a descriptor record.
  • The SQL_DESC_OCTET_LENGTH_PTR field of an application descriptor record.
  • In the case of a multirow fetch, the SQL_DESC_ARRAY_STATUS_PTR and SQL_DESC_ROWS_PROCESSED_PTR fields of a descriptor header.

When a descriptor is allocated, the deferred fields of each descriptor record initially have a null value. The meaning of the null value is as follows:

  • If SQL_DESC_ARRAY_STATUS_PTR has a null value, a multirow fetch fails to return this component of the per-row diagnostic information.
  • If SQL_DESC_DATA_PTR has a null value, the record is unbound.
  • If the SQL_DESC_OCTET_LENGTH_PTR field of an ARD has a null value, the driver does not return length information for that column.
  • If the SQL_DESC_OCTET_LENGTH_PTR field of an APD has a null value and the parameter is a character string, the driver assumes that string is null-terminated. For output dynamic parameters, a null value in this field prevents the driver from returning length information. (If the SQL_DESC_TYPE field does not indicate a character-string parameter, the SQL_DESC_OCTET_LENGTH_PTR field is ignored.)

The application must not deallocate or discard variables used for deferred fields between the time it associates them with the fields and the time the driver reads or writes them.


Consistency Check

A consistency check is performed by the driver automatically whenever an application sets the SQL_DESC_DATA_PTR field of the APD, ARD, or IPD. Whenever this field is set, the driver checks that the value of the SQL_DESC_TYPE field and the values applicable to the SQL_DESC_TYPE field in the same record are valid and consistent.

The SQL_DESC_DATA_PTR field of an IPD is not normally set; however, an application can do so to force a consistency check of IPD fields. The value that the SQL_DESC_DATA_PTR field of the IPD is set to is not actually stored and cannot be retrieved by a call to SQLGetDescField or SQLGetDescRec; the setting is made only to force the consistency check. A consistency check cannot be performed on an IRD.

For more information on the consistency check, see SQLSetDescRec.


Allocating and Freeing Descriptors

Descriptors are either implicitly or explicitly allocated, as described in the following sections.

  • Implicitly Allocated Descriptors
  • Explicitly Allocated Descriptors
  • Initialization of Descriptor Fields
  • Automatic Population of the IPD
  • Freeing Descriptors


Implicitly Allocated Descriptors

When a statement handle is allocated, the application implicitly allocates one set of four descriptors. The application can obtain the handles of these implicitly allocated descriptors as attributes of the statement handle. When the application frees the statement handle, the driver frees all implicitly allocated descriptors on that handle.


Explicitly Allocated Descriptors

An application can explicitly allocate an application descriptor on a connection at any time it is connected to the database. By specifying that descriptor handle as an attribute of a statement handle using SQLSetStmtAttr, the application directs the driver to use that descriptor in place of the corresponding implicitly allocated application descriptors. The application cannot specify alternate implementation descriptors.

An application can associate an explicitly allocated descriptor with more than one statement. Only when an application is actually connected to the database can a descriptor be an explicitly allocated descriptor. The application can free such a descriptor explicitly, or implicitly by freeing its connection.


Initialization of Descriptor Fields

When an application row descriptor is allocated, its fields receive initial values as indicated in SQLSetDescField. The initial value of the SQL_DESC_TYPE field is SQL_DEFAULT. This provides for a standard treatment of database data for presentation to the application. The application may specify different treatment of the data by setting fields of the descriptor record.

The initial value of SQL_DESC_ARRAY_SIZE in the descriptor header is 1. The application can modify this field to enable multirow fetch.

The concept of a default value is not valid for the fields of an IRD. An application can gain access to the fields of an IRD only when there is a prepared or executed statement associated with it.

Certain fields of an IPD are defined only after the IPD has been automatically populated by the driver. If not, they are undefined. These fields are SQL_DESC_CASE_SENSITIVE, SQL_DESC_FIXED_PREC_SCALE, SQL_DESC_TYPE_NAME, SQL_DESC_UNSIGNED, and SQL_DESC_LOCAL_TYPE_NAME.


Automatic Population of the IPD

Some drivers are capable of setting the fields of the IPD after a parameterized query has been prepared. The descriptor fields are automatically populated with information about the parameter, including the data type, precision, scale, and other characteristics. This is equivalent to supporting SQLDescribeParam. This information can be particularly valuable to an application when it has no other way to discover it, such as when an ad hoc query is performed with parameters that the application does not know about.

An application determines whether the driver supports automatic population by calling SQLGetConnectAttr with an Attribute of SQL_ATTR_AUTO_IPD. If SQL_TRUE is returned, the driver supports it and the application can enable it by setting the SQL_ATTR_ENABLE_AUTO_IPD statement attribute to SQL_TRUE.

When automatic population is supported and enabled, the driver populates the fields of the IPD after an SQL statement containing parameter markers has been prepared by a call to SQLPrepare. An application can retrieve this information by calling SQLGetDescField or SQLGetDescRec, or SQLDescribeParam. The application can use the information to bind the most appropriate application buffer for a parameter or to specify a data conversion for it.

Automatic population of the IPD might produce a performance penalty. An application can turn it off by resetting the SQL_ATTR_ENABLE_AUTO_IPD statement attribute to SQL_FALSE (the default value).


Freeing Descriptors

Explicitly allocated descriptors can be freed either explicitly, by calling SQLFreeHandle with HandleType of SQL_HANDLE_DESC, or implicitly, when the connection handle is freed. When an explicitly allocated descriptor is freed, all statement handles to which the freed descriptor applied automatically revert to the descriptors implicitly allocated for them.

Implicitly allocated descriptors can be freed only by calling SQLDisconnect, which drops any statements or descriptors open on the connection, or by calling SQLFreeHandle with a HandleType of SQL_HANDLE_STMT to free a statement handle and all the implicitly allocated descriptors associated with the statement. An implicitly allocated descriptor cannot be freed by calling SQLFreeHandle with a HandleType of SQL_HANDLE_DESC.

Even when freed, an implicitly allocated descriptor remains valid, and SQLGetDescField can be called on its fields.


Getting and Setting Descriptor Fields

The following sections describe the methods an application can use to retrieve or set the values in descriptor fields.

  • Obtaining Descriptor Handles
  • Retrieving the Values in Descriptor Fields
  • Setting Descriptor Fields
  • Copying Descriptors
  • Using Concise Functions


Obtaining Descriptor Handles

An application obtains the handle of any explicitly allocated descriptor as an output argument of the call to SQLAllocHandle. The handle of an implicitly allocated descriptor is obtained by calling SQLGetStmtAttr.


Retrieving the Values in Descriptor Fields

An application can call SQLGetDescField to obtain a single field of a descriptor record. SQLGetDescField gives the application access to all the descriptor fields defined in ODBC, and to driver-defined fields as well.

SQLGetDescRec can be called to retrieve the settings of multiple descriptor fields that affect the data type and storage of column or parameter data.


Setting Descriptor Fields

To modify the fields of a descriptor, an application can call SQLSetDescField. Some fields are read-only and cannot be set. (See the SQLSetDescField function description.)

Descriptor record fields are set with a record number (RecNumber) of 1 or higher, while descriptor header fields are set with a record number of 0. A record number of 0 is also used to set bookmark fields, in accordance with the convention that bookmarks are contained in column 0. This might leave the impression that bookmark fields are contained within the descriptor header, but this is not the case. Bookmark fields are distinct from header fields.

When setting fields individually, the application should follow the sequence defined in SQLSetDescField. Setting some fields causes the driver to set other fields. This ensures that the descriptor is always ready to use once the application has specified a data type. When the application sets the SQL_DESC_TYPE field, the driver checks that other fields that specify the type are valid and consistent.

If a function call that would set a descriptor field fails, the contents of the descriptor field are undefined after the failed function call.


Copying Descriptors

The SQLCopyDesc function is called to copy the fields of one descriptor to another descriptor. Fields can be copied only to an application descriptor or an IPD, but not to an IRD. Fields can be copied from any type of descriptor. Only those fields that are defined for both the source and target descriptors are copied. SQLCopyDesc does not copy the SQL_DESC_ALLOC_TYPE field, because a descriptor's allocation type cannot be changed. Copied fields overwrite the existing fields.

An ARD on one statement handle can serve as the APD on another statement handle. This allows an application to copy rows between tables without copying data at the application level. To do this, a row descriptor that describes a fetched row of a table is reused as a parameter descriptor for a parameter in an INSERT statement. The SQL_MAX_CONCURRENT_ACTIVITIES information type must be greater than 1 for this operation to succeed.


Using Concise Functions

Some ODBC functions gain implicit access to descriptors. Application writers may find them more convenient than calling SQLSetDescField or SQLGetDescField. These functions are called concise functions because they perform a number of functions, including setting or getting descriptor fields. Some concise functions let an application set or retrieve several related descriptor fields in a single function call.

Concise functions can be called without first retrieving a descriptor handle for use as an argument. These functions work with the descriptor fields associated with the statement handle that they are called on.

The concise functions SQLBindCol and SQLBindParameter bind a column or parameter by setting the descriptor fields that correspond to their arguments. Each of these functions performs more tasks than simply setting descriptors. SQLBindCol and SQLBindParameter provide a complete specification of the binding of a data column or dynamic parameter. An application can, however, change individual details of a binding by calling SQLSetDescField or SQLSetDescRec and can completely bind a column or parameter by making a series of suitable calls to these functions.

The concise functions SQLColAttribute, SQLDescribeCol, SQLDescribeParam, SQLNumParams, and SQLNumResultCols retrieve values in descriptor fields.

SQLSetDescRec and SQLGetDescRec are concise functions that, with one call, set or get multiple descriptor fields that affect the data type and storage of column or parameter data. SQLSetDescRec is an effective way to change the binding of column or parameter data in one step.

SQLSetStmtAttr and SQLGetStmtAttr serve as concise functions in some cases. (See "Descriptor Fields," earlier in this chapter.)


Chapter 14: Transactions

A transaction is a unit of work that is done as a single, atomic operation; that is, the operation succeeds or fails as a whole. For example, consider transferring money from one bank account to another. This involves two steps: withdrawing the money from the first account and depositing it in the second. It is important that both steps succeed; it is not acceptable for one step to succeed and the other to fail. A database that supports transactions is able to guarantee this.

Transactions can be completed by either being committed or being rolled back. When a transaction is committed, the changes made in that transaction are made permanent. When a transaction is rolled back, the affected rows are returned to the state they were in before the transaction was started. To extend the account transfer example, an application executes one SQL statement to debit the first account and a different SQL statement to credit the second account. If both statements succeed, the application then commits the transaction. But if either statement fails for any reason, the application rolls back the transaction. In either case, the application guarantees a consistent state at the end of the transaction.

A single transaction can encompass multiple database operations that occur at different times. If other transactions had complete access to the intermediate results, the transactions might interfere with one another. For example, suppose one transaction inserts a row, a second transaction reads that row, and the first transaction is rolled back. The second transaction now has data for a row that does not exist.

To solve this problem, there are various schemes to isolate transactions from one another. Transaction isolation is generally implemented by locking rows, which precludes more than one transaction from using the same row at the same time. In some databases, locking a row may also lock other rows.

With increased transaction isolation comes reduced concurrency, or the ability of two transactions to use the same data at the same time. For more information, see "Setting the Transaction Isolation Level," later in this chapter.

A full discussion of transactions is well beyond the scope of this book. For more information on transactions, see the "Recommended Reading" section of this guide's introduction, "About the ODBC Programmer's Reference."


Transactions in ODBC

Transactions in ODBC are completed at the connection level; that is, when an application completes a transaction, it commits or rolls back all work done through all statement handles on that connection.


Transaction Support

The degree of support for transactions is driver-defined. ODBC is designed to be implemented on a single-user or desktop database that has no need to manage multiple updates to its data. Moreover, some databases that support transactions do so only for the Data Manipulation Language (DML) statements of SQL; there are restrictions or special transaction semantics regarding the use of Data Definition Language (DDL) when a transaction is active. That is, there may be transaction support for multiple simultaneous updates to tables but not for changing the number and definition of tables during a transaction.

An application determines whether transactions are supported, whether DDL can be included in a transaction, and any special effects of including DDL in a transaction, by calling SQLGetInfo with the SQL_TXN_CAPABLE option. For more information, see the SQLGetInfo function description.

If the driver does not support transactions but the application has the ability (using an API other than ODBC) to lock and unlock data, applications can achieve transaction support by locking and unlocking records and tables as needed. To implement the account-transfer example, the application would lock the records for both accounts, copy the current values, debit the first account, credit the second account, and unlock the records. If any steps failed, the application would reset the accounts using the copies.

Even data sources that support transactions might not be able to support more than one transaction at a time within a particular environment. Applications call SQLGetInfo with the SQL_MULTIPLE_ACTIVE_TXN option to determine whether a data source can support simultaneous active transactions on more than one connection in the same environment. Because there is one transaction per connection, this is only interesting to applications that have multiple connections to the same data source.


Commit Mode

Transactions in ODBC can be in one of two modes: auto-commit mode or manual-commit mode, as described in the following sections.

  • Auto-Commit Mode
  • Manual-Commit Mode
  • Setting the Commit Mode


Auto-Commit Mode

In auto-commit mode, every database operation is a transaction that is committed when performed. This mode is suitable for many real-world transactions that consist of a single SQL statement. It is unnecessary to delimit or specify completion of these transactions. In databases without transaction support, auto-commit mode is the only supported mode. In such databases, statements are committed when they are executed and there is no way to roll them back; they are therefore always in auto-commit mode.

If the underlying DBMS does not support auto-commit mode transactions, the driver can emulate them by manually committing each SQL statement as it is executed.

If a batch of SQL statements is executed in auto-commit mode, it is data source–specific when the statements in the batch are committed. They can be committed as they are executed or as a whole after the entire batch has been executed. Some data sources may support both of these behaviors and may provide a way of selecting one or the others. In particular, if an error occurs in the middle of the batch, it is data source–specific whether the already-executed statements are committed or rolled back. Thus, interoperable applications that use batches and require them to be committed or rolled back as a whole should execute batches only in manual-commit mode.


Manual-Commit Mode

In manual-commit mode, applications must explicitly complete transactions by calling SQLEndTran to commit them or roll them back. This is the normal transaction mode for most relational databases.

Transactions in ODBC do not have to be explicitly initiated. Instead, a transaction begins implicitly whenever the application starts operating on the database. If the data source requires explicit transaction initiation, the driver must provide it whenever the application executes a statement requiring a transaction and there is no current transaction.


Setting the Commit Mode

Applications specify the transaction mode with the SQL_ATTR_AUTOCOMMIT connection attribute. By default, ODBC transactions are in auto-commit mode (unless SQLSetConnectAttr and SQLSetConnectOption are not supported, which is unlikely). Switching from manual-commit mode to auto-commit mode automatically commits any open transaction on the connection.


Committing and Rolling Back Transactions

To commit or roll back a transaction in manual-commit mode, an application calls SQLEndTran. Drivers for DBMSs that support transactions typically implement this function by executing a COMMIT or ROLLBACK statement. The Driver Manager does not call SQLEndTran when the connection is in auto-commit mode; it simply returns SQL_SUCCESS, even if the application attempts to roll back the transaction. Because drivers for DBMSs that do not support transactions are always in auto-commit mode, they can either implement SQLEndTran to return SQL_SUCCESS without doing anything or not implement it at all.

Note Applications should not commit or roll back transactions by executing COMMIT or ROLLBACK statements with SQLExecute or SQLExecDirect. The effects of doing this are undefined. Possible problems include the driver no longer knowing when a transaction is active and these statements failing against data sources that do not support transactions. These applications should call SQLEndTran instead.

If an application passes the environment handle to SQLEndTran but does not pass a connection handle, the Driver Manager conceptually calls SQLEndTran with the environment handle for each driver that has one or more active connections in the environment. The driver then commits the transactions on each connection in the environment. However, it is important to realize that neither the driver nor the Driver Manager performs a two-phase commit on the connections in the environment; this is merely a programming convenience to simultaneously call SQLEndTran for all connections in the environment.

(A two-phase commit is generally used to commit transactions that are spread across multiple data sources. In its first phase, the data sources are polled as to whether they can commit their part of the transaction. In the second phase, the transaction is actually committed on all data sources. If any data sources reply in the first phase that they cannot commit the transaction, the second phase does not occur.)


Effect of Transactions on Cursors and Prepared Statements

Committing or rolling back a transaction has the following effect on cursors and access plans:

  • All cursors are closed, and access plans for prepared statements on that connection are deleted.
  • All cursors are closed, and access plans for prepared statements on that connection remain intact.
  • All cursors remain open, and access plans for prepared statements on that connection remain intact.

For example, suppose a data source exhibits the first behavior in this list, the most restrictive of these behaviors. Now suppose an application does the following:

  1. Sets the commit mode to manual-commit.
  2. Creates a result set of sales orders on statement 1.
  3. Creates a result set of the lines in a sales order on statement 2, when the user highlights that order.
  4. Calls SQLExecute to execute a positioned update statement that has been prepared on statement 3, when the user updates a line.
  5. Calls SQLEndTran to commit the positioned update statement.

Because of the data source's behavior, the call to SQLEndTran in step 5 causes it to close the cursors on statements 1 and 2 and to delete the access plan on all statements. The application must reexecute statements 1 and 2 to re-create the result sets and reprepare the statement on statement 3.

In auto-commit mode, functions other than SQLEndTran commit transactions:

  • SQLExecute or SQLExecDirect. In the preceding example, the call to SQLExecute in step 4 commits a transaction. This causes the data source to close the cursors on statements 1 and 2 and delete the access plan on all statements on that connection.
  • SQLBulkOperations or SQLSetPos. In the preceding example, suppose that in step 4 the application calls SQLSetPos with the SQL_UPDATE option on statement 2, instead of executing a positioned update statement on statement 3. This commits a transaction and causes the data source to close the cursors on statements 1 and 2, and discards all access plans on that connection.
  • SQLCloseCursor. In the preceding example, suppose that when the user highlights a different sales order, the application calls SQLCloseCursor on statement 2 before creating a result of the lines for the new sales order. The call to SQLCloseCursor commits the SELECT statement that created the result set of lines and causes the data source to close the cursor on statement 1, and then discards all access plans on that connection.

Applications, especially screen-based applications in which the user scrolls around the result set and updates or deletes rows, must be careful to code around this behavior.

To determine how a data source behaves when a transaction is committed or rolled back, an application calls SQLGetInfo with the SQL_CURSOR_COMMIT_BEHAVIOR and SQL_CURSOR_ROLLBACK_BEHAVIOR options.


Transaction Isolation

Transaction isolation refers to the degree of interaction between multiple concurrent transactions. To see why this is important, you'll need to first look at the idea of serializability.


Serializability

Ideally, transactions should be serializable. Transactions are said to be serializable if the results of running transactions simultaneously are the same as the results of running them serially—that is, one after the other. It is not important which transaction executes first, only that the result does not reflect any mixing of the transactions.

For example, suppose transaction A multiplies data values by 2 and transaction B adds 1 to data values. Now suppose that there are two data values: 0 and 10. If these transactions are run one after the other, the new values will be 1 and 21 if transaction A is run first, or 2 and 22 if transaction B is run first. But what if the order in which the two transactions are run is different for each value? If transaction A is run first on the first value and transaction B is run first on the second value, the new values are 1 and 22. If this order is reversed, the new values are 2 and 21. The transactions are serializable if 1, 21 and 2, 22 are the only possible results. The transactions are not serializable if 1, 22 or 2, 21 is a possible result.

So why is serializability desirable? In other words, why is it important that it appears that one transaction finishes before the next transaction starts? Consider the following problem. A salesman is entering orders at the same time a clerk is sending out bills. Suppose the salesman enters an order from Company X but does not commit it; the salesman is still talking to the representative from Company X. The clerk requests a list of all open orders and discovers the order for Company X and sends them a bill. Now the representative from Company X decides they want to change their order, so the salesman changes it before committing the transaction. Company X gets an incorrect bill.

If the salesman's and clerk's transactions were serializable, this problem would never have occurred. Either the salesman's transaction would have finished before the clerk's transaction started, in which case the clerk would have sent out the correct bill, or the clerk's transaction would have finished before the salesman's transaction started, in which case the clerk would not have sent a bill to Company X at all.


Transaction Isolation Levels

Transaction isolation levels are a measure of the extent to which transaction isolation succeeds. In particular, transaction isolation levels are defined by the presence or absence of the following phenomena:

  • Dirty Reads. A dirty read occurs when a transaction reads data that has not yet been committed. For example, suppose transaction 1 updates a row. Transaction 2 reads the updated row before transaction 1 commits the update. If transaction 1 rolls back the change, transaction 2 will have read data that is considered never to have existed.
  • Nonrepeatable Reads. A nonrepeatable read occurs when a transaction reads the same row twice but gets different data each time. For example, suppose transaction 1 reads a row. Transaction 2 updates or deletes that row and commits the update or delete. If transaction 1 rereads the row, it retrieves different row values or discovers that the row has been deleted.
  • Phantoms. A phantom is a row that matches the search criteria but is not initially seen. For example, suppose transaction 1 reads a set of rows that satisfy some search criteria. Transaction 2 generates a new row (through either an update or an insert) that matches the search criteria for transaction 1. If transaction 1 reexecutes the statement that reads the rows, it gets a different set of rows.

The four transaction isolation levels (as defined by SQL-92) are defined in terms of these phenomena. In the following table, an "X" marks each phenomenon that can occur.

Transaction isolation level | Dirty reads | Nonrepeatable reads | Phantoms
Read uncommitted | X | X | X
Read committed | -- | X | X
Repeatable read | -- | -- | X
Serializable | -- | -- | --

The following table describes simple ways that a DBMS might implement the transaction isolation levels.

Important Most DBMSs use more complex schemes than these to increase concurrency. These examples are provided for illustration purposes only. In particular, ODBC does not prescribe how particular DBMSs isolate transactions from each other.

Transaction isolation | Possible implementation
Read uncommitted | Transactions are not isolated from each other. If the DBMS supports other transaction isolation levels, it ignores whatever mechanism it uses to implement those levels. So that they do not adversely affect other transactions, transactions running at the Read Uncommitted level are usually read-only.
Read committed The transaction waits until rows write-locked by other transactions are unlocked; this prevents it from reading any "dirty" data.

The transaction holds a read lock (if it only reads the row) or write lock (if it updates or deletes the row) on the current row to prevent other transactions from updating or deleting it. The transaction releases read locks when it moves off the current row. It holds write locks until it is committed or rolled back.
Repeatable read The transaction waits until rows write-locked by other transactions are unlocked; this prevents it from reading any "dirty" data.

The transaction holds read locks on all rows it returns to the application and write locks on all rows it inserts, updates, or deletes. For example, if the transaction includes the SQL statement SELECT * FROM Orders, the transaction read-locks rows as the application fetches them. If the transaction includes the SQL statement DELETE FROM Orders WHERE Status = 'CLOSED', the transaction write-locks rows as it deletes them.

Because other transactions cannot update or delete these rows, the current transaction avoids any nonrepeatable reads. The transaction releases its locks when it is committed or rolled back.
Serializable The transaction waits until rows write-locked by other transactions are unlocked; this prevents it from reading any "dirty" data.

The transaction holds a read lock (if it only reads rows) or write lock (if it can update or delete rows) on the range of rows it affects. For example, if the transaction includes the SQL statement SELECT * FROM Orders, the range is the entire Orders table; the transaction read-locks the table and does not allow any new rows to be inserted into it. If the transaction includes the SQL statement DELETE FROM Orders WHERE Status = 'CLOSED', the range is all rows with a Status of "CLOSED"; the transaction write-locks all rows in the Orders table with a Status of "CLOSED" and does not allow any rows to be inserted or updated such that the resulting row has a Status of "CLOSED".

Because other transactions cannot update or delete the rows in the range, the current transaction avoids any nonrepeatable reads. Because other transactions cannot insert any rows in the range, the current transaction avoids any phantoms. The transaction releases its lock when it is committed or rolled back.

It is important to note that the transaction isolation level does not affect a transaction's ability to see its own changes; transactions can always see any changes they make. For example, a transaction might consist of two UPDATE statements, the first of which raises the pay of all employees by 10 percent and the second of which sets the pay of any employees over some maximum amount to that amount. This succeeds as a single transaction only because the second UPDATE statement can see the results of the first.


Setting the Transaction Isolation Level

To set the transaction isolation level, an application uses the SQL_ATTR_TXN_ISOLATION connection attribute. If the data source does not support the requested isolation level, the driver or data source can set a higher level. To determine what transaction isolation levels a data source supports and what the default isolation level is, an application calls SQLGetInfo with the SQL_TXN_ISOLATION_OPTION and SQL_DEFAULT_TXN_ISOLATION options, respectively.

Higher levels of transaction isolation offer the most protection for the integrity of database data. Serializable transactions are guaranteed to be unaffected by other transactions and therefore guaranteed to maintain database integrity.

However, a higher level of transaction isolation can cause slower performance because it increases the chances that the application will have to wait for locks on data to be released. An application may specify a lower level of isolation to increase performance in the following cases:

  • When it can be guaranteed that no other transactions exist that might interfere with an application's transactions. This situation occurs only in limited circumstances, such as when one person in a small company maintains dBASE files containing personnel data on one computer and does not share these files.
  • When speed is more critical than accuracy and any errors are likely to be small. For example, suppose that a company makes many small sales and that large sales are rare. A transaction that estimates the total value of all open sales might safely use the Read Uncommitted isolation level. Although the transaction would include orders in the process of being opened or closed that are subsequently rolled back, these would tend to cancel each other out and the transaction would be much faster because it is not blocked each time it encounters such an order.

For more information, see the section "Optimistic Concurrency," later in this chapter.


Scrollable Cursors and Transaction Isolation

The following table lists the factors governing the visibility of changes.

Changes made by: | Visibility depends on:
Cursor | Cursor type, cursor implementation
Other statements in same transaction | Cursor type
Statements in other transactions | Cursor type, transaction isolation level

These factors are shown in the following illustration.

Visibility of Changes

The following table summarizes the ability of each cursor type to detect changes made by itself, by other operations in its own transaction, and by other transactions. The visibility of the latter changes depends on the cursor type and the isolation level of the transaction containing the cursor.

Cursor type\action | Self | Own Txn | Othr Txn (RU[a]) | Othr Txn (RC[a]) | Othr Txn (RR[a]) | Othr Txn (S[a])
Static |  |  |  |  |  |
Insert | Maybe[b] | No | No | No | No | No
Update | Maybe[b] | No | No | No | No | No
Delete | Maybe[b] | No | No | No | No | No
Keyset-driven |  |  |  |  |  |
Insert | Maybe[b] | No | No | No | No | No
Update | Yes | Yes | Yes | Yes | No | No
Delete | Maybe[b] | Yes | Yes | Yes | No | No
Dynamic |  |  |  |  |  |
Insert | Yes | Yes | Yes | Yes | Yes | No
Update | Yes | Yes | Yes | Yes | No | No
Delete | Yes | Yes | Yes | Yes | No | No

[a] The letters in parentheses indicate the isolation level of the transaction containing the cursor; the isolation level of the other transaction (in which the change was made) is irrelevant.

RU: Read uncommitted
RC: Read committed
RR: Repeatable read
S: Serializable

[b] Depends on how the cursor is implemented. Whether the cursor can detect such changes is reported through the SQL_STATIC_SENSITIVITY option in SQLGetInfo.


Concurrency Control

Concurrency is the ability of two transactions to use the same data at the same time, and with increased transaction isolation usually comes reduced concurrency. This is because transaction isolation is usually implemented by locking rows, and as more rows are locked, fewer transactions can be completed without being blocked at least temporarily by a locked row. While reduced concurrency is generally accepted as a trade-off for the higher transaction isolation levels necessary to maintain database integrity, it can become a problem in interactive applications with high read/write activity that use cursors.

For example, suppose an application executes the SQL statement SELECT * FROM Orders. It calls SQLFetchScroll to scroll around the result set and allows the user to update, delete, or insert orders. After the user updates, deletes, or inserts an order, the application commits the transaction.

If the isolation level is Repeatable Read, the transaction might—depending on how it is implemented—lock each row returned by SQLFetchScroll. If the isolation level is Serializable, the transaction might lock the entire Orders table. In either case, the transaction releases its locks only when it is committed or rolled back. So if the user spends a lot of time reading orders and very little time updating, deleting, or inserting them, the transaction could easily lock a large number of rows, making them unavailable to other users.

This is a problem even if the cursor is read-only and the application allows the user to read only existing orders. In this case, the application commits the transaction, and releases locks, when it calls SQLCloseCursor (in auto-commit mode) or SQLEndTran (in manual-commit mode).


Concurrency Types

To solve the problem of reduced concurrency in cursors, ODBC exposes four different types of cursor concurrency:

  • Read-only. The cursor can read data but cannot update or delete data. This is the default concurrency type. Although the DBMS might lock rows to enforce the Repeatable Read and Serializable isolation levels, it can use read locks instead of write locks. This results in higher concurrency because other transactions can at least read the data.
  • Locking. The cursor uses the lowest level of locking necessary to make sure it can update or delete rows in the result set. This usually results in very low concurrency levels, especially at the Repeatable Read and Serializable transaction isolation levels.
  • Optimistic concurrency using row versions and optimistic concurrency using values. The cursor uses optimistic concurrency: It updates or deletes rows only if they have not changed since they were last read. To detect changes, it compares row versions or values. There is no guarantee that the cursor will be able to update or delete a row, but concurrency is much higher than when locking is used. For more information, see the following section, "Optimistic Concurrency."

An application specifies what type of concurrency it wants the cursor to use with the SQL_ATTR_CONCURRENCY statement attribute. To determine what types are supported, it calls SQLGetInfo with the SQL_SCROLL_CONCURRENCY option.


Optimistic Concurrency

Optimistic concurrency derives its name from the optimistic assumption that collisions between transactions will rarely occur; a collision is said to have occurred when another transaction updates or deletes a row of data between the time it is read by the current transaction and the time it is updated or deleted. It is the opposite of pessimistic concurrency, or locking, in which the application developer believes that such collisions are commonplace.

In optimistic concurrency, a row is left unlocked until the time comes to update or delete it. At that point, the row is reread and checked to see if it has been changed since it was last read. If the row has changed, the update or delete fails and must be tried again.

To determine whether a row has been changed, its new version is checked against a cached version of the row. This checking can be based on the row version, such as the timestamp column in SQL Server or the ROWID column in Oracle, or the values of each column in the row. Many DBMSs do not support row versions.

Optimistic concurrency can be implemented by the data source or the application. In either case, the application should use a low transaction isolation level such as Read Committed; using a higher level negates the increased concurrency gained by using optimistic concurrency.

If optimistic concurrency is implemented by the data source, the application sets the SQL_ATTR_CONCURRENCY statement attribute to SQL_CONCUR_ROWVER or SQL_CONCUR_VALUES. To update or delete a row, it executes a positioned update or delete statement or calls SQLSetPos just as it would with pessimistic concurrency; the driver or data source returns SQLSTATE 01001 (Cursor operation conflict) if the update or delete fails due to a collision.

If the application itself implements optimistic concurrency, it sets the SQL_ATTR_CONCURRENCY statement attribute to SQL_CONCUR_READ_ONLY to read a row. If it will compare row versions and does not know the row version column, it calls SQLSpecialColumns with the SQL_ROWVER option to determine the name of this column.

The application updates or deletes the row by increasing the concurrency to SQL_CONCUR_LOCK (to gain write access to the row) and executing an UPDATE or DELETE statement with a WHERE clause that specifies the version or values the row had when the application read it. If the row has changed since then, the statement will fail. If the WHERE clause does not uniquely identify the row, the statement might also update or delete other rows; row versions always uniquely identify rows, but row values uniquely identify rows only if they include the primary key.


Chapter 15: Diagnostics

Functions in ODBC return diagnostic information in two ways. The return code indicates the overall success or failure of the function, while diagnostic records provide detailed information about the function. At least one diagnostic record—the header record—is returned even if the function succeeds.

Diagnostic information is used at development time to catch programming errors such as invalid handles and syntax errors in hard-coded SQL statements. It is used at run time to catch run-time errors and warnings such as data truncation, access violations, and syntax errors in SQL statements entered by the user.


Return Codes

Each function in ODBC returns a code, known as its return code, which indicates the overall success or failure of the function. Program logic is generally based on return codes.

For example, the following code calls SQLFetch to retrieve the rows in a result set. It checks the return code of the function to determine if the end of the result set was reached (SQL_NO_DATA), if any warning information was returned (SQL_SUCCESS_WITH_INFO), or if an error occurred (SQL_ERROR).

SQLRETURN   rc;
SQLHSTMT    hstmt;

while ((rc=SQLFetch(hstmt)) != SQL_NO_DATA) {
   if (rc == SQL_SUCCESS_WITH_INFO) {
      // Call function to display warning information.
   } else if (rc == SQL_ERROR) {
      // Call function to display error information.
      break;
   }
   // Process row.
}

The return code SQL_INVALID_HANDLE always indicates a programming error and should never be encountered at run time. All other return codes provide run-time information, although SQL_ERROR may indicate a programming error.

The following table defines the return codes.

Return code | Description
SQL_SUCCESS | Function completed successfully. The application calls SQLGetDiagField to retrieve additional information from the header record.
SQL_SUCCESS_WITH_INFO | Function completed successfully, possibly with a nonfatal error (warning). The application calls SQLGetDiagRec or SQLGetDiagField to retrieve additional information.
SQL_ERROR | Function failed. The application calls SQLGetDiagRec or SQLGetDiagField to retrieve additional information. The contents of any output arguments to the function are undefined.
SQL_INVALID_HANDLE | Function failed due to an invalid environment, connection, statement, or descriptor handle. This indicates a programming error. No additional information is available from SQLGetDiagRec or SQLGetDiagField. This code is returned only when the handle is a null pointer or is the wrong type, such as when a statement handle is passed for an argument that requires a connection handle.
SQL_NO_DATA | No more data was available. The application calls SQLGetDiagRec or SQLGetDiagField to retrieve additional information. One or more driver-defined status records in class 02xxx may be returned. Note In ODBC 2.x, this return code was named SQL_NO_DATA_FOUND.
SQL_NEED_DATA | More data is needed, such as when parameter data is sent at execution time or additional connection information is required. The application calls SQLGetDiagRec or SQLGetDiagField to retrieve additional information, if any.
SQL_STILL_EXECUTING | A function that was started asynchronously is still executing. The application calls SQLGetDiagRec or SQLGetDiagField to retrieve additional information, if any.


Diagnostic Records

Associated with each environment, connection, statement, and descriptor handle are diagnostic records. These records contain diagnostic information about the last function called that used a particular handle. The records are replaced only when another function is called using that handle. There is no limit to the number of diagnostic records that can be stored at any one time.

There are two types of diagnostic records: a header record and zero or more status records. The header record is record 0; the status records are records 1 and above. Diagnostic records are composed of a number of separate fields, which are different for the header record and the status records. In addition, ODBC components can define their own diagnostic record fields.

Although diagnostic records can be thought of as structures, there is no requirement for them to actually be structures; how a driver stores the diagnostic information is driver-specific.

Fields in diagnostic records are retrieved with SQLGetDiagField. The SQLSTATE, native error number, and diagnostic message fields of status records can be retrieved in a single call with SQLGetDiagRec.


Header Record

The fields in the header record contain general information about a function's execution, including the return code, row count, number of status records, and type of statement executed. The header record is always created unless the function returns SQL_INVALID_HANDLE. For a complete list of fields in the header record, see the SQLGetDiagField function description.


Status Records

The fields in the status records contain information about specific errors or warnings returned by the Driver Manager, driver, or data source, including the SQLSTATE, native error number, diagnostic message, column number, and row number. Status records can be created only if the function returns SQL_ERROR, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA, SQL_NEED_DATA, or SQL_STILL_EXECUTING. For a complete list of fields in the status records, see the SQLGetDiagField function description.


Sequence of Status Records

If two or more status records are returned, the Driver Manager and driver rank them according to the following rules. The record with the highest rank is the first record. The source of a record (Driver Manager, driver, gateway, and so on) is not considered when ranking records.

  • Errors. Status records that describe errors have the highest rank. Among error records, records that indicate a transaction failure or possible transaction failure outrank all other records. If two or more records describe the same error condition, SQLSTATEs defined by the X/Open CLI specification (classes 03 through HZ) outrank ODBC-defined and driver-defined SQLSTATEs.
  • Implementation-defined No Data values. Status records that describe driver-defined No Data values (class 02) have the second highest rank.
  • Warnings. Status records that describe warnings (class 01) have the lowest rank. If two or more records describe the same warning condition, warning SQLSTATEs defined by the X/Open CLI specification outrank ODBC-defined and driver-defined SQLSTATEs.

If there are two or more records with the highest rank, it is undefined which record is the first record. The order of all other records is undefined. In particular, because warnings may appear before errors, applications should check all status records when a function returns a value other than SQL_SUCCESS.


SQLSTATEs

SQLSTATEs provide detailed information about the cause of a warning or error. The SQLSTATEs in this manual are based on those found in the ISO/IEF CLI specification, although those SQLSTATEs that start with IM are specific to ODBC.

Unlike return codes, the SQLSTATEs in this manual are guidelines, and drivers are not required to return them. Therefore, while drivers should return the proper SQLSTATE for any error or warning they are capable of detecting, applications should not count on this always occurring. The reasons for this situation are twofold:

  • Incompleteness. Although this manual lists a large number of errors and warnings and possible causes for those errors and warnings, it is not complete and probably never will be; driver implementations simply vary too much. Any given driver probably will not return all of the SQLSTATEs listed in this manual and might return SQLSTATEs not listed in this manual.
  • Complexity. Some database engines—particularly relational database engines—return literally thousands of errors and warnings. The drivers for such engines are unlikely to map all of these errors and warnings to SQLSTATEs because of the effort involved, the inexactness of the mappings, the large size of the resulting code, and the low value of the resulting code, which often returns programming errors that should never be encountered at run time. Therefore, drivers should map as many errors and warnings as seems reasonable and be sure to map those errors and warnings on which application logic might be based, such as SQLSTATE 01004 (Data truncated).

Because SQLSTATEs are not returned reliably, most applications just display them to the user along with their associated diagnostic message, which is often tailored to the specific error or warning that occurred, and native error code. There is rarely any loss of functionality in doing this, because applications cannot base programming logic on most SQLSTATEs anyway. For example, suppose SQLExecDirect returns SQLSTATE 42000 (Syntax error or access violation). If the SQL statement that caused this error is hard-coded or built by the application, this is a programming error and the code needs to be fixed. If the SQL statement is entered by the user, this is a user error and the application has done all that is possible by informing the user of the problem.

When applications do base programming logic on SQLSTATEs, they should be prepared for the SQLSTATE not to be returned or for a different SQLSTATE to be returned. Exactly which SQLSTATEs are returned reliably can be based only on experience with numerous drivers. However, a general guideline is that SQLSTATEs for errors that occur in the driver or Driver Manager, as opposed to the data source, are more likely to be returned reliably. For example, most drivers probably return SQLSTATE HYC00 (Optional feature not implemented), while fewer drivers probably return SQLSTATE 42021 (Column already exists).

The following SQLSTATEs indicate run-time errors or warnings and are good candidates on which to base programming logic. However, there is no guarantee that all drivers return them.

  • 01004 (Data truncated)
  • 01S02 (Option value changed)
  • HY008 (Operation canceled)
  • HYC00 (Optional feature not implemented)
  • HYT00 (Timeout expired)

SQLSTATE HYC00 (Optional feature not implemented) is particularly significant because it is the only way in which an application can determine whether a driver supports a particular statement or connection attribute.

For a complete list of SQLSTATEs and what functions return them, see Appendix A: ODBC Error Codes. For a detailed explanation of the conditions under which each function might return a particular SQLSTATE, see that function.


Diagnostic Messages

A diagnostic message is returned with each SQLSTATE. The same SQLSTATE is often returned with a number of different messages. For example, SQLSTATE 42000 (Syntax error or access violation) is returned for most errors in SQL syntax. However, each syntax error is likely to be described by a different message.

Sample diagnostic messages are listed in the Error column in the table of SQLSTATEs in Appendix A and in each function. Although drivers can return these messages, they are more likely to return whatever message is passed to them by the data source.

Applications generally display diagnostic messages to the user, along with the SQLSTATE and native error code. This helps the user and support personnel determine the cause of any problems. The component information embedded in the message is particularly helpful in doing this.

Diagnostic messages come from data sources and components in an ODBC connection, such as drivers, gateways, and the Driver Manager. Typically, data sources do not directly support ODBC. Consequently, if a component in an ODBC connection receives a message from a data source, it must identify the data source as the source of the message. It must also identify itself as the component that received the message.

If the source of an error or warning is a component itself, the diagnostic message must explain this. Therefore, the text of messages has two different formats. For errors and warnings that do not occur in a data source, the diagnostic message must use this format:

[vendor-identifier][ODBC-component-identifier]component-supplied-text

For errors and warnings that occur in a data source, the diagnostic message must use this format:

[vendor-identifier][ODBC-component-identifier][data-source-identifier]data-source-supplied-text

The following table shows the meaning of each element.

Element | Meaning
vendor-identifier | Identifies the vendor of the component in which the error or warning occurred or that received the error or warning directly from the data source.
ODBC-component-identifier | Identifies the component in which the error or warning occurred or that received the error or warning directly from the data source.
data-source-identifier | Identifies the data source. For file-based drivers, this is typically a file format, such as Xbase[1] For DBMS-based drivers, this is the DBMS product.
component-supplied-text | Generated by the ODBC component.
data-source-supplied-text | Generated by the data source.

[1] In this case, the driver is acting as both the driver and the data source.

Brackets ([ ]) must be included in the message and do not indicate optional items.


Using SQLGetDiagRec and SQLGetDiagField

Applications call SQLGetDiagRec or SQLGetDiagField to retrieve diagnostic information. These functions accept an environment, connection, statement, or descriptor handle and return diagnostics from the function that last used that handle. The diagnostics logged on a particular handle are discarded when a new function is called using that handle. If the function returned multiple diagnostic records, the application calls these functions multiple times; the total number of status records is retrieved by calling SQLGetDiagField for the header record (record 0) with the SQL_DIAG_NUMBER option.

Applications retrieve individual diagnostic fields by calling SQLGetDiagField and specifying the field to retrieve. Certain diagnostic fields do not have any meaning for certain types of handles. For a list of diagnostic fields and their meanings, see the SQLGetDiagField function description.

Applications retrieve the SQLSTATE, native error code, and diagnostic message in a single call by calling SQLGetDiagRec; SQLGetDiagRec cannot be used to retrieve information from the header record.

For example, the following code prompts the user for an SQL statement and executes it. If any diagnostic information was returned, it calls SQLGetDiagField to get the number of status records and SQLGetDiagRec to get the SQLSTATE, native error code, and diagnostic message from those records.

SQLCHAR       SqlState[6], SQLStmt[100], Msg[SQL_MAX_MESSAGE_LENGTH];
SQLINTEGER    NativeError;
SQLSMALLINT   i, MsgLen;
SQLRETURN     rc1, rc2;
SQLHSTMT      hstmt;

// Prompt the user for an SQL statement.
GetSQLStmt(SQLStmt);

// Execute the SQL statement and return any errors or warnings.
rc1 = SQLExecDirect(hstmt, SQLStmt, SQL_NTS);
if ((rc1 == SQL_SUCCESS_WITH_INFO) || (rc1 == SQL_ERROR)) {
   // Get the status records.
   i = 1;
   while ((rc2 = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError,
            Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA) {
      DisplayError(SqlState,NativeError,Msg,MsgLen);
      i++;
   }
}

if ((rc1 == SQL_SUCCESS) || (rc1 == SQL_SUCCESS_WITH_INFO)) {
   // Process statement results, if any.
}


Implementing SQLGetDiagRec and SQLGetDiagField

SQLGetDiagRec and SQLGetDiagField are implemented by the Driver Manager and each driver. The Driver Manager and each driver maintain diagnostic records for each environment, connection, statement, and descriptor handle, and free those records only when another function is called with that handle or the handle is freed.

Although both the Driver Manager and each driver must determine the first status record according to the rankings in "Sequence of Status Records," described earlier in this chapter, the Driver Manager determines the final sequence of records.

SQLGetDiagRec and SQLGetDiagField do not post diagnostic records about themselves.


Diagnostic Handling Rules

The following rules govern diagnostic handling in SQLGetDiagRec and SQLGetDiagField.

For all ODBC components:

  • Must not replace, alter, or mask errors or warnings received from another ODBC component.
  • May add an additional status record when they receive a diagnostic message from another ODBC component. The added record must add real information value to the original message.

For the ODBC component that directly interfaces a data source:

  • Must prefix its vendor identifier, its component identifier, and the data source's identifier to the diagnostic message it receives from the data source.
  • Must preserve the data source's native error code.
  • Must preserve the data source's diagnostic message.

For any ODBC component that generates an error or warning independent of the data source:

  • Must supply the correct SQLSTATE for the error or warning.
  • Must generate the text of the diagnostic message.
  • Must prefix its vendor identifier and its component identifier to the diagnostic message.
  • Must return a native error code, if one is available and meaningful.

For the ODBC component that interfaces with the Driver Manager:

  • Must initialize the output arguments of SQLGetDiagRec and SQLGetDiagField.
  • Must format and return the diagnostic information as output arguments of SQLGetDiagRec and SQLGetDiagField when that function is called.

For one ODBC component other than the Driver Manager:

  • Must set the SQLSTATE based on the native error. For file-based drivers and DBMS-based drivers that do not use a gateway, the driver must set the SQLSTATE. For DBMS-based drivers that use a gateway, either the driver or a gateway that supports ODBC may set the SQLSTATE.


Role of the Driver Manager

The Driver Manager determines the final order in which to return status records that it generates. In particular, it determines which record has the highest rank and is to be returned first. The driver is responsible for ordering status records that it generates. If status records are posted by both the Driver Manager and the driver, the Driver Manager is responsible for ordering them. For more information, see "Sequence of Status Records," earlier in this chapter.

The Driver Manager does as much error checking as it can. This saves every driver from checking for the same errors. For example, if a function argument accepts a discrete number of values, such as Operation in SQLSetPos, the Driver Manager checks that the specified value is legal.

The following sections describe the types of conditions checked by the Driver Manager. They are not intended to be exhaustive; for a complete list of the SQLSTATEs the Driver Manager returns, see the "Diagnostics" section of each function; the description of each check made by the Driver Manager starts with the letters "(DM)." Also see the state transition tables in Appendix B: ODBC State Transition Tables; errors shown in parentheses are detected by the Driver Manager.


Argument Value Checks

The Driver Manager checks the following types of arguments. Unless otherwise noted, the Driver Manager returns SQL_ERROR for errors in argument values.

  • Environment, connection, and statement handles usually cannot be null pointers. The Driver Manager returns SQL_INVALID_HANDLE when it finds a null handle.
  • Required pointer arguments, such as OutputHandlePtr in SQLAllocHandle and CursorName in SQLSetCursorName, cannot be null pointers.
  • Option flags that do not support driver-specific values must be a legal value. For example, Operation in SQLSetPos must be SQL_POSITION, SQL_REFRESH, SQL_UPDATE, SQL_DELETE, or SQL_ADD.
  • Option flags must be supported in the version of ODBC supported by the driver. For example, InfoType in SQLGetInfo cannot be SQL_ASYNC_MODE (introduced in ODBC 3.0) when calling an ODBC 2.0 driver.
  • Column and parameter numbers must be greater than 0 or greater than or equal to 0, depending on the function. The driver must check the upper limit of these argument values based on the current result set or SQL statement.
  • Length/indicator arguments and data buffer length arguments must contain appropriate values. For example, the argument that specifies the length of a table name in SQLColumns (NameLength3) must be SQL_NTS or a value greater than 0; BufferLength in SQLDescribeCol must be greater than or equal to 0. The driver might also need to check these arguments. For example, it might check that NameLength3 is less than or equal to the maximum length of a table name in the data source.


State Transition Checks

The Driver Manager checks that the state of the environment, connection, or statement is appropriate for the function being called. For example, a connection must be in an allocated state when SQLConnect is called; a statement must be in a prepared state when SQLExecute is called. The Driver Manager returns SQL_ERROR for state transition errors.


General Error Checks

The Driver Manager checks one general error. It always returns SQL_ERROR when it encounters the following error: The function must be supported by the driver.


Driver Manager Error and Warning Checks

The Driver Manager completely or partially implements a number of functions and therefore checks for all or some of the errors and warnings in those functions.

  • The Driver Manager implements SQLDataSources and SQLDrivers and checks for all errors and warnings in these functions.
  • The Driver Manager checks whether a driver implements SQLGetFunctions. If the driver does not implement SQLGetFunctions, the Driver Manager implements and checks for all errors and warnings in it.
  • The Driver Manager partially implements SQLAllocHandle, SQLConnect, SQLDriverConnect, SQLBrowseConnect, SQLFreeHandle, SQLGetDiagRec, and SQLGetDiagField and checks for some errors in these functions. It may return the same errors as the driver for some of these functions because both perform similar operations. For example, the Driver Manager or driver may return SQLSTATE IM008 (Dialog failed) if either one is unable to display a login dialog box for SQLDriverConnect.


Role of the Driver

The driver checks for all errors and warnings not checked by the Driver Manager and orders status records that it generates. (An ODBC 2.x driver does not order status records.) This includes errors and warnings in data truncation, data conversion, syntax, and some state transitions. The driver might also check errors and warnings partially checked by the Driver Manager. For example, although the Driver Manager checks whether the value of Operation in SQLSetPos is legal, the driver must check whether it is supported.

The driver also maps native errors—that is, errors returned by the data source—to SQLSTATEs. For example, the driver might map a number of different native errors for illegal SQL syntax to SQLSTATE 42000 (Syntax error or access violation). The driver returns the native error number in the SQL_DIAG_NATIVE field of the status record. Driver documentation should show how errors and warnings are mapped from the data source to arguments in SQLGetDiagRec and SQLGetDiagField.


Diagnostic Handling Examples

The following examples show how various components in an ODBC connection might generate diagnostic messages and how various drivers might return diagnostics to the application with SQLGetDiagRec.

  • File-Based Driver Diagnostic Example
  • DBMS-Based Driver Diagnostic Example
  • Gateways Diagnostic Example
  • Driver Manager Diagnostic Example


File-Based Driver Diagnostic Example

A file-based driver acts both as an ODBC driver and as a data source. It can therefore generate errors and warnings both as a component in an ODBC connection and as a data source. Because it also is the component that interfaces with the Driver Manager, it formats and returns arguments for SQLGetDiagRec.

For example, if a Microsoft® driver for dBASE could not allocate sufficient memory, it might return the following values from SQLGetDiagRec:

SQLSTATE:         "HY001"
Native Error:      42052
Diagnostic Msg:   "[Microsoft][ODBC dBASE Driver]Unable to allocate sufficient memory."

Because this error was not related to the data source, the driver only added prefixes to the diagnostic message for the vendor ([Microsoft]) and the driver ([ODBC dBASE Driver]).

If the driver could not find the file Employee.dbf, it might return the following values from SQLGetDiagRec:

SQLSTATE:         "42S02"
Native Error:      -1305
Diagnostic Msg:   "[Microsoft][ODBC dBASE Driver][dBASE]No such table or object"

Because this error was related to the data source, the driver added the file format of the data source ([dBASE]) as a prefix to the diagnostic message. Because the driver was also the component that interfaced with the data source, it added prefixes for the vendor ([Microsoft]) and the driver ([ODBC dBASE Driver]).


DBMS-Based Driver Diagnostic Example

A DBMS-based driver sends requests to a DBMS and returns information to the application through the Driver Manager. Because the driver is the component that interfaces with the Driver Manager, it formats and returns arguments for SQLGetDiagRec.

For example, if, using SQL/Services, a Microsoft driver for Oracle Rdb encountered an invalid cursor name, it might return the following values from SQLGetDiagRec:

SQLSTATE:         "34000"
Native Error:      0
Diagnostic Msg:   "[Microsoft][ODBC Rdb Driver]Invalid cursor name: EMPLOYEE_CURSOR."

Because the error occurred in the driver, it added prefixes to the diagnostic message for the vendor ([Microsoft]) and the driver ([ODBC Rdb Driver]).

If the DBMS could not find the table EMPLOYEE, the driver might format and return the following values from SQLGetDiagRec:

SQLSTATE:         "42S02"
Native Error:      -1
Diagnostic Msg:   "[Microsoft][ODBC Rdb Driver][Rdb] %SQL-F-RELNOTDEF, Table EMPLOYEE "
                  "is not defined in schema."

Because the error occurred in the data source, the driver added a prefix for the data source identifier ([Rdb]) to the diagnostic message. Because the driver was the component that interfaced with the data source, it added prefixes for its vendor ([Microsoft]) and identifier ([ODBC Rdb Driver]) to the diagnostic message.


Gateways Diagnostic Example

In a gateway architecture, a driver sends requests to a gateway that supports ODBC. The gateway sends the requests to a DBMS. Because it is the component that interfaces with the Driver Manager, the driver formats and returns arguments for SQLGetDiagRec.

For example, if Oracle based a gateway to Rdb on Microsoft Open Data Services and if Rdb could not find the table EMPLOYEE, the gateway might generate this diagnostic message:

"[42S02][-1][DEC][ODS Gateway][Rdb]%SQL-F-RELNOTDEF, Table EMPLOYEE is not defined "
   "in schema."

Because the error occurred in the data source, the gateway added a prefix for the data source identifier ([Rdb]) to the diagnostic message. Because the gateway was the component that interfaced with the data source, it added prefixes for its vendor ([DEC]) and identifier ([ODS Gateway]) to the diagnostic message. It also added the SQLSTATE value and the Rdb error code to the beginning of the diagnostic message. This permitted it to preserve the semantics of its own message structure and still supply the ODBC diagnostic information to the driver. The driver parses the error information attached to the error statement by the gateway.

Because the gateway driver is the component that interfaces with the Driver Manager, it would use the preceding diagnostic message to format and return the following values from SQLGetDiagRec:

SQLSTATE:         "42S02"
Native Error:      -1
Diagnostic Msg:   "[DEC][ODS Gateway][Rdb]%SQL-F-RELNOTDEF, Table EMPLOYEE is not "
                  "defined in schema."


Driver Manager Diagnostic Example

The Driver Manager can also generate diagnostic messages. For example, if an application passed an invalid direction option to SQLDataSources, the Driver Manager might format and return the following values from SQLGetDiagRec:

SQLSTATE:         "HY103"
Native Error:      0
Diagnostic Msg:   "[Microsoft][ODBC Driver Manager]Direction option out of range"

Because the error occurred in the Driver Manager, it added prefixes to the diagnostic message for its vendor ([Microsoft]) and its identifier ([ODBC Driver Manager]).


Chapter 16: Interoperability

Interoperability is the ability of a single application to operate with many different DBMSs. The need to write generic, interoperable applications was one of the major factors leading to the development of ODBC. However, interoperability is not a simple path followed from "not interoperable" to "completely interoperable." The path has many branches, and each requires trade-offs among features, speed, code complexity, and development time.

The process of writing an interoperable application follows several steps:

  1. Deciding whether the application will use ODBC.
  2. Choosing a level of interoperability and deciding which trade-offs are necessary to reach that level.
  3. Writing interoperable code and testing it as fully as possible.

It should be noted that interoperability is primarily the domain of the application writer. Drivers are designed to work with a single DBMS and, by definition, are not interoperable. They play a role in interoperability by correctly implementing and exposing ODBC over a single DBMS.


Is ODBC the Answer?

Before delving into the question of interoperability, consider the following question: Should the application use ODBC at all? This might seem a strange question to ask in a guide to ODBC, but it is, in fact, a legitimate one. ODBC was not designed to completely replace native database APIs, nor was it designed to provide database access in all circumstances. It was designed to provide a common interface to databases and was intended to free application programmers from having to learn about and maintain links to multiple databases.

Custom applications are prime candidates for native database APIs. The main reason is that custom applications often work with a single DBMS and have no need to be interoperable. Native database APIs might do a better job than ODBC of exposing the capabilities of a particular DBMS and might expose capabilities not exposed by ODBC. Furthermore, because the developers of custom applications are usually familiar with the native database API for their DBMS, there is little reason to learn ODBC. However, it is interesting to note that for some DBMSs, ODBC is the native database API.

So which applications are candidates for ODBC? The best candidates are applications that work with more than one DBMS. This includes virtually all generic and vertical applications. It also includes a number of custom applications. For example, custom applications that use several different DBMSs are much easier and cleaner to write with ODBC than with multiple native APIs. And custom applications written with ODBC are much easier to migrate as a company moves from one DBMS to another or deploys the same application against different DBMSs.


Choosing a Level of Interoperability

Assuming the application will use ODBC, the next step is to determine what level of interoperability is required. The basic level of interoperability is usually a function of the application type: Custom applications tend not to be interoperable, vertical applications tend to be interoperable among a limited number of DBMSs, and generic applications tend to be interoperable among all DBMSs.


Custom Applications

Custom applications typically perform a specific task for a few DBMSs. For example, an application might retrieve data from a single DBMS and generate a report, or it might transfer data among several DBMSs. What these applications have in common is that these DBMSs are known before the application is written and are unlikely to change over the life of the application.

The custom application therefore requires little or no interoperability. The application developer can choose a single driver for each DBMS and code directly to those drivers. The application can safely contain driver-specific code to exploit the capabilities of those drivers and might even make calls to the native database API to use functionality not supported by ODBC.

The major interoperability concern of most custom applications is whether the target DBMSs will change in the future. If so, this process can be simplified by writing more interoperable code to start with. However, such changing of DBMSs is rare and generally entails a large amount of work. Because of this, developers of custom applications rarely choose to increase interoperability at the expense of functionality; they usually choose to recode that functionality when they change DBMSs.


Vertical Applications

Vertical applications typically perform a well-defined task against a single DBMS. For example, an order entry application tracks the orders in a company. What these types of applications have in common is that the database schema is usually designed by the application developer and, while the application might work with a number of different DBMSs, it works with a single DBMS for a single customer.

Because vertical applications usually require certain functionality, such as scrollable cursors or transactions, they rarely support all DBMSs. Instead, they tend to be highly interoperable among a limited set of DBMSs. Typically, vertical application developers choose to support those DBMSs that represent a large fraction of the market and ignore the rest. They might even choose to support specific drivers for those DBMSs to reduce their testing and product support costs.

Because vertical applications can support a known set of DBMSs, they sometimes contain driver-specific or DBMS-specific code. However, such code is best kept to a minimum because it requires extra time to maintain.


Generic Applications

Generic applications sometimes perform a hard-coded task, such as a spreadsheet retrieving data from a database. They might also perform a variety of user-defined tasks, such as a generic query application allowing the user to enter and execute an SQL statement. What generic applications have in common is that they must work with a variety of different DBMSs and that the developer does not know beforehand what these DBMSs will be.

Therefore, generic applications need to be highly interoperable. The developer must make many choices, trading off interoperability for features, and must write code that expects drivers to support a wide range of functionality. While generic applications might be tuned to work with popular DBMSs, they rarely contain driver-specific or DBMS-specific code.


Determining the Target DBMSs and Drivers

The next question to consider is, what are the target DBMSs for the application, and what drivers are available that support those DBMSs? Because generic applications tend to be highly interoperable, the question of target DBMSs is most applicable to custom and vertical applications. However, the question of target drivers applies to all applications, because drivers vary widely in speed, quality, feature support, and availability. Also, if drivers are to be redistributed with the application, the cost and availability of licensing plans need to be considered.

For many custom applications, the target DBMSs are obvious: They are existing DBMSs that the application is designed to access. DBMSs to which future migration is planned should also be considered. However, the major question for these applications is which driver or drivers to use with them. For other custom applications—those which are not designed to access an existing DBMS—the target DBMSs can be chosen based on feature support, concurrent user support, driver availability, and affordability.

For vertical applications, the target DBMSs are usually chosen based on feature support, driver availability, and market. For example, a vertical application designed for small businesses must target DBMSs that are affordable to those businesses; a vertical application designed as an add-on to existing DBMSs must target widely used DBMSs.

When choosing target DBMSs, the differences between desktop and server databases should be considered. Desktop databases such as dBASE, Paradox, and Btrieve are less powerful than server databases. Because they are generally accessed through the less powerful SQL engines found in most file-based drivers, they often lack full transaction support, support fewer concurrent users, and have limited SQL. However, they are inexpensive and have a large installed base.

Server databases such as Oracle, DB2, and SQL Server provide full transaction support, support many concurrent users, and have rich SQL. They are much more expensive and have a smaller installed base. On the other hand, software prices tend to be higher, somewhat offsetting a smaller potential market.

Thus, target DBMSs sometimes can be chosen based on the features required by the application and the application's target market. For example, an order entry system for large corporations might not target desktop databases because these lack adequate transaction support. A similar system designed for small businesses might exclude most server databases on the basis of cost. And developers of generic applications might target both but avoid using the advanced features found in server databases.


Considering Database Features to Use

After the basic level of interoperability is known, the database features used by the application must be considered. For example, what SQL statements will the application execute? Will the application use scrollable cursors? Transactions? Procedures? Long data? For ideas about what features might not be supported by all DBMSs, see the SQLGetInfo, SQLSetConnectAttr, and SQLSetStmtAttr function descriptions, and Appendix C: SQL Grammar. The features required by an application might eliminate some DBMSs from the list of target DBMSs. They might also show that the application can easily target many DBMSs.

For example, if the required features are simple, they can usually be implemented with a high degree of interoperability. An application that executes a simple SELECT statement and retrieves results with a forward-only cursor is likely to be highly interoperable by virtue of its simplicity: Almost all drivers and DBMSs support the functionality it needs.

However, if the required features are more complex, such as scrollable cursors, positioned update and delete statements, and procedures, trade-offs must often be made. There are several possibilities:

  • Lower interoperability, more features. The application includes the features but works only with DBMSs that support them.
  • Higher interoperability, fewer features. The application drops the features but works with more DBMSs.
  • Higher interoperability, optional features. The application includes the features but makes them available only with those DBMSs that support them.
  • Higher interoperability, more features. The application uses the features with DBMSs that support them and emulates them for DBMSs that do not.

The first two cases are relatively simple to implement, because the features are used either with all supported DBMSs or with none. The latter two cases, on the other hand, are more complex. It is necessary in both cases to check whether the DBMS supports the features and in the last case to write a potentially large amount of code to emulate these features. Therefore, these schemes are likely to require more development time and may be slower at run time.

Consider a generic query application that can connect to a single data source. The application accepts a query from the user and displays the results in a window. Now suppose this application has one feature that allows users to display the results of multiple queries simultaneously. That is, they can execute a query and look at some of the results, execute a different query and look at some of its results, and then return to the first query. This presents an interoperability problem because some drivers support only a single active statement.

The application has a number of choices, based on what the driver returns for the SQL_MAX_CONCURRENT_ACTIVITIES option in SQLGetInfo:

  • Always support multiple queries. After connecting to a driver, the application checks the number of active statements. If the driver supports only one active statement, the application closes the connection and informs the user that the driver does not support required functionality. The application is easy to implement and has full functionality but has lower interoperability.
  • Never support multiple queries. The application drops the feature altogether. It is easy to implement and has high interoperability but has less functionality.
  • Support multiple queries only if the driver does. After connecting to a driver, the application checks the number of active statements. The application allows the user to start a new statement when one is already active only if the driver supports multiple active statements. The application has higher functionality and interoperability but is harder to implement.
  • Always support multiple queries and emulate them when necessary. After connecting to a driver, the application checks the number of active statements. The application always allows the user to start a new statement when one is already active. If the driver supports only one active statement, the application opens an additional connection to that driver and executes the new statement on that connection. The application has full functionality and high interoperability but is harder to implement.


Length of the Product Cycle

The final question about interoperability is time. Developing an interoperable application usually takes longer than developing a noninteroperable one. The reason is that the application must check DBMS capabilities, perform the same tasks differently for different DBMSs, work around functionality supported by some DBMSs but not others, and so on.

In addition to development time, product lifetime must be considered. If the application is designed to be used once, such as an application that transfers data when migrating from one DBMS to another, there is no point in making it interoperable. The application will be used once and discarded.

If the application will exist for a long time, it might be easier to maintain as an interoperable application. This is true even for custom applications that have a single DBMS as a target. The reason is that interoperable code uses a limited subset of database features. The driver is required to keep those features available, even in the face of changes to the underlying DBMS. Thus, interoperable code can shift the burden of coping with changes to the DBMS from the application developer to the driver developer.


Writing an Interoperable Application

Whenever an application uses the same code against more than one driver, that code must be interoperable among those drivers. In most cases, this is an easy task. For example, the code to fetch rows with a forward-only cursor is the same for all drivers. In some cases, this can be more difficult. For example, the code to construct identifiers for use in SQL statements needs to consider identifier case, quoting, and one-part, two-part, and three-part naming conventions.

In general, interoperable code must cope with problems of feature support and feature variability. Feature support refers to whether or not a particular feature is supported. For example, not all DBMSs support transactions, and interoperable code must work correctly regardless of transaction support. Feature variability refers to variation in the manner in which a particular feature is supported. For example, catalog names are placed at the start of identifiers in some DBMSs and at the end of identifiers in others.

Applications can deal with feature support and feature variability at design time or at run time. To deal with feature support and variability at design time, a developer looks at the target DBMSs and drivers and makes sure that the same code will be interoperable among them. This is generally the way in which applications with low or limited interoperability deal with these problems.

For example, if the developer guarantees that a vertical application will work only with four particular DBMSs and if each of those DBMSs supports transactions, the application does not need code to check for transaction support at run time. It can always assume transactions are available because of the design-time decision to use only four DBMSs, each of which supports transactions.

To deal with feature support and variability at run time, the application must test for different capabilities at run time and act accordingly. This is generally the way in which highly interoperable applications deal with these problems. For feature support problems, this means writing code that makes the feature optional or writing code that emulates the feature when it is not available. For feature variability problems, this means writing code that supports all possible variations.


Checking Feature Support and Variability

To check feature support and variability, applications generally call SQLGetInfo, SQLGetFunctions, and SQLGetTypeInfo. A good starting place is the driver's API and SQL grammar conformance levels. These describe broad levels of feature support. The application can then call SQLGetInfo with other options to determine the support or variability of features it needs, SQLGetFunctions to determine whether functions it needs beyond the returned conformance level are supported, and SQLGetTypeInfo to determine what SQL data types are supported.

An application can determine whether a statement or connection attribute is supported by calling SQLSetStmtAttr or SQLSetConnectAttr with that attribute. If the function returns SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the attribute is supported; if it returns SQL_ERROR and SQLSTATE HYC00 (Optional feature not implemented), the attribute is not supported.

Applications can also determine a limited amount of information before connecting to the driver by calling SQLDrivers.


Features to Watch For

This section describes a number of features that application developers often take for granted. In fact, these features vary widely in support and manner of support among DBMSs; failure to code for them is likely to cause problems in interoperable applications.

This section does not list all features that application developers need to consider. For that information, see the SQLGetInfo, SQLSetStmtAttr, and SQLSetConnectAttr function descriptions, Appendix C: SQL Grammar, and the sections of this manual that discuss each feature.


Version Number

There are several versions of ODBC, each with different features. An application determines which ODBC version the Driver Manager and a particular driver support by calling SQLGetInfo with the SQL_ODBC_VER and SQL_DRIVER_ODBC_VER options.


Multiple Active Statements and Connections

Some drivers and DBMSs limit the number of statements and connections that can be active at one time. These numbers can be as small as one. For more information, see the SQL_MAX_CONCURRENT_ACTIVITIES and SQL_MAX_DRIVER_CONNECTIONS options in the SQLGetInfo function description, and "Statement Handles" and "Connection Handles" in Chapter 4: ODBC Fundamentals.


Transaction Support in DBMSs

Some databases, especially desktop databases such as dBASE, Paradox, and Btrieve, do not support transactions. Even among databases that support transactions, there is variation in what kinds of SQL statements can be in a transaction. For more information, see the SQL_TXN_CAPABLE option in the SQLGetInfo function description.


Commit and Rollback Behavior

A common behavior among server DBMSs is to close cursors and discard prepared statements when a statement is committed or rolled back. Desktop databases are more likely to keep cursors open and keep prepared statements. For more information, see the SQL_CURSOR_COMMIT_BEHAVIOR and SQL_CURSOR_ROLLBACK_BEHAVIOR options in the SQLGetInfo function description and "Effect of Transactions on Cursors and Prepared Statements" in Chapter 14: Transactions.


NOT NULL in CREATE TABLE Statements

Some databases, and especially desktop databases, do not support the NOT NULL column constraint in CREATE TABLE statements. For more information, see the SQL_NON_NULLABLE_COLUMNS option in the SQLGetInfo function description.


Supported Data Types

The data types supported by DBMSs vary considerably. An application can determine the names and characteristics of supported data types by calling SQLGetTypeInfo. Because of wide variation in data type names, the application must use the data type names returned by SQLGetTypeInfo in CREATE TABLE statements. For more information, see "Data Types in ODBC" in Chapter 4: ODBC Fundamentals.


ODBC SQL Grammar

Interoperable applications should always use the ODBC SQL grammar in SQL statements. However, considerable variation is possible even within this grammar. For more information, see "Interoperability of SQL Statements" in Chapter 8: SQL Statements.


Batch Processing

Support for batches of SQL statements is not widespread, so interoperable applications should use them conditionally or not at all. For more information, see "Executing Batches" in Chapter 9: Executing Statements.


Testing Interoperable Applications

Testing interoperable applications is at best a time-consuming business and at worst impossible because new drivers continually appear on the market. However, a reasonable degree of testing is possible. Applications with limited or low interoperability need only be tested against those drivers they are guaranteed to support. However, they must be fully tested against these drivers.

Highly interoperable applications cannot be tested practically against all drivers. The best that most application developers can do is to test them fully against a small number of drivers and cursorily against several more. Tested drivers should include the most popular drivers for the most popular DBMSs in the application's market; if the market covers all DBMSs, drivers for both desktop and server DBMSs should be tested.

One of the problems in testing ODBC applications is the number of components involved: the application itself, the Driver Manager, the driver, the DBMS, and possibly network software or gateways. Applications can make it easier to track errors by posting the error messages returned by ODBC functions through SQLGetDiagField and SQLGetDiagRec. These messages identify the manufacturer and component in which errors occur. For more information, see Chapter 15: Diagnostics.


Chapter 17: Programming Considerations

This chapter briefly discusses a number of topics related to writing ODBC applications and drivers.


Multithreading

On multithread operating systems, drivers must be thread-safe. That is, it must be possible for applications to use the same handle on more than one thread. How this is achieved is driver-specific, and it is likely that drivers will serialize any attempts to concurrently use the same handle on two different threads.

Applications commonly use multiple threads instead of asynchronous processing. The application creates a separate thread, calls an ODBC function on it, and then continues processing on the main thread. Rather than having to continually poll the asynchronous function, as is the case when the SQL_ATTR_ASYNC_ENABLE statement attribute is used, the application can simply let the newly created thread finish.

Functions that accept a statement handle and are running on one thread can be canceled by calling SQLCancel with the same statement handle from another thread. Although drivers should not serialize the use of SQLCancel in this manner, there is no guarantee that calling SQLCancel will actually cancel the function running on the other thread.


Alignment

The alignment issues in an ODBC application are generally no different than they are in any other application. That is, most ODBC applications have few or no problems with alignment. The penalties for not aligning addresses vary with the hardware and operating system and might be as minor as a slight performance penalty or as major as a fatal run-time error. Therefore, ODBC applications, and portable ODBC applications in particular, should be careful to align data properly.

One example of when ODBC applications encounter alignment issues is when they allocate a large block of memory and bind different parts of that memory to the columns in a result set. This is most likely to occur when a generic application must determine the shape of a result set at run time and allocate and bind memory accordingly.

For example, suppose an application executes a SELECT statement entered by the user and fetches the results from this statement. Because the shape of this result set is not known when the program is written, the application must determine the type of each column after the result set is created and bind memory accordingly. The easiest way to do this is to allocate a large block of memory and bind different addresses in that block to each column. To access the data in a column, the application casts the memory bound to that column.

The following diagram shows a sample result set and how a block of memory might be bound to it using the default C data type for each SQL data type. Each "X" represents a single byte of memory. (This example shows only the data buffers that are bound to the columns. This is done for simplicity. In actual code, the length/indicator buffers must also be aligned.)

Assuming the bound addresses are stored in the Address array, the application uses the following expressions to access the memory bound to each column:

   (SQLCHAR *)       Address[0]
   (SQLSMALLINT *)   Address[1]
   (SQLINTEGER *)    Address[2]

Notice that the addresses bound to the second and third columns start on odd-numbered bytes and that the address bound to the third column is not divisible by four, which is the size of an SDWORD. On some machines, this will not be a problem; on others, it will cause a slight performance penalty; on still others, it will cause a fatal run-time error. A better solution would be to align each bound address on its natural alignment boundary. Assuming this is 1 for a UCHAR, 2 for an SWORD, and 4 for an SDWORD, this would give the result shown in the following illustration, where an "X" represents a byte of memory that is used and an "O" represents a byte of memory that is unused.

While this solution does not use all of the application's memory, it does not encounter any alignment problems. Unfortunately, it takes a fair amount of code to implement this solution, as each column must be aligned individually according to its type. A simpler solution is to align all columns on the size of the largest alignment boundary, which is 4 in the example shown in the following illustration.

Although this solution leaves larger holes, the code to implement it is relatively simple and fast. In most cases, this offsets the penalty paid in unused memory. For an example that uses this method, see "Using SQLBindCol" in Chapter 10: Retrieving Results (Basic).


Unicode

Unicode is a method of software character encoding that treats all characters as having a fixed width of two bytes. This method is used as an alternative to Microsoft® Windows® ANSI character encoding, which because it represents characters in one byte, is limited to 256 characters. Because Unicode can represent over 65,000 characters, it accommodates many languages whose characters are not represented in ANSI encoding.

Unicode does not require the use of code pages, which ANSI uses to accommodate a limited set of languages. Unicode is an improvement upon the Double-Byte Character Set (DBCS), which uses a mixture of 8-bit and 16-bit characters and still requires code pages. For more information about the Unicode standard, see http://www.unicode.org.

The ODBC 3.5 (or higher) Driver Manager is Unicode-enabled. This affects two major areas: function calls and string data types. The Driver Manager maps function string arguments and string data as required by the application and driver, both of which can be either Unicode-enabled or ANSI-enabled. These two areas are discussed in detail in the sections, "Unicode Function Arguments" and "Unicode Data," later in this chapter.

The ODBC 3.5 (or higher) Driver Manager supports the use of a Unicode driver with both a Unicode application and an ANSI application. It also supports the use of an ANSI driver with an ANSI application. The Driver Manager provides limited Unicode-to-ANSI mapping for a Unicode application working with an ANSI driver.


Unicode Function Arguments

The ODBC 3.5 (or higher) Driver Manager supports both ANSI and Unicode versions of all functions that accept pointers to character strings or SQLPOINTER in their arguments. The Unicode functions are implemented as functions (with a suffix of W), not as macros. The ANSI functions (which can be called with or without a suffix of A) are identical to the current ODBC API functions.

Unicode functions that always return or take strings or length arguments are passed as count-of-characters. For functions that return length information for server data, the display size and precision are described in number of characters. When a length (transfer size of the data) could refer to string or nonstring data, the length is described in octet lengths. For example, SQLGetInfoW will still take the length as count-of-bytes, but SQLExecDirectW will use count-of-characters.

The following is a list of the ODBC API functions that support both Unicode (W) and ANSI (A) versions:

SQLBrowseConnect | SQLGetDiagField
SQLColAttribute | SQLGetDiagRec
SQLColAttributes | SQLGetInfo
SQLColumnPrivileges | SQLGetStmtAttr
SQLColumns | SQLNativeSQL
SQLConnect | SQLPrepare
SQLDataSources | SQLPrimaryKeys
SQLDescribeCol | SQLProcedureColumns
SQLDriverConnect | SQLProcedures
SQLDrivers | SQLSetConnectAttr
SQLError | SQLSetConnectOption
SQLExecDirect | SQLSetCursorName
SQLForeignKeys | SQLSetDescField
SQLGetConnectAttr | SQLSetStmtAttr
SQLGetConnectOption | SQLSpecialColumns
SQLGetCursorName | SQLStatistics
SQLGetDescField | SQLTablePrivileges
SQLGetDescRec | SQLTables

The following is a list of the ODBC Installer and ODBC Translator functions that require both Unicode (W) and ANSI (A) versions:

SQLConfigDataSource | SQLInstallDriver
SQLCreateDataSource | SQLInstallDriverManager
SQLDataSourceToDriver | SQLInstallODBC
SQLDriverToDataSource | SQLRemoveDSNFromINI
SQLGetAvailableDrivers | SQLValidDSN
SQLGetInstalledDrivers | SQLWriteDSNToINI
SQLGetTranslator |

Note Deprecated functions have Unicode-to-ANSI mapping support because the ODBC 3.x Driver Manager supports recompiling ODBC 2.x applications with the UNICODE #define.


Unicode Applications

You can recompile an application as a Unicode application in one of two ways:

  • Include the Unicode #define contained in the Sqlucode.h header file in the application.
  • Compile the application with the compiler's Unicode option. (This option will be different for different compilers.)

To convert an ANSI application to a Unicode application, write the application to store and pass Unicode data. In addition, calls to functions that support SQLPOINTER arguments must be converted to use count of bytes.

Once an application is compiled as a Unicode application, if the application calls an ODBC API function (without a suffix), the Driver Manager recognizes the application as a Unicode application and converts the function call to a Unicode function (with the W suffix) if the underlying driver supports Unicode. When an ANSI application makes a function call without a suffix, the Driver Manager converts it to ANSI if the underlying driver supports ANSI. If both the application and the driver support the same character encoding, the driver manager passes the calls through to the driver (with certain exceptions for ANSI applications).

An application can call both Unicode functions (with the W suffix) and ANSI functions (with or without the A suffix). Unicode and ANSI function calls can be mixed. If the cursor library is to be used, however, Unicode and ANSI function calls cannot be mixed. The cursor library is either Unicode or ANSI, not a mixture.

An application can be written such that it can be compiled as either a Unicode application or an ANSI application. In this case, character data types can be declared as SQL_C_TCHAR. This is a macro that inserts SQL_C_WCHAR if the application is compiled as a Unicode application or inserts SQL_C_CHAR if it is compiled as an ANSI application. The application programmer must be careful of functions that take SQLPOINTER as their argument, because the size of the length argument will change (for string data types) depending on whether the application is ANSI or Unicode.

A function can be called in one of three ways: as a Unicode-only function call (with the W suffix), as an ANSI-only function call (with the A suffix), or as the ODBC function call with no suffix. The arguments to the three forms of a function are identical. Only those functions with SQLCHAR * arguments or SQLPOINTER arguments that point to strings require Unicode and ANSI forms. For functions that have arguments that can be declared as a character type, such as SQLBindCol or SQLGetData (which do not have Unicode and ANSI forms), the argument can be declared as the Unicode type, the ANSI type, or in the case of a C type argument, the SQL_C_TCHAR macro. For more information, see "Unicode Data," later in this chapter.

An application can be written as a Unicode application even if no Unicode drivers are available for it to work with. The Driver Manager will map Unicode functions and data types to ANSI. There are some restrictions to the Unicode to ANSI mappings that can be performed. The existence of a Unicode driver for the Unicode application to work with will result in better performance and will remove the restrictions inherent in the Unicode to ANSI mappings.


Unicode Drivers

Whether a driver should be a Unicode driver or an ANSI driver depends entirely on the nature of the data source. If the data source supports Unicode data, the driver should be a Unicode driver. If the data source only supports ANSI data, the driver should remain an ANSI driver.

A Unicode driver must export SQLConnectW in order to be recognized as a Unicode driver by the Driver Manager.

A Unicode driver must accept Unicode functions (with a suffix of W) and store Unicode data. It can also accept ANSI functions, but is not required to. (The Driver Manager does not pass an ANSI function call with the A suffix to the driver, but converts it to an ANSI function call without the suffix and then passes it to the driver.)

A Unicode driver must be able to return result sets in either Unicode or ANSI, depending on the application's binding. If an application binds to SQL_C_CHAR, the Unicode driver must convert SQL_WCHAR data to SQL_CHAR. The driver manager will map SQL_C_WCHAR to SQL_C_CHAR for ANSI drivers but does no mapping for Unicode drivers.

Note When determining the driver type, the Driver Manager will call SQLSetConnectAttr and set the SQL_ATTR_ANSI_APP attribute at connection time. If the application is using ANSI APIs, SQL_ATTR_ANSI_APP will be set to SQL_AA_TRUE, and if it is using Unicode, it will be set to a value of SQL_AA_FALSE. This attribute is used so that the driver can exhibit different behavior based on the application type. The attribute cannot be set by the application directly, and it is not supported by SQLGetConnectAttr. If a driver exhibits the same behavior for both ANSI and Unicode applications, it should return SQL_ERROR for this attribute. If the driver returns SQL_SUCCESS, the Driver Manager will separate ANSI and Unicode connections when Connection Pooling is used.


Function Mapping in the Driver Manager

The driver manager supports two entry points for functions that take string arguments. The undecorated function (SQLDriverConnect) is the ANSI form of the function. The Unicode form is decorated with a W (SQLDriverConnectW.)

The ODBC header file also supports functions decorated with an A, (SQLDriverConnectA) for the convenience of mixed ANSI/Unicode applications. Calls made to the A functions are actually calls into the undecorated entry point (SQLDriverConnect.)

If the application is compiled with the _UNICODE #define, the ODBC header file will map undecorated function calls (SQLDriverConnect) to the Unicode version (SQLDriverConnectW.)

The Driver Manager recognizes a driver as a Unicode driver if SQLConnectW is supported by the driver.

If the driver is a Unicode driver, the Driver Manager makes function calls as follows:

  • Passes a function without string arguments or parameters directly through to the driver.
  • Passes Unicode functions (with the W suffix) directly through to the driver.
  • Converts an ANSI function (with the A suffix) to a Unicode function (with the W suffix) by converting the string arguments into Unicode characters and passes the Unicode function to the driver.

If the driver is an ANSI driver, the Driver Manager makes function calls as follows:

  • Passes functions without string arguments or parameters directly through to the driver.
  • Converts Unicode functions (with the W suffix) to an ANSI function call and passes it to the driver.
  • Passes an ANSI function directly to the driver.

The Driver Manager is Unicode-enabled internally. As a result, the optimum performance is obtained by a Unicode application working with a Unicode driver, because the Driver Manager simply passes Unicode functions through to the driver. When an ANSI application is working with an ANSI driver, the Driver Manager must convert strings from ANSI to Unicode when processing some functions, such as SQLDriverConnect. After processing the function, the Driver Manager must then convert the Unicode string back to ANSI before sending the function to the ANSI driver.

An application should not modify or read its bound parameter buffers when the driver returns SQL_STILL_EXECUTING or SQL_NEED_DATA. The Driver Manager leaves the buffers bound to ANSI until the driver returns SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, or SQL_ERROR. A multithreaded application should not gain access to any bound parameter values that another thread is executing an SQL statement on. The Driver Manager converts the data from Unicode to ANSI "in place," and the other thread might see ANSI data in these buffers while the driver is still processing the SQL statement. Applications that bind Unicode data to an ANSI driver must not bind two different columns to the same address.


Unicode Data

SQL Unicode data types are provided to describe data that resides in Unicode natively on the DBMS. A C Unicode data type is provided to allow an application to bind data to a Unicode buffer. The Driver Manager can convert data from a Unicode C type (SQL_C_WCHAR) to make it function with an ANSI driver.

An ODBC 3.0 or 2.x application will always bind to the ANSI data types. For optimum performance, an ODBC 3.5 (or higher) application should bind to the ANSI data C type if the SQL column type is ANSI, and should bind to the Unicode C data type if the SQL column type is Unicode.

The SQL Unicode type indicators are SQL_WCHAR, SQL_WVARCHAR, and SQL_WLONGVARCHAR. SQL_WCHAR data has a fixed string length, while SQL_WVARCHAR has a variable length with a declared maximum and SQL_WLONGVARCHAR has a variable length with a maximum that depends on the data source.

The C Unicode type indicator is SQL_C_WCHAR. This is the default for each of the SQL Unicode type indicators. All of the SQL types can be converted to SQL_C_WCHAR, and SQL_C_WCHAR can be converted to all of the SQL types. An application can retrieve data in one of three ways:

  • Retrieve the data as SQL_C_CHAR.
  • Retrieve the data as SQL_C_WCHAR.
  • Declare the data as SQL_C_TCHAR. This is a macro that inserts SQL_C_WCHAR if the application is compiled as a Unicode application or inserts SQL_C_CHAR if it is compiled as an ANSI application.

SQL_C_TCHAR is declared in a function as follows:

SQLBindParameter(StatementHandle, 1, SQL_PARAM_INPUT, SQL_C_TCHAR, SQL_WCHAR, NameLen, 0, Name, 0, &Name)

When the application is compiled as a Unicode application, the ValueType argument would be changed from SQL_C_TCHAR to SQL_C_WCHAR. When the application is compiled as an ANSI application, the ValueType argument would be changed to SQL_C_CHAR.

Unicode drivers must still support ANSI data types, including SQL_CHAR. If an application working with a non-Unicode driver binds to SQL_WCHAR, the Driver Manager will map the SQL_WCHAR data to SQL_CHAR. However, if an application working with a Unicode driver binds to SQL_CHAR, the Driver Manager will not map the SQL_CHAR data to SQL_WCHAR. The Unicode driver must accept the SQL_CHAR data.

The Driver Manager stores driver and DSN names in Unicode and maps them to ANSI as needed. If a Unicode character cannot be mapped to an ANSI character (as can occur if characters from a code page that is not the native code page of the computer are used in driver and DSN names), the characters that could not be converted are represented by a default character supplied by the system.


Translation DLLs

The application and data source often store data in different character sets. ODBC provides a generic mechanism that allows the driver to translate data from one character set to another. It consists of a DLL that implements the translation functions SQLDriverToDataSource and SQLDataSourceToDriver, which are called by the driver to translate all data flowing between the data source and driver. This DLL can be written by the application developer, the driver developer, or a third party.

The translation DLL for a particular data source can be specified in the system information for that data source; for more information, see "Data Source Specification Subkeys" in Chapter 19: Configuring Data Sources. It can also be set at run time with the SQL_ATTR_TRANSLATE_DLL and SQL_ATTR_TRANSLATE_OPTION connection attributes.

The translation option is a value that can be interpreted only by a particular translation DLL. For example, if the translation DLL translates between different code pages, the option might give the numbers of the code pages used by the application and the data source. There is no requirement for a translation DLL to use a translation option.

After a translation DLL has been specified, the driver loads it and calls it to translate all data flowing between the application and data source. This includes all SQL statements and character parameters being sent to the data source, and all character results, character metadata such as column names, and error messages retrieved from the data source. Connection data is not translated, because the translation DLL is not loaded until after the application has connected to the data source.


Diagnostic Tools

Two facilities can assist with diagnosing problems in ODBC applications. Both are available from the ODBC Administrator and implemented by the Driver Manager.

Tracing provides a means to record to a log file the sequence of function calls.

Visual Studio Analyzer allows the analysis of information about the interaction of components in a distributed environment.


Tracing

The ODBC Driver Manager has a trace facility that allows the sequence of function calls made by an ODBC application to be recorded and transcribed into a log file. Tracing is performed by a trace DLL that captures calls between the application and the Driver Manager, and between the Driver Manager and the driver. This method of tracing replaces the tracing performed by the ODBC 2.x Driver Manager and the tracing performed in ODBC 2.x by ODBC Spy.


Trace DLL

The DLL that performs tracing is one of the ODBC core components. The trace DLL is provided as a sample DLL in the ODBC component of the Microsoft® Data Access Components (MDAC) SDK, so the registry entry, interface, and sample code for the trace DLL are available. This DLL can be replaced by a trace DLL produced by either an ODBC user or a third-party vendor. A custom trace DLL should be given a different name than the original sample trace DLL. Trace DLLs must be installed in the system directory, or they will fail to load. The connection strings will not be passed to the trace DLL by the Driver Manager.

The trace DLL traces input arguments, output arguments, deferred arguments, return codes, and SQLSTATEs. When tracing is enabled, the Driver Manager calls the trace DLL at two points: once upon function entry (before argument validation) and again just before the function returns.

When an application calls a function, the Driver Manager calls a trace function in the trace DLL before calling the function in the driver or processing the call itself. Each ODBC function has a corresponding trace function (prefixed with Trace) that is identical to the ODBC function with the exception of the name. When the trace function is called, the trace DLL captures the input arguments and returns a return code. Because the trace DLL is called before the Driver Manager validates arguments, invalid function calls are traced, so state transition errors and invalid arguments are logged.

After calling the trace function in the trace DLL, the Driver Manager calls the ODBC function in the driver. It then calls TraceReturn in the trace DLL. This function takes two arguments: the value returned by the trace DLL for the trace function, and the return code returned by the driver to the Driver Manager for the ODBC function (or the value returned by the Driver Manager itself if it processed the function). The function uses the value returned for the trace function to manipulate captured input argument values. It writes the code returned for the ODBC function to the log file (or displays it dynamically, if that is enabled). It dereferences the output argument pointers and logs the output argument values.


Trace File

An application specifies the trace file either by setting the TraceFile keyword in the Odbc.ini registry entry or by calling SQLSetConnectAttr with the SQL_ATTR_TRACEFILE connection attribute. If the file does not exist when tracing is enabled, the Driver Manager will create the file. Each application should have its own dedicated trace file to avoid contention. An application can use more than one trace file; an application's setup program can provide the user with a choice of trace files. If tracing is enabled dynamically, an application can also display trace results, rather than logging to the trace file.

The trace file provides a log of each ODBC function call with the data types and values of all arguments. It logs all input functions and logs all returned functions with return codes and error states.

In ODBC 3.x, parameters to connection functions are not provided to the trace DLL.


Enabling Tracing

Tracing can be enabled in the following three ways:

  • Set the Trace and TraceFile keywords in the Odbc.ini registry entry. This enables or disables tracing when SQLAllocHandle with a HandleType of SQL_HANDLE_ENV is called. These options are set in the Tracing tab of the ODBC Data Source Administrator dialog box displayed during data source setup. For more information, see "Registry Entries for Data Sources" in Chapter 19: Configuring Data Sources.
  • Call SQLSetConnectAttr to set the SQL_ATTR_TRACE connection attribute to SQL_OPT_TRACE_ON. This enables or disables tracing for the duration of the connection. For more information, see the SQLSetConnectAttr function description.
  • Use ODBCSharedTraceFlag to turn tracing on or off dynamically. (For more information, see the next section, "Dynamic Tracing.")


Dynamic Tracing

Tracing can be enabled or disabled at any point in an application run. This allows an application to trace any number of function calls.

The variable ODBCSharedTraceFlag is set to enable tracing dynamically. This variable is shared among all running copies of the Driver Manager. If any application sets this variable, tracing is enabled for all ODBC applications currently running. To turn tracing off when dynamic tracing is enabled, an application calls SQLSetConnectAttr to set SQL_ATTR_TRACE to SQL_TRACE_OFF. This call will turn tracing off for that application only. Applications that are linked with Odbc32.lib can modify use of this variable. Trace data can be displayed in a real-time window, instead of the trace file, which must be opened after the ODBC session. Controls can be added to an application's screen to turn tracing on or off at will.

The trace DLL shipped with ODBC 3.x is not thread-safe. It is not guaranteed that the log file is written correctly if global tracing is enabled (the variable ODBCSharedTraceFlag is set) and more than one application writes to the trace file at the same time. This condition does not return an error.


Connection Pooling Considerations

Performing any of the following actions using an SQL Command (rather than through the ODBC API) can affect the connection's state and cause unexpected problems when connection pooling is active:

  • Opening a connection and changing the default database
  • Using the SET statement to change any configurable options (including SET ROWCOUNT, ANSI_NULL, IMPLICIT_TRANSACTIONS, SHOWPLAN, STATISTICS, TEXTSIZE, and DATEFORMAT)
  • Creating temporary tables and stored procedures

If any of these actions are performed outside of the ODBC API, the next person who uses the connection will automatically inherit the previous settings, tables, or procedures.

Note Do not expect certain settings to be present in the connection state. You should always set the connection state in your application and ensure that the application removes any unused connection pooling settings.


Visual Studio Analyzer

Microsoft® Visual Studio™ Analyzer provides a high-level view of the ODBC application. The developer can evaluate, analyze, and debug the structure, performance, and interactions from the application's perspective, rather than from a component or code perspective. The information gathered by Visual Studio Analyzer is in the form of events, which represent some kind of interaction between two components of the application. Events can be composed of function calls and returns from functions, such as connections, database queries, and transactions.

For more information about Visual Studio Analyzer, see the Visual Studio Analyzer Reference Guide within the Visual Studio suite of documentation.


Enabling Visual Studio Analyzer

Microsoft Visual Studio Analyzer is an application-analysis tool designed to provide a high-level look at an application's performance across all tiers and systems. It focuses on the interaction between components. Visual Studio Analyzer can be started and stopped from the ODBC Administrator's Tracing tab.

To view any of the events that ODBC generates, it must be configured as follows:

  1. Click Start.
  2. Point to Settings, and then click Control Panel.
  3. Double-click the ODBC Administrator.
  4. Click the Tracing tab.
  5. Click Start Visual Studio Analyzer Tracing.
  6. Click OK.

Visual Studio Analyzer event-generation continues until Stop Visual Studio Analyzer Tracing is selected.


Events Generated by the ODBC Driver Manager

Events generated by the ODBC Driver Manager are registered when the Start Visual Studio Analyzer button is clicked. The tool itself offers system-defined events and the ability to create custom events. For more information about events, see the Visual Studio Analyzer Reference Guide within the Visual Studio suite of documentation.

Visual Studio Analyzer event | Description
Call | Generated on every ODBC API entry.
ReturnException | Generated on every ODBC API return if the return code is SQL_ERROR.
ReturnNormal | Generated on every ODBC API return if the return code is not SQL_ERROR.
Connection Start | Indicates that a connection started; generated when the ODBC Driver Manager calls the driver's connection APIs.
Connection Complete | Indicates that a connection completed; generated when the driver's connection APIs return to the ODBC Driver Manager.
Disconnect Start | Generated when the ODBC Driver Manager calls the driver's SQLDisconnect function.
Disconnect Complete | Generated when the driver's SQLDisconnect function returns to the ODBC Driver Manager.
QuerySend | Generated when the ODBC Driver Manager calls the driver's SQLPrepare, SQLExecute, SQLExecDirect functions, as well as catalog functions such as SQLTables and SQLColumns.
QueryResult | Generated when the driver returns a result set to the ODBC Driver Manager for functions involving queries.
TransactionStart | Generated when an application sets the value of SQL_ATTR_AUTOCOMMIT to SQL_AUTOCOMMIT_OFF, or after an application successfully calls SQLEndTran.
TransactionCommit | Generated when an application calls SQLEndTran to commit a local transaction.
TransactionRollback | Generated when an application calls SQLEndTran to roll back a local transaction.
JoinDTC | Generated when an application joins the Distributed Transaction Coordinator (DTC).
LeaveDTC | Generated when an application leaves the Distributed Transaction Coordinator (DTC).


Driver-Specific Data Types, Descriptor Types, Information Types, Diagnostic Types, and Attributes

Drivers can allocate driver-specific values for the following:

  • SQL data type indicators. These are used in ParameterType in SQLBindParameter and in DataType in SQLGetTypeInfo and returned by SQLColAttribute, SQLColumns, SQLDescribeCol, SQLGetTypeInfo, SQLDescribeParam, SQLProcedureColumns, and SQLSpecialColumns.
  • Descriptor fields. These are used in FieldIdentifier in SQLColAttribute, SQLGetDescField, and SQLSetDescField.
  • Diagnostic fields. These are used in DiagIdentifier in SQLGetDiagField and SQLGetDiagRec.
  • Information types. These are used in InfoType in SQLGetInfo.
  • Connection and statement attributes. These are used in Attribute in SQLGetConnectAttr, SQLGetStmtAttr, SQLSetConnectAttr, and SQLSetStmtAttr.

For each of these items, there are two sets of values: values reserved for use by ODBC, and values reserved for use by drivers. Before implementing driver-specific values, a driver writer must request a value for each driver-specific type, field, or attribute from X/Open. Driver-specific data types, descriptor fields, diagnostic fields, information types, statement attributes, and connection attributes must be described in the driver documentation.

When any of these values is passed to an ODBC function, the driver must check whether the value is valid. Drivers return SQLSTATE HYC00 (Optional feature not implemented) for driver-specific values that apply to other drivers.


Backward Compatibility and Standards Compliance

Backward compatibility is the ability of newer ODBC components to work with old ODBC components. The following sections discuss how these components are affected by the changes in ODBC 3.x. The information contained in them primarily addresses the writing of an ODBC 3.x application and how backward compatibility issues are handled by ODBC drivers. For specific guidelines about how backward compatibility issues affect the writing of an ODBC 3.x driver, see Appendix G: Driver Guidelines for Backward Compatibility.


Affected ODBC Components

Backward compatibility describes how applications, the Driver Manager, and drivers are affected by the introduction of a new version of the Driver Manager. This affects applications and driver when either or both of them remain in the old version. There are, therefore, three types of backward compatibility to consider, as shown in the following table.

Type | Version of DM | Version of application | Version of driver
Backward Compatibility of Driver Manager | 3.x | 2.x | 2.x
Backward Compatibility of Driver[1] | 3.x | 2.x | 3.x
Backward Compatibility of Application | 3.x | 3.x | 2.x

[1] The backward compatibility of drivers is primarily discussed in Appendix G: Driver Guidelines for Backward Compatibility.

Note A standards-compliant application—for example, an application that has been written in accordance with the X/Open or ISO CLI standards—is guaranteed to work with an ODBC 3.x driver through the ODBC 3.x Driver Manager. It is assumed that the functionality that the application is using is available in the driver. It is also assumed that the standards-compliant application has been compiled with the ODBC 3.x header files.


Types of Changes

Three types of changes are made in ODBC 3.x (and any version of ODBC). Each of these affects backward compatibility differently and is handled in a different way. These changes are described in the following table.

Type of change | Description
New features | These are features that are new to ODBC 3.x, such as out-of-line binding or descriptors. These are implemented only when the application and driver, as well as the Driver Manager, are of version 3.x, so there is no attempt to make these backward compatible.
Duplicated features | These are features that exist in ODBC 2.x and ODBC 3.x but are implemented in different ways in each. The functions SQLAllocHandle and SQLAllocStmt are an example. Backward compatibility issues for these and other duplicated features are mostly handled by mappings in the Driver Manager.
Behavioral changes | These are features that are handled differently in ODBC 2.x and ODBC 3.x. A datetime #define is an example. These features are handled by the ODBC 3.x driver based on an environment attribute setting. (See the section "Behavioral Changes," later in this chapter.)


Application/Driver Compatibility

ODBC applications and driver fall into a number of categories in addition to their version. Some of these applications are incompatible with some drivers; in other cases, the type of the application or driver may have a bearing on the backward compatibility issues between them.


Types of Applications

ODBC applications can be classified as follows:

  • Pure ODBC 2.x application. A 32-bit application that:
    • Calls only ODBC 2.x functions (including the ODBC 1.0 function SQLSetParam). These include ODBC 1.x applications that have been ported to 32-bit.
    • Expects ODBC 2.x behavior for features that have had behavioral changes. (See "Behavioral Changes," later in this chapter).
    • Has not been recompiled with ODBC 3.5 headers.
  • Pure ODBC 2.x Recompiled application. A pure ODBC 2.x application that has been recompiled using the ODBC 3.5 header files, by setting ODBCVER=0x0250.
  • Pure ODBC 2.x Unicode application. A pure ODBC 2.x recompiled application that is Unicode-compliant and uses the SQL_WCHAR data type.
  • Pure X/Open and ISO–compliant ODBC application. A 32-bit application that:
    • Calls functions defined in the X/Open or ISO CLI standards. (These functions may include deprecated 3.0 functions.)
    • Does not use the Unicode data types.
    • Expects ODBC 3.0 behavior for features that have had behavioral changes.
  • Pure ODBC 3.0 application. A 32-bit application that:
    • Is compiled with 3.0 headers.
    • Calls any ODBC 3.0 function, possibly including those that are deprecated.
    • Expects ODBC 3.0 behavior for features that have had behavioral changes.
  • Pure ODBC 3.5 (or higher) application. A 32-bit application that:
    • May use Unicode data types.
    • Calls any ODBC 3.5 function, possibly including those that are deprecated.
    • Expects ODBC 3.5 behavior for features that have had behavioral changes.
  • Replaced application. A 32-bit application that:
    • Implements ODBC 3.x behavior for duplicated functionality.
    • Uses any new features in ODBC 3.x only within conditional code.
    • Has limited conditional code to handle behavioral changes or has registered itself to be an ODBC 2.x application.


Types of Drivers

ODBC drivers can be classified as follows:

  • 32-bit ODBC 2.x driver. A 32-bit driver that:
    • Exports only ODBC 2.x functions.
    • Exhibits ODBC 2.x behavior for behavioral changes.
  • ISO and X/Open–compliant driver. A 32-bit driver that:
    • Exports all functions that are documented in the X/Open or ISO CLI documents. This will include some of functions that are deprecated in ODBC.
    • Exhibits ODBC 3.0 behavior for behavioral changes.
    • Does not necessarily go through the ODBC 3.0 Driver Manager.
  • ODBC 3.0 driver. A 32-bit driver that:
    • Exports only functions that are in ODBC 3.0 minus deprecated functions.
    • Is capable of exhibiting ODBC 2.x behavior or ODBC 3.0 behavior with respect to behavioral changes, based on the SQL_ATTR_APP_ODBC_VERSION environment attribute.
  • ODBC 3.5 (or higher) ANSI driver. A 32-bit driver that:
    • Exports only functions that are in ODBC 3.5 minus deprecated functions.
    • Is capable of exhibiting ODBC 2.x behavior or ODBC 3.0 behavior, or ODBC 3.5 behavior with respect to behavioral changes, based on the SQL_ATTR_APP_ODBC_VERSION environment attribute.
  • ODBC 3.5 (or higher) Unicode driver. A 32-bit driver that:
    • Supports all the features of an ODBC 3.5 ANSI driver.
    • Exports Unicode versions of all ODBC string APIs.
    • Can store and process Unicode data on the data source.

Note 16-bit ODBC drivers will not work directly with the ODBC 3.x Driver Manager. However, it is possible for 16-bit drivers to work with the 2.0 ODBC Driver Manager, which subsequently thunks up to the 3.x Driver Manager.


Compatibility Matrix

The following table describes the compatibility of the types of applications and drivers defined previously in this chapter.

Application type and version | 32-bit ODBC 2.x driver | ODBC 3.x driver | ISO and X/Open–compliant driver
16-bit application, any version | Compatible | Compatible | Compatible
Pure 2.x application | Compatible | Compatible | Not compatible[3]
Pure 2.x recompiled application | Compatible | Compatible[1] | Not compatible[3]
Pure 2.x Unicode application | Compatible | Compatible[1] | Not Compatible[3]
Pure X/Open and ISO–compliant application | Not compatible | Compatible[2] | Compatible[2]
Pure 3.0 application | Not compatible | Compatible | Not compatible[4]
Pure 3.5 (or higher) application | Not compatible | Compatible | Not compatible[4]
Replaced application | Compatible | Compatible | Not compatible[3]

[1] The application must recompile using ODBC 3.5 (or higher) headers with the UNICODE option (if it is a Unicode application) and must set ODBCVER to 0x0250.

[2] The application must compile using ODBC 3.5 (or higher) headers and link with the ODBC Driver Manager. It must also set the header flag ODBC_STD.

[3] This configuration can potentially fail to work because there are features in ODBC 2.x that are not in the standards, such as bookmarks.

[4] This configuration can potentially fail to work because there are features in ODBC 3.x that are not in the standards, such as bookmarks.


New Features

The following new functionality has been introduced in ODBC 3.x. An ODBC 3.x application working with an ODBC 2.x driver will not be able to use this functionality. The ODBC 3.x Driver Manager does not map these features when working with an ODBC 2.x driver.

  • Functions that take a descriptor handle as an argument: SQLSetDescField, SQLGetDescField, SQLSetDescRec, SQLGetDescRec, and SQLCopyDesc.
  • The functions SQLSetEnvAttr and SQLGetEnvAttr.
  • The use of SQLAllocHandle to allocate a descriptor handle. (The use of SQLAllocHandle to allocate environment, connection, and statement handles is duplicated, not new, functionality.)
  • The use of SQLGetConnectAttr to get the SQL_ATTR_AUTO_IPD connection attributes. (The use of SQLSetConnectAttr to set, and SQLGetConnectAttr to get, other connection attributes is duplicated, not new, functionality.)
  • The use of SQLSetStmtAttr to set, and SQLGetStmtAttr to get, the following statement attributes. (The use of SQLSetStmtAttr to set, and SQLGetStmtAttr to get, other statement attributes is duplicated, not new, functionality.)

    SQL_ATTR_APP_ROW_DESC
    SQL_ATTR_APP_PARAM_DESC
    SQL_ATTR_ENABLE_AUTO_IPD
    SQL_ATTR_FETCH_BOOKMARK_PTR
    SQL_ATTR_BIND_OFFSET
    SQL_ATTR_METADATA_ID
    SQL_ATTR_PARAM_BIND_OFFSET_PTR
    SQL_ATTR_PARAM_BIND_TYPE
    SQL_ATTR_PARAM_OPERATION_PTR
    SQL_DESC_PARAM_STATUS_PTR
    SQL_ATTR_PARAMS_PROCESSED_PTR
    SQL_ATTR_PARAMSET_SIZE
    SQL_ATTR_ROW_BIND_OFFSET_PTR
    SQL_ATTR_ROW_OPERATION_PTR
    SQL_ATTR_ROW_ARRAY_SIZE

  • The use of SQLGetStmtAttr to get the following statement attributes. (The use of SQLGetStmtAttr to get other statement attributes is duplicated functionality, not new functionality.)

    SQL_ATTR_IMP_ROW_DESC
    SQL_ATTR_IMP_PARAM_DESC

  • Use of the interval C data type, the interval SQL data types, the BIGINT C data types, and the SQL_C_NUMERIC data structure.
  • Row-wise binding of parameters.
  • Offset-based bookmark fetches, such as calling SQLFetchScroll with a FetchOrientation argument of SQL_FETCH_BOOKMARK and specifying an offset other than 0.
  • SQLFetch returning the row status array, number of rows fetched, fetching multiple rows, intermixing calls with SQLFetchScroll, and intermixing calls with SQLBulkOperations or SQLSetPos. For more information, see the next section, "Block Cursors, Scrollable Cursors, and Backward Compatibility for ODBC 3.x Applications."
  • Named parameters.
  • Any of the ODBC 3.x–specific SQLGetInfo options. (If an ODBC 3.x application working with an ODBC 2.x driver calls the SQL_XXX_CURSOR_ATTRIBUTES1 information types, which have replaced several ODBC 2.x information types, some of the information might be reliable, but some might be unreliable. For more information, see SQLGetInfo.)
  • Bind offsets.
  • Updating, refreshing, and deleting by bookmarks (through a call to SQLBulkOperations).
  • Calling SQLBulkOperations or SQLSetPos in the S5 state.
  • The ROW_NUMBER and COLUMN_NUMBER fields in the diagnostic record (which have to be retrieved by the replacement functions SQLGetDiagField or SQLGetDiagRec).
  • Approximate row counts.
  • Warning information (SQL_ROW_SUCCESS_WITH_INFO from SQLFetchScroll).
  • Variable-length bookmarks.
  • Extended error information for arrays of parameters.
  • All of the new columns in the result sets returned by the catalog functions.
  • Use of SQLDescribeCol and SQLColAttribute on column 0.
  • Use of any ODBC 3.x–specific column attributes in a call to SQLColAttribute.
  • Use of multiple environment handles.


Block Cursors, Scrollable Cursors, and Backward Compatibility for ODBC 3.x Applications

The existence of both SQLFetchScroll and SQLExtendedFetch represents the first clear split in ODBC between the Application Programming Interface (API), which is the set of functions the application calls, and the Service Provider Interface (SPI), which is the set of functions the driver implements. This split is required to balance the requirement in ODBC 3.x, which uses SQLFetchScroll, to align with the standards and be compatible with ODBC 2.x, which uses SQLExtendedFetch.

The ODBC 3.x API, which is the set of functions the application calls, includes SQLFetchScroll and related statement attributes. The ODBC 3.x SPI, which is the set of functions the driver implements, includes SQLFetchScroll, SQLExtendedFetch, and related statement attributes. Because ODBC does not formally enforce this split between the API and the SPI, it is possible for ODBC 3.x applications to call SQLExtendedFetch and related statement attributes. However, there is no reason for ODBC 3.x applications to do this. For more information about APIs and SPIs, see the introduction to Chapter 3: ODBC Architecture.

For information about how the ODBC 3.x Driver Manager maps calls to ODBC 2.x and ODBC 3.x drivers, and what functions and statement attributes an ODBC 3.x driver should implement for block and scrollable cursors, see "What the Driver Does" in Appendix G: Driver Guidelines for Backward Compatibility.

The following table summarizes what functions and statement attributes an ODBC 3.x application should use with block and scrollable cursors. It also lists changes between ODBC 2.x and ODBC 3.x in this area that ODBC 3.x applications should be aware of to be compatible with ODBC 2.x drivers.

Function or
statement attribute
Comments
SQL_ATTR_FETCH_BOOKMARK_PTR Points to the bookmark to use with SQLFetchScroll.

When an application sets this in an ODBC 2.x driver, this must point to a fixed-length bookmark.
SQL_ATTR_ROW_STATUS_PTR Points to the row status array filled by SQLFetch, SQLFetchScroll, SQLBulkOperations, and SQLSetPos.

If an application sets this in an ODBC 2.x driver and calls SQLBulkOperation with an Operation of SQL_ADD before calling SQLFetchScroll, SQLFetch, or SQLExtendedFetch, SQLSTATE HY011 (Attribute cannot be set now) is returned.

When an application calls SQLFetch in an ODBC 2.x driver, SQLFetch is mapped to SQLExtendedFetch and therefore returns values in this array.
SQL_ATTR_ROWS_FETCHED_PTR Points to the buffer in which SQLFetch and SQLFetchScroll return the number of rows fetched.

When an application calls SQLFetch in an ODBC 2.x driver, SQLFetch is mapped to SQLExtendedFetch and therefore returns a value in this buffer.
SQL_ATTR_ROW_ARRAY_SIZE Sets the rowset size.

If an application calls SQLBulkOperations with an Operation of SQL_ADD in an ODBC 2.x driver, SQL_ROWSET_SIZE will be used for the call, not SQL_ATTR_ROW_ARRAY_SIZE, because the call is mapped to SQLSetPos with an Operation of SQL_ADD, which uses SQL_ROWSET_SIZE.

Calling SQLSetPos with an Operation of SQL_ADD or SQLExtendedFetch in an ODBC 2.x driver uses SQL_ROWSET_SIZE.

Calling SQLFetch or SQLFetchScroll in an ODBC 2.x driver uses SQL_ATTR_ROW_ARRAY_SIZE.
SQLBulkOperations Performs insert and bookmark operations. When SQLBulkOperations with an Operation of SQL_ADD is called in an ODBC 2.x driver, it is mapped to SQLSetPos with an Operation of SQL_ADD. The following are implementation details:
  • When working with an ODBC 2.x driver, an application must use only the implicitly allocated ARD associated with the StatementHandle; it cannot allocate another ARD for adding rows, because explicit descriptor operations are not supported in an ODBC 2.x driver. An application must use SQLBindCol to bind to the ARD, not SQLSetDescField or SQLSetDescRec.
  • When calling an ODBC 3.x driver, an application can call SQLBulkOperations with an Operation of SQL_ADD before calling SQLFetch or SQLFetchScroll. When calling an ODBC 2.x driver, an application must call SQLFetchScroll before calling SQLBulkOperations with an Operation of SQL_ADD.
SQLFetch Returns the next rowset. The following are implementation details:
  • When an application calls SQLFetch in an ODBC 2.x driver, it is mapped to SQLExtendedFetch.
  • When an application calls SQLFetch in an ODBC 3.x driver, it returns the number of rows specified with the SQL_ATTR_ROW_ARRAY_SIZE statement attribute.
SQLFetchScroll Returns the specified rowset. The following are implementation details:
  • When an application calls SQLFetchScroll in an ODBC 2.x driver, it returns SQLSTATE 01S01 (Error in row) before each error that applies to a single row. It does this only because the ODBC 3.x Driver Manager maps this to SQLExtendedFetch and SQLExtendedFetch returns this SQLSTATE. When an application calls SQLFetchScroll in an ODBC 3.x driver, it never returns SQLSTATE 01S01 (Error in row).
  • When an application calls SQLFetchScroll in an ODBC 2.x driver with FetchOrientation set to SQL_FETCH_BOOKMARK, the FetchOffset argument must be set to 0. SQLSTATE HYC00 (Optional feature not implemented) is returned if offset-based bookmark fetching is attempted with an ODBC 2.x driver.

Note ODBC 3.x applications should not use SQLExtendedFetch or the SQL_ROWSET_SIZE statement attribute. Instead, they should use SQLFetchScroll and the SQL_ATTR_ROW_ARRAY_SIZE statement attribute. ODBC 3.x applications should not use SQLSetPos with an Operation of SQL_ADD but should use SQLBulkOperations with an Operation of SQL_ADD.


Duplicated Features

The following ODBC 2.x functions have been duplicated by ODBC 3.x functions. As a result, the ODBC 2.x functions are deprecated in ODBC 3.x. The ODBC 3.x functions are referred to as replacement functions.

When an application uses a deprecated ODBC 2.x function and the underlying driver is an ODBC 3.x driver, the Driver Manager maps the function call to the corresponding replacement function. The only exception to this rule is SQLExtendedFetch. (See the footnote at the end of the following table.) For more information on these mappings, see Mapping Deprecated Functions in Appendix G: Driver Guidelines for Backward Compatibility.

When an application uses a replacement function and the underlying driver is an ODBC 2.x driver, the Driver Manager maps the function call to the corresponding deprecated function.

ODBC 2.x function | ODBC 3.x function
SQLAllocConnect | SQLAllocHandle
SQLAllocEnv | SQLAllocHandle
SQLAllocStmt | SQLAllocHandle
SQLColAttributes | SQLColAttribute
SQLError | SQLGetDiagRec
SQLExtendedFetch[1] | SQLFetchScroll
SQLFreeConnect | SQLFreeHandle
SQLFreeEnv | SQLFreeHandle
SQLGetConnectOption | SQLGetConnectAttr
SQLGetStmtOption | SQLGetStmtAttr
SQLParamOptions | SQLSetStmtAttr, SQLGetStmtAttr
SQLSetConnectOption | SQLSetConnectAttr
SQLSetParam | SQLBindParameter
SQLSetStmtOption | SQLSetStmtAttr
SQLTransact | SQLEndTran

[1] The function SQLExtendedFetch is duplicated functionality; SQLFetchScroll provides the same functionality in ODBC 3.x. However, the Driver Manager does not map SQLExtendedFetch to SQLFetchScroll when going against an ODBC 3.x driver. For more details, see "What the Driver Manager Does" in Appendix G: Driver Guidelines for Backward Compatibility. The Driver Manager maps SQLFetchScroll to SQLExtendedFetch when going against an ODBC 2.x driver.

Note The function SQLBindParam is a special case. SQLBindParam is duplicated functionality. This is not an ODBC 2.x function, but a function that is present in the X/Open and ISO standards. The functionality provided by this function is completely subsumed by that of SQLBindParameter. As a result, the Driver Manager maps a call to SQLBindParam to SQLBindParameter when the underlying driver is an ODBC 3.x driver. When the underlying driver is an ODBC 2.x driver, however, the Driver Manager does not perform this mapping.


Behavioral Changes

Behavioral changes are those changes for which the syntax of the interface remains the same, but the semantics have changed. For these changes, functionality used in ODBC 2.x behaves differently than the same functionality in ODBC 3.x.

Whether an application exhibits ODBC 2.x behavior or ODBC 3.x behavior is determined by the SQL_ATTR_ODBC_VERSION environment attribute. This 32-bit value is set to SQL_OV_ODBC2 to exhibit ODBC 2.x behavior, and SQL_OV_ODBC3 to exhibit ODBC 3.x behavior.

The SQL_ATTR_ODBC_VERSION environment attribute is set by a call to SQLSetEnvAttr. After an application calls SQLAllocHandle to allocate an environment handle, it must call SQLSetEnvAttr immediately to set the behavior it exhibits. (As a result, there is a new environment state to describe the environment handle in an allocated, but versionless, state.) For more information, see Appendix B: ODBC State Transition Tables.

An application states what behavior it exhibits with the SQL_ATTR_ODBC_VERSION environment attribute, but the attribute has no effect on the application's connection with an ODBC 2.x or ODBC 3.x driver. An ODBC 3.x application can connect to either an ODBC 2.x or 3.x driver, no matter what the setting of the environment attribute.

ODBC 3.x applications should never call SQLAllocEnv. As a result, if the Driver Manager receives a call to SQLAllocEnv, it recognizes the application as an ODBC 2.x application.

The SQL_ATTR_ODBC_VERSION attribute affects three different aspects of an ODBC 3.x driver's behavior:

  • SQLSTATEs
  • Data types for date, time, and timestamp
  • The CatalogName argument in SQLTables accepts search patterns in ODBC 3.x, but not in ODBC 2.x

The setting of the SQL_ATTR_ODBC_VERSION environment attribute does not affect SQLSetParam or SQLBindParam. SQLColAttribute is also not affected by this bit. Although SQLColAttribute returns attributes that are affected by the version of ODBC (date type, precision, scale and length), the intended behavior is determined by the value of the FieldIdentifier argument. When FieldIdentifier is equal to SQL_DESC_TYPE, SQLColAttribute returns the ODBC 3.x codes for date, time, and timestamp; when FieldIdentifier is equal to SQL_COLUMN_TYPE, SQLColAttribute returns the ODBC 2.x codes for date, time, and timestamp.


SQLSTATE Mappings

In ODBC 3.x, HYxxx SQLSTATEs are returned instead of S1xxx, and 42Sxx SQLSTATEs are returned instead of S00XX. This was done to align with X/Open and ISO standards. In many cases, the mapping is not one-to-one because the standards have redefined the interpretation of several SQLSTATEs.

When an ODBC 2.x application is upgraded to an ODBC 3.x application, the application has to be changed to expect ODBC 3.x SQLSTATEs instead of ODBC 2.x SQLSTATEs. The following table lists the ODBC 3.x SQLSTATEs that each ODBC 2.x SQLSTATE is mapped to.

When the SQL_ATTR_ODBC_VERSION environment attribute is set to SQL_OV_ODBC2, the driver posts ODBC 2.x SQLSTATEs instead of ODBC 3.x SQLSTATEs when SQLGetDiagField or SQLGetDiagRec is called. A specific mapping can be determined by noting the ODBC 2.x SQLSTATE in column 1 of the following table that corresponds to the ODBC 3.x SQLSTATE in column 2.

ODBC 2.x SQLSTATE | ODBC 3.x SQLSTATE | Comments
01S03 | 01001 |
01S04 | 01001 |
22003 | HY019 |
22008 | 22007 |
22005 | 22018 |
24000 | 07005 |
37000 | 42000 |
70100 | HY018 |
S0001 | 42S01 |
S0002 | 42S02 |
S0011 | 42S11 |
S0012 | 42S12 |
S0021 | 42S21 |
S0022 | 42S22 |
S0023 | 42S23 |
S1000 | HY000 |
S1001 | HY001 |
S1002 | 07009 | ODBC 2.x SQLSTATE S1002 is mapped to ODBC 3.x SQLSTATE 07009 if the underlying function is SQLBindCol, SQLColAttribute, SQLExtendedFetch, SQLFetch, SQLFetchScroll, or SQLGetData.
S1003 | HY003 |
S1004 | HY004 |
S1008 | HY008 |
S1009 | HY009 | Returned for an invalid use of a null pointer.
S1009 | HY024 | Returned for an invalid attribute value.
S1009 | HY092 | Returned for updating or deleting data by a call to SQLSetPos, or adding, updating, or deleting data by a call to SQLBulkOperations, when the concurrency is read-only.
S1010 | HY007 HY010 | SQLSTATE S1010 is mapped to SQLSTATE HY007 when SQLDescribeCol is called prior to calling SQLPrepare, SQLExecDirect, or a catalog function for the StatementHandle. Otherwise, SQLSTATE S1010 is mapped to SQLSTATE HY010.
S1011 | HY011 |
S1012 | HY012 |
S1090 | HY090 |
S1091 | HY091 |
S1092 | HY092 |
S1093 | 07009 | ODBC 3.x SQLSTATE 07009 is mapped to ODBC 2.x SQLSTATE S1093 if the underlying function is SQLBindParameter or SQLDescribeParam.
S1096 | HY096 |
S1097 | HY097 |
S1098 | HY098 |
S1099 | HY099 |
S1100 | HY100 |
S1101 | HY101 |
S1103 | HY103 |
S1104 | HY104 |
S1105 | HY105 |
S1106 | HY106 |
S1107 | HY107 |
S1108 | HY108 |
S1109 | HY109 |
S1110 | HY110 |
S1111 | HY111 |
S1C00 | HYC00 |
S1T00 | HYT00 |

Note ODBC 3.x SQLSTATE 07008 is mapped to ODBC 2.x SQLSTATE S1000.


Datetime Data Type Changes

In ODBC 3.x, the identifiers for date, time, and timestamp SQL data types have changed from SQL_DATE, SQL_TIME, and SQL_TIMESTAMP (with instances of #define in the header file of 9, 10, and 11) to SQL_TYPE_DATE, SQL_TYPE_TIME, and SQL_TYPE_TIMESTAMP (with instances of #define in the header file of 91, 92, and 93), respectively. The corresponding C type identifiers have changed from SQL_C_DATE, SQL_C_TIME, and SQL_C_TIMESTAMP to SQL_C_TYPE_DATE, SQL_C_TYPE_TIME, and SQL_C_TYPE_TIMESTAMP, respectively.

The column size and decimal digits returned for the SQL datetime data types in ODBC 3.x are the same as the precision and scale returned for them in ODBC 2.x. These values are different than the values in the SQL_DESC_PRECISION and SQL_DESC_SCALE descriptor fields. (For more information, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" in Appendix D: Data Types.)

These changes affect SQLDescribeCol, SQLDescribeParam, and SQLColAttribute; SQLBindCol, SQLBindParameter, and SQLGetData; and SQLColumns, SQLGetTypeInfo, SQLProcedureColumns, SQLStatistics, and SQLSpecialColumns.

The following table shows how the ODBC 3.x Driver Manager performs mapping of the date, time, and timestamp C data types entered in the TargetType arguments of SQLBindCol and SQLGetData or in the ValueType argument of SQLBindParameter.

Data type code entered | 2.x app to 2.x driver | 2.x app to 3.x driver | 3.x app to 2.x driver | 3.x app to 3.x driver
SQL_C_DATE (9) | No mapping | SQL_C_TYPE_DATE (91) | No mapping[1] | SQL_C_TYPE_DATE (91)
SQL_C_TYPE_DATE (91) | Error (from DM) | Error (from DM) | SQL_C_DATE (9) | No mapping[2]
SQL_C_TIME (10) | No mapping | SQL_C_TYPE_TIME (92) | No mapping[1] | SQL_C_TYPE_TIME (92)
SQL_C_TYPE_TIME (92) | Error (from DM) | Error (from DM) | SQL_C_TIME (10) | No mapping[2]
SQL_C_TIMESTAMP (11) | No mapping | SQL_C_TYPE_TIMESTAMP (93) | No mapping[1] | SQL_C_TYPE_TIMESTAMP (93)
SQL_C_TYPE_TIMESTAMP (93) | Error (from DM) | Error (from DM) | SQL_C_TIMESTAMP (11) | No mapping[2]

[1] As a result of this, an ODBC 3.x application working with an ODBC 2.x driver can use the date, time, or timestamp codes returned in the result sets that are returned by the catalog functions.

[2] As a result of this, an ODBC 3.x application working with an ODBC 3.x driver can use the date, time, or timestamp codes returned in the result sets that are returned by the catalog functions.

The following table shows how the ODBC 3.x Driver Manager performs mapping of the date, time, and timestamp SQL data types entered in the ParameterType argument of SQLBindParameter or in the DataType argument of SQLGetTypeInfo.

Data type code entered | 2.x app to 2.x driver | 2.x app to 3.x driver | 3.x app to 2.x driver | 3.x app to 3.x driver
SQL_DATE (9) | No mapping | SQL_TYPE_DATE (91) | No mapping[1] | SQL_TYPE_DATE (91)
SQL_TYPE_DATE (91) | Error (from DM) | Error (from DM) | SQL_DATE (9) | No mapping[2]
SQL_TIME (10) | No mapping | SQL_TYPE_TIME (92) | No mapping[1] | SQL_TYPE_TIME (92)
SQL_TYPE_TIME (92) | Error (from DM) | Error (from DM) | SQL_TIME (10) | No mapping[2]
SQL_TIMESTAMP (11) | No mapping | SQL_TYPE_TIMESTAMP (93) | No mapping[1] | SQL_TYPE_TIMESTAMP (93)
SQL_TYPE_TIMESTAMP (93) | Error (from DM) | Error (from DM) | SQL_TIMESTAMP (11) | No mapping[2]

[1] As a result of this, an ODBC 3.x application working with an ODBC 2.x driver can use the date, time, or timestamp codes returned in the result sets that are returned by the catalog functions.

[2] As a result of this, an ODBC 3.x application working with an ODBC 3.x driver can use the date, time, or timestamp codes returned in the result sets that are returned by the catalog functions.


Writing ODBC 3.x Applications

When an ODBC 2.x application is upgraded to ODBC 3.x, it should be written such that it works with both ODBC 2.x and 3.x drivers. The application should incorporate conditional code to take full advantage of the ODBC 3.x features.

The SQL_ATTR_ODBC_VERSION environment attribute should be set to SQL_OV_ODBC2. This will ensure that the driver behaves like an ODBC 2.x driver with respect to the changes described in the section "Behavioral Changes," earlier in this chapter.

If the application will use any of the features described in the section "New Features," earlier in this chapter, conditional code should be used to determine whether the driver is an ODBC 3.x or ODBC 2.x driver. The application uses SQLGetDiagField and SQLGetDiagRec to obtain ODBC 3.x SQLSTATEs while doing error processing on these conditional code fragments. The following points about the new functionality should be considered:

  • An application affected by the change in rowset size behavior should be careful not to call SQLFetch when the array size is greater than 1. These applications should replace calls to SQLExtendedFetch with calls to SQLSetStmtAttr to set the SQL_ATTR_ARRAY_STATUS_PTR statement attribute and to SQLFetchScroll, so that they have common code that works with both ODBC 3.x and ODBC 2.x drivers. Because SQLSetStmtAttr with SQL_ATTR_ROW_ARRAY_SIZE will be mapped to SQLSetStmtAttr with SQL_ROWSET_SIZE for ODBC 2.x drivers, applications can just set SQL_ATTR_ROW_ARRAY_SIZE for their multirow fetch operations.
  • Most applications that are upgrading are not actually affected by changes in SQLSTATE codes. For those applications that are affected, they can do a mechanical search and replace in most cases using the error conversion table in the "SQLSTATE Mapping" section to convert ODBC 3.x error codes to ODBC 2.x codes. Since the ODBC 3.x Driver Manager will perform mapping from ODBC 2.x SQLSTATEs to ODBC 3.x SQLSTATEs, these application writers need only check for the ODBC 3.x SQLSTATEs and not worry about including ODBC 2.x SQLSTATEs in conditional code.
  • If an application makes great use of date, time, and timestamp data types, the application can declare itself to be an ODBC 2.x application and use its existing code instead of using conditioning code.

The upgrade should also include the following steps:

  • Call SQLSetEnvAttr before allocating a connection to set the SQL_ATTR_ODBC_VERSION environment attribute to SQL_OV_ODBC2.
  • Replace all calls to SQLAllocEnv, SQLAllocConnect, or SQLAllocStmt with calls to SQLAllocHandle with the appropriate HandleType argument of SQL_HANDLE_ENV, SQL_HANDLE_DBC, or SQL_HANDLE_STMT.
  • Replace all calls to SQLFreeEnv or SQLFreeConnect with calls to SQLFreeHandle with the appropriate HandleType argument of SQL_HANDLE_DBC or SQL_HANDLE_STMT.
  • Replace all calls to SQLSetConnectOption with calls to SQLSetConnectAttr. If setting an attribute whose value is a string, set the StringLength argument appropriately. Change Attribute argument from SQL_XXXX to SQL_ATTR_XXXX.
  • Replace all calls to SQLGetConnectOption with calls to SQLGetConnectAttr. If getting a string or binary attribute, set BufferLength to the appropriate value and pass in a StringLength argument. Change Attribute argument from SQL_XXXX to SQL_ATTR_XXXX.
  • Replace all calls to SQLSetStmtOption with calls to SQLSetStmtAttr. If setting an attribute whose value is a string, set the StringLength argument appropriately. Change Attribute argument from SQL_XXXX to SQL_ATTR_XXXX.
  • Replace all calls to SQLGetStmtOption with calls to SQLGetStmtAttr. If getting a string or binary attribute, set BufferLength to the appropriate value and pass in a StringLength argument. Change Attribute argument from SQL_XXXX to SQL_ATTR_XXXX.
  • Replace all calls to SQLTransact with calls to SQLEndTran. If the rightmost valid handle in the SQLTransact call is an environment handle, a HandleType argument of SQL_HANDLE_ENV should be used in the SQLEndTran call with the appropriate Handle argument. If the rightmost valid handle in your SQLTransact call is a connection handle, a HandleType argument of SQL_HANDLE_DBC should be used in the SQLEndTran call with the appropriate Handle argument.
  • Replace all calls to SQLColAttributes with calls to SQLColAttribute. If the FieldIdentifier argument is either SQL_COLUMN_PRECISION, SQL_COLUMN_SCALE, or SQL_COLUMN_LENGTH, do not change anything other than the name of the function. If not, change FieldIdentifier from SQL_COLUMN_XXXX to SQL_DESC_XXXX. If FieldIdentifier is SQL_DESC_CONCISE_TYPE and the data type is a datetime data type, change to the corresponding ODBC 3.x data type.
  • If using block cursors, scrollable cursors, or both, the application does the following:
    • Sets the rowset size, cursor type, and cursor concurrency using SQLSetStmtAttr.
    • Calls SQLSetStmtAttr to set SQL_ATTR_ROW_STATUS_PTR to point to an array of status records.
    • Calls SQLSetStmtAttr to set SQL_ATTR_ROWS_FETCHED_PTR to point to an SQLINTEGER.
    • 69Performs the required bindings and executes the SQL statement.
    • Calls SQLFetchScroll in a loop to fetch rows and move around in the result set.
    • If it wants to fetch by bookmark, the application calls SQLSetStmtAttr to set SQL_ATTR_FETCH_BOOKMARK_PTR to a variable that will contain the bookmark for the row that it wants to fetch, and calls SQLFetchScroll with a FetchOrientation argument of SQL_FETCH_BOOKMARK.
  • If using arrays of parameters, the application does the following:
    • Calls SQLSetStmtAttr to set the SQL_ATTR_PARAMSET_SIZE attribute to the size of the parameter array.
    • Calls SQLSetStmtAttr to set SQL_ATTR_ROWS_PROCESSED_PTR to point to an internal UDWORD variable.
    • Performs prepare, bind, and execute operations as appropriate.
    • If execution halts for some reason (such as SQL_NEED_DATA), it can find the "current" row of parameters by inspecting the location pointed to by SQL_ATTR_ROWS_PROCESSED_PTR.


Mapping Replacement Functions for Backward Compatibility of Applications

An ODBC 3.x application working through the ODBC 3.x Driver Manager will work against an ODBC 2.x driver as long as no new features are used. Both duplicated functionality and behavioral changes do, however, affect the way that the ODBC 3.x application works on an ODBC 2.x driver. When working with an ODBC 2.x driver, the Driver Manager maps the following ODBC 3.x functions, which have replaced one or more ODBC 2.x functions, into the corresponding ODBC 2.x functions.

ODBC 3.x function | ODBC 2.x function
SQLAllocHandle | SQLAllocEnv, SQLAllocConnect, or SQLAllocStmt
SQLBulkOperations | SQLSetPos
SQLColAttribute | SQLColAttributes
SQLEndTran | SQLTransact
SQLFetch | SQLExtendedFetch
SQLFetchScroll | SQLExtendedFetch
SQLFreeHandle | SQLFreeEnv, SQLFreeConnect, or SQLFreeStmt
SQLGetConnectAttr | SQLGetConnectOption
SQLGetDiagRec | SQLError
SQLGetStmtAttr | SQLGetStmtOption[1]
SQLSetConnectAttr | SQLSetConnectOption
SQLSetStmtAttr | SQLSetStmtOption[1]

[1] Other actions might also be taken, depending on the attribute being requested.

SQLAllocHandle

The Driver Manager maps this to SQLAllocEnv, SQLAllocConnect, or SQLAllocStmt, as appropriate. The following call to SQLAllocHandle:

SQLAllocHandle(HandleType, InputHandle, OutputHandlePtr);

will result in the Driver Manager performing the following (conceptual, no error checking) mapping:

switch (HandleType) {
   case SQL_HANDLE_ENV: return (SQLAllocEnv(OutputHandlePtr));
   case SQL_HANDLE_DBC: return (SQLAllocConnect (InputHandle, OutputHandlePtr));
   case SQL_HANDLE_STMT: return (SQLAllocStmt (InputHandle, OutputHandlePtr));
   default: // return SQL_ERROR, SQLSTATE HY092 ("Invalid attribute/option identifier")
}

SQLBulkOperations

The Driver Manager maps this to SQLSetPos. The following call to SQLBulkOperations:

SQLBulkOperations(hstmt, Operation);

will result in the following sequence of steps:

  1. If the Operation argument is SQL_ADD, the Driver Manager calls SQLSetPos as follows:
    SQLSetPos (hstmt, 0, SQL_ADD, SQL_LOCK_NO_CHANGE);
  2. If the Operation argument is not SQL_ADD, the driver returns SQLSTATE HY092 (Invalid attribute/option identifier).
  3. If the application attempts to change the SQL_ATTR_ROW_STATUS_PTR between calls to SQLFetch or SQLFetchScroll and SQLBulkOperations, the Driver Manager will return SQLSTATE HY011 (Attribute cannot be set now).
  4. If the Operation argument is SQL_ADD, the application must call SQLBindCol to bind the data to be inserted. It cannot call SQLSetDescField or SQLSetDescRec to bind the data to be inserted.
  5. If the Operation argument is SQL_ADD and the number of rows to be inserted is not the same as the current rowset size, SQLSetStmtAttr must be called to set the SQL_ATTR_ROW_ARRAY_SIZE statement attribute to the number of rows to be inserted before calling SQLBulkOperations. To revert back to the previous rowset size, the application must set the SQL_ATTR_ROW_ARRAY_SIZE statement attribute before SQLFetch, SQLFetchScroll, or SQLSetPos is called.

SQLColAttribute

The Driver Manager maps this to SQLColAttributes. The following call to SQLColAttribute:

SQLColAttribute(StatementHandle, ColumnNumber, FieldIdentifier, CharacterAttributePtr, BufferLength, StringLengthPtr, NumericAttributePtr);

will result in the following sequence of steps:

  1. If FieldIdentifier is one of the following:

    SQL_DESC_PRECISION, SQL_DESC_SCALE, SQL_DESC_LENGTH, SQL_DESC_OCTET_LENGTH, SQL_DESC_UNNAMED, SQL_DESC_BASE_COLUMN_NAME, SQL_DESC_LITERAL_PREFIX, SQL_DESC_LITERAL_SUFFIX, or SQL_DESC_LOCAL_TYPE_NAME

    the Driver Manager returns SQL_ERROR with SQLSTATE HY091 (Invalid descriptor field identifier). No further rules of this section apply.

  2. The Driver Manager maps SQL_COLUMN_COUNT, SQL_COLUMN_NAME, or SQL_COLUMN_NULLABLE to SQL_DESC_COUNT, SQL_DESC_NAME, or SQL_DESC_NULLABLE, respectively. (An ODBC 2.x driver need only support SQL_COLUMN_COUNT, SQL_COLUMN_NAME, and SQL_COLUMN_NULLABLE, not SQL_DESC_COUNT, SQL_DESC_NAME, and SQL_DESC_NULLABLE.) The call to SQLColAttribute is mapped to:
    SQLColAttributes(StatementHandle, ColumnNumber, FieldIdentifier, CharacterAttributePtr, BufferLength, StringLengthPtr, NumericAttributePtr);
  3. All other FieldIdentifier values are passed through to the driver, with SQLColAttribute mapped to SQLColAttributes as shown previously.
  4. If BufferLength is less than 0, the Driver Manager returns SQL_ERROR with SQLSTATE HY090 (Invalid string or buffer length). No further rules of this section apply.
  5. If FieldIdentifier is SQL_DESC_CONCISE_TYPE and the returned type is a concise datetime data type, the Driver Manager maps the return values for date, time, and timestamp codes.
  6. The Driver Manager performs necessary checks to see whether SQLSTATE HY010 (Function sequence error) needs to be raised. If so, the Driver Manager returns SQL_ERROR and SQLSTATE HY010 (Function sequence error). No further rules of this section apply.

SQLEndTran

The Driver Manager maps this to SQLTransact. The following call to SQLEndTran:

SQLEndTran(HandleType, Handle, CompletionType);

will result in the Driver Manager performing the following (conceptual, no error checking) mapping:

switch (HandleType) {
   case SQL_HANDLE_ENV:return(SQLTransact(Handle, SQL_NULL_HDBC, CompletionType));
   case SQL_HANDLE_DBC:return(SQLTransact(SQL_NULL_HENV, Handle, CompletionType);
   default: // return SQL_ERROR, SQLSTATE HY092 ("Invalid attribute/option identifier")
}

SQLFetch

The Driver Manager maps this to SQLExtendedFetch with a FetchOrientation argument of SQL_FETCH_NEXT. The following call to SQLFetch:

SQLFetch (StatementHandle);

will result in the Driver Manager calling SQLExtendedFetch, as follows:

rc = SQLExtendedFetch(StatementHandle, FetchOrientation, FetchOffset, &RowCount, RowStatusArray);

In this call, the pcRow argument is set to the value that the application sets the SQL_ATTR_ROWS_FETCHED_PTR statement attribute to through a call to SQLSetStmtAttr.

Note When the application calls SQLSetStmtAttr to set SQL_ATTR_ROW_STATUS_PTR to point to a status array, the Driver Manager caches the pointer. RowStatusArray can be equal to a null pointer.

If the driver does not support SQLExtendedFetch and the cursor library is loaded, the Driver Manager uses the cursor library's SQLExtendedFetch to map SQLFetch to SQLExtendedFetch. If the driver does not support SQLExtendedFetch and the cursor library is not loaded, the Driver Manager passes the call to SQLFetch through to the driver. If the application calls SQLSetStmtAttr to set SQL_ATTR_ROW_STATUS_PTR, the Driver Manager ensures that the array is populated. If the application calls SQLSetStmtAttr to set SQL_ATTR_ROWS_FETCHED_PTR, the Driver Manager sets this field to 1.

SQLFetchScroll

The Driver Manager maps this to SQLExtendedFetch. The following call to SQLFetchScroll:

SQLFetchScroll(StatementHandle, FetchOrientation, FetchOffset);

will result in the following sequence of steps:

  1. When the application calls SQLSetStmtAttr to set SQL_ATTR_ROW_STATUS_PTR (which sets the SQL_DESC_ARRAY_STATUS_PTR field in the IRD) to point to a status array, the Driver Manager caches this pointer. Let this pointer be RowStatusArray; otherwise, let RowStatusArray be equal to a null pointer. If the RowStatusArray argument is set to a null pointer, the Driver Manager generates a row-status array.
  2. If FetchOrientation is not one of SQL_FETCH_NEXT, SQL_FETCH_PRIOR, SQL_FETCH_ABSOLUTE, SQL_FETCH_RELATIVE, SQL_FETCH_FIRST, SQL_FETCH_LAST, or SQL_FETCH_BOOKMARK, the Driver Manager returns with SQL_ERROR and SQLSTATE HY106 (Fetch type out of range). No further rules of this section apply.
  3. Case:
  • If FetchOrientation is equal to SQL_FETCH_BOOKMARK, then:
    • If SQLSetStmtAttr was called earlier to set the value of SQL_ATTR_FETCH_BOOKMARK_PTR, then let Bmk be the value obtained by dereferencing the pointer SQL_DESC_FETCH_BOOKMARK_PTR.
    • Otherwise, return SQL_ERROR with SQLSTATE HY111 (Invalid bookmark value). No further rules of this section apply.

    The Driver Manager now calls SQLExtendedFetch, as follows:

    rc = SQLExtendedFetch(StatementHandle, FetchOrientation, Bmk, pcRow, RowStatusArray);
  • Otherwise, the Driver Manager calls SQLExtendedFetch, as follows:
    rc = SQLExtendedFetch(StatementHandle, FetchOrientation, FetchOffset, pcRow, RowStatusArray);

    In these calls, the pcRow argument is set to the value that the application sets the SQL_ATTR_ROWS_FETCHED_PTR statement attribute to through a call to SQLSetStmtAttr.

  1. SQL_ATTR_ROW_ARRAY_SIZE is mapped to SQL_ROWSET_SIZE.
  2. If rc is equal to SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, and if FetchOrientation is equal to SQL_FETCH_BOOKMARK and FetchOffset is not equal to 0, then the Driver Manager posts a warning, SQLSTATE 01S10 (Attempt to fetch by a bookmark offset, offset value ignored), and returns SQL_SUCCESS_WITH_INFO.

SQLFreeHandle

The Driver Manager maps this to SQLFreeEnv, SQLFreeConnect, or SQLFreeStmt as appropriate. The following call to SQLFreeHandle:

SQLFreeHandle(HandleType, Handle);

will result in the Driver Manager performing the following (conceptual, no error checking) mapping:

switch (HandleType) {
   case SQL_HANDLE_ENV: return (SQLFreeEnv(Handle));
   case SQL_HANDLE_DBC: return (SQLFreeConnect(Handle));
   case SQL_HANDLE_STMT: return (SQLFreeStmt(Handle, SQL_DROP));
   default: // return SQL_ERROR, SQLSTATE HY092 ("Invalid attribute/option identifier")
}

SQLGetConnectAttr

The Driver Manager maps this to SQLGetConnectOption. The following call to SQLGetConnectAttr:

SQLGetConnectAttr(ConnectionHandle, Attribute, ValuePtr, BufferLength, StringLengthPtr);

will result in the following sequence of steps:

  1. If Attribute is not a driver-defined connection or statement attribute and is not an attribute defined in ODBC 2.x, the Driver Manager returns SQL_ERROR with SQLSTATE HY092 (Invalid attribute/option identifier). No further rules in this section apply.
  2. If Attribute is equal to SQL_ATTR_AUTO_IPD or SQL_ATTR_METADATA_ID, the Driver Manager returns SQL_ERROR with SQLSTATE HY092 (Invalid attribute/option identifier).
  3. The Driver Manager performs necessary checks to see if SQLSTATE 08003 (Connection not open) or SQLSTATE HY010 (Function sequence error) needs to be raised. If so, the Driver Manager returns SQL_ERROR and posts the appropriate error message. No further rules of this section apply.
  4. The Driver Manager calls SQLGetConnectOption as follows:
    SQLGetConnectOption (ConnectionHandle, Attribute, ValuePtr);

    Note that the BufferLength and StringLengthPtr are ignored.

SQLGetData

When an ODBC 3.x application working with an ODBC 2.x driver calls SQLGetData with the ColumnNumber argument equal to 0, the ODBC 3.x Driver Manager maps this to a call to SQLGetStmtOption with the Option attribute set to SQL_GET_BOOKMARK.

SQLGetStmtAttr

The Driver Manager maps this to SQLGetStmtOption. The following call to SQLGetStmtAttr:

SQLGetStmtAttr(StatementHandle, Attribute, ValuePtr, BufferLength, StringLengthPtr);

will result in the following sequence of steps:

  1. If Attribute is not a driver-defined connection or statement attribute and is not an attribute defined in ODBC 2.x, the Driver Manager returns SQL_ERROR with SQLSTATE HY092 (Invalid attribute/option identifier). No further rules in this section apply.
  2. If Attribute is one of the following:

    SQL_ATTR_APP_ROW_DESC, SQL_ATTR_APP_PARAM_DESC, SQL_ATTR_AUTO_IPD, SQL_ATTR_ROW_BIND_TYPE. SQL_ATTR_IMP_ROW_DESC, SQL_ATTR_IMP_PARAM_DESC, SQL_ATTR_METADATA_ID, SQL_ATTR_PARAM_BIND_TYPE, SQL_ATTR_PREDICATE_PTR, SQL_ATTR_PREDICATE_OCTET_LENGTH_PTR, SQL_ATTR_PARAM_BIND_OFFSET_PTR, SQL_ATTR_ROW_BIND_OFFSET_PTR, SQL_ATTR_ROW_OPERATION_PTR, SQL_ATTR_PARAM_OPERATION_PTR

    then the Driver Manager returns SQL_ERROR with SQLSTATE HY092 (Invalid attribute/option identifier). No further rules of this section apply.

  3. The Driver Manager performs necessary checks to see whether SQLSTATE HY010 (Function sequence error) needs to be raised. If so, the Driver Manager returns SQL_ERROR and SQLSTATE HY010 (Function sequence error). No further rules of this section apply.
  4. If Attribute is equal to SQL_ATTR_ROWS_FETCHED_PTR, the Driver Manager returns a pointer to the internal Driver Manager variable cRow, which it has used or will use in a call to SQLExtendedFetch. No further rules of this section apply.
  5. If Attribute is equal to SQL_DESC_FETCH_BOOKMARK_PTR, the Driver Manager returns the appropriate pointer that it had cached during a call to SQLSetStmtAttr.
  6. If Attribute is equal to SQL_ATTR_ROW_STATUS_PTR, the Driver Manager returns the appropriate pointer that it had cached during a call to SQLSetStmtAttr.
  7. The Driver Manager calls SQLGetStmtOption as follows:
    SQLGetStmtOption (hstmt, fOption, pvParam);

    where hstmt, fOption, and pvParam will be set to the values of StatementHandle, Attribute, and ValuePtr, respectively. The BufferLength and StringLengthPtr are ignored.

SQLSetConnectAttr

The Driver Manager maps this to SQLSetConnectOption. The following call to SQLSetConnectAttr:

SQLSetConnectAttr(ConnectionHandle, Attribute, ValuePtr, StringLength);

will result in the following sequence of steps:

  1. If Attribute is not a driver-defined connection or statement attribute and is not an attribute defined in ODBC 2.x, the Driver Manager returns SQL_ERROR with SQLSTATE HY092 (Invalid attribute/option identifier). No further rules in this section apply.
  2. If Attribute is equal to SQL_ATTR_AUTO_IPD, the Driver Manager returns SQL_ERROR with SQLSTATE HY092 (Invalid attribute/option identifier).
  3. The Driver Manager performs necessary checks to see whether SQLSTATE 08003 (Connection not open) or SQLSTATE HY010 (Function sequence error) need to be raised. If one of these errors needs to be raised, the Driver Manager returns SQL_ERROR and posts the appropriate error message. No further rules of this section apply.
  4. The Driver Manager calls SQLSetConnectOption as follows:
    SQLSetConnectOption (hdbc, fOption, vParam);

    where hdbc, fOption, and vParam will be set to the values of ConnectionHandle, Attribute, and ValuePtr, respectively. StringLengthPtr is ignored.

Note The ability to set statement attributes on the connection level has been deprecated. Statement attributes should never be set on the connection level by an ODBC 3.x application.

SQLSetStmtAttr

The Driver Manager maps this to SQLSetStmtOption. The following call to SQLSetStmtAttr:

SQLSetStmtAttr(StatementHandle, Attribute, ValuePtr, StringLength);

will result in the following sequence of steps:

  1. If Attribute is not a driver-defined connection or statement attribute and is not an attribute defined in ODBC 2.x, the Driver Manager returns SQL_ERROR with SQLSTATE HY092 (Invalid attribute/option identifier). No further rules in this section apply.
  2. If Attribute is one of the following:

    SQL_ATTR_APP_ROW_DESC, SQL_ATTR_APP_PARAM_DESC, SQL_ATTR_AUTO_IPD, SQL_ATTR_ROW_BIND_TYPE. SQL_ATTR_IMP_ROW_DESC, SQL_ATTR_IMP_PARAM_DESC, SQL_ATTR_METADATA_ID, SQL_ATTR_PARAM_BIND_TYPE, SQL_ATTR_PREDICATE_PTR, SQL_ATTR_PREDICATE_OCTET_LENGTH_PTR, SQL_ATTR_PARAM_BIND_OFFSET_PTR, SQL_ATTR_ROW_BIND_OFFSET_PTR, SQL_ATTR_ROW_OPERATION_PTR, SQL_ATTR_PARAM_OPERATION_PTR.

    then the Driver Manager returns SQL_ERROR with SQLSTATE HY092 (Invalid attribute/option identifier). No further rules of this section apply.

  3. The Driver Manager performs the necessary checks to see whether SQLSTATE HY010 (Function sequence error) need to be raised. If so, the Driver Manager returns SQL_ERROR and SQLSTATE HY010 (Function sequence error). No further rules of this section apply.
  4. If Attribute is equal to SQL_ATTR_PARAMSET_SIZE or SQL_ATTR_PARAMS_PROCESSED_PTR, see the section "Mappings for Handling Parameter Arrays," later in this topic. No further rules of this section apply.
  5. If Attribute is equal to SQL_ATTR_ROWS_FETCHED_PTR, the Driver Manager caches the pointer value for later use with SQLFetchScroll.
  6. If Attribute is equal to SQL_ATTR_ROW_STATUS_PTR, the Driver Manager caches the pointer value for later use with SQLFetchScroll or SQLSetPos. No further rules of this section apply.
  7. If Attribute is equal to SQL_ATTR_FETCH_BOOKMARK_PTR, the Driver Manager caches ValuePtr and will use the cached value later in a call to SQLFetchScroll. No further rules of this section apply.
  8. The Driver Manager calls SQLSetStmtOption as follows:
    SQLSetStmtOption (hstmt, fOption, vParam);

    where hstmt, fOption, and vParam will be set to the values of StatementHandle, Attribute, and ValuePtr, respectively. The StringLength argument is ignored.

    If an ODBC 2.x driver supports character-string, driver-specific statement options, an ODBC 3.x application should call SQLSetStmtOption to set those options.

Mappings for Handling Parameter Arrays

When the application calls:

SQLSetStmtAttr (StatementHandle, SQL_ATTR_PARAMSET_SIZE, Size, StringLength);

the Driver Manager calls:

SQLParamOptions (StatementHandle, Size, &RowCount);

The Driver Manager later returns a pointer to this variable when the application calls SQLGetStmtAttr to retrieve SQL_ATTR_PARAMS_PROCESSED_PTR. The Driver Manager cannot change this internal variable until the statement handle is returned to the prepared or allocated state.

An ODBC 3.x application can call SQLGetStmtAttr to obtain the value of SQL_ATTR_PARAMS_PROCESSED_PTR even though it has not explicitly set the SQL_DESC_ARRAY_SIZE field in the APD. This situation could arise, for example, if the application has a generic routine that checks for the current "row" of parameters being processed when SQLExecute returns SQL_NEED_DATA. This routine is invoked whether or not the SQL_DESC_ARRAY_SIZE is 1 or is greater than 1. To account for this, the Driver Manager will need to define this internal variable whether or not the application has called SQLSetStmtAttr to set the SQL_DESC_ARRAY_SIZE field in APD. If SQL_DESC_ARRAY_SIZE has not been set, the Driver Manager has to make sure that this variable contains the value 1 prior to returning from SQLExecDirect or SQLExecute.

Error Handling

In ODBC 3.x, calling SQLFetch or SQLFetchScroll populates the SQL_DESC_ARRAY_STATUS_PTR in the IRD, and the SQL_DIAG_ROW_NUMBER field of a given diagnostic record contains the number of the row in the rowset that this record pertains to. Using this, the application can correlate an error message with a given row position.

An ODBC 2.x driver will be unable to provide this functionality. However, it will provide error demarcation with SQLSTATE 01S01 (Error in row). An ODBC 3.x application that is using SQLFetch or SQLFetchScroll while going against an ODBC 2.x driver needs to be aware of this fact. Note also that such an application will be unable to call SQLGetDiagField to actually get the SQL_DIAG_ROW_NUMBER field anyway. An ODBC 3.x application working with an ODBC 2.x driver will be able to call SQLGetDiagField only with a DiagIdentifier argument of SQL_DIAG_MESSAGE_TEXT, SQL_DIAG_NATIVE, SQL_DIAG_RETURNCODE, or SQL_DIAG_SQLSTATE. The ODBC 3.x Driver Manager maintains the diagnostic data structure when working with an ODBC 2.x driver, but the ODBC 2.x driver returns only these four fields.

When an ODBC 2.x application is working with an ODBC 2.x driver, if an operation can cause multiple errors to be returned by the Driver Manager, different errors may be returned by the ODBC 3.x Driver Manager than by the ODBC 2.x Driver Manager.

Mappings for Bookmark Operations

The ODBC 3.x Driver Manager performs the following mappings when an ODBC 3.x application working with an ODBC 2.x driver performs bookmark operations.

SQLBindCol

When an ODBC 3.x application working with an ODBC 2.x driver calls SQLBindCol to bind to column 0 with fCType equal to SQL_C_VARBOOKMARK, the ODBC 3.x Driver Manager checks to see whether the BufferLength argument is less than 4 or greater than 4, and if so, returns SQLSTATE HY090 (Invalid string or buffer length). If the BufferLength argument is equal to 4, the Driver Manager calls SQLBindCol in the driver, after replacing fCType with SQL_C_BOOKMARK.

SQLColAttribute

When an ODBC 3.x application working with an ODBC 2.x driver calls SQLColAttribute with the ColumnNumber argument set to 0, the Driver Manager returns the FieldIdentifier values listed in the following table.

FieldIdentifier | Value
SQL_DESC_AUTO_UNIQUE_VALUE | SQL_FALSE
SQL_DESC_CASE_SENSITIVE | SQL_FALSE
SQL_DESC_CATALOG_NAME | "" (empty string)
SQL_DESC_CONCISE_TYPE | SQL_BINARY
SQL_DESC_COUNT | The same value returned by SQLNumResultCols
SQL_DESC_DATETIME_INTERVAL_CODE | 0
SQL_DESC_DISPLAY_SIZE | 8
SQL_DESC_FIXED_PREC_SCALE | SQL_FALSE
SQL_DESC_LABEL | "" (empty string)
SQL_DESC_LENGTH | 0
SQL_DESC_LITERAL_PREFIX | "" (empty string)
SQL_DESC_LITERAL_SUFFIX | "" (empty string)
SQL_DESC_LOCAL_TYPE_NAME | "" (empty string)
SQL_DESC_NAME | "" (empty string)
SQL_DESC_NULLABLE | SQL_NO_NULLS
SQL_DESC_OCTET_LENGTH | 4
SQL_DESC_PRECISION | 4
SQL_DESC_SCALE | 0
SQL_DESC_SCHEMA_NAME | "" (empty string)
SQL_DESC_SEARCHABLE | SQL_PRED_NONE
SQL_DESC_TABLE_NAME | "" (empty string)
SQL_DESC_TYPE | SQL_BINARY
SQL_DESC_TYPE_NAME | "" (empty string)
SQL_DESC_UNNAMED | SQL_UNNAMED
SQL_DESC_UNSIGNED | SQL_FALSE
SQL_DESC_UPDATEABLE | SQL_ATTR_READ_ONLY

SQLDescribeCol

When an ODBC 3.x application working with an ODBC 2.x driver calls SQLDescribeCol with the ColumnNumber argument set to 0, the Driver Manager returns the values listed in the following table.

Buffer | Value
ColumnName | "" (empty string)
*NameLengthPtr | 0
*DataTypePtr | SQL_BINARY
*ColumnSizePtr | 4
*DecimalDigitsPtr | 0
*NullablePtr | SQL_NO_NULLS

SQLGetData

When an ODBC 3.x application working with an ODBC 2.x driver makes the following call to SQLGetData to retrieve a bookmark:

SQLGetData(StatementHandle, 0, SQL_C_VARBOOKMARK, TargetValuePtr, BufferLength, StrLen_or_IndPtr)

the call is mapped to SQLGetStmtOption with an fOption of SQL_GET_BOOKMARK, as follows:

SQLGetStmtOption(hstmt, SQL_GET_BOOKMARK, TargetValuePtr)

where hstmt and pvParam are set to the values in StatementHandle and TargetValuePtr, respectively. The bookmark is returned in the buffer pointed to by the pvParam (TargetValuePtr) argument. The value in the buffer pointed to by the StrLen_or_IndPtr argument in the call to SQLGetData is set to 4.

This mapping is necessary to account for the case in which SQLFetch was called prior to the call to SQLGetData and the ODBC 2.x driver did not support SQLExtendedFetch. In this case, SQLFetch would be passed through to the ODBC 2.x driver, in which case bookmark retrieval is not supported.

SQLGetData cannot be called multiple times in an ODBC 2.x driver to retrieve a bookmark in parts, so calling SQLGetData with the BufferLength argument set to a value less than 4 and the ColumnNumber argument set to 0 will return SQLSTATE HY090 (Invalid string or buffer length). SQLGetData can, however, be called multiple times to retrieve the same bookmark.

SQLSetStmtAttr

When an ODBC 3.x application working with an ODBC 2.x driver calls SQLSetStmtAttr to set the SQL_ATTR_USE_BOOKMARKS attribute to SQL_UB_VARIABLE, the Driver Manager sets the attribute to SQL_UB_ON in the underlying ODBC 2.x driver.


Calling SQLCloseCursor

Because SQLCloseCursor is almost the same as SQLFreeStmt with SQL_CLOSE, the Driver Manager does not map this function. Replacement functions are mapped so that existing ODBC 2.x applications can easily move to ODBC 3.x by using the new functions. Such a move makes it easier for such applications to begin using new ODBC 3.x functionality inside of conditional code in a modular fashion. SQLCloseCursor does not represent any new functionality. An application does not gain any advantage by moving to SQLCloseCursor from SQLFreeStmt with SQL_CLOSE.


Calling SQLGetDiagField

When an ODBC 3.x application calls SQLGetDiagField in an ODBC 2.x driver, the driver will return SQL_SUCCESS and the appropriate information in *DiagInfoPtr if the DiagIdentifier argument is SQL_DIAG_CLASS_ORIGIN, SQL_DIAG_CLASS_SUBCLASS_ORIGIN, SQL_DIAG_CONNECTION_NAME, SQL_DIAG_MESSAGE_TEXT, SQL_DIAG_NATIVE, SQL_DIAG_NUMBER, SQL_DIAG_RETURNCODE, SQL_DIAG_SERVER_NAME, or SQL_DIAG_SQLSTATE. All other diagnostic fields will return SQL_ERROR.


Calling SQLSetPos

In ODBC 2.x, the pointer to the row status array was an argument to SQLExtendedFetch. The row status array was later updated by a call to SQLSetPos. Some drivers have relied on the fact that this array does not change between SQLExtendedFetch and SQLSetPos. In ODBC 3.x, the pointer to the status array is a descriptor field and therefore the application can easily change it to point to a different array. This can be a problem when an ODBC 3.x application is working with an ODBC 2.x driver but is calling SQLSetStmtAttr to set the array status pointer and is calling SQLFetchScroll to fetch data. The Driver Manager maps it as a sequence of calls to SQLExtendedFetch. In the following code, an error would normally be raised when the Driver Manager maps the second SQLSetStmtAttr call when working with an ODBC 2.x driver:

SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, rgfRowStatus, 0);
SQLFetchScroll(hstmt, fFetchType, iRow);
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, rgfRowStat1, 0);
SQLSetPos(hstmt, iRow, fOption, fLock);

The error would be raised if there were no way to change the row status pointer in ODBC 2.x between calls to SQLExtendedFetch. Instead, the Driver Manager performs the following steps when working with an ODBC 2.x driver:

  1. Initializes an internal Driver Manager flag fSetPosError to TRUE.
  2. When an application calls SQLFetchScroll, the Driver Manager sets fSetPosError to FALSE.
  3. When the application calls SQLSetStmtAttr to set SQL_ATTR_ROW_STATUS_PTR, the Driver Manager sets fSetPosError equal to TRUE.
  4. When the application calls SQLSetPos, with fSetPosError equal to TRUE, the Driver Manager raises SQL_ERROR with SQLSTATE HY011 (Attribute cannot be set now) to indicate that the application attempted to call SQLSetPos after changing the row status pointer but prior to calling SQLFetchScroll.


Cursor Library Operations

If an application working with an ODBC 2.x driver makes calls to the ODBC 3.x cursor library, the application might be able to use ODBC 3.x features that are not supported by the ODBC 2.x driver. An application writer should be careful how these features are used, however. Use of the ODBC 3.x cursor library does not make an ODBC 2.x driver into an ODBC 3.x driver.


Mapping the Cursor Attributes1 Information Types

When an ODBC 3.x application calls SQLGetInfo in an ODBC 2.x driver with the SQL_XXXX_CURSOR_ATTRIBUTES1 information type (for dynamic, forward-only, keyset-driver, or static cursors), the setting of the bits returned by Driver Manager depends on what the ODBC 2.x driver returns for the corresponding ODBC 2.x information types. The bits are set as shown in the following table.

Bit in SQL_XXXX_CURSOR_ATTRIBUTES1 | Cursor type | ODBC 2.x information type
SQL_CA1_NEXT | All | SQL_FETCH_DIRECTION
SQL_CA1_ABSOLUTE SQL_CA1_RELATIVE SQL_CA1_BOOKMARK | Dynamic, keyset-driver, static | SQL_FETCH_DIRECTION
SQL_CA1_LOCK_NO_CHANGE SQL_CA1_LOCK_UNLOCK SQL_CA1_LOCK_EXCLUSIVE | Dynamic, keyset-driver, static | SQL_LOCK_TYPES
SQL_CA1_POSITIONED_UPDATE SQL_CA1_POSITIONED_DELETE SQL_CA1_SELECT_FOR_UPDATE | All | SQL_POSITIONED_STATEMENTS
SQL_CA1_POS_POSITION SQL_CA1_POS_DELETE SQL_CA1_POS_REFRESH SQL_CA1_POS_BULK_ADD | Dynamic, keyset-driver, static | SQL_POS_OPERATIONS


SQL_NO_DATA

When an ODBC 3.x application calls SQLExecDirect, SQLExecute, or SQLParamData in an ODBC 2.x driver to execute a searched update or delete statement that does not affect any rows at the data source, the driver should return SQL_SUCCESS, not SQL_NO_DATA. When an ODBC 2.x or ODBC 3.x application working with an ODBC 3.x driver calls SQLExecDirect, SQLExecute, or SQLParamData with the same result, the ODBC 3.x driver should return SQL_NO_DATA.


Writing ODBC 3.x Drivers

The following table shows function support in an ODBC 3.x driver and an ODBC application, and the mapping performed by the Driver Manager when the functions are called against an ODBC 3.x driver.

Function | Supported by an ODBC 3.x driver? | Supported by an ODBC 3.x application? | Mapped/supported by the ODBC 3.x Driver Manager to an ODBC 3.x driver?
SQLAllocConnect | No | No[1] | Yes
SQLAllocEnv | No | No[1] | Yes
SQLAllocHandle | Yes | Yes | No
SQLAllocStmt | No | No[1] | Yes
SQLBindCol | Yes | Yes | No
SQLBindParam | No | Yes[2] | Yes
SQLBindParameter | Yes | Yes | No
SQLBrowseConnect | Yes | Yes | No
SQLCancel | Yes | Yes | No
SQLCloseCursor | Yes | Yes | No
SQLColAttribute | Yes | Yes | No
SQLColAttributes | No[3] | No | Yes
SQLColumnPrivileges | Yes | Yes | No
SQLColumns | Yes | Yes | No
SQLConnect | Yes | Yes | No
SQLCopyDesc | Yes | Yes | Yes[4]
SQLDataSources | No | Yes | Yes
SQLDescribeCol | Yes | Yes | No
SQLDescribeParam | Yes | Yes | No
SQLDisconnect | Yes | Yes | No
SQLDriverConnect | Yes | Yes | No
SQLDrivers | No | Yes | Yes
SQLEndTran | Yes | Yes | No
SQLError | No | No[1] | Yes
SQLExecDirect | Yes | Yes | No
SQLExecute | Yes | Yes | No
SQLExtendedFetch | Yes | No | No
SQLFetch | Yes | Yes | No
SQLFetchScroll | Yes | Yes | No
SQLForeignKeys | Yes | Yes | No
SQLFreeConnect | No | Yes[1] | Yes
SQLFreeEnv | No | Yes[1] | Yes
SQLFreeHandle | Yes | Yes | No
SQLFreeStmt | Yes | Yes | No
SQLGetConnectAttr | Yes | Yes | No
SQLGetConnectOption | No[5] | No[1] | Yes
SQLGetCursorName | Yes | Yes | No
SQLGetData | Yes | Yes | No
SQLGetDescField | Yes | Yes | No
SQLGetDescRec | Yes | Yes | No
SQLGetDiagField | Yes | Yes | No
SQLGetDiagRec | Yes | Yes | No
SQLGetEnvAttr | Yes | Yes | No
SQLGetFunctions | No[6] | Yes | Yes
SQLGetInfo | Yes | Yes | No
SQLGetStmtAttr | Yes | Yes | No
SQLGetStmtOption | No[5] | No[1] | Yes
SQLGetTypeInfo | Yes | Yes | No
SQLMoreResults | Yes | Yes | No
SQLNativeSql | Yes | Yes | No
SQLNumParams | Yes | Yes | No
SQLNumResultCols | Yes | Yes | No
SQLParamData | Yes | Yes | No
SQLParamOptions | No | No | Yes
SQLPrepare | Yes | Yes | No
SQLPrimaryKeys | Yes | Yes | No
SQLProcedureColumns | Yes | Yes | No
SQLProcedures | Yes | Yes | No
SQLPutData | Yes | Yes | No
SQLRowCount | Yes | Yes | No
SQLSetConnectAttr | Yes | Yes | No
SQLSetConnectOption | No[5] | No[1] | Yes
SQLSetCursorName | Yes | Yes | No
SQLSetDescField | Yes | Yes | No
SQLSetDescRec | Yes | Yes | No
SQLSetEnvAttr | Yes | Yes | No
SQLSetPos | Yes | Yes | No
SQLSetParam | No | No | Yes
SQLSetScrollOption | Yes | Yes | No
SQLSetStmtAttr | Yes | Yes | No
SQLSetStmtOption | No[5] | No[1] | Yes
SQLSpecialColumns | Yes | Yes | No
SQLStatistics | Yes | Yes | No
SQLTablePrivileges | Yes | Yes | No
SQLTables | Yes | Yes | No
SQLTransact | No | No[1] | Yes

[1] This function is deprecated in ODBC 3.x. ODBC 3.x applications should not use this function. However, an X/Open or ISO CLI–compliant application can call this function.

[2] ODBC 3.x applications should use SQLBindParameter instead of SQLBindParam. However, an X/Open or ISO CLI–compliant application can call this function.

[3] Driver writers should note that the ODBC 2.x column attributes SQL_COLUMN_PRECISION, SQL_COLUMN_SCALE, and SQL_COLUMN_LENGTH must be supported with SQLColAttribute.

[4] SQLCopyDesc is partially implemented by the Driver Manager when a descriptor is being copied across connections that belong to different drivers. Drivers are required to support SQLCopyDesc across two of their own connections. Functions such as SQLDrivers, which are implemented solely by the Driver Manager, do not show up on this list.

[5] Under certain circumstances, drivers may need to support this function. For more information, see this function's reference page.

[6] The driver can choose to support SQLGetFunctions if the set of functions that the driver supports varies from connection to connection.


ODBC in Windows

The following items apply only to ODBC running in Microsoft® Windows NT®/Windows 2000 and Microsoft Windows® 95/98 operating systems.


Standards-Compliant Applications and Drivers

A standards-compliant application or driver is one that conforms to the X/Open CAE Specification "Data Management: SQL Call-Level Interface (CLI)," and the ISO/IEC 9075-3:1995 (E) Call-Level Interface (SQL/CLI).

ODBC 3.x guarantees the following features:

  • An application written to the X/Open and ISO CLI specifications will work with an ODBC 3.x driver or a standards-compliant driver when it is compiled with the ODBC 3.x header files and linked with ODBC 3.x libraries, and when it gains access to the driver through the ODBC 3.x Driver Manager.
  • A driver written to the X/Open and ISO CLI specifications will work with an ODBC 3.x application or a standards-compliant application when it is compiled with the ODBC 3.x header files and linked with ODBC 3.x libraries, and when the application gains access to the driver through the ODBC 3.x Driver Manager.

Standards-compliant applications and drivers are compiled with the ODBC_STD compile flag.

Standards-compliant applications exhibit the following behavior:

  • If a standards-compliant application calls SQLAllocEnv (which can occur because SQLAllocEnv is a valid function in the X/Open and ISO CLI), the call is mapped to SQLAllocHandleStd at compile time. As a result, at run time, the application calls SQLAllocHandleStd. During the course of processing this call, the Driver Manager sets the SQL_ATTR_ODBC_VERSION environment attribute to SQL_OV_ODBC3. A call to SQLAllocHandleStd is equivalent to a call to SQLAllocHandle with a HandleType of SQL_HANDLE_ENV and a call to SQLSetEnvAttr to set SQL_ATTR_ODBC_VERSION to SQL_OV_ODBC3.
  • If a standards-compliant application calls SQLBindParam (which can occur because SQLBindParam is a valid function in the X/Open and ISO CLI), the ODBC 3.x Driver Manager maps the call to the equivalent call in SQLBindParameter. (See "SQLBindParam Mapping" in Appendix G: Driver Guidelines for Backward Compatibility.)
  • To align with the ISO CLI, the ODBC 3.x header files contain aliases for information types used in calls to SQLGetInfo. A standards-compliant application can use these aliases instead of the ODBC 3.x information types. For more information, see the next section, "Header Files."
  • A standards-compliant application must verify that all features it supports are supported in the driver it will work with. Setting the SQL_ATTR_CURSOR_SCROLLABLE statement attribute to SQL_SCROLLABLE and setting the SQL_ATTR_CURSOR_SENSITIVITY statement attribute to SQL_INSENSITIVE or SQL_SENSITIVE are capabilities that are available as optional features in the standards but are not included in the ODBC 3.x Core level and therefore might not be supported by all ODBC 3.x drivers. If a standards-compliant application uses these capabilities, it should verify that the driver that it will work with supports them.


Header Files

The Sql.h header file contains prototypes for the functions and features in the Core ODBC Interface conformance level. The Sqlext.h header file contains prototypes for the functions and features in the Level 1 and Level 2 API conformance levels. The Sqltypes.h header file contains type definitions and indicators for the SQL data types.

The header files all contain a #define, ODBCVER, that an application or driver can set to be compiled for different versions of ODBC.

To align with the ISO CLI and X/Open CLI, the header files contain aliases for the information types used in calls to SQLGetInfo. In the following table, the column "ODBC name" indicates the ODBC name for the information type in Chapter 21: ODBC API Reference. The column "Alias in header file" indicates the name that is used in the ISO CLI and the X/Open CLI. The actual numeric value of these manifest names is the same in both ODBC and the standard CLIs. These aliases enable a standards-compliant application or driver to compile with the ODBC 3.x header files.

These aliases include expansions of abbreviations in the ODBC names so that the names are more understandable. "MAX" is expanded to "MAXIMUM", "LEN" to "LENGTH", "MULT" to "MULTIPLE", "OJ" to "OUTER_JOIN", and "TXN" to "TRANSACTION."

ODBC name | Alias in header file
SQL_MAX_CATALOG_NAME_LEN | SQL_MAXIMUM_CATALOG_NAME_LENGTH
SQL_MAX_COLUMN_NAME_LEN | SQL_MAXIMUM_COLUMN_NAME_LENGTH
SQL_MAX_COLUMNS_IN_GROUP_BY | SQL_MAXIMUM_COLUMNS_IN_GROUP_BY
SQL_MAX_COLUMNS_IN_ORDER_BY | SQL_MAXIMUM_COLUMNS_IN_ORDER_BY
SQL_MAX_COLUMNS_IN_SELECT | SQL_MAXIMUM_COLUMNS_IN_SELECT
SQL_MAX_COLUMNS_IN_TABLE | SQL_MAXIMUM_COLUMNS_IN_TABLE
SQL_MAX_CONCURRENT_ACTIVITIES | SQL_MAXIMUM_CONCURRENT_ACTIVITIES
SQL_MAX_CURSOR_NAME_LEN | SQL_MAXIMUM_CURSOR_NAME_LENGTH
SQL_MAX_DRIVER_CONNECTIONS | SQL_MAXIMUM_DRIVER_CONNECTIONS
SQL_MAX_IDENTIFIER_LEN | SQL_MAXIMUM_IDENTIFIER_LENGTH
SQL_MAX_SCHEMA_NAME_LEN | SQL_MAXIMUM_SCHEMA_NAME_LENGTH
SQL_MAX_STATEMENT_LEN | SQL_MAXIMUM_STATEMENT_LENGTH
SQL_MAX_TABLE_NAME_LEN | SQL_MAXIMUM_TABLE_NAME_LENGTH
SQL_MAX_TABLES_IN_SELECT | SQL_MAXIMUM_TABLES_IN_SELECT
SQL_MAX_USER_NAME_LEN | SQL_MAXIMUM_USER_NAME_LENGTH
SQL_MULT_RESULT_SETS | SQL_MULTIPLE_RESULT_SETS
SQL_OJ_CAPABILITIES | SQL_OUTER_JOIN_CAPABILITIES
SQL_TXN_CAPABLE | SQL_TRANSACTION_CAPABLE
SQL_TXN_ISOLATION_OPTION | SQL_TRANSACTION_ISOLATION_OPTION


CString Class

Because objects of the CString class in Microsoft® Visual C++® are signed and string arguments in ODBC functions are unsigned, applications that pass CString objects to ODBC functions without casting them will receive compiler warnings.


Creating and Terminating Threads

Multithread applications that use ODBC should call the Microsoft® Visual C++® Run-Time Library functions _beginthread and _endthread (or _beginthreadex and _endthreadex) to create and terminate threads that call the ODBC Driver Manager. If applications call the Microsoft Windows NT® functions CreateThread and EndThread instead, memory leaks will occur because the Driver Manager and some ODBC drivers call C run-time functions that will not work on a thread created by calling CreateThread. For more information, see the Microsoft Windows® documentation.


Part 3 Installing and Configuring ODBC Software

ODBC Programmer's Reference

Part 3: Installing and Configuring ODBC Software

Part 3 of the ODBC Programmer's Reference provides information about installation and also includes a setup .dll function reference. It contains the following chapters:

  • Chapter 18: Installing ODBC Components
  • Chapter 19: Configuring Data Sources


Chapter 18: Installing ODBC Components

This chapter describes how ODBC components are installed and removed. Because driver developers always install an ODBC component (their driver), they need to read this chapter. Application developers need to read this chapter only if they will ship ODBC components with their applications. ODBC components include the Driver Manager, drivers, translators, the installer DLL, the cursor library, and any related files. For the purposes of this chapter, ODBC applications are not considered to be ODBC components.

Note This chapter is specific to Microsoft® Windows® platforms. How ODBC components are installed on other platforms is platform-specific.

ODBC components are installed and removed on a component-by-component basis, not a file-by-file basis. For example, if a translator consists of the translator itself and a number of data files, these files are installed and removed as a group; they must not be installed and removed on a file-by-file basis. The reason for this is to make sure that only complete components exist on the system.

For purposes of installing and removing components, the following are defined to be ODBC components:

  • Core components. The Driver Manager, cursor library, installer DLL, and any other related files make up the core components and must be installed and removed as a group.
  • Drivers. Each driver is a separate component.
  • Translators. Each translator is a separate component.

With the support of Unicode in ODBC 3.5 and later, some consideration must be given to using OLE DB components with ODBC. The 1.1 version of the OLE DB Provider for ODBC was written to specific Unicode specifications within ODBC 3.0. Because these specifications changed in ODBC 3.5, it is necessary to have version 1.5 or later of the provider when using ODBC 3.5 and later. Specific information about this and other redistributable file issues can be found in the "Redistributing Microsoft Data Access Components" article in the MSDN Library. The article includes, among other topics:

  • Defining Microsoft Universal Data Access Strategy, Microsoft Data Access Components (MDAC), and the MDAC SDK
  • Redistributing MDAC (including dependencies and file lists)
  • Redistribution with various development environments
  • Pros and cons of using the redistribution provided by Microsoft


Installation Components

The installation process starts when the user runs the setup program. The setup program works in conjunction with the installer DLL and a driver setup DLL for each driver. Both the setup program and the installer DLL use the arguments in the SQLInstallDriverEx and SQLInstallTranslatorEx functions to determine which files to copy or delete for each component. The following illustration shows the relationship between these installation components.

Important The Odbc.inf file that was used in ODBC 2.x to describe the files required by each ODBC component is not used in ODBC 3.x. Drivers that ship ODBC 3.x components do not need to create an Odbc.inf file. The removal of SQLInstallDriver and SQLInstallODBC, and the deprecation of SQLInstallTranslator, have rendered Odbc.inf unnecessary. The driver information that used to be in the Driver Keyword sections of Odbc.inf is now provided in the lpszDriver argument in SQLInstallDriverEx. The translator information that used to be in the [ODBC Translator] and Translator Specification sections of Odbc.inf is now provided in the lpszTranslator argument of SQLInstallTranslatorEx. These changes allow the ODBC Installer to be more portable across platforms.


Setup Program

The user runs the setup program to start the setup process. The setup program is written by the application or driver developer. In addition to installing ODBC components, it can install other software. For example, application developers might use the same setup program both to install ODBC components and to install their applications.

Developers can write the setup program from scratch, using the Microsoft® Windows® SDK setup utilities or setup software from other vendors. This gives those developers complete control over the setup program's look and feel. The setup program can be written to install additional software, such as an ODBC application. For more information on the Windows SDK setup utilities, see the Windows SDK documentation.

How much of the installation is actually done by the setup program depends on what functions it calls in the installer DLL. The installer DLL contains functions to install individual ODBC components. The setup program simply calls SQLInstallDriverManager, SQLInstallDriverEx, or SQLInstallTranslatorEx in the installer DLL to retrieve the path of the directory in which the component is to be installed and to add information about the component to the registry. These functions do not actually copy files; the setup program does this using the information in the arguments of these functions.

The installer DLL also contains functions to remove ODBC components. The setup program calls SQLRemoveDriverManager, SQLRemoveDriver, or SQLRemoveTranslator in the installer DLL to decrement a component's usage count in the registry and, if the component's new usage count falls to 0, remove all information about the component from the registry. These functions do not actually remove the files for the component; the setup program does this if the new usage count falls to 0.


Installer DLL

The installer DLL contains functions to install and remove ODBC components, maintain registry information about those components, and maintain registry information about data sources. It is written by Microsoft and can be redistributed by users of the MDAC SDK. For a complete description of the functions in the installer DLL, see Chapter 23: Installer DLL API Reference.


Driver Setup DLL

The driver setup DLL contains the ConfigDriver and ConfigDSN functions. ConfigDriver performs driver-specific installation tasks, such as entering driver-specific information into the registry. ConfigDSN maintains driver-specific information about data sources in the registry. For a complete description of these functions, see Chapter 22: Setup DLL API Reference.

The driver setup DLL is written by the driver developer. It can be part of the driver DLL or a separate DLL.


Usage Counting

Two types of usage counts are maintained in the registry for each component: a component usage count and one or more optional file usage counts. The component usage count helps the installer DLL maintain registry entries. It is stored in the UsageCount value under the ODBC Core, driver, and translator subkeys. For the format of the UsageCount value and more information about these subkeys, see "Registry Entries for ODBC Components," later in this chapter.

When a component is first installed, the installer DLL creates a subkey for it and sets the data for the UsageCount value in that subkey to 1. When the component is installed again, the installer DLL increments the usage count. When the component is removed, the installer DLL decrements the usage count. If the usage count falls to 0, the installer DLL removes the subkey for the component.

Caution An application should not physically remove Driver Manager files when the component usage count and the file usage count reach zero.

File usage counts help determine when a file must actually be copied or deleted as opposed to incrementing or decrementing the usage count. This is important because ODBC components, and therefore the files in ODBC components, are shared and can be installed or removed by a variety of applications. The application can delete driver and translator files if the component usage count and the file usage count reach zero. Driver Manager files should not, however, be deleted when both the component usage count and the file usage count have reached zero, because these files can be used by other applications that have not incremented the file usage count.

Note File usage counts are optional in Microsoft® Windows NT®/Windows 2000 and Microsoft Windows® 95/98.

File usage counts are maintained by the setup program after it calls SQLInstallDriverManager, SQLInstallDriverEx, SQLInstallTranslatorEx, SQLRemoveDriverManager, SQLRemoveDriver, or SQLRemoveTranslator.

When a component is first installed, the setup program or installer DLL creates a value under the following key for each file in that component that is not already on the system:

HKEY_LOCAL_MACHINE
SOFTWARE
Microsoft
Windows
CurrentVersion
SharedDlls

It sets the data for those values to 1 and copies the file to the system. When the component is installed again, the setup program or installer DLL increments the usage counts. When the component is removed, the setup program or installer DLL decrements the usage counts. If any usage count falls to 0, the setup program or installer DLL removes the value for the file and, if the component is a driver or a translator, deletes the file. Driver Manager files should not be deleted.

The format of the file usage count value is shown in the following table.

Name | Data type | Data
full-path | REG_DWORD | count

For example, suppose a driver for Informix uses the Infrmx32.dll and Infrmx32.hlp files, and suppose that this driver has been installed twice. The values under the SharedDlls subkey for the Informix driver would be as follows:

C:\WINDOWS\SYSTEM32\INFRMX32.DLL : REG_DWORD : 0x2
C:\WINDOWS\SYSTEM32\INFRMX32.HLP : REG_DWORD : 0x2


Preventing Repeated Connection Attempts

When Connection Pooling is enabled and the database server has become unavailable, the Driver Manager attempts to reestablish a connection to the server repeatedly. ODBCSetTryWaitValue and ODBCGetTryWaitValue prevent this and, consequently, the large number of generated requests. ODBCSetTryWaitValue saves the information in the registry at the following location:

HKEY_LOCAL_MACHINE
SOFTWARE
Odbc
Odbcinst.ini
ODBC Connection Pooling
Retry Wait

If there is a bad connection in the pool, the ODBC Driver Manager will attempt to connect before the connection can be reused for the first time. If the connection still fails, the ODBC Driver Manager returns the error and marks the connection with the time. From that point until the RetryWait value expires, the ODBC Driver Manager returns a failure without testing the connection.

ODBCSetTryWaitValue

BOOL ODBCSetTryWaitValue ( DWORD dwValue );

Arguments

dwValue [Input]
The number of seconds to wait until the Driver Manager attempts to connect again.

Returns

The function returns TRUE if it is successful, FALSE if it fails.

ODBCGetTryWaitValue

DWORD ODBCGetTryWaitValue ( );

Arguments

None.

Returns

The function returns a DWORD value containing the number of seconds the Driver Manager will wait before attempting to connect again.


ODBC Application Files

This section contains various listings of local files that are applicable to the ODBC API, including:

  • Redistributable files
  • Header files


Redistributable Files

A number of files that are shipped with the ODBC component of the MDAC SDK may be redistributed by application and driver developers. All developers who ship ODBC drivers must redistribute the files described in the following table.

ODBC component | File | Core component?
Administrator | Odbcad32.exe | No
Code page | 12520437.cpx 12520850.cpx Mscpxl32.dll | No No No
Connection pooling | Mtxdm.dll | No
Driver Manager | Ds16gt.dll Ds32gt.dll Odbc16gt.dll Odbc32.dll Odbc32gt.dll Odbccp32.cpl Odbccp32.dll Odbccr32.dll Odbccu32.dll Odbcint.dll Odbctrac.dll Msdadc.dll Msdaenum.dll Msdaer.dll Msdaerr.dll Msdaps.dll Msdasql.dll Msdasqlr.dll Msdatl2.dll Msdatt.dll Oledb32x.dll Oledb32.dll Oledb32r.dll | Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
Help | Odbcinst.chm | Yes
Support files | Msvcrt.dll | No File has dependencies


ODBC Header Files

Note Redistribution of ODBC header files is not required. None are considered "Core".

Header Files

Odbcinst.h
Sql.h
Sqlext.h
Sqltypes.h
Sqlucode.h
Msdasql.h
Msdadc.h


Registry Entries for ODBC Components

The installer DLL maintains information in the registry about each installed ODBC component. On computers running Microsoft® Windows NT® and Microsoft Windows® 95/98, this information is stored in subkeys under the following key in the registry:

HKEY_LOCAL_MACHINE
SOFTWARE
ODBC
Odbcinst.ini

Because Odbcinst.ini is a subkey of the HKEY_LOCAL_MACHINE tree, the information about ODBC components is available to all users of the machine. For information about the registry, see the Platform SDK documentation and the release notes for the ODBC component of the MDAC SDK.


ODBC Core Subkey

The value under the ODBC Core subkey gives the usage count for the core components (Driver Manager, cursor library, installer DLL, and so on). The format of this value is shown in the following table.

Name | Data type | Data
UsageCount | REG_DWORD | count

For example, suppose the ODBC Core components have been installed by the setup programs for three different applications and two different drivers. The value under the ODBC Core subkey would be:

UsageCount : REG_DWORD : 0x5


ODBC Drivers Subkey

The values under the ODBC Drivers subkey list the installed drivers. The format of these values is shown in the following table.

Name | Data type | Data
driver-description | REG_SZ | Installed

The driver-description name is defined by the driver developer. It is usually the name of the DBMS associated with the driver.

For example, suppose drivers have been installed for formatted text files and SQL Server. The values under the ODBC Drivers subkey might be:

Text : REG_SZ : Installed
SQL Server : REG_SZ : Installed


Driver Specification Subkeys

Each driver listed in the ODBC Drivers subkey has a subkey of its own. This subkey has the same name as the corresponding value under the ODBC Drivers subkey. The values under this subkey list the full paths of the driver and driver setup DLLs, the values of the driver keywords returned by SQLDrivers, and the usage count. The formats of the values are as shown in the following table.

Name | Data type | Data
APILevel | REG_SZ | 0 | 1 | 2
ConnectFunctions | REG_SZ | {Y|N}{Y|N}{Y|N}
CreateDSN | REG_SZ | driver-description
Driver | REG_SZ | driver-DLL-path
DriverODBCVer | REG_SZ | nn.nn
FileExtns | REG_SZ | *. file-extension1[,*. file-extension2]...
FileUsage | REG_SZ | 0 | 1 | 2
Setup | REG_SZ | setup-DLL-path
SQLLevel | REG_SZ | 0 | 1 | 2
UsageCount | REG_DWORD | count

The use of each keyword is shown in the following table.

Keyword | Usage
APILevel A number indicating the ODBC interface conformance level supported by the driver:

0 = None

1 = Level 1 supported

2 = Level 2 supported

This must be the same as the value returned for the SQL_ODBC_INTERFACE_CONFORMANCE option in SQLGetInfo.
CreateDSN | The name of one or more data sources to be created when the driver is installed. The system information must include one data source specification section for each data source listed with the CreateDSN keyword. These sections should not include the Driver keyword, because this is specified in the driver specification section, but must include enough information for the ConfigDSN function in the driver setup DLL to create a data source specification without displaying any dialog boxes. For the format of a data source specification section, see "Data Source Specification Subkeys" in Chapter 19: Configuring Data Sources.
ConnectFunctions | A three-character string indicating whether the driver supports SQLConnect, SQLDriverConnect, and SQLBrowseConnect. If the driver supports SQLConnect, the first character is "Y"; otherwise, it is "N". If the driver supports SQLDriverConnect, the second character is "Y"; otherwise, it is "N". If the driver supports SQLBrowseConnect, the third character is "Y"; otherwise, it is "N". For example, if a driver supports SQLConnect and SQLDriverConnect but not SQLBrowseConnect, the three-character string is "YYN".
DriverODBCVer A character string with the version of ODBC that the driver supports. The version is of the form nn.nn, where the first two digits are the major version and the next two digits are the minor version. For the version of ODBC described in this manual, the driver must return "03.00".

This must be the same as the value returned for the SQL_DRIVER_ODBC_VER option in SQLGetInfo.
FileExtns | For file-based drivers, a comma-separated list of extensions of the files the driver can use. For example, a dBASE driver might specify *.dbf and a formatted text file driver might specify *.txt,*.csv. For an example of how an application might use this information, see the FileUsage keyword.
FileUsage A number indicating how a file-based driver directly treats files in a data source.

0 = The driver is not a file-based driver. For example, an ORACLE driver is a DBMS-based driver.

1 = A file-based driver treats files in a data source as tables. For example, an Xbase driver treats each Xbase file as a table.

2 = A file-based driver treats files in a data source as a catalog. For example, a Microsoft® Access driver treats each Microsoft Access file as a complete database.

An application might use this to determine how users will select data. For example, Xbase and Paradox users often think of data as stored in files, while ORACLE and Microsoft Access users generally think of data as stored in tables.

When a user selects Open Data File from the File menu, an application could display the Windows File Open common dialog box. The list of file types would use the file extensions specified with the FileExtns keyword for drivers that specify a FileUsage value of 1 and "Y" as the second character of the value of the ConnectFunctions keyword. After the user selects a file, the application would call SQLDriverConnect with the DRIVER keyword and then execute a SELECT * FROM table-name statement.

When the user selects Import Data from the File menu, an application could display a list of descriptions for drivers that specify a FileUsage value of 0 or 2, and "Y" as the second character of the value of the ConnectFunctions keyword. After the user selects a driver, the application would call SQLDriverConnect with the DRIVER keyword and then display a custom Select Table dialog box.
SQLLevel A number indicating the SQL-92 grammar supported by the driver:

0 = SQL-92 Entry

1 = FIPS127-2 Transitional

2 = SQL-92 Intermediate

3 = SQL-92 Full

This must be the same as the value returned for the SQL_SQL_CONFORMANCE option in SQLGetInfo.

For information about usage counts, see "Usage Counting" earlier in this chapter.

For example, suppose a driver for formatted text files has a driver DLL named Text.dll, a separate driver setup DLL named Txtsetup.dll, and has been installed three times. If the driver supports the Level 1 API conformance level, supports the Minimum SQL grammar conformance level, treats files as tables, and can use files with the .txt and .csv extensions, the values under the Text subkey might be as follows:

APILevel : REG_SZ : 1
ConnectFunctions : REG_SZ : YYN
Driver : REG_SZ : C:\WINDOWS\SYSTEM32\TEXT.DLL
DriverODBCVer : REG_SZ : 03.00.00
FileExtns : REG_SZ : *.txt,*.csv
FileUsage : REG_SZ : 1
Setup : REG_SZ : C:\WINDOWS\SYSTEM32\TXTSETUP.DLL
SQLLevel : REG_SZ : 0
UsageCount : REG_DWORD : 0x3


Default Driver Subkey

The Default subkey contains a single value that describes the driver used by the default data source. The format of this value is shown in the following table.

Name | Data type | Data
Driver | REG_SZ | default-driver-description

The default-driver-description name is the same as the name of the value under the ODBC Drivers subkey that describes the driver.

For example, if the default data source uses the SQL Server driver, the value under the Default subkey might be:

Driver : REG_SZ : SQL Server

Note The default driver contained in the Default subkey can refer to either a default user DSN or a default system DSN. If both a default user DSN and a default system DSN have been created, the default driver is determined by the DSN that was created last, so it might not be a valid entry for the DSN that was created first.


ODBC Translators Subkey

The values under the ODBC Translators subkey list the installed translators. The format of these values is shown in the following table.

Name | Data type | Data
translator-desc | REG_SZ | Installed

The translator-desc name is defined by the translator developer.

For example, suppose a user has installed the Microsoft® Code Page Translator and a custom ASCII to EBCDIC translator. The values under the ODBC Translators subkey might be as follows:

MS Code Page Translator: REG_SZ : Installed
ASCII to EBCDIC: REG_SZ : Installed.


Translator Specification Subkeys

Each translator listed in the ODBC Translators subkey has a subkey of its own. This subkey has the same name as the corresponding value under the ODBC Translators subkey. The values under this subkey list the full paths of the translator and translator setup DLLs and the usage count. The formats of the values are as shown in the following table.

Name | Data type | Data
Translator | REG_SZ | translator-DLL-path
Setup | REG_SZ | setup-DLL-path
UsageCount | REG_DWORD | count

For example, suppose the Microsoft® Code Page Translator has a translation DLL named Mscpxl32.dll, that the translator setup functions are in the same DLL, and that the translator has been installed three times. The values under the Microsoft Code Page Translator subkey might be as follows:

Translator : REG_SZ : C:\WINDOWS\SYSTEM32\MSCPXL32.DLL
Setup : REG_SZ : C:\WINDOWS\SYSTEM32\MSCPXL32.DLL
UsageCount : REG_DWORD : 0x3


Chapter 19: Configuring Data Sources

Information about data sources is stored in the system registry. Users modify data source information through an administration program. This can be the ODBC Administrator shipped with the Microsoft® Data Access Components (MDAC) SDK, the ODBC Control Panel device, or an administration program written by an application or driver developer.

Note This chapter is specific to Microsoft Windows® platforms. How data sources are configured on other platforms is platform-specific.


Configuration Components

Data sources are configured by the installer DLL, which in turn calls driver setup DLLs and translator setup DLLs as needed. The installer DLL is either invoked directly from the Control Panel or loaded and called by another program, known as the administration program. The following illustration shows the relationship between the configuration components.


Administration Program

An administration program, the ODBC Administrator, is shipped with the MDAC SDK and can be redistributed by users of the SDK. In addition, developers can write their own administration programs. Generally, developers write their own administration programs only if they want to retain complete control over data source configuration, or if they are configuring data sources directly from an application that is acting as an administration program. For example, a spreadsheet program might allow users to add and then use data sources at run time.

The administration program first loads the installer DLL. It then calls functions in the installer DLL to perform the following tasks:

  • Add, modify, or delete data sources interactively. The administration program can call SQLManageDataSources, SQLCreateDataSource, or SQLConfigDataSource.

    SQLManageDataSources displays a dialog box with which the user can add, modify, or delete data sources and specify tracing options; this function is called when the installer DLL is invoked directly from the Control Panel. SQLCreateDataSource displays a dialog box with which the user can only add data sources. SQLConfigDataSource passes the call directly to the driver setup DLL.

    In all cases, the installer DLL calls ConfigDSN in the driver setup DLL to actually add, modify, or delete the data source. The driver setup DLL might prompt the user for additional information.

  • Add, modify, or delete data sources silently. The administration program calls SQLConfigDataSource in the installer DLL and passes it a null window handle, the name of a data source to add, modify, or delete, and a list of values for the registry. The installer DLL calls ConfigDSN in the driver setup DLL to actually add, modify, or delete the data source.
  • Add, modify, or delete a default data source. The default data source is the same as any other data source, except that its name is Default. It is added, modified, or deleted in the same fashion as any other data source.


The Installer DLL

The installer DLL contains functions to maintain registry information about data sources, install and remove ODBC components, and maintain registry information about those components. It is written by Microsoft and can be redistributed by users of the MDAC SDK. For a complete description of the functions in the installer DLL, see Chapter 23: Installer DLL API Reference.


Driver Setup DLLs

The driver setup DLL contains the ConfigDriver and ConfigDSN functions. ConfigDSN maintains driver-specific information about data sources in the registry. ConfigDriver performs driver-specific installation tasks, such as entering driver-specific information into the registry. For a complete description of these functions, see Chapter 22: Setup DLL API Reference.

ConfigDSN calls the following functions in the installer DLL to maintain data source information in the registry:

  • SQLWriteDSNToIni. Add a data source.
  • SQLRemoveDSNFromIni. Delete a data source.
  • SQLWritePrivateProfileString. Write a driver-specific value under a data source specification subkey.
  • SQLGetPrivateProfileString. Read a driver-specific value from a data source specification subkey.
  • SQLGetTranslator. Prompt the user for a translator name and option. This function calls ConfigTranslator in the translator setup DLL.

The driver setup DLL is written by the driver developer. It can be part of the driver DLL or a separate DLL.


Translator Setup DLLs

The translator setup DLL contains the ConfigTranslator function, which returns the default option for a translator. If necessary, it prompts the user for this information. For a complete description of this function, see Chapter 22: Setup DLL API Reference.

The translator setup DLL is written by the translator developer. It can be part of the translator DLL or a separate DLL.


Registry Entries for Data Sources

The installer DLL maintains information in the registry about each data source. In Microsoft® Windows NT®/Windows 2000 and Microsoft Windows® 95/98, this information is stored in subkeys under one of the following two keys in the registry:

HKEY_LOCAL_MACHINE
SOFTWARE
ODBC
Odbc.ini
HKEY_CURRENT_USER
SOFTWARE
ODBC
Odbc.ini

Which key is used depends on whether the data source is a system data source, which is available to all users, or a user data source, which is available only to the current user. System data sources are stored on the HKEY_LOCAL_MACHINE tree, and user data sources are stored on the HKEY_CURRENT_USER tree. In all other respects, system data sources and user data sources are identical. For information on the registry, see the Platform SDK documentation.


ODBC Data Sources Subkey

The values under the ODBC Data Sources subkey list the data sources. The format of these values is as shown in the following table.

Name | Data type | Data
data-source-name | REG_SZ | driver-description

The data-source-name value is defined by the administration program (which usually prompts the user for it), and driver-description is defined by the driver developer (it is usually the name of the DBMS associated with the driver).

For example, suppose three data sources have been defined: Inventory, which uses SQL Server; Payroll, which uses dBASE; and Personnel, which uses formatted text files. The values under the ODBC Data Sources subkey might be as follows:

Inventory : REG_SZ : SQL Server
Payroll : REG_SZ : dBASE
Personnel : REG_SZ : Text


Data Source Specification Subkeys

Each data source listed in the ODBC Data Sources subkey has a subkey of its own. This subkey has the same name as the corresponding value under the ODBC Data Sources subkey. The values under this subkey must list the driver DLL and may list a description of the data source. If the driver supports translators, the values may list the name of a default translator, the default translation DLL, and the default translation option. The values may also list other information required by the driver to connect to the data source. For example, the driver might require a server name, database name, or schema name.

The formats of the values are as shown in the following table. Only the Driver value is required.

Name | Data type | Data
Description | REG_SZ | description
Driver | REG_SZ | driver-DLL-path
TranslationDLL | REG_SZ | translator-DLL-path
TranslationName | REG_SZ | translator-name
TranslationOption | REG_SZ | translation-option
opt-value-name | opt-value-type | opt-value-data

For example, suppose the SQL Server driver requires the server name and a flag for OEM to ANSI conversion and defines the Server and OEMTOANSI values for these. Suppose also that the Inventory data source uses the Microsoft® Code Page Translator to translate between the Windows® Latin 1 (1250) and Multilingual (850) code pages. The values under the Inventory subkey might be as follows:

Description : REG_SZ : Inventory database on server InvServ
Driver : REG_SZ : C:\WINDOWS\SYSTEM32\SQLSRV32.DLL
OEMTOANSI : REG_SZ : Yes
Server : REG_SZ : InvServ
TranslationDLL : REG_SZ : C:\WINDOWS\SYSTEM32\MSCPXL32.DLL
TranslationName : REG_SZ : MS Code Page Translator
TranslationOption : REG_SZ : 12500850


Default Subkey

The registry may specify a default data source with the Default subkey. This subkey is a special case of a data source specification subkey and has the same values as any other data source specification subkey. The only difference is that it is not listed as a value under the ODBC Data Sources subkey.


ODBC Subkey

The values under the ODBC subkey specify ODBC tracing options. These options are set through the Tracing tab of the ODBC Data Source Administrator dialog box displayed by SQLManageDataSources. The ODBC subkey itself is optional. The format of these values is as shown in the following table.

Name | Data type | Data
Trace | REG_SZ | 0 | 1
TraceAutoStop | REG_SZ | 0 | 1
TraceFile | REG_SZ | tracefile-path

The values have the meanings described in the following table.

Value Meaning
Trace If the Trace value is set to 1 when an application calls SQLAllocHandle with the SQL_HANDLE_ENV option, tracing is enabled for the calling application.

If the Trace keyword is set to 0 when an application calls SQLAllocHandle with the SQL_HANDLE_ENV option, tracing is disabled for the calling application. This is the default value.

An application can enable or disable tracing with the SQL_ATTR_TRACE connection attribute. However, doing so does not change the data for this value.
TraceFile If tracing is enabled, the Driver Manager writes to the trace file specified by the TraceFile value.

If no trace file is specified, the Driver Manager writes to the Sql.log file on the current drive. This is the default value.

Tracing should be used only for a single application, or each application should specify a different trace file. Otherwise, two or more applications will attempt to open the same trace file at the same time, causing an error.

An application can specify a new trace file with the SQL_ATTR_TRACEFILE connection attribute. However, doing so does not change the data for this value.
TraceAutoStop If the TraceAutoStop value is set to 1 when an application calls SQLFreeHandle with the SQL_HANDLE_ENV option, tracing is disabled for all applications and the Trace value is set to 0. This is the default value.

If the TraceAutoStop value is set to 0, tracing must be disabled through the Tracing tab of the ODBC Data Source Administrator dialog box displayed by the SQLManageDataSources function.

For example, suppose that tracing is enabled, the trace file is C:\Odbc.log, and that tracing is to stop automatically. The values under the ODBC subkey would be as follows:

Trace : REG_SZ : 1
TraceAutoStop : REG_SZ : 1
TraceFile : REG_SZ : C:\ODBC.LOG


Part 4 API Reference

ODBC Programmer's Reference

Part 4: API Reference

Part 4 of the ODBC Programmer's Reference contains syntax and semantic information for all ODBC functions. It includes the following chapters:

  • Chapter 20: Function Summary
  • Chapter 21: ODBC API Reference
  • Chapter 22: Setup DLL API Reference
  • Chapter 23: Installer DLL API Reference
  • Chapter 24: Translation DLL Function Reference


Chapter 20: Function Summary

This chapter summarizes the functions used by ODBC-enabled applications and related software:

  • ODBC function summary
  • Setup DLL function summary
  • Installer DLL function summary
  • Translation DLL function summary


ODBC Function Summary

The following table lists ODBC functions, grouped by type of task, and includes the conformance designation and a brief description of the purpose of each function. For more information about conformance designations, see "ODBC and the Standard CLI" in Chapter 1: Introduction to ODBC. For more information about the syntax and semantics for each function, see Chapter 21: ODBC API Reference.

An application can call the SQLGetInfo function to obtain conformance information about a driver. To obtain information about support for a specific function in a driver, an application can call SQLGetFunctions.

Task | Function name | Conformance | Purpose
Connecting to a data source | SQLAllocHandle | ISO 92 | Obtains an environment, connection, statement, or descriptor handle.
 | SQLConnect | ISO 92 | Connects to a specific driver by data source name, user ID, and password.
 | SQLDriverConnect | ODBC | Connects to a specific driver by connection string or requests that the Driver Manager and driver display connection dialog boxes for the user.
 | SQLBrowseConnect | ODBC | Returns successive levels of connection attributes and valid attribute values. When a value has been specified for each connection attribute, connects to the data source.
Obtaining information about a driver and data source | SQLDataSources SQLDrivers | ISO 92 ODBC | Returns the list of available data sources. Returns the list of installed drivers and their attributes.
 | SQLGetInfo | ISO 92 | Returns information about a specific driver and data source.
 | SQLGetFunctions | ISO 92 | Returns supported driver functions.
 | SQLGetTypeInfo | ISO 92 | Returns information about supported data types.
Setting and retrieving driver attributes | SQLSetConnectAttr SQLGetConnectAttr | ISO 92 ISO 92 | Sets a connection attribute. Returns the value of a connection attribute.
 | SQLSetEnvAttr | ISO 92 | Sets an environment attribute.
 | SQLGetEnvAttr | ISO 92 | Returns the value of an environment attribute.
 | SQLSetStmtAttr | ISO 92 | Sets a statement attribute.
 | SQLGetStmtAttr | ISO 92 | Returns the value of a statement attribute.
Setting and retrieving descriptor fields | SQLGetDescField SQLGetDescRec | ISO 92 ISO 92 | Returns the value of a single descriptor field. Returns the values of multiple descriptor fields.
 | SQLSetDescField | ISO 92 | Sets a single descriptor field.
 | SQLSetDescRec | ISO 92 | Sets multiple descriptor fields.
Preparing SQL requests | SQLPrepare | ISO 92 | Prepares an SQL statement for later execution.
 | SQLBindParameter | ODBC | Assigns storage for a parameter in an SQL statement.
 | SQLGetCursorName | ISO 92 | Returns the cursor name associated with a statement handle.
 | SQLSetCursorName | ISO 92 | Specifies a cursor name.
 | SQLSetScrollOptions | ODBC | Sets options that control cursor behavior.
Submitting requests | SQLExecute SQLExecDirect | ISO 92 ISO 92 | Executes a prepared statement. Executes a statement.
 | SQLNativeSql | ODBC | Returns the text of an SQL statement as translated by the driver.
 | SQLDescribeParam | ODBC | Returns the description for a specific parameter in a statement.
 | SQLNumParams | ISO 92 | Returns the number of parameters in a statement.
 | SQLParamData | ISO 92 | Used in conjunction with SQLPutData to supply parameter data at execution time. (Useful for long data values.)
 | SQLPutData | ISO 92 | Sends part or all of a data value for a parameter. (Useful for long data values.)
Retrieving results and information about results | SQLRowCount SQLNumResultCols | ISO 92 ISO 92 | Returns the number of rows affected by an insert, update, or delete request. Returns the number of columns in the result set.
 | SQLDescribeCol | ISO 92 | Describes a column in the result set.
 | SQLColAttribute | ISO 92 | Describes attributes of a column in the result set.
 | SQLBindCol | ISO 92 | Assigns storage for a result column and specifies the data type.
 | SQLFetch | ISO 92 | Returns multiple result rows.
 | SQLFetchScroll | ISO 92 | Returns scrollable result rows.
 | SQLGetData | ISO 92 | Returns part or all of one column of one row of a result set. (Useful for long data values.)
 | SQLSetPos | ODBC | Positions a cursor within a fetched block of data and allows an application to refresh data in the rowset or to update or delete data in the result set.
 | SQLBulkOperations | ODBC | Performs bulk insertions and bulk bookmark operations, including update, delete, and fetch by bookmark.
 | SQLMoreResults | ODBC | Determines whether there are more result sets available and, if so, initializes processing for the next result set.
 | SQLGetDiagField | ISO 92 | Returns additional diagnostic information (a single field of the diagnostic data structure).
 | SQLGetDiagRec | ISO 92 | Returns additional diagnostic information (multiple fields of the diagnostic data structure).
Obtaining information about the data source's system tables (catalog functions) | SQLColumnPrivileges SQLColumns | ODBC X/Open | Returns a list of columns and associated privileges for one or more tables. Returns the list of column names in specified tables.
 | SQLForeignKeys | ODBC | Returns a list of column names that make up foreign keys, if they exist for a specified table.
 | SQLPrimaryKeys | ODBC | Returns the list of column names that make up the primary key for a table.
 | SQLProcedureColumns | ODBC | Returns the list of input and output parameters, as well as the columns that make up the result set for the specified procedures.
 | SQLProcedures | ODBC | Returns the list of procedure names stored in a specific data source.
 | SQLSpecialColumns | X/Open | Returns information about the optimal set of columns that uniquely identifies a row in a specified table, or the columns that are automatically updated when any value in the row is updated by a transaction.
 | SQLStatistics | ISO 92 | Returns statistics about a single table and the list of indexes associated with the table.
 | SQLTablePrivileges | ODBC | Returns a list of tables and the privileges associated with each table.
 | SQLTables | X/Open | Returns the list of table names stored in a specific data source.
Terminating a statement | SQLFreeStmt | ISO 92 | Ends statement processing, discards pending results, and, optionally, frees all resources associated with the statement handle.
 | SQLCloseCursor | ISO 92 | Closes a cursor that has been opened on a statement handle.
 | SQLCancel | ISO 92 | Cancels an SQL statement.
 | SQLEndTran | ISO 92 | Commits or rolls back a transaction.
Terminating a connection | SQLDisconnect SQLFreeHandle | ISO 92 ISO 92 | Closes the connection. Releases an environment, connection, statement, or descriptor handle.


Setup DLL Function Summary

The following table describes setup DLL functions. For more information about the syntax and semantics for each function, see Chapter 22: Setup DLL API Reference.

Task | Function name | Purpose
Setting up data sources and translators | ConfigDriver ConfigDSN ConfigTranslator | Installs or uninstalls a driver. Adds, modifies, or deletes a data source. Returns a default translation option.


Installer DLL Function Summary

The following table describes the functions in the installer DLL. For more information about the syntax and semantics for each function, see Chapter 23: Installer DLL API Reference.

Task | Function name | Purpose
Installing ODBC | SQLConfigDriver | Loads the driver-specific setup DLL.
 | SQLGetInstalledDrivers | Returns a list of installed drivers.
 | SQLInstallDriverEx | Adds a driver to the system information.
 | SQLInstallDriverManager | Returns the target directory for the Driver Manager.
 | SQLInstallerError | Returns error or status information for the installer functions.
 | SQLInstallTranslatorEx | Adds a translator to the system information.
 | SQLPostInstallerError | Allows a driver or translator setup library to report errors.
 | SQLRemoveDriver | Removes a driver from the system information.
 | SQLRemoveDriverManager | Removes ODBC core components from the system information.
 | SQLRemoveTranslator | Removes the translator from the system information.
Configuring data sources | SQLConfigDataSource | Calls the driver-specific setup DLL.
 | SQLCreateDataSource | Displays a dialog box to add a data source.
 | SQLGetConfigMode | Retrieves the configuration mode that indicates where the Odbc.ini entry listing DSN values is in the system information.
 | SQLGetPrivateProfileString | Writes a value to the system information.
 | SQLGetTranslator | Displays a dialog box to select a translator.
 | SQLManageDataSources | Displays a dialog box to configure data sources and drivers.
 | SQLReadFileDSN | Reads information from file DSNs.
 | SQLRemoveDefaultDataSource | Removes the default data source.
 | SQLRemoveDSNFromIni | Removes a data source.
 | SQLSetConfigMode | Sets the configuration mode that indicates where the Odbc.ini entry listing DSN values is in the system information.
 | SQLValidDSN | Checks the length and validity of the data source name.
 | SQLWriteDSNToIni | Adds a data source.
 | SQLWriteFileDSN | Writes information to file DSNs.
 | SQLWritePrivateProfileString | Gets a value from the system information.


Translation DLL Function Summary

The following table describes translation DLL functions. For more information about the syntax and semantics for each function, see Chapter 24: Translation DLL Function Reference.

Task | Function name | Purpose
Translating data | SQLDataSourceToDriver | Translates all data flowing from the data source to the driver.
 | SQLDriverToDataSource | Translates all data flowing from the driver to the data source.


Chapter 21: ODBC API Reference

The following topics describe each ODBC function in alphabetical order. Each function is defined as a C programming language function. Descriptions include the following:

  • Purpose
  • ODBC version
  • Standard CLI conformance level
  • Syntax
  • Arguments
  • Return values
  • Diagnostics
  • Comments about usage and implementation
  • Code example
  • References to related functions

The standard CLI conformance level can be one of the following: ISO 92, X/Open, ODBC, or Deprecated. A function tagged as ISO 92–conformant also appears in X/Open version 1, because X/Open is a pure superset of ISO 92. A function tagged as X/Open-compliant also appears in ODBC 3.x, because ODBC 3.x is a pure superset of X/Open version 1. A function tagged as ODBC-compliant appears in neither standard. A function tagged as deprecated has been deprecated in ODBC 3.x.

Handling of diagnostic information is described in the SQLGetDiagField function description. The text associated with SQLSTATE values is included to provide a description of the condition but is not intended to prescribe specific text.


SQLAllocConnect

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

In ODBC 3.x, the ODBC 2.x function SQLAllocConnect has been replaced by SQLAllocHandle. For more information, see SQLAllocHandle.

Note For more information about what the Driver Manager maps this function to when an ODBC 2.x application is working with an ODBC 3.x driver, see "Mapping Deprecated Functions" in Appendix G: Driver Guidelines for Backward Compatibility.


SQLAllocEnv

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

In ODBC 3.x, the ODBC 2.x function SQLAllocEnv has been replaced by SQLAllocHandle. For more information, see SQLAllocHandle.

Note For more information about what the Driver Manager maps this function to when an ODBC 2.x application is working with an ODBC 3.x driver, see "Mapping Deprecated Functions" in Appendix G: Driver Guidelines for Backward Compatibility.


SQLAllocHandle

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLAllocHandle allocates an environment, connection, statement, or descriptor handle.

Note This function is a generic function for allocating handles that replaces the ODBC 2.0 functions SQLAllocConnect, SQLAllocEnv, and SQLAllocStmt. To allow applications calling SQLAllocHandle to work with ODBC 2.x drivers, a call to SQLAllocHandle is mapped in the Driver Manager to SQLAllocConnect, SQLAllocEnv, or SQLAllocStmt, as appropriate. For more information, see "Comments." For more information about what the Driver Manager maps this function to when an ODBC 3.x application is working with an ODBC 2.x driver, see "Mapping Replacement Functions for Backward Compatibility of Applications" in Chapter 17: Programming Considerations.

Syntax

SQLRETURN SQLAllocHandle(
     SQLSMALLINT     HandleType,
     SQLHANDLE     InputHandle,
     SQLHANDLE *     OutputHandlePtr);

Arguments

HandleType
[Input]
The type of handle to be allocated by SQLAllocHandle. Must be one of the following values:

SQL_HANDLE_ENV
SQL_HANDLE_DBC
SQL_HANDLE_STMT
SQL_HANDLE_DESC

InputHandle
[Input]
The input handle in whose context the new handle is to be allocated. If HandleType is SQL_HANDLE_ENV, this is SQL_NULL_HANDLE. If HandleType is SQL_HANDLE_DBC, this must be an environment handle, and if it is SQL_HANDLE_STMT or SQL_HANDLE_DESC, it must be a connection handle.
OutputHandlePtr
[Output]
Pointer to a buffer in which to return the handle to the newly allocated data structure.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_INVALID_HANDLE, or SQL_ERROR.

When allocating a handle other than an environment handle, if SQLAllocHandle returns SQL_ERROR, it sets OutputHandlePtr to SQL_NULL_HDBC, SQL_NULL_HSTMT, or SQL_NULL_HDESC, depending on the value of HandleType, unless the output argument is a null pointer. The application can then obtain additional information from the diagnostic data structure associated with the handle in the InputHandle argument.

Environment Handle Allocation Errors

Environment allocation occurs both within the Driver Manager and within each driver. The error returned by SQLAllocHandle with a HandleType of SQL_HANDLE_ENV depends on the level in which the error occurred.

If the Driver Manager cannot allocate memory for *OutputHandlePtr when SQLAllocHandle with a HandleType of SQL_HANDLE_ENV is called, or the application provides a null pointer for OutputHandlePtr, SQLAllocHandle returns SQL_ERROR. The Driver Manager sets *OutputHandlePtr to SQL_NULL_HENV (unless the application provided a null pointer, which returns SQL_ERROR). There is no handle with which to associate additional diagnostic information. (If the driver has additional diagnostic information, it will put the information on a skeletal handle that it allocates; the Driver Manager will read the information from the diagnostic structure associated with this handle.)

The Driver Manager does not call the driver-level environment handle allocation function until the application calls SQLConnect, SQLBrowseConnect, or SQLDriverConnect. If an error occurs in the driver-level SQLAllocHandle function, then the Driver Manager–level SQLConnect, SQLBrowseConnect, or SQLDriverConnect function returns SQL_ERROR. The diagnostic data structure contains SQLSTATE IM004 (Driver's SQLAllocHandle failed), followed by a driver-specific SQLSTATE value from the driver. For example, SQLSTATE HY001 (Memory allocation error) indicates that the Driver Manager's call to the driver-level SQLAllocHandle returned SQL_ERROR. The error is returned on a connection handle.

For additional information about the flow of function calls between the Driver Manager and a driver, see SQLConnect.

Diagnostics

When SQLAllocHandle returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with the appropriate HandleType and Handle set to the value of InputHandle. SQL_SUCCESS_WITH_INFO (but not SQL_ERROR) can be returned for the OutputHandle argument. The following table lists the SQLSTATE values commonly returned by SQLAllocHandle and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08003 | Connection does not exist | (DM) The HandleType argument was SQL_HANDLE_STMT or SQL_HANDLE_DESC, but the connection specified by the InputHandle argument was not open. The connection process must be completed successfully (and the connection must be open) for the driver to allocate a statement or descriptor handle.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | (DM) The Driver Manager was unable to allocate memory for the specified handle. The driver was unable to allocate memory for the specified handle.
HY009 | Invalid use of null pointer | (DM) The OutputHandlePtr argument was a null pointer.
HY010 | Function sequence error | (DM) The HandleType argument was SQL_HANDLE_DBC, and SQLSetEnvAttr has not been called to set the SQL_ODBC_VERSION environment attribute.
HY013 | Memory management error | The HandleType argument was SQL_HANDLE_DBC, SQL_HANDLE_STMT, or SQL_HANDLE_DESC; and the function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY014 | Limit on the number of handles exceeded | The driver-defined limit for the number of handles that can be allocated for the type of handle indicated by the HandleType argument has been reached.
HY092 | Invalid attribute/option identifier | (DM) The HandleType argument was not: SQL_HANDLE_ENV, SQL_HANDLE_DBC, SQL_HANDLE_STMT, or SQL_HANDLE_DESC.
HYC00 | Optional feature not implemented | The HandleType argument was SQL_HANDLE_DESC and the driver was an ODBC 2.x driver.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The HandleType argument was SQL_HANDLE_STMT, and the driver was not a valid ODBC driver. (DM) The HandleType argument was SQL_HANDLE_DESC, and the driver does not support allocating a descriptor handle.

Comments

SQLAllocHandle is used to allocate handles for environments, connections, statements, and descriptors, as described in the following sections. For general information about handles, see "Handles" in Chapter 4: ODBC Fundamentals.

More than one environment, connection, or statement handle can be allocated by an application at a time if multiple allocations are supported by the driver. In ODBC, no limit is defined on the number of environment, connection, statement, or descriptor handles that can be allocated at any one time. Drivers may impose a limit on the number of a certain type of handle that can be allocated at a time; for more information, see the driver documentation.

If the application calls SQLAllocHandle with *OutputHandlePtr set to an environment, connection, statement, or descriptor handle that already exists, the driver overwrites the information associated with the handle, unless the application is using connection pooling (see "Allocating an Environment Attribute for Connection Pooling" later in this section). The Driver Manager does not check to see whether the handle entered in *OutputHandlePtr is already in use, nor does it check the previous contents of a handle before overwriting them.

Note It is incorrect ODBC application programming to call SQLAllocHandle twice with the same application variable defined for *OutputHandlePtr without calling SQLFreeHandle to free the handle before reallocating it. Overwriting ODBC handles in such a manner can lead to inconsistent behavior or errors on the part of ODBC drivers.

On operating systems that support multiple threads, applications can use the same environment, connection, statement, or descriptor handle on different threads. Drivers must therefore support safe, multithread access to this information; one way of achieving this, for example, is through the use of a critical section or a semaphore. For more information about threading, see "Multithreading" in Chapter 17: Programming Considerations.

SQLAllocHandle does not set the SQL_ATTR_ODBC_VERSION environment attribute when it is called to allocate an environment handle; the environment attribute must be set by the application, or SQLSTATE HY010 (Function sequence error) will be returned when SQLAllocHandle is called to allocate a connection handle.

For standards-compliant applications, SQLAllocHandle is mapped to SQLAllocHandleStd at compile time. The difference between these two functions is that SQLAllocHandleStd sets the SQL_ATTR_ODBC_VERSION environment attribute to SQL_OV_ODBC3 when it is called with the HandleType argument set to SQL_HANDLE_ENV. This is done because standards-compliant applications are always ODBC 3.x applications. Moreover, the standards do not require the application version to be registered. This is the only difference between these two functions; otherwise, they are identical.

Allocating an Environment Handle

An environment handle provides access to global information such as valid connection handles and active connection handles. For general information about environment handles, see "Environment Handles" in Chapter 4: ODBC Fundamentals.

To request an environment handle, an application calls SQLAllocHandle with a HandleType of SQL_HANDLE_ENV and an InputHandle of SQL_NULL_HANDLE. The driver allocates memory for the environment information and passes the value of the associated handle back in the *OutputHandlePtr argument. The application passes the *OutputHandle value in all subsequent calls that require an environment handle argument. For more information, see "Allocating the Environment Handle" in Chapter 6: Connecting to a Data Source or Driver.

Under a Driver Manager's environment handle, if there already exists a driver's environment handle, then SQLAllocHandle with a HandleType of SQL_HANDLE_ENV is not called in that driver when a connection is made, only SQLAllocHandle with a HandleType of SQL_HANDLE_DBC. If a driver's environment handle does not exist under the Driver Manager's environment handle, then both SQLAllocHandle with a HandleType of SQL_HANDLE_ENV and SQLAllocHandle with a HandleType of SQL_HANDLE_DBC are called in the driver when the first connection handle of the environment is connected to the driver.

When the Driver Manager processes the SQLAllocHandle function with a HandleType of SQL_HANDLE_ENV, it checks the Trace keyword in the [ODBC] section of the system information. If it is set to 1, the Driver Manager enables tracing for the current application on a computer running Microsoft® Windows® 95/98, Microsoft Windows NT® Server/Windows 2000 Server, or Microsoft Windows NT Workstation/Windows 2000 Professional. If the trace flag is set, tracing starts when the first environment handle is allocated and ends when the last environment handle is freed. For more information, see Chapter 19: Configuring Data Sources.

After allocating an environment handle, an application must call SQLSetEnvAttr on the environment handle to set the SQL_ATTR_ODBC_VERSION environment attribute. If this attribute is not set before SQLAllocHandle is called to allocate a connection handle on the environment, the call to allocate the connection will return SQLSTATE HY010 (Function sequence error). For more information, see "Declaring the Application's ODBC Version" in Chapter 6: Connecting to a Data Source or Driver.

Allocating Shared Environments for Connection Pooling

Environments can be shared among multiple components on a single process. A shared environment can be used by more than one component simultaneously. When a component uses a shared environment, it can use pooled connections, which allow it to allocate and use an existing connection without re-creating that connection.

Before allocating a shared environment to be used for connection pooling, an application must call SQLSetEnvAttr to set the SQL_ATTR_CONNECTION_POOLING environment attribute to SQL_CP_ONE_PER_DRIVER or SQL_CP_ONE_PER_HENV. SQLSetEnvAttr in this case is called with EnvironmentHandle set to null, which makes the attribute a process-level attribute.

After connection pooling has been enabled, an application calls SQLAllocHandle with the HandleType argument set to SQL_HANDLE_ENV. The environment allocated by this call will be an implicit shared environment because connection pooling has been enabled. (For more information about connection pooling, see SQLConnect.)

When a shared environment is allocated, the environment to be used is not determined until SQLAllocHandle with a HandleType of SQL_HANDLE_DBC is called. At that point, the Driver Manager attempts to find an existing environment that matches the environment attributes requested by the application. If no such environment exists, one is created as a shared environment. The Driver Manager maintains a reference count for each shared environment; the count is set to 1 when the environment is first created. If a matching environment is found, the handle of that environment is returned to the application and the reference count is incremented. An environment handle allocated this way can be used in any ODBC function that accepts an environment handle as an input argument.

Allocating a Connection Handle

A connection handle provides access to information such as the valid statement and descriptor handles on the connection and whether a transaction is currently open. For general information about connection handles, see "Connection Handles" in Chapter 4: ODBC Fundamentals.

To request a connection handle, an application calls SQLAllocHandle with a HandleType of SQL_HANDLE_DBC. The InputHandle argument is set to the environment handle that was returned by the call to SQLAllocHandle that allocated that handle. The driver allocates memory for the connection information and passes the value of the associated handle back in *OutputHandlePtr. The application passes the *OutputHandlePtr value in all subsequent calls that require a connection handle. For more information, see "Allocating a Connection Handle" in Chapter 6: Connecting to a Data Source or Driver.

The Driver Manager processes the SQLAllocHandle function and calls the driver's SQLAllocHandle function when the application calls SQLConnect, SQLBrowseConnect, or SQLDriverConnect. (For more information, see SQLConnect.)

If the SQL_ATTR_ODBC_VERSION environment attribute is not set before SQLAllocHandle is called to allocate a connection handle on the environment, the call to allocate the connection will return SQLSTATE HY010 (Function sequence error).

When an application calls SQLAllocHandle with the InputHandle argument set to SQL_HANDLE_DBC and also set to a shared environment handle, the Driver Manager attempts to find an existing shared environment that matches the environment attributes set by the application. If no such environment exists, one is created, with a reference count (maintained by the Driver Manager) of 1. If a matching shared environment is found, that handle is returned to the application and its reference count is incremented.

The actual connection to be used is not determined by the Driver Manager until SQLConnect or SQLDriverConnect is called. The Driver Manager uses the connection options in the call to SQLConnect (or the connection keywords in the call to SQLDriverConnect) and the connection attributes set after connection allocation to determine which connection in the pool should be used. For more information, see SQLConnect.

Allocating a Statement Handle

A statement handle provides access to statement information, such as error messages, the cursor name, and status information for SQL statement processing. For general information about statement handles, see "Statement Handles" in Chapter 4: ODBC Fundamentals.

To request a statement handle, an application connects to a data source and then calls SQLAllocHandle prior to submitting SQL statements. In this call, HandleType should be set to SQL_HANDLE_STMT and InputHandle should be set to the connection handle that was returned by the call to SQLAllocHandle that allocated that handle. The driver allocates memory for the statement information, associates the statement handle with the specified connection, and passes the value of the associated handle back in *OutputHandlePtr. The application passes the *OutputHandlePtr value in all subsequent calls that require a statement handle. For more information, see "Allocating a Statement Handle" in Chapter 9: Executing Statements.

When the statement handle is allocated, the driver automatically allocates a set of four descriptors and assigns the handles for these descriptors to the SQL_ATTR_APP_ROW_DESC, SQL_ATTR_APP_PARAM_DESC, SQL_ATTR_IMP_ROW_DESC, and SQL_ATTR_IMP_PARAM_DESC statement attributes. These are called implicitly allocated descriptors. To allocate an application descriptor explicitly, see the following section, "Allocating a Descriptor Handle."

Allocating a Descriptor Handle

When an application calls SQLAllocHandle with a HandleType of SQL_HANDLE_DESC, the driver allocates an application descriptor. These are called explicitly allocated descriptors. The application directs a driver to use an explicitly allocated application descriptor in place of an automatically allocated one for a given statement handle by calling the SQLSetStmtAttr function with the SQL_ATTR_APP_ROW_DESC or SQL_ATTR_APP_PARAM_DESC attribute. An implementation descriptor cannot be allocated explicitly, nor can an implementation descriptor be specified in an SQLSetStmtAttr function call.

Explicitly allocated descriptors are associated with a connection handle rather than a statement handle (as automatically allocated descriptors are). Descriptors remain allocated only when an application is actually connected to the database. Because explicitly allocated descriptors are associated with a connection handle, an application can associate an explicitly allocated descriptor with more than one statement within a connection. An implicitly allocated application descriptor, on the other hand, cannot be associated with more than one statement handle. (It cannot be associated with any statement handle other than the one that it was allocated for.) Explicitly allocated descriptor handles can be freed explicitly either by the application or by calling SQLFreeHandle with a HandleType of SQL_HANDLE_DESC, or implicitly when the connection is closed.

When the explicitly allocated descriptor is freed, the implicitly allocated descriptor is once again associated with the statement. (The SQL_ATTR_APP_ROW_DESC or SQL_ATTR_APP_PARAM_DESC attribute for that statement is once again set to the implicitly allocated descriptor handle.) This is true for all statements that were associated with the explicitly allocated descriptor on the connection.

For more information about descriptors, see Chapter 13: Descriptors.

Code Example

See SQLBrowseConnect, SQLConnect, and SQLSetCursorName.

Related Functions

For information about | See
Executing an SQL statement | SQLExecDirect
Executing a prepared SQL statement | SQLExecute
Freeing an environment, connection, statement, or descriptor handle | SQLFreeHandle
Preparing a statement for execution | SQLPrepare
Setting a connection attribute | SQLSetConnectAttr
Setting a descriptor field | SQLSetDescField
Setting an environment attribute | SQLSetEnvAttr
Setting a statement attribute | SQLSetStmtAttr


SQLAllocStmt

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

In ODBC 3.x, the ODBC 2.x function SQLAllocStmt has been replaced by SQLAllocHandle. For more information, see SQLAllocHandle.

Note For more information about what the Driver Manager maps this function to when an ODBC 2.x application is working with an ODBC 3.x driver, see "Mapping Deprecated Functions" in Appendix G: Driver Guidelines for Backward Compatibility.


SQLBindCol

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLBindCol binds application data buffers to columns in the result set.

Syntax

SQLRETURN SQLBindCol(
     SQLHSTMT     StatementHandle,
     SQLUSMALLINT     ColumnNumber,
     SQLSMALLINT     TargetType,
     SQLPOINTER     TargetValuePtr,
     SQLINTEGER     BufferLength,
     SQLLEN *     StrLen_or_Ind);

Arguments

StatementHandle
[Input]
Statement handle.
ColumnNumber
[Input]
Number of the result set column to bind. Columns are numbered in increasing column order starting at 0, where column 0 is the bookmark column. If bookmarks are not used—that is, the SQL_ATTR_USE_BOOKMARKS statement attribute is set to SQL_UB_OFF—then column numbers start at 1.
TargetType
[Input]
The identifier of the C data type of the *TargetValuePtr buffer. When retrieving data from the data source with SQLFetch, SQLFetchScroll, SQLBulkOperations, or SQLSetPos, the driver converts the data to this type; when sending data to the data source with SQLBulkOperations or SQLSetPos, the driver converts the data from this type. For a list of valid C data types and type identifiers, see the "C Data Types" section in Appendix D: Data Types.

If the TargetType argument is an interval data type, the default interval leading precision (2) and the default interval seconds precision (6), as set in the SQL_DESC_DATETIME_INTERVAL_PRECISION and SQL_DESC_PRECISION fields of the ARD, respectively, are used for the data. If the TargetType argument is SQL_C_NUMERIC, the default precision (driver-defined) and default scale (0), as set in the SQL_DESC_PRECISION and SQL_DESC_SCALE fields of the ARD, are used for the data. If any default precision or scale is not appropriate, the application should explicitly set the appropriate descriptor field by a call to SQLSetDescField or SQLSetDescRec.

TargetValuePtr
[Deferred Input/Output]
Pointer to the data buffer to bind to the column. SQLFetch and SQLFetchScroll return data in this buffer. SQLBulkOperations returns data in this buffer when Operation is SQL_FETCH_BY_BOOKMARK; it retrieves data from this buffer when Operation is SQL_ADD or SQL_UPDATE_BY_BOOKMARK. SQLSetPos returns data in this buffer when Operation is SQL_REFRESH; it retrieves data from this buffer when Operation is SQL_UPDATE.

If TargetValuePtr is a null pointer, the driver unbinds the data buffer for the column. An application can unbind all columns by calling SQLFreeStmt with the SQL_UNBIND option. An application can unbind the data buffer for a column but still have a length/indicator buffer bound for the column, if the TargetValuePtr argument in the call to SQLBindCol is a null pointer but the StrLen_or_IndPtr argument is a valid value.

BufferLength
[Input]
Length of the *TargetValuePtr buffer in bytes.

The driver uses BufferLength to avoid writing past the end of the *TargetValuePtr buffer when returning variable-length data, such as character or binary data. Note that the driver counts the null-termination character when returning character data to *TargetValuePtr. *TargetValuePtr must therefore contain space for the null-termination character or the driver will truncate the data.

When the driver returns fixed-length data, such as an integer or a date structure, the driver ignores BufferLength and assumes the buffer is large enough to hold the data. It is therefore important for the application to allocate a large enough buffer for fixed-length data or the driver will write past the end of the buffer.

SQLBindCol returns SQLSTATE HY090 (Invalid string or buffer length) when BufferLength is less than 0 but not when BufferLength is 0. However, if TargetType specifies a character type, an application should not set BufferLength to 0, because ISO CLI–compliant drivers return SQLSTATE HY090 (Invalid string or buffer length) in that case.

StrLen_or_IndPtr
[Deferred Input/Output]
Pointer to the length/indicator buffer to bind to the column. SQLFetch and SQLFetchScroll return a value in this buffer. SQLBulkOperations retrieves a value from this buffer when Operation is SQL_ADD, SQL_UPDATE_BY_BOOKMARK, or SQL_DELETE_BY_BOOKMARK. SQLBulkOperations returns a value in this buffer when Operation is SQL_FETCH_BY_BOOKMARK. SQLSetPos returns a value in this buffer when Operation is SQL_REFRESH; it retrieves a value from this buffer when Operation is SQL_UPDATE.

SQLFetch, SQLFetchScroll, SQLBulkOperations, and SQLSetPos can return the following values in the length/indicator buffer:

  • The length of the data available to return
  • SQL_NO_TOTAL
  • SQL_NULL_DATA

The application can place the following values in the length/indicator buffer for use with SQLBulkOperations or SQLSetPos:

  • The length of the data being sent
  • SQL_NTS
  • SQL_NULL_DATA
  • SQL_DATA_AT_EXEC
  • The result of the SQL_LEN_DATA_AT_EXEC macro
  • SQL_COLUMN_IGNORE

If the indicator buffer and the length buffer are separate buffers, the indicator buffer can return only SQL_NULL_DATA, while the length buffer can return all other values.

For more information, see SQLBulkOperations, SQLFetch, SQLSetPos, and the "Using Length/Indicator Values" section in Chapter 4: ODBC Fundamentals.

If StrLen_or_IndPtr is a null pointer, no length or indicator value is used. This is an error when fetching data and the data is NULL.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLBindCol returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLBindCol and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
07006 | Restricted data type attribute violation | (DM) The ColumnNumber argument was 0, and the TargetType argument was not SQL_C_BOOKMARK or SQL_C_VARBOOKMARK.
07009 | Invalid descriptor index | The value specified for the argument ColumnNumber exceeded the maximum number of columns in the result set.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY003 | Invalid application buffer type | The argument TargetType was neither a valid data type nor SQL_C_DEFAULT.
HY010 | Function sequence error | (DM) An asynchronously executing function was called for the StatementHandle and was still executing when this function was called. (DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) The value specified for the argument BufferLength was less than 0. (DM) The driver was an ODBC 2.x driver, the ColumnNumber argument was set to 0, and the value specified for the argument BufferLength was not equal to 4.
HYC00 | Optional feature not implemented | The driver or data source does not support the conversion specified by the combination of the TargetType argument and the driver-specific SQL data type of the corresponding column. The argument ColumnNumber was 0 and the driver does not support bookmarks. The driver supports only ODBC 2.x and the argument TargetType was one of the following: SQL_C_GUID SQL_C_NUMERIC SQL_C_SBIGINT SQL_C_UBIGINT and any of the interval C data types listed in "C Data Types" in Appendix D: Data Types.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

SQLBindCol is used to associate, or bind, columns in the result set to data buffers and length/indicator buffers in the application. When the application calls SQLFetch, SQLFetchScroll, or SQLSetPos to fetch data, the driver returns the data for the bound columns in the specified buffers; for more information, see SQLFetch. When the application calls SQLBulkOperations to update or insert a row or SQLSetPos to update a row, the driver retrieves the data for the bound columns from the specified buffers; for more information, see SQLBulkOperations or SQLSetPos. For more information about binding, see Chapter 10: Retrieving Results (Basic).

Note that columns do not have to be bound to retrieve data from them. An application can also call SQLGetData to retrieve data from columns. Although it is possible to bind some columns in a row and call SQLGetData for others, this is subject to some restrictions. For more information, see SQLGetData.

Binding, Unbinding, and Rebinding Columns

A column can be bound, unbound, or rebound at any time, even after data has been fetched from the result set. The new binding takes effect the next time a function that uses bindings is called. For example, suppose an application binds the columns in a result set and calls SQLFetch. The driver returns the data in the bound buffers. Now suppose the application binds the columns to a different set of buffers. The driver does not place the data for the just-fetched row in the newly bound buffers. Instead, it waits until SQLFetch is called again and then places the data for the next row in the newly bound buffers.

Note The statement attribute SQL_ATTR_USE_BOOKMARKS should always be set before binding a column to column 0. This is not required but is strongly recommended.

Binding Columns

To bind a column, an application calls SQLBindCol and passes the column number, type, address, and length of a data buffer, and the address of a length/indicator buffer. For information about how these addresses are used, see "Buffer Addresses," later in this section. For more information about binding columns, see "Using SQLBindCol" in Chapter 10: Retrieving Results (Basic).

The use of these buffers is deferred; that is, the application binds them in SQLBindCol but the driver accesses them from other functions—namely SQLBulkOperations, SQLFetch, SQLFetchScroll, or SQLSetPos. It is the application's responsibility to make sure that the pointers specified in SQLBindCol remain valid as long as the binding remains in effect. If the application allows these pointers to become invalid—for example, it frees a buffer—and then calls a function that expects them to be valid, the consequences are undefined. For more information, see "Deferred Buffers" in Chapter 4: ODBC Fundamentals.

The binding remains in effect until it is replaced by a new binding, the column is unbound, or the statement is freed.

Unbinding Columns

To unbind a single column, an application calls SQLBindCol with ColumnNumber set to the number of that column and TargetValuePtr set to a null pointer. If ColumnNumber refers to an unbound column, SQLBindCol still returns SQL_SUCCESS.

To unbind all columns, an application calls SQLFreeStmt with fOption set to SQL_UNBIND. This also can be accomplished by setting the SQL_DESC_COUNT field of the ARD to zero.

Rebinding Columns

An application can perform either of two operations to change a binding:

  • Call SQLBindCol to specify a new binding for a column that is already bound. The driver overwrites the old binding with the new one.
  • Specify an offset to be added to the buffer address that was specified by the binding call to SQLBindCol. For more information, see the next section, "Binding Offsets."

Binding Offsets

A binding offset is a value that is added to the addresses of the data and length/indicator buffers (as specified in the TargetValuePtr and StrLen_or_IndPtr argument) before they are dereferenced. When offsets are used, the bindings are a "template" of how the application's buffers are laid out, and the application can move this "template" to different areas of memory by changing the offset. Because the same offset is added to each address in each binding, the relative offsets between buffers for different columns must be the same within each set of buffers. This is always true when row-wise binding is used; the application must carefully lay out its buffers for this to be true when column-wise binding is used.

Using a binding offset has much the same effect as rebinding a column by calling SQLBindCol. The difference is that a new call to SQLBindCol specifies new addresses for the data buffer and length/indicator buffer, while use of a binding offset does not change the addresses but merely adds an offset to them. The application can specify a new offset whenever it wants, and this offset is always added to the originally bound addresses. In particular, if the offset is set to 0 or if the statement attribute is set to a null pointer, the driver uses the originally bound addresses.

To specify a binding offset, the application sets the SQL_ATTR_ROW_BIND_OFFSET_PTR statement attribute to the address of an SQLINTEGER buffer. Before the application calls a function that uses bindings, it places an offset in bytes in this buffer. To determine the address of the buffer to use, the driver adds the offset to the address in the binding. The sum of the address and the offset must be a valid address, but the address to which the offset is added need not be valid. For more information about how binding offsets are used, see "Buffer Addresses," later in this section.

Binding Arrays

If the rowset size (the value of the SQL_ATTR_ROW_ARRAY_SIZE statement attribute) is greater than 1, the application binds arrays of buffers rather than single buffers. For more information, see "Block Cursors" in Chapter 11: Retrieving Results (Advanced).

The application can bind arrays in two ways:

  • Bind an array to each column. This is called column-wise binding because each data structure (array) contains data for a single column.
  • Define a structure to hold the data for an entire row and bind an array of these structures. This is called row-wise binding because each data structure contains the data for a single row.

Each array of buffers must have at least as many elements as the size of the rowset.

Note An application must verify that alignment is valid. For more information about alignment considerations, see "Alignment" in Chapter 17: Programming Considerations.

Column-Wise Binding

In column-wise binding, the application binds separate data and length/indicator arrays to each column.

To use column-wise binding, the application first sets the SQL_ATTR_ROW_BIND_TYPE statement attribute to SQL_BIND_BY_COLUMN. (This is the default.) For each column to be bound, the application performs the following steps:

  1. Allocates a data buffer array.
  2. Allocates an array of length/indicator buffers.

    Note If the application writes directly to descriptors when column-wise binding is used, separate arrays can be used for length and indicator data.

  3. Calls SQLBindCol with the following arguments:
    • TargetType is the type of a single element in the data buffer array.
    • TargetValuePtr is the address of the data buffer array.
    • BufferLength is the size of a single element in the data buffer array. The BufferLength argument is ignored when the data is fixed-length data.
    • StrLen_or_IndPtr is the address of the length/indicator array.

For more information about how this information is used, see "Buffer Addresses," later in this section. For more information about column-wise binding, see "Column-Wise Binding" in Chapter 11: Retrieving Results (Advanced).

Row-Wise Binding

In row-wise binding, the application defines a structure containing data and length/indicator buffers for each column to be bound.

To use row-wise binding, the application performs the following steps:

  1. Defines a structure to hold a single row of data (including both data and length/indicator buffers) and allocates an array of these structures.

    Note If the application writes directly to descriptors when row-wise binding is used, separate fields can be used for length and indicator data.

  2. Sets the SQL_ATTR_ROW_BIND_TYPE statement attribute to the size of the structure containing a single row of data or to the size of an instance of a buffer into which the results columns will be bound. The length must include space for all of the bound columns, and any padding of the structure or buffer, to make sure that when the address of a bound column is incremented with the specified length, the result will point to the beginning of the same column in the next row. When using the sizeof operator in ANSI C, this behavior is guaranteed.
  3. Calls SQLBindCol with the following arguments for each column to be bound:
    • TargetType is the type of the data buffer member to be bound to the column.
    • TargetValuePtr is the address of the data buffer member in the first array element.
    • BufferLength is the size of the data buffer member.
    • StrLen_or_IndPtr is the address of the length/indicator member to be bound.

For more information about how this information is used, see "Buffer Addresses," later in this section. For more information about column-wise binding, see "Row-Wise Binding" in Chapter 11: Retrieving Results (Advanced).

Buffer Addresses

The buffer address is the actual address of the data or length/indicator buffer. The driver calculates the buffer address just prior to writing to the buffers (such as during fetch time). It is calculated from the following formula, which uses the addresses specified in the TargetValuePtr and StrLen_or_IndPtr arguments, the binding offset, and the row number:

Bound Address + Binding Offset + ((Row Number – 1) x Element Size)

where the formula's variables are defined as described in the following table.

Variable | Description
Bound Address For data buffers, the address specified with the TargetValuePtr argument in SQLBindCol.

For length/indicator buffers, the address specified with the StrLen_or_IndPtr argument in SQLBindCol. For more information, see "Additional Comments" in the "Descriptors and SQLBindCol" section.

If the bound address is 0, no data value is returned, even if the address as calculated by the previous formula is nonzero.
Binding Offset If row-wise binding is used, the value stored at the address specified with the SQL_ATTR_ROW_BIND_OFFSET_PTR statement attribute.

If column-wise binding is used or if the value of the SQL_ATTR_ROW_BIND_OFFSET_PTR statement attribute is a null pointer, Binding Offset is 0.
Row Number | The 1-based number of the row in the rowset. For single-row fetches, which are the default, this is 1.
Element Size The size of an element in the bound array.

If column-wise binding is used, this is sizeof(SQLINTEGER) for length/indicator buffers. For data buffers, it is the value of the BufferLength argument in SQLBindCol if the data type is variable length, and the size of the data type if the data type is fixed length.

If row-wise binding is used, this is the value of the SQL_ATTR_ROW_BIND_TYPE statement attribute for both data and length/indicator buffers.

Descriptors and SQLBindCol

The following sections describe how SQLBindCol interacts with descriptors.

Caution Calling SQLBindCol for one statement can affect other statements. This occurs when the ARD associated with the statement is explicitly allocated and is also associated with other statements. Because SQLBindCol modifies the descriptor, the modifications apply to all statements with which this descriptor is associated. If this is not the required behavior, the application should dissociate this descriptor from the other statements before calling SQLBindCol.

Argument Mappings

Conceptually, SQLBindCol performs the following steps in sequence:

  1. Calls SQLGetStmtAttr to obtain the ARD handle.
  2. Calls SQLGetDescField to get this descriptor's SQL_DESC_COUNT field, and if the value in the ColumnNumber argument exceeds the value of SQL_DESC_COUNT, calls SQLSetDescField to increase the value of SQL_DESC_COUNT to ColumnNumber.
  3. Calls SQLSetDescField multiple times to assign values to the following fields of the ARD:
    • Sets SQL_DESC_TYPE and SQL_DESC_CONCISE_TYPE to the value of TargetType, except that if TargetType is one of the concise identifiers of a datetime or interval subtype, it sets SQL_DESC_TYPE to SQL_DATETIME or SQL_INTERVAL, respectively; sets SQL_DESC_CONCISE_TYPE to the concise identifier; and sets SQL_DESC_DATETIME_INTERVAL_CODE to the corresponding datetime or interval subcode.
    • Sets one or more of SQL_DESC_LENGTH, SQL_DESC_PRECISION, SQL_DESC_SCALE, and SQL_DESC_DATETIME_INTERVAL_PRECISION, as appropriate for TargetType.
    • Sets the SQL_DESC_OCTET_LENGTH field to the value of BufferLength.
    • Sets the SQL_DESC_DATA_PTR field to the value of TargetValue.
    • Sets the SQL_DESC_INDICATOR_PTR field to the value of StrLen_or_Ind. (See the following paragraph.)
    • Sets the SQL_DESC_OCTET_LENGTH_PTR field to the value of StrLen_or_Ind. (See the following paragraph.)

The variable that the StrLen_or_Ind argument refers to is used for both indicator and length information. If a fetch encounters a null value for the column, it stores SQL_NULL_DATA in this variable; otherwise, it stores the data length in this variable. Passing a null pointer as StrLen_or_Ind keeps the fetch operation from returning the data length but makes the fetch fail if it encounters a null value and has no way to return SQL_NULL_DATA.

If the call to SQLBindCol fails, the content of the descriptor fields it would have set in the ARD are undefined and the value of the SQL_DESC_COUNT field of the ARD is unchanged.

Implicit Resetting of COUNT Field

SQLBindCol sets SQL_DESC_COUNT to the value of the ColumnNumber argument only when this would increase the value of SQL_DESC_COUNT. If the value in the TargetValuePtr argument is a null pointer and the value in the ColumnNumber argument is equal to SQL_DESC_COUNT (that is, when unbinding the highest bound column), then SQL_DESC_COUNT is set to the number of the highest remaining bound column.

Cautions Regarding SQL_DEFAULT

To retrieve column data successfully, the application must determine correctly the length and starting point of the data in the application buffer. When the application specifies an explicit TargetType, application misconceptions are readily detected. However, when the application specifies a TargetType of SQL_DEFAULT, SQLBindCol can be applied to a column of a different data type from the one intended by the application, either from changes to the metadata or by applying the code to a different column. In this case, the application may fail to determine the start or length of the fetched column data. This can lead to unreported data errors or memory violations.

Code Example

In the following example, an application executes a SELECT statement on the Customers table to return a result set of the customer IDs, names, and phone numbers, sorted by name. It then calls SQLBindCol to bind the columns of data to local buffers. Finally, the application fetches each row of data with SQLFetch and prints each customer's name, ID, and phone number.

For more code examples, see SQLBulkOperations, SQLColumns, SQLFetchScroll, and SQLSetPos.

#define NAME_LEN 50
#define PHONE_LEN 10

SQLCHAR      szName[NAME_LEN], szPhone[PHONE_LEN];
SQLINTEGER   sCustID, cbName, cbCustID, cbPhone;
SQLHSTMT      hstmt;
SQLRETURN   retcode;

retcode = SQLExecDirect(hstmt,
            "SELECT CUSTID, NAME, PHONE FROM CUSTOMERS ORDER BY 2, 1, 3",
SQL_NTS);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
/* Bind columns 1, 2, and 3 */
SQLBindCol(hstmt, 1, SQL_C_ULONG, &sCustID, 0, &cbCustID);
SQLBindCol(hstmt, 2, SQL_C_CHAR, szName, NAME_LEN, &cbName);
SQLBindCol(hstmt, 3, SQL_C_CHAR, szPhone, PHONE_LEN, &cbPhone);
/* Fetch and print each row of data. On */
/* an error, display a message and exit. */
while (TRUE) {
retcode = SQLFetch(hstmt);
if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
show_error();
      }
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){
fprintf(out, "%-*s %-5d %*s", NAME_LEN-1, szName,
sCustID, PHONE_LEN-1, szPhone);
} else {
break;
      }
   }
}

Related Functions

For information about | See
Returning information about a column in a result set | SQLDescribeCol
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Fetching multiple rows of data | SQLFetch
Releasing column buffers on the statement | SQLFreeStmt
Fetching part or all of a column of data | SQLGetData
Returning the number of result set columns | SQLNumResultCols


SQLBindParameter

Conformance

Version Introduced: ODBC 2.0
Standards Compliance: ODBC

Summary

SQLBindParameter binds a buffer to a parameter marker in an SQL statement. SQLBindParameter supports binding to a Unicode C data type, even if the underlying driver does not support Unicode data.

Note This function replaces the ODBC 1.0 function SQLSetParam. For more information, see "Comments."

Syntax

SQLRETURN SQLBindParameter(
     SQLHSTMT     StatementHandle,
     SQLUSMALLINT     ParameterNumber,
     SQLSMALLINT     InputOutputType,
     SQLSMALLINT     ValueType,
     SQLSMALLINT     ParameterType,
     SQLUINTEGER     ColumnSize,
     SQLSMALLINT     DecimalDigits,
     SQLPOINTER     ParameterValuePtr,
     SQLINTEGER     BufferLength,
     SQLINTEGER *     StrLen_or_IndPtr);

Arguments

StatementHandle
[Input]
Statement handle.
ParameterNumber
[Input]
Parameter number, ordered sequentially in increasing parameter order, starting at 1.
InputOutputType
[Input]
The type of the parameter. For more information, see "InputOutputType Argument" in "Comments."
ValueType
[Input]
The C data type of the parameter. For more information, see "ValueType Argument" in "Comments."
ParameterType
[Input]
The SQL data type of the parameter. For more information, see "ParameterType Argument" in "Comments."
ColumnSize
[Input]
The size of the column or expression of the corresponding parameter marker. For more information, see "ColumnSize Argument" in "Comments."
DecimalDigits
[Input]
The decimal digits of the column or expression of the corresponding parameter marker. For further information concerning column size, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size," in Appendix D: Data Types.
ParameterValuePtr
[Deferred Input]
A pointer to a buffer for the parameter's data. For more information, see "ParameterValuePtr Argument" in "Comments."
BufferLength
[Input/Output]
Length of the ParameterValuePtr buffer in bytes. For more information, see "BufferLength Argument" in "Comments."
StrLen_or_IndPtr
[Deferred Input]
A pointer to a buffer for the parameter's length. For more information, see "StrLen_or_IndPtr Argument" in "Comments."

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLBindParameter returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLBindParameter and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
07006 | Restricted data type attribute violation | The data type identified by the ValueType argument cannot be converted to the data type identified by the ParameterType argument. Note that this error may be returned by SQLExecDirect, SQLExecute, or SQLPutData at execution time, instead of by SQLBindParameter.
07009 | Invalid descriptor index | (DM) The value specified for the argument ParameterNumber was less than 1.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY003 | Invalid application buffer type | The value specified by the argument ValueType was not a valid C data type or SQL_C_DEFAULT.
HY004 | Invalid SQL data type | The value specified for the argument ParameterType was neither a valid ODBC SQL data type identifier nor a driver-specific SQL data type identifier supported by the driver.
HY009 | Invalid use of null pointer | (DM) The argument ParameterValuePtr was a null pointer, the argument StrLen_or_IndPtr was a null pointer, and the argument InputOutputType was not SQL_PARAM_OUTPUT. (DM) SQL_PARAM_OUTPUT, where the argument ParameterValuePtr was a null pointer, the C type was char or binary, and the BufferLength (cbValueMax) was greater than 0.
HY010 | Function sequence error | (DM) An asynchronously executing function was called for the StatementHandle and was still executing when this function was called. (DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY021 | Inconsistent descriptor information | The descriptor information checked during a consistency check was not consistent. (See the "Consistency Checks" section in SQLSetDescField.) The value specified for the argument DecimalDigits was outside the range of values supported by the data source for a column of the SQL data type specified by the ParameterType argument.
HY090 | Invalid string or buffer length | (DM) The value in BufferLength was less than 0. (See the description of the SQL_DESC_DATA_PTR field in SQLSetDescField.)
HY104 | Invalid precision or scale value | The value specified for the argument ColumnSize or DecimalDigits was outside the range of values supported by the data source for a column of the SQL data type specified by the ParameterType argument.
HY105 | Invalid parameter type | (DM) The value specified for the argument InputOutputType was invalid. (See "Comments.")
HYC00 | Optional feature not implemented | The driver or data source does not support the conversion specified by the combination of the value specified for the argument ValueType and the driver-specific value specified for the argument ParameterType. The value specified for the argument ParameterType was a valid ODBC SQL data type identifier for the version of ODBC supported by the driver but was not supported by the driver or data source. The driver supports only ODBC 2.x and the argument ValueType was one of the following: SQL_C_GUID SQL_C_NUMERIC SQL_C_SBIGINT SQL_C_UBIGINT and all of the interval C data types listed in "C Data Types" in Appendix D: Data Types.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

An application calls SQLBindParameter to bind each parameter marker in an SQL statement. Bindings remain in effect until the application calls SQLBindParameter again, calls SQLFreeStmt with the SQL_RESET_PARAMS option, or calls SQLSetDescField to set the SQL_DESC_COUNT header field of the APD to 0.

For more information about parameters, see "Statement Parameters" in Chapter 9: Executing Statements. For more information concerning parameter data types and parameter markers, see "Parameter Data Types" and "Parameter Markers" in Appendix C: SQL Grammar.

ParameterNumber Argument

If ParameterNumber in the call to SQLBindParameter is greater than the value of SQL_DESC_COUNT, SQLSetDescField is called to increase the value of SQL_DESC_COUNT to ParameterNumber.

InputOutputType Argument

The InputOutputType argument specifies the type of the parameter. This argument sets the SQL_DESC_PARAMETER_TYPE field of the IPD. All parameters in SQL statements that do not call procedures, such as INSERT statements, are input parameters. Parameters in procedure calls can be input, input/output, or output parameters. (An application calls SQLProcedureColumns to determine the type of a parameter in a procedure call; parameters whose type cannot be determined are assumed to be input parameters.)

The InputOutputType argument is one of the following values:

  • SQL_PARAM_INPUT. The parameter marks a parameter in an SQL statement that does not call a procedure, such as an INSERT statement, or it marks an input parameter in a procedure. For example, the parameters in INSERT INTO Employee VALUES (?, ?, ?) are input parameters, while the parameters in {call AddEmp(?, ?, ?)} can be, but are not necessarily, input parameters.

    When the statement is executed, the driver sends data for the parameter to the data source; the *ParameterValuePtr buffer must contain a valid input value, or the *StrLen_or_IndPtr buffer must contain SQL_NULL_DATA, SQL_DATA_AT_EXEC, or the result of the SQL_LEN_DATA_AT_EXEC macro.

    If an application cannot determine the type of a parameter in a procedure call, it sets InputOutputType to SQL_PARAM_INPUT; if the data source returns a value for the parameter, the driver discards it.

  • SQL_PARAM_INPUT_OUTPUT. The parameter marks an input/output parameter in a procedure. For example, the parameter in {call GetEmpDept(?)} is an input/output parameter that accepts an employee's name and returns the name of the employee's department.

    When the statement is executed, the driver sends data for the parameter to the data source; the *ParameterValuePtr buffer must contain a valid input value, or the *StrLen_or_IndPtr buffer must contain SQL_NULL_DATA, SQL_DATA_AT_EXEC, or the result of the SQL_LEN_DATA_AT_EXEC macro. After the statement is executed, the driver returns data for the parameter to the application; if the data source does not return a value for an input/output parameter, the driver sets the *StrLen_or_IndPtr buffer to SQL_NULL_DATA.

    Note When an ODBC 1.0 application calls SQLSetParam in an ODBC 2.0 driver, the Driver Manager converts this to a call to SQLBindParameter in which the InputOutputType argument is set to SQL_PARAM_INPUT_OUTPUT.

  • SQL_PARAM_OUTPUT. The parameter marks the return value of a procedure or an output parameter in a procedure; in either case, these are known as output parameters. For example, the parameter in {?=call GetNextEmpID} is an output parameter that returns the next employee ID.

    After the statement is executed, the driver returns data for the parameter to the application, unless the ParameterValuePtr and StrLen_or_IndPtr arguments are both null pointers, in which case the driver discards the output value. If the data source does not return a value for an output parameter, the driver sets the *StrLen_or_IndPtr buffer to SQL_NULL_DATA.

ValueType Argument

The ValueType argument specifies the C data type of the parameter. This argument sets the SQL_DESC_TYPE, SQL_DESC_CONCISE_TYPE, and SQL_DESC_DATETIME_INTERVAL_CODE fields of the APD. This must be one of the values in the "C Data Types" section of Appendix D: Data Types.

If the ValueType argument is one of the interval data types, the SQL_DESC_TYPE field of the ParameterNumber record of the APD is set to SQL_INTERVAL, the SQL_DESC_CONCISE_TYPE field of the APD is set to the concise interval data type, and the SQL_DESC_DATETIME_INTERVAL_CODE field of the ParameterNumber record is set to a subcode for the specific interval data type. (See Appendix D: Data Types.) The default interval leading precision (2) and default interval seconds precision (6), as set in the SQL_DESC_DATETIME_INTERVAL_PRECISION and SQL_DESC_PRECISION fields of the APD, respectively, are used for the data. If either default precision is not appropriate, the application should explicitly set the descriptor field by a call to SQLSetDescField or SQLSetDescRec.

If the ValueType argument is one of the datetime data types, the SQL_DESC_TYPE field of the ParameterNumber record of the APD is set to SQL_DATETIME, the SQL_DESC_CONCISE_TYPE field of the ParameterNumber record of the APD is set to the concise datetime C data type, and the SQL_DESC_DATETIME_INTERVAL_CODE field of the ParameterNumber record is set to a subcode for the specific datetime data type. (See Appendix D: Data Types.)

If the ValueType argument is an SQL_C_NUMERIC data type, the default precision (which is driver-defined) and the default scale (0), as set in the SQL_DESC_PRECISION and SQL_DESC_SCALE fields of the APD, are used for the data. If the default precision or scale is not appropriate, the application should explicitly set the descriptor field by a call to SQLSetDescField or SQLSetDescRec.

SQL_C_DEFAULT specifies that the parameter value be transferred from the default C data type for the SQL data type specified with ParameterType.

For more information, see "Default C Data Types," "Converting Data from C to SQL Data Types," and "Converting Data from SQL to C Data Types" in Appendix D: Data Types.

ParameterType Argument

This must be one of the values listed in the "SQL Data Types" section of Appendix D: Data Types, or it must be a driver-specific value. This argument sets the SQL_DESC_TYPE, SQL_DESC_CONCISE_TYPE, and SQL_DESC_DATETIME_INTERVAL_CODE fields of the IPD.

If the ParameterType argument is one of the datetime identifiers, the SQL_DESC_TYPE field of the IPD is set to SQL_DATETIME, the SQL_DESC_CONCISE_TYPE field of the IPD is set to the concise datetime SQL data type, and the SQL_DESC_DATETIME_INTERVAL_CODE field is set to the appropriate datetime subcode value.

If ParameterType is one of the interval identifiers, the SQL_DESC_TYPE field of the IPD is set to SQL_INTERVAL, the SQL_DESC_CONCISE_TYPE field of the IPD is set to the concise SQL interval data type, and the SQL_DESC_DATETIME_INTERVAL_CODE field of the IPD is set to the appropriate interval subcode. The SQL_DESC_DATETIME_INTERVAL_PRECISION field of the IPD is set to the interval leading precision, and the SQL_DESC_PRECISION field is set to the interval seconds precision, if applicable. If the default value of SQL_DESC_DATETIME_INTERVAL_PRECISION or SQL_DESC_PRECISION is not appropriate, the application should explicitly set it by calling SQLSetDescField. For more information about any of these fields, see SQLSetDescField.

If the ValueType argument is a SQL_NUMERIC data type, the default precision (which is driver-defined) and the default scale (0), as set in the SQL_DESC_PRECISION and SQL_DESC_SCALE fields of the IPD, are used for the data. If the default precision or scale is not appropriate, the application should explicitly set the descriptor field by a call to SQLSetDescField or SQLSetDescRec.

For information about how data is converted, see "Converting Data from C to SQL Data Types" and "Converting Data from SQL to C Data Types" in Appendix D: Data Types.

ColumnSize Argument

The ColumnSize argument specifies the size of the column or expression corresponding to the parameter marker, the length of that data, or both. This argument sets different fields of the IPD, depending on the SQL data type (the ParameterType argument). The following rules apply to this mapping:

  • If ParameterType is SQL_CHAR, SQL_VARCHAR, SQL_LONGVARCHAR, SQL_BINARY, SQL_VARBINARY, SQL_LONGVARBINARY, or one of the concise SQL datetime or interval data types, the SQL_DESC_LENGTH field of the IPD is set to the value of ColumnSize. (For more information, see the "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" section in Appendix D: Data Types.)
  • If ParameterType is SQL_DECIMAL, SQL_NUMERIC, SQL_FLOAT, SQL_REAL, or SQL_DOUBLE, the SQL_DESC_PRECISION field of the IPD is set to the value of ColumnSize.
  • For other data types, the ColumnSize argument is ignored.

For more information, see "Passing Parameter Values" and SQL_DATA_AT_EXEC in "StrLen_or_IndPtr Argument."

DecimalDigits Argument

If ParameterType is SQL_TYPE_TIME, SQL_TYPE_TIMESTAMP, SQL_INTERVAL_SECOND, SQL_INTERVAL_DAY_TO_SECOND, SQL_INTERVAL_HOUR_TO_SECOND, or SQL_INTERVAL_MINUTE_TO_SECOND, the SQL_DESC_PRECISION field of the IPD is set to DecimalDigits. If ParameterType is SQL_NUMERIC or SQL_DECIMAL, the SQL_DESC_SCALE field of the IPD is set to DecimalDigits. For all other data types, the DecimalDigits argument is ignored.

ParameterValuePtr Argument

The ParameterValuePtr argument points to a buffer that, when SQLExecute or SQLExecDirect is called, contains the actual data for the parameter. The data must be in the form specified by the ValueType argument. This argument sets the SQL_DESC_DATA_PTR field of the APD. An application can set the ParameterValuePtr argument to a null pointer, as long as *StrLen_or_IndPtr is SQL_NULL_DATA or SQL_DATA_AT_EXEC. (This applies only to input or input/output parameters.)

If *StrLen_or_IndPtr is the result of the SQL_LEN_DATA_AT_EXEC(length) macro or SQL_DATA_AT_EXEC, then ParameterValuePtr is an application-defined, 32-bit value that is associated with the parameter. It is returned to the application through SQLParamData. For example, ParameterValuePtr might be a token such as a parameter number, a pointer to data, or a pointer to a structure that the application used to bind input parameters. Note, however, that if the parameter is an input/output parameter, ParameterValuePtr must be a pointer to a buffer where the output value will be stored. If the value in the SQL_ATTR_PARAMSET_SIZE statement attribute is greater than 1, the application can use the value pointed to by the SQL_ATTR_PARAMS_PROCESSED_PTR statement attribute in conjunction with the ParameterValuePtr argument. For example, ParameterValuePtr might point to an array of values and the application might use the value pointed to by SQL_ATTR_PARAMS_PROCESSED_PTR to retrieve the correct value from the array. For more information, see "Passing Parameter Values" later in this section.

If the InputOutputType argument is SQL_PARAM_INPUT_OUTPUT or SQL_PARAM_OUTPUT, ParameterValuePtr points to a buffer in which the driver returns the output value. If the procedure returns one or more result sets, the *ParameterValuePtr buffer is not guaranteed to be set until all result sets/row counts have been processed. If the buffer is not set until processing is complete, the output parameters and return values are unavailable until SQLMoreResults returns SQL_NO_DATA. Calling SQLCloseCursor or SQLFreeStmt with an Option of SQL_CLOSE will cause these values to be discarded.

If the value in the SQL_ATTR_PARAMSET_SIZE statement attribute is greater than 1, ParameterValuePtr points to an array. A single SQL statement processes the entire array of input values for an input or input/output parameter and returns an array of output values for an input/output or output parameter.

BufferLength Argument

For character and binary C data, the BufferLength argument specifies the length of the *ParameterValuePtr buffer (if it is a single element) or the length of an element in the *ParameterValuePtr array (if the value in the SQL_ATTR_PARAMSET_SIZE statement attribute is greater than 1). This argument sets the SQL_DESC_OCTET_LENGTH record field of the APD. If the application specifies multiple values, BufferLength is used to determine the location of values in the *ParameterValuePtr array, both on input and on output. For input/output and output parameters, it is used to determine whether to truncate character and binary C data on output:

  • For character C data, if the number of bytes available to return is greater than or equal to BufferLength, the data in *ParameterValuePtr is truncated to BufferLength less the length of a null-termination character and is null-terminated by the driver.
  • For binary C data, if the number of bytes available to return is greater than BufferLength, the data in *ParameterValuePtr is truncated to BufferLength bytes.

For all other types of C data, the BufferLength argument is ignored. The length of the *ParameterValuePtr buffer (if it is a single element) or the length of an element in the *ParameterValuePtr array (if the application calls SQLSetStmtAttr with an Attribute argument of SQL_ATTR_PARAMSET_SIZE to specify multiple values for each parameter) is assumed to be the length of the C data type.

Note When an ODBC 1.0 application calls SQLSetParam in an ODBC 3.x driver, the Driver Manager converts this to a call to SQLBindParameter in which the BufferLength argument is always SQL_SETPARAM_VALUE_MAX. Because the Driver Manager returns an error if an ODBC 3.x application sets BufferLength to SQL_SETPARAM_VALUE_MAX, an ODBC 3.x driver can use this to determine when it is called by an ODBC 1.0 application.

In SQLSetParam, the way in which an application specifies the length of the *ParameterValuePtr buffer so that the driver can return character or binary data, and the way in which an application sends an array of character or binary parameter values to the driver, are driver-defined.

StrLen_or_IndPtr Argument

The StrLen_or_IndPtr argument points to a buffer that, when SQLExecute or SQLExecDirect is called, contains one of the following. (This argument sets the SQL_DESC_OCTET_LENGTH_PTR and SQL_DESC_INDICATOR_PTR record fields of the application parameter pointers.)

  • The length of the parameter value stored in *ParameterValuePtr. This is ignored except for character or binary C data.
  • SQL_NTS. The parameter value is a null-terminated string.
  • SQL_NULL_DATA. The parameter value is NULL.
  • SQL_DEFAULT_PARAM. A procedure is to use the default value of a parameter, rather than a value retrieved from the application. This value is valid only in a procedure called in ODBC canonical syntax, and then only if the InputOutputType argument is SQL_PARAM_INPUT or SQL_PARAM_INPUT_OUTPUT. When *StrLen_or_IndPtr is SQL_DEFAULT_PARAM, the ValueType, ParameterType, ColumnSize, DecimalDigits, BufferLength, and ParameterValuePtr arguments are ignored for input parameters and are used only to define the output parameter value for input/output parameters.
  • The result of the SQL_LEN_DATA_AT_EXEC(length) macro. The data for the parameter will be sent with SQLPutData. If the ParameterType argument is SQL_LONGVARBINARY, SQL_LONGVARCHAR, or a long, data source–specific data type, and the driver returns "Y" for the SQL_NEED_LONG_DATA_LEN information type in SQLGetInfo, length is the number of bytes of data to be sent for the parameter; otherwise, length must be a nonnegative value and is ignored. For more information, see "Passing Parameter Values," later in this section.

    For example, to specify that 10,000 bytes of data will be sent with SQLPutData for an SQL_LONGVARCHAR parameter, an application sets *StrLen_or_IndPtr to SQL_LEN_DATA_AT_EXEC(10000).

  • SQL_DATA_AT_EXEC. The data for the parameter will be sent with SQLPutData. This value is used by ODBC 1.0 applications when calling ODBC 3.x drivers. For more information, see "Passing Parameter Values," later in this section.

If StrLen_or_IndPtr is a null pointer, the driver assumes that all input parameter values are non-NULL and that character and binary data are null-terminated. If InputOutputType is SQL_PARAM_OUTPUT and ParameterValuePtr and StrLen_or_IndPtr are both null pointers, the driver discards the output value.

Note Application developers are strongly discouraged from specifying a null pointer for StrLen_or_IndPtr when the data type of the parameter is SQL_C_BINARY. To ensure that a driver does not unexpectedly truncate SQL_C_BINARY data, StrLen_or_IndPtr should contain a pointer to a valid length value.

If the InputOutputType argument is SQL_PARAM_INPUT_OUTPUT or SQL_PARAM_OUTPUT, StrLen_or_IndPtr points to a buffer in which the driver returns SQL_NULL_DATA, the number of bytes available to return in *ParameterValuePtr (excluding the null-termination byte of character data), or SQL_NO_TOTAL (if the number of bytes available to return cannot be determined). If the procedure returns one or more result sets, the *StrLen_or_IndPtr buffer is not guaranteed to be set until all results have been fetched.

If the value in the SQL_ATTR_PARAMSET_SIZE statement attribute is greater than 1, StrLen_or_IndPtr points to an array of SQLINTEGER values. These can be any of the values listed earlier in this section and are processed with a single SQL statement.

Passing Parameter Values

An application can pass the value for a parameter either in the *ParameterValuePtr buffer or with one or more calls to SQLPutData. Parameters whose data is passed with SQLPutData are known as data-at-execution parameters. These are commonly used to send data for SQL_LONGVARBINARY and SQL_LONGVARCHAR parameters, and can be mixed with other parameters.

To pass parameter values, an application performs the following sequence of steps:

  1. Calls SQLBindParameter for each parameter to bind buffers for the parameter's value (ParameterValuePtr argument) and length/indicator (StrLen_or_IndPtr argument). For data-at-execution parameters, ParameterValuePtr is an application-defined, 32-bit value such as a parameter number or a pointer to data. The value will be returned later and can be used to identify the parameter.
  2. Places values for input and input/output parameters in the *ParameterValuePtr and *StrLen_or_IndPtr buffers:
    • For normal parameters, the application places the parameter value in the *ParameterValuePtr buffer and the length of that value in the *StrLen_or_IndPtr buffer. For more information, see "Setting Parameter Values" in Chapter 9: Executing Statements.
    • For data-at-execution parameters, the application places the result of the SQL_LEN_DATA_AT_EXEC(length) macro (when calling an ODBC 2.0 driver) in the *StrLen_or_IndPtr buffer.
  3. Calls SQLExecute or SQLExecDirect to execute the SQL statement.
    • If there are no data-at-execution parameters, the process is complete.
    • If there are any data-at-execution parameters, the function returns SQL_NEED_DATA.
  4. Calls SQLParamData to retrieve the application-defined value specified in the ParameterValuePtr argument of SQLBindParameter for the first data-at-execution parameter to be processed. SQLParamData returns SQL_NEED_DATA.

    Note Although data-at-execution parameters are similar to data-at-execution columns, the value returned by SQLParamData is different for each.

    Data-at-execution parameters are parameters in an SQL statement for which data will be sent with SQLPutData when the statement is executed with SQLExecDirect or SQLExecute. They are bound with SQLBindParameter. The value returned by SQLParamData is a 32-bit value passed to SQLBindParameter in the ParameterValuePtr argument.

    Data-at-execution columns are columns in a rowset for which data will be sent with SQLPutData when a row is updated or added with SQLBulkOperations or updated with SQLSetPos. They are bound with SQLBindCol. The value returned by SQLParamData is the address of the row in the *TargetValuePtr buffer (set by a call to SQLBindCol) that is being processed.

  5. Calls SQLPutData one or more times to send data for the parameter. More than one call is needed if the data value is larger than the *ParameterValuePtr buffer specified in SQLPutData; multiple calls to SQLPutData for the same parameter are allowed only when sending character C data to a column with a character, binary, or data source–specific data type or when sending binary C data to a column with a character, binary, or data source–specific data type.
  6. Calls SQLParamData again to signal that all data has been sent for the parameter.
    • If there are more data-at-execution parameters, SQLParamData returns SQL_NEED_DATA and the application-defined value for the next data-at-execution parameter to be processed. The application repeats steps 4 and 5.
    • If there are no more data-at-execution parameters, the process is complete. If the statement was successfully executed, SQLParamData returns SQL_SUCCESS or SQL_SUCCESS_WITH_INFO; if the execution failed, it returns SQL_ERROR. At this point, SQLParamData can return any SQLSTATE that can be returned by the function used to execute the statement (SQLExecDirect or SQLExecute).

      Output values for any input/output or output parameters are available in the *ParameterValuePtr and *StrLen_or_IndPtr buffers after the application retrieves all result sets generated by the statement.

Calling SQLExecute or SQLExecDirect puts the statement in an SQL_NEED_DATA state. At this point, the application can call only SQLCancel, SQLGetDiagField, SQLGetDiagRec, SQLGetFunctions, SQLParamData, or SQLPutData with the statement or the connection handle associated with the statement. If it calls any other function with the statement or the connection associated with the statement, the function returns SQLSTATE HY010 (Function sequence error). The statement leaves the SQL_NEED_DATA state when SQLParamData or SQLPutData returns an error, SQLParamData returns SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, or the statement is canceled.

If the application calls SQLCancel while the driver still needs data for data-at-execution parameters, the driver cancels statement execution; the application can then call SQLExecute or SQLExecDirect again.

Using Arrays of Parameters

When an application prepares a statement with parameter markers and passes in an array of parameters, there are two different ways this can be executed. One way is for the driver to rely on the array-processing capabilities of the back end, in which case the entire statement with the array of parameters is treated as one atomic unit. Oracle is an example of a data source that supports array processing capabilities. Another way to implement this feature is for the driver to generate a batch of SQL statements, one SQL statement for each set of parameters in the parameter array, and execute the batch. Arrays of parameters cannot be used with an UPDATE WHERE CURRENT OF statement.

When an array of parameters is processed, individual result sets/row counts (one for each parameter set) can be available or result sets/rows counts can be rolled up into one. The SQL_PARAM_ARRAY_ROW_COUNTS option in SQLGetInfo indicates whether row counts are available for each set of parameters (SQL_PARC_BATCH) oronly one row count is available (SQL_PARC_NO_BATCH).

The SQL_PARAM_ARRAY_SELECTS option in SQLGetInfo indicates whether a result set is available for each set of parameters (SQL_PAS_BATCH) or only one result set is available (SQL_PAS_NO_BATCH). If the driver does not allow a result set–generating statement to be executed with an array of parameters, SQL_PARAM_ARRAY_SELECTS returns SQL_PAS_NO_SELECT.

For more information, see SQLGetInfo.

To support arrays of parameters, the SQL_ATTR_PARAMSET_SIZE statement attribute is set to specify the number of values for each parameter. If the field is greater than 1, the SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR fields of the APD must point to arrays. The cardinality of each array is equal to the value of SQL_ATTR_PARAMSET_SIZE.

The SQL_DESC_ROWS_PROCESSED_PTR field of the APD points to a buffer that contains the number of sets of parameters that have been processed, including error sets. As each set of parameters is processed, the driver stores a new value in the buffer. No number will be returned if this is a null pointer. When arrays of parameters are used, the value pointed to by the SQL_DESC_ROWS_PROCESSED_PTR field of the APD is populated even if SQL_ERROR is returned by the setting function. If SQL_NEED_DATA is returned, the value pointed to by the SQL_DESC_ROWS_PROCESSED_PTR field of the APD is set to the set of parameters that is being processed.

What occurs when an array of parameters is bound and an UPDATE WHERE CURRENT OF statement is executed is driver-defined.

Column-Wise Parameter Binding

In column-wise binding, the application binds separate parameter and length/indicator arrays to each parameter.

To use column-wise binding, the application first sets the SQL_ATTR_PARAM_BIND_TYPE statement attribute to SQL_PARAM_BIND_BY_COLUMN. (This is the default.) For each column to be bound, the application performs the following steps:

  1. Allocates a parameter buffer array.
  2. Allocates an array of length/indicator buffers.

    Note If the application writes directly to descriptors when column-wise binding is used, separate arrays can be used for length and indicator data.

  3. Calls SQLBindParameter with the following arguments:
    • ValueType is the C type of a single element in the parameter buffer array.
    • ParameterType is the SQL type of the parameter.
    • ParameterValuePtr is the address of the parameter buffer array.
    • BufferLength is the size of a single element in the parameter buffer array. The BufferLength argument is ignored when the data is fixed-length data.
    • StrLen_or_IndPtr is the address of the length/indicator array.

For more information about how this information is used, see "ParameterValuePtr Argument" in "Comments," later in this section. For more information about column-wise binding of parameters, see the "Binding Arrays of Parameters" section in Chapter 9: Executing Statements.

Row-Wise Parameter Binding

In row-wise binding, the application defines a structure containing parameter and length/indicator buffers for each parameter to be bound.

To use row-wise binding, the application performs the following steps:

  1. Defines a structure to hold a single set of parameters (including both parameter and length/indicator buffers) and allocates an array of these structures.

    Note If the application writes directly to descriptors when row-wise binding is used, separate fields can be used for length and indicator data.

  2. Sets the SQL_ATTR_PARAM_BIND_TYPE statement attribute to the size of the structure containing a single set of parameters or to the size of an instance of a buffer into which the parameters will be bound. The length must include space for all of the bound parameters, and any padding of the structure or buffer, to make sure that when the address of a bound parameter is incremented with the specified length, the result will point to the beginning of the same parameter in the next row. When using the sizeof operator in ANSI C, this behavior is guaranteed.
  3. Calls SQLBindParameter with the following arguments for each parameter to be bound:
    • ValueType is the type of the parameter buffer member to be bound to the column.
    • ParameterType is the SQL type of the parameter.
    • ParameterValuePtr is the address of the parameter buffer member in the first array element.
    • BufferLength is the size of the parameter buffer member.
    • StrLen_or_IndPtr is the address of the length/indicator member to be bound.

For more information about how this information is used, see "ParameterValuePtr Argument," later in this section. For more information about row-wise binding of parameters, see the "Binding Arrays of Parameters" section in Chapter 9: Executing Statements.

Error Information

If a driver does not implement parameter arrays as batches (the SQL_PARAM_ARRAY_ROW_COUNTS option is equal to SQL_PARC_NO_BATCH), error situations are handled as if one statement were executed. If the driver does implement parameter arrays as batches, an application can use the SQL_DESC_ARRAY_STATUS_PTR header field of the IPD to determine which parameter of an SQL statement or which parameter in an array of parameters caused SQLExecDirect or SQLExecute to return an error. This field contains status information for each row of parameter values. If the field indicates that an error has occurred, fields in the diagnostic data structure will indicate the row and parameter number of the parameter that failed. The number of elements in the array will be defined by the SQL_DESC_ARRAY_SIZE header field in the APD, which can be set by the SQL_ATTR_PARAMSET_SIZE statement attribute.

Note The SQL_DESC_ARRAY_STATUS_PTR header field in the APD is used to ignore parameters. For more information about ignoring parameters, see the next section, "Ignoring a Set of Parameters."

When SQLExecute or SQLExecDirect returns SQL_ERROR, the elements in the array pointed to by the SQL_DESC_ARRAY_STATUS_PTR field in the IPD will contain SQL_PARAM_ERROR, SQL_PARAM_SUCCESS, SQL_PARAM_SUCCESS_WITH_INFO, SQL_PARAM_UNUSED, or SQL_PARAM_DIAG_UNAVAILABLE.

For each element in this array, the diagnostic data structure contains one or more status records. The SQL_DIAG_ROW_NUMBER field of the structure indicates the row number of the parameter values that caused the error. If it is possible to determine the particular parameter in a row of parameters that caused the error, the parameter number will be entered in the SQL_DIAG_COLUMN_NUMBER field.

SQL_PARAM_UNUSED is entered when a parameter has not been used because an error occurred in an earlier parameter that forced SQLExecute or SQLExecDirect to abort. For example, if there are 50 parameters and an error occurred while executing the fortieth set of parameters that caused SQLExecute or SQLExecDirect to abort, then SQL_PARAM_UNUSED is entered in the status array for parameters 41 through 50.

SQL_PARAM_DIAG_UNAVAILABLE is entered when the driver treats arrays of parameters as a monolithic unit, so it does not generate this individual parameter level of error information.

Some errors in the processing of a single set of parameters cause processing of the subsequent sets of parameters in the array to stop. Other errors do not affect the processing of subsequent parameters. Which errors will stop processing is driver-defined. If processing is not stopped, all parameters in the array are processed, SQL_SUCCESS_WITH_INFO is returned as a result of the error, and the buffer defined by SQL_ATTR_PARAMS_PROCESSED_PTR is set to the total number of sets of parameters processed (as defined by the SQL_ATTR_PARAMSET_SIZE statement attribute), which includes error sets.

Caution ODBC behavior when an error occurs in the processing of an array of parameters is different in ODBC 3.x than it was in ODBC 2.x. In ODBC 2.x, the function returned SQL_ERROR and processing ceased. The buffer pointed to by the pirow argument of SQLParamOptions contained the number of the error row. In ODBC 3.x, the function returns SQL_SUCCESS_WITH_INFO and processing may either cease or continue. If it continues, the buffer specified by SQL_ATTR_PARAMS_PROCESSED_PTR will be set to the value of all parameters processed, including those that resulted in an error. This change in behavior may cause problems for existing applications.

When SQLExecute or SQLExecDirect returns before completing the processing of all parameter sets in a parameter array, such as when SQL_ERROR or SQL_NEED_DATA is returned, the status array contains statuses for those parameters that have already been processed. The location pointed to by the SQL_DESC_ROWS_PROCESSED_PTR field in the IPD contains the row number in the parameter array that caused the SQL_ERROR or SQL_NEED_DATA error code. When an array of parameters is sent to a SELECT statement, the availability of status array values is driver-defined; they may be available after the statement has been executed or as result sets are fetched.

Ignoring a Set of Parameters

The SQL_DESC_ARRAY_STATUS_PTR field of the APD (as set by the SQL_ATTR_PARAM_STATUS_PTR statement attribute) can be used to indicate that a set of bound parameters in an SQL statement should be ignored. To direct the driver to ignore one or more sets of parameters during execution, an application should perform the following steps:

  1. Call SQLSetDescField to set the SQL_DESC_ARRAY_STATUS_PTR header field of the APD to point to an array of SQLUSMALLINT values to contain status information. This field can also be set by calling SQLSetStmtAttr with an Attribute of SQL_ATTR_PARAM_OPERATION_PTR, which allows an application to set the field without obtaining a descriptor handle.
  2. Set each element of the array defined by the SQL_DESC_ARRAY_STATUS_PTR field of the APD to one of two values:
    • SQL_PARAM_IGNORE, to indicate that the row is excluded from statement execution.
    • SQL_PARAM_PROCEED, to indicate that the row is included in statement execution.
  3. Call SQLExecDirect or SQLExecute to execute the prepared statement.

The following rules apply to the array defined by the SQL_DESC_ARRAY_STATUS_PTR field of the APD:

  • The pointer is set to null by default.
  • If the pointer is null, then all sets of parameters are used, as if all elements were set to SQL_ROW_PROCEED.
  • Setting an element to SQL_PARAM_PROCEED does not guarantee that the operation will use that particular set of parameters.
  • SQL_PARAM_PROCEED is defined as 0 in the header file.

An application can set the SQL_DESC_ARRAY_STATUS_PTR field in the APD to point to the same array as that pointed to by the SQL_DESC_ARRAY_STATUS_PTR field in the IRD. This is useful when binding parameters to row data. Parameters then can be ignored according to the status of the row data. In addition to SQL_PARAM_IGNORE, the following codes cause a parameter in an SQL statement to be ignored: SQL_ROW_DELETED, SQL_ROW_UPDATED, and SQL_ROW_ERROR. In addition to SQL_PARAM_PROCEED, the following codes cause an SQL statement to proceed: SQL_ROW_SUCCESS, SQL_ROW_SUCCESS_WITH_INFO, and SQL_ROW_ADDED.

Rebinding Parameters

An application can perform either of two operations to change a binding:

  • Call SQLBindParameter to specify a new binding for a column that is already bound. The driver overwrites the old binding with the new one.
  • Specify an offset to be added to the buffer address that was specified by the binding call to SQLBindParameter. For more information, see the next section, "Rebinding with Offsets."

Rebinding with Offsets

Rebinding of parameters is especially useful when an application has a buffer area setup that is capable of containing many parameters but a call to SQLExecDirect or SQLExecute uses only a few of the parameters. The remaining space in the buffer area can be used for the next set of parameters by modifying the existing binding by an offset.

The SQL_DESC_BIND_OFFSET_PTR header field in the APD points to the binding offset. If the field is non-null, the driver dereferences the pointer and, if none of the values in the SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR fields is a null pointer, adds the dereferenced value to those fields in the descriptor records at execution time. The new pointer values are used when the SQL statements are executed. The offset remains valid after rebinding. Because SQL_DESC_BIND_OFFSET_PTR is a pointer to the offset rather than the offset itself, an application can change the offset directly, without having to call SQLSetDescField or SQLSetDescRec to change the descriptor field. The pointer is set to null by default. The SQL_DESC_BIND_OFFSET_PTR field of the ARD can be set by a call to SQLSetDescField or by a call to SQLSetStmtAttr with an fAttribute of SQL_ATTR_PARAM_BIND_OFFSET_PTR.

The binding offset is always added directly to the values in the SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR fields. If the offset is changed to a different value, the new value is still added directly to the value in each descriptor field. The new offset is not added to the sum of the field value and any earlier offsets.

Descriptors

How a parameter is bound is determined by fields of the APDs and IPDs. The arguments in SQLBindParameter are used to set those descriptor fields. The fields also can be set by the SQLSetDescField functions, although SQLBindParameter is more efficient to use because the application does not have to obtain a descriptor handle to call SQLBindParameter.

Caution Calling SQLBindParameter for one statement can affect other statements. This occurs when the ARD associated with the statement is explicitly allocated and is also associated with other statements. Because SQLBindParameter modifies the fields of the APD, the modifications apply to all statements with which this descriptor is associated. If this is not the required behavior, the application should dissociate this descriptor from the other statements before calling SQLBindParameter.

Conceptually, SQLBindParameter performs the following steps in sequence:

  1. Calls SQLGetStmtAttr to obtain the APD handle.
  2. Calls SQLGetDescField to get the APD's SQL_DESC_COUNT field, and if the value of the ColumnNumber argument exceeds the value of SQL_DESC_COUNT, calls SQLSetDescField to increase the value of SQL_DESC_COUNT to ColumnNumber.
  3. Calls SQLSetDescField multiple times to assign values to the following fields of the APD:
    • Sets SQL_DESC_TYPE and SQL_DESC_CONCISE_TYPE to the value of ValueType, except that if ValueType is one of the concise identifiers of a datetime or interval subtype, it sets SQL_DESC_TYPE to SQL_DATETIME or SQL_INTERVAL, respectively, sets SQL_DESC_CONCISE_TYPE to the concise identifier, and sets SQL_DESC_DATETIME_INTERVAL_CODE to the corresponding datetime or interval subcode.
    • Sets the SQL_DESC_OCTET_LENGTH field to the value of BufferLength.
    • Sets the SQL_DESC_DATA_PTR field to the value of ParameterValue.
    • Sets the SQL_DESC_OCTET_LENGTH_PTR field to the value of StrLen_or_Ind.
    • Sets the SQL_DESC_INDICATOR_PTR field also to the value of StrLen_or_Ind.

    The StrLen_or_Ind parameter specifies both the indicator information and the length for the parameter value.

  4. Calls SQLGetStmtAttr to obtain the IPD handle.
  5. Calls SQLGetDescField to get the IPD's SQL_DESC_COUNT field, and if the value of the ColumnNumber argument exceeds the value of SQL_DESC_COUNT, calls SQLSetDescField to increase the value of SQL_DESC_COUNT to ColumnNumber.
  6. Calls SQLSetDescField multiple times to assign values to the following fields of the IPD:
    • Sets SQL_DESC_TYPE and SQL_DESC_CONCISE_TYPE to the value of ParameterType, except that if ParameterType is one of the concise identifiers of a datetime or interval subtype, it sets SQL_DESC_TYPE to SQL_DATETIME or SQL_INTERVAL, respectively, sets SQL_DESC_CONCISE_TYPE to the concise identifier, and sets SQL_DESC_DATETIME_INTERVAL_CODE to the corresponding datetime or interval subcode.
    • Sets one or more of SQL_DESC_LENGTH, SQL_DESC_PRECISION, and SQL_DESC_DATETIME_INTERVAL_PRECISION, as appropriate for ParameterType.
    • Sets SQL_DESC_SCALE to the value of DecimalDigits.

If the call to SQLBindParameter fails, the content of the descriptor fields that it would have set in the APD are undefined, and the SQL_DESC_COUNT field of the APD is unchanged. In addition, the SQL_DESC_LENGTH, SQL_DESC_PRECISION, SQL_DESC_SCALE, and SQL_DESC_TYPE fields of the appropriate record in the IPD are undefined and the SQL_DESC_COUNT field of the IPD is unchanged.

Conversion of Calls to and from SQLSetParam

When an ODBC 1.0 application calls SQLSetParam in an ODBC 3.x driver, the ODBC 3.x Driver Manager maps the call as shown in the following table.

Call by ODBC 1.0 application | Call to ODBC 3.x driver
SQLSetParam( StatementHandle, ParameterNumber, ValueType, ParameterType, LengthPrecision, ParameterScale, ParameterValuePtr, StrLen_or_IndPtr); | SQLBindParameter( StatementHandle, ParameterNumber, SQL_PARAM_INPUT_OUTPUT, ValueType, ParameterType, ColumnSize, DecimalDigits, ParameterValuePtr, SQL_SETPARAM_VALUE_MAX, StrLen_or_IndPtr);

Code Example

In the following example, an application prepares an SQL statement to insert data into the ORDERS table. The SQL statement contains parameters for the ORDERID, CUSTID, OPENDATE, SALESPERSON, and STATUS columns. For each parameter in the statement, the application calls SQLBindParameter to specify the ODBC C data type and the SQL data type of the parameter, and to bind a buffer to each parameter. For each row of data, the application assigns data values to each parameter and calls SQLExecute to execute the statement.

For more code examples, see SQLBulkOperations, SQLProcedures, SQLPutData, and SQLSetPos.

#define SALES_PERSON_LEN 10
#define STATUS_LEN 6

SQLSMALLINT      sOrderID;
SQLSMALLINT      sCustID;
DATE_STRUCT      dsOpenDate;
SQLCHAR         szSalesPerson[SALES_PERSON_LEN];
SQLCHAR         szStatus[STATUS_LEN);
SQLINTEGER      cbOrderID = 0, cbCustID = 0, cbOpenDate = 0, cbSalesPerson = SQL_NTS,
cbStatus = SQL_NTS;
SQLRETURN retcode;
SQLHSTMT hstmt;

/* Prepare the SQL statement with parameter markers. */
retcode = SQLPrepare(hstmt,
"INSERT INTO ORDERS (ORDERID, CUSTID, OPENDATE, SALESPERSON,
STATUS) VALUES (?, ?, ?, ?, ?)", SQL_NTS);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
/* Specify data types and buffers for OrderID, CustID, OpenDate, SalesPerson, */
/* Status parameter data. */
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_SSHORT,
SQL_INTEGER, 0, 0, &sOrderID, 0, &cbOrderID);
SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_SSHORT,
SQL_INTEGER, 0, 0, &sCustID, 0, &cbCustID);
SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_TYPE_DATE,
SQL_TYPE_DATE, 0, 0, &dsOpenDate, 0, &cbOpenDate);
SQLBindParameter(hstmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_CHAR, SALES_PERSON_LEN, 0, szSalesPerson, 0, &cbSalesPerson);
SQLBindParameter(hstmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_CHAR, STATUS_LEN, 0, szStatus, 0, &cbStatus);
/* Specify first row of parameter data. */
sOrderID = 1001;
sCustID = 298;
dsOpenDate.year = 1996;
dsOpenDate.month = 3;
dsOpenDate.day = 8;
strcpy(szSalesPerson, "Johnson");
strcpy(szStatus, "Closed");

/* Execute statement with first row. */
retcode = SQLExecute(hstmt);
/* Specify second row of parameter data. */
sOrderID = 1002;
sCustID = 501;
dsOpenDate.year = 1996;
dsOpenDate.month = 3;
dsOpenDate.day = 9;
strcpy(szSalesPerson, "Bailey");
strcpy(szStatus, "Open");

/* Execute statement with second row. */
retcode = SQLExecute(hstmt);
}

Code Example

In the following example, an application executes an SQL Server stored procedure using a named parameter. Note that the named parameter (@quote) is bound with a ParameterNumber of 1, while it is the second parameter in the procedure definition. Because the first parameter (@title_id) has a default value of 1, the named parameter is the only dynamic parameter.

/* Define the stored procedure "test" */
CREATE PROCEDURE test @title_id int = 1, @quote char(30)
AS <blah>
/* Prepare the procedure invocation statement */
SQLPrepare(hstmt, "{call test(?)}", SQL_NTS);
/* Populate record 1 of IPD */
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
30, 0, szQuote, 0, &cbValue);
/* Get IPD handle and set the NAMED and UNNAMED fields for record # 1 */
SQLGetStmtAttr(hstmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0);
SQLSetDescField(hIpd, 1, SQL_DESC_NAME, "@quote", SQL_NTS);
/* Assuming that szQuote has been appropriately initialized, execute the statement */
SQLExecute(hstmt);

Related Functions

For information about | See
Returning information about a parameter in a statement | SQLDescribeParam
Executing an SQL statement | SQLExecDirect
Executing a prepared SQL statement | SQLExecute
Releasing parameter buffers on the statement | SQLFreeStmt
Returning the number of statement parameters | SQLNumParams
Returning the next parameter to send data for | SQLParamData
Specifying multiple parameter values | SQLParamOptions
Sending parameter data at execution time | SQLPutData


SQLBrowseConnect

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ODBC

Summary

SQLBrowseConnect supports an iterative method of discovering and enumerating the attributes and attribute values required to connect to a data source. Each call to SQLBrowseConnect returns successive levels of attributes and attribute values. When all levels have been enumerated, a connection to the data source is completed and a complete connection string is returned by SQLBrowseConnect. A return code of SQL_SUCCESS or SQL_SUCCESS_WITH_INFO indicates that all connection information has been specified and the application is now connected to the data source.

Syntax

SQLRETURN SQLBrowseConnect(
     SQLHDBC     ConnectionHandle,
     SQLCHAR *     InConnectionString,
     SQLSMALLINT     StringLength1,
     SQLCHAR *     OutConnectionString,
     SQLSMALLINT     BufferLength,
     SQLSMALLINT *     StringLength2Ptr);

Arguments

ConnectionHandle
[Input]
Connection handle.
InConnectionString
[Input]
Browse request connection string (see "InConnectionString Argument" in "Comments").
StringLength1
[Input]
Length of *InConnectionString.
OutConnectionString
[Output]
Pointer to a buffer in which to return the browse result connection string (see "OutConnectionString Argument" in "Comments").
BufferLength
[Input]
Length of the *OutConnectionString buffer.
StringLength2Ptr
[Output]
The total number of bytes (excluding the null-termination byte) available to return in *OutConnectionString. If the number of bytes available to return is greater than or equal to BufferLength, the connection string in *OutConnectionString is truncated to BufferLength minus the length of a null-termination character.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NEED_DATA, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLBrowseConnect returns SQL_ERROR, SQL_SUCCESS_WITH_INFO, or SQL_NEED_DATA, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of ConnectionHandle. The following table lists the SQLSTATE values commonly returned by SQLBrowseConnect and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | The buffer *OutConnectionString was not large enough to return the entire browse result connection string, so the string was truncated. The buffer *StringLength2Ptr contains the length of the untruncated browse result connection string. (Function returns SQL_SUCCESS_WITH_INFO.)
01S00 | Invalid connection string attribute | An invalid attribute keyword was specified in the browse request connection string (InConnectionString). (Function returns SQL_NEED_DATA.) An attribute keyword was specified in the browse request connection string (InConnectionString) that does not apply to the current connection level. (Function returns SQL_NEED_DATA.)
01S02 | Value changed | The driver did not support the specified value of the ValuePtr argument in SQLSetConnectAttr and substituted a similar value. (Function returns SQL_SUCCESS_WITH_INFO.)
08001 | Client unable to establish connection | The driver was unable to establish a connection with the data source.
08002 | Connection name in use | (DM) The specified connection had already been used to establish a connection with a data source, and the connection was open.
08004 | Server rejected the connection | The data source rejected the establishment of the connection for implementation-defined reasons.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was attempting to connect failed before the function completed processing.
28000 | Invalid authorization specification | Either the user identifier or the authorization string or both, as specified in the browse request connection string (InConnectionString), violated restrictions defined by the data source.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | (DM) The Driver Manager was unable to allocate memory required to support execution or completion of the function. The driver was unable to allocate memory required to support execution or completion of the function.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) The value specified for argument StringLength1 was less than 0 and was not equal to SQL_NTS. (DM) The value specified for argument BufferLength was less than 0.
HYT00 | Timeout expired | The login timeout period expired before the connection to the data source completed. The timeout period is set through SQLSetConnectAttr, SQL_ATTR_LOGIN_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver corresponding to the specified data source name does not support the function.
IM002 | Data source not found and no default driver specified | (DM) The data source name specified in the browse request connection string (InConnectionString) was not found in the system information, nor was there a default driver specification. (DM) ODBC data source and default driver information could not be found in the system information.
IM003 | Specified driver could not be loaded | (DM) The driver listed in the data source specification in the system information or specified by the DRIVER keyword was not found or could not be loaded for some other reason.
IM004 | Driver's SQLAllocHandle on SQL_HANDLE _ENV failed | (DM) During SQLBrowseConnect, the Driver Manager called the driver's SQLAllocHandle function with a HandleType of SQL_HANDLE_ENV and the driver returned an error.
IM005 | Driver's SQLAllocHandle on SQL_HANDLE_DBC failed | (DM) During SQLBrowseConnect, the Driver Manager called the driver's SQLAllocHandle function with a HandleType of SQL_HANDLE_DBC and the driver returned an error.
IM006 | Driver's SQLSetConnectAttr failed | (DM) During SQLBrowseConnect, the Driver Manager called the driver's SQLSetConnectAttr function and the driver returned an error.
IM009 | Unable to load translation DLL | The driver was unable to load the translation DLL that was specified for the data source or for the connection.
IM010 | Data source name too long | (DM) The attribute value for the DSN keyword was longer than SQL_MAX_DSN_LENGTH characters.
IM011 | Driver name too long | (DM) The attribute value for the DRIVER keyword was longer than 255 characters.
IM012 | DRIVER keyword syntax error | (DM) The keyword-value pair for the DRIVER keyword contained a syntax error.

Comments

InConnectionString Argument

A browse request connection string has the following syntax:

connection-string ::= attribute[;] | attribute; connection-string
attribute ::= attribute-keyword=attribute-value | DRIVER=[{]attribute-value[}]
attribute-keyword ::= DSN | UID | PWD
| driver-defined-attribute-keyword
attribute-value ::= character-string
driver-defined-attribute-keyword ::= identifier

where character-string has zero or more characters; identifier has one or more characters; attribute-keyword is not case-sensitive; attribute-value may be case-sensitive; and the value of the DSN keyword does not consist solely of blanks. Because of connection string and initialization file grammar, keywords and attribute values that contain the characters []{}(),;?*=!@ should be avoided. Because of the grammar in the system information, keywords and data source names cannot contain the backslash (\) character. For an ODBC 2.x driver, braces are required around the attribute value for the DRIVER keyword.

If any keywords are repeated in the browse request connection string, the driver uses the value associated with the first occurrence of the keyword. If the DSN and DRIVER keywords are included in the same browse request connection string, the Driver Manager and driver use whichever keyword appears first.

For information about how an application chooses a data source or driver, see "Choosing a Data Source or Driver" in Chapter 6: Connecting to a Data Source or Driver.

OutConnectionString Argument

The browse result connection string is a list of connection attributes. A connection attribute consists of an attribute keyword and a corresponding attribute value. The browse result connection string has the following syntax:

connection-string ::= attribute[;] | attribute; connection-string
attribute ::= [*]attribute-keyword=attribute-value
attribute-keyword ::= ODBC-attribute-keyword
| driver-defined-attribute-keyword
ODBC-attribute-keyword = {UID | PWD}[:localized-identifier]
driver-defined-attribute-keyword ::= identifier[:localized-identifier]
attribute-value ::= {attribute-value-list} | ?
(The braces are literal; they are returned by the driver.)
attribute-value-list ::= character-string [:localized-character string] | character-string [:localized-character string], attribute-value-list

where character-string and localized-character string have zero or more characters; identifier and localized-identifier have one or more characters; attribute-keyword is not case-sensitive; and attribute-value may be case-sensitive. Because of connection string and initialization file grammar, keywords, localized identifiers, and attribute values that contain the characters []{}(),;?*=!@ should be avoided. Because of the grammar in the system information, keywords and data source names cannot contain the backslash (\) character.

The browse result connection string syntax is used according to the following semantic rules:

  • If an asterisk (*) precedes an attribute-keyword, the attribute is optional and can be omitted in the next call to SQLBrowseConnect.
  • The attribute keywords UID and PWD have the same meaning as defined in SQLDriverConnect.
  • A driver-defined-attribute-keyword names the kind of attribute for which an attribute value may be supplied. For example, it might be SERVER, DATABASE, HOST, or DBMS.
  • ODBC-attribute-keywords and driver-defined-attribute-keywords include a localized or user-friendly version of the keyword. This might be used by applications as a label in a dialog box. However, UID, PWD, or the identifier alone must be used when passing a browse request string to the driver.
  • The {attribute-value-list} is an enumeration of actual values valid for the corresponding attribute-keyword. Note that the braces ({}) do not indicate a list of choices; they are returned by the driver. For example, it might be a list of server names or a list of database names.
  • If the attribute-value is a single question mark (?), a single value corresponds to the attribute-keyword. For example, UID=JohnS; PWD=Sesame.
  • Each call to SQLBrowseConnect returns only the information required to satisfy the next level of the connection process. The driver associates state information with the connection handle so that the context can always be determined on each call.

Using SQLBrowseConnect

SQLBrowseConnect requires an allocated connection. The Driver Manager loads the driver that was specified in or that corresponds to the data source name specified in the initial browse request connection string; for information about when this occurs, see the "Comments" section in SQLConnect. The driver may establish a connection with the data source during the browsing process. If SQLBrowseConnect returns SQL_ERROR, outstanding connections are terminated and the connection is returned to an unconnected state.

Note SQLBrowseConnect does not support connection pooling. If SQLBrowseConnect is called while connection pooling is enabled, SQLSTATE HY000 (General error) will be returned.

When SQLBrowseConnect is called for the first time on a connection, the browse request connection string must contain the DSN keyword or the DRIVER keyword. If the browse request connection string contains the DSN keyword, the Driver Manager locates a corresponding data source specification in the system information:

  • If the Driver Manager finds the corresponding data source specification, it loads the associated driver DLL; the driver can retrieve information about the data source from the system information.
  • If the Driver Manager cannot find the corresponding data source specification, it locates the default data source specification and loads the associated driver DLL; the driver can retrieve information about the default data source from the system information. "DEFAULT" is passed to the driver for the DSN.
  • If the Driver Manager cannot find the corresponding data source specification and there is no default data source specification, it returns SQL_ERROR with SQLSTATE IM002 (Data source not found and no default driver specified).

If the browse request connection string contains the DRIVER keyword, the Driver Manager loads the specified driver; it does not attempt to locate a data source in the system information. Because the DRIVER keyword does not use information from the system information, the driver must define enough keywords so that a driver can connect to a data source using only the information in the browse request connection strings.

On each call to SQLBrowseConnect, the application specifies the connection attribute values in the browse request connection string. The driver returns successive levels of attributes and attribute values in the browse result connection string; it returns SQL_NEED_DATA as long as there are connection attributes that have not yet been enumerated in the browse request connection string. The application uses the contents of the browse result connection string to build the browse request connection string for the next call to SQLBrowseConnect. All mandatory attributes (those not preceded by an asterisk in the OutConnectionString argument) must be included in the next call to SQLBrowseConnect. Note that the application cannot use the contents of previous browse result connection strings when building the current browse request connection string; that is, it cannot specify different values for attributes set in previous levels.

When all levels of connection and their associated attributes have been enumerated, the driver returns SQL_SUCCESS, the connection to the data source is complete, and a complete connection string is returned to the application. The connection string is suitable to use, in conjunction with SQLDriverConnect, with the SQL_DRIVER_NOPROMPT option to establish another connection. The complete connection string cannot be used in another call to SQLBrowseConnect, however; if SQLBrowseConnect were called again, the entire sequence of calls would have to be repeated.

SQLBrowseConnect also returns SQL_NEED_DATA if there are recoverable, nonfatal errors during the browse process; for example, an invalid password or attribute keyword supplied by the application. When SQL_NEED_DATA is returned and the browse result connection string is unchanged, an error has occurred and the application can call SQLGetDiagRec to return the SQLSTATE for browse-time errors. This permits the application to correct the attribute and continue the browse.

An application can terminate the browse process at any time by calling SQLDisconnect. The driver will terminate any outstanding connections and return the connection to an unconnected state.

For more information, see "Connecting with SQLBrowseConnect" in Chapter 6: Connecting to a Data Source or Driver.

If a driver supports SQLBrowseConnect, the driver keyword section in the system information for the driver must contain the ConnectFunctions keyword with the third character set to "Y."

Code Example

In the following example, an application calls SQLBrowseConnect repeatedly. Each time SQLBrowseConnect returns SQL_NEED_DATA, it passes back information about the data it needs in *OutConnectionString. The application passes OutConnectionString to its routine GetUserInput (not shown). GetUserInput parses the information, builds and displays a dialog box, and returns the information entered by the user in *InConnectionString. The application passes the user's information to the driver in the next call to SQLBrowseConnect. After the application has provided all necessary information for the driver to connect to the data source, SQLBrowseConnect returns SQL_SUCCESS and the application proceeds.

For a more detailed example of connecting to a SQL Server driver by calling SQLBrowseConnect, see "SQL Server Browsing Example" in Chapter 6: Connecting to a Data Source or Driver.

For example, to connect to the data source Sales, the following actions might occur. First, the application passes the following string to SQLBrowseConnect:

"DSN=Sales"

The Driver Manager loads the driver associated with the data source Sales. It then calls the driver's SQLBrowseConnect function with the same arguments it received from the application. The driver returns the following string in *OutConnectionString:

"HOST:Server={red,blue,green};UID:ID=?;PWD:Password=?"

The application passes this string to its GetUserInput routine, which builds a dialog box that asks the user to select the red, blue, or green server and to enter a user ID and password. The routine passes the following user-specified information back in *InConnectionString, which the application passes to SQLBrowseConnect:

"HOST=red;UID=Smith;PWD=Sesame"

SQLBrowseConnect uses this information to connect to the red server as Smith with the password Sesame, and then returns the following string in *OutConnectionString:

"*DATABASE:Database={SalesEmployees,SalesGoals,SalesOrders}"

The application passes this string to its GetUserInput routine, which builds a dialog box that asks the user to select a database. The user selects empdata and the application calls SQLBrowseConnect a final time with this string:

"DATABASE=SalesOrders"

This is the final piece of information the driver needs to connect to the data source; SQLBrowseConnect returns SQL_SUCCESS, and *OutConnectionString contains the completed connection string:

"DSN=Sales;HOST=red;UID=Smith;PWD=Sesame;DATABASE=SalesOrders"
#define BRWS_LEN 100
SQLHENV       henv;
SQLHDBC       hdbc;
SQLHSTMT      hstmt;
SQLRETURN     retcode;
SQLCHAR       szConnStrIn[BRWS_LEN], szConnStrOut[BRWS_LEN];
SQLSMALLINT   cbConnStrOut;

/* Allocate the environment handle. */
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
/* Set the version environment attribute. */
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3, 0);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
/* Allocate the connection handle. */
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
/* Call SQLBrowseConnect until it returns a value other than */
/* SQL_NEED_DATA (pass the data source name the first time). */
/* If SQL_NEED_DATA is returned, call GetUserInput (not */
/* shown) to build a dialog from the values in szConnStrOut.   */
      /* The user-supplied values are returned in szConnStrIn,   */
      /* which is passed in the next call to SQLBrowseConnect.   */

         lstrcpy(szConnStrIn, "DSN=Sales");
         do {
            retcode = SQLBrowseConnect(hdbc, szConnStrIn, SQL_NTS,
                     szConnStrOut, BRWS_LEN, &cbConnStrOut);
            if (retcode == SQL_NEED_DATA)
               GetUserInput(szConnStrOut, szConnStrIn);
         } while (retcode == SQL_NEED_DATA);

         if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){

            /* Allocate the statement handle. */
            retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);

if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
/* Process data after successful connection */
                  ;
                  ;
                  ;
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
            }
SQLDisconnect(hdbc);
         }
      }
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
   }
}
SQLFreeHandle(SQL_HANDLE_ENV, henv);

Related Functions

For information about | See
Allocating a connection handle | SQLAllocHandle
Connecting to a data source | SQLConnect
Disconnecting from a data source | SQLDisconnect
Connecting to a data source using a connection string or dialog box | SQLDriverConnect
Returning driver descriptions and attributes | SQLDrivers
Freeing a connection handle | SQLFreeHandle


SQLBulkOperations

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ODBC

Summary

SQLBulkOperations performs bulk insertions and bulk bookmark operations, including update, delete, and fetch by bookmark.

Syntax

SQLRETURN SQLBulkOperations(
     SQLHSTMT     StatementHandle,
     SQLUSMALLINT     Operation);

Arguments

StatementHandle
[Input]
Statement handle.
Operation
[Input]
Operation to perform:

SQL_ADD
SQL_UPDATE_BY_BOOKMARK
SQL_DELETE_BY_BOOKMARK
SQL_FETCH_BY_BOOKMARK

For more information, see "Comments."

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NEED_DATA, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLBulkOperations returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLBulkOperations and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

For all those SQLSTATEs that can return SQL_SUCCESS_WITH_INFO or SQL_ERROR (except 01xxx SQLSTATEs), SQL_SUCCESS_WITH_INFO is returned if an error occurs on one or more, but not all, rows of a multirow operation, and SQL_ERROR is returned if an error occurs on a single-row operation.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data right truncation | The Operation argument was SQL_FETCH_BY_BOOKMARK, and string or binary data returned for a column or columns with a data type of SQL_C_CHAR or SQL_C_BINARY resulted in the truncation of nonblank character or non-NULL binary data.
01S01 Error in row The Operation argument was SQL_ADD, and an error occurred in one or more rows while performing the operation but at least one row was successfully added. (Function returns SQL_SUCCESS_WITH_INFO.)

(This error is raised only when an application is working with an ODBC 2.x driver.)
01S07 Fractional truncation The Operation argument was SQL_FETCH_BY_BOOKMARK, the data type of the application buffer was not SQL_C_CHAR or SQL_C_BINARY, and the data returned to application buffers for one or more columns was truncated. (For numeric C data types, the fractional part of the number was truncated. For time, timestamp, and interval C data types containing a time component, the fractional portion of the time was truncated.)

(Function returns SQL_SUCCESS_WITH_INFO.)
07006 Restricted data type attribute violation The Operation argument was SQL_FETCH_BY_BOOKMARK, and the data value of a column in the result set could not be converted to the data type specified by the TargetType argument in the call to SQLBindCol.

The Operation argument was SQL_UPDATE_BY_BOOKMARK or SQL_ADD, and the data value in the application buffers could not be converted to the data type of a column in the result set.
07009 | Invalid descriptor index | The argument Operation was SQL_ADD, and a column was bound with a column number greater than the number of columns in the result set.
21S02 | Degree of derived table does not match column list | The argument Operation was SQL_UPDATE_BY_BOOKMARK; and no columns were updatable because all columns were either unbound or read-only, or the value in the bound length/indicator buffer was SQL_COLUMN_IGNORE.
22001 | String data right truncation | The assignment of a character or binary value to a column in the result set resulted in the truncation of nonblank (for characters) or non-null (for binary) characters or bytes.
22003 Numeric value out of range The Operation argument was SQL_ADD or SQL_UPDATE_BY_BOOKMARK, and the assignment of a numeric value to a column in the result set caused the whole (as opposed to fractional) part of the number to be truncated.

The argument Operation was SQL_FETCH_BY_BOOKMARK, and returning the numeric value for one or more bound columns would have caused a loss of significant digits.
22007 Invalid datetime format The Operation argument was SQL_ADD or SQL_UPDATE_BY_BOOKMARK, and the assignment of a date or timestamp value to a column in the result set caused the year, month, or day field to be out of range.

The argument Operation was SQL_FETCH_BY_BOOKMARK, and returning the date or timestamp value for one or more bound columns would have caused the year, month, or day field to be out of range.
22008 Date/time field overflow The Operation argument was SQL_ADD or SQL_UPDATE_BY_BOOKMARK, and the performance of datetime arithmetic on data being sent to a column in the result set resulted in a datetime field (the year, month, day, hour, minute, or second field) of the result falling outside the permissible range of values for the field or being invalid based on the Gregorian calendar's natural rules for datetimes.

The Operation argument was SQL_FETCH_BY_BOOKMARK, and the performance of datetime arithmetic on data being retrieved from the result set resulted in a datetime field (the year, month, day, hour, minute, or second field) of the result falling outside the permissible range of values for the field or being invalid based on the Gregorian calendar's natural rules for datetimes.
22015 Interval field overflow The Operation argument was SQL_ADD or SQL_UPDATE_BY_BOOKMARK, and the assignment of an exact numeric or interval C type to an interval SQL data type caused a loss of significant digits.

The Operation argument was SQL_ADD or SQL_UPDATE_BY_BOOKMARK; when assigning to an interval SQL type, there was no representation of the value of the C type in the interval SQL type.

The Operation argument was SQL_FETCH_BY_BOOKMARK, and assigning from an exact numeric or interval SQL type to an interval C type caused a loss of significant digits in the leading field.

The Operation argument was SQL_FETCH_BY_BOOKMARK; when assigning to an interval C type, there was no representation of the value of the SQL type in the interval C type.
22018 Invalid character value for cast specification The Operation argument was SQL_FETCH_BY_BOOKMARK; the C type was an exact or approximate numeric, a datetime, or an interval data type; the SQL type of the column was a character data type; and the value in the column was not a valid literal of the bound C type.

The argument Operation was SQL_ADD or SQL_UPDATE_BY_BOOKMARK; the SQL type was an exact or approximate numeric, a datetime, or an interval data type; the C type was SQL_C_CHAR; and the value in the column was not a valid literal of the bound SQL type.

23000 Integrity constraint violation The Operation argument was SQL_ADD, SQL_DELETE_BY_BOOKMARK, or SQL_UPDATE_BY_BOOKMARK, and an integrity constraint was violated.

The Operation argument was SQL_ADD, and a column that was not bound is defined as NOT NULL and has no default.

The Operation argument was SQL_ADD, the length specified in the bound StrLen_or_IndPtr buffer was SQL_COLUMN_IGNORE, and the column did not have a default value.
24000 | Invalid cursor state | The StatementHandle was in an executed state, but no result set was associated with the StatementHandle.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
42000 | Syntax error or access violation | The driver was unable to lock the row as needed to perform the operation requested in the Operation argument.
44000 | WITH CHECK OPTION violation | The Operation argument was SQL_ADD or SQL_UPDATE_BY_BOOKMARK, and the insert or update was performed on a viewed table (or a table derived from the viewed table) that was created by specifying WITH CHECK OPTION, such that one or more rows affected by the insert or update will no longer be present in the viewed table.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY010 Function sequence error (DM) The specified StatementHandle was not in an executed state. The function was called without first calling SQLExecDirect, SQLExecute, or a catalog function.

(DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.

(DM) The driver was an ODBC 2.x driver, and SQLBulkOperations was called for a StatementHandle before SQLFetchScroll or SQLFetch was called.

(DM) SQLBulkOperations was called after SQLExtendedFetch was called on the StatementHandle.
HY011 | Attribute cannot be set now | (DM) The driver was an ODBC 2.x driver, and the SQL_ATTR_ROW_STATUS_PTR statement attribute was set between calls to SQLFetch or SQLFetchScroll and SQLBulkOperations.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 Invalid string or buffer length The Operation argument was SQL_ADD or SQL_UPDATE_BY_BOOKMARK; a data value was not a null pointer; the C data type was SQL_C_BINARY or SQL_C_CHAR; and the column length value was less than 0, but not equal to SQL_DATA_AT_EXEC, SQL_COLUMN_IGNORE, SQL_NTS, or SQL_NULL_DATA, or less than or equal to SQL_LEN_DATA_AT_EXEC_OFFSET.

The value in a length/indicator buffer was SQL_DATA_AT_EXEC; the SQL type was either SQL_LONGVARCHAR, SQL_LONGVARBINARY, or a long data source–specific data type; and the SQL_NEED_LONG_DATA_LEN information type in SQLGetInfo was "Y".

The Operation argument was SQL_ADD, the SQL_ATTR_USE_BOOKMARK statement attribute was set to SQL_UB_VARIABLE, and column 0 was bound to a buffer whose length was not equal to the maximum length for the bookmark for this result set. (This length is available in the SQL_DESC_OCTET_LENGTH field of the IRD and can be obtained by calling SQLDescribeCol, SQLColAttribute, or SQLGetDescField.)
HY092 Invalid attribute identifier (DM) The value specified for the Operation argument was invalid.

The Operation argument was SQL_ADD, SQL_UPDATE_BY_BOOKMARK, or SQL_DELETE_BY_BOOKMARK, and the SQL_ATTR_CONCURRENCY statement attribute was set to SQL_CONCUR_READ_ONLY.

The Operation argument was SQL_DELETE_BY_BOOKMARK, SQL_FETCH_BY_BOOKMARK, or SQL_UPDATE_BY_BOOKMARK, and the bookmark column was not bound or the SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_OFF.
HYC00 | Optional feature not implemented | The driver or data source does not support the operation requested in the Operation argument.
HYT00 | Timeout expired | The query timeout period expired before the data source returned the result set. The timeout period is set through SQLSetStmtAttr with an Attribute argument of SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

Caution For information about what statement states SQLBulkOperations can be called in and what it needs to do for compatibility with ODBC 2.x applications, see the "Block Cursors, Scrollable Cursors, and Backward Compatibility" section in Appendix G: Driver Guidelines for Backward Compatibility.

An application uses SQLBulkOperations to perform the following operations on the base table or view that corresponds to the current query:

  • Add new rows.
  • Update a set of rows where each row is identified by a bookmark.
  • Delete a set of rows where each row is identified by a bookmark.
  • Fetch a set of rows where each row is identified by a bookmark.

After a call to SQLBulkOperations, the block cursor position is undefined. The application has to call SQLFetchScroll to set the cursor position. An application should call SQLFetchScroll only with a FetchOrientation argument of SQL_FETCH_FIRST, SQL_FETCH_LAST, SQL_FETCH_ABSOLUTE, or SQL_FETCH_BOOKMARK. The cursor position is undefined if the application calls SQLFetch or SQLFetchScroll with a FetchOrientation argument of SQL_FETCH_PRIOR, SQL_FETCH_NEXT, or SQL_FETCH_RELATIVE.

A column can be ignored in bulk operations performed by a call to SQLBulkOperations by setting the column length/indicator buffer specified in the call to SQLBindCol, to SQL_COLUMN_IGNORE.

It is not necessary for the application to set the SQL_ATTR_ROW_OPERATION_PTR statement attribute when calling SQLBulkOperations because rows cannot be ignored when performing bulk operations with this function.

The buffer pointed to by the SQL_ATTR_ROWS_FETCHED_PTR statement attribute contains the number of rows affected by a call to SQLBulkOperations.

When the Operation argument is SQL_ADD or SQL_UPDATE_BY_BOOKMARK and the select-list of the query specification associated with the cursor contains more than one reference to the same column, it is driver-defined whether an error is generated or the driver ignores the duplicated references and performs the requested operations.

For more information about using SQLBulkOperations, see "Updating Data with SQLBulkOperations" in Chapter 12: Updating Data.

Performing Bulk Inserts

To insert data with SQLBulkOperations, an application performs the following sequence of steps:

  1. 69Executes a query that returns a result set.
  2. Sets the SQL_ATTR_ROW_ARRAY_SIZE statement attribute to the number of rows that it wants to insert.
  3. Calls SQLBindCol to bind the data that it wants to insert. The data is bound to an array with a size equal to the value of SQL_ATTR_ROW_ARRAY_SIZE.

    Note The size of the array pointed to by the SQL_ATTR_ROW_STATUS_PTR statement attribute should either be equal to SQL_ATTR_ROW_ARRAY_SIZE or SQL_ATTR_ROW_STATUS_PTR should be a null pointer.

  4. Calls SQLBulkOperations(StatementHandle, SQL_ADD) to perform the insertion.
  5. If the application has set the SQL_ATTR_ROW_STATUS_PTR statement attribute, it can inspect this array to see the result of the operation.

If an application binds column 0 before calling SQLBulkOperations with an Operation argument of SQL_ADD, the driver will update the bound column 0 buffers with the bookmark values for the newly inserted row. For this to occur, the application must have set the SQL_ATTR_USE_BOOKMARKS statement attribute to SQL_UB_VARIABLE before executing the statement. (This does not work with an ODBC 2.x driver.)

Long data can be added in parts by SQLBulkOperations, using calls to SQLParamData and SQLPutData. For more information, see "Providing Long Data for Bulk Inserts and Updates" later in this function reference.

It is not necessary for the application to call SQLFetch or SQLFetchScroll before calling SQLBulkOperations (except when going against an ODBC 2.x driver; see "Backward Compatibility and Standards Compliance" in Chapter 17: Programming Considerations).

The behavior is driver-defined if SQLBulkOperations, with an Operation argument of SQL_ADD, is called on a cursor that contains duplicate columns. The driver can return a driver-defined SQLSTATE, add the data to the first column that appears in the result set, or perform other driver-defined behavior.

Performing Bulk Updates Using Bookmarks

To perform bulk updates using bookmarks with SQLBulkOperations, an application performs the following steps in sequence:

  1. Sets the SQL_ATTR_USE_BOOKMARKS statement attribute to SQL_UB_VARIABLE.
  2. Executes a query that returns a result set.
  3. Sets the SQL_ATTR_ROW_ARRAY_SIZE statement attribute to the number of rows that it wants to update.
  4. Calls SQLBindCol to bind the data that it wants to update. The data is bound to an array with a size equal to the value of SQL_ATTR_ROW_ARRAY_SIZE. It also calls SQLBindCol to bind column 0 (the bookmark column).
  5. Copies the bookmarks for rows that it is interested in updating into the array bound to column 0.
  6. Updates the data in the bound buffers.

    Note The size of the array pointed to by the SQL_ATTR_ROW_STATUS_PTR statement attribute should either be equal to SQL_ATTR_ROW_ARRAY_SIZE or SQL_ATTR_ROW_STATUS_PTR should be a null pointer.

  7. Calls SQLBulkOperations(StatementHandle, SQL_UPDATE_BY_BOOKMARK).

    Note If the application has set the SQL_ATTR_ROW_STATUS_PTR statement attribute, it can inspect this array to see the result of the operation.

  8. Optionally calls SQLBulkOperations(StatementHandle, SQL_FETCH_BY_BOOKMARK) to fetch data into the bound application buffers to verify that the update has occurred.
  9. If data has been updated, the driver changes the value in the row status array for the appropriate rows to SQL_ROW_UPDATED.

Bulk updates performed by SQLBulkOperations can include long data by using calls to SQLParamData and SQLPutData. For more information, see "Providing Long Data for Bulk Inserts and Updates" later in this function reference.

If bookmarks persist across cursors, the application does not need to call SQLFetch or SQLFetchScroll before updating by bookmarks. It can use bookmarks that it has stored from a previous cursor. If bookmarks do not persist across cursors, the application has to call SQLFetch or SQLFetchScroll to retrieve the bookmarks.

The behavior is driver-defined if SQLBulkOperations, with an Operation argument of SQL_UPDATE_BY_BOOKMARK, is called on a cursor that contains duplicate columns. The driver can return a driver-defined SQLSTATE, update the first column that appears in the result set, or perform other driver-defined behavior.

Performing Bulk Fetches Using Bookmarks

To perform bulk fetches using bookmarks with SQLBulkOperations, an application performs the following steps in sequence:

  1. Sets the SQL_ATTR_USE_BOOKMARKS statement attribute to SQL_UB_VARIABLE.
  2. Executes a query that returns a result set.
  3. Sets the SQL_ATTR_ROW_ARRAY_SIZE statement attribute to the number of rows that it wants to fetch.
  4. Calls SQLBindCol to bind the data that it wants to fetch. The data is bound to an array with a size equal to the value of SQL_ATTR_ROW_ARRAY_SIZE. It also calls SQLBindCol to bind column 0 (the bookmark column).
  5. Copies the bookmarks for rows that it is interested in fetching into the array bound to column 0. (This assumes that the application has already obtained the bookmarks separately.)

    Note The size of the array pointed to by the SQL_ATTR_ROW_STATUS_PTR statement attribute should either be equal to SQL_ATTR_ROW_ARRAY_SIZE or SQL_ATTR_ROW_STATUS_PTR should be a null pointer.

  6. Calls SQLBulkOperations(StatementHandle, SQL_FETCH_BY_BOOKMARK).
  7. If the application has set the SQL_ATTR_ROW_STATUS_PTR statement attribute, then it can inspect this array to see the result of the operation.

If bookmarks persist across cursors, the application does not need to call SQLFetch or SQLFetchScroll before fetching by bookmarks. It can use bookmarks that it has stored from a previous cursor. If bookmarks do not persist across cursors, the application has to call SQLFetch or SQLFetchScroll once to retrieve the bookmarks.

Performing Bulk Deletes Using Bookmarks

To perform bulk deletes using bookmarks with SQLBulkOperations, an application performs the following steps in sequence:

  1. Sets the SQL_ATTR_USE_BOOKMARKS statement attribute to SQL_UB_VARIABLE.
  2. Executes a query that returns a result set.
  3. Sets the SQL_ATTR_ROW_ARRAY_SIZE statement attribute to the number of rows that it wants to delete.
  4. Calls SQLBindCol to bind column 0 (the bookmark column).
  5. Copies the bookmarks for rows that it is interested in deleting into the array bound to column 0.

    Note The size of the array pointed to by the SQL_ATTR_ROW_STATUS_PTR statement attribute should either be equal to SQL_ATTR_ROW_ARRAY_SIZE or SQL_ATTR_ROW_STATUS_PTR should be a null pointer.

  6. Calls SQLBulkOperations(StatementHandle, SQL_DELETE_BY_BOOKMARK).
  7. If the application has set the SQL_ATTR_ROW_STATUS_PTR statement attribute, it can inspect this array to see the result of the operation.

If bookmarks persist across cursors, the application does not need to call SQLFetch or SQLFetchScroll before deleting by bookmarks. It can use bookmarks that it has stored from a previous cursor. If bookmarks do not persist across cursors, the application has to call SQLFetch or SQLFetchScroll once to retrieve the bookmarks.

Providing Long Data for Bulk Inserts and Updates

Long data can be provided for bulk inserts and updates performed by calls to SQLBulkOperations. To insert or update long data, an application performs the following steps in addition to the steps described in the "Performing Bulk Inserts" and "Performing Bulk Updates Using Bookmarks" sections earlier in this section.

  1. When it binds the data using SQLBindCol, the application places an application-defined value, such as the column number, in the *TargetValuePtr buffer for data-at-execution columns. The value can be used later to identify the column.

    The application places the result of the SQL_LEN_DATA_AT_EXEC(length) macro in the *StrLen_or_IndPtr buffer. If the SQL data type of the column is SQL_LONGVARBINARY, SQL_LONGVARCHAR, or a long data source–specific data type and the driver returns "Y" for the SQL_NEED_LONG_DATA_LEN information type in SQLGetInfo, length is the number of bytes of data to be sent for the parameter; otherwise, it must be a nonnegative value and is ignored.

  2. When SQLBulkOperations is called, if there are data-at-execution columns, the function returns SQL_NEED_DATA and proceeds to step 3 below. (If there are no data-at-execution columns, the process is complete.)
  3. The application calls SQLParamData to retrieve the address of the *TargetValuePtr buffer for the first data-at-execution column to be processed. SQLParamData returns SQL_NEED_DATA. The application retrieves the application-defined value from the *TargetValuePtr buffer.

    Note Although data-at-execution parameters are similar to data-at-execution columns, the value returned by SQLParamData is different for each.

    Data-at-execution columns are columns in a rowset for which data will be sent with SQLPutData when a row is updated or inserted with SQLBulkOperations. They are bound with SQLBindCol. The value returned by SQLParamData is the address of the row in the *TargetValuePtr buffer that is being processed.

  4. The application calls SQLPutData one or more times to send data for the column. More than one call is needed if all the data value cannot be returned in the *TargetValuePtr buffer specified in SQLPutData; multiple calls to SQLPutData for the same column are allowed only when sending character C data to a column with a character, binary, or data source–specific data type or when sending binary C data to a column with a character, binary, or data source–specific data type.
  5. The application calls SQLParamData again to signal that all data has been sent for the column.
    • If there are more data-at-execution columns, SQLParamData returns SQL_NEED_DATA and the address of the TargetValuePtr buffer for the next data-at-execution column to be processed. The application repeats steps 4 and 5.
    • If there are no more data-at-execution columns, the process is complete. If the statement was executed successfully, SQLParamData returns SQL_SUCCESS or SQL_SUCCESS_WITH_INFO; if the execution failed, it returns SQL_ERROR. At this point, SQLParamData can return any SQLSTATE that can be returned by SQLBulkOperations.

If the operation is canceled or an error occurs in SQLParamData or SQLPutData after SQLBulkOperations returns SQL_NEED_DATA and before data is sent for all data-at-execution columns, the application can call only SQLCancel, SQLGetDiagField, SQLGetDiagRec, SQLGetFunctions, SQLParamData, or SQLPutData for the statement or the connection associated with the statement. If it calls any other function for the statement or the connection associated with the statement, the function returns SQL_ERROR and SQLSTATE HY010 (Function sequence error).

If the application calls SQLCancel while the driver still needs data for data-at-execution columns, the driver cancels the operation. The application can then call SQLBulkOperations again; canceling does not affect the cursor state or the current cursor position.

Row Status Array

The row status array contains status values for each row of data in the rowset after a call to SQLBulkOperations. The driver sets the status values in this array after a call to SQLFetch, SQLFetchScroll, SQLSetPos, or SQLBulkOperations. This array is initially populated by a call to SQLBulkOperations if SQLFetch or SQLFetchScroll has not been called prior to SQLBulkOperations. This array is pointed to by the SQL_ATTR_ROW_STATUS_PTR statement attribute. The number of elements in the row status arrays must equal the number of rows in the rowset (as defined by the SQL_ATTR_ROW_ARRAY_SIZE statement attribute). For information about this row status array, see SQLFetch.

Code Example

The following example fetches 10 rows of data at a time from the Customers table. It then prompts the user for an action to take. To reduce network traffic, the example buffer updates, deletes, and inserts locally in the bound arrays, but at offsets past the rowset data. When the user chooses to send updates, deletes, and inserts to the data source, the code sets the binding offset appropriately and calls SQLBulkOperations. For simplicity, the user cannot buffer more than 10 updates, deletes, or inserts.

#define UPDATE_ROW            100
#define DELETE_ROW            101
#define ADD_ROW               102
#define SEND_TO_DATA_SOURCE   103
#define UPDATE_OFFSET          10
#define INSERT_OFFSET          20
#define DELETE_OFFSET          30

// Define a structure to hold the customer data. Assume we know the maximum bookmark
// size to be 10 bytes.
typedef tagCustStruct {
SQLCHAR       Bookmark[10];
SQLINTEGER    BookmarkLen;
SQLUINTEGER   CustID;
SQLINTEGER    CustIDInd;
SQLCHAR       Name[51];
SQLINTEGER    NameLenOrInd;
SQLCHAR       Address[51];
SQLINTEGER    AddressLenOrInd;
SQLCHAR       Phone[11];
SQLINTEGER    PhoneLenOrInd;
} CustStruct;

// Allocate 40 of these structures. Elements 0-9 are for the current rowset,
// elements 10-19 are for the buffered updates, elements 20-29 are for
// the buffered inserts, and elements 30-39 are for the buffered deletes.
CustStruct CustArray[40];
SQLUSMALLINT RowStatusArray[10], Action, RowNum, NumUpdates = 0, NumInserts = 0,
NumDeletes = 0;
SQLINTEGER   BindOffset = 0;
SQLRETURN    rc;
SQLHSTMT     hstmt;

// Set the following statement attributes:
// SQL_ATTR_CURSOR_TYPE:           Keyset-driven
// SQL_ATTR_ROW_BIND_TYPE:         Row-wise
// SQL_ATTR_ROW_ARRAY_SIZE:        10
// SQL_ATTR_USE_BOOKMARKS:         Use variable-length bookmarks
// SQL_ATTR_ROW_STATUS_PTR:        Points to RowStatusArray
// SQL_ATTR_ROW_BIND_OFFSET_PTR:   Points to BindOffset
SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, SQL_CURSOR_KEYSET_DRIVEN, 0);
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_TYPE, sizeof(CustStruct), 0);
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, 10, 0);
SQLSetStmtAttr(hstmt, SQL_ATTR_USE_BOOKMARKS, SQL_UB_VARIABLE, 0);
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, RowStatusArray, 0);
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_OFFSET_PTR, &BindOffset, 0);
// Bind arrays to the bookmark, CustID, Name, Address, and Phone columns.
SQLBindCol(hstmt, 0, SQL_C_VARBOOKMARK, CustArray[0].Bookmark,
sizeof(CustArray[0].Bookmark), &CustArray[0].BookmarkLen);
SQLBindCol(hstmt, 1, SQL_C_ULONG, &CustArray[0].CustID, 0, &CustArray[0].CustIDInd);
SQLBindCol(hstmt, 2, SQL_C_CHAR, CustArray[0].Name, sizeof(CustArray[0].Name),
&CustArray[0].NameLenOrInd);
SQLBindCol(hstmt, 3, SQL_C_CHAR, CustArray[0].Address, sizeof(CustArray[0].Address),
&CustArray[0].AddressLenOrInd);
SQLBindCol(hstmt, 4, SQL_C_CHAR, CustArray[0].Phone, sizeof(CustArray[0].Phone),
&CustArray[0].PhoneLenOrInd);
// Execute a statement to retrieve rows from the Customers table.
SQLExecDirect(hstmt, "SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
// Fetch and display the first 10 rows.
rc = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0);
DisplayCustData(CustArray, 10);
// Call GetAction to get an action and a row number from the user.
while (GetAction(&Action, &RowNum)) {
switch (Action) {

case SQL_FETCH_NEXT:
case SQL_FETCH_PRIOR:
case SQL_FETCH_FIRST:
case SQL_FETCH_LAST:
case SQL_FETCH_ABSOLUTE:
case SQL_FETCH_RELATIVE:
// Fetch and display the requested data.
SQLFetchScroll(hstmt, Action, RowNum);
DisplayCustData(CustArray, 10);
break;

case UPDATE_ROW:
// Check if we have reached the maximum number of buffered updates.
if (NumUpdates < 10) {
// Get the new customer data and place it in the next available element of
// the buffered updates section of CustArray, copy the bookmark of the row
// being updated to the same element, and increment the update counter.
// Checking to see we have not already buffered an update for this
// row not shown.
GetNewCustData(CustArray, UPDATE_OFFSET + NumUpdates);
memcopy(CustArray[UPDATE_OFFSET + NumUpdates].Bookmark,
CustArray[RowNum - 1].Bookmark,
CustArray[RowNum - 1].BookmarkLen);
CustArray[UPDATE_OFFSET + NumUpdates].BookmarkLen =
CustArray[RowNum - 1].BookmarkLen;
NumUpdates++;
} else {
DisplayError("Buffers full. Send buffered changes to the data source.");
   }
break;
case DELETE_ROW:
// Check if we have reached the maximum number of buffered deletes.
if (NumDeletes < 10) {
// Copy the bookmark of the row being deleted to the next available element
// of the buffered deletes section of CustArray and increment the delete
// counter. Checking to see we have not already buffered an update for
// this row not shown.
memcopy(CustArray[DELETE_OFFSET + NumDeletes].Bookmark,
CustArray[RowNum - 1].Bookmark,
CustArray[RowNum - 1].BookmarkLen);
CustArray[DELETE_OFFSET + NumDeletes].BookmarkLen =
CustArray[RowNum - 1].BookmarkLen;
NumDeletes++;
} else {
DisplayError("Buffers full. Send buffered changes to the data source.");
   }
break;
case ADD_ROW:
// Check if we have reached the maximum number of buffered inserts.
if (NumInserts < 10) {
// Get the new customer data and place it in the next available element of
// the buffered inserts section of CustArray and increment the insert
// counter.
GetNewCustData(CustArray, INSERT_OFFSET + NumInserts);
NumInserts++;
} else {
DisplayError("Buffers full. Send buffered changes to the data source.");
   }
break;
case SEND_TO_DATA_SOURCE:
// If there are any buffered updates, inserts, or deletes, set the array size
// to that number, set the binding offset to use the data in the buffered
// update, insert, or delete part of CustArray, and call SQLBulkOperations to
// do the updates, inserts, or deletes. Because we will never have more than
// 10 updates, inserts, or deletes, we can use the same row status array.
if (NumUpdates) {
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, NumUpdates, 0);
BindOffset = UPDATE_OFFSET * sizeof(CustStruct);
SQLBulkOperations(hstmt, SQL_UPDATE_BY_BOOKMARK);
NumUpdates = 0;
   }

if (NumInserts) {
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, NumInserts, 0);
BindOffset = INSERT_OFFSET * sizeof(CustStruct);
SQLBulkOperations(hstmt, SQL_ADD);
NumInserts = 0;
   }

if (NumDeletes) {
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, NumDeletes, 0);
BindOffset = DELETE_OFFSET * sizeof(CustStruct);
SQLBulkOperations(hstmt, SQL_DELETE_BY_BOOKMARK);
NumDeletes = 0;
   }

// If there were any updates, inserts, or deletes, reset the binding offset
// and array size to their original values.
if (NumUpdates || NumInserts || NumDeletes) {
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, 10, 0);
BindOffset = 0;
   }
break;
   }
}

// Close the cursor.
SQLFreeStmt(hstmt, SQL_CLOSE);

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Getting a single field of a descriptor | SQLGetDescField
Getting multiple fields of a descriptor | SQLGetDescRec
Setting a single field of a descriptor | SQLSetDescField
Setting multiple fields of a descriptor | SQLSetDescRec
Positioning the cursor, refreshing data in the rowset, or updating or deleting data in the rowset | SQLSetPos
Setting a statement attribute | SQLSetStmtAttr


SQLCancel

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLCancel cancels the processing on a statement.

Syntax

SQLRETURN SQLCancel(
     SQLHSTMT     StatementHandle);

Arguments

StatementHandle
[Input]
Statement handle.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLCancel returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLCancel and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the argument *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY018 | Server declined cancel request | The server declined the cancel request.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

SQLCancel can cancel the following types of processing on a statement:

  • A function running asynchronously on the statement.
  • A function on a statement that needs data.
  • A function running on the statement on another thread.

In ODBC 2.x, if an application calls SQLCancel when no processing is being done on the statement, SQLCancel has the same effect as SQLFreeStmt with the SQL_CLOSE option; this behavior is defined only for completeness and applications should call SQLFreeStmt or SQLCloseCursor to close cursors.

When SQLCancel is called, diagnostic records are returned for a function running asynchronously in a statement or for a function on a statement that needs data; diagnostic records are not returned, however, for a function running on a statement on another thread.

Canceling Asynchronous Processing

After an application calls a function asynchronously, it calls the function repeatedly to determine whether it has finished processing. If the function is still processing, it returns SQL_STILL_EXECUTING. If the function has finished processing, it returns a different code.

After any call to the function that returns SQL_STILL_EXECUTING, an application can call SQLCancel to cancel the function. If the cancel request is successful, the driver returns SQL_SUCCESS. This message does not indicate that the function was actually canceled; it indicates that the cancel request was processed. When or if the function is actually canceled is driver-dependent and data source–dependent. The application must continue to call the original function until the return code is not SQL_STILL_EXECUTING. If the function was successfully canceled, the return code is SQL_ERROR and SQLSTATE HY008 (Operation canceled). If the function completed its normal processing, the return code is SQL_SUCCESS or SQL_SUCCESS_WITH_INFO if the function succeeded or SQL_ERROR and a SQLSTATE other than HY008 (Operation canceled) if the function failed.

Note In ODBC 3.5, a call to SQLCancel when no processing is being done on the statement is not treated as SQLFreeStmt with the SQL_CLOSE option, but has is no effect at all. To close a cursor, an application should call SQLCloseCursor, not SQLCancel.

For more information about asynchronous processing, see "Asynchronous Execution" in Chapter 9: Executing Statements.

Canceling Functions that Need Data

After SQLExecute or SQLExecDirect returns SQL_NEED_DATA and before data has been sent for all data-at-execution parameters, an application can call SQLCancel to cancel the statement execution. After the statement has been canceled, the application can call SQLExecute or SQLExecDirect again. For more information, see SQLBindParameter.

After SQLBulkOperations or SQLSetPos returns SQL_NEED_DATA and before data has been sent for all data-at-execution columns, an application can call SQLCancel to cancel the operation. After the operation has been canceled, the application can call SQLBulkOperations or SQLSetPos again; canceling does not affect the cursor state or the current cursor position. For more information, see SQLBulkOperations or SQLSetPos.

Canceling Functions in Multithread Applications

In a multithread application, the application can cancel a function that is running synchronously on a statement. To cancel the function, the application calls SQLCancel with the same statement handle as that used by the target function, but on a different thread. How the function is canceled depends on the driver and the operating system. As in canceling a function running asynchronously, the return code of the SQLCancel indicates only whether the driver processed the request successfully. Only SQL_SUCCESS or SQL_ERROR can be returned; no SQLSTATEs are returned. If the original function is canceled, it returns SQL_ERROR and SQLSTATE HY008 (Operation canceled).

If an SQL statement is being executed when SQLCancel is called on another thread to cancel the statement execution, it is possible for the execution to succeed and return SQL_SUCCESS while the cancel is also successful. In this case, the Driver Manager assumes that the cursor opened by the statement execution is closed by the cancel, so the application will not be able to use the cursor.

For more information about threading, see "Multithreading" in Chapter 17: Programming Considerations.

Related Functions

For information about | See
Binding a buffer to a parameter | SQLBindParameter
Performing bulk insert or update operations | SQLBulkOperations
Executing an SQL statement | SQLExecDirect
Executing a prepared SQL statement | SQLExecute
Freeing a statement handle | SQLFreeStmt
Obtaining a field of a diagnostic record or a field of the diagnostic header | SQLGetDiagField
Obtaining multiple fields of a diagnostic data structure | SQLGetDiagRec
Returning the next parameter to send data for | SQLParamData
Sending parameter data at execution time | SQLPutData
Positioning the cursor in a rowset, refreshing data in the rowset, or updating or deleting data in the result set | SQLSetPos


SQLCloseCursor

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLCloseCursor closes a cursor that has been opened on a statement and discards pending results.

Syntax

SQLRETURN SQLCloseCursor(
     SQLHSTMT     StatementHandle);

Arguments

StatementHandle
[Input]
Statement handle.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLCloseCursor returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLCloseCursor and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
24000 | Invalid cursor state | No cursor was open on the StatementHandle. (This is returned only by an ODBC 3.x driver.)
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY010 | Function sequence error | (DM) An asynchronously executing function was called for the StatementHandle and was still executing when this function was called. (DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

SQLCloseCursor returns SQLSTATE 24000 (Invalid cursor state) if no cursor is open. Calling SQLCloseCursor is equivalent to calling SQLFreeStmt with the SQL_CLOSE option, with the exception that SQLFreeStmt with SQL_CLOSE has no effect on the application if no cursor is open on the statement, while SQLCloseCursor returns SQLSTATE 24000 (Invalid cursor state).

Note If an ODBC 3.x application working with an ODBC 2.x driver calls SQLCloseCursor when no cursor is open, SQLSTATE 24000 (Invalid cursor state) is not returned, because the Driver Manager maps SQLCloseCursor to SQLFreeStmt with SQL_CLOSE.

For more information, see "Closing the Cursor" in Chapter 10: Retrieving Results (Basic).

Code Example

See SQLBrowseConnect and SQLConnect.

Related Functions

For information about | See
Canceling statement processing | SQLCancel
Freeing a handle | SQLFreeHandle
Processing multiple result sets | SQLMoreResults


SQLColAttribute

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLColAttribute returns descriptor information for a column in a result set. Descriptor information is returned as a character string, a 32-bit descriptor-dependent value, or an integer value.

Note For more information about what the Driver Manager maps this function to when an ODBC 3.x application is working with an ODBC 2.x driver, see "Mapping Replacement Functions for Backward Compatibility of Applications" in Chapter 17: Programming Considerations.

Syntax

SQLRETURN SQLColAttribute (
     SQLHSTMT     StatementHandle,
     SQLUSMALLINT     ColumnNumber,
     SQLUSMALLINT     FieldIdentifier,
     SQLPOINTER     CharacterAttributePtr,
     SQLSMALLINT     BufferLength,
     SQLSMALLINT *     StringLengthPtr,
     SQLPOINTER     NumericAttributePtr);

Arguments

StatementHandle
[Input]
Statement handle.
ColumnNumber
[Input]
The number of the record in the IRD from which the field value is to be retrieved. This argument corresponds to the column number of result data, ordered sequentially in increasing column order, starting at 1. Columns can be described in any order.

Column 0 can be specified in this argument, but all values except SQL_DESC_TYPE and SQL_DESC_OCTET_LENGTH will return undefined values.

FieldIdentifier
[Input]
The field in row ColumnNumber of the IRD that is to be returned (see "Comments").
CharacterAttributePtr
[Output]
Pointer to a buffer in which to return the value in the FieldIdentifier field of the ColumnNumber row of the IRD, if the field is a character string. Otherwise, the field is unused.
BufferLength
[Input]
If FieldIdentifier is an ODBC-defined field and CharacterAttributePtr points to a character string or binary buffer, this argument should be the length of *CharacterAttributePtr. If FieldIdentifier is an ODBC-defined field and *CharacterAttributePtr is an integer, this field is ignored. If the *CharacterAttributePtr is a Unicode string (when calling SQLColAttributeW), the BufferLength argument must be an even number. If FieldIdentifier is a driver-defined field, the application indicates the nature of the field to the Driver Manager by setting the BufferLength argument. BufferLength can have the following values:
  • If CharacterAttributePtr is a pointer to a pointer, BufferLength should have the value SQL_IS_POINTER.
  • If CharacterAttributePtr is a pointer to a character string, the BufferLength is the length of the buffer.
  • If CharacterAttributePtr is a pointer to a binary buffer, the application places the result of the SQL_LEN_BINARY_ATTR(length) macro in BufferLength. This places a negative value in BufferLength.
  • If CharacterAttributePtr is a pointer to a fixed-length data type, BufferLength must be one of the following: SQL_IS_INTEGER, SQL_IS_UNINTEGER, SQL_SMALLINT, or SQLUSMALLINT.
StringLengthPtr
[Output]
Pointer to a buffer in which to return the total number of bytes (excluding the null-termination byte for character data) available to return in *CharacterAttributePtr.

For character data, if the number of bytes available to return is greater than or equal to BufferLength, the descriptor information in *CharacterAttributePtr is truncated to BufferLength minus the length of a null-termination character and is null-terminated by the driver.

For all other types of data, the value of BufferLength is ignored and the driver assumes the size of *CharacterAttributePtr is 32 bits.

NumericAttributePtr
[Output]
Pointer to an integer buffer in which to return the value in the FieldIdentifier field of the ColumnNumber row of the IRD, if the field is a numeric descriptor type, such as SQL_DESC_COLUMN_LENGTH. Otherwise, the field is unused.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLColAttribute returns either SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLColAttribute and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | The buffer *CharacterAttributePtr was not large enough to return the entire string value, so the string value was truncated. The length of the untruncated string value is returned in *StringLengthPtr. (Function returns SQL_SUCCESS_WITH_INFO.)
07005 | Prepared statement not a cursor-specification | The statement associated with the StatementHandle did not return a result set and FieldIdentifier was not SQL_DESC_COUNT. There were no columns to describe.
07009 Invalid descriptor index (DM) The value specified for ColumnNumber was equal to 0, and the SQL_ATTR_USE_BOOKMARKS statement attribute was SQL_UB_OFF.

The value specified for the argument ColumnNumber was greater than the number of columns in the result set.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagField from the diagnostic data structure describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY010 Function sequence error (DM) The function was called prior to calling SQLPrepare, SQLExecDirect, or a catalog function for the StatementHandle.

(DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) *CharacterAttributePtr is a character string, and BufferLength was less than 0 but not equal to SQL_NTS.
HY091 | Invalid descriptor field identifier | The value specified for the argument FieldIdentifier was not one of the defined values and was not an implementation-defined value.
HYC00 | Driver not capable | The value specified for the argument FieldIdentifier was not supported by the driver.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

When called after SQLPrepare and before SQLExecute, SQLColAttribute can return any SQLSTATE that can be returned by SQLPrepare or SQLExecute, depending on when the data source evaluates the SQL statement associated with the StatementHandle.

For performance reasons, an application should not call SQLColAttribute before executing a statement.

Comments

For information about how applications use the information returned by SQLColAttribute, see "Result Set Metadata" in Chapter 10: Retrieving Results (Basic).

SQLColAttribute returns information either in *NumericAttributePtr or in *CharacterAttributePtr. Integer information is returned in *NumericAttributePtr as a 32-bit, signed value; all other formats of information are returned in *CharacterAttributePtr. When information is returned in *NumericAttributePtr, the driver ignores CharacterAttributePtr, BufferLength, and StringLengthPtr. When information is returned in *CharacterAttributePtr, the driver ignores NumericAttributePtr.

SQLColAttribute returns values from the descriptor fields of the IRD. The function is called with a statement handle rather than a descriptor handle. The values returned by SQLColAttribute for the FieldIdentifier values listed later in this section can also be retrieved by calling SQLGetDescField with the appropriate IRD handle.

The currently defined descriptor fields, the version of ODBC in which they were introduced, and the arguments in which information is returned for them are shown later in this section; more descriptor types may be defined by drivers to take advantage of different data sources.

An ODBC 3.x driver must return a value for each of the descriptor fields. If a descriptor field does not apply to a driver or data source and unless otherwise stated, the driver returns 0 in *StringLengthPtr or an empty string in *CharacterAttributePtr.

Backward Compatibility

The ODBC 3.x function SQLColAttribute replaces the deprecated ODBC 2.x function SQLColAttributes. When mapping SQLColAttributes to SQLColAttribute (when an ODBC 2.x application is working with an ODBC 3.x driver), or mapping SQLColAttribute to SQLColAttributes (when an ODBC 3.x application is working with an ODBC 2.x driver), the Driver Manager either passes the value of FieldIdentifier through, maps it to a new value, or returns an error, as follows:

Note The prefix used in FieldIdentifier values in ODBC 3.x has been changed from that used in ODBC 2.x. The new prefix is "SQL_DESC"; the old prefix was "SQL_COLUMN".

  • If the #define value of the ODBC 2.x FieldIdentifier is the same as the #define value of the ODBC 3.x FieldIdentifier, the value in the function call is just passed through.
  • The #define values of the ODBC 2.x FieldIdentifiers SQL_COLUMN_LENGTH, SQL_COLUMN_PRECISION, and SQL_COLUMN_SCALE are different from the #define values of the ODBC 3.x FieldIdentifiers SQL_DESC_PRECISION, SQL_DESC_SCALE, and SQL_DESC_LENGTH. An ODBC 2.x driver need only support the ODBC 2.x values. An ODBC 3.x driver must support both "SQL_COLUMN" and "SQL_DESC" values for these three FieldIdentifiers. These values are different because precision, scale, and length are defined differently in ODBC 3.x than they were in ODBC 2.x. For more information, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" in Appendix D: Data Types.
  • If the #define value of the ODBC 2.x FieldIdentifier is different from the #define value of the ODBC 3.x FieldIdentifier, as occurs with the COUNT, NAME, and NULLABLE values, the value in the function call is mapped to the corresponding value. For example, SQL_COLUMN_COUNT is mapped to SQL_DESC_COUNT, and SQL_DESC_COUNT is mapped to SQL_COLUMN_COUNT, depending on the direction of the mapping.
  • If FieldIdentifier is a new value in ODBC 3.x, for which there was no corresponding value in ODBC 2.x, it will not be mapped when an ODBC 3.x application uses it in a call to SQLColAttribute in an ODBC 2.x driver, and the call will return SQLSTATE HY091 (Invalid descriptor field identifier).

The following table lists the descriptor types returned by SQLColAttribute.

FieldIdentifier 		Information
returned in
Description
SQL_DESC_AUTO_UNIQUE_VALUE
(ODBC 1.0) 		NumericAttributePtr SQL_TRUE if the column is an autoincrementing column.

SQL_FALSE if the column is not an autoincrementing column or is not numeric.

This field is valid for numeric data type columns only. An application can insert values into a row containing an autoincrement column, but typically cannot update values in the column.

When an insert is made into an autoincrement column, a unique value is inserted into the column at insert time. The increment is not defined, but is data source–specific. An application should not assume that an autoincrement column starts at any particular point or increments by any particular value.
SQL_DESC_BASE_COLUMN_NAME
(ODBC 3.0) 		CharacterAttributePtr The base column name for the result set column. If a base column name does not exist (as in the case of columns that are expressions), then this variable contains an empty string.

This information is returned from the SQL_DESC_BASE_COLUMN_NAME record field of the IRD, which is a read-only field.
SQL_DESC_BASE_TABLE_NAME
(ODBC 3.0) 		CharacterAttributePtr The name of the base table that contains the column. If the base table name cannot be defined or is not applicable, then this variable contains an empty string.

This information is returned from the SQL_DESC_BASE_TABLE_NAME record field of the IRD, which is a read-only field.
SQL_DESC_CASE_SENSITIVE
(ODBC 1.0) 		NumericAttributePtr SQL_TRUE if the column is treated as case-sensitive for collations and comparisons.

SQL_FALSE if the column is not treated as case-sensitive for collations and comparisons or is noncharacter.
SQL_DESC_CATALOG_NAME
(ODBC 2.0) 		CharacterAttributePtr The catalog of the table that contains the column. The returned value is implementation-defined if the column is an expression or if the column is part of a view. If the data source does not support catalogs or the catalog name cannot be determined, an empty string is returned. This VARCHAR record field is not limited to 128 characters.
SQL_DESC_CONCISE_TYPE
(ODBC 1.0) 		NumericAttributePtr The concise data type.

For the datetime and interval data types, this field returns the concise data type; for example, SQL_TYPE_TIME or SQL_INTERVAL_YEAR. (For more information, see "Data Type Identifiers and Descriptors" in Appendix D: Data Types.)

This information is returned from the SQL_DESC_CONCISE_TYPE record field of the IRD.
SQL_DESC_COUNT
(ODBC 1.0) 		NumericAttributePtr The number of columns available in the result set. This returns 0 if there are no columns in the result set. The value in the ColumnNumber argument is ignored.

This information is returned from the SQL_DESC_COUNT header field of the IRD.
SQL_DESC_DISPLAY_SIZE
(ODBC 1.0) 		NumericAttributePtr Maximum number of characters required to display data from the column. For more information about display size, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" in Appendix D: Data Types.
SQL_DESC_FIXED_PREC_SCALE
(ODBC 1.0) 		NumericAttributePtr SQL_TRUE if the column has a fixed precision and nonzero scale that are data source–specific.

SQL_FALSE if the column does not have a fixed precision and nonzero scale that are data source–specific.
SQL_DESC_LABEL
(ODBC 2.0) 		CharacterAttributePtr The column label or title. For example, a column named EmpName might be labeled Employee Name or might be labeled with an alias.

If a column does not have a label, the column name is returned. If the column is unlabeled and unnamed, an empty string is returned.
SQL_DESC_LENGTH
(ODBC 3.0) 		NumericAttributePtr A numeric value that is either the maximum or actual character length of a character string or binary data type. It is the maximum character length for a fixed-length data type, or the actual character length for a variable-length data type. Its value always excludes the null-termination byte that ends the character string.

This information is returned from the SQL_DESC_LENGTH record field of the IRD.

For more information about length, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" in Appendix D: Data Types.
SQL_DESC_LITERAL_PREFIX
(ODBC 3.0) 		CharacterAttributePtr This VARCHAR(128) record field contains the character or characters that the driver recognizes as a prefix for a literal of this data type. This field contains an empty string for a data type for which a literal prefix is not applicable. For more information, see "Literal Prefixes and Suffixes" in Chapter 8: SQL Statements.
SQL_DESC_LITERAL_SUFFIX
(ODBC 3.0) 		CharacterAttributePtr This VARCHAR(128) record field contains the character or characters that the driver recognizes as a suffix for a literal of this data type. This field contains an empty string for a data type for which a literal suffix is not applicable. For more information, see "Literal Prefixes and Suffixes" in Chapter 8: SQL Statements.
SQL_DESC_LOCAL_TYPE_NAME
(ODBC 3.0) 		CharacterAttributePtr This VARCHAR(128) record field contains any localized (native language) name for the data type that may be different from the regular name of the data type. If there is no localized name, then an empty string is returned. This field is for display purposes only. The character set of the string is locale-dependent and is typically the default character set of the server.
SQL_DESC_NAME
(ODBC 3.0) 		CharacterAttributePtr The column alias, if it applies. If the column alias does not apply, the column name is returned. In either case, SQL_DESC_UNNAMED is set to SQL_NAMED. If there is no column name or a column alias, an empty string is returned and SQL_DESC_UNNAMED is set to SQL_UNNAMED.

This information is returned from the SQL_DESC_NAME record field of the IRD.
SQL_DESC_NULLABLE
(ODBC 3.0) 		NumericAttributePtr SQL_ NULLABLE if the column can have NULL values; SQL_NO_NULLS if the column does not have NULL values; or SQL_NULLABLE_UNKNOWN if it is not known whether the column accepts NULL values.

This information is returned from the SQL_DESC_NULLABLE record field of the IRD.
SQL_DESC_NUM_PREX_RADIX
(ODBC 3.0) 		NumericAttributePtr If the data type in the SQL_DESC_TYPE field is an approximate numeric data type, this SQLINTEGER field contains a value of 2 because the SQL_DESC_PRECISION field contains the number of bits. If the data type in the SQL_DESC_TYPE field is an exact numeric data type, this field contains a value of 10 because the SQL_DESC_PRECISION field contains the number of decimal digits. This field is set to 0 for all non-numeric data types.
SQL_DESC_OCTET_LENGTH
(ODBC 3.0) 		NumericAttributePtr The length, in bytes, of a character string or binary data type. For fixed-length character or binary types, this is the actual length in bytes. For variable-length character or binary types, this is the maximum length in bytes. This value includes the null terminator.

This information is returned from the SQL_DESC_OCTET_LENGTH record field of the IRD.

For more information about length, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" in Appendix D: Data Types.
SQL_DESC_PRECISION
(ODBC 3.0) 		NumericAttributePtr A numeric value that for a numeric data type denotes the applicable precision. For data types SQL_TYPE_TIME, SQL_TYPE_TIMESTAMP, and all the interval data types that represent a time interval, its value is the applicable precision of the fractional seconds component.

This information is returned from the SQL_DESC_PRECISION record field of the IRD.
SQL_DESC_SCALE
(ODBC 3.0) 		NumericAttributePtr A numeric value that is the applicable scale for a numeric data type. For DECIMAL and NUMERIC data types, this is the defined scale. It is undefined for all other data types.

This information is returned from the SCALE record field of the IRD.
SQL_DESC_SCHEMA_NAME
(ODBC 2.0) 		CharacterAttributePtr The schema of the table that contains the column. The returned value is implementation-defined if the column is an expression or if the column is part of a view. If the data source does not support schemas or the schema name cannot be determined, an empty string is returned. This VARCHAR record field is not limited to 128 characters.
SQL_DESC_SEARCHABLE
(ODBC 1.0) 		NumericAttributePtr SQL_PRED_NONE if the column cannot be used in a WHERE clause. (This is the same as the SQL_UNSEARCHABLE value in ODBC 2.x.)

SQL_PRED_CHAR if the column can be used in a WHERE clause but only with the LIKE predicate. (This is the same as the SQL_LIKE_ONLY value in ODBC 2.x.)

SQL_PRED_BASIC if the column can be used in a WHERE clause with all the comparison operators except LIKE. (This is the same as the SQL_EXCEPT_LIKE value in ODBC 2.x.)

SQL_PRED_SEARCHABLE if the column can be used in a WHERE clause with any comparison operator.

Columns of type SQL_LONGVARCHAR and SQL_LONGVARBINARY usually return SQL_PRED_CHAR.
SQL_DESC_TABLE_NAME
(ODBC 2.0) 		CharacterAttributePtr The name of the table that contains the column. The returned value is implementation-defined if the column is an expression or if the column is part of a view.

If the table name cannot be determined, an empty string is returned.
SQL_DESC_TYPE
(ODBC 3.0) 		NumericAttributePtr A numeric value that specifies the SQL data type.

When ColumnNumber is equal to 0, SQL_BINARY is returned for variable-length bookmarks and SQL_INTEGER is returned for fixed-length bookmarks.

For the datetime and interval data types, this field returns the verbose data type: SQL_DATETIME or SQL_INTERVAL. (For more information, see "Data Type Identifiers and Descriptors" in Appendix D: Data Types.

This information is returned from the SQL_DESC_TYPE record field of the IRD.

Note To work against ODBC 2.x drivers, use SQL_DESC_CONCISE_TYPE instead.
SQL_DESC_TYPE_NAME
(ODBC 1.0) 		CharacterAttributePtr Data source–dependent data type name; for example, "CHAR", "VARCHAR", "MONEY", "LONG VARBINARY", or "CHAR ( ) FOR BIT DATA".

If the type is unknown, an empty string is returned.
SQL_DESC_UNNAMED
(ODBC 3.0) 		NumericAttributePtr SQL_NAMED or SQL_UNNAMED. If the SQL_DESC_NAME field of the IRD contains a column alias or a column name, SQL_NAMED is returned. If there is no column name or column alias, SQL_UNNAMED is returned.

This information is returned from the SQL_DESC_UNNAMED record field of the IRD.
SQL_DESC_UNSIGNED
(ODBC 1.0) 		NumericAttributePtr SQL_TRUE if the column is unsigned (or not numeric).

SQL_FALSE if the column is signed.
SQL_DESC_UPDATABLE
(ODBC 1.0) 		NumericAttributePtr Column is described by the values for the defined constants:

SQL_ATTR_READONLY
SQL_ATTR_WRITE
SQL_ATTR_READWRITE_UNKNOWN

SQL_DESC_UPDATABLE describes the updatability of the column in the result set, not the column in the base table. The updatability of the base column on which the result set column is based may be different from the value in this field. Whether a column is updatable can be based on the data type, user privileges, and the definition of the result set itself. If it is unclear whether a column is updatable, SQL_ATTR_READWRITE_UNKNOWN should be returned.

SQLColAttribute is an extensible alternative to SQLDescribeCol. SQLDescribeCol returns a fixed set of descriptor information based on ANSI-89 SQL. SQLColAttribute allows access to the more extensive set of descriptor information available in ANSI SQL-92 and DBMS vendor extensions.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Returning information about a column in a result set | SQLDescribeCol
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Fetching multiple rows of data | SQLFetch


SQLColAttributes

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

In ODBC 3.x, the ODBC 2.0 function SQLColAttributes has been replaced by SQLColAttribute. For more information, see SQLColAttribute.

Note For more information about what the Driver Manager maps this function to when an ODBC 2.x application is working with an ODBC 3.x driver, see "Mapping Deprecated Functions" in Appendix G: Driver Guidelines for Backward Compatibility.


SQLColumnPrivileges

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ODBC

Summary

SQLColumnPrivileges returns a list of columns and associated privileges for the specified table. The driver returns the information as a result set on the specified StatementHandle.

Syntax

SQLRETURN SQLColumnPrivileges(
     SQLHSTMT     StatementHandle,
     SQLCHAR *     CatalogName,
     SQLSMALLINT     NameLength1,
     SQLCHAR *     SchemaName,
     SQLSMALLINT     NameLength2,
     SQLCHAR *     TableName,
     SQLSMALLINT     NameLength3,
     SQLCHAR *     ColumnName,
     SQLSMALLINT     NameLength4);

Arguments

StatementHandle
[Input]
Statement handle.
CatalogName
[Input]
Catalog name. If a driver supports names for some catalogs but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those catalogs that do not have names. CatalogName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, CatalogName is treated as an identifier and its case is not significant. If it is SQL_FALSE, CatalogName is an ordinary argument; it is treated literally, and its case is significant. For more information, see "Arguments in Catalog Functions" in Chapter 7: Catalog Functions.

NameLength1
[Input]
Length of *CatalogName.
SchemaName
[Input]
Schema name. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have schemas. SchemaName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, SchemaName is treated as an identifier. If it is SQL_FALSE, SchemaName is an ordinary argument; it is treated literally, and its case is significant.

NameLength2
[Input]
Length of *SchemaName.
TableName
[Input]
Table name. This argument cannot be a null pointer. TableName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, TableName is treated as an identifier and its case is not significant. If it is SQL_FALSE, TableName is an ordinary argument; it is treated literally, and its case is significant.

NameLength3
[Input]
Length of *TableName.
ColumnName
[Input]
String search pattern for column names.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, ColumnName is treated as an identifier and its case is not significant. If it is SQL_FALSE, ColumnName is a pattern value argument; it is treated literally, and its case is significant.

NameLength4
[Input]
Length of *ColumnName.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLColumnPrivileges returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLColumnPrivileges and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
24000 Invalid cursor state A cursor was open on the StatementHandle, and SQLFetch or SQLFetchScroll had been called. This error is returned by the Driver Manager if SQLFetch or SQLFetchScroll has not returned SQL_NO_DATA, and is returned by the driver if SQLFetch or SQLFetchScroll has returned SQL_NO_DATA.

A cursor was open on the StatementHandle, but SQLFetch or SQLFetchScroll had not been called.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 Memory allocation
error 		The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY009 Invalid use of null pointer The TableName argument was a null pointer.

The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, the CatalogName argument was a null pointer, and the SQL_CATALOG_NAME InfoType returns that catalog names are supported.

(DM) The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, and the SchemaName or ColumnName argument was a null pointer.

HY010 Function sequence error (DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) The value of one of the name length arguments was less than 0 but not equal to SQL_NTS.
The value of one of the name length arguments exceeded the maximum length value for the corresponding name. (See "Comments.")
HYC00 Optional feature not implemented A catalog name was specified, and the driver or data source does not support catalogs.

A schema name was specified, and the driver or data source does not support schemas.

A string search pattern was specified for the column name, and the data source does not support search patterns for that argument.

The combination of the current settings of the SQL_CONCURRENCY and SQL_CURSOR_TYPE statement attributes was not supported by the driver or data source.

The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.
HYT00 | Timeout expired | The query timeout period expired before the data source returned the result set. The timeout period is set through SQLSetStmtAttr, SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

SQLColumnPrivileges returns the results as a standard result set, ordered by TABLE_CAT, TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, and PRIVILEGE.

Note SQLColumnPrivileges might not return privileges for all columns. For example, a driver might not return information about privileges for pseudo-columns, such as Oracle ROWID. Applications can use any valid column, regardless of whether it is returned by SQLColumnPrivileges.

The lengths of VARCHAR columns are not shown in the table; the actual lengths depend on the data source. To determine the actual lengths of the CATALOG_NAME, SCHEMA_NAME, TABLE_NAME, and COLUMN_NAME columns, an application can call SQLGetInfo with the SQL_MAX_CATALOG_NAME_LEN, SQL_MAX_SCHEMA_NAME_LEN, SQL_MAX_TABLE_NAME_LEN, and SQL_MAX_COLUMN_NAME_LEN options.

Note For more information about the general use, arguments, and returned data of ODBC catalog functions, see Chapter 7: Catalog Functions.

The following columns have been renamed for ODBC 3.x. The column name changes do not affect backward compatibility because applications bind by column number.

ODBC 2.0 column | ODBC 3.x column
TABLE_QUALIFIER | TABLE_CAT
TABLE_OWNER | TABLE_SCHEM

The following table lists the columns in the result set. Additional columns beyond column 8 (IS_GRANTABLE) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "Data Returned by Catalog Functions" in Chapter 7: Catalog Functions.

Column name 		Column number
Data type
Comments
TABLE_CAT
(ODBC 1.0) 		1 Varchar Catalog identifier; NULL if not applicable to the data source. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have catalogs.
TABLE_SCHEM
(ODBC 1.0) 		2 Varchar Schema identifier; NULL if not applicable to the data source. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have schemas.
TABLE_NAME
(ODBC 1.0) 		3 Varchar
not NULL 		Table identifier.
COLUMN_NAME
(ODBC 1.0) 		4 Varchar
not NULL 		Column name. The driver returns an empty string for a column that does not have a name.
GRANTOR
(ODBC 1.0) 		5 Varchar Name of the user who granted the privilege; NULL if not applicable to the data source.

For all rows in which the value in the GRANTEE column is the owner of the object, the GRANTOR column will be "_SYSTEM".
GRANTEE
(ODBC 1.0) 		6 Varchar
not NULL 		Name of the user to whom the privilege was granted.
PRIVILEGE
(ODBC 1.0) 		7 Varchar
not NULL 		Identifies the column privilege. May be one of the following (or others supported by the data source when implementation-defined):

SELECT: The grantee is permitted to retrieve data for the column.

INSERT: The grantee is permitted to provide data for the column in new rows that are inserted into the associated table.

UPDATE: The grantee is permitted to update data in the column.

REFERENCES: The grantee is permitted to refer to the column within a constraint (for example, a unique, referential, or table check constraint).
IS_GRANTABLE
(ODBC 1.0) 		8 Varchar Indicates whether the grantee is permitted to grant the privilege to other users; "YES", "NO", or "NULL" if unknown or not applicable to the data source.

A privilege is either grantable or not grantable, but not both. The result set returned by SQLColumnPrivileges will never contain two rows for which all columns except the IS_GRANTABLE column contain the same value.

Code Example

For a code example of a similar function, see SQLColumns.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Returning the columns in a table or tables | SQLColumns
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Fetching multiple rows of data | SQLFetch
Returning privileges for a table or tables | SQLTablePrivileges
Returning a list of tables in a data source | SQLTables


SQLColumns

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: X/Open

Summary

SQLColumns returns the list of column names in specified tables. The driver returns this information as a result set on the specified StatementHandle.

Syntax

SQLRETURN SQLColumns(
     SQLHSTMT     StatementHandle,
     SQLCHAR *     CatalogName,
     SQLSMALLINT     NameLength1,
     SQLCHAR *     SchemaName,
     SQLSMALLINT     NameLength2,
     SQLCHAR *     TableName,
     SQLSMALLINT     NameLength3,
     SQLCHAR *     ColumnName,
     SQLSMALLINT     NameLength4);

Arguments

StatementHandle
[Input]
Statement handle.
CatalogName
[Input]
Catalog name. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have catalogs. CatalogName cannot contain a string search pattern.
If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, CatalogName is treated as an identifier and its case is not significant. If it is SQL_FALSE, CatalogName is an ordinary argument; it is treated literally, and its case is significant. For more information, see "Arguments in Catalog Functions" in Chapter 7: Catalog Functions.
NameLength1
[Input]
Length of *CatalogName.
SchemaName
[Input]
String search pattern for schema names. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have schemas.
If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, SchemaName is treated as an identifier and its case is not significant. If it is SQL_FALSE, SchemaName is a pattern value argument; it is treated literally, and its case is significant.
NameLength2
[Input]
Length of *SchemaName.
TableName
[Input]
String search pattern for table names.
If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, TableName is treated as an identifier and its case is not significant. If it is SQL_FALSE, TableName is a pattern value argument; it is treated literally, and its case is significant.
NameLength3
[Input]
Length of *TableName.
ColumnName
[Input]
String search pattern for column names.
If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, ColumnName is treated as an identifier and its case is not significant. If it is SQL_FALSE, ColumnName is a pattern value argument; it is treated literally, and its case is significant.
NameLength4
[Input]
Length of *ColumnName.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLColumns returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLColumns and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
24000 Invalid cursor state A cursor was open on the StatementHandle, and SQLFetch or SQLFetchScroll had been called. This error is returned by the Driver Manager if SQLFetch or SQLFetchScroll has not returned SQL_NO_DATA, and is returned by the driver if SQLFetch or SQLFetchScroll has returned SQL_NO_DATA.

A cursor was open on the StatementHandle but SQLFetch or SQLFetchScroll had not been called.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY009 Invalid use of null pointer The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, the CatalogName argument was a null pointer, and the SQL_CATALOG_NAME InfoType returns that catalog names are supported.

(DM) The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, and the SchemaName, TableName, or ColumnName argument was a null pointer.
HY010 Function sequence error (DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) The value of one of the name length arguments was less than 0 but not equal to SQL_NTS.
The value of one of the name length arguments exceeded the maximum length value for the corresponding catalog or name. The maximum length of each catalog or name may be obtained by calling SQLGetInfo with the InfoType values. (See "Comments.")
HYC00 Optional feature not implemented A catalog name was specified, and the driver or data source does not support catalogs.

A schema name was specified, and the driver or data source does not support schemas.

A string search pattern was specified for the schema name, table name, or column name, and the data source does not support search patterns for one or more of those arguments.

The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source.

The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.
HYT00 | Timeout expired | The query timeout period expired before the data source returned the result set. The timeout period is set through SQLSetStmtAttr, SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

This function typically is used before statement execution to retrieve information about columns for a table or tables from the data source's catalog. SQLColumns can be used to retrieve data for all types of items returned by SQLTables. In addition to base tables, this may include (but is not limited to) views, synonyms, system tables, and so on. By contrast, the functions SQLColAttribute and SQLDescribeCol describe the columns in a result set and the function SQLNumResultCols returns the number of columns in a result set. For more information, see "Uses of Catalog Data" in Chapter 7: Catalog Functions.

Note For more information about the general use, arguments, and returned data of ODBC catalog functions, see Chapter 7: Catalog Functions.

SQLColumns returns the results as a standard result set, ordered by TABLE_CAT, TABLE_SCHEM, TABLE_NAME, and ORDINAL_POSITION.

Note When an application works with an ODBC 2.x driver, no ORDINAL_POSITION column is returned in the result set. As a result, when working with ODBC 2.x drivers, the order of the columns in the column list returned by SQLColumns is not necessarily the same as the order of the columns returned when the application performs a SELECT statement on all columns in that table.

Note SQLColumns might not return all columns. For example, a driver might not return information about pseudo-columns, such as Oracle ROWID. Applications can use any valid column, whether or not it is returned by SQLColumns.

Some columns that can be returned by SQLStatistics are not returned by SQLColumns. For example, SQLColumns does not return the columns in an index created over an expression or filter, such as SALARY + BENEFITS or DEPT = 0012.

The lengths of VARCHAR columns are not shown in the table; the actual lengths depend on the data source. To determine the actual lengths of the TABLE_CAT, TABLE_SCHEM, TABLE_NAME, and COLUMN_NAME columns, an application can call SQLGetInfo with the SQL_MAX_CATALOG_NAME_LEN, SQL_MAX_SCHEMA_NAME_LEN, SQL_MAX_TABLE_NAME_LEN, and SQL_MAX_COLUMN_NAME_LEN options.

The following columns have been renamed for ODBC 3.x. The column name changes do not affect backward compatibility because applications bind by column number.

ODBC 2.0 column | ODBC 3.x column
TABLE_QUALIFIER | TABLE_CAT
TABLE_OWNER | TABLE_SCHEM
PRECISION | COLUMN_SIZE
LENGTH | BUFFER_LENGTH
SCALE | DECIMAL_DIGITS
RADIX | NUM_PREC_RADIX

The following columns have been added to the result set returned by SQLColumns for ODBC 3.x:

CHAR_OCTET_LENGTH | ORDINAL_POSITION
COLUMN_DEF | SQL_DATA_TYPE
IS_NULLABLE | SQL_DATETIME_SUB

The following table lists the columns in the result set. Additional columns beyond column 18 (IS_NULLABLE) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "Data Returned by Catalog Functions" in Chapter 7: Catalog Functions.

Column name 		Column
number
Data type
Comments
TABLE_CAT
(ODBC 1.0) 		1 Varchar Catalog name; NULL if not applicable to the data source. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have catalogs.
TABLE_SCHEM
(ODBC 1.0) 		2 Varchar Schema name; NULL if not applicable to the data source. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have schemas.
TABLE_NAME
(ODBC 1.0) 		3 Varchar not NULL Table name.
COLUMN_NAME
(ODBC 1.0) 		4 Varchar not NULL Column name. The driver returns an empty string for a column that does not have a name.
DATA_TYPE
(ODBC 1.0) 		5 Smallint not NULL SQL data type. This can be an ODBC SQL data type or a driver-specific SQL data type. For datetime and interval data types, this column returns the concise data type (such as SQL_TYPE_DATE or SQL_INTERVAL_YEAR_TO_MONTH, rather than the nonconcise data type such as SQL_DATETIME or SQL_INTERVAL). For a list of valid ODBC SQL data types, see "SQL Data Types" in Appendix D: Data Types. For information about driver-specific SQL data types, see the driver's documentation.

The data types returned for ODBC 3.x and ODBC 2.x applications may be different. For more information, see "Backward Compatibility and Standards Compliance" in Chapter 17: Programming Considerations.
TYPE_NAME
(ODBC 1.0) 		6 Varchar not NULL Data source–dependent data type name; for example, "CHAR", "VARCHAR", "MONEY", "LONG VARBINAR", or "CHAR ( ) FOR BIT DATA".
COLUMN_SIZE
(ODBC 1.0) 		7 Integer If DATA_TYPE is SQL_CHAR or SQL_VARCHAR, this column contains the maximum length in characters of the column. For datetime data types, this is the total number of characters required to display the value when converted to characters. For numeric data types, this is either the total number of digits or the total number of bits allowed in the column, according to the NUM_PREC_RADIX column. For interval data types, this is the number of characters in the character representation of the interval literal (as defined by the interval leading precision, see "Interval Data Type Length" in Appendix D: Data Types). For more information, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" in Appendix D: Data Types.
BUFFER_LENGTH
(ODBC 1.0) 		8 Integer The length in bytes of data transferred on an SQLGetData, SQLFetch, or SQLFetchScroll operation if SQL_C_DEFAULT is specified. For numeric data, this size may be different than the size of the data stored on the data source. This value might be different than COLUMN_SIZE column for character data. For more information about length, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" in Appendix D: Data Types.
DECIMAL_DIGITS
(ODBC 1.0) 		9 Smallint The total number of significant digits to the right of the decimal point. For SQL_TYPE_TIME and SQL_TYPE_TIMESTAMP, this column contains the number of digits in the fractional seconds component. For the other data types, this is the decimal digits of the column on the data source. For interval data types that contain a time component, this column contains the number of digits to the right of the decimal point (fractional seconds). For interval data types that do not contain a time component, this column is 0. For more information about decimal digits, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" in Appendix D: Data Types. NULL is returned for data types where DECIMAL_DIGITS is not applicable.
NUM_PREC_RADIX
(ODBC 1.0) 		10 Smallint For numeric data types, either 10 or 2. If it is 10, the values in COLUMN_SIZE and DECIMAL_DIGITS give the number of decimal digits allowed for the column. For example, a DECIMAL(12,5) column would return a NUM_PREC_RADIX of 10, a COLUMN_SIZE of 12, and a DECIMAL_DIGITS of 5; a FLOAT column could return a NUM_PREC_RADIX of 10, a COLUMN_SIZE of 15, and a DECIMAL_DIGITS of NULL.

If it is 2, the values in COLUMN_SIZE and DECIMAL_DIGITS give the number of bits allowed in the column. For example, a FLOAT column could return a RADIX of 2, a COLUMN_SIZE of 53, and a DECIMAL_DIGITS of NULL.

NULL is returned for data types where NUM_PREC_RADIX is not applicable.
NULLABLE
(ODBC 1.0) 		11 Smallint not NULL SQL_NO_NULLS if the column could not include NULL values.

SQL_NULLABLE if the column accepts NULL values.

SQL_NULLABLE_UNKNOWN if it is not known whether the column accepts NULL values.

The value returned for this column is different from the value returned for the IS_NULLABLE column. The NULLABLE column indicates with certainty that a column can accept NULLs, but cannot indicate with certainty that a column does not accept NULLs. The IS_NULLABLE column indicates with certainty that a column cannot accept NULLs, but cannot indicate with certainty that a column accepts NULLs.
REMARKS
(ODBC 1.0) 		12 Varchar A description of the column.
COLUMN_DEF
(ODBC 3.0) 		13 Varchar The default value of the column. The value in this column should be interpreted as a string if it is enclosed in quotation marks.

If NULL was specified as the default value, then this column is the word NULL, not enclosed in quotation marks. If the default value cannot be represented without truncation, then this column contains TRUNCATED, with no enclosing single quotation marks. If no default value was specified, then this column is NULL.

The value of COLUMN_DEF can be used in generating a new column definition, except when it contains the value TRUNCATED.
SQL_DATA_TYPE
(ODBC 3.0) 		14 Smallint not NULL SQL data type, as it appears in the SQL_DESC_TYPE record field in the IRD. This can be an ODBC SQL data type or a driver-specific SQL data type. This column is the same as the DATA_TYPE column, with the exception of datetime and interval data types. This column returns the nonconcise data type (such as SQL_DATETIME or SQL_INTERVAL), rather than the concise data type (such as SQL_TYPE_DATE or SQL_INTERVAL_YEAR_TO_MONTH) for datetime and interval data types. If this column returns SQL_DATETIME or SQL_INTERVAL, the specific data type can be determined from the SQL_DATETIME_SUB column. For a list of valid ODBC SQL data types, see "SQL Data Types" in Appendix D: Data Types. For information about driver-specific SQL data types, see the driver's documentation.

The data types returned for ODBC 3.x and ODBC 2.x applications may be different. For more information, see "Backward Compatibility and Standards Compliance" in Chapter 17: Programming Considerations.
SQL_DATETIME_SUB
(ODBC 3.0) 		15 Smallint The subtype code for datetime and interval data types. For other data types, this column returns a NULL. For more information about datetime and interval subcodes, see "SQL_DESC_DATETIME_INTERVAL_CODE" in SQLSetDescField.
CHAR_OCTET_LENGTH
(ODBC 3.0) 		16 Integer The maximum length in bytes of a character or binary data type column. For all other data types, this column returns a NULL.
ORDINAL_POSITION
(ODBC 3.0) 		17 Integer not NULL The ordinal position of the column in the table. The first column in the table is number 1.
IS_NULLABLE
(ODBC 3.0) 		18 Varchar "NO" if the column does not include NULLs.

"YES" if the column could include NULLs.

This column returns a zero-length string if nullability is unknown.

ISO rules are followed to determine nullability. An ISO SQL–compliant DBMS cannot return an empty string.

The value returned for this column is different from the value returned for the NULLABLE column. (See the description of the NULLABLE column.)

Code Example

In the following example, an application declares buffers for the result set returned by SQLColumns. It calls SQLColumns to return a result set that describes each column in the EMPLOYEE table. It then calls SQLBindCol to bind the columns in the result set to the buffers. Finally, the application fetches each row of data with SQLFetch and processes it.

#define STR_LEN 128+1
#define REM_LEN 254+1

/* Declare buffers for result set data */

SQLCHAR       szCatalog[STR_LEN], szSchema[STR_LEN];
SQLCHAR       szTableName[STR_LEN], szColumnName[STR_LEN];
SQLCHAR       szTypeName[STR_LEN], szRemarks[REM_LEN];
SQLCHAR       szColumnDefault[STR_LEN], szIsNullable[STR_LEN];
SQLINTEGER    ColumnSize, BufferLength, CharOctetLength, OrdinalPosition;
SQLSMALLINT   DataType, DecimalDigits, NumPrecRadix, Nullable;
SQLSMALLINT   SQLDataType, DatetimeSubtypeCode;
SQLRETURN     retcode;
SQLHSTMT      hstmt;

/* Declare buffers for bytes available to return */

SQLINTEGER cbCatalog, cbSchema, cbTableName, cbColumnName;
SQLINTEGER cbDataType, cbTypeName, cbColumnSize, cbBufferLength;
SQLINTEGER cbDecimalDigits, cbNumPrecRadix, cbNullable, cbRemarks;
SQLINTEGER cbColumnDefault, cbSQLDataType, cbDatetimeSubtypeCode, cbCharOctetLength;
SQLINTEGER cbOrdinalPosition, cbIsNullable;

retcode = SQLColumns(hstmt,
         NULL, 0,                /* All catalogs */
         NULL, 0,                /* All schemas */
         "CUSTOMERS", SQL_NTS,   /* CUSTOMERS table */
         NULL, 0);               /* All columns */

if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {

   /* Bind columns in result set to buffers */

   SQLBindCol(hstmt, 1, SQL_C_CHAR, szCatalog, STR_LEN,&cbCatalog);
   SQLBindCol(hstmt, 2, SQL_C_CHAR, szSchema, STR_LEN, &cbSchema);
   SQLBindCol(hstmt, 3, SQL_C_CHAR, szTableName, STR_LEN,&cbTableName);
   SQLBindCol(hstmt, 4, SQL_C_CHAR, szColumnName, STR_LEN, &cbColumnName);
   SQLBindCol(hstmt, 5, SQL_C_SSHORT, &DataType, 0, &cbDataType);
   SQLBindCol(hstmt, 6, SQL_C_CHAR, szTypeName, STR_LEN, &cbTypeName);
   SQLBindCol(hstmt, 7, SQL_C_SLONG, &ColumnSize, 0, &cbColumnSize);
   SQLBindCol(hstmt, 8, SQL_C_SLONG, &BufferLength, 0, &cbBufferLength);
   SQLBindCol(hstmt, 9, SQL_C_SSHORT, &DecimalDigits, 0, &cbDecimalDigits);
   SQLBindCol(hstmt, 10, SQL_C_SSHORT, &NumPrecRadix, 0, &cbNumPrecRadix);
   SQLBindCol(hstmt, 11, SQL_C_SSHORT, &Nullable, 0, &cbNullable);
   SQLBindCol(hstmt, 12, SQL_C_CHAR, szRemarks, REM_LEN, &cbRemarks);
   SQLBindCol(hstmt, 13, SQL_C_CHAR, szColumnDefault, STR_LEN, &cbColumnDefault);
SQLBindCol(hstmt, 14, SQL_C_SSHORT, &SQLDataType, 0, &cbSQLDataType);
   SQLBindCol(hstmt, 15, SQL_C_SSHORT, &DatetimeSubtypeCode, 0,
      &cbDatetimeSubtypeCode);
   SQLBindCol(hstmt, 16, SQL_C_SLONG, &CharOctetLength, 0, &cbCharOctetLength);
   SQLBindCol(hstmt, 17, SQL_C_SLONG, &OrdinalPosition, 0, &cbOrdinalPosition);
   SQLBindCol(hstmt, 18, SQL_C_CHAR, szIsNullable, STR_LEN, &cbIsNullable);
   while(TRUE) {
      retcode = SQLFetch(hstmt);
      if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
         show_error( );
      }
      if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){
            ;   /* Process fetched data */
      } else {
         break;
      }
   }
}

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Returning privileges for a column or columns | SQLColumnPrivileges
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Fetching multiple rows of data | SQLFetch
Returning columns that uniquely identify a row, or columns automatically updated by a transaction | SQLSpecialColumns
Returning table statistics and indexes | SQLStatistics
Returning a list of tables in a data source | SQLTables
Returning privileges for a table or tables | SQLTablePrivileges


SQLConnect

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLConnect establishes connections to a driver and a data source. The connection handle references storage of all information about the connection to the data source, including status, transaction state, and error information.

Syntax

SQLRETURN SQLConnect(
     SQLHDBC     ConnectionHandle,
     SQLCHAR *     ServerName,
     SQLSMALLINT     NameLength1,
     SQLCHAR *     UserName,
     SQLSMALLINT     NameLength2,
     SQLCHAR *     Authentication,
     SQLSMALLINT     NameLength3);

Arguments

ConnectionHandle
[Input]
Connection handle.
ServerName
[Input]
Data source name. For information about how an application chooses a data source, see "Choosing a Data Source or Driver" in Chapter 6: Connecting to a Data Source or Driver.
NameLength1
[Input]
Length of *ServerName.
UserName
[Input]
User identifier.
NameLength2
[Input]
Length of *UserName.
Authentication
[Input]
Authentication string (typically the password).
NameLength3
[Input]
Length of *Authentication.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLConnect returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_DBC and a Handle of ConnectionHandle. The following table lists the SQLSTATE values commonly returned by SQLConnect and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01S02 | Option value changed | The driver did not support the specified value of the ValuePtr argument in SQLSetConnectAttr and substituted a similar value. (Function returns SQL_SUCCESS_WITH_INFO.)
08001 | Client unable to establish connection | The driver was unable to establish a connection with the data source.
08002 | Connection name in use | (DM) The specified ConnectionHandle had already been used to establish a connection with a data source, and the connection was still open or the user was browsing for a connection.
08004 | Server rejected the connection | The data source rejected the establishment of the connection for implementation-defined reasons.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was attempting to connect failed before the function completed processing.
28000 | Invalid authorization specification | The value specified for the argument UserName or the value specified for the argument Authentication violated restrictions defined by the data source.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | (DM) The Driver Manager was unable to allocate memory required to support execution or completion of the function. The driver was unable to allocate memory required to support execution or completion of the function.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) The value specified for argument NameLength1, NameLength2, or NameLength3 was less than 0 but not equal to SQL_NTS. (DM) The value specified for argument NameLength1 exceeded the maximum length for a data source name.
HYT00 | Timeout expired | The query timeout period expired before the connection to the data source completed. The timeout period is set through SQLSetConnectAttr, SQL_ATTR_LOGIN_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver specified by the data source name does not support the function.
IM002 | Data source not found and no default driver specified | (DM) The data source name specified in the argument ServerName was not found in the system information, nor was there a default driver specification.
IM003 | Specified driver could not be connected to | (DM) The driver listed in the data source specification in system information was not found or could not be connected to for some other reason.
IM004 | Driver's SQLAllocHandle on SQL_HANDLE_ENV failed | (DM) During SQLConnect, the Driver Manager called the driver's SQLAllocHandle function with a HandleType of SQL_HANDLE_ENV and the driver returned an error.
IM005 | Driver's SQLAllocHandle on SQL_HANDLE_DBC failed | (DM) During SQLConnect, the Driver Manager called the driver's SQLAllocHandle function with a HandleType of SQL_HANDLE_DBC and the driver returned an error.
IM006 | Driver's SQLSetConnectAttr failed | During SQLConnect, the Driver Manager called the driver's SQLSetConnectAttr function and the driver returned an error. (Function returns SQL_SUCCESS_WITH_INFO.)
IM009 | Unable to connect to translation DLL | The driver was unable to connect to the translation DLL that was specified for the data source.
IM010 | Data source name too long | (DM) *ServerName was longer than SQL_MAX_DSN_LENGTH characters.

Comments

For information about why an application uses SQLConnect, see "Connecting with SQLConnect" in Chapter 6: Connecting to a Data Source or Driver.

The Driver Manager does not connect to a driver until the application calls a function (SQLConnect, SQLDriverConnect, or SQLBrowseConnect) to connect to the driver. Until that point, the Driver Manager works with its own handles and manages connection information. When the application calls a connection function, the Driver Manager checks whether a driver is currently connected to for the specified ConnectionHandle:

  • If a driver is not connected to, the Driver Manager connects to the driver and calls SQLAllocHandle with a HandleType of SQL_HANDLE_ENV, SQLAllocHandle with a HandleType of SQL_HANDLE_DBC, SQLSetConnectAttr (if the application specified any connection attributes), and the connection function in the driver. The Driver Manager returns SQLSTATE IM006 (Driver's SQLSetConnectOption failed) and SQL_SUCCESS_WITH_INFO for the connection function if the driver returned an error for SQLSetConnectAttr. For more information, see Chapter 6: Connecting to a Data Source or Driver.
  • If the specified driver is already connected to on the ConnectionHandle, the Driver Manager calls only the connection function in the driver. In this case, the driver must make sure that all connection attributes for the ConnectionHandle maintain their current settings.
  • If a different driver is connected to, the Driver Manager calls SQLFreeHandle with a HandleType of SQL_HANDLE_DBC, and then, if no other driver is connected to in that environment, it calls SQLFreeHandle with a HandleType of SQL_HANDLE_ENV in the connected driver and then disconnects that driver. It then performs the same operations as when a driver is not connected to.

The driver then allocates handles and initializes itself.

When the application calls SQLDisconnect, the Driver Manager calls SQLDisconnect in the driver. However, it does not disconnect the driver. This keeps the driver in memory for applications that repeatedly connect to and disconnect from a data source. When the application calls SQLFreeHandle with a HandleType of SQL_HANDLE_DBC, the Driver Manager calls SQLFreeHandle with a HandleType of SQL_HANDLE_DBC and then SQLFreeHandle with a HandleType of SQL_HANDLE_ENV in the driver, and then disconnects the driver.

An ODBC application can establish more than one connection.

Driver Manager Guidelines

The contents of *ServerName affect how the Driver Manager and a driver work together to establish a connection to a data source.

  • If *ServerName contains a valid data source name, the Driver Manager locates the corresponding data source specification in the system information and connects to the associated driver. The Driver Manager passes each SQLConnect argument to the driver.
  • If the data source name cannot be found or ServerName is a null pointer, the Driver Manager locates the default data source specification and connects to the associated driver. The Driver Manager passes to the driver the UserName and Authentication arguments unmodified, and "DEFAULT" for the ServerName argument.
  • If the ServerName argument is "DEFAULT", the Driver Manager locates the default data source specification and connects to the associated driver. The Driver Manager passes each SQLConnect argument to the driver.
  • If the data source name cannot be found or ServerName is a null pointer, and the default data source specification does not exist, the Driver Manager returns SQL_ERROR with SQLSTATE IM002 (Data source name not found and no default driver specified).

After being connected to by the Driver Manager, a driver can locate its corresponding data source specification in the system information and use driver-specific information from the specification to complete its set of required connection information.

If a default translation library is specified in the system information for the data source, the driver connects to it. A different translation library can be connected to by calling SQLSetConnectAttr with the SQL_ATTR_TRANSLATE_LIB attribute. A translation option can be specified by calling SQLSetConnectAttr with the SQL_ATTR_TRANSLATE_OPTION attribute.

If a driver supports SQLConnect, the driver keyword section of the system information for the driver must contain the ConnectFunctions keyword with the first character set to "Y."

Connection Pooling

Connection pooling allows an application to reuse a connection that has already been created. When connection pooling is enabled and SQLConnect is called, the Driver Manager attempts to make the connection using a connection that is part of a pool of connections in an environment that has been designated for connection pooling. This environment is a shared environment that is used by all applications that use the connections in the pool.

Connection pooling is enabled before the environment is allocated by calling SQLSetEnvAttr to set SQL_ATTR_CONNECTION_POOLING to SQL_CP_ONE_PER_DRIVER (which specifies a maximum of one pool per driver) or SQL_CP_ONE_PER_HENV (which specifies a maximum of one pool per environment). SQLSetEnvAttr in this case is called with EnvironmentHandle set to null, which makes the attribute a process-level attribute. If SQL_ATTR_CONNECTION_POOLING is set to SQL_CP_OFF, connection pooling is disabled.

Once connection pooling has been enabled, SQLAllocHandle with a HandleType of SQL_HANDLE_ENV is called to allocate an environment. The environment allocated by this call is a shared environment because connection pooling has been enabled. The environment to be used is not determined, however, until SQLAllocHandle with a HandleType of SQL_HANDLE_DBC is called.

SQLAllocHandle with a HandleType of SQL_HANDLE_DBC is called to allocate a connection. The Driver Manager attempts to find an existing shared environment that matches the environment attributes set by the application. If no such environment exists, one is created as an implicit shared environment. If a matching shared environment is found, the environment handle is returned to the application and its reference count is incremented.

The connection to be used is not determined, however, until SQLConnect is called. At that point, the Driver Manager attempts to find an existing connection in the connection pool that matches the criteria requested by the application. This criteria includes the connection options requested in the call to SQLConnect (the values of the ServerName, UserName, and Authentication keywords) and any connection attributes set since SQLAllocHandle with a HandleType of SQL_HANDLE_DBC was called. The Driver Manager checks this criteria against the corresponding connection keywords and attributes in connections in the pool. If a match is found, the connection in the pool is used. If no match is found, a new connection is created.

If the SQL_ATTR_CP_MATCH environment attribute is set to SQL_CP_STRICT_MATCH, the match must be exact for a connection in the pool to be used. If the SQL_ATTR_CP_MATCH environment attribute is set to SQL_CP_RELAXED_MATCH, the connection options in the call to SQLConnect must match but not all of the connection attributes must match.

The following rules are applied when a connection attribute, as set by the application before SQLConnect is called, does not match the connection attribute of the connection in the pool:

  • If the connection attribute must be set before the connection is made:

    If SQL_ATTR_CP_MATCH is SQL_CP_STRICT_MATCH, SQL_ATTR_PACKET_SIZE in the pooled connection must be identical to the attribute set by the application. If SQL_CP_RELAXED_MATCH, the values of SQL_ATTR_PACKET_SIZE can be different.

    The value of SQL_ATTR_LOGIN_VALUE does not affect the match.

  • If the connection attribute can be set either before or after the connection is made:

    If the connection attribute has not been set by the application but has been set on the connection in the pool, and there is a default, the connection attribute in the pooled connection is set back to the default and a match is declared. If there is no default, the pooled connection is not considered a match.

    If the connection attribute has been set by the application but has not been set on the connection in the pool, the connection attribute on the pool is changed to that set by the application and a match is declared.

    If the connection attribute has been set by the application, and has also been set on the connection in the pool but the values are different, the value of the application's connection attribute is used and a match is declared.

  • If the values of driver-specific connection attributes are not identical and SQL_ATTR_CP_MATCH is set to SQL_CP_STRICT_MATCH, the connection in the pool is not used.

When the application calls SQLDisconnect to disconnect, the connection is returned to the connection pool and is available for reuse.

Optimizing Connection Pooling Performance

When distributed transactions are involved, it is possible to optimize connection pooling performance by using SQL_DTC_TRANSITION_COST, which is a SQLUINTEGER bitmask. The transitions referred to are the transitions of the connection attribute SQL_ATTR_ENLIST_IN_DTC going from value 0 to nonzero, and vice versa. This is a connection going from not enlisted in a distributed transaction to enlisted in a distributed transaction, and vice versa. Depending on how the driver has implemented enlistment (setting connection attribute SQL_ATTR_ENLIST_IN_DTC), these transitions may be expensive and should therefore be avoided for best performance.

The value returned by the driver contains any combination of the following bits:

  • SQL_DTC_ENLIST_EXPENSIVE, when set, implies the zero to nonzero transition is significantly more expensive than a transition from nonzero to another nonzero value (enlisting a previously enlisted connection in its next transaction).
  • SQL_DTC_UNENLIST_EXPENSIVE, when set, implies the nonzero to zero transition is significantly more expensive than using a connection whose SQL_ATTR_ENLIST_IN_DTC attribute is already set to zero.

There is a performance vs. connection usage tradeoff. If a driver indicates that one or more of these transitions are expensive, the driver manager's connection pooler responds to this by keeping more connections in the pool. Some of the connections in the pool are preferred for nontransactional use, and some are preferred for transactional use. However, if the driver indicates that these transitions are not expensive, fewer connections can be used, perhaps alternating between nontransactional and transactional use.

Drivers that do not support SQL_ATTR_ENLIST_IN_DTC do not need to support SQL_DTC_TRANSITION_COST. For drivers that support SQL_ATTR_ENLIST_IN_DTC but not SQL_DTC_TRANSITION_COST, it is assumed that the transitions are not expensive, as though the driver returned 0 (no bits set) for this value.

Although SQL_DTC_TRANSITION_COST was introduced in ODBC 3.5, an ODBC 2.x driver can also support it because the driver manager will query this information regardless of the driver version.

Code Example

In the following example, an application allocates environment and connection handles. It then connects to the SalesOrders data source with the user ID JohnS and the password Sesame and processes data. When it has finished processing data, it disconnects from the data source and frees the handles.

SQLHENV     henv;
SQLHDBC     hdbc;
SQLHSTMT    hstmt;
SQLRETURN   retcode;

      /*Allocate environment handle */
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);

if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
   /* Set the ODBC version environment attribute */
   retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);

   if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
      /* Allocate connection handle */
      retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);

      if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
         /* Set login timeout to 5 seconds. */
         SQLSetConnectAttr(hdbc, (void*)SQL_LOGIN_TIMEOUT, 5, 0);

         /* Connect to data source */
         retcode = SQLConnect(hdbc, (SQLCHAR*) "Sales", SQL_NTS,
                  (SQLCHAR*) "JohnS", SQL_NTS,
                  (SQLCHAR*) "Sesame", SQL_NTS);

         if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){
            /* Allocate statement handle */
            retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);

            if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
            /* Process data */
                  ;
                  ;
                  ;

               SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
            }
            SQLDisconnect(hdbc);
         }
         SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
      }
   }
   SQLFreeHandle(SQL_HANDLE_ENV, henv);

Related Functions

For information about | See
Allocating a handle | SQLAllocHandle
Discovering and enumerating values required to connect to a data source | SQLBrowseConnect
Disconnecting from a data source | SQLDisconnect
Connecting to a data source using a connection string or dialog box | SQLDriverConnect
Returning the setting of a connection attribute | SQLGetConnectAttr
Setting a connection attribute | SQLSetConnectAttr


SQLCopyDesc

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLCopyDesc copies descriptor information from one descriptor handle to another.

Syntax

SQLRETURN SQLCopyDesc(
     SQLHDESC     SourceDescHandle,
     SQLHDESC     TargetDescHandle);

Arguments

SourceDescHandle
[Input]
Source descriptor handle.
TargetDescHandle
[Input]
Target descriptor handle. The TargetDescHandle argument can be a handle to an application descriptor or an IPD. TargetDescHandle cannot be set to a handle to an IRD, or SQLCopyDesc will return SQLSTATE HY016 (Cannot modify an implementation row descriptor).

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLCopyDesc returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_DESC and a Handle of TargetDescHandle. If an invalid SourceDescHandle was passed in the call, SQL_INVALID_HANDLE will be returned but no SQLSTATE will be returned. The following table lists the SQLSTATE values commonly returned by SQLCopyDesc and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

When an error is returned, the call to SQLCopyDesc is immediately aborted, and the contents of the fields in the TargetDescHandle descriptor are undefined.

Because SQLCopyDesc may be implemented by calling SQLGetDescField and SQLSetDescField, SQLCopyDesc may return SQLSTATEs returned by SQLGetDescField or SQLSetDescField.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate the memory required to support execution or completion of the function.
HY007 | Associated statement is not prepared | SourceDescHandle was associated with an IRD, and the associated statement handle was not in the prepared or executed state.
HY010 | Function sequence error | (DM) The descriptor handle in SourceDescHandle or TargetDescHandle was associated with a StatementHandle for which an asynchronously executing function (not this one) was called and was still executing when this function was called. (DM) The descriptor handle in SourceDescHandle or TargetDescHandle was associated with a StatementHandle for which SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY016 | Cannot modify an implementation row descriptor | TargetDescHandle was associated with an IRD.
HY021 | Inconsistent descriptor information | The descriptor information checked during a consistency check was not consistent. For more information, see "Consistency Checks" in SQLSetDescField.
HY092 | Invalid attribute/option identifier | The call to SQLCopyDesc prompted a call to SQLSetDescField, but *ValuePtr was not valid for the FieldIdentifier argument on TargetDescHandle.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the SourceDescHandle or TargetDescHandle does not support the function.

Comments

A call to SQLCopyDesc copies the fields of the source descriptor handle to the target descriptor handle. Fields can be copied only to an application descriptor or an IPD, but not to an IRD. Fields can be copied from either an application or an implementation descriptor.

Fields can be copied from an IRD only if the statement handle is in the prepared or executed state; otherwise, the function returns SQLSTATE HY007 (Associated statement is not prepared).

Fields can be copied from an IPD whether or not a statement has been prepared. If an SQL statement with dynamic parameters has been prepared and automatic population of the IPD is supported and enabled, then the IPD is populated by the driver. When SQLCopyDesc is called with the IPD as the SourceDescHandle, the populated fields are copied. If the IPD is not populated by the driver, the contents of the fields originally in the IPD are copied.

All fields of the descriptor, except SQL_DESC_ALLOC_TYPE (which specifies whether the descriptor handle was automatically or explicitly allocated), are copied, whether or not the field is defined for the destination descriptor. Copied fields overwrite the existing fields.

The driver copies all descriptor fields if the SourceDescHandle and TargetDescHandle arguments are associated with the same driver, even if the drivers are on two different connections or environments. If the SourceDescHandle and TargetDescHandle arguments are associated with different drivers, the Driver Manager copies ODBC-defined fields, but does not copy driver-defined fields or fields that are not defined by ODBC for the type of descriptor.

The call to SQLCopyDesc is aborted immediately if an error occurs.

When the SQL_DESC_DATA_PTR field is copied, a consistency check is performed on the target descriptor. If the consistency check fails, SQLSTATE HY021 (Inconsistent descriptor information) is returned and the call to SQLCopyDesc is immediately aborted. For more information on consistency checks, see "Consistency Checks" in SQLSetDescRec.

Descriptor handles can be copied across connections even if the connections are under different environments. If the Driver Manager detects that the source and the destination descriptor handles do not belong to the same connection and the two connections belong to separate drivers, it implements SQLCopyDesc by performing a field-by-field copy using SQLGetDescField and SQLSetDescField.

When SQLCopyDesc is called with a SourceDescHandle on one driver and a TargetDescHandle on another driver, the error queue of the SourceDescHandle is cleared. This occurs because SQLCopyDesc in this case is implemented by calls to SQLGetDescField and SQLSetDescField.

Note An application might be able to associate an explicitly allocated descriptor handle with a StatementHandle, rather than calling SQLCopyDesc to copy fields from one descriptor to another. An explicitly allocated descriptor can be associated with another StatementHandle on the same ConnectionHandle by setting the SQL_ATTR_APP_ROW_DESC or SQL_ATTR_APP_PARAM_DESC statement attribute to the handle of the explicitly allocated descriptor. When this is done, SQLCopyDesc does not have to be called to copy descriptor field values from one descriptor to another. A descriptor handle cannot be associated with a StatementHandle on another ConnectionHandle, however; to use the same descriptor field values on StatementHandles on different ConnectionHandles, SQLCopyDesc has to be called.

For a description of the fields in a descriptor header or record, see SQLSetDescField. For more information on descriptors, see Chapter 13: Descriptors.

Copying Rows Between Tables

An application may copy data from one table to another without copying the data at the application level. To do this, the application binds the same data buffers and descriptor information to a statement that fetches the data and the statement that inserts the data into a copy. This can be accomplished either by sharing an application descriptor (binding an explicitly allocated descriptor as both the ARD to one statement and the APD in another) or by using SQLCopyDesc to copy the bindings between the ARD and the APD of the two statements. If the statements are on different connections, SQLCopyDesc must be used. In addition, SQLCopyDesc has to be called to copy the bindings between the IRD and the IPD of the two statements. When copying across statements on the same connection, the SQL_ACTIVE_STATEMENTS information type returned by the driver for a call to SQLGetInfo must be greater than 1 for this operation to succeed. (This is not the case when copying across connections.)

Code Example

In the following example, descriptor operations are used to copy the fields of the PartsSource table into the PartsCopy table. The contents of the PartsSource table are fetched into rowset buffers in hstmt0. These values are used as parameters of an INSERT statement on hstmt1 to populate the columns of the PartsCopy table. To do so, the fields of the IRD of hstmt0 are copied to the fields of the IPD of hstmt1, and the fields of the ARD of hstmt0 are copied to the fields of the APD of hstmt1.

#define ROWS 100
#define DESC_LEN 50
#define SQL_SUCCEEDED(rc) (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO)

// Template for a row
typedef struct {
   SQLINTEGER   sPartID;
   SQLINTEGER   cbPartID;
   SQLUCHAR     szDescription[DESC_LENGTH];
   SQLINTEGER   cbDescription;
   REAL         sPrice;
   SQLINTEGER   cbPrice;
} PartsSource;

PartsSource    rget[ROWS];          // rowset buffer
SQLUSMALLINT   sts_ptr[ROWS];       // status pointer
SQLHSTMT       hstmt0, hstmt1;
SQLHDESC       hArd0, hIrd0, hApd1, hIpd1;

// ARD and IRD of hstmt0
SQLGetStmtAttr(hstmt0, SQL_ATTR_APP_ROW_DESC, &hArd0, 0, NULL);
SQLGetStmtAttr(hstmt0, SQL_ATTR_IMP_ROW_DESC, &hIrd0, 0, NULL);

// APD and IPD of hstmt1
SQLGetStmtAttr(hstmt1, SQL_ATTR_APP_PARAM_DESC, &hApd1, 0, NULL);
SQLGetStmtAttr(hstmt1, SQL_ATTR_IMP_PARAM_DESC, &hIpd1, 0, NULL);

// Use row-wise binding on hstmt0 to fetch rows
SQLSetStmtAttr(hstmt0, SQL_ATTR_ROW_BIND_TYPE, (SQLPOINTER) sizeof(PartsSource), 0);

// Set rowset size for hstmt0
SQLSetStmtAttr(hstmt0, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER) ROWS, 0);

// Execute a select statement
SQLExecDirect(hstmt0, "SELECT PARTID, DESCRIPTION, PRICE FROM PARTS ORDER BY 3, 1, 2"",
               SQL_NTS);

// Bind
SQLBindCol(hstmt0, 1, SQL_C_SLONG, rget[0].sPartID, 0,
   &rget[0].cbPartID);
SQLBindCol(hstmt0, 2, SQL_C_CHAR, &rget[0].szDescription, DESC_LEN,
   &rget[0].cbDescription);
SQLBindCol(hstmt0, 3, SQL_C_FLOAT, rget[0].sPrice,
   0, &rget[0].cbPrice);

// Perform parameter bindings on hstmt1.
SQLCopyDesc(hArd0, hApd1);
SQLCopyDesc(hIrd0, hIpd1);

// Set the array status pointer of IRD
SQLSetStmtAttr(hstmt0, SQL_ATTR_ROW_STATUS_PTR, sts_ptr, SQL_IS_POINTER);

// Set the ARRAY_STATUS_PTR field of APD to be the same
// as that in IRD.
SQLSetStmtAttr(hstmt1, SQL_ATTR_PARAM_OPERATION_PTR, sts_ptr, SQL_IS_POINTER);

// Prepare an insert statement on hstmt1. PartsCopy is a copy of
// PartsSource
SQLPrepare(hstmt1, "INSERT INTO PARTS_COPY VALUES (?, ?, ?)", SQL_NTS);

// In a loop, fetch a rowset, and copy the fetched rowset to PARTS_COPY

rc = SQLFetchScroll(hstmt0, SQL_FETCH_NEXT, 0);
while (SQL_SUCCEEDED(rc)) {

   // After the call to SQLFetchScroll, the status array has row
   // statuses. This array is used as input status in the APD
   // and hence determines which elements of the rowset buffer
   // are inserted.
   SQLExecute(hstmt1);

   rc = SQLFetchScroll(hstmt0, SQL_FETCH_NEXT, 0);
} // while

Related Functions

For information about | See
Getting multiple descriptor fields | SQLGetDescRec
Setting a single descriptor field | SQLSetDescField
Setting multiple descriptor fields | SQLSetDescRec


SQLDataSources

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLDataSources returns information about a data source. This function is implemented solely by the Driver Manager.

Syntax

SQLRETURN SQLDataSources(
     SQLHENV     EnvironmentHandle,
     SQLUSMALLINT     Direction,
     SQLCHAR *     ServerName,
     SQLSMALLINT     BufferLength1,
     SQLSMALLINT *     NameLength1Ptr,
     SQLCHAR *     Description,
     SQLSMALLINT     BufferLength2,
     SQLSMALLINT *     NameLength2Ptr);

Arguments

EnvironmentHandle
[Input]
Environment handle.
Direction
[Input]
Determines which data source the Driver Manager returns information on. Can be:

SQL_FETCH_NEXT (to fetch the next data source name in the list), SQL_FETCH_FIRST (to fetch from the beginning of the list), SQL_FETCH_FIRST_USER (to fetch the first user DSN), or SQL_FETCH_FIRST_SYSTEM (to fetch the first system DSN).

When Direction is set to SQL_FETCH_FIRST, subsequent calls to SQLDataSources with Direction set to SQL_FETCH_NEXT return both user and system DSNs. When Direction is set to SQL_FETCH_FIRST_USER, all subsequent calls to SQLDataSources with Direction set to SQL_FETCH_NEXT return only user DSNs. When Direction is set to SQL_FETCH_FIRST_SYSTEM, all subsequent calls to SQLDataSources with Direction set to SQL_FETCH_NEXT return only system DSNs.

ServerName
[Output]
Pointer to a buffer in which to return the data source name.
BufferLength1
[Input]
Length of the *ServerName buffer, in bytes; this does not need to be longer than SQL_MAX_DSN_LENGTH plus the null-termination character.
NameLength1Ptr
[Output]
Pointer to a buffer in which to return the total number of bytes (excluding the null-termination byte) available to return in *ServerName. If the number of bytes available to return is greater than or equal to BufferLength1, the data source name in *ServerName is truncated to BufferLength1 minus the length of a null-termination character.
Description
[Output]
Pointer to a buffer in which to return the description of the driver associated with the data source. For example, dBASE or SQL Server.
BufferLength2
[Input]
Length of the *Description buffer.
NameLength2Ptr
[Output]
Pointer to a buffer in which to return the total number of bytes (excluding the null-termination byte) available to return in *Description. If the number of bytes available to return is greater than or equal to BufferLength2, the driver description in *Description is truncated to BufferLength2 minus the length of a null-termination character.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLDataSources returns either SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_ENV and a Handle of EnvironmentHandle. The following table lists the SQLSTATE values commonly returned by SQLDataSources and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | (DM) Driver Manager–specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | (DM) The buffer *ServerName was not large enough to return the entire data source name, so the name was truncated. The length of the entire data source name is returned in *NameLength1Ptr. (Function returns SQL_SUCCESS_WITH_INFO.) (DM) The buffer *Description was not large enough to return the entire driver description, so the description was truncated. The length of the untruncated data source description is returned in *NameLength2Ptr. (Function returns SQL_SUCCESS_WITH_INFO.)
HY000 | General error | (DM) An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | (DM) The Driver Manager was unable to allocate memory required to support execution or completion of the function.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) The value specified for argument BufferLength1 was less than 0. (DM) The value specified for argument BufferLength2 was less than 0.
HY103 | Invalid retrieval code | (DM) The value specified for the argument Direction was not equal to SQL_FETCH_FIRST, SQL_FETCH_FIRST_USER, SQL_FETCH_FIRST_SYSTEM, or SQL_FETCH_NEXT.

Comments

Because SQLDataSources is implemented in the Driver Manager, it is supported for all drivers regardless of a particular driver's standards compliance.

An application can call SQLDataSources multiple times to retrieve all data source names. The Driver Manager retrieves this information from the system information. When there are no more data source names, the Driver Manager returns SQL_NO_DATA. If SQLDataSources is called with SQL_FETCH_NEXT immediately after it returns SQL_NO_DATA, it will return the first data source name. For information about how an application uses the information returned by SQLDataSources, see "Choosing a Data Source or Driver" in Chapter 6: Connecting to a Data Source or Driver.

If SQL_FETCH_NEXT is passed to SQLDataSources the very first time it is called, it will return the first data source name.

The driver determines how data source names are mapped to actual data sources.

Related Functions

For information about | See
Discovering and listing values required to connect to a data source | SQLBrowseConnect
Connecting to a data source | SQLConnect
Connecting to a data source using a connection string or dialog box | SQLDriverConnect
Returning driver descriptions and attributes | SQLDrivers


SQLDescribeCol

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLDescribeCol returns the result descriptor—column name, type, column size, decimal digits, and nullability—for one column in the result set. This information also is available in the fields of the IRD.

Syntax

SQLRETURN SQLDescribeCol(
     SQLHSTMT     StatementHandle,
     SQLSMALLINT     ColumnNumber,
     SQLCHAR *     ColumnName,
     SQLSMALLINT     BufferLength,
     SQLSMALLINT *     NameLengthPtr,
     SQLSMALLINT *     DataTypePtr,
     SQLUINTEGER *     ColumnSizePtr,
     SQLSMALLINT *     DecimalDigitsPtr,
     SQLSMALLINT *     NullablePtr);

Arguments

StatementHandle
[Input]
Statement handle.
ColumnNumber
[Input]
Column number of result data, ordered sequentially in increasing column order, starting at 1. The ColumnNumber argument can also be set to 0 to describe the bookmark column.
ColumnName
[Output]
Pointer to a buffer in which to return the column name. This value is read from the SQL_DESC_NAME field of the IRD. If the column is unnamed or the column name cannot be determined, the driver returns an empty string.
BufferLength
[Input]
Length of the *ColumnName buffer, in characters.
NameLengthPtr
[Output]
Pointer to a buffer in which to return the total number of bytes (excluding the null-termination byte) available to return in *ColumnName. If the number of bytes available to return is greater than or equal to BufferLength, the column name in *ColumnName is truncated to BufferLength minus the length of a null-termination character.
DataTypePtr
[Output]
Pointer to a buffer in which to return the SQL data type of the column. This value is read from the SQL_DESC_CONCISE_TYPE field of the IRD. This will be one of the values in the "SQL Data Types" section of Appendix D: Data Types, or a driver-specific SQL data type. If the data type cannot be determined, the driver returns SQL_UNKNOWN_TYPE.

In ODBC 3.x, SQL_TYPE_DATE, SQL_TYPE_TIME, or SQL_TYPE_TIMESTAMP is returned in *DataTypePtr for date, time, or timestamp data, respectively; in ODBC 2.x, SQL_DATE, SQL_TIME, or SQL_TIMESTAMP is returned. The Driver Manager performs the required mappings when an ODBC 2.x application is working with an ODBC 3.x driver or when an ODBC 3.x application is working with an ODBC 2.x driver.

When ColumnNumber is equal to 0 (for a bookmark column), SQL_BINARY is returned in *DataTypePtr for variable-length bookmarks. (SQL_INTEGER is returned if bookmarks are used by an ODBC 3.x application working with an ODBC 2.x driver or by an ODBC 2.x application working with an ODBC 3.x driver.)

For more information on these data types, see "SQL Data Types" in Appendix D: Data Types. For information about driver-specific SQL data types, see the driver's documentation.

ColumnSizePtr
[Output]
Pointer to a buffer in which to return the size of the column on the data source. If the column size cannot be determined, the driver returns 0. For more information on column size, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" in Appendix D: Data Types.
DecimalDigitsPtr
[Output]
Pointer to a buffer in which to return the number of decimal digits of the column on the data source. If the number of decimal digits cannot be determined or is not applicable, the driver returns 0. For more information on decimal digits, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" in Appendix D: Data Types.
NullablePtr
[Output]
Pointer to a buffer in which to return a value that indicates whether the column allows NULL values. This value is read from the SQL_DESC_NULLABLE field of the IRD. The value is one of the following:

SQL_NO_NULLS: The column does not allow NULL values.

SQL_NULLABLE: The column allows NULL values.

SQL_NULLABLE_UNKNOWN: The driver cannot determine if the column allows NULL values.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLDescribeCol returns either SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLDescribeCol and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | The buffer *ColumnName was not large enough to return the entire column name, so the column name was truncated. The length of the untruncated column name is returned in *NameLengthPtr. (Function returns SQL_SUCCESS_WITH_INFO.)
07005 Prepared statement not a cursor-
specification 		The statement associated with the StatementHandle did not return a result set. There were no columns to describe.
07009 Invalid descriptor index (DM) The value specified for the argument ColumnNumber was equal to 0, and the SQL_ATTR_USE_BOOKMARKS statement option was SQL_UB_OFF.

The value specified for the argument ColumnNumber was greater than the number of columns in the result set.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation failure | The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY010 Function sequence error (DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) The function was called prior to calling SQLPrepare, SQLExecute, or a catalog function on the statement handle.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) The value specified for argument BufferLength was less than 0.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

SQLDescribeCol can return any SQLSTATE that can be returned by SQLPrepare or SQLExecute when called after SQLPrepare and before SQLExecute, depending on when the data source evaluates the SQL statement associated with the statement handle.

For performance reasons, an application should not call SQLDescribeCol before executing a statement.

Comments

An application typically calls SQLDescribeCol after a call to SQLPrepare and before or after the associated call to SQLExecute. An application can also call SQLDescribeCol after a call to SQLExecDirect. For more information, see "Result Set Metadata" in Chapter 10: Retrieving Results (Basic).

SQLDescribeCol retrieves the column name, type, and length generated by a SELECT statement. If the column is an expression, *ColumnName is either an empty string or a driver-defined name.

Note ODBC supports SQL_NULLABLE_UNKNOWN as an extension, even though the X/Open and SQL Access Group Call Level Interface specification does not specify the option for SQLDescribeCol.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Returning information about a column in a result set | SQLColAttribute
Fetching multiple rows of data | SQLFetch
Returning the number of result set columns | SQLNumResultCols
Preparing a statement for execution | SQLPrepare


SQLDescribeParam

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ODBC

Summary

SQLDescribeParam returns the description of a parameter marker associated with a prepared SQL statement. This information is also available in the fields of the IPD.

Syntax

SQLRETURN SQLDescribeParam(
     SQLHSTMT     StatementHandle,
     SQLUSMALLINT     ParameterNumber,
     SQLSMALLINT *     DataTypePtr,
     SQLUINTEGER *     ParameterSizePtr,
     SQLSMALLINT *     DecimalDigitsPtr,
     SQLSMALLINT *     NullablePtr);

Argument

StatementHandle
[Input]
Statement handle.
ParameterNumber
[Input]
Parameter marker number ordered sequentially in increasing parameter order, starting at 1.
DataTypePtr
[Output]
Pointer to a buffer in which to return the SQL data type of the parameter. This value is read from the SQL_DESC_CONCISE_TYPE record field of the IPD. This will be one of the values in the "SQL Data Types" section of Appendix D: Data Types, or a driver-specific SQL data type.

In ODBC 3.x, SQL_TYPE_DATE, SQL_TYPE_TIME, or SQL_TYPE_TIMESTAMP will be returned in *DataTypePtr for date, time, or timestamp data, respectively; in ODBC 2.x, SQL_DATE, SQL_TIME, or SQL_TIMESTAMP will be returned. The Driver Manager performs the required mappings when an ODBC 2.x application is working with an ODBC 3.x driver or when an ODBC 3.x application is working with an ODBC 2.x driver.

When ColumnNumber is equal to 0 (for a bookmark column), SQL_BINARY is returned in *DataTypePtr for variable-length bookmarks. (SQL_INTEGER is returned if bookmarks are used by an ODBC 3.x application working with an ODBC 2.x driver or by an ODBC 2.x application working with an ODBC 3.x driver.)

For more information, see "SQL Data Types" in Appendix D: Data Types. For information about driver-specific SQL data types, see the driver's documentation.

ParameterSizePtr
[Output]
Pointer to a buffer in which to return the size of the column or expression of the corresponding parameter marker as defined by the data source. For further information concerning column size, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size," in Appendix D: Data Types.
DecimalDigitsPtr
[Output]
Pointer to a buffer in which to return the number of decimal digits of the column or expression of the corresponding parameter as defined by the data source. For more information on decimal digits, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size," in Appendix D: Data Types.
NullablePtr
[Output]
Pointer to a buffer in which to return a value that indicates whether the parameter allows NULL values. This value is read from the SQL_DESC_NULLABLE field of the IPD. One of the following:
  • SQL_NO_NULLS: The parameter does not allow NULL values (this is the default value).
  • SQL_NULLABLE: The parameter allows NULL values.
  • SQL_NULLABLE_UNKNOWN: The driver cannot determine if the parameter allows NULL values.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLDescribeParam returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLDescribeParam and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
07009 Invalid descriptor index (DM) The value specified for the argument ParameterNumber is less than 1.

The value specified for the argument ParameterNumber was greater than the number of parameters in the associated SQL statement.

The parameter marker was part of a non-DML statement.

The parameter marker was part of a SELECT list.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
21S01 | Insert value list does not match column list | The number of parameters in the INSERT statement did not match the number of columns in the table named in the statement.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY010 Function sequence error (DM) The function was called prior to calling SQLPrepare or SQLExecDirect for the StatementHandle.

(DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

Parameter markers are numbered in increasing parameter order, starting with 1, in the order they appear in the SQL statement.

SQLDescribeParam does not return the type (input, input/output, or output) of a parameter in an SQL statement. Except in calls to procedures, all parameters in SQL statements are input parameters. To determine the type of each parameter in a call to a procedure, an application calls SQLProcedureColumns.

For more information, see "Describing Parameters" in Chapter 9: Executing Statements.

Code Example

The following example prompts the user for an SQL statement and then prepares that statement. Next, it calls SQLNumParams to determine whether the statement contains any parameters. If so, it calls SQLDescribeParam to describe those parameters and SQLBindParameter to bind them. Finally, it prompts the user for the values of any parameters and then executes the statement.

SQLCHAR       Statement[100];
SQLSMALLINT   NumParams, i, DataType, DecimalDigits, Nullable;
SQLUINTEGER   ParamSize;
SQLHSTMT      hstmt;

// Prompt the user for an SQL statement and prepare it.
GetSQLStatement(Statement);
SQLPrepare(hstmt, Statement, SQL_NTS);

// Check to see if there are any parameters. If so, process them.
SQLNumParams(hstmt, &NumParams);
if (NumParams) {
   // Allocate memory for three arrays. The first holds pointers to buffers in which
   // each parameter value will be stored in character form. The second contains the
   // length of each buffer. The third contains the length/indicator value for each
   // parameter.
   SQLPOINTER * PtrArray = (SQLPOINTER *) malloc(NumParams * sizeof(SQLPOINTER));
   SQLINTEGER * BufferLenArray = (SQLINTEGER *) malloc(NumParams * sizeof(SQLINTEGER));
   SQLINTEGER * LenOrIndArray = (SQLINTEGER *) malloc(NumParams * sizeof(SQLINTEGER));

   for (i = 0; i < NumParams; i++) {
   // Describe the parameter.
   SQLDescribeParam(hstmt, i + 1, &DataType, &ParamSize, &DecimalDigits, &Nullable);

   // Call a helper function to allocate a buffer in which to store the parameter
   // value in character form. The function determines the size of the buffer from
   // the SQL data type and parameter size returned by SQLDescribeParam and returns
   // a pointer to the buffer and the length of the buffer.
   AllocParamBuffer(DataType, ParamSize, &PtrArray[i], &BufferLenArray[i]);

   // Bind the memory to the parameter. Assume that we only have input parameters.
   SQLBindParameter(hstmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, DataType, ParamSize,
         DecimalDigits, PtrArray[i], BufferLenArray[i],
         &LenOrIndArray[i]);

   // Prompt the user for the value of the parameter and store it in the memory
   // allocated earlier. For simplicity, this function does not check the value
   // against the information returned by SQLDescribeParam. Instead, the driver does
   // this when the statement is executed.
   GetParamValue(PtrArray[i], BufferLenArray[i], &LenOrIndArray[i]);
   }
}

// Execute the statement.
SQLExecute(hstmt);

// Process the statement further, such as retrieving results (if any) and closing the
// cursor (if any). Code not shown.

// Free the memory allocated for each parameter and the memory allocated for the arrays
// of pointers, buffer lengths, and length/indicator values.
for (i = 0; i < NumParams; i++) free(PtrArray[i]);
free(PtrArray);
free(BufferLenArray);
free(LenOrIndArray);

Related Functions

For information about | See
Binding a buffer to a parameter | SQLBindParameter
Canceling statement processing | SQLCancel
Executing a prepared SQL statement | SQLExecute
Preparing a statement for execution | SQLPrepare


SQLDisconnect

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLDisconnect closes the connection associated with a specific connection handle.

Syntax

SQLRETURN SQLDisconnect(
     SQLHDBC     ConnectionHandle);

Arguments

ConnectionHandle
[Input]
Connection handle.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLDisconnect returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_DBC and a Handle of ConnectionHandle. The following table lists the SQLSTATE values commonly returned by SQLDisconnect and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01002 | Disconnect error | An error occurred during the disconnect. However, the disconnect succeeded. (Function returns SQL_SUCCESS_WITH_INFO.)
08003 | Connection does not exist | (DM) The connection specified in the argument ConnectionHandle was not open.
25000 | Invalid transaction state | There was a transaction in process on the connection specified by the argument ConnectionHandle. The transaction remains active.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY010 | Function sequence error | (DM) An asynchronously executing function was called for a StatementHandle associated with the ConnectionHandle and was still executing when SQLDisconnect was called. (DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for a StatementHandle associated with the ConnectionHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request, and the connection is still active. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the ConnectionHandle does not support the function.

Comments

If an application calls SQLDisconnect after SQLBrowseConnect returns SQL_NEED_DATA and before it returns a different return code, the driver cancels the connection browsing process and returns the connection to an unconnected state.

If an application calls SQLDisconnect while there is an incomplete transaction associated with the connection handle, the driver returns SQLSTATE 25000 (Invalid transaction state), indicating that the transaction is unchanged and the connection is open. An incomplete transaction is one that has not been committed or rolled back with SQLEndTran.

If an application calls SQLDisconnect before it has freed all statements associated with the connection, the driver, after it successfully disconnects from the data source, frees those statements and all descriptors that have been explicitly allocated on the connection. However, if one or more of the statements associated with the connection are still executing asynchronously, SQLDisconnect returns SQL_ERROR with a SQLSTATE value of HY010 (Function sequence error).

For information about how an application uses SQLDisconnect, see "Disconnecting from a Data Source or Driver" in Chapter 6: Connecting to a Data Source or Driver.

Disconnecting from a Pooled Connection

If connection pooling is enabled for a shared environment and an application calls SQLDisconnect on a connection in that environment, the connection is returned to the connection pool and is still available to other components using the same shared environment.

Code Example

See SQLBrowseConnect and SQLConnect.

Related Functions

For information about | See
Allocating a handle | SQLAllocHandle
Connecting to a data source | SQLConnect
Connecting to a data source using a connection string or dialog box | SQLDriverConnect
Executing a commit or rollback operation | SQLEndTran
Freeing a connection handle | SQLFreeConnect


SQLDriverConnect

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ODBC

Summary

SQLDriverConnect is an alternative to SQLConnect. It supports data sources that require more connection information than the three arguments in SQLConnect, dialog boxes to prompt the user for all connection information, and data sources that are not defined in the system information.

SQLDriverConnect provides the following connection attributes:

  • Establish a connection using a connection string that contains the data source name, one or more user IDs, one or more passwords, and other information required by the data source.
  • Establish a connection using a partial connection string or no additional information; in this case, the Driver Manager and the driver can each prompt the user for connection information.
  • Establish a connection to a data source that is not defined in the system information. If the application supplies a partial connection string, the driver can prompt the user for connection information.
  • Establish a connection to a data source using a connection string constructed from the information in a .dsn file.

After a connection is established, SQLDriverConnect returns the completed connection string. The application can use this string for subsequent connection requests. For more information, see "Connecting with SQLDriverConnect" in Chapter 6: Connecting to a Data Source or Driver.

Syntax

SQLRETURN SQLDriverConnect(
     SQLHDBC     ConnectionHandle,
     SQLHWND     WindowHandle,
     SQLCHAR *     InConnectionString,
     SQLSMALLINT     StringLength1,
     SQLCHAR *     OutConnectionString,
     SQLSMALLINT     BufferLength,
     SQLSMALLINT *     StringLength2Ptr,
     SQLUSMALLINT     DriverCompletion);

Arguments

ConnectionHandle
[Input]
Connection handle.
WindowHandle
[Input]
Window handle. The application can pass the handle of the parent window, if applicable, or a null pointer if either the window handle is not applicable or SQLDriverConnect will not present any dialog boxes.
InConnectionString
[Input]
A full connection string (see the syntax in "Comments"), a partial connection string, or an empty string.
StringLength1
[Input]
Length of *InConnectionString, in bytes.
OutConnectionString
[Output]
Pointer to a buffer for the completed connection string. Upon successful connection to the target data source, this buffer contains the completed connection string. Applications should allocate at least 1,024 bytes for this buffer.
BufferLength
[Input]
Length of the *OutConnectionString buffer. If the *OutConnectionString value is a Unicode string (when calling SQLDriverConnectW), the BufferLength argument must be an even number.
StringLength2Ptr
[Output]
Pointer to a buffer in which to return the total number of characters (excluding the null-termination character) available to return in *OutConnectionString. If the number of characters available to return is greater than or equal to BufferLength, the completed connection string in *OutConnectionString is truncated to BufferLength minus the length of a null-termination character.
DriverCompletion
[Input]
Flag that indicates whether the Driver Manager or driver must prompt for more connection information:

SQL_DRIVER_PROMPT,
SQL_DRIVER_COMPLETE,
SQL_DRIVER_COMPLETE_REQUIRED, or
SQL_DRIVER_NOPROMPT.

(For additional information, see "Comments.")

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLDriverConnect returns either SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with an fHandleType of SQL_HANDLE_DBC and an hHandle of ConnectionHandle. The following table lists the SQLSTATE values commonly returned by SQLDriverConnect and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | The buffer *OutConnectionString was not large enough to return the entire connection string, so the connection string was truncated. The length of the untruncated connection string is returned in *StringLength2Ptr. (Function returns SQL_SUCCESS_WITH_INFO.)
01S00 | Invalid connection string attribute | An invalid attribute keyword was specified in the connection string (InConnectionString), but the driver was able to connect to the data source anyway. (Function returns SQL_SUCCESS_WITH_INFO.)
01S02 | Option value changed | The driver did not support the specified value pointed to by the ValuePtr argument in SQLSetConnectAttr and substituted a similar value. (Function returns SQL_SUCCESS_WITH_INFO.)
01S08 | Error saving file DSN | The string in *InConnectionString contained a FILEDSN keyword, but the .dsn file was not saved. (Function returns SQL_SUCCESS_WITH_INFO.)
01S09 | Invalid keyword | (DM) The string in *InConnectionString contained a SAVEFILE keyword but not a DRIVER or a FILEDSN keyword. (Function returns SQL_SUCCESS_WITH_INFO.)
08001 | Client unable to establish connection | The driver was unable to establish a connection with the data source.
08002 | Connection name in use | (DM) The specified ConnectionHandle had already been used to establish a connection with a data source, and the connection was still open.
08004 | Server rejected the connection | The data source rejected the establishment of the connection for implementation-defined reasons.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was attempting to connect failed before the function completed processing.
28000 | Invalid authorization specification | Either the user identifier or the authorization string, or both, as specified in the connection string (InConnectionString), violated restrictions defined by the data source.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *szMessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The Driver Manager was unable to allocate memory required to support execution or completion of the function. The driver was unable to allocate memory required to support execution or completion of the function.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) The value specified for argument StringLength1 was less than 0 and was not equal to SQL_NTS. (DM) The value specified for argument BufferLength was less than 0.
HY092 | Invalid attribute/option identifier | (DM) The DriverCompletion argument was SQL_DRIVER_PROMPT, and the WindowHandle argument was a null pointer.
HY110 | Invalid driver completion | (DM) The value specified for the argument DriverCompletion was not equal to SQL_DRIVER_PROMPT, SQL_DRIVER_COMPLETE, SQL_DRIVER_COMPLETE_REQUIRED, or SQL_DRIVER_NOPROMPT. (DM) Connection pooling was enabled, and the value specified for the argument DriverCompletion was not equal to SQL_DRIVER_NOPROMPT.
HYC00 | Optional feature not implemented | The driver does not support the version of ODBC behavior that the application requested.
HYT00 | Timeout expired | The login timeout period expired before the connection to the data source completed. The timeout period is set through SQLSetConnectAttr, SQL_ATTR_LOGIN_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver corresponding to the specified data source name does not support the function.
IM002 | Data source not found and no default driver specified | (DM) The data source name specified in the connection string (InConnectionString) was not found in the system information, and there was no default driver specification. (DM) ODBC data source and default driver information could not be found in the system information.
IM003 | Specified driver could not be loaded | (DM) The driver listed in the data source specification in the system information or specified by the DRIVER keyword was not found or could not be loaded for some other reason.
IM004 | Driver's SQLAllocHandle on SQL_HANDLE_ENV failed | (DM) During SQLDriverConnect, the Driver Manager called the driver's SQLAllocHandle function with an fHandleType of SQL_HANDLE_ENV and the driver returned an error.
IM005 | Driver's SQLAllocHandle on SQL_HANDLE_DBC failed. | (DM) During SQLDriverConnect, the Driver Manager called the driver's SQLAllocHandle function with an fHandleType of SQL_HANDLE_DBC and the driver returned an error.
IM006 | Driver's SQLSetConnectAttr failed | (DM) During SQLDriverConnect, the Driver Manager called the driver's SQLSetConnectAttr function and the driver returned an error.
IM007 | No data source or driver specified; dialog prohibited | No data source name or driver was specified in the connection string, and DriverCompletion was SQL_DRIVER_NOPROMPT.
IM008 | Dialog failed | The driver attempted to display its login dialog box and failed. WindowHandle was a null pointer, and DriverCompletion was not SQL_DRIVER_NO_PROMPT.
IM009 | Unable to load translation DLL | The driver was unable to load the translation DLL that was specified for the data source or for the connection.
IM010 | Data source name too long | (DM) The attribute value for the DSN keyword was longer than SQL_MAX_DSN_LENGTH characters.
IM011 | Driver name too long | (DM) The attribute value for the DRIVER keyword was longer than 255 characters.
IM012 | DRIVER keyword syntax error | (DM) The keyword-value pair for the DRIVER keyword contained a syntax error. (DM) The string in *InConnectionString contained a FILEDSN keyword, but the .dsn file did not contain a DRIVER keyword or a DSN keyword.
IM014 | Invalid name of file DSN | (DM) The string in *InConnectionString contained a FILEDSN keyword, but the name of the .dsn file was invalid.
IM015 | Corrupt file data source | (DM) The string in *InConnectionString contained a FILEDSN keyword, but the .dsn file was unreadable.

Comments

A connection string has the following syntax:

connection-string ::= empty-string[;] | attribute[;] | attribute; connection-string
empty-string ::=
attribute ::= attribute-keyword=attribute-value | DRIVER=[{]attribute-value[}]
attribute-keyword ::= DSN | UID | PWD
| driver-defined-attribute-keyword
attribute-value ::= character-string
driver-defined-attribute-keyword ::= identifier

where character-string has zero or more characters; identifier has one or more characters; attribute-keyword is not case-sensitive; attribute-value may be case-sensitive; and the value of the DSN keyword does not consist solely of blanks.

Because of connection string and initialization file grammar, keywords and attribute values that contain the characters []{}(),;?*=!@ not enclosed with braces should be avoided. The value of the DSN keyword cannot consist only of blanks and should not contain leading blanks. Because of the grammar of the system information, keywords and data source names cannot contain the backslash (\) character.

Applications do not have to add braces around the attribute value after the DRIVER keyword unless the attribute contains a semicolon (;), in which case the braces are required. If the attribute value that the driver receives includes braces, the driver should not remove them but they should be part of the returned connection string.

A DSN or connection string value enclosed with braces ({}) containing any of the characters []{}(),;?*=!@ is passed intact to the driver. However, when using these characters in a keyword, the Driver Manager returns an error when working with file DSNs but passes the connection string to the driver for regular connection strings. Avoid using embedded braces in a keyword value.

The connection string may include any number of driver-defined keywords. Because the DRIVER keyword does not use information from the system information, the driver must define enough keywords so that a driver can connect to a data source using only the information in the connection string. (For more information, see "Driver Guidelines," later in this section.) The driver defines which keywords are required to connect to the data source.

The following table describes the attribute values of the DSN, FILEDSN, DRIVER, UID, PWD, and SAVEFILE keywords.

Keyword | Attribute value description
DSN | Name of a data source as returned by SQLDataSources or the data sources dialog box of SQLDriverConnect.
FILEDSN | Name of a .dsn file from which a connection string will be built for the data source. These data sources are called file data sources.
DRIVER | Description of the driver as returned by the SQLDrivers function. For example, Rdb or SQL Server.
UID | A user ID.
PWD | The password corresponding to the user ID, or an empty string if there is no password for the user ID (PWD=;).
SAVEFILE | The file name of a .dsn file in which the attribute values of keywords used in making the present, successful connection should be saved.

For information about how an application chooses a data source or driver, see "Choosing a Data Source or Driver" in Chapter 6: Connecting to a Data Source or Driver.

If any keywords are repeated in the connection string, the driver uses the value associated with the first occurrence of the keyword. If the DSN and DRIVER keywords are included in the same connection string, the Driver Manager and the driver use whichever keyword appears first.

The FILEDSN and DSN keywords are mutually exclusive: whichever keyword appears first is used, and the one that appears second is ignored. The FILEDSN and DRIVER keywords, on the other hand, are not mutually exclusive. If any keyword appears in a connection string with FILEDSN, then the attribute value of the keyword in the connection string is used rather than the attribute value of the same keyword in the .dsn file.

If the FILEDSN keyword is used, the keywords specified in a .dsn file are used to create a connection string. (For more information, see "File Data Sources," later in this section.) The UID keyword is optional; a .dsn file may be created with only the DRIVER keyword. The PWD keyword is not stored in a .dsn file. The default directory for saving and loading a .dsn file will be a combination of the path specified by CommonFileDir in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
Windows\CurrentVersion and "ODBC\DataSources". (If CommonFileDir were "C:\Program Files\Common Files", the default directory would be "C:\Program Files\Common Files\ODBC\Data Sources".)

Note A .dsn file can be manipulated directly by calling the SQLReadFileDSN and SQLWriteFileDSN functions in the installer DLL.

If the SAVEFILE keyword is used, the attribute values of keywords used in making the present, successful connection will be saved as a .dsn file with the name of the attribute value of the SAVEFILE keyword. The SAVEFILE keyword must be used in conjunction with the DRIVER keyword, the FILEDSN keyword, or both, or the function returns SQL_SUCCESS_WITH_INFO with SQLSTATE 01S09 (Invalid keyword). The SAVEFILE keyword must appear before the DRIVER keyword in the connection string, or the results will be undefined.

Driver Manager Guidelines

The Driver Manager constructs a connection string to pass to the driver in the InConnectionString argument of the driver's SQLDriverConnect function. The Driver Manager does not modify the InConnectionString argument passed to it by the application.

The action of the Driver Manager is based on the value of the DriverCompletion argument:

  • SQL_DRIVER_PROMPT: If the connection string does not contain either the DRIVER, DSN, or FILEDSN keyword, the Driver Manager displays the Data Sources dialog box. It constructs a connection string from the data source name returned by the dialog box and any other keywords passed to it by the application. If the data source name returned by the dialog box is empty, the Driver Manager specifies the keyword-value pair DSN=Default. (This dialog box will not display a data source with the name "Default".)
  • SQL_DRIVER_COMPLETE or SQL_DRIVER_COMPLETE_REQUIRED: If the connection string specified by the application includes the DSN keyword, the Driver Manager copies the connection string specified by the application. Otherwise, it takes the same actions as it does when DriverCompletion is SQL_DRIVER_PROMPT.
  • SQL_DRIVER_NOPROMPT: The Driver Manager copies the connection string specified by the application.

If the connection string specified by the application contains the DRIVER keyword, the Driver Manager copies the connection string specified by the application.

Using the connection string it has constructed, the Driver Manager determines which driver to use, connects to that driver, and passes the connection string it has constructed to the driver; for more information about the interaction of the Driver Manager and the driver, see the "Comments" section in SQLConnect. If the connection string does not contain the DRIVER keyword, the Driver Manager determines which driver to use as follows:

  1. If the connection string contains the DSN keyword, the Driver Manager retrieves the driver associated with the data source from the system information.
  2. If the connection string does not contain the DSN keyword or the data source is not found, the Driver Manager retrieves the driver associated with the Default data source from the system information. (For more information, see "Default Subkey" in Chapter 19: Configuring Data Sources.) The Driver Manager changes the value of the DSN keyword in the connection string to "DEFAULT".
  3. If the DSN keyword in the connection string is set to "DEFAULT", the Driver Manager retrieves the driver associated with the Default data source from the system information.
  4. If the data source is not found and the Default data source is not found, the Driver Manager returns SQL_ERROR with SQLSTATE IM002 (Data source not found and no default driver specified).

File Data Sources

If the connection string specified by the application in the call to SQLDriverConnect contains the FILEDSN keyword, and this keyword is not superseded by either the DSN or DRIVER keyword, then the Driver Manager creates a connection string using the information in the .dsn file and the InConnectionString argument. The Driver Manager proceeds as follows:

  1. Checks whether the file name of the .dsn file is valid. If not, it returns SQL_ERROR with SQLSTATE IM014 (Invalid name of file DSN). If the file name is an empty string ("") and SQL_DRIVER_NOPROMPT is not specified, then the File-Open dialog box is displayed. If the file name contains a valid path but no file name or an invalid file name, and SQL_DRIVER_NOPROMPT is not specified, then the File-Open dialog box is displayed with the current directory set to the one specified in the file name. If the file name is an empty string ("") or the file name contains a valid path but no file name or an invalid file name, and SQL_DRIVER_NOPROMPT is specified, then SQL_ERROR is returned with SQLSTATE IM014 (Invalid name of file DSN).
  2. Reads all keywords in the [ODBC] section of the .dsn file. If the DRIVER keyword is not present, it returns SQL_ERROR with SQLSTATE IM012 (Driver keyword syntax error), except where the .dsn file is unshareable and thus contains only the DSN keyword.

    If the file data source is unshareable, the Driver Manager reads the value of the DSN keyword and connects as necessary to the user or system data source pointed to by the unshareable file data source. Steps 3 through 5 are not performed.

  3. Constructs a connection string for the driver. The driver connection string is the union of the keywords specified in the .dsn file and those specified in the original application connection string. Rules for the construction of the driver connection string where keywords overlap are as follows:
    • If the DRIVER keyword exists in the application connection string and the drivers specified by the DRIVER keywords are not the same in the .dsn file and the application connection string, then the driver information in the .dsn file is ignored and the driver information in the application connection string is used. If the drivers specified by the DRIVER keyword are the same in the .dsn file and the application's connection string, then where all keywords overlap, those specified in the application connection string have precedence over those specified in the .dsn file.
    • In the new connection string, the FILEDSN keyword is eliminated.
  4. Loads the driver by looking in the registry entry HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI\<Driver Name>\Driver where <Driver Name> is specified by the DRIVER keyword.
  5. Passes the driver the new connections string.

For examples of .dsn files, see "Connecting Using File Data Sources" in Chapter 6: Connecting to a Data Source or Driver.

SAVEFILE Keyword

If the connection string specified by the application contains the SAVEFILE keyword, then the Driver Manager saves the connection string in a .dsn file. The Driver Manager proceeds as follows:

  1. Checks whether the file name of the .dsn file included as the attribute value of the SAVEFILE keyword is valid. If not, it returns SQL_ERROR with SQLSTATE IM014 (Invalid name of file DSN). The validity of the file name is determined by standard system naming rules. If the file name is an empty string ("") and the DriverCompletion argument is not SQL_DRIVER_NOPROMPT, then the file name is valid. If the file name already exists, then if DriverCompletion is SQL_DRIVER_NOPROMPT, the file is overwritten. If DriverCompletion is SQL_DRIVER_PROMPT, SQL_DRIVER_COMPLETE, or SQL_DRIVER_COMPLETE_REQUIRED, a dialog box prompts the user to specify whether the file should be overwritten. If No is entered, then the File-Save dialog box appears.
  2. If the driver returns SQL_SUCCESS and the file name was not an empty string, then the Driver Manager writes the connection information returned in the OutConnectionString argument to the specified file with the format specified in the "Connection Strings" section earlier in this chapter.
  3. If the driver returns SQL_SUCCESS and the file name was an empty string (""), then the Driver Manager calls the File-Save common dialog box with the hwnd specified and writes the connection information returned in OutConnectionString to the file specified in the File-Save common dialog box with the format specified in the "Connection Strings" section earlier in this chapter.
  4. If the driver returns SQL_SUCCESS, it returns the OutConnectionString argument containing the connection string to the application.
  5. If the driver returns SQL_SUCCESS_WITH_INFO or SQL_ERROR, then the Driver Manager returns the SQLSTATE to the application.

Driver Guidelines

The driver checks whether the connection string passed to it by the Driver Manager contains the DSN or DRIVER keyword. If the connection string contains the DRIVER keyword, the driver cannot retrieve information about the data source from the system information. If the connection string contains the DSN keyword or does not contain either the DSN or the DRIVER keyword, the driver can retrieve information about the data source from the system information as follows:

  1. If the connection string contains the DSN keyword, the driver retrieves the information for the specified data source.
  2. If the connection string does not contain the DSN keyword, the specified data source is not found, or the DSN keyword is set to "DEFAULT", the driver retrieves the information for the Default data source.

The driver uses any information it retrieves from the system information to augment the information passed to it in the connection string. If the information in the system information duplicates information in the connection string, the driver uses the information in the connection string.

Based on the value of DriverCompletion, the driver prompts the user for connection information, such as the user ID and password, and connects to the data source:

  • SQL_DRIVER_PROMPT: The driver displays a dialog box, using the values from the connection string and system information (if any) as initial values. When the user exits the dialog box, the driver connects to the data source. It also constructs a connection string from the value of the DSN or DRIVER keyword in *InConnectionString and the information returned from the dialog box. It places this connection string in the *OutConnectionString buffer.
  • SQL_DRIVER_COMPLETE or SQL_DRIVER_COMPLETE_REQUIRED: If the connection string contains enough information, and that information is correct, the driver connects to the data source and copies *InConnectionString to *OutConnectionString. If any information is missing or incorrect, the driver takes the same actions as it does when DriverCompletion is SQL_DRIVER_PROMPT, except that if DriverCompletion is SQL_DRIVER_COMPLETE_REQUIRED, the driver disables the controls for any information not required to connect to the data source.
  • SQL_DRIVER_NOPROMPT: If the connection string contains enough information, the driver connects to the data source and copies *InConnectionString to *OutConnectionString. Otherwise, the driver returns SQL_ERROR for SQLDriverConnect.

On successful connection to the data source, the driver also sets *StringLength2Ptr to the length of the output connection string that is available to return in *OutConnectionString.

If the user cancels a dialog box presented by the Driver Manager or the driver, SQLDriverConnect returns SQL_NO_DATA.

For information about how the Driver Manager and the driver interact during the connection process, see SQLConnect.

If a driver supports SQLDriverConnect, the driver keyword section of the system information for the driver must contain the ConnectFunctions keyword with the second character set to "Y".

Connecting When Connection Pooling Is Enabled

Connection pooling allows an application to reuse a connection that has already been created. When SQLDriverConnect is called, the Driver Manager attempts to make the connection using a connection that is part of a pool of connections in an environment that has been designated for connection pooling. For more information on connection pooling, see SQLConnect.

The following restrictions apply when an application calls SQLDriverConnect to connect to a pooled connection:

  • No connection pooling processing is performed when the SAVEFILE keyword is specified in the connection string.
  • If connection pooling is enabled, SQLDriverConnect can be called only with a DriverCompletion argument of SQL_DRIVER_NOPROMPT; if SQLDriverConnect is called with any other DriverCompletion, SQLSTATE HY110 (Invalid driver completion) will be returned.

Connection Attributes

The SQL_ATTR_LOGIN_TIMEOUT connection attribute, set using SQLSetConnectAttr, defines the number of seconds to wait for a login request to complete with a successful connection by the driver before returning to the application. If the user is prompted to complete the connection string, a waiting period for each login request begins when the driver starts the connection process.

The driver opens the connection in SQL_MODE_READ_WRITE access mode by default. To set the access mode to SQL_MODE_READ_ONLY, the application must call SQLSetConnectAttr with the SQL_ATTR_ACCESS_MODE attribute prior to calling SQLDriverConnect.

If a default translation library is specified in the system information for the data source, the driver loads it. A different translation library can be loaded by calling SQLSetConnectAttr with the SQL_ATTR_TRANSLATE_LIB attribute. A translation option can be specified by calling SQLSetConnectAttr with the SQL_ATTR_TRANSLATE_OPTION option.

Code Example

For a code example, see the "Connecting with SQLDriverConnect" section of Chapter 6: Connecting to a Data Source or Driver.

Related Functions

For information about | See
Allocating a handle | SQLAllocHandle
Discovering and enumerating values required to connect to a data source | SQLBrowseConnect
Connecting to a data source | SQLConnect
Disconnecting from a data source | SQLDisconnect
Returning driver descriptions and attributes | SQLDrivers
Freeing a handle | SQLFreeHandle
Setting a connection attribute | SQLSetConnectAttr


SQLDrivers

Conformance

Version Introduced: ODBC 2.0
Standards Compliance: ODBC

Summary

SQLDrivers lists driver descriptions and driver attribute keywords. This function is implemented solely by the Driver Manager.

Syntax

SQLRETURN SQLDrivers(
     SQLHENV     EnvironmentHandle,
     SQLUSMALLINT     Direction,
     SQLCHAR *     DriverDescription,
     SQLSMALLINT     BufferLength1,
     SQLSMALLINT *     DescriptionLengthPtr,
     SQLCHAR *     DriverAttributes,
     SQLSMALLINT     BufferLength2,
     SQLSMALLINT *     AttributesLengthPtr);

Arguments

EnvironmentHandle
[Input]
Environment handle.
Direction
[Input]
Determines whether the Driver Manager fetches the next driver description in the list (SQL_FETCH_NEXT) or whether the search starts from the beginning of the list (SQL_FETCH_FIRST).
DriverDescription
[Output]
Pointer to a buffer in which to return the driver description.
BufferLength1
[Input]
Length of the *DriverDescription buffer, in bytes.
DescriptionLengthPtr
[Output]
Pointer to a buffer in which to return the total number of bytes (excluding the null-termination byte) available to return in *DriverDescription. If the number of bytes available to return is greater than or equal to BufferLength1, the driver description in *DriverDescription is truncated to BufferLength1 minus the length of a null-termination character.
DriverAttributes
[Output]
Pointer to a buffer in which to return the list of driver attribute value pairs (see "Comments").
BufferLength2
[Input]
Length of the *DriverAttributes buffer, in bytes. If the *DriverDescription value is a Unicode string (when calling SQLDriversW), the BufferLength argument must be an even number.
AttributesLengthPtr
[Output]
Pointer to a buffer in which to return the total number of bytes (excluding the null-termination byte) available to return in *DriverAttributes. If the number of bytes available to return is greater than or equal to BufferLength2, the list of attribute value pairs in *DriverAttributes is truncated to BufferLength2 minus the length of the null-termination character.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLDrivers returns either SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_ENV and a Handle of EnvironmentHandle. The following table lists the SQLSTATE values commonly returned by SQLDrivers and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | (DM) Driver Manager–specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | (DM) The buffer *DriverDescription was not large enough to return the entire driver description, so the description was truncated. The length of the entire driver description is returned in *DescriptionLengthPtr. (Function returns SQL_SUCCESS_WITH_INFO.) (DM) The buffer *DriverAttributes was not large enough to return the entire list of attribute value pairs, so the list was truncated. The length of the untruncated list of attribute value pairs is returned in *AttributesLengthPtr. (Function returns SQL_SUCCESS_WITH_INFO.)
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | (DM) The Driver Manager was unable to allocate memory required to support execution or completion of the function.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) The value specified for argument BufferLength1 was less than 0. (DM) The value specified for argument BufferLength2 was less than 0 or equal to 1.
HY103 | Invalid retrieval code | (DM) The value specified for the argument Direction was not equal to SQL_FETCH_FIRST or SQL_FETCH_NEXT.

Comments

SQLDrivers returns the driver description in the *DriverDescription buffer. It returns additional information about the driver in the *DriverAttributes buffer as a list of keyword-value pairs. All keywords listed in the system information for drivers will be returned for all drivers, except for CreateDSN, which is used to prompt creation of data sources and therefore is optional. Each pair is terminated with a null byte, and the entire list is terminated with a null byte (that is, two null bytes mark the end of the list). For example, a file-based driver using C syntax might return the following list of attributes ("\0" represents a null character):

FileUsage=1\0FileExtns=*.dbf\0\0

If *DriverAttributes is not large enough to hold the entire list, the list is truncated, SQLDrivers returns SQLSTATE 01004 (Data truncated), and the length of the list (excluding the final null-termination byte) is returned in *AttributesLengthPtr.

Driver attribute keywords are added from the system information when the driver is installed. For more information, see Chapter 18: Installing ODBC Components.

An application can call SQLDrivers multiple times to retrieve all driver descriptions. The Driver Manager retrieves this information from the system information. When there are no more driver descriptions, SQLDrivers returns SQL_NO_DATA. If SQLDrivers is called with SQL_FETCH_NEXT immediately after it returns SQL_NO_DATA, it returns the first driver description. For information about how an application uses the information returned by SQLDrivers, see "Choosing a Data Source or Driver" in Chapter 6: Connecting to a Data Source or Driver.

If SQL_FETCH_NEXT is passed to SQLDrivers the very first time it is called, SQLDrivers returns the first data source name.

Because SQLDrivers is implemented in the Driver Manager, it is supported for all drivers regardless of a particular driver's standards compliance.

Related Functions

For information about | See
Discovering and listing values required to connect to a data source | SQLBrowseConnect
Connecting to a data source | SQLConnect
Returning data source names | SQLDataSources
Connecting to a data source using a connection string or dialog box | SQLDriverConnect


SQLEndTran

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLEndTran requests a commit or rollback operation for all active operations on all statements associated with a connection. SQLEndTran can also request that a commit or rollback operation be performed for all connections associated with an environment.

Note For more information about what the Driver Manager maps this function to when an ODBC 3.x application is working with an ODBC 2.x driver, see "Mapping Replacement Functions for Backward Compatibility of Applications" in Chapter 17: Programming Considerations.

Syntax

SQLRETURN SQLEndTran(
     SQLSMALLINT     HandleType,
     SQLHANDLE     Handle,
     SQLSMALLINT     CompletionType);

Arguments

HandleType
[Input]
Handle type identifier. Contains either SQL_HANDLE_ENV (if Handle is an environment handle) or SQL_HANDLE_DBC (if Handle is a connection handle).
Handle
[Input]
The handle, of the type indicated by HandleType, indicating the scope of the transaction. See "Comments" for more information.
CompletionType
[Input]
One of the following two values:

SQL_COMMIT
SQL_ROLLBACK

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLEndTran returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with the appropriate HandleType and Handle. The following table lists the SQLSTATE values commonly returned by SQLEndTran and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08003 | Connection not open | (DM) The HandleType was SQL_HANDLE_DBC, and the Handle was not in a connected state.
08007 | Connection failure during transaction | The HandleType was SQL_HANDLE_DBC, and the connection associated with the Handle failed during the execution of the function, and it cannot be determined whether the requested COMMIT or ROLLBACK occurred before the failure.
25S01 | Transaction state unknown | One or more of the connections in Handle failed to complete the transaction with the outcome specified, and the outcome is unknown.
25S02 | Transaction is still active | The driver was not able to guarantee that all work in the global transaction could be completed atomically, and the transaction is still active.
25S03 | Transaction is rolled back | The driver was not able to guarantee that all work in the global transaction could be completed atomically, and all work in the transaction active in Handle was rolled back.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40002 | Integrity constraint violation | The CompletionType was SQL_COMMIT, and the commitment of changes caused integrity constraint violation. As a result, the transaction was rolled back.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *szMessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY010 | Function sequence error | (DM) An asynchronously executing function was called for a StatementHandle associated with the ConnectionHandle and was still executing when SQLEndTran was called. (DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for a StatementHandle associated with the ConnectionHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY012 | Invalid transaction operation code | (DM) The value specified for the argument CompletionType was neither SQL_COMMIT nor SQL_ROLLBACK.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY092 | Invalid attribute/option identifier | (DM) The value specified for the argument HandleType was neither SQL_HANDLE_ENV nor SQL_HANDLE_DBC.
HYC00 | Optional feature not implemented | The driver or data source does not support the ROLLBACK operation.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the ConnectionHandle does not support the function.

Comments

For an ODBC 3.x driver, if HandleType is SQL_HANDLE_ENV and Handle is a valid environment handle, then the Driver Manager will call SQLEndTran in each driver associated with the environment. The Handle argument for the call to a driver will be the driver's environment handle. For an ODBC 2.x driver, if HandleType is SQL_HANDLE_ENV and Handle is a valid environment handle, and there are multiple connections in a connected state in that environment, then the Driver Manager will call SQLTransact in the driver once for each connection in a connected state in that environment. The Handle argument in each call will be the connection's handle. In either case, the driver will attempt to commit or roll back transactions, depending on the value of CompletionType, on all connections that are in a connected state on that environment. Connections that are not active do not affect the transaction.

Note SQLEndTran cannot be used to commit or roll back transactions on a shared environment. SQLSTATE HY092 (Invalid attribute/option identifier) will be returned if SQLEndTran is called with Handle set to either the handle of a shared environment or the handle of a connection on a shared environment.

The Driver Manager will return SQL_SUCCESS only if it receives SQL_SUCCESS for each connection. If the Driver Manager receives SQL_ERROR on one or more connections, it returns SQL_ERROR to the application, and the diagnostic information is placed in the diagnostic data structure of the environment. To determine which connection or connections failed during the commit or rollback operation, the application can call SQLGetDiagRec for each connection.

Note The Driver Manager does not simulate a global transaction across all connections and therefore does not use two-phase commit protocols.

If CompletionType is SQL_COMMIT, SQLEndTran issues a commit request for all active operations on any statement associated with an affected connection. If CompletionType is SQL_ROLLBACK, SQLEndTran issues a rollback request for all active operations on any statement associated with an affected connection. If no transactions are active, SQLEndTran returns SQL_SUCCESS with no effect on any data sources. For more information, see "Committing and Rolling Back Transactions" in Chapter 14: Transactions.

If the driver is in manual-commit mode (by calling SQLSetConnectAttr with the SQL_ATTR_AUTOCOMMIT attribute set to SQL_AUTOCOMMIT_OFF), a new transaction is implicitly started when an SQL statement that can be contained within a transaction is executed against the current data source. For more information, see "Commit Mode" in Chapter 14: Transactions.

To determine how transaction operations affect cursors, an application calls SQLGetInfo with the SQL_CURSOR_ROLLBACK_BEHAVIOR and SQL_CURSOR_COMMIT_BEHAVIOR options. For more information, see the following paragraphs and also see "Effect of Transactions on Cursors and Prepared Statements" in Chapter 14: Transactions.

If the SQL_CURSOR_ROLLBACK_BEHAVIOR or SQL_CURSOR_COMMIT_BEHAVIOR value equals SQL_CB_DELETE, SQLEndTran closes and deletes all open cursors on all statements associated with the connection and discards all pending results. SQLEndTran leaves any statement present in an allocated (unprepared) state; the application can reuse them for subsequent SQL requests or can call SQLFreeStmt or SQLFreeHandle with a HandleType of SQL_HANDLE_STMT to deallocate them.

If the SQL_CURSOR_ROLLBACK_BEHAVIOR or SQL_CURSOR_COMMIT_BEHAVIOR value equals SQL_CB_CLOSE, SQLEndTran closes all open cursors on all statements associated with the connection. SQLEndTran leaves any statement present in a prepared state; the application can call SQLExecute for a statement associated with the connection without first calling SQLPrepare.

If the SQL_CURSOR_ROLLBACK_BEHAVIOR or SQL_CURSOR_COMMIT_BEHAVIOR value equals SQL_CB_PRESERVE, SQLEndTran does not affect open cursors associated with the connection. Cursors remain at the row they pointed to prior to the call to SQLEndTran.

For drivers and data sources that support transactions, calling SQLEndTran with either SQL_COMMIT or SQL_ROLLBACK when no transaction is active returns SQL_SUCCESS (indicating that there is no work to be committed or rolled back) and has no effect on the data source.

When a driver is in autocommit mode, the Driver Manager does not call SQLEndTran in the driver. SQLEndTran always returns SQL_SUCCESS regardless of whether it is called with a CompletionType of SQL_COMMIT or SQL_ROLLBACK.

Drivers or data sources that do not support transactions (SQLGetInfo option SQL_TXN_CAPABLE is SQL_TC_NONE) are effectively always in autocommit mode and therefore always return SQL_SUCCESS for SQLEndTran whether or not they are called with a CompletionType of SQL_COMMIT or SQL_ROLLBACK. Such drivers and data sources do not actually roll back transactions when requested to do so.

Related Functions

For information about | See
Returning information about a driver or data source | SQLGetInfo
Freeing a handle | SQLFreeHandle
Freeing a statement handle | SQLFreeStmt


SQLError

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

SQLError returns error or status information.

For more information about what the Driver Manager maps this function to when an ODBC 2.x application is working with an ODBC 3.x driver, see "Mapping Deprecated Functions" in Appendix G: Driver Guidelines for Backward Compatibility.


SQLExecDirect

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLExecDirect executes a preparable statement, using the current values of the parameter marker variables if any parameters exist in the statement. SQLExecDirect is the fastest way to submit an SQL statement for one-time execution.

Syntax

SQLRETURN SQLExecDirect(
     SQLHSTMT     StatementHandle,
     SQLCHAR *     StatementText,
     SQLINTEGER     TextLength);

Arguments

StatementHandle
[Input]
Statement handle.
StatementText
[Input]
SQL statement to be executed.
TextLength
[Input]
Length of *StatementText.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NEED_DATA, SQL_STILL_EXECUTING, SQL_ERROR, SQL_NO_DATA, or SQL_INVALID_HANDLE.

Diagnostics

When SQLExecDirect returns either SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLExecDirect and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01001 Cursor operation conflict *StatementText contained a positioned update or delete statement, and no rows or more than one row were updated or deleted. (For more information about updates to more than one row, see the description of the SQL_ATTR_SIMULATE_CURSOR Attribute in SQLSetStmtAttr.)

(Function returns SQL_SUCCESS_WITH_INFO.)
01003 | NULL value eliminated in set function | The argument StatementText contained a set function (such as AVG, MAX, MIN, and so on), but not the COUNT set function, and NULL argument values were eliminated before the function was applied. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | String or binary data returned for an input/output or output parameter resulted in the truncation of nonblank character or non-NULL binary data. If it was a string value, it was right-truncated. (Function returns SQL_SUCCESS_WITH_INFO.)
01006 | Privilege not revoked | *StatementText contained a REVOKE statement, and the user did not have the specified privilege. (Function returns SQL_SUCCESS_WITH_INFO.)
01007 | Privilege not granted | *StatementText was a GRANT statement, and the user could not be granted the specified privilege.
01S02 Option value changed A specified statement attribute was invalid because of implementation working conditions, so a similar value was temporarily substituted. (SQLGetStmtAttr can be called to determine what the temporarily substituted value is.) The substitute value is valid for the StatementHandle until the cursor is closed, at which point the statement attribute reverts to its previous value. The statement attributes that can be changed are:

SQL_ ATTR_CONCURRENCY
SQL_ ATTR_CURSOR_TYPE
SQL_ ATTR_KEYSET_SIZE
SQL_ ATTR_MAX_LENGTH
SQL_ ATTR_MAX_ROWS
SQL_ ATTR_QUERY_TIMEOUT
SQL_ ATTR_SIMULATE_CURSOR

(Function returns SQL_SUCCESS_WITH_INFO.)
01S07 Fractional truncation The data returned for an input/output or output parameter was truncated such that the fractional part of a numeric data type was truncated or the fractional portion of the time component of a time, timestamp, or interval data type was truncated.

(Function returns SQL_SUCCESS_WITH_INFO.)
07002 COUNT field incorrect The number of parameters specified in SQLBindParameter was less than the number of parameters in the SQL statement contained in *StatementText.

SQLBindParameter was called with ParameterValuePtr set to a null pointer, StrLen_or_IndPtr not set to SQL_NULL_DATA or SQL_DATA_AT_EXEC, and InputOutputType not set to SQL_PARAM_OUTPUT, so that the number of parameters specified in SQLBindParameter was greater than the number of parameters in the SQL statement contained in *StatementText.
07006 Restricted data type attribute violation The data value identified by the ValueType argument in SQLBindParameter for the bound parameter could not be converted to the data type identified by the ParameterType argument in SQLBindParameter.

The data value returned for a parameter bound as SQL_PARAM_INPUT_OUTPUT or SQL_PARAM_OUTPUT could not be converted to the data type identified by the ValueType argument in SQLBindParameter.

(If the data values for one or more rows could not be converted but one or more rows were successfully returned, this function returns SQL_SUCCESS_WITH_INFO.)
07S01 | Invalid use of default parameter | A parameter value, set with SQLBindParameter, was SQL_DEFAULT_PARAM, and the corresponding parameter did not have a default value.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
21S01 | Insert value list does not match column list | *StatementText contained an INSERT statement, and the number of values to be inserted did not match the degree of the derived table.
21S02 | Degree of derived table does not match column list | *StatementText contained a CREATE VIEW statement, and the unqualified column list (the number of columns specified for the view in the column-identifier arguments of the SQL statement) contained more names than the number of columns in the derived table defined by the query-specification argument of the SQL statement.
22001 | String data, right truncation | The assignment of a character or binary value to a column resulted in the truncation of nonblank character data or non-null binary data.
22002 | Indicator variable required but not supplied | NULL data was bound to an output parameter whose StrLen_or_IndPtr set by SQLBindParameter was a null pointer.
22003 Numeric value out of range *StatementText contained an SQL statement that contained a bound numeric parameter or literal, and the value caused the whole (as opposed to fractional) part of the number to be truncated when assigned to the associated table column.

Returning a numeric value (as numeric or string) for one or more input/output or output parameters would have caused the whole (as opposed to fractional) part of the number to be truncated.
22007 Invalid datetime format *StatementText contained an SQL statement that contained a date, time, or timestamp structure as a bound parameter, and the parameter was, respectively, an invalid date, time, or timestamp.

An input/output or output parameter was bound to a date, time, or timestamp C structure, and a value in the returned parameter was, respectively, an invalid date, time, or timestamp. (Function returns SQL_SUCCESS_WITH_INFO.)
22008 Datetime field overflow *StatementText contained an SQL statement that contained a datetime expression that, when computed, resulted in a date, time, or timestamp structure that was invalid.

A datetime expression computed for an input/output or output parameter resulted in a date, time, or timestamp C structure that was invalid.
22012 Division by zero *StatementText contained an SQL statement that contained an arithmetic expression that caused division by zero.

An arithmetic expression calculated for an input/output or output parameter resulted in division by zero.
22015 Interval field overflow *StatementText contained an exact numeric or interval parameter that, when converted to an interval SQL data type, caused a loss of significant digits.

*StatementText contained an interval parameter with more than one field that, when converted to a numeric data type in a column, had no representation in the numeric data type.

*StatementText contained parameter data that was assigned to an interval SQL type, and there was no representation of the value of the C type in the interval SQL type.

Assigning an input/output or output parameter that was an exact numeric or interval SQL type to an interval C type caused a loss of significant digits.

When an input/output or output parameter was assigned to an interval C structure, there was no representation of the data in the interval data structure.
22018 Invalid character value for cast specification *StatementText contained a C type that was an exact or approximate numeric, a datetime, or an interval data type; the SQL type of the column was a character data type; and the value in the column was not a valid literal of the bound C type.

When an input/output or output parameter was returned, the SQL type was an exact or approximate numeric, a datetime, or an interval data type; the C type was SQL_C_CHAR; and the value in the column was not a valid literal of the bound SQL type.
22019 | Invalid escape character | *StatementText contained an SQL statement that contained a LIKE predicate with an ESCAPE in the WHERE clause, and the length of the escape character following ESCAPE was not equal to 1.
22025 | Invalid escape sequence | *StatementText contained an SQL statement that contained "LIKE pattern value ESCAPE escape character" in the WHERE clause, and the character following the escape character in the pattern value was not one of "%" or "_".
23000 | Integrity constraint violation | *StatementText contained an SQL statement that contained a parameter or literal. The parameter value was NULL for a column defined as NOT NULL in the associated table column, a duplicate value was supplied for a column constrained to contain only unique values, or some other integrity constraint was violated.
24000 Invalid cursor state A cursor was positioned on the StatementHandle by SQLFetch or SQLFetchScroll. This error is returned by the Driver Manager if SQLFetch or SQLFetchScroll has not returned SQL_NO_DATA, and is returned by the driver if SQLFetch or SQLFetchScroll has returned SQL_NO_DATA.

A cursor was open but not positioned on the StatementHandle.

*StatementText contained a positioned update or delete statement, and the cursor was positioned before the start of the result set or after the end of the result set.
34000 | Invalid cursor name | *StatementText contained a positioned update or delete statement, and the cursor referenced by the statement being executed was not open.
3D000 | Invalid catalog name | The catalog name specified in StatementText was invalid.
3F000 | Invalid schema name | The schema name specified in StatementText was invalid.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
42000 Syntax error or access violation *StatementText contained an SQL statement that was not preparable or contained a syntax error.

The user did not have permission to execute the SQL statement contained in *StatementText.
42S01 | Base table or view already exists | *StatementText contained a CREATE TABLE or CREATE VIEW statement, and the table name or view name specified already exists.
42S02 Base table or view not found *StatementText contained a DROP TABLE or a DROP VIEW statement, and the specified table name or view name did not exist.

*StatementText contained an ALTER TABLE statement, and the specified table name did not exist.

*StatementText contained a CREATE VIEW statement, and a table name or view name defined by the query specification did not exist.

*StatementText contained a CREATE INDEX statement, and the specified table name did not exist.

*StatementText contained a GRANT or REVOKE statement, and the specified table name or view name did not exist.

*StatementText contained a SELECT statement, and a specified table name or view name did not exist.

*StatementText contained a DELETE, INSERT, or UPDATE statement, and the specified table name did not exist.

*StatementText contained a CREATE TABLE statement, and a table specified in a constraint (referencing a table other than the one being created) did not exist.

*StatementText contained a CREATE SCHEMA statement, and a specified table name or view name did not exist.
42S11 Index already exists *StatementText contained a CREATE INDEX statement, and the specified index name already existed.

*StatementText contained a CREATE SCHEMA statement, and the specified index name already existed.
42S12 | Index not found | *StatementText contained a DROP INDEX statement, and the specified index name did not exist.
42S21 | Column already exists | *StatementText contained an ALTER TABLE statement, and the column specified in the ADD clause is not unique or identifies an existing column in the base table.
42S22 Column not found *StatementText contained a CREATE INDEX statement, and one or more of the column names specified in the column list did not exist.

*StatementText contained a GRANT or REVOKE statement, and a specified column name did not exist.

*StatementText contained a SELECT, DELETE, INSERT, or UPDATE statement, and a specified column name did not exist.

*StatementText contained a CREATE TABLE statement, and a column specified in a constraint (referencing a table other than the one being created) did not exist.

*StatementText contained a CREATE SCHEMA statement, and a specified column name did not exist.
44000 WITH CHECK OPTION violation The argument StatementText contained an INSERT statement performed on a viewed table or a table derived from the viewed table that was created by specifying WITH CHECK OPTION, such that one or more rows affected by the INSERT statement will no longer be present in the viewed table.

The argument StatementText contained an UPDATE statement performed on a viewed table or a table derived from the viewed table that was created by specifying WITH CHECK OPTION, such that one or more rows affected by the UPDATE statement will no longer be present in the viewed table.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY009 | Invalid use of null pointer | (DM) *StatementText was a null pointer.
HY010 Function sequence error (DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 Invalid string or buffer length (DM) The argument TextLength was less than or equal to 0 but not equal to SQL_NTS.

A parameter value, set with SQLBindParameter, was a null pointer, and the parameter length value was not 0, SQL_NULL_DATA, SQL_DATA_AT_EXEC, SQL_DEFAULT_PARAM, or less than or equal to SQL_LEN_DATA_AT_EXEC_OFFSET.

A parameter value, set with SQLBindParameter, was not a null pointer; the C data type was SQL_C_BINARY or SQL_C_CHAR; and the parameter length value was less than 0 but was not SQL_NTS, SQL_NULL_DATA, SQL_DATA_AT_EXEC, SQL_DEFAULT_PARAM, or less than or equal to SQL_LEN_DATA_AT_EXEC_OFFSET.

A parameter length value bound by SQLBindParameter was set to SQL_DATA_AT_EXEC; the SQL type was either SQL_LONGVARCHAR, SQL_LONGVARBINARY, or a long data source–specific data type; and the SQL_NEED_LONG_DATA_LEN information type in SQLGetInfo was "Y".
HY105 | Invalid parameter type | The value specified for the argument InputOutputType in SQLBindParameter was SQL_PARAM_OUTPUT, and the parameter was an input parameter.
HY109 | Invalid cursor position | *StatementText contained a positioned update or delete statement, and the cursor was positioned (by SQLSetPos or SQLFetchScroll) on a row that had been deleted or could not be fetched.
HYC00 Optional feature not implemented The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source.

The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.
HYT00 | Timeout expired | The query timeout period expired before the data source returned the result set. The timeout period is set through SQLSetStmtAttr, SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

The application calls SQLExecDirect to send an SQL statement to the data source. For more information about direct execution, see "Direct Execution" in Chapter 9: Executing Statements. The driver modifies the statement to use the form of SQL used by the data source and then submits it to the data source. In particular, the driver modifies the escape sequences used to define certain features in SQL. For the syntax of escape sequences, see "Escape Sequences in ODBC" in Chapter 8: SQL Statements.

The application can include one or more parameter markers in the SQL statement. To include a parameter marker, the application embeds a question mark (?) into the SQL statement at the appropriate position. For information about parameters, see "Statement Parameters" in Chapter 9: Executing Statements.

If the SQL statement is a SELECT statement and if the application called SQLSetCursorName to associate a cursor with a statement, then the driver uses the specified cursor. Otherwise, the driver generates a cursor name.

If the data source is in manual-commit mode (requiring explicit transaction initiation) and a transaction has not already been initiated, the driver initiates a transaction before it sends the SQL statement. For more information, see "Manual-Commit Mode" in Chapter 14: Transactions.

If an application uses SQLExecDirect to submit a COMMIT or ROLLBACK statement, it will not be interoperable between DBMS products. To commit or roll back a transaction, an application calls SQLEndTran.

If SQLExecDirect encounters a data-at-execution parameter, it returns SQL_NEED_DATA. The application sends the data using SQLParamData and SQLPutData. See SQLBindParameter, SQLParamData, SQLPutData, and "Sending Long Data" in Chapter 9: Executing Statements for more information.

If SQLExecDirect executes a searched update or delete statement that does not affect any rows at the data source, the call to SQLExecDirect returns SQL_NO_DATA.

If the value of the SQL_ATTR_PARAMSET_SIZE statement attribute is greater than 1 and the SQL statement contains at least one parameter marker, SQLExecDirect will execute the SQL statement once for each set of parameter values from the arrays pointed to by the ParameterValuePointer argument in the call to SQLBindParameter. For more information, see "Arrays of Parameter Values" in Chapter 9: Executing Statements.

If bookmarks are turned on and a query is executed that cannot support bookmarks, the driver should attempt to coerce the environment to one that supports bookmarks by changing an attribute value and returning SQLSTATE 01S02 (Option value changed). If the attribute cannot be changed, the driver should return SQLSTATE HY024 (Invalid attribute value).

Note When using connection pooling, an application must not execute SQL statements that change the database or the context of the database, such as the USE database statement in SQL Server, which changes the catalog used by a data source.

Code Example

See SQLBindCol and SQLGetData.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Executing a commit or rollback operation | SQLEndTran
Executing a prepared SQL statement | SQLExecute
Fetching multiple rows of data | SQLFetch
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Returning a cursor name | SQLGetCursorName
Fetching part or all of a column of data | SQLGetData
Returning the next parameter to send data for | SQLParamData
Preparing a statement for execution | SQLPrepare
Sending parameter data at execution time | SQLPutData
Setting a cursor name | SQLSetCursorName
Setting a statement attribute | SQLSetStmtAttr


SQLExecute

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLExecute executes a prepared statement, using the current values of the parameter marker variables if any parameter markers exist in the statement.

Syntax

SQLRETURN SQLExecute(
     SQLHSTMT     StatementHandle);

Arguments

StatementHandle
[Input]
Statement handle.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NEED_DATA, SQL_STILL_EXECUTING, SQL_ERROR, SQL_NO_DATA, or SQL_INVALID_HANDLE.

Diagnostics

When SQLExecute returns either SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLExecute and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01001 Cursor operation conflict The prepared statement associated with the StatementHandle contained a positioned update or delete statement, and no rows or more than one row were updated or deleted. (For more information about updates to more than one row, see the description of the SQL_ATTR_SIMULATE_CURSOR Attribute in SQLSetStmtAttr.)

(Function returns SQL_SUCCESS_WITH_INFO.)
01003 | NULL value eliminated in set function | The prepared statement associated with StatementHandle contained a set function (such as AVG, MAX, MIN, and so on), but not the COUNT set function, and NULL argument values were eliminated before the function was applied. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | String or binary data returned for an output parameter resulted in the truncation of nonblank character or non-NULL binary data. If it was a string value, it was right-truncated. (Function returns SQL_SUCCESS_WITH_INFO.)
01006 | Privilege not revoked | The prepared statement associated with the StatementHandle was a REVOKE statement, and the user did not have the specified privilege. (Function returns SQL_SUCCESS_WITH_INFO.)
01007 | Privilege not granted | The prepared statement associated with the StatementHandle was a GRANT statement, and the user could not be granted the specified privilege.
01S02 | Option value changed | A specified statement attribute was invalid because of implementation working conditions, so a similar value was temporarily substituted. (SQLGetStmtAttr can be called to determine what the temporarily substituted value is.) The substitute value is valid for the StatementHandle until the cursor is closed, at which point the statement attribute reverts to its previous value. The statement attributes that can be changed are: SQL_ATTR_CONCURRENCY, SQL_ATTR_CURSOR_TYPE, SQL_ATTR_KEYSET_SIZE, SQL_ATTR_MAX_LENGTH, SQL_ATTR_MAX_ROWS, SQL_ATTR_QUERY_TIMEOUT, and SQL_ATTR_SIMULATE_CURSOR. (Function returns SQL_SUCCESS_WITH_INFO.)
01S07 Fractional truncation The data returned for an input/output or output parameter was truncated such that the fractional part of a numeric data type was truncated or the fractional portion of the time component of a time, timestamp, or interval data type was truncated.

(Function returns SQL_SUCCESS_WITH_INFO.)
07002 COUNT field incorrect The number of parameters specified in SQLBindParameter was less than the number of parameters in the SQL statement contained in *StatementText.

SQLBindParameter was called with ParameterValuePtr set to a null pointer, StrLen_or_IndPtr not set to SQL_NULL_DATA or SQL_DATA_AT_EXEC, and InputOutputType not set to SQL_PARAM_OUTPUT, so that the number of parameters specified in SQLBindParameter was greater than the number of parameters in the SQL statement contained in *StatementText.
07006 Restricted data type attribute violation The data value identified by the ValueType argument in SQLBindParameter for the bound parameter could not be converted to the data type identified by the ParameterType argument in SQLBindParameter.

The data value returned for a parameter bound as SQL_PARAM_INPUT_OUTPUT or SQL_PARAM_OUTPUT could not be converted to the data type identified by the ValueType argument in SQLBindParameter.

(If the data values for one or more rows could not be converted but one or more rows were successfully returned, this function returns SQL_SUCCESS_WITH_INFO.)
07S01 | Invalid use of default parameter | A parameter value, set with SQLBindParameter, was SQL_DEFAULT_PARAM, and the corresponding parameter was not a parameter for an ODBC canonical procedure invocation.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
21S02 | Degree of derived table does not match column list | The prepared statement associated with the StatementHandle contained a CREATE VIEW statement, and the unqualified column list (the number of columns specified for the view in the column-identifier arguments of the SQL statement) contained more names than the number of columns in the derived table defined by the query-specification argument of the SQL statement.
22001 | String data, right truncation | The assignment of a character or binary value to a column resulted in the truncation of nonblank (character) or non-null (binary) characters or bytes.
22002 | Indicator variable required but not supplied | NULL data was bound to an output parameter whose StrLen_or_IndPtr set by SQLBindParameter was a null pointer.
22003 Numeric value out of range The prepared statement associated with the StatementHandle contained a bound numeric parameter, and the parameter value caused the whole (as opposed to fractional) part of the number to be truncated when assigned to the associated table column.

Returning a numeric value (as numeric or string) for one or more input/output or output parameters would have caused the whole (as opposed to fractional) part of the number to be truncated.
22007 Invalid datetime format The prepared statement associated with the StatementHandle contained an SQL statement that contained a date, time, or timestamp structure as a bound parameter, and the parameter was, respectively, an invalid date, time, or timestamp.

An input/output or output parameter was bound to a date, time, or timestamp C structure, and a value in the returned parameter was, respectively, an invalid date, time, or timestamp. (Function returns SQL_SUCCESS_WITH_INFO.)
22008 Datetime field overflow The prepared statement associated with the StatementHandle contained an SQL statement that contained a datetime expression that, when computed, resulted in a date, time, or timestamp structure that was invalid.

A datetime expression computed for an input/output or output parameter resulted in a date, time, or timestamp C structure that was invalid.
22012 Division by zero The prepared statement associated with the StatementHandle contained an arithmetic expression that caused division by zero.

An arithmetic expression calculated for an input/output or output parameter resulted in division by zero.
22015 Interval field overflow *StatementText contained an exact numeric or interval parameter that, when converted to an interval SQL data type, caused a loss of significant digits.

*StatementText contained an interval parameter with more than one field that, when converted to a numeric data type in a column, had no representation in the numeric data type.

*StatementText contained parameter data that was assigned to an interval SQL type, and there was no representation of the value of the C type in the interval SQL type.

Assigning an input/output or output parameter that was an exact numeric or interval SQL type to an interval C type caused a loss of significant digits.

When an input/output or output parameter was assigned to an interval C structure, there was no representation of the data in the interval data structure.
22018 Invalid character value for cast specification *StatementText contained a C type that was an exact or approximate numeric, a datetime, or an interval data type; the SQL type of the column was a character data type; and the value in the column was not a valid literal of the bound C type.

When an input/output or output parameter was returned, the SQL type was an exact or approximate numeric, a datetime, or an interval data type; the C type was SQL_C_CHAR; and the value in the column was not a valid literal of the bound SQL type.
22019 Invalid escape
character 		The prepared statement associated with StatementHandle contained a LIKE predicate with an ESCAPE in the WHERE clause, and the length of the escape character following ESCAPE was not equal to 1.
22025 Invalid escape
sequence 		The prepared statement associated with StatementHandle contained "LIKE pattern value ESCAPE escape character" in the WHERE clause, and the character following the escape character in the pattern value was not one of "%" or "_".
23000 | Integrity constraint violation | The prepared statement associated with the StatementHandle contained a parameter. The parameter value was NULL for a column defined as NOT NULL in the associated table column, a duplicate value was supplied for a column constrained to contain only unique values, or some other integrity constraint was violated.
24000 Invalid cursor state A cursor was positioned on the StatementHandle by SQLFetch or SQLFetchScroll. This error is returned by the Driver Manager if SQLFetch or SQLFetchScroll has not returned SQL_NO_DATA, and is returned by the driver if SQLFetch or SQLFetchScroll has returned SQL_NO_DATA.

A cursor was open on the StatementHandle.

The prepared statement associated with the StatementHandle contained a positioned update or delete statemen,t and the cursor was positioned before the start of the result set or after the end of the result set.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
42000 | Syntax error or access violation | The user did not have permission to execute the prepared statement associated with the StatementHandle.
44000 WITH CHECK OPTION violation The prepared statement associated with StatementHandle contained an INSERT statement performed on a viewed table or a table derived from the viewed table that was created by specifying WITH CHECK OPTION, such that one or more rows affected by the INSERT statement will no longer be present in the viewed table.

The prepared statement associated with the StatementHandle contained an UPDATE statement performed on a viewed table or a table derived from the viewed table that was created by specifying WITH CHECK OPTION, such that one or more rows affected by the UPDATE statement will no longer be present in the viewed table.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 Memory allocation
error 		The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY010 Function sequence error (DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.

(DM) The StatementHandle was not prepared.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 Invalid string or buffer length A parameter value, set with SQLBindParameter, was a null pointer, and the parameter length value was not 0, SQL_NULL_DATA, SQL_DATA_AT_EXEC, SQL_DEFAULT_PARAM, or less than or equal to SQL_LEN_DATA_AT_EXEC_OFFSET.

A parameter value, set with SQLBindParameter, was not a null pointer; the C data type was SQL_C_BINARY or SQL_C_CHAR; and the parameter length value was less than 0 but was not SQL_NTS, SQL_NULL_DATA, SQL_DEFAULT_PARAM, or SQL_DATA_AT_EXEC, or less than or equal to SQL_LEN_DATA_AT_EXEC_OFFSET.

A parameter length value bound by SQLBindParameter was set to SQL_DATA_AT_EXEC; the SQL type was either SQL_LONGVARCHAR, SQL_LONGVARBINARY, or a long data source–specific data type; and the SQL_NEED_LONG_DATA_LEN information type in SQLGetInfo was "Y".
HY105 | Invalid parameter type | The value specified for the argument InputOutputType in SQLBindParameter was SQL_PARAM_OUTPUT, and the parameter was an input parameter.
HY109 | Invalid cursor position | The prepared statement was a positioned update or delete statement, and the cursor was positioned (by SQLSetPos or SQLFetchScroll) on a row that had been deleted or could not be fetched.
HYC00 Optional feature not implemented The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source.

The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.
HYT00 | Timeout expired | The query timeout period expired before the data source returned the result set. The timeout period is set through SQLSetStmtAttr, SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

SQLExecute can return any SQLSTATE that can be returned by SQLPrepare, based on when the data source evaluates the SQL statement associated with the statement.

Comments

SQLExecute executes a statement prepared by SQLPrepare. After the application processes or discards the results from a call to SQLExecute, the application can call SQLExecute again with new parameter values. For more information about prepared execution, see "Prepared Execution" in Chapter 9: Executing Statements.

To execute a SELECT statement more than once, the application must call SQLCloseCursor before reexecuting the SELECT statement.

If the data source is in manual-commit mode (requiring explicit transaction initiation) and a transaction has not already been initiated, the driver initiates a transaction before it sends the SQL statement. For more information, see "Manual-Commit Mode" in Chapter 14: Transactions.

If an application uses SQLPrepare to prepare and SQLExecute to submit a COMMIT or ROLLBACK statement, it will not be interoperable between DBMS products. To commit or roll back a transaction, call SQLEndTran.

If SQLExecute encounters a data-at-execution parameter, it returns SQL_NEED_DATA. The application sends the data using SQLParamData and SQLPutData. See SQLBindParameter, SQLParamData, SQLPutData, and "Sending Long Data" in Chapter 9: Executing Statements for more information.

If SQLExecute executes a searched update or delete statement that does not affect any rows at the data source, the call to SQLExecute returns SQL_NO_DATA.

If the value of the SQL_ATTR_PARAMSET_SIZE statement attribute is greater than 1 and the SQL statement contains at least one parameter marker, SQLExecute executes the SQL statement once for each set of parameter values in the arrays pointed to by the *ParameterValuePtr argument in the calls to SQLBindParameter. For more information, see "Arrays of Parameter Values" in Chapter 9: Executing Statements.

If bookmarks are enabled and a query is executed that cannot support bookmarks, the driver should attempt to coerce the environment to one that supports bookmarks by changing an attribute value and returning SQLSTATE 01S02 (Option value changed). If the attribute cannot be changed, the driver should return SQLSTATE HY024 (Invalid attribute value).

Note When using connection pooling, an application must not execute SQL statements that change the database or the context of the database, such as the USE database statement in SQL Server, which changes the catalog used by a data source.

Code Example

See SQLBindParameter, SQLBulkOperations, SQLPutData, and SQLSetPos.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Closing the cursor | SQLCloseCursor
Executing a commit or rollback operation | SQLEndTran
Executing an SQL statement | SQLExecDirect
Fetching multiple rows of data | SQLFetch
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Freeing a statement handle | SQLFreeStmt
Returning a cursor name | SQLGetCursorName
Fetching part or all of a column of data | SQLGetData
Returning the next parameter to send data for | SQLParamData
Preparing a statement for execution | SQLPrepare
Sending parameter data at execution time | SQLPutData
Setting a cursor name | SQLSetCursorName
Setting a statement attribute | SQLSetStmtAttr


SQLExtendedFetch

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

SQLExtendedFetch fetches the specified rowset of data from the result set and returns data for all bound columns. Rowsets can be specified at an absolute or relative position or by bookmark.

Note In ODBC 3.x, SQLExtendedFetch has been replaced by SQLFetchScroll. ODBC 3.x applications should not call SQLExtendedFetch; instead they should call SQLFetchScroll. The Driver Manager maps SQLFetchScroll to SQLExtendedFetch when working with an ODBC 2.x driver. ODBC 3.x drivers should support SQLExtendedFetch if they want to work with ODBC 2.x applications that call it. For more information, see "Comments" and "Block Cursors, Scrollable Cursors, and Backward Compatibility" in Appendix G: Driver Guidelines for Backward Compatibility.

Syntax

SQLRETURN SQLExtendedFetch(
     SQLHSTMT     StatementHandle,
     SQLUSMALLINT     FetchOrientation,
     SQLINTEGER     FetchOffset,
     SQLUINTEGER *     RowCountPtr,
     SQLUSMALLINT *     RowStatusArray);

Arguments

StatementHandle
[Input]
Statement handle.
FetchOrientation
[Input]
Type of fetch. This is the same as FetchOrientation in SQLFetchScroll.
FetchOffset
[Input]
Number of the row to fetch. This is the same as FetchOffset in SQLFetchScroll, with one exception. When FetchOrientation is SQL_FETCH_BOOKMARK, FetchOffset is a fixed-length bookmark, not an offset from a bookmark. In other words, SQLExtendedFetch retrieves the bookmark from this argument, not the SQL_ATTR_FETCH_BOOKMARK_PTR statement attribute. It does not support variable-length bookmarks and does not support fetching a rowset at an offset (other than 0) from a bookmark.
RowCountPtr
[Output]
Pointer to a buffer in which to return the number of rows actually fetched. This buffer is used in the same manner as the buffer specified by the SQL_ATTR_ROWS_FETCHED_PTR statement attribute. This buffer is used only by SQLExtendedFetch. It is not used by SQLFetch or SQLFetchScroll.
RowStatusArray
[Output]
Pointer to an array in which to return the status of each row. This array is used in the same manner as the array specified by the SQL_ATTR_ROW_STATUS_PTR statement attribute.

However, the address of this array is not stored in the SQL_DESC_STATUS_ARRAY_PTR field in the IRD. Furthermore, this array is used only by SQLExtendedFetch and by SQLBulkOperations with an Operation of SQL_ADD or SQLSetPos when it is called after SQLExtendedFetch. It is not used by SQLFetch or SQLFetchScroll, and it is not used by SQLBulkOperations or SQLSetPos when they are called after SQLFetch or SQLFetchScroll. It is also not used when SQLBulkOperations with an Operation of SQL_ADD is called before any fetch function is called. In other words, it is used only in statement state S7. It is not used in statement states S5 or S6. For more information, see "Statement Transitions" in Appendix B: ODBC State Transition Tables.

Applications should provide a valid pointer in the RowStatusArray argument; if not, the behavior of SQLExtendedFetch and the behavior of calls to SQLBulkOperations or SQLSetPos after a cursor has been positioned by SQLExtendedFetch are undefined.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLExtendedFetch returns either SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLError. The following table lists the SQLSTATE values commonly returned by SQLExtendedFetch and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise. If an error occurs on a single column, SQLGetDiagField can be called with a DiagIdentifier of SQL_DIAG_COLUMN_NUMBER to determine the column the error occurred on; and SQLGetDiagField can be called with a DiagIdentifier of SQL_DIAG_ROW_NUMBER to determine the row containing that column.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 String data, right truncated String or binary data returned for a column resulted in the truncation of nonblank character or non-NULL binary data. If it was a string value, it was right-truncated. If it was a numeric value, the fractional part of the number was truncated.
(Function returns SQL_SUCCESS_WITH_INFO.)
01S01 | Error in row | An error occurred while fetching one or more rows. (Function returns SQL_SUCCESS_WITH_INFO.)
01S06 | Attempt to fetch before the result set returned the first rowset | The requested rowset overlapped the start of the result set when the current position was beyond the first row, and either FetchOrientation was SQL_PRIOR or FetchOrientation was SQL_RELATIVE with a negative FetchOffset whose absolute value was less than or equal to the current SQL_ROWSET_SIZE. (Function returns SQL_SUCCESS_WITH_INFO.)
01S07 Fractional truncation The data returned for a column was truncated. For numeric data types, the fractional part of the number was truncated. For time, timestamp, and interval data types containing a time component, the fractional portion of the time was truncated.

(Function returns SQL_SUCCESS_WITH_INFO.)
07006 | Restricted data type attribute violation | A data value could not be converted to the C data type specified by TargetType in SQLBindCol.
07009 | Invalid descriptor index | Column 0 was bound with SQLBindCol, and the SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_OFF.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
22002 Indicator variable required but not supplied NULL data was fetched into a column whose StrLen_or_IndPtr set by SQLBindCol was a null pointer.

(Function returns SQL_SUCCESS_WITH_INFO.)
22003 Numeric value out of range Returning the numeric value (as numeric or string) for one or more columns would have caused the whole (as opposed to fractional) part of the number to be truncated.

(Function returns SQL_SUCCESS_WITH_INFO.)

For more information, see Guidelines for Interval and Numeric Data Types in Appendix D: Data Types.
22007 Invalid datetime format A character column in the result set was bound to a date, time, or timestamp C structure, and a value in the column was, respectively, an invalid date, time, or timestamp.

(Function returns SQL_SUCCESS_WITH_INFO.)
22012 Division by zero A value from an arithmetic expression was returned, which resulted in division by zero.

(Function returns SQL_SUCCESS_WITH_INFO.)
22015 Interval field overflow Assigning from an exact numeric or interval SQL type to an interval C type caused a loss of significant digits in the leading field.

When fetching data to an interval C type, there was no representation of the value of the SQL type in the interval C type.

(Function returns SQL_SUCCESS_WITH_INFO.)
22018 Invalid character value for cast specification The C type was an exact or approximate numeric, a datetime, or an interval data type; the SQL type of the column was a character data type; and the value in the column was not a valid literal of the bound C type.

(Function returns SQL_SUCCESS_WITH_INFO.)
24000 | Invalid cursor state | The StatementHandle was in an executed state, but no result set was associated with the StatementHandle.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLError in the *MessageText buffer describes the error and its cause.
HY001 Memory allocation
error 		The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle, and then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY010 Function sequence error (DM) The specified StatementHandle was not in an executed state. The function was called without first calling SQLExecDirect, SQLExecute, or a catalog function.

(DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.

(DM) SQLExtendedFetch was called for the StatementHandle after SQLFetch or SQLFetchScroll was called and before SQLFreeStmt was called with the SQL_CLOSE option.

(DM) SQLBulkOperations was called for a statement before SQLFetch, SQLFetchScroll, or SQLExtendedFetch was called, and then SQLExtendedFetch was called before SQLFreeStmt was called with the SQL_CLOSE option.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY106 Fetch type out of range (DM) The value specified for the argument FetchOrientation was invalid. (See "Comments.")

The argument FetchOrientation was SQL_FETCH_BOOKMARK, and the SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_OFF.

The value of the SQL_CURSOR_TYPE statement option was SQL_CURSOR_FORWARD_ONLY, and the value of argument FetchOrientation was not SQL_FETCH_NEXT.

The argument FetchOrientation was SQL_FETCH_RESUME.
HY107 | Row value out of range | The value specified with the SQL_CURSOR_TYPE statement option was SQL_CURSOR_KEYSET_DRIVEN, but the value specified with the SQL_KEYSET_SIZE statement attribute was greater than 0 and less than the value specified with the SQL_ROWSET_SIZE statement attribute.
HY111 | Invalid bookmark value | The argument FetchOrientation was SQL_FETCH_BOOKMARK, and the bookmark specified in the FetchOffset argument was not valid.
HYC00 Optional feature not implemented Driver or data source does not support the specified fetch type.

The driver or data source does not support the conversion specified by the combination of the TargetType in SQLBindCol and the SQL data type of the corresponding column. This error applies only when the SQL data type of the column was mapped to a driver-specific SQL data type.
HYT00 | Timeout expired | The query timeout period expired before the data source returned the result set. The timeout period is set through SQLSetStmtOption, SQL_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

The behavior of SQLExtendedFetch is identical to that of SQLFetchScroll, with the following exceptions:

  • SQLExtendedFetch and SQLFetchScroll use different methods to return the number of rows fetched. SQLExtendedFetch returns the number of rows fetched in *RowCountPtr; SQLFetchScroll returns the number of rows fetched directly to the buffer pointed to by SQL_ATTR_ROWS_FETCHED_PTR. For more information, see the RowCountPtr argument.
  • SQLExtendedFetch and SQLFetchScroll return the status of each row in different arrays. For more information, see the RowStatusArray argument.
  • SQLExtendedFetch and SQLFetchScroll use different methods to retrieve the bookmark when FetchOrientation is SQL_FETCH_BOOKMARK. SQLExtendedFetch does not support variable-length bookmarks or fetching rowsets at an offset other than 0 from a bookmark. For more information, see the FetchOffset argument.
  • SQLExtendedFetch and SQLFetchScroll use different rowset sizes. SQLExtendedFetch uses the value of the SQL_ROWSET_SIZE statement attribute, and SQLFetchScroll uses the value of the SQL_ATTR_ROW_ARRAY_SIZE statement attribute.
  • SQLExtendedFetch has slightly different error handling semantics than SQLFetchScroll. For more information, see "Error Handling" in the "Comments" section of SQLFetchScroll.
  • SQLExtendedFetch does not support binding offsets (the SQL_ATTR_ROW_BIND_OFFSET_PTR statement attribute).
  • Calls to SQLExtendedFetch cannot be mixed with calls to SQLFetch or SQLFetchScroll, and if SQLBulkOperations is called before any fetch function is called, SQLExtendedFetch cannot be called until the cursor is closed and reopened. That is, SQLExtendedFetch can be called only in statement state S7. For more information, see "Statement Transitions" in Appendix B: ODBC State Transition Tables.

When an application calls SQLFetchScroll while using an ODBC 2.x driver, the Driver Manager maps this call to SQLExtendedFetch. For more information, see "SQLFetchScroll and ODBC 2.x Drivers" in SQLFetchScroll.

In ODBC 2.x, SQLExtendedFetch was called to fetch multiple rows and SQLFetch was called to fetch a single row. In ODBC 3.x, on the other hand, SQLFetch can be called to fetch multiple rows.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Performing bulk insert, update, or delete operations | SQLBulkOperations
Canceling statement processing | SQLCancel
Returning information about a column in a result set | SQLDescribeCol
Executing an SQL statement | SQLExecDirect
Executing a prepared SQL statement | SQLExecute
Returning the number of result set columns | SQLNumResultCols
Positioning the cursor, refreshing data in the rowset, or updating or deleting data in the result set | SQLSetPos
Setting a statement attribute | SQLSetStmtAttr


SQLFetch

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLFetch fetches the next rowset of data from the result set and returns data for all bound columns.

Syntax

SQLRETURN SQLFetch(
     SQLHSTMT     StatementHandle);

Arguments

StatementHandle
[Input]
Statement handle.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLFetch returns either SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLFetch and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise. If an error occurs on a single column, SQLGetDiagField can be called with a DiagIdentifier of SQL_DIAG_COLUMN_NUMBER to determine the column the error occurred on; and SQLGetDiagField can be called with a DiagIdentifier of SQL_DIAG_ROW_NUMBER to determine the row containing that column.

For all those SQLSTATEs that can return SQL_SUCCESS_WITH_INFO or SQL_ERROR (except 01xxx SQLSTATEs), SQL_SUCCESS_WITH_INFO is returned if an error occurs on one or more, but not all, rows of a multirow operation, and SQL_ERROR is returned if an error occurs on a single-row operation.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | String or binary data returned for a column resulted in the truncation of nonblank character or non-NULL binary data. If it was a string value, it was right-truncated.
01S01 Error in row An error occurred while fetching one or more rows.

(If this SQLSTATE is returned when an ODBC 3.x application is working with an ODBC 2.x driver, it can be ignored.)
01S07 Fractional truncation The data returned for a column was truncated. For numeric data types, the fractional part of the number was truncated. For time, timestamp, and interval data types containing a time component, the fractional portion of the time was truncated.

(Function returns SQL_SUCCESS_WITH_INFO.)
07006 Restricted data type attribute violation The data value of a column in the result set could not be converted to the data type specified by TargetType in SQLBindCol.

Column 0 was bound with a data type of SQL_C_BOOKMARK, and the SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE.

Column 0 was bound with a data type of SQL_C_VARBOOKMARK, and the SQL_ATTR_USE_BOOKMARKS statement attribute was not set to SQL_UB_VARIABLE.
07009 Invalid descriptor index The driver was an ODBC 2.x driver that does not support SQLExtendedFetch, and a column number specified in the binding for a column was 0.

Column 0 was bound, and the SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_OFF.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
22001 | String data, right truncated | A variable-length bookmark returned for a column was truncated.
22002 | Indicator variable required but not supplied | NULL data was fetched into a column whose StrLen_or_IndPtr set by SQLBindCol (or SQL_DESC_INDICATOR_PTR set by SQLSetDescField or SQLSetDescRec) was a null pointer.
22003 Numeric value out of range Returning the numeric value (as numeric or string) for one or more bound columns would have caused the whole (as opposed to fractional) part of the number to be truncated.

For more information, see "Converting Data from SQL to C Data Types" in Appendix D: Data Types.
22007 | Invalid datetime format | A character column in the result set was bound to a date, time, or timestamp C structure, and a value in the column was, respectively, an invalid date, time, or timestamp.
22012 | Division by zero | A value from an arithmetic expression was returned, which resulted in division by zero.
22015 Interval field overflow Assigning from an exact numeric or interval SQL type to an interval C type caused a loss of significant digits in the leading field.

When fetching data to an interval C type, there was no representation of the value of the SQL type in the interval C type.

22018 Invalid character value for cast specification A character column in the result set was bound to a character C buffer, and the column contained a character for which there was no representation in the character set of the buffer.

The C type was an exact or approximate numeric, a datetime, or an interval data type; the SQL type of the column was a character data type; and the value in the column was not a valid literal of the bound C type.

24000 | Invalid cursor state | The StatementHandle was in an executed state but no result set was associated with the StatementHandle.
40001 | Serialization failure | The transaction in which the fetch was executed was terminated to prevent deadlock.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 Memory allocation
error 		The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY010 Function sequence error (DM) The specified StatementHandle was not in an executed state. The function was called without first calling SQLExecDirect, SQLExecute or a catalog function.

(DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.

(DM) SQLFetch was called for the StatementHandle after SQLExtendedFetch was called and before SQLFreeStmt with the SQL_CLOSE option was called.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | The SQL_ATTR_USE_BOOKMARK statement attribute was set to SQL_UB_VARIABLE, and column 0 was bound to a buffer whose length was not equal to the maximum length for the bookmark for this result set. (This length is available in the SQL_DESC_OCTET_LENGTH field of the IRD and can be obtained by calling SQLDescribeCol, SQLColAttribute, or SQLGetDescField.)
HY107 | Row value out of range | The value specified with the SQL_ATTR_CURSOR_TYPE statement attribute was SQL_CURSOR_KEYSET_DRIVEN, but the value specified with the SQL_ATTR_KEYSET_SIZE statement attribute was greater than 0 and less than the value specified with the SQL_ATTR_ROW_ARRAY_SIZE statement attribute.
HYC00 | Optional feature not implemented | The driver or data source does not support the conversion specified by the combination of the TargetType in SQLBindCol and the SQL data type of the corresponding column.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

SQLFetch returns the next rowset in the result set. It can be called only while a result set exists—that is, after a call that creates a result set and before the cursor over that result set is closed. If any columns are bound, it returns the data in those columns. If the application has specified a pointer to a row status array or a buffer in which to return the number of rows fetched, SQLFetch returns this information as well. Calls to SQLFetch can be mixed with calls to SQLFetchScroll but cannot be mixed with calls to SQLExtendedFetch. For more information, see "Fetching a Row of Data" in Chapter 10: Retrieving Results (Basic).

If an ODBC 3.x application works with an ODBC 2.x driver, the Driver Manager maps SQLFetch calls to SQLExtendedFetch for an ODBC 2.x driver that supports SQLExtendedFetch. If the ODBC 2.x driver does not support SQLExtendedFetch, the Driver Manager maps SQLFetch calls to SQLFetch in the ODBC 2.x driver, which can fetch only a single row.

For more information, see "Block Cursors, Scrollable Cursors, and Backward Compatibility" in Appendix G: Driver Guidelines for Backward Compatibility.

Positioning the Cursor

When the result set is created, the cursor is positioned before the start of the result set. SQLFetch fetches the next rowset. It is equivalent to calling SQLFetchScroll with FetchOrientation set to SQL_FETCH_NEXT. For more information about cursors, see "Cursors" in Chapter 10: Retrieving Results (Basic) and "Block Cursors" in Chapter 11: Retrieving Results (Advanced).

The SQL_ATTR_ROW_ARRAY_SIZE statement attribute specifies the number of rows in the rowset. If the rowset being fetched by SQLFetch overlaps the end of the result set, SQLFetch returns a partial rowset. That is, if S + R – 1 is greater than L, where S is the starting row of the rowset being fetched, R is the rowset size, and L is the last row in the result set, then only the first L – S + 1 rows of the rowset are valid. The remaining rows are empty and have a status of SQL_ROW_NOROW.

After SQLFetch returns, the current row is the first row of the rowset.

The rules listed in the following table describe cursor positioning after a call to SQLFetch, based on the conditions listed in the second table below.

Condition | First row of new rowset
Before start | 1
CurrRowsetStart <= LastResultRow – RowsetSize[1] | CurrRowsetStart + RowsetSize[2]
CurrRowsetStart > LastResultRow – RowsetSize[1] | After end
After end | After end

[1] If the rowset size is changed between fetches, this is the rowset size that was used with the previous fetch.

[2] If the rowset size is changed between fetches, this is the rowset size that was used with the new fetch.

Notation | Meaning
Before start | The block cursor is positioned before the start of the result set. If the first row of the new rowset is before the start of the result set, SQLFetch returns SQL_NO_DATA.
After end | The block cursor is positioned after the end of the result set. If the first row of the new rowset is after the end of the result set, SQLFetch returns SQL_NO_DATA.
CurrRowsetStart | The number of the first row in the current rowset.
LastResultRow | The number of the last row in the result set.
RowsetSize | The rowset size.

For example, suppose a result set has 100 rows and the rowset size is 5. The following table shows the rowset and return code returned by SQLFetch for different starting positions.

Current rowset | Return code | New rowset | # of rows fetched
Before start | SQL_SUCCESS | 1 to 5 | 5
1 to 5 | SQL_SUCCESS | 6 to 10 | 5
52 to 56 | SQL_SUCCESS | 57 to 61 | 5
91 to 95 | SQL_SUCCESS | 96 to 100 | 5
93 to 97 | SQL_SUCCESS | 98 to 100. Rows 4 and 5 of the row status array are set to SQL_ROW_NOROW. | 3
96 to 100 | SQL_NO_DATA | None. | 0
99 to 100 | SQL_NO_DATA | None. | 0
After end | SQL_NO_DATA | None. | 0

Returning Data in Bound Columns

As SQLFetch returns each row, it places the data for each bound column in the buffer bound to that column. If no columns are bound, SQLFetch does not return any data but does move the block cursor forward. The data can still be retrieved with SQLGetData. If the cursor is a multirow cursor (that is, the SQL_ATTR_ROW_ARRAY_SIZE is greater than 1), SQLGetData can be called only if SQL_GD_BLOCK is returned when SQLGetInfo is called with an InfoType of SQL_GETDATA_EXTENSIONS. (For more information, see SQLGetData.)

For each bound column in a row, SQLFetch does the following:

  1. Sets the length/indicator buffer to SQL_NULL_DATA and proceeds to the next column if the data is NULL. If the data is NULL and no length/indicator buffer was bound, SQLFetch returns SQLSTATE 22002 (Indicator variable required but not supplied) for the row and proceeds to the next row. For information about how to determine the address of the length/indicator buffer, see "Buffer Addresses" in SQLBindCol.

    If the data for the column is not NULL, SQLFetch proceeds to step 2.

  2. If the SQL_ATTR_MAX_LENGTH statement attribute is set to a nonzero value and the column contains character or binary data, the data is truncated to SQL_ATTR_MAX_LENGTH bytes.

    Note The SQL_ATTR_MAX_LENGTH statement attribute is intended to reduce network traffic. It is generally implemented by the data source, which truncates the data before returning it across the network. Drivers and data sources are not required to support it. Therefore, to guarantee that data is truncated to a particular size, an application should allocate a buffer of that size and specify the size in the cbValueMax argument in SQLBindCol.

  3. Converts the data to the type specified by TargetType in SQLBindCol.
  4. If the data was converted to a variable-length data type, such as character or binary, SQLFetch checks whether the length of the data exceeds the length of the data buffer. If the length of character data (including the null-termination character) exceeds the length of the data buffer, SQLFetch truncates the data to the length of the data buffer less the length of a null-termination character. It then null-terminates the data. If the length of binary data exceeds the length of the data buffer, SQLFetch truncates it to the length of the data buffer. The length of the data buffer is specified with BufferLength in SQLBindCol.

    SQLFetch never truncates data converted to fixed-length data types; it always assumes that the length of the data buffer is the size of the data type.

  5. Places the converted (and possibly truncated) data in the data buffer. For information about how to determine the address of the data buffer, see "Buffer Addresses" in SQLBindCol.
  6. Places the length of the data in the length/indicator buffer. If the indicator pointer and the length pointer were both set to the same buffer (as a call to SQLBindCol does), the length is written in the buffer for valid data and SQL_NULL_DATA is written in the buffer for NULL data. If no length/indicator buffer was bound, SQLFetch does not return the length.
    • For character or binary data, this is the length of the data after conversion and before truncation due to the data buffer being too small. If the driver cannot determine the length of the data after conversion, as is sometimes the case with long data, it sets the length to SQL_NO_TOTAL. If data was truncated due to the SQL_ATTR_MAX_LENGTH statement attribute, the value of this attribute—as opposed to the actual length—is placed in the length/indicator buffer. This is because this attribute is designed to truncate data on the server before conversion, so the driver has no way of figuring out what the actual length is.
    • For all other data types, this is the length of the data after conversion; that is, it is the size of the type to which the data was converted.

    For information about how to determine the address of the length/indicator buffer, see "Buffer Addresses" in SQLBindCol.

  7. If the data is truncated during conversion without a loss of significant digits (for example, the real number 1.234 is truncated to the integer 1 when converted), SQLFetch returns SQLSTATE 01S07 (Fractional truncation) and SQL_SUCCESS_WITH_INFO. If the data is truncated because the length of the data buffer is too small (for example, the string "abcdef" is placed in a 4-byte buffer), SQLFetch returns SQLSTATE 01004 (Data truncated) and SQL_SUCCESS_WITH_INFO. If data is truncated due to the SQL_ATTR_MAX_LENGTH statement attribute, SQLFetch returns SQL_SUCCESS and does not return SQLSTATE 01S07 (Fractional truncation) or SQLSTATE 01004 (Data truncated). If data is truncated during conversion with a loss of significant digits (for example, if an SQL_INTEGER value greater than 100,000 were converted to an SQL_C_TINYINT), SQLFetch returns SQLSTATE 22003 (Numeric value out of range) and SQL_ERROR (if the rowset size is 1) or SQL_SUCCESS_WITH_INFO (if the rowset size is greater than 1).

The contents of the bound data buffer and the length/indicator buffer are undefined if SQLFetch or SQLFetchScroll does not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO.

Row Status Array

The row status array is used to return the status of each row in the rowset. The address of this array is specified with the SQL_ATTR_ROW_STATUS_PTR statement attribute. The array is allocated by the application and must have as many elements as are specified by the SQL_ATTR_ROW_ARRAY_SIZE statement attribute. Its values are set by SQLFetch, SQLFetchScroll, and SQLBulkOperations or SQLSetPos (except when they have been called after the cursor has been positioned by SQLExtendedFetch). If the value of the SQL_ATTR_ROW_STATUS_PTR statement attribute is a null pointer, these functions do not return the row status.

The contents of the row status array buffer are undefined if SQLFetch or SQLFetchScroll does not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO.

The following values are returned in the row status array.

Row status array value | Description
SQL_ROW_SUCCESS | The row was successfully fetched and has not changed since it was last fetched from this result set.
SQL_ROW_SUCCESS_WITH_INFO | The row was successfully fetched and has not changed since it was last fetched from this result set. However, a warning was returned about the row.
SQL_ROW_ERROR | An error occurred while fetching the row.
SQL_ROW_UPDATED[1],[2], and [3] | The row was successfully fetched and has changed since it was last fetched from this result set. If the row is fetched again from this result set or is refreshed by SQLSetPos, the status is changed to the row's new status.
SQL_ROW_DELETED[3] | The row has been deleted since it was last fetched from this result set.
SQL_ROW_ADDED[4] | The row was inserted by SQLBulkOperations. If the row is fetched again from this result set or is refreshed by SQLSetPos, its status is SQL_ROW_SUCCESS.
SQL_ROW_NOROW | The rowset overlapped the end of the result set, and no row was returned that corresponded to this element of the row status array.

[1] For keyset, mixed, and dynamic cursors, if a key value is updated, the row of data is considered to have been deleted and a new row added.

[2] Some drivers cannot detect updates to data and therefore cannot return this value. To determine whether a driver can detect updates to refetched rows, an application calls SQLGetInfo with the SQL_ROW_UPDATES option.

[3] SQLFetch can return this value only when it is intermixed with calls to SQLFetchScroll. The reason for this is that SQLFetch moves forward through the result set and when used exclusively, does not refetch any rows. Because no rows are refetched, SQLFetch does not detect changes made to previously fetched rows. However, if SQLFetchScroll positions the cursor before any previously fetched rows and SQLFetch is used to fetch those rows, SQLFetch can detect any changes to those rows.

[4] Returned by SQLBulkOperations only. Not set by SQLFetch or SQLFetchScroll.

Rows Fetched Buffer

The rows fetched buffer is used to return the number of rows fetched, including those rows for which no data was returned because an error occurred while they were being fetched. In other words, it is the number of rows for which the value in the row status array is not SQL_ROW_NOROW. The address of this buffer is specified with the SQL_ATTR_ROWS_FETCHED_PTR statement attribute. The buffer is allocated by the application. It is set by SQLFetch and SQLFetchScroll. If the value of the SQL_ATTR_ROWS_FETCHED_PTR statement attribute is a null pointer, these functions do not return the number of rows fetched. To determine the number of the current row in the result set, an application can call SQLGetStmtAttr with the SQL_ATTR_ROW_NUMBER attribute.

The contents of the rows fetched buffer are undefined if SQLFetch or SQLFetchScroll does not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, except when SQL_NO_DATA is returned, in which case the value in the rows fetched buffer is set to 0.

Error Handling

Errors and warnings can apply to individual rows or to the entire function. For more information about diagnostic records, see Chapter 15: Diagnostics, and SQLGetDiagField.

Errors and Warnings on the Entire Function

If an error applies to the entire function, such as SQLSTATE HYT00 (Timeout expired) or SQLSTATE 24000 (Invalid cursor state), SQLFetch returns SQL_ERROR and the applicable SQLSTATE. The contents of the rowset buffers are undefined and the cursor position is unchanged.

If a warning applies to the entire function, SQLFetch returns SQL_SUCCESS_WITH_INFO and the applicable SQLSTATE. The status records for warnings that apply to the entire function are returned before the status records that apply to individual rows.

Errors and Warnings in Individual Rows

If an error (such as SQLSTATE 22012 (Division by zero)) or a warning (such as SQLSTATE 01004 (Data truncated)) applies to a single row, SQLFetch does the following:

  • Sets the corresponding element of the row status array to SQL_ROW_ERROR for errors or SQL_ROW_SUCCESS_WITH_INFO for warnings.
  • Adds zero or more status records containing SQLSTATEs for the error or warning.
  • Sets the row and column number fields in the status records. If SQLFetch cannot determine a row or column number, it sets that number to SQL_ROW_NUMBER_UNKNOWN or SQL_COLUMN_NUMBER_UNKNOWN, respectively. If the status record does not apply to a particular column, SQLFetch sets the column number to SQL_NO_COLUMN_NUMBER.

SQLFetch continues fetching rows until it has fetched all of the rows in the rowset. It returns SQL_SUCCESS_WITH_INFO unless an error occurs in every row of the rowset (not counting rows with status SQL_ROW_NOROW), in which case it returns SQL_ERROR. In particular, if the rowset size is 1 and an error occurs in that row, SQLFetch returns SQL_ERROR.

SQLFetch returns the status records in row number order. That is, it returns all status records for unknown rows (if any), then all status records for the first row (if any), then all status records for the second row (if any), and so on. The status records for each individual row are ordered according to the normal rules for ordering status records; for more information, see "Sequence of Status Records" in SQLGetDiagField.

Descriptors and SQLFetch

The following sections describe how SQLFetch interacts with descriptors.

Argument Mappings

The driver does not set any descriptor fields based on the arguments of SQLFetch.

Other Descriptor Fields

The following descriptor fields are used by SQLFetch.

Descriptor field | Desc. | Field in | Set through
SQL_DESC_ARRAY_SIZE | ARD | header | SQL_ATTR_ROW_ARRAY_SIZE statement attribute
SQL_DESC_ARRAY_STATUS_PTR | IRD | header | SQL_ATTR_ROW_STATUS_PTR statement attribute
SQL_DESC_BIND_OFFSET_PTR | ARD | header | SQL_ATTR_ROW_BIND_OFFSET_PTR statement attribute
SQL_DESC_BIND_TYPE | ARD | header | SQL_ATTR_ROW_BIND_TYPE statement attribute
SQL_DESC_COUNT | ARD | header | ColumnNumber argument of SQLBindCol
SQL_DESC_DATA_PTR | ARD | records | TargetValuePtr argument of SQLBindCol
SQL_DESC_INDICATOR_PTR | ARD | records | StrLen_or_IndPtr argument in SQLBindCol
SQL_DESC_OCTET_LENGTH | ARD | records | BufferLength argument in SQLBindCol
SQL_DESC_OCTET_LENGTH_PTR | ARD | records | StrLen_or_IndPtr argument in SQLBindCol
SQL_DESC_ROWS_PROCESSED_PTR | IRD | header | SQL_ATTR_ROWS_FETCHED_PTR statement attribute
SQL_DESC_TYPE | ARD | records | TargetType argument in SQLBindCol

All descriptor fields can also be set through SQLSetDescField.

Separate Length and Indicator Buffers

Applications can bind a single buffer or two separate buffers to be used to hold length and indicator values. When an application calls SQLBindCol, the driver sets the SQL_DESC_OCTET_LENGTH_PTR and SQL_DESC_INDICATOR_PTR fields of the ARD to the same address, which is passed in the StrLen_or_IndPtr argument. When an application calls SQLSetDescField or SQLSetDescRec, it can set these two fields to different addresses.

SQLFetch determines whether the application has specified separate length and indicator buffers. In this case, when the data is not NULL, SQLFetch sets the indicator buffer to 0 and returns the length in the length buffer. When the data is NULL, SQLFetch sets the indicator buffer to SQL_NULL_DATA and does not modify the length buffer.

Code Example

See SQLBindCol, SQLColumns, SQLGetData, and SQLProcedures.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Returning information about a column in a result set | SQLDescribeCol
Executing an SQL statement | SQLExecDirect
Executing a prepared SQL statement | SQLExecute
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Closing the cursor on the statement | SQLFreeStmt
Fetching part or all of a column of data | SQLGetData
Returning the number of result set columns | SQLNumResultCols
Preparing a statement for execution | SQLPrepare


SQLFetchScroll

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLFetchScroll fetches the specified rowset of data from the result set and returns data for all bound columns. Rowsets can be specified at an absolute or relative position or by bookmark.

When working with an ODBC 2.x driver, the Driver Manager maps this function to SQLExtendedFetch. For more information, see "Mapping Replacement Functions for Backward Compatibility of Applications" in Chapter 17: Programming Considerations.

Syntax

SQLRETURN SQLFetchScroll(
     SQLHSTMT     StatementHandle,
     SQLSMALLINT     FetchOrientation,
     SQLINTEGER     FetchOffset);

Arguments

StatementHandle
[Input]
Statement handle.
FetchOrientation
[Input]
Type of fetch:

SQL_FETCH_NEXT
SQL_FETCH_PRIOR
SQL_FETCH_FIRST
SQL_FETCH_LAST
SQL_FETCH_ABSOLUTE
SQL_FETCH_RELATIVE
SQL_FETCH_BOOKMARK

For more information, see "Positioning the Cursor" in the "Comments" section.

FetchOffset
[Input]
Number of the row to fetch. The interpretation of this argument depends on the value of the FetchOrientation argument. For more information, see "Positioning the Cursor" in the "Comments" section.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLFetchScroll returns either SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLFetchScroll and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise. If an error occurs on a single column, SQLGetDiagField can be called with a DiagIdentifier of SQL_DIAG_COLUMN_NUMBER to determine the column the error occurred on; and SQLGetDiagField can be called with a DiagIdentifier of SQL_DIAG_ROW_NUMBER to determine the row containing that column.

For all those SQLSTATEs that can return SQL_SUCCESS_WITH_INFO or SQL_ERROR (except 01xxx SQLSTATEs), SQL_SUCCESS_WITH_INFO is returned if an error occurs on one or more, but not all, rows of a multirow operation, and SQL_ERROR is returned if an error occurs on a single-row operation.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | String or binary data returned for a column resulted in the truncation of nonblank character or non-NULL binary data. String values are right-truncated.
01S01 Error in row An error occurred while fetching one or more rows.

(This SQLSTATE is returned only by ODBC 2.x drivers.)
01S06 Attempt to fetch before the result set returned the first rowset The requested rowset overlapped the start of the result set when FetchOrientation was SQL_FETCH_PRIOR, the current position was beyond the first row, and the number of the current row is less than or equal to the rowset size.

The requested rowset overlapped the start of the result set when FetchOrientation was SQL_FETCH_PRIOR, the current position was beyond the end of the result set, and the rowset size was greater than the result set size.

The requested rowset overlapped the start of the result set when FetchOrientation was SQL_FETCH_RELATIVE, FetchOffset was negative, and the absolute value of FetchOffset was less than or equal to the rowset size.

The requested rowset overlapped the start of the result set when FetchOrientation was SQL_FETCH_ABSOLUTE, FetchOffset was negative, and the absolute value of FetchOffset was greater than the result set size but less than or equal to the rowset size.

(Function returns SQL_SUCCESS_WITH_INFO.)
01S07 Fractional truncation The data returned for a column was truncated. For numeric data types, the fractional part of the number was truncated. For time, timestamp, and interval data types containing a time component, the fractional portion of the time was truncated.

(Function returns SQL_SUCCESS_WITH_INFO.)
07006 Restricted data type attribute violation A data value of a column in the result set could not be converted to the C data type specified by TargetType in SQLBindCol.

Column 0 was bound with a data type of SQL_C_BOOKMARK, and the SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE.

Column 0 was bound with a data type of SQL_C_VARBOOKMARK, and the SQL_ATTR_USE_BOOKMARKS statement attribute was not set to SQL_UB_VARIABLE.
07009 | Invalid descriptor index | Column 0 was bound, and the SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_OFF.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
22001 | String data, right truncation | A variable-length bookmark returned for a row was truncated.
22002 | Indicator variable required but not supplied | NULL data was fetched into a column whose StrLen_or_IndPtr set by SQLBindCol (or SQL_DESC_INDICATOR_PTR set by SQLSetDescField or SQLSetDescRec) was a null pointer.
22003 Numeric value out of range Returning the numeric value (as numeric or string) for one or more bound columns would have caused the whole (as opposed to fractional) part of the number to be truncated.

For more information, see Guidelines for Interval and Numeric Data Types in Appendix D: Data Types.
22007 | Invalid datetime format | A character column in the result set was bound to a date, time, or timestamp C structure, and a value in the column was, respectively, an invalid date, time, or timestamp.
22012 | Division by zero | A value from an arithmetic expression was returned, which resulted in division by zero.
22015 Interval field overflow Assigning from an exact numeric or interval SQL type to an interval C type caused a loss of significant digits in the leading field.

When fetching data to an interval C type, there was no representation of the value of the SQL type in the interval C type.

22018 Invalid character value for cast specification A character column in the result set was bound to a character C buffer, and the column contained a character for which there was no representation in the character set of the buffer.

The C type was an exact or approximate numeric, a datetime, or an interval data type; the SQL type of the column was a character data type; and the value in the column was not a valid literal of the bound C type.
24000 | Invalid cursor state | The StatementHandle was in an executed state, but no result set was associated with the StatementHandle.
40001 | Serialization failure | The transaction in which the fetch was executed was terminated to prevent deadlock.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 Memory allocation
error 		The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY010 Function sequence error (DM) The specified StatementHandle was not in an executed state. The function was called without first calling SQLExecDirect, SQLExecute, or a catalog function.

(DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.

(DM) SQLFetchScroll was called for a StatementHandle after SQLExtendedFetch was called and before SQLFreeStmt with SQL_CLOSE was called.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | The SQL_ATTR_USE_BOOKMARK statement attribute was set to SQL_UB_VARIABLE, and column 0 was bound to a buffer whose length was not equal to the maximum length for the bookmark for this result set. (This length is available in the SQL_DESC_OCTET_LENGTH field of the IRD and can be obtained by calling SQLDescribeCol, SQLColAttribute, or SQLGetDescField.)
HY106 Fetch type out of range (DM) The value specified for the argument FetchOrientation was invalid.

(DM) The argument FetchOrientation was SQL_FETCH_BOOKMARK, and the SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_OFF.

The value of the SQL_ATTR_CURSOR_TYPE statement attribute was SQL_CURSOR_FORWARD_ONLY, and the value of argument FetchOrientation was not SQL_FETCH_NEXT.

The value of the SQL_ATTR_CURSOR_SCROLLABLE statement attribute was SQL_NONSCROLLABLE, and the value of argument FetchOrientation was not SQL_FETCH_NEXT.
HY107 | Row value out of range | The value specified with the SQL_ATTR_CURSOR_TYPE statement attribute was SQL_CURSOR_KEYSET_DRIVEN, but the value specified with the SQL_ATTR_KEYSET_SIZE statement attribute was greater than 0 and less than the value specified with the SQL_ATTR_ROW_ARRAY_SIZE statement attribute.
HY111 | Invalid bookmark value | The argument FetchOrientation was SQL_FETCH_BOOKMARK, and the bookmark pointed to by the value in the SQL_ATTR_FETCH_BOOKMARK_PTR statement attribute was not valid or was a null pointer.
HYC00 Optional feature not implemented Driver or data source does not support the specified fetch type.

The driver or data source does not support the conversion specified by the combination of the TargetType in SQLBindCol and the SQL data type of the corresponding column.

FetchOrientation was SQL_FETCH_BOOKMARK, FetchOffset was not equal to 0, and the underlying driver is an ODBC 2.x driver.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

SQLFetchScroll returns a specified rowset from the result set. Rowsets can be specified by absolute or relative position or by bookmark. SQLFetchScroll can be called only while a result set exists—that is, after a call that creates a result set and before the cursor over that result set is closed. If any columns are bound, it returns the data in those columns. If the application has specified a pointer to a row status array or a buffer in which to return the number of rows fetched, SQLFetchScroll returns this information as well. Calls to SQLFetchScroll can be mixed with calls to SQLFetch but cannot be mixed with calls to SQLExtendedFetch.

For more information, see "Using Block Cursors" and "Using Scrollable Cursors" in Chapter 11: Retrieving Results (Advanced).

Positioning the Cursor

When the result set is created, the cursor is positioned before the start of the result set. SQLFetchScroll positions the block cursor based on the values of the FetchOrientation and FetchOffset arguments as shown in the following table. The exact rules for determining the start of the new rowset are shown in the next section.

FetchOrientation | Meaning
SQL_FETCH_NEXT | Return the next rowset. This is equivalent to calling SQLFetch. SQLFetchScroll ignores the value of FetchOffset.
SQL_FETCH_PRIOR | Return the prior rowset. SQLFetchScroll ignores the value of FetchOffset.
SQL_FETCH_RELATIVE | Return the rowset FetchOffset from the start of the current rowset.
SQL_FETCH_ABSOLUTE | Return the rowset starting at row FetchOffset.
SQL_FETCH_FIRST | Return the first rowset in the result set. SQLFetchScroll ignores the value of FetchOffset.
SQL_FETCH_LAST | Return the last complete rowset in the result set. SQLFetchScroll ignores the value of FetchOffset.
SQL_FETCH_BOOKMARK | Return the rowset FetchOffset rows from the bookmark specified by the SQL_ATTR_FETCH_BOOKMARK_PTR statement attribute.

Drivers are not required to support all fetch orientations; an application calls SQLGetInfo with an information type of SQL_DYNAMIC_CURSOR_ATTRIBUTES1, SQL_KEYSET_CURSOR_ATTRIBUTES1, or SQL_STATIC_CURSOR_ATTRIBUTES1 (depending on the type of the cursor) to determine which fetch orientations are supported by the driver. The application should look at the SQL_CA1_NEXT, SQL_CA1_RELATIVE, SQL_CA1_ABSOLUTE, and WQL_CA1_BOOKMARK bitmasks in these information types. Furthermore, if the cursor is forward-only and FetchOrientation is not SQL_FETCH_NEXT, SQLFetchScroll returns SQLSTATE HY106 (Fetch type out of range).

The SQL_ATTR_ROW_ARRAY_SIZE statement attribute specifies the number of rows in the rowset. If the rowset being fetched by SQLFetchScroll overlaps the end of the result set, SQLFetchScroll returns a partial rowset. That is, if S + R – 1 is greater than L, where S is the starting row of the rowset being fetched, R is the rowset size, and L is the last row in the result set, then only the first L – S + 1 rows of the rowset are valid. The remaining rows are empty and have a status of SQL_ROW_NOROW.

After SQLFetchScroll returns, the current row is the first row of the rowset.

Cursor Positioning Rules

The following sections describe the exact rules for each value of FetchOrientation. These rules use the following notation.

Notation | Meaning
Before start | The block cursor is positioned before the start of the result set. If the first row of the new rowset is before the start of the result set, SQLFetchScroll returns SQL_NO_DATA.
After end | The block cursor is positioned after the end of the result set. If the first row of the new rowset is after the end of the result set, SQLFetchScroll returns SQL_NO_DATA.
CurrRowsetStart | The number of the first row in the current rowset.
LastResultRow | The number of the last row in the result set.
RowsetSize | The rowset size.
FetchOffset | The value of the FetchOffset argument.
BookmarkRow | The row corresponding to the bookmark specified by the SQL_ATTR_FETCH_BOOKMARK_PTR statement attribute.

SQL_FETCH_NEXT

The following rules apply.

Condition | First row of new rowset
Before start | 1
CurrRowsetStart + RowsetSize[1] <= LastResultRow | CurrRowsetStart + RowsetSize[1]
CurrRowsetStart + RowsetSize[1]> LastResultRow | After end
After end | After end

[1] If the rowset size has been changed since the previous call to fetch rows, this is the rowset size that was used with the previous call.

SQL_FETCH_PRIOR

The following rules apply.

Condition | First row of new rowset
Before start | Before start
CurrRowsetStart = 1 | Before start
1 < CurrRowsetStart <= RowsetSize[2] | 1[1]
CurrRowsetStart > RowsetSize[2] | CurrRowsetStart – RowsetSize[2]
After end AND LastResultRow < RowsetSize[2] | 1[1]
After end AND LastResultRow >= RowsetSize[2] | LastResultRow – RowsetSize + 1[2]

[1] SQLFetchScroll returns SQLSTATE 01S06 (Attempt to fetch before the result set returned the first rowset) and SQL_SUCCESS_WITH_INFO.

[2] If the rowset size has been changed since the previous call to fetch rows, this is the new rowset size.

SQL_FETCH_RELATIVE

The following rules apply.

Condition | First row of new rowset
(Before start AND FetchOffset > 0) OR (After end AND FetchOffset < 0) | --[1]
BeforeStart AND FetchOffset <= 0 | Before start
CurrRowsetStart = 1 AND FetchOffset < 0 | Before start
CurrRowsetStart > 1 AND CurrRowsetStart + FetchOffset < 1 AND | FetchOffset | > RowsetSize[3] | Before start
CurrRowsetStart > 1 AND CurrRowsetStart + FetchOffset < 1 AND | FetchOffset | <= RowsetSize[3] | 1[2]
1 <= CurrRowsetStart + FetchOffset <= LastResultRow | CurrRowsetStart + FetchOffset
CurrRowsetStart + FetchOffset > LastResultRow | After end
After end AND FetchOffset >= 0 | After end

[1] SQLFetchScroll returns the same rowset as if it was called with FetchOrientation set to SQL_FETCH_ABSOLUTE. For more information, see the "SQL_FETCH_ABSOLUTE" section.

[2] SQLFetchScroll returns SQLSTATE 01S06 (Attempt to fetch before the result set returned the first rowset) and SQL_SUCCESS_WITH_INFO.

[3] If the rowset size has been changed since the previous call to fetch rows, this is the new rowset size.

SQL_FETCH_ABSOLUTE

The following rules apply.

Condition | First row of new rowset
FetchOffset < 0 AND | FetchOffset | <= LastResultRow | LastResultRow + FetchOffset + 1
FetchOffset < 0 AND | FetchOffset | > LastResultRow AND | FetchOffset | > RowsetSize[2] | Before start
FetchOffset < 0 AND | FetchOffset | > LastResultRow AND | FetchOffset | <= RowsetSize[2] | 1[1]
FetchOffset = 0 | Before start
1 <= FetchOffset <= LastResultRow | FetchOffset
FetchOffset > LastResultRow | After end

[1] SQLFetchScroll returns SQLSTATE 01S06 (Attempt to fetch before the result set returned the first rowset) and SQL_SUCCESS_WITH_INFO.

[2] If the rowset size has been changed since the previous call to fetch rows, this is the new rowset size.

An absolute fetch performed against a dynamic cursor cannot provide the required result because row positions in a dynamic cursor are undetermined. Such an operation is equivalent to a fetch first followed by a fetch relative; it is not an atomic operation, as is an absolute fetch on a static cursor.

SQL_FETCH_FIRST

The following rules apply.

Condition | First row of new rowset
Any | 1

SQL_FETCH_LAST

The following rules apply.

Condition | First row of new rowset
RowsetSize [1] <= LastResultRow | LastResultRow – RowsetSize + 1[1]
RowsetSize [1] > LastResultRow | 1

[1] If the rowset size has been changed since the previous call to fetch rows, this is the new rowset size.

SQL_FETCH_BOOKMARK

The following rules apply.

Condition | First row of new rowset
BookmarkRow + FetchOffset < 1 | Before start
1 <= BookmarkRow + FetchOffset <= LastResultRow | BookmarkRow + FetchOffset
BookmarkRow + FetchOffset > LastResultRow | After end

For information about bookmarks, see the "Bookmarks" section in Chapter 11: Retrieving Results (Advanced).

Effect of Deleted, Added, and Error Rows on Cursor Movement

Static and keyset-driven cursors sometimes detect rows added to the result set and remove rows deleted from the result set. By calling SQLGetInfo with the SQL_STATIC_CURSOR_ATTRIBUTES2 and SQL_KEYSET_CURSOR_ATTRIBUTES2 options and looking at the SQL_CA2_SENSITIVITY_ADDITIONS, SQL_CA2_SENSITIVITY_DELETIONS, and SQL_CA2_SENSITIVITY_UPDATES bitmasks, an application determines whether the cursors implemented by a particular driver do this. For drivers that can detect deleted rows and remove them, the following paragraphs describe the effects of this behavior. For drivers that can detect deleted rows but cannot remove them, deletions have no effect on cursor movements, and the following paragraphs do not apply.

If the cursor detects rows added to the result set or removes rows deleted from the result set, it appears as if it detects these changes only when it fetches data. This includes the case when SQLFetchScroll is called with FetchOrientation set to SQL_FETCH_RELATIVE and FetchOffset set to 0 to refetch the same rowset, but does not include the case when SQLSetPos is called with fOption set to SQL_REFRESH. In the latter case, the data in the rowset buffers is refreshed, but not refetched, and deleted rows are not removed from the result set. Thus, when a row is deleted from or inserted into the current rowset, the cursor does not modify the rowset buffers. Instead, it detects the change when it fetches any rowset that previously included the deleted row or now includes the inserted row.

For example:

// Fetch the next rowset.
SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0);

// Delete third row of the rowset. Does not modify the rowset buffers.
SQLSetPos(hstmt, 3, SQL_DELETE, SQL_LOCK_NO_CHANGE);

// The third row has a status of SQL_ROW_DELETED after this call.
SQLSetPos(hstmt, 3, SQL_REFRESH, SQL_LOCK_NO_CHANGE);

// Refetch the same rowset. The third row is removed, replaced by what
// was previously the fourth row.
SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 0);

When SQLFetchScroll returns a new rowset that has a position relative to the current rowset—that is, FetchOrientation is SQL_FETCH_NEXT, SQL_FETCH_PRIOR, or SQL_FETCH_RELATIVE—it does not include changes to the current rowset when calculating the starting position of the new rowset. However, it does include changes outside the current rowset if it is capable of detecting them. Furthermore, when SQLFetchScroll returns a new rowset that has a position independent of the current rowset—that is, FetchOrientation is SQL_FETCH_FIRST, SQL_FETCH_LAST, SQL_FETCH_ABSOLUTE, or SQL_FETCH_BOOKMARK—it includes all changes it is capable of detecting, even if they are in the current rowset.

When determining whether newly added rows are inside or outside the current rowset, a partial rowset is considered to end at the last valid row; that is, the last row for which the row status is not SQL_ROW_NOROW. For example, suppose the cursor is capable of detecting newly added rows, the current rowset is a partial rowset, the application adds new rows, and the cursor adds these rows to the end of the result set. If the application calls SQLFetchScroll with FetchOrientation set to SQL_FETCH_NEXT, SQLFetchScroll returns the rowset starting with the first newly added row.

For example, suppose the current rowset comprises rows 21 to 30, the rowset size is 10, the cursor removes rows deleted from the result set, and the cursor detects rows added to the result set. The following table shows the rows SQLFetchScroll returns in various situations.

Change | Fetch type | FetchOffset | New rowset[1]
Delete row 21 | NEXT | 0 | 31 to 40
Delete row 31 | NEXT | 0 | 32 to 41
Insert row between rows 21 and 22 | NEXT | 0 | 31 to 40
Insert row between rows 30 and 31 | NEXT | 0 | Inserted row, 31 to 39
Delete row 21 | PRIOR | 0 | 11 to 20
Delete row 20 | PRIOR | 0 | 10 to 19
Insert row between rows 21 and 22 | PRIOR | 0 | 11 to 20
Insert row between rows 20 and 21 | PRIOR | 0 | 12 to 20, inserted row
Delete row 21 | RELATIVE | 0 | 22 to 31[2]
Delete row 21 | RELATIVE | 1 | 22 to 31
Insert row between rows 21 and 22 | RELATIVE | 0 | 21, inserted row, 22 to 29
Insert row between rows 21 and 22 | RELATIVE | 1 | 22 to 31
Delete row 21 | ABSOLUTE | 21 | 22 to 31[2]
Delete row 22 | ABSOLUTE | 21 | 21, 23 to 31
Insert row between rows 21 and 22 | ABSOLUTE | 22 | Inserted row, 22 to 29

[1] This column uses the row numbers before any rows were inserted or deleted.

[2] In this case, the cursor attempts to return rows starting with row 21. Because row 21 has been deleted, the first row it returns is row 22.

Error rows (that is, rows with a status of SQL_ROW_ERROR) do not affect cursor movement. For example, if the current rowset starts with row 11 and the status of row 11 is SQL_ROW_ERROR, calling SQLFetchScroll with FetchOrientation set to SQL_FETCH_RELATIVE and FetchOffset set to 5 returns the rowset starting with row 16, just as it would if the status for row 11 was SQL_SUCCESS.

Returning Data in Bound Columns

SQLFetchScroll returns data in bound columns in the same way as SQLFetch. For more information, see "Returning Data in Bound Columns" in SQLFetch.

If no columns are bound, SQLFetchScroll does not return data but does move the block cursor to the specified position. Whether data can be retrieved from unbound columns of a block cursor with SQLGetData depends on the driver. This capability is supported if a call to SQLGetInfo returns the SQL_GD_BLOCK bit for the SQL_GETDATA_EXTENSIONS information type.

Buffer Addresses

SQLFetchScroll uses the same formula to determine the address of data and length/indicator buffers as SQLFetch. For more information, see "Buffer Addresses" in SQLBindCol.

Row Status Array

SQLFetchScroll sets values in the row status array in the same manner as SQLFetch. For more information, see "Row Status Array" in SQLFetch.

Rows Fetched Buffer

SQLFetchScroll returns the number of rows fetched in the rows fetched buffer in the same manner as SQLFetch. For more information, see "Rows Fetched Buffer" in SQLFetch.

Error Handling

When an application calls SQLFetchScroll in an ODBC 3.x driver, the Driver Manager calls SQLFetchScroll in the driver. When an application calls SQLFetchScroll in an ODBC 2.x driver, the Driver Manager calls SQLExtendedFetch in the driver. Because SQLFetchScroll and SQLExtendedFetch handle errors in a slightly different manner, the application sees slightly different error behavior when it calls SQLFetchScroll in ODBC 2.x and ODBC 3.x drivers.

SQLFetchScroll returns errors and warnings in the same manner as SQLFetch; for more information, see "Error Handling" in SQLFetch. SQLExtendedFetch returns errors in the same manner as SQLFetch, with the following exceptions:

  • When a warning occurs that applies to a particular row in the rowset, SQLExtendedFetch sets the corresponding entry in the row status array to SQL_ROW_SUCCESS, not SQL_ROW_SUCCESS_WITH_INFO.
  • If errors occur in every row in the rowset, SQLExtendedFetch returns SQL_SUCCESS_WITH_INFO, not SQL_ERROR.
  • In each group of status records that applies to an individual row, the first status record returned by SQLExtendedFetch must contain SQLSTATE 01S01 (Error in row); SQLFetchScroll does not return this SQLSTATE. If SQLExtendedFetch is unable to return additional SQLSTATEs, it still must return this SQLSTATE.

SQLFetchScroll and Optimistic Concurrency

If a cursor uses optimistic concurrency—that is, the SQL_ATTR_CONCURRENCY statement attribute has a value of SQL_CONCUR_VALUES or SQL_CONCUR_ROWVER—SQLFetchScroll updates the optimistic concurrency values used by the data source to detect whether a row has changed. This happens whenever SQLFetchScroll fetches a new rowset, including when it refetches the current rowset. (It is called with FetchOrientation set to SQL_FETCH_RELATIVE and FetchOffset set to 0.)

SQLFetchScroll and ODBC 2.x Drivers

When an application calls SQLFetchScroll in an ODBC 2.x driver, the Driver Manager maps this call to SQLExtendedFetch. It passes the following values for the arguments of SQLExtendedFetch.

SQLExtendedFetch argument | Value
StatementHandle | StatementHandle in SQLFetchScroll.
FetchOrientation | FetchOrientation in SQLFetchScroll.
FetchOffset | If FetchOrientation is not SQL_FETCH_BOOKMARK, the value of the FetchOffset argument in SQLFetchScroll is used. If FetchOrientation is SQL_FETCH_BOOKMARK, the value stored at the address specified by the SQL_ATTR_FETCH_BOOKMARK_PTR statement attribute is used.
RowCountPtr | The address specified by the SQL_ATTR_ROWS_FETCHED_PTR statement attribute.
RowStatusArray | The address specified by the SQL_ATTR_ROW_STATUS_PTR statement attribute.

For more information, see "Block Cursors, Scrollable Cursors, and Backward Compatibility" in Appendix G: Driver Guidelines for Backward Compatibility.

Descriptors and SQLFetchScroll

SQLFetchScroll interacts with descriptors in the same manner as SQLFetch. For more information, see the "Descriptors and SQLFetchScroll" section in SQLFetch.

Code Example

See "Column-Wise Binding" and "Row-Wise Binding" in Chapter 11: Retrieving Results (Advanced), and "Positioned Update and Delete Statements" and "Updating Rows in the Rowset with SQLSetPos" in Chapter 12: Updating Data.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Performing bulk insert, update, or delete operations | SQLBulkOperations
Canceling statement processing | SQLCancel
Returning information about a column in a result set | SQLDescribeCol
Executing an SQL statement | SQLExecDirect
Executing a prepared SQL statement | SQLExecute
Fetching a single row or a block of data in a forward-only direction | SQLFetch
Closing the cursor on the statement | SQLFreeStmt
Returning the number of result set columns | SQLNumResultCols
Positioning the cursor, refreshing data in the rowset, or updating or deleting data in the result set | SQLSetPos
Setting a statement attribute | SQLSetStmtAttr


SQLForeignKeys

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ODBC

Summary

SQLForeignKeys can return:

  • A list of foreign keys in the specified table (columns in the specified table that refer to primary keys in other tables).
  • A list of foreign keys in other tables that refer to the primary key in the specified table.

The driver returns each list as a result set on the specified statement.

Syntax

SQLRETURN SQLForeignKeys(
     SQLHSTMT     StatementHandle,
     SQLCHAR *     PKCatalogName,
     SQLSMALLINT     NameLength1,
     SQLCHAR *     PKSchemaName,
     SQLSMALLINT     NameLength2,
     SQLCHAR *     PKTableName,
     SQLSMALLINT     NameLength3,
     SQLCHAR *     FKCatalogName,
     SQLSMALLINT     NameLength4,
     SQLCHAR *     FKSchemaName,
     SQLSMALLINT     NameLength5,
     SQLCHAR *     FKTableName,
     SQLSMALLINT     NameLength6);

Arguments

StatementHandle
[Input]
Statement handle.
PKCatalogName
[Input]
Primary key table catalog name. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have catalogs. PKCatalogName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, PKCatalogName is treated as an identifier and its case is not significant. If it is SQL_FALSE, PKCatalogName is an ordinary argument; it is treated literally, and its case is significant. For more information, see "Arguments in Catalog Functions" in Chapter 7: Catalog Functions.

NameLength1
[Input]
Length of *PKCatalogName, in bytes.
PKSchemaName
[Input]
Primary key table schema name. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have schemas. PKSchemaName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, PKSchemaName is treated as an identifier and its case is not significant. If it is SQL_FALSE, PKSchemaName is an ordinary argument; it is treated literally, and its case is significant.

NameLength2
[Input]
Length of *PKSchemaName, in bytes.
PKTableName
[Input]
Primary key table name. PKTableName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, PKTableName is treated as an identifier and its case is not significant. If it is SQL_FALSE, PKTableName is an ordinary argument; it is treated literally, and its case is significant.

NameLength3
[Input]
Length of *PKTableName.
FKCatalogName
[Input]
Foreign key table catalog name. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have catalogs. FKCatalogName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, FKCatalogName is treated as an identifier and its case is not significant. If it is SQL_FALSE, FKCatalogName is an ordinary argument; it is treated literally, and its case is significant.

NameLength4
[Input]
Length of *FKCatalogName.
FKSchemaName
[Input]
Foreign key table schema name. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have schemas. FKSchemaName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, FKSchemaName is treated as an identifier and its case is not significant. If it is SQL_FALSE, FKSchemaName is an ordinary argument; it is treated literally, and its case is significant.

NameLength5
[Input]
Length of *FKSchemaName.
FKTableName
[Input]
Foreign key table name. FKTableName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, FKTableName is treated as an identifier and its case is not significant. If it is SQL_FALSE, FKTableName is an ordinary argument; it is treated literally, and its case is significant.

NameLength6
[Input]
Length of *FKTableName.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLForeignKeys returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLForeignKeys and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
24000 Invalid cursor state A cursor was open on the StatementHandle, and SQLFetch or SQLFetchScroll had been called. This error is returned by the Driver Manager if SQLFetch or SQLFetchScroll has not returned SQL_NO_DATA, and is returned by the driver if SQLFetch or SQLFetchScroll has returned SQL_NO_DATA.

A cursor was open on the StatementHandle, but SQLFetch or SQLFetchScroll had not been called.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 Memory allocation
error 		The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle, and then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY009 Invalid use of null pointer (DM) The arguments PKTableName and FKTableName were both null pointers.

The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, the FKCatalogName or PKCatalogName argument was a null pointer, and the SQL_CATALOG_NAME InfoType returns that catalog names are supported.

(DM) The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, and the FKSchemaName, PKSchemaName, FKTableName, or PKTableName argument was a null pointer.
HY010 Function sequence error (DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) The value of one of the name length arguments was less than 0 but not equal to SQL_NTS.
The value of one of the name length arguments exceeded the maximum length value for the corresponding name. (See "Comments.")
HYC00 Optional feature not implemented A catalog name was specified, and the driver or data source does not support catalogs.

A schema name was specified, and the driver or data source does not support schemas.
The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source.

The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.
HYT00 | Timeout expired | The query timeout period expired before the data source returned the result set. The timeout period is set through SQLSetStmtAttr, SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

For information about how the information returned by this function might be used, see "Uses of Catalog Data" in Chapter 7: Catalog Functions.

If *PKTableName contains a table name, SQLForeignKeys returns a result set containing the primary key of the specified table and all of the foreign keys that refer to it. The list of foreign keys in other tables does not include foreign keys that point to unique constraints in the specified table.

If *FKTableName contains a table name, SQLForeignKeys returns a result set containing all of the foreign keys in the specified table that point to primary keys in others tables, and the primary keys in the other tables to which they refer. The list of foreign keys in the specified table does not contain foreign keys that refer to unique constraints in other tables.

If both *PKTableName and *FKTableName contain table names, SQLForeignKeys returns the foreign keys in the table specified in *FKTableName that refer to the primary key of the table specified in *PKTableName. This should be one key at most.

Note For more information about the general use, arguments, and returned data of ODBC catalog functions, see Chapter 7: Catalog Functions.

SQLForeignKeys returns results as a standard result set. If the foreign keys associated with a primary key are requested, the result set is ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and KEY_SEQ. If the primary keys associated with a foreign key are requested, the result set is ordered by PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ. The following table lists the columns in the result set.

The lengths of VARCHAR columns are not shown in the table; the actual lengths depend on the data source. To determine the actual lengths of the PKTABLE_CAT or FKTABLE_CAT, PKTABLE_SCHEM or FKTABLE_SCHEM, PKTABLE_NAME or FKTABLE_NAME, and PKCOLUMN_NAME or FKCOLUMN_NAME columns, an application can call SQLGetInfo with the SQL_MAX_CATALOG_NAME_LEN, SQL_MAX_SCHEMA_NAME_LEN, SQL_MAX_TABLE_NAME_LEN, and SQL_MAX_COLUMN_NAME_LEN options.

The following columns have been renamed for ODBC 3.x. The column name changes do not affect backward compatibility because applications bind by column number.

ODBC 2.0 column | ODBC 3.x column
PKTABLE_QUALIFIER | PKTABLE_CAT
PKTABLE_OWNER | PKTABLE_SCHEM
FKTABLE_QUALIFIER | FK_TABLE_CAT
FKTABLE_OWNER | FKTABLE_SCHEM

The following table lists the columns in the result set. Additional columns beyond column 14 (REMARKS) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "Data Returned by Catalog Functions" in Chapter 7: Catalog Functions.

Column name 		Column number
Data type
Comments
PKTABLE_CAT
(ODBC 1.0) 		1 Varchar Primary key table catalog name; NULL if not applicable to the data source. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have catalogs.
PKTABLE_SCHEM
(ODBC 1.0) 		2 Varchar Primary key table schema name; NULL if not applicable to the data source. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have schemas.
PKTABLE_NAME
(ODBC 1.0) 		3 Varchar
not NULL 		Primary key table name.
PKCOLUMN_NAME
(ODBC 1.0) 		4 Varchar
not NULL 		Primary key column name. The driver returns an empty string for a column that does not have a name.
FKTABLE_CAT
(ODBC 1.0) 		5 Varchar Foreign key table catalog name; NULL if not applicable to the data source. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have catalogs.
FKTABLE_SCHEM
(ODBC 1.0) 		6 Varchar Foreign key table schema name; NULL if not applicable to the data source. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have schemas.
FKTABLE_NAME
(ODBC 1.0) 		7 Varchar
not NULL 		Foreign key table name.
FKCOLUMN_NAME
(ODBC 1.0) 		8 Varchar
not NULL 		Foreign key column name. The driver returns an empty string for a column that does not have a name.
KEY_SEQ
(ODBC 1.0) 		9 Smallint
not NULL 		Column sequence number in key (starting with 1).
UPDATE_RULE
(ODBC 1.0) 		10 Smallint Action to be applied to the foreign key when the SQL operation is UPDATE. Can have one of the following values. (The referenced table is the table that has the primary key; the referencing table is the table that has the foreign key.)

SQL_CASCADE: When the primary key of the referenced table is updated, the foreign key of the referencing table is also updated.

SQL_NO_ACTION: If an update of the primary key of the referenced table would cause a "dangling reference" in the referencing table (that is, rows in the referencing table would have no counterparts in the referenced table), then the update is rejected. If an update of the foreign key of the referencing table would introduce a value that does not exist as a value of the primary key of the referenced table, then the update is rejected. (This action is the same as the SQL_RESTRICT action in ODBC 2.x.)

SQL_SET_NULL: When one or more rows in the referenced table are updated such that one or more components of the primary key are changed, the components of the foreign key in the referencing table that correspond to the changed components of the primary key are set to NULL in all matching rows of the referencing table.

SQL_SET_DEFAULT: When one or more rows in the referenced table are updated such that one or more components of the primary key are changed, the components of the foreign key in the referencing table that correspond to the changed components of the primary key are set to the applicable default values in all matching rows of the referencing table.

NULL if not applicable to the data source.
DELETE_RULE
(ODBC 1.0) 		11 Smallint Action to be applied to the foreign key when the SQL operation is DELETE. Can have one of the following values. (The referenced table is the table that has the primary key; the referencing table is the table that has the foreign key.)

SQL_CASCADE: When a row in the referenced table is deleted, all the matching rows in the referencing tables are also deleted.

SQL_NO_ACTION: If a delete of a row in the referenced table would cause a "dangling reference" in the referencing table (that is, rows in the referencing table would have no counterparts in the referenced table), then the update is rejected. (This action is the same as the SQL_RESTRICT action in ODBC 2.x.)

SQL_SET_NULL: When one or more rows in the referenced table are deleted, each component of the foreign key of the referencing table is set to NULL in all matching rows of the referencing table.

SQL_SET_DEFAULT: When one or more rows in the referenced table are deleted, each component of the foreign key of the referencing table is set to the applicable default in all matching rows of the referencing table.

NULL if not applicable to the data source.
FK_NAME
(ODBC 2.0) 		12 Varchar Foreign key name. NULL if not applicable to the data source.
PK_NAME
(ODBC 2.0) 		13 Varchar Primary key name. NULL if not applicable to the data source.
DEFERRABILITY
(ODBC 3.0) 		14 Smallint SQL_INITIALLY_DEFERRED,
SQL_INITIALLY_IMMEDIATE,
SQL_NOT_DEFERRABLE.

Code Example

As illustrated in the following table, this example uses three tables, named ORDERS, LINES, and CUSTOMERS.

ORDERS | LINES | CUSTOMERS
ORDERID | ORDERID | CUSTID
CUSTID | LINES | NAME
OPENDATE | PARTID | ADDRESS
SALESPERSON | QUANTITY | PHONE
STATUS |  |

In the ORDERS table, CUSTID identifies the customer to whom the sale has been made. It is a foreign key that refers to CUSTID in the CUSTOMERS table.

In the LINES table, ORDERID identifies the sales order with which the line item is associated. It is a foreign key that refers to ORDERID in the ORDERS table.

This example calls SQLPrimaryKeys to get the primary key of the ORDERS table. The result set will have one row; the significant columns are shown in the following table.

TABLE_NAME | COLUMN_NAME | KEY_SEQ
ORDERS | ORDERID | 1

Next, the example calls SQLForeignKeys to get the foreign keys in other tables that reference the primary key of the ORDERS table. The result set will have one row; the significant columns are shown in the following table.

PKTABLE_NAME | PKCOLUMN_NAME | FKTABLE_NAME | FKCOLUMN_NAME | KEY_SEQ
ORDERS | CUSTID | LINES | CUSTID | 1

Finally, the example calls SQLForeignKeys to get the foreign keys in the ORDERS table that refer to the primary keys of other tables. The result set will have one row; the significant columns are shown in the following table.

PKTABLE_NAME | PKCOLUMN_NAME | FKTABLE_NAME | FKCOLUMN_NAME | KEY_SEQ
CUSTOMERS | CUSTID | ORDERS | CUSTID | 1
#define TAB_LEN SQL_MAX_TABLE_NAME_LEN + 1
#define COL_LEN SQL_MAX_COLUMN_NAME_LEN + 1

LPSTR   szTable;              /* Table to display */

UCHAR szPkTable[TAB_LEN];   /* Primary key table name */
UCHAR szFkTable[TAB_LEN];   /* Foreign key table name */
UCHAR szPkCol[COL_LEN];     /* Primary key column */
UCHAR szFkCol[COL_LEN];     /* Foreign key column */

SQLHSTMT      hstmt;
SQLINTEGER    cbPkTable, cbPkCol, cbFkTable, cbFkCol, cbKeySeq;
SQLSMALLINT   iKeySeq;
SQLRETURN     retcode;

/* Bind the columns that describe the primary and foreign keys. */
/* Ignore the table schema, name, and catalog for this example. */

SQLBindCol(hstmt, 3, SQL_C_CHAR, szPkTable, TAB_LEN, &cbPkTable);
SQLBindCol(hstmt, 4, SQL_C_CHAR, szPkCol, COL_LEN, &cbPkCol);
SQLBindCol(hstmt, 5, SQL_C_SSHORT, &iKeySeq, TAB_LEN, &cbKeySeq);
SQLBindCol(hstmt, 7, SQL_C_CHAR, szFkTable, TAB_LEN, &cbFkTable);
SQLBindCol(hstmt, 8, SQL_C_CHAR, szFkCol, COL_LEN, &cbFkCol);

strcpy(szTable, "ORDERS");

/* Get the names of the columns in the primary key. */

retcode = SQLPrimaryKeys(hstmt,
         NULL, 0,             /* Catalog name */
         NULL, 0,             /* Schema name */
         szTable, SQL_NTS);   /* Table name */

while ((retcode == SQL_SUCCESS) || (retcode == SQL SUCCESS_WITH_INFO)) {

   /* Fetch and display the result set. This will be a list of the */
   /* columns in the primary key of the ORDERS table. */

   retcode = SQLFetch(hstmt);
   if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
      fprintf(out, "Table: %s Column: %s Key Seq: %hd \n", szPkTable, szPkCol,
      iKeySeq);
}

/* Close the cursor (the hstmt is still allocated). */

SQLFreeStmt(hstmt, SQL_CLOSE);

/* Get all the foreign keys that refer to ORDERS primary key.*/

retcode = SQLForeignKeys(hstmt,
         NULL, 0,            /* Primary catalog */
         NULL, 0,            /* Primary schema */
         szTable, SQL_NTS,   /* Primary table */
         NULL, 0,            /* Foreign catalog */
         NULL, 0,            /* Foreign schema */
         NULL, 0);           /* Foreign table */

while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO)) {

/* Fetch and display the result set. This will be all of the */
/* foreign keys in other tables that refer to the ORDERS */
/* primary key. */

   retcode = SQLFetch(hstmt);
   if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
      fprintf(out, "%-s ( %-s ) <-- %-s ( %-s )\n", szPkTable,
               szPkCol, szFkTable, szFkCol);
}

/* Close the cursor (the hstmt is still allocated). */

SQLFreeStmt(hstmt, SQL_CLOSE);

/* Get all the foreign keys in the ORDERS table. */

retcode = SQLForeignKeys(hstmt,
         NULL, 0,             /* Primary catalog */
         NULL, 0,             /* Primary schema */
         NULL, 0,             /* Primary table */
         NULL, 0,             /* Foreign catalog */
         NULL, 0,             /* Foreign schema */
         szTable, SQL_NTS);   /* Foreign table */

while ((retcode == SQL_SUCCESS) || (retcode == SQL_SUCCESS_WITH_INFO)) {

/* Fetch and display the result set. This will be all of the */
/* primary keys in other tables that are referred to by foreign */
/* keys in the ORDERS table. */

   retcode = SQLFetch(hstmt);
   if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
      fprintf(out, "%-s ( %-s )--> %-s ( %-s )\n", szFkTable,
      szFkCol, szPkTable, szPkCol);
}

/* Free the hstmt. */

SQLFreeStmt(hstmt, SQL_DROP);

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Fetching a single row or a block of data in a forward-only direction | SQLFetch
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Returning the columns of a primary key | SQLPrimaryKeys
Returning table statistics and indexes | SQLStatistics


SQLFreeConnect

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

In ODBC 3.x, the ODBC 2.0 function SQLFreeConnect has been replaced by SQLFreeHandle. For more information, see SQLFreeHandle.

Note For more information about what the Driver Manager maps this function to when an ODBC 2.x application is working with an ODBC 3.x driver, see "Mapping Deprecated Functions" in Appendix G: Driver Guidelines for Backward Compatibility.


SQLFreeEnv

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

In ODBC 3.x, the ODBC 2.0 function SQLFreeEnv has been replaced by SQLFreeHandle. For more information, see SQLFreeHandle.

Note For more information about what the Driver Manager maps this function to when an ODBC 2.x application is working with an ODBC 3.x driver, see "Mapping Deprecated Functions" in Appendix G: Driver Guidelines for Backward Compatibility.


SQLFreeHandle

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLFreeHandle frees resources associated with a specific environment, connection, statement, or descriptor handle.

Note This function is a generic function for freeing handles. It replaces the ODBC 2.0 functions SQLFreeConnect (for freeing a connection handle) and SQLFreeEnv (for freeing an environment handle). SQLFreeConnect and SQLFreeEnv are both deprecated in ODBC 3.x. SQLFreeHandle also replaces the ODBC 2.0 function SQLFreeStmt (with the SQL_DROP Option) for freeing a statement handle. For more information, see "Comments." For more information about what the Driver Manager maps this function to when an ODBC 3.x application is working with an ODBC 2.x driver, see "Mapping Replacement Functions for Backward Compatibility of Applications" in Chapter 17: Programming Considerations.

Syntax

SQLRETURN SQLFreeHandle(
     SQLSMALLINT     HandleType,
     SQLHANDLE     Handle);

Arguments

HandleType
[Input]
The type of handle to be freed by SQLFreeHandle. Must be one of the following values:

SQL_HANDLE_ENV
SQL_HANDLE_DBC
SQL_HANDLE_STMT
SQL_HANDLE_DESC

If HandleType is not one of these values, SQLFreeHandle returns SQL_INVALID_HANDLE.

Handle
[Input]
The handle to be freed.

Returns

SQL_SUCCESS, SQL_ERROR, or SQL_INVALID_HANDLE.

If SQLFreeHandle returns SQL_ERROR, the handle is still valid.

Diagnostics

When SQLFreeHandle returns SQL_ERROR, an associated SQLSTATE value may be obtained from the diagnostic data structure for the handle that SQLFreeHandle attempted to free but could not. The following table lists the SQLSTATE values commonly returned by SQLFreeHandle and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY010 Function sequence error (DM) The HandleType argument was SQL_HANDLE_ENV, and at least one connection was in an allocated or connected state. SQLDisconnect and SQLFreeHandle with a HandleType of SQL_HANDLE_DBC must be called for each connection before calling SQLFreeHandle with a HandleType of SQL_HANDLE_ENV.

(DM) The HandleType argument was SQL_HANDLE_DBC, and the function was called before calling SQLDisconnect for the connection.

(DM) The HandleType argument was SQL_HANDLE_STMT; an asynchronously executing function was called on the statement handle; and the function was still executing when this function was called.

(DM) The HandleType argument was SQL_HANDLE_STMT; SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called with the statement handle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.

(DM) All subsidiary handles and other resources were not released before SQLFreeHandle was called.
HY013 | Memory management error | The HandleType argument was SQL_HANDLE_STMT or SQL_HANDLE_DESC, and the function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY017 | Invalid use of an automatically allocated descriptor handle. | (DM) The Handle argument was set to the handle for an automatically allocated descriptor.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 Driver does not support this function (DM) The HandleType argument was SQL_HANDLE_DESC, and the driver was an ODBC 2.x driver.

(DM) The HandleType argument was SQL_HANDLE_STMT, and the driver was not a valid ODBC driver.

Comments

SQLFreeHandle is used to free handles for environments, connections, statements, and descriptors, as described in the following sections. For general information about handles, see "Handles" in Chapter 4: ODBC Fundamentals.

An application should not use a handle after it has been freed; the Driver Manager does not check the validity of a handle in a function call.

Freeing an Environment Handle

Prior to calling SQLFreeHandle with a HandleType of SQL_HANDLE_ENV, an application must call SQLFreeHandle with a HandleType of SQL_HANDLE_DBC for all connections allocated under the environment. Otherwise, the call to SQLFreeHandle returns SQL_ERROR and the environment and any active connection remains valid. For more information, see "Environment Handles" in Chapter 4: ODBC Fundamentals and "Allocating the Environment Handle" in Chapter 6: Connecting to a Data Source or Driver.

When the Driver Manager processes the call to SQLFreeHandle with a HandleType of SQL_HANDLE_ENV, it checks the TraceAutoStop keyword in the [ODBC] section of the ODBC subkey of the system information. If it is set to 1, the Driver Manager disables tracing for all applications and sets the Trace keyword in the [ODBC] section of the ODBC subkey of the system information to 0. For information on what happens to tracing when SQLFreeHandle is called, see "ODBC Subkey" in Chapter 19: Configuring Data Sources.

If the environment is a shared environment, the application that calls SQLFreeHandle with a HandleType of SQL_HANDLE_ENV no longer has access to the environment after the call, but the environment's resources are not necessarily freed. The call to SQLFreeHandle decrements the environment's reference count, which is maintained by the Driver Manager. If the reference count does not reach zero, the shared environment is not freed, because it is still being used by another component. If the reference count reaches zero, the shared environment's resources are freed.

Freeing a Connection Handle

Prior to calling SQLFreeHandle with a HandleType of SQL_HANDLE_DBC, an application must call SQLDisconnect for the connection if there is a connection on this handle. Otherwise, the call to SQLFreeHandle returns SQL_ERROR and the connection remains valid.

For more information, see "Connection Handles" in Chapter 4: ODBC Fundamentals and "Disconnecting from a Data Source or Driver" in Chapter 6: Connecting to a Data Source or Driver.

Freeing a Statement Handle

A call to SQLFreeHandle with a HandleType of SQL_HANDLE_STMT frees all resources that were allocated by a call to SQLAllocHandle with a HandleType of SQL_HANDLE_STMT. When an application calls SQLFreeHandle to free a statement that has pending results, the pending results are deleted. When an application frees a statement handle, the driver frees the four automatically allocated descriptors associated with that handle. For more information, see "Statement Handles" in Chapter 4: ODBC Fundamentals and "Freeing a Statement Handle" in Chapter 9: Executing Statements.

Note that SQLDisconnect automatically drops any statements and descriptors open on the connection.

Freeing a Descriptor Handle

A call to SQLFreeHandle with a HandleType of SQL_HANDLE_DESC frees the descriptor handle in Handle. The call to SQLFreeHandle does not release any memory allocated by the application that may be referenced by a pointer field (including SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR) of any descriptor record of Handle. The memory allocated by the driver for fields that are not pointer fields is freed when the handle is freed. When a user-allocated descriptor handle is freed, all statements that the freed handle had been associated with revert to their respective automatically allocated descriptor handles.

Note ODBC 2.x drivers do not support freeing descriptor handles, just as they do not support allocating descriptor handles.

Note that SQLDisconnect automatically drops any statements and descriptors open on the connection. When an application frees a statement handle, the driver frees all the automatically generated descriptors associated with that handle.

For more information about descriptors, see Chapter 13: Descriptors.

Code Example

See SQLBrowseConnect and SQLConnect.

Related Functions

For information about | See
Allocating a handle | SQLAllocHandle
Canceling statement processing | SQLCancel
Setting a cursor name | SQLSetCursorName


SQLFreeStmt

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLFreeStmt stops processing associated with a specific statement, closes any open cursors associated with the statement, discards pending results, or, optionally, frees all resources associated with the statement handle.

Syntax

SQLRETURN SQLFreeStmt(
     SQLHSTMT     StatementHandle,
     SQLUSMALLINT     Option);

Arguments

StatementHandle
[Input]
Statement handle
Option
[Input]
One of the following options:

SQL_ CLOSE: Closes the cursor associated with StatementHandle (if one was defined) and discards all pending results. The application can reopen this cursor later by executing a SELECT statement again with the same or different parameter values. If no cursor is open, this option has no effect for the application. SQLCloseCursor can also be called to close a cursor. For more information, see "Closing the Cursor" in Chapter 10: Retrieving Results (Basic).

SQL_DROP: This option is deprecated. A call to SQLFreeStmt with an Option of SQL_DROP is mapped in the Driver Manager to SQLFreeHandle.

SQL_UNBIND: Sets the SQL_DESC_COUNT field of the ARD to 0, releasing all column buffers bound by SQLBindCol for the given StatementHandle. This does not unbind the bookmark column; to do that, the SQL_DESC_DATA_PTR field of the ARD for the bookmark column is set to NULL. Note that if this operation is performed on an explicitly allocated descriptor that is shared by more than one statement, the operation will affect the bindings of all statements that share the descriptor. For more information, see "Overview of Retrieving Results (Basic)" in Chapter 10: Retrieving Results (Basic).

SQL_RESET_PARAMS: Sets the SQL_DESC_COUNT field of the APD to 0, releasing all parameter buffers set by SQLBindParameter for the given StatementHandle. If this operation is performed on an explicitly allocated descriptor that is shared by more than one statement, this operation will affect the bindings of all the statements that share the descriptor. For more information, see "Binding Parameters" in Chapter 9: Executing Statements.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLFreeStmt returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLFreeStmt and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY010 | Function sequence error | (DM) An asynchronously executing function was called for the StatementHandle and was still executing when this function was called. (DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY092 | Option type out of range | (DM) The value specified for the argument Option was not: SQL_CLOSE SQL_DROP SQL_UNBIND SQL_RESET_PARAMS
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

Calling SQLFreeStmt with the SQL_CLOSE option is equivalent to calling SQLCloseCursor, with the exception that SQLFreeStmt with SQL_CLOSE has no effect on the application if no cursor is open on the statement. If no cursor is open, a call to SQLCloseCursor returns SQLSTATE 24000 (Invalid cursor state).

An application should not use a statement handle after it has been freed; the Driver Manager does not check the validity of a handle in a function call.

Code Example

See SQLBrowseConnect and SQLConnect.

Related Functions

For information about | See
Allocating a handle | SQLAllocHandle
Canceling statement processing | SQLCancel
Closing a cursor | SQLCloseCursor
Freeing a handle | SQLFreeHandle
Setting a cursor name | SQLSetCursorName


SQLGetConnectAttr

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLGetConnectAttr returns the current setting of a connection attribute.

Note For more information about what the Driver Manager maps this function to when an ODBC 3.x application is working with an ODBC 2.x driver, see "Mapping Replacement Functions for Backward Compatibility of Applications" in Chapter 17: Programming Considerations.

Syntax

SQLRETURN SQLGetConnectAttr(
     SQLHDBC     ConnectionHandle,
     SQLINTEGER     Attribute,
     SQLPOINTER     ValuePtr,
     SQLINTEGER     BufferLength,
     SQLINTEGER *     StringLengthPtr);

Arguments

ConnectionHandle
[Input]
Connection handle.
Attribute
[Input]
Attribute to retrieve.
ValuePtr
[Output]
A pointer to memory in which to return the current value of the attribute specified by Attribute.
BufferLength
[Input]
If Attribute is an ODBC-defined attribute and ValuePtr points to a character string or a binary buffer, this argument should be the length of *ValuePtr. If Attribute is an ODBC-defined attribute and *ValuePtr is an integer, BufferLength is ignored. If the value in *ValuePtr is a Unicode string (when calling SQLGetConnectAttrW), the BufferLength argument must be an even number.

If Attribute is a driver-defined attribute, the application indicates the nature of the attribute to the Driver Manager by setting the BufferLength argument. BufferLength can have the following values:

  • If *ValuePtr is a pointer to a character string, then BufferLength is the length of the string or SQL_NTS.
  • If *ValuePtr is a pointer to a binary buffer, then the application places the result of the SQL_LEN_BINARY_ATTR(length) macro in BufferLength. This places a negative value in BufferLength.
  • If *ValuePtr is a pointer to a value other than a character string or binary string, then BufferLength should have the value SQL_IS_POINTER.
  • If *ValuePtr contains a fixed-length data type, then BufferLength is either SQL_IS_INTEGER or SQL_IS_UINTEGER, as appropriate.
StringLengthPtr
[Output]
A pointer to a buffer in which to return the total number of bytes (excluding the null-termination character) available to return in *ValuePtr. If *ValuePtr is a null pointer, no length is returned. If the attribute value is a character string and the number of bytes available to return is greater than BufferLength minus the length of the null-termination character, the data in *ValuePtr is truncated to BufferLength minus the length of the null-termination character and is null-terminated by the driver.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLGetConnectAttr returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained from the diagnostic data structure by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_DBC and a Handle of ConnectionHandle. The following table lists the SQLSTATE values commonly returned by SQLGetConnectAttr and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | The data returned in *ValuePtr was truncated to be BufferLength minus the length of a null-termination character. The length of the untruncated string value is returned in *StringLengthPtr. (Function returns SQL_SUCCESS_WITH_INFO.)
08003 | Connection does not exist | (DM) An Attribute value that required an open connection was specified.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned from the diagnostic data structure by the argument MessageText in SQLGetDiagField describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY010 | Function sequence error | (DM) SQLBrowseConnect was called for the ConnectionHandle and returned SQL_NEED_DATA. This function was called before SQLBrowseConnect returned SQL_SUCCESS_WITH_INFO or SQL_SUCCESS.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) *ValuePtr is a character string, and BufferLength was less than zero but not equal to SQL_NTS.
HY092 | Invalid attribute/option identifier | The value specified for the argument Attribute was not valid for the version of ODBC supported by the driver.
HYC00 | Optional feature not implemented | The value specified for the argument Attribute was a valid ODBC connection attribute for the version of ODBC supported by the driver, but was not supported by the driver.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver corresponding to the ConnectionHandle does not support the function.

Comments

For general information about connection attributes, see "Connection Attributes" in Chapter 6: Connecting to a Data Source or Driver.

For a list of attributes that can be set, see SQLSetConnectAttr. Note that if Attribute specifies an attribute that returns a string, ValuePtr must be a pointer to a buffer for the string. The maximum length of the returned string, including the null-termination character, will be BufferLength bytes.

Depending on the attribute, an application does not need to establish a connection prior to calling SQLGetConnectAttr. However, if SQLGetConnectAttr is called and the specified attribute does not have a default and has not been set by a prior call to SQLSetConnectAttr, SQLGetConnectAttr will return SQL_NO_DATA.

If Attribute is SQL_ATTR_ TRACE or SQL_ATTR_ TRACEFILE, ConnectionHandle does not have to be valid, and SQLGetConnectAttr will not return SQL_ERROR or SQL_INVALID_HANDLE if ConnectionHandle is invalid. These attributes apply to all connections. SQLGetConnectAttr will return SQL_ERROR or SQL_INVALID_HANDLE if another argument is invalid.

While an application can set statement attributes using SQLSetConnectAttr, an application cannot use SQLGetConnectAttr to retrieve statement attribute values; it must call SQLGetStmtAttr to retrieve the setting of statement attributes.

Both SQL_ATTR_AUTO_IPD and SQL_ATTR_CONNECTION_DEAD connection attributes can be returned by a call to SQLGetConnectAttr but cannot be set by a call to SQLSetConnectAttr.

Related Functions

For information about | See
Returning the setting of a statement attribute | SQLGetStmtAttr
Setting a connection attribute | SQLSetConnectAttr
Setting an environment attribute | SQLSetEnvAttr
Setting a statement attribute | SQLSetStmtAttr


SQLGetConnectOption

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

In ODBC 3.x, the ODBC 2.x function SQLGetConnectOption has been replaced by SQLGetConnectAttr. For more information, see SQLGetConnectAttr.

Note For more information about what the Driver Manager maps this function to when an ODBC 2.x application is working with an ODBC 3.x driver, see "Mapping Deprecated Functions" in Appendix G: Driver Guidelines for Backward Compatibility.


SQLGetCursorName

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLGetCursorName returns the cursor name associated with a specified statement.

Syntax

SQLRETURN SQLGetCursorName(
     SQLHSTMT     StatementHandle,
     SQLCHAR *     CursorName,
     SQLSMALLINT     BufferLength,
     SQLSMALLINT *     NameLengthPtr);

Arguments

StatementHandle
[Input]
Statement handle.
CursorName
[Output]
Pointer to a buffer in which to return the cursor name.
BufferLength
[Input]
Length of *CursorName, in bytes. If the value in *CursorName is a Unicode string (when calling SQLGetCursorNameW), the BufferLength argument must be an even number.
NameLengthPtr
[Output]
Pointer to memory in which to return the total number of bytes (excluding the null-termination character) available to return in *CursorName. If the number of bytes available to return is greater than or equal to BufferLength, the cursor name in *CursorName is truncated to BufferLength minus the length of a null-termination character.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLGetCursorName returns either SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLGetCursorName and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | The buffer *CursorName was not large enough to return the entire cursor name, so the cursor name was truncated. The length of the untruncated cursor name is returned in *NameLengthPtr. (Function returns SQL_SUCCESS_WITH_INFO.)
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY010 | Function sequence error | (DM) An asynchronously executing function was called for the StatementHandle and was still executing when this function was called. (DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY015 | No cursor name available | (DM) The driver was an ODBC 2.x driver, there was no open cursor on the statement, and no cursor name had been set with SQLSetCursorName.
HY090 | Invalid string or buffer length | (DM) The value specified in the argument BufferLength was less than 0.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

Cursor names are used only in positioned update and delete statements (for example, UPDATE table-name ...WHERE CURRENT OF cursor-name). For more information, see "Positioned Update and Delete Statements" in Chapter 12: Updating Data. If the application does not call SQLSetCursorName to define a cursor name, the driver generates a name. This name begins with the letters SQL_CUR.

Note In ODBC 2.x, when there was no open cursor and no name had been set by a call to SQLSetCursorName, a call to SQLGetCursorName returned SQLSTATE HY015 (No cursor name available). In ODBC 3.x, this is no longer true; regardless of when SQLGetCursorName is called, the driver returns the cursor name.

SQLGetCursorName returns the name of a cursor whether or not the name was created explicitly or implicitly. A cursor name is implicitly generated if SQLSetCursorName is not called. SQLSetCursorName can be called to rename a cursor on a statement as long as the cursor is in an allocated or prepared state.

A cursor name that is set either explicitly or implicitly remains set until the StatementHandle with which it is associated is dropped, using SQLFreeHandle with a HandleType of SQL_HANDLE_STMT.

Related Functions

For information about | See
Executing an SQL statement | SQLExecDirect
Executing a prepared SQL statement | SQLExecute
Preparing a statement for execution | SQLPrepare
Setting a cursor name | SQLSetCursorName


SQLGetData

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLGetData retrieves data for a single column in the result set. It can be called multiple times to retrieve variable-length data in parts.

Syntax

SQLRETURN SQLGetData(
     SQLHSTMT     StatementHandle,
     SQLUSMALLINT     ColumnNumber,
     SQLSMALLINT     TargetType,
     SQLPOINTER     TargetValuePtr,
     SQLINTEGER     BufferLength,
     SQLINTEGER *     StrLen_or_IndPtr);

Arguments

StatementHandle
[Input]
Statement handle.
ColumnNumber
[Input]
Number of the column for which to return data. Result set columns are numbered in increasing column order starting at 1. The bookmark column is column number 0; this can be specified only if bookmarks are enabled.
TargetType
[Input]
The type identifier of the C data type of the *TargetValuePtr buffer. For a list of valid C data types and type identifiers, see the "C Data Types" section in Appendix D: Data Types. If TargetType is SQL_ARD_TYPE, the driver uses the type identifier specified in the SQL_DESC_CONCISE_TYPE field of the ARD. If it is SQL_C_DEFAULT, the driver selects the default C data type based on the SQL data type of the source.
TargetValuePtr
[Output]
Pointer to the buffer in which to return the data.
BufferLength
[Input]
Length of the *TargetValuePtr buffer in bytes.

The driver uses BufferLength to avoid writing past the end of the *TargetValuePtr buffer when returning variable-length data, such as character or binary data. Note that the driver counts the null-termination character when returning character data to *TargetValuePtr. *TargetValuePtr must therefore contain space for the null-termination character, or the driver will truncate the data.

When the driver returns fixed-length data, such as an integer or a date structure, the driver ignores BufferLength and assumes the buffer is large enough to hold the data. It is therefore important for the application to allocate a large enough buffer for fixed-length data or the driver will write past the end of the buffer.

SQLGetData returns SQLSTATE HY090 (Invalid string or buffer length) when BufferLength is less than 0 but not when BufferLength is 0.

If TargetValuePtr is set to a null pointer, BufferLength is ignored by the driver.

StrLen_or_IndPtr
[Output]
Pointer to the buffer in which to return the length or indicator value. If this is a null pointer, no length or indicator value is returned. This returns an error when the data being fetched is NULL.

SQLGetData can return the following values in the length/indicator buffer:

  • The length of the data available to return
  • SQL_NO_TOTAL
  • SQL_NULL_DATA

    For more information, see the "Using Length/Indicator Values" section in Chapter 4: ODBC Fundamentals and "Comments" in this section.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLGetData returns either SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLGetData and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 String data, right truncated Not all of the data for the specified column, ColumnNumber, could be retrieved in a single call to the function. The length of the data remaining in the specified column prior to the current call to SQLGetData is returned in *StrLen_or_IndPtr. (Function returns SQL_SUCCESS_WITH_INFO.)

The TargetValuePtr argument was a null pointer, and more data was available to return. (Function returns SQL_SUCCESS_WITH_INFO.)

For more information on using multiple calls to SQLGetData for a single column, see "Comments."
01S07 Fractional truncation The data returned for one or more columns was truncated. For numeric data types, the fractional part of the number was truncated. For time, timestamp, and interval data types containing a time component, the fractional portion of the time was truncated.

(Function returns SQL_SUCCESS_WITH_INFO.)
07006 | Restricted data type attribute violation | The data value of a column in the result set cannot be converted to the C data type specified by the argument TargetType.
07009 Invalid descriptor index The value specified for the argument ColumnNumber was 0, and the SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_OFF.

The value specified for the argument ColumnNumber was greater than the number of columns in the result set.

(DM) The specified column was bound. This description does not apply to drivers that return the SQL_GD_BOUND bitmask for the SQL_GETDATA_EXTENSIONS option in SQLGetInfo.

(DM) The number of the specified column was less than or equal to the number of the highest bound column. This description does not apply to drivers that return the SQL_GD_ANY_COLUMN bitmask for the SQL_GETDATA_EXTENSIONS option in SQLGetInfo.

(DM) The application has already called SQLGetData for the current row; the number of the column specified in the current call was less than the number of the column specified in the preceding call; and the driver does not return the SQL_GD_ANY_ORDER bitmask for the SQL_GETDATA_EXTENSIONS option in SQLGetInfo.

(DM) The TargetType argument was SQL_ARD_TYPE, and the ColumnNumber descriptor record in the ARD failed the consistency check.

(DM) The TargetType argument was SQL_ARD_TYPE, and the value in the SQL_DESC_COUNT field of the ARD was less than the ColumnNumber argument.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
22002 | Indicator variable required but not supplied | StrLen_or_IndPtr was a null pointer and NULL data was retrieved.
22003 Numeric value out of range Returning the numeric value (as numeric or string) for the column would have caused the whole (as opposed to fractional) part of the number to be truncated.

For more information, see Appendix D: Data Types.
22007 | Invalid datetime format | The character column in the result set was bound to a C date, time, or timestamp structure, and the value in the column was an invalid date, time, or timestamp, respectively. For more information, see Appendix D: Data Types.
22012 | Division by zero | A value from an arithmetic expression that resulted in division by zero was returned.
22015 Interval field overflow Assigning from an exact numeric or interval SQL type to an interval C type caused a loss of significant digits in the leading field.

When returning data to an interval C type, there was no representation of the value of the SQL type in the interval C type.
22018 Invalid character value for cast specification A character column in the result set was returned to a character C buffer, and the column contained a character for which there was no representation in the character set of the buffer.

The C type was an exact or approximate numeric, a datetime, or an interval data type; the SQL type of the column was a character data type; and the value in the column was not a valid literal of the bound C type.
24000 Invalid cursor state (DM) The function was called without first calling SQLFetch or SQLFetchScroll to position the cursor on the row of data required.

(DM) The StatementHandle was in an executed state, but no result set was associated with the StatementHandle.

A cursor was open on the StatementHandle and SQLFetch or SQLFetchScroll had been called, but the cursor was positioned before the start of the result set or after the end of the result set.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY003 Invalid application buffer type (DM) The argument TargetType was neither a valid data type, SQL_C_DEFAULT, nor SQL_ARD_TYPE.

(DM) The argument ColumnNumber was 0, and the argument TargetType was not SQL_C_BOOKMARK for a fixed-length bookmark or SQL_C_VARBOOKMARK for a variable-length bookmark.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle, and then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application, and then the function was called again on the StatementHandle.
HY009 | Invalid use of null pointer | (DM) The argument CursorName was a null pointer.
HY010 Function sequence error (DM) The specified StatementHandle was not in an executed state. The function was called without first calling SQLExecDirect, SQLExecute or a catalog function.

(DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.

(DM) The StatementHandle was in an executed state, but no result set was associated with the StatementHandle.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 Invalid string or buffer length (DM) The value specified for argument BufferLength was less than 0.

The value specified for argument BufferLength was less than 4, the ColumnNumber argument was set to 0, and the driver was an ODBC 2.x driver.
HY109 Invalid cursor position The cursor was positioned (by SQLSetPos, SQLFetch, SQLFetchScroll, or SQLBulkOperations) on a row that had been deleted or could not be fetched.

The cursor was a forward-only cursor, and the rowset size was greater than one.
HYC00 Optional feature not implemented The driver or data source does not support use of SQLGetData with multiple rows in SQLFetchScroll. This description does not apply to drivers that return the SQL_GD_BLOCK bitmask for the SQL_GETDATA_EXTENSIONS option in SQLGetInfo.

The driver or data source does not support the conversion specified by the combination of the TargetType argument and the SQL data type of the corresponding column. This error applies only when the SQL data type of the column was mapped to a driver-specific SQL data type.

The driver supports only ODBC 2.x, and the argument TargetType was one of the following:

SQL_C_GUID
SQL_C_NUMERIC
SQL_C_SBIGINT
SQL_C_UBIGINT

and any of the interval C data types listed in "C Data Types" in Appendix D: Data Types.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver corresponding to the StatementHandle does not support the function.

Comments

SQLGetData returns the data in a specified column. SQLGetData can be called only after one or more rows have been fetched from the result set by SQLFetch, SQLFetchScroll, or SQLExtendedFetch. If variable-length data is too large to be returned in a single call to SQLGetData (due to a limitation in the application), SQLGetData can retrieve it in parts. It is possible to bind some columns in a row and call SQLGetData for others, although this is subject to some restrictions. For more information, see "Getting Long Data" in Chapter 10: Retrieving Results (Basic).

Using SQLGetData

If the driver does not support extensions to SQLGetData, the function can return data only for unbound columns with a number greater than that of the last bound column. Furthermore, within a row of data, the value of the ColumnNumber argument in each call to SQLGetData must be greater than or equal to the value of ColumnNumber in the previous call; that is, data must be retrieved in increasing column number order. Finally, if no extensions are supported, SQLGetData cannot be called if the rowset size is greater than 1.

Drivers can relax any of these restrictions. To determine what restrictions a driver relaxes, an application calls SQLGetInfo with any of the following SQL_GETDATA_EXTENSIONS options:

  • SQL_GD_ANY_COLUMN. If this option is returned, SQLGetData can be called for any unbound column, including those before the last bound column.
  • SQL_GD_ANY_ORDER. If this option is returned, SQLGetData can be called for unbound columns in any order.
  • SQL_GD_BLOCK. If this option is returned by SQLGetInfo for the SQL_GETDATA_EXTENSIONS InfoType, the driver supports calls to SQLGetData when the rowset size is greater than 1 and the application can call SQLSetPos with the SQL_POSITION option to position the cursor on the correct row before calling SQLGetData.
  • SQL_GD_BOUND. If this option is returned, SQLGetData can be called for bound columns as well as unbound columns.

There are two exceptions to these restrictions and a driver's ability to relax them. First, SQLGetData should never be called for a forward-only cursor when the rowset size is greater than 1. Second, if a driver supports bookmarks, it must always support the ability to call SQLGetData for column 0, even if it does not allow applications to call SQLGetData for other columns before the last bound column. (When an application is working with an ODBC 2.x driver, SQLGetData will successfully return a bookmark when called with ColumnNumber equal to 0 after a call to SQLFetch, because SQLFetch is mapped by the ODBC 3.x Driver Manager to SQLExtendedFetch with a FetchOrientation of SQL_FETCH_NEXT, and SQLGetData with a ColumnNumber of 0 is mapped by the ODBC 3.x Driver Manager to SQLGetStmtOption with an fOption of SQL_GET_BOOKMARK.)

SQLGetData cannot be used to retrieve the bookmark for a row just inserted by calling SQLBulkOperations with the SQL_ADD option, because the cursor is not positioned on the row. An application can retrieve the bookmark for such a row by binding column 0 before calling SQLBulkOperations with SQL_ADD, in which case SQLBulkOperations returns the bookmark in the bound buffer. SQLFetchScroll can then be called with SQL_FETCH_BOOKMARK to reposition the cursor on that row.

If the TargetType argument is an interval data type, the default interval leading precision (2) and the default interval seconds precision (6), as set in the SQL_DESC_DATETIME_INTERVAL_PRECISION and SQL_DESC_PRECISION fields of the ARD, respectively, are used for the data. If the TargetType argument is an SQL_C_NUMERIC data type, the default precision (driver-defined) and default scale (0), as set in the SQL_DESC_PRECISION and SQL_DESC_SCALE fields of the ARD, are used for the data. If any default precision or scale is not appropriate, the application should explicitly set the appropriate descriptor field by a call to SQLSetDescField or SQLSetDescRec. It can set the SQL_DESC_CONCISE_TYPE field to SQL_C_NUMERIC and call SQLGetData with a TargetType argument of SQL_ARD_TYPE, which will cause the precision and scale values in the descriptor fields to be used.

Note In ODBC 2.x, applications set TargetType to SQL_C_DATE, SQL_C_TIME, or SQL_C_TIMESTAMP to indicate that *TargetValuePtr is a date, time, or timestamp structure. In ODBC 3.x, applications set TargetType to SQL_C_TYPE_DATE, SQL_C_TYPE_TIME, or SQL_C_TYPE_TIMESTAMP. The Driver Manager makes appropriate mappings if necessary, based on the application and driver version.

Retrieving Variable-Length Data in Parts

SQLGetData can be used to retrieve data from a column that contains variable-length data in parts—that is, when the identifier of the SQL data type of the column is SQL_CHAR, SQL_VARCHAR, SQL_LONGVARCHAR, SQL_WCHAR, SQL_WVARCHAR, SQL_WLONGVARCHAR, SQL_BINARY, SQL_VARBINARY, SQL_LONGVARBINARY, or a driver-specific identifier for a variable-length type.

To retrieve data from a column in parts, the application calls SQLGetData multiple times in succession for the same column. On each call, SQLGetData returns the next part of the data. It is up to the application to reassemble the parts, taking care to remove the null-termination character from intermediate parts of character data. If there is more data to return or not enough buffer was allocated for the terminating character, SQLGetData returns SQL_SUCCESS_WITH_INFO and SQLSTATE 01004 (Data truncated). When it returns the last part of the data, SQLGetData returns SQL_SUCCESS. Neither SQL_NO_TOTAL nor zero can be returned on the last valid call to retrieve data from a column, because the application would then have no way of knowing how much of the data in the application buffer is valid. If SQLGetData is called after this, it returns SQL_NO_DATA. For more information, see the next section, "Retrieving Data with SQLGetData."

Variable-length bookmarks can be returned in parts by SQLGetData. As with other data, a call to SQLGetData to return variable-length bookmarks in parts will return SQLSTATE 01004 (String data, right truncated) and SQL_SUCCESS_WITH_INFO when there is more data to be returned. This is different than the case when a variable-length bookmark is truncated by a call to SQLFetch or SQLFetchScroll, which returns SQL_ERROR and SQLSTATE 22001 (String data, right truncated).

SQLGetData cannot be used to return fixed-length data in parts. If SQLGetData is called more than one time in a row for a column containing fixed-length data, it returns SQL_NO_DATA for all calls after the first.

Retrieving Data with SQLGetData

To return data for the specified column, SQLGetData performs the following sequence of steps:

  1. Returns SQL_NO_DATA if it has already returned all of the data for the column.
  2. Sets *StrLen_or_IndPtr to SQL_NULL_DATA if the data is NULL. If the data is NULL and StrLen_or_IndPtr was a null pointer, SQLGetData returns SQLSTATE 22002 (Indicator variable required but not supplied).

    If the data for the column is not NULL, SQLGetData proceeds to step 3.

  3. If the SQL_ATTR_MAX_LENGTH statement attribute is set to a nonzero value, if the column contains character or binary data, and if SQLGetData has not previously been called for the column, the data is truncated to SQL_ATTR_MAX_LENGTH bytes.

    Note The SQL_ATTR_MAX_LENGTH statement attribute is intended to reduce network traffic. It is generally implemented by the data source, which truncates the data before returning it across the network. Drivers and data sources are not required to support it. Therefore, to guarantee that data is truncated to a particular size, an application should allocate a buffer of that size and specify the size in the BufferLength argument.

  4. Converts the data to the type specified in TargetType. The data is given the default precision and scale for that data type. If TargetType is SQL_ARD_TYPE, the data type in the SQL_DESC_CONCISE_TYPE field of the ARD is used. If TargetType is SQL_ARD_TYPE, the data is given the precision and scale in the SQL_DESC_DATETIME_INTERVAL_PRECISION, SQL_DESC_PRECISION, and SQL_DESC_SCALE fields of the ARD, depending on the data type in the SQL_DESC_CONCISE_TYPE field. If any default precision or scale is not appropriate, the application should explicitly set the appropriate descriptor field by a call to SQLSetDescField or SQLSetDescRec.
  5. If the data was converted to a variable-length data type, such as character or binary, SQLGetData checks whether the length of the data exceeds BufferLength. If the length of character data (including the null-termination character) exceeds BufferLength, SQLGetData truncates the data to BufferLength less the length of a null-termination character. It then null-terminates the data. If the length of binary data exceeds the length of the data buffer, SQLGetData truncates it to BufferLength bytes.

    If the data buffer supplied is too small to hold the null-termination character, SQLGetData returns SQL_SUCCESS_WITH_INFO and SQLSTATE 01004.

    SQLGetData never truncates data converted to fixed-length data types; it always assumes that the length of *TargetValuePtr is the size of the data type.

  6. Places the converted (and possibly truncated) data in *TargetValuePtr. Note that SQLGetData cannot return data out of line.
  7. Places the length of the data in *StrLen_or_IndPtr. If StrLen_or_IndPtr was a null pointer, SQLGetData does not return the length.
    • For character or binary data, this is the length of the data after conversion and before truncation due to BufferLength. If the driver cannot determine the length of the data after conversion, as is sometimes the case with long data, it returns SQL_SUCCESS_WITH_INFO and sets the length to SQL_NO_TOTAL. (The last call to SQLGetData must always return the length of the data, not zero or SQL_NO_TOTAL.) If data was truncated due to the SQL_ATTR_MAX_LENGTH statement attribute, the value of this attribute—as opposed to the actual length—is placed in *StrLen_or_IndPtr. This is because this attribute is designed to truncate data on the server before conversion, so the driver has no way of figuring out what the actual length is. When SQLGetData is called multiple times in succession for the same column, this is the length of the data available at the start of the current call; that is, the length decreases with each subsequent call.
    • For all other data types, this is the length of the data after conversion; that is, it is the size of the type to which the data was converted.
  8. If the data is truncated without loss of significance during conversion (for example, the real number 1.234 is truncated when converted to the integer 1) or because BufferLength is too small (for example, the string "abcdef" is placed in a 4-byte buffer), SQLGetData returns SQLSTATE 01004 (Data truncated) and SQL_SUCCESS_WITH_INFO. If data is truncated without loss of significance due to the SQL_ATTR_MAX_LENGTH statement attribute, SQLGetData returns SQL_SUCCESS and does not return SQLSTATE 01004 (Data truncated).

The contents of the bound data buffer (if SQLGetData is called on a bound column) and the length/indicator buffer are undefined if SQLGetData does not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO.

Successive calls to SQLGetData will retrieve data from the last column requested; prior offsets become invalid. For example, when the following sequence is performed:

SQLGetData(icol=n), SQLGetData(icol=m), SQLGetData(icol=n)

the second call to SQLGetData(icol=n) retrieves data from the start of the n column. Any offset in the data due to earlier calls to SQLGetData for the column is no longer valid.

Descriptors and SQLGetData

SQLGetData does not interact directly with any descriptor fields.

If TargetType is SQL_ARD_TYPE, the data type in the SQL_DESC_CONCISE_TYPE field of the ARD is used. If TargetType is either SQL_ARD_TYPE or SQL_C_DEFAULT, the data is given the precision and scale in the SQL_DESC_DATETIME_INTERVAL_PRECISION, SQL_DESC_PRECISION, and SQL_DESC_SCALE fields of the ARD, depending on the data type in the SQL_DESC_CONCISE_TYPE field.

Code Example

In the following example, an application executes a SELECT statement to return a result set of the customer IDs, names, and phone numbers sorted by name, ID, and phone number. For each row of data, it calls SQLFetch to position the cursor to the next row. It calls SQLGetData to retrieve the fetched data; the buffers for the data and the returned number of bytes are specified in the call to SQLGetData. Finally, it prints each employee's name, ID, and phone number.

#define NAME_LEN 50
#define PHONE_LEN 50

SQLCHAR      szName[NAME_LEN], szPhone[PHONE_LEN];
SQLINTEGER   sCustID, cbName, cbAge, cbBirthday;
SQLRETURN    retcode;
SQLHSTMT     hstmt;

retcode = SQLExecDirect(hstmt,
   "SELECT CUSTID, NAME, PHONE FROM CUSTOMERS ORDER BY 2, 1, 3",
   SQL_NTS);

if (retcode == SQL_SUCCESS) {
   while (TRUE) {
      retcode = SQLFetch(hstmt);
      if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
         show_error();
      }
      if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){

         /* Get data for columns 1, 2, and 3 */

         SQLGetData(hstmt, 1, SQL_C_ULONG, &sCustID, 0, &cbCustID);
         SQLGetData(hstmt, 2, SQL_C_CHAR, szName, NAME_LEN, &cbName);
         SQLGetData(hstmt, 3, SQL_C_CHAR, szPhone, PHONE_LEN,
            &cbPhone);

         /* Print the row of data */

         fprintf(out, "%-5d %-*s %*s", sCustID, NAME_LEN-1, szName,
            PHONE_LEN-1, szPhone);
      } else {
         break;
      }
   }
}

Related Functions

For information about | See
Assigning storage for a column in a result set | SQLBindCol
Performing bulk operations that do not relate to the block cursor position | SQLBulkOperations
Canceling statement processing | SQLCancel
Executing an SQL statement | SQLExecDirect
Executing a prepared SQL statement | SQLExecute
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Fetching a single row of data or a block of data in a forward-only direction | SQLFetch
Sending parameter data at execution time | SQLPutData
Positioning the cursor, refreshing data in the rowset, or updating or deleting data in the rowset | SQLSetPos


SQLGetDescField

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLGetDescField returns the current setting or value of a single field of a descriptor record.

Syntax

SQLRETURN SQLGetDescField(
     SQLHDESC     DescriptorHandle,
     SQLSMALLINT     RecNumber,
     SQLSMALLINT     FieldIdentifier,
     SQLPOINTER     ValuePtr,
     SQLINTEGER     BufferLength,
     SQLINTEGER *     StringLengthPtr);

Arguments

DescriptorHandle
[Input]
Descriptor handle.
RecNumber
[Input]
Indicates the descriptor record from which the application seeks information. Descriptor records are numbered from 0, with record number 0 being the bookmark record. If the FieldIdentifier argument indicates a header field, RecNumber is ignored. If RecNumber is less than or equal to SQL_DESC_COUNT but the row does not contain data for a column or parameter, a call to SQLGetDescField will return the default values of the fields. (For more information, see "Initialization of Descriptor Fields" in SQLSetDescField.)
FieldIdentifier
[Input]
Indicates the field of the descriptor whose value is to be returned. For more information, see the "FieldIdentifier Argument" section in SQLSetDescField.
ValuePtr
[Output]
Pointer to a buffer in which to return the descriptor information. The data type depends on the value of FieldIdentifier.
BufferLength
[Input]
If FieldIdentifier is an ODBC-defined field and ValuePtr points to a character string or a binary buffer, this argument should be the length of *ValuePtr. If FieldIdentifier is an ODBC-defined field and *ValuePtr is an integer, BufferLength is ignored. If the value in *ValuePtr is of a Unicode data type (when calling SQLGetDescFieldW), the BufferLength argument must be an even number.

If FieldIdentifier is a driver-defined field, the application indicates the nature of the field to the Driver Manager by setting the BufferLength argument. BufferLength can have the following values:

  • If *ValuePtr is a pointer to a character string, then BufferLength is the length of the string or SQL_NTS.
  • If *ValuePtr is a pointer to a binary buffer, then the application places the result of the SQL_LEN_BINARY_ATTR(length) macro in BufferLength. This places a negative value in BufferLength.
  • If *ValuePtr is a pointer to a value other than a character string or binary string, then BufferLength should have the value SQL_IS_POINTER.
  • If *ValuePtr is contains a fixed-length data type, then BufferLength is either SQL_IS_INTEGER, SQL_IS_UINTEGER, SQL_IS_SMALLINT, or SQL_IS_USMALLINT, as appropriate.
StringLengthPtr
[Output]
Pointer to the buffer in which to return the total number of bytes (excluding the number of bytes required for the null-termination character) available to return in *ValuePtr.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, SQL_NO_DATA, or SQL_INVALID_HANDLE.

SQL_NO_DATA is returned if RecNumber is greater than the current number of descriptor records.

SQL_NO_DATA is returned if DescriptorHandle is an IRD handle and the statement is in the prepared or executed state but there was no open cursor associated with it.

Diagnostics

When SQLGetDescField returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLGetDescField and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | The buffer *ValuePtr was not large enough to return the entire descriptor field, so the field was truncated. The length of the untruncated descriptor field is returned in *StringLengthPtr. (Function returns SQL_SUCCESS_WITH_INFO.)
07009 | Invalid descriptor index | (DM) The RecNumber argument was equal to 0, the SQL_ATTR_USE_BOOKMARK statement attribute was SQL_UB_OFF, and the DescriptorHandle argument was an IRD handle. (This error can be returned for an explicitly allocated descriptor only if the descriptor is associated with a statement handle.) The FieldIdentifier argument was a record field, the RecNumber argument was 0, and the DescriptorHandle argument was an IPD handle. The RecNumber argument was less than 0.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate the memory required to support execution or completion of the function.
HY007 | Associated statement is not prepared | DescriptorHandle was associated with a StatementHandle as an IRD, and the associated statement handle had not been prepared or executed.
HY010 | Function sequence error | (DM) DescriptorHandle was associated with a StatementHandle for which an asynchronously executing function (not this one) was called and was still executing when this function was called. (DM) DescriptorHandle was associated with a StatementHandle for which SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY021 | Inconsistent descriptor information | The SQL_DESC_TYPE and SQL_DESC_DATETIME_INTERVAL_CODE fields do not form a valid ODBC SQL type, a valid driver-specific SQL type (for IPDs), or a valid ODBC C type (for APDs or ARDs).
HY090 | Invalid string or buffer length | (DM) *ValuePtr was a character string, and BufferLength was less than zero.
HY091 | Invalid descriptor field identifier | FieldIdentifier was not an ODBC-defined field and was not an implementation-defined value. FieldIdentifier was undefined for the DescriptorHandle.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the DescriptorHandle does not support the function.

Comments

An application can call SQLGetDescField to return the value of a single field of a descriptor record. A call to SQLGetDescField can return the setting of any field in any descriptor type, including header fields, record fields, and bookmark fields. An application can obtain the settings of multiple fields in the same or different descriptors, in arbitrary order, by making repeated calls to SQLGetDescField. SQLGetDescField can also be called to return driver-defined descriptor fields.

For performance reasons, an application should not call SQLGetDescField for an IRD before executing a statement.

The settings of multiple fields that describe the name, data type, and storage of column or parameter data can also be retrieved in a single call to SQLGetDescRec. SQLGetStmtAttr can be called to return the setting of a single field in the descriptor header that is also a statement attribute. SQLColAttribute, SQLDescribeCol, and SQLDescribeParam return record or bookmark fields.

When an application calls SQLGetDescField to retrieve the value of a field that is undefined for a particular descriptor type, the function returns SQL_SUCCESS but the value returned for the field is undefined. For example, calling SQLGetDescField for the SQL_DESC_NAME or SQL_DESC_NULLABLE field of an APD or ARD will return SQL_SUCCESS but an undefined value for the field.

When an application calls SQLGetDescField to retrieve the value of a field that is defined for a particular descriptor type but that has no default value and has not been set yet, the function returns SQL_SUCCESS but the value returned for the field is undefined. For more information on the initialization of descriptor fields and descriptions of the fields, see "Initialization of Descriptor Fields" in SQLSetDescField. For more information on descriptors, see Chapter 13: Descriptors.

Related Functions

For information about | See
Getting multiple descriptor fields | SQLGetDescRec
Setting a single descriptor field | SQLSetDescField
Setting multiple descriptor fields | SQLSetDescRec


SQLGetDescRec

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLGetDescRec returns the current settings or values of multiple fields of a descriptor record. The fields returned describe the name, data type, and storage of column or parameter data.

Syntax

SQLRETURN SQLGetDescRec(
     SQLHDESC     DescriptorHandle,
     SQLSMALLINT     RecNumber,
     SQLCHAR *     Name,
     SQLSMALLINT     BufferLength,
     SQLSMALLINT *     StringLengthPtr,
     SQLSMALLINT *     TypePtr,
     SQLSMALLINT *     SubTypePtr,
     SQLINTEGER *     LengthPtr,
     SQLSMALLINT *     PrecisionPtr,
     SQLSMALLINT *     ScalePtr,
     SQLSMALLINT *     NullablePtr);

Arguments

DescriptorHandle
[Input]
Descriptor handle.
RecNumber
[Input]
Indicates the descriptor record from which the application seeks information. Descriptor records are numbered from 1, with record number 0 being the bookmark record. The RecNumber argument must be less than or equal to the value of SQL_DESC_COUNT. If RecNumber is less than or equal to SQL_DESC_COUNT but the row does not contain data for a column or parameter, a call to SQLGetDescRec will return the default values of the fields. (For more information, see "Initialization of Descriptor Fields" in SQLSetDescField.)
Name
[Output]
A pointer to a buffer in which to return the SQL_DESC_NAME field for the descriptor record.
BufferLength
[Input]
Length of the *Name buffer, in bytes.
StringLengthPtr
[Output]
A pointer to a buffer in which to return the number of bytes of data available to return in the *Name buffer, excluding the null-termination character. If the number of bytes was greater than or equal to BufferLength, the data in *Name is truncated to BufferLength minus the length of a null-termination character, and is null-terminated by the driver.
TypePtr
[Output]
A pointer to a buffer in which to return the value of the SQL_DESC_TYPE field for the descriptor record.
SubTypePtr
[Output]
For records whose type is SQL_DATETIME or SQL_INTERVAL, this is a pointer to a buffer in which to return the value of the SQL_DESC_DATETIME_INTERVAL_CODE field.
LengthPtr
[Output]
A pointer to a buffer in which to return the value of the SQL_DESC_OCTET_LENGTH field for the descriptor record.
PrecisionPtr
[Output]
A pointer to a buffer in which to return the value of the SQL_DESC_PRECISION field for the descriptor record.
ScalePtr
[Output]
A pointer to a buffer in which to return the value of the SQL_DESC_SCALE field for the descriptor record.
NullablePtr
[Output]
A pointer to a buffer in which to return the value of the SQL_DESC_NULLABLE field for the descriptor record.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, SQL_NO_DATA, or SQL_INVALID_HANDLE.

SQL_NO_DATA is returned if RecNumber is greater than the current number of descriptor records.

SQL_NO_DATA is returned if DescriptorHandle is an IRD handle and the statement is in the prepared or executed state but there was no open cursor associated with it.

Diagnostics

When SQLGetDescRec returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_DESC and a Handle of DescriptorHandle. The following table lists the SQLSTATE values commonly returned by SQLGetDescRec and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | The buffer *Name was not large enough to return the entire descriptor field, so the field was truncated. The length of the untruncated descriptor field is returned in *StringLengthPtr. (Function returns SQL_SUCCESS_WITH_INFO.)
07009 | Invalid descriptor index | The FieldIdentifier argument was a record field, the RecNumber argument was set to 0, and the DescriptorHandle argument was an IPD handle. (DM) The RecNumber argument was set to 0, and the SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_OFF, and the DescriptorHandle argument was an IRD handle. The RecNumber argument was less than 0.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate the memory required to support execution or completion of the function.
HY007 | Associated statement is not prepared | DescriptorHandle was associated with an IRD, and the associated statement handle was not in the prepared or executed state.
HY010 | Function sequence error | (DM) DescriptorHandle was associated with a StatementHandle for which an asynchronously executing function (not this one) was called and was still executing when this function was called. (DM) DescriptorHandle was associated with a StatementHandle for which SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with DescriptorHandle does not support the function.

Comments

An application can call SQLGetDescRec to retrieve the values of the following descriptor fields for a single column or parameter:

  • SQL_DESC_NAME
  • SQL_DESC_TYPE
  • SQL_DESC_DATETIME_INTERVAL_CODE (for records whose type is SQL_DATETIME or SQL_INTERVAL)
  • SQL_DESC_OCTET_LENGTH
  • SQL_DESC_PRECISION
  • SQL_DESC_SCALE
  • SQL_DESC_NULLABLE

SQLGetDescRec does not retrieve the values for header fields.

An application can inhibit the return of a field's setting by setting the argument corresponding to the field to a null pointer.

When an application calls SQLGetDescRec to retrieve the value of a field that is undefined for a particular descriptor type, the function returns SQL_SUCCESS but the value returned for the field is undefined. For example, calling SQLGetDescRec for the SQL_DESC_NAME or SQL_DESC_NULLABLE field of an APD or ARD will return SQL_SUCCESS but an undefined value for the field.

When an application calls SQLGetDescRec to retrieve the value of a field that is defined for a particular descriptor type but that has no default value and has not been set yet, the function returns SQL_SUCCESS but the value returned for the field is undefined. For more information, see "Initialization of Descriptor Fields" in SQLSetDescField.

The values of fields can also be retrieved individually by a call to SQLGetDescField. For a description of the fields in a descriptor header or record, see SQLSetDescField. For more information on descriptors, see Chapter 13: Descriptors.

Related Functions

For information about | See
Binding a column | SQLBindCol
Binding a parameter | SQLBindParameter
Getting a descriptor field | SQLGetDescField
Setting multiple descriptor fields | SQLSetDescRec


SQLGetDiagField

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLGetDiagField returns the current value of a field of a record of the diagnostic data structure (associated with a specified handle) that contains error, warning, and status information.

Syntax

SQLRETURN SQLGetDiagField(
     SQLSMALLINT     HandleType,
     SQLHANDLE     Handle,
     SQLSMALLINT     RecNumber,
     SQLSMALLINT     DiagIdentifier,
     SQLPOINTER     DiagInfoPtr,
     SQLSMALLINT     BufferLength,
     SQLSMALLINT *     StringLengthPtr);

Arguments

HandleType
[Input]
A handle type identifier that describes the type of handle for which diagnostics are required. Must be one of the following:

SQL_HANDLE_ENV
SQL_HANDLE_DBC
SQL_HANDLE_STMT
SQL_HANDLE_DESC

Handle
[Input]
A handle for the diagnostic data structure, of the type indicated by HandleType. If HandleType is SQL_HANDLE_ENV, Handle can be either a shared or an unshared environment handle.
RecNumber
[Input]
Indicates the status record from which the application seeks information. Status records are numbered from 1. If the DiagIdentifier argument indicates any field of the diagnostics header, RecNumber is ignored. If not, it should be greater than 0.
DiagIdentifier
[Input]
Indicates the field of the diagnostic whose value is to be returned. For more information, see the "DiagIdentifier Argument" section in "Comments."
DiagInfoPtr
[Output]
Pointer to a buffer in which to return the diagnostic information. The data type depends on the value of DiagIdentifier.
BufferLength
[Input]
If DiagIdentifier is an ODBC-defined diagnostic and DiagInfoPtr points to a character string or a binary buffer, this argument should be the length of *DiagInfoPtr. If DiagIdentifier is an ODBC-defined field and *DiagInfoPtr is an integer, BufferLength is ignored. If the value in *DiagInfoPtr is a Unicode string (when calling SQLGetDiagFieldW), the BufferLength argument must be an even number.

If DiagIdentifier is a driver-defined field, the application indicates the nature of the field to the Driver Manager by setting the BufferLength argument. BufferLength can have the following values:

  • If *ValuePtr is a pointer to a character string, then BufferLength is the length of the string or SQL_NTS.
  • If *ValuePtr is a pointer to a binary buffer, then the application places the result of the SQL_LEN_BINARY_ATTR(length) macro in BufferLength. This places a negative value in BufferLength.
  • If *ValuePtr is a pointer to a value other than a character string or binary string, then BufferLength should have the value SQL_IS_POINTER.
  • If *ValuePtr is contains a fixed-length data type, then BufferLength is SQL_IS_INTEGER, SQL_IS_UINTEGER, SQL_IS_SMALLINT, or SQL_IS_USMALLINT, as appropriate.
StringLengthPtr
[Output]
Pointer to a buffer in which to return the total number of bytes (excluding the number of bytes required for the null-termination character) available to return in *DiagInfoPtr, for character data. If the number of bytes available to return is greater than BufferLength, the text in *DiagInfoPtr is truncated to BufferLength minus the length of a null-termination character.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, SQL_INVALID_HANDLE, or SQL_NO_DATA.

Diagnostics

SQLGetDiagField does not post diagnostic records for itself. It uses the following return values to report the outcome of its own execution:

  • SQL_SUCCESS: The function successfully returned diagnostic information.
  • SQL_SUCCESS_WITH_INFO: *DiagInfoPtr was too small to hold the requested diagnostic field, so the data in the diagnostic field was truncated. To determine that a truncation occurred, the application must compare BufferLength to the actual number of bytes available, which is written to *StringLengthPtr.
  • SQL_INVALID_HANDLE: The handle indicated by HandleType and Handle was not a valid handle.
  • SQL_ERROR: One of the following occurred:
    • The DiagIdentifier argument was not one of the valid values.
    • The DiagIdentifier argument was SQL_DIAG_CURSOR_ROW_COUNT, SQL_DIAG_DYNAMIC_FUNCTION, SQL_DIAG_DYNAMIC_FUNCTION_CODE, or SQL_DIAG_ROW_COUNT, but Handle was not a statement handle. (The Driver Manager returns this diagnostic.)
    • The RecNumber argument was negative or 0 when DiagIdentifier indicated a field from a diagnostic record. RecNumber is ignored for header fields.
    • The value requested was a character string and BufferLength was less than zero.
  • SQL_NO_DATA: RecNumber was greater than the number of diagnostic records that existed for the handle specified in Handle. The function also returns SQL_NO_DATA for any positive RecNumber if there are no diagnostic records for Handle.

Comments

An application typically calls SQLGetDiagField to accomplish one of three goals:

  1. To obtain specific error or warning information when a function call has returned SQL_ERROR or SQL_SUCCESS_WITH_INFO (or SQL_NEED_DATA for the SQLBrowseConnect function).
  2. To find out the number of rows in the data source that were affected when insert, delete, or update operations were performed with a call to SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos (from the SQL_DIAG_ROW_COUNT header field), or to find out the number of rows that exist in the current open cursor, if the driver is able to provide this information (from the SQL_DIAG_CURSOR_ROW_COUNT header field).
  3. To determine which function was executed by a call to SQLExecDirect or SQLExecute (from the SQL_DIAG_DYNAMIC_FUNCTION and SQL_DIAG_DYNAMIC_FUNCTION_CODE header fields).

Any ODBC function can post zero or more diagnostic records each time it is called, so an application can call SQLGetDiagField after any ODBC function call. There is no limit to the number of diagnostic records that can be stored at any one time. SQLGetDiagField retrieves only the diagnostic information most recently associated with the diagnostic data structure specified in the Handle argument. If the application calls an ODBC function other than SQLGetDiagField or SQLGetDiagRec, any diagnostic information from a previous call with the same handle is lost.

An application can scan all diagnostic records by incrementing RecNumber, as long as SQLGetDiagField returns SQL_SUCCESS. The number of status records is indicated in the SQL_DIAG_NUMBER header field. Calls to SQLGetDiagField are nondestructive to the header and record fields. The application can call SQLGetDiagField again at a later time to retrieve a field from a record, as long as a function other than the diagnostic functions has not been called in the interim, which would post records on the same handle.

An application can call SQLGetDiagField to return any diagnostic field at any time, with the exception of SQL_DIAG_CURSOR_ROW_COUNT or SQL_DIAG_ROW_COUNT, which will return SQL_ERROR if Handle is not a statement handle. If any other diagnostic field is undefined, the call to SQLGetDiagField will return SQL_SUCCESS (provided no other diagnostic is encountered) and an undefined value is returned for the field.

For more information, see "Using SQLGetDiagRec and SQLGetDiagField" and "Implementing SQLGetDiagRec and SQLGetDiagField" in Chapter 15: Diagnostics.

HandleType Argument

Each handle type can have diagnostic information associated with it. The HandleType argument denotes the handle type of Handle.

Some header and record fields cannot be returned for environment, connection, statement, and descriptor handles. Those handles for which a field is not applicable are indicated in the "Header Fields" and "Record Fields" sections following.

If HandleType is SQL_HANDLE_ENV, Handle can be either a shared or unshared environment handle.

No driver-specific header diagnostic fields should be associated with an environment handle.

The only diagnostic header fields that are defined for a descriptor handle are SQL_DIAG_NUMBER and SQL_DIAG_RETURNCODE.

DiagIdentifier Argument

This argument indicates the identifier of the field required from the diagnostic data structure. If RecNumber is greater than or equal to 1, the data in the field describes the diagnostic information returned by a function. If RecNumber is 0, the field is in the header of the diagnostic data structure and therefore contains data pertaining to the function call that returned the diagnostic information, not to the specific information.

Drivers can define driver-specific header and record fields in the diagnostic data structure.

An ODBC 3.x application working with an ODBC 2.x driver will be able to call SQLGetDiagField only with a DiagIdentifier argument of SQL_DIAG_CLASS_ORIGIN, SQL_DIAG_CLASS_SUBCLASS_ORIGIN, SQL_DIAG_CONNECTION_NAME, SQL_DIAG_MESSAGE_TEXT, SQL_DIAG_NATIVE, SQL_DIAG_NUMBER, SQL_DIAG_RETURNCODE, SQL_DIAG_SERVER_NAME, or SQL_DIAG_SQLSTATE. All other diagnostic fields will return SQL_ERROR.

Header Fields

The header fields listed in the following table can be included in the DiagIdentifier argument.

DiagIdentifier | Return type | Returns
SQL_DIAG_CURSOR_ROW_COUNT | SQLINTEGER | This field contains the count of rows in the cursor. Its semantics depend upon the SQLGetInfo information types SQL_DYNAMIC_CURSOR_ATTRIBUTES2, SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2, SQL_KEYSET_CURSOR_ATTRIBUTES2, and SQL_STATIC_CURSOR_ATTRIBUTES2, which indicate which row counts are available for each cursor type (in the SQL_CA2_CRC_EXACT and SQL_CA2_CRC_APPROXIMATE bits). The contents of this field are defined only for statement handles and only after SQLExecute, SQLExecDirect, or SQLMoreResults has been called. Calling SQLGetDiagField with a DiagIdentifier of SQL_DIAG_CURSOR_ROW_COUNT on other than a statement handle will return SQL_ERROR.
SQL_DIAG_DYNAMIC_FUNCTION | SQLCHAR * | This is a string that describes the SQL statement that the underlying function executed. (See "Values of the Dynamic Function fields," later in this section, for specific values.) The contents of this field are defined only for statement handles and only after a call to SQLExecute, SQLExecDirect, or SQLMoreResults. Calling SQLGetDiagField with a DiagIdentifier of SQL_DIAG_DYNAMIC_FUNCTION on other than a statement handle will return SQL_ERROR. The value of this field is undefined before a call to SQLExecute or SQLExecDirect.
SQL_DIAG_DYNAMIC_FUNCTION_CODE | SQLINTEGER | This is a numeric code that describes the SQL statement that was executed by the underlying function. (See "Values of the Dynamic Function Fields," later in this section, for specific value.) The contents of this field are defined only for statement handles and only after a call to SQLExecute, SQLExecDirect, or SQLMoreResults. Calling SQLGetDiagField with a DiagIdentifier of SQL_DIAG_DYNAMIC_FUNCTION_CODE on other than a statement handle will return SQL_ERROR. The value of this field is undefined before a call to SQLExecute or SQLExecDirect.
SQL_DIAG_NUMBER | SQLINTEGER | The number of status records that are available for the specified handle.
SQL_DIAG_RETURNCODE | SQLRETURN | Return code returned by the function. For a list of return codes, see "Return Codes" in Chapter 15: Diagnostics. The driver does not have to implement SQL_DIAG_RETURNCODE; it is always implemented by the Driver Manager. If no function has yet been called on the Handle, SQL_SUCCESS will be returned for SQL_DIAG_RETURNCODE.
SQL_DIAG_ROW_COUNT | SQLINTEGER | The number of rows affected by an insert, delete, or update performed by SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos. It is driver-defined after a cursor specification has been executed. The contents of this field are defined only for statement handles. Calling SQLGetDiagField with a DiagIdentifier of SQL_DIAG_ROW_COUNT on other than a statement handle will return SQL_ERROR. The data in this field is also returned in the RowCountPtr argument of SQLRowCount. The data in this field is reset after every nondiagnostic function call, whereas the row count returned by SQLRowCount remains the same until the statement is set back to the prepared or allocated state.

Record Fields

The record fields listed in the following table can be included in the DiagIdentifier argument.

DiagIdentifier | Return type | Returns
SQL_DIAG_CLASS_ORIGIN | SQLCHAR * | A string that indicates the document that defines the class portion of the SQLSTATE value in this record. Its value is "ISO 9075" for all SQLSTATEs defined by X/Open and ISO call-level interface. For ODBC-specific SQLSTATEs (all those whose SQLSTATE class is "IM"), its value is "ODBC 3.0".
SQL_DIAG_COLUMN_NUMBER | SQLINTEGER | If the SQL_DIAG_ROW_NUMBER field is a valid row number in a rowset or a set of parameters, this field contains the value that represents the column number in the result set or the parameter number in the set of parameters. Result set column numbers always start at 1; if this status record pertains to a bookmark column, the field can be zero. Parameter numbers start at 1. It has the value SQL_NO_COLUMN_NUMBER if the status record is not associated with a column number or parameter number. If the driver cannot determine the column number or parameter number that this record is associated with, this field has the value SQL_COLUMN_NUMBER_UNKNOWN. The contents of this field are defined only for statement handles.
SQL_DIAG_CONNECTION_NAME | SQLCHAR * | A string that indicates the name of the connection that the diagnostic record relates to. This field is driver-defined. For diagnostic data structures associated with the environment handle and for diagnostics that do not relate to any connection, this field is a zero-length string.
SQL_DIAG_MESSAGE_TEXT | SQLCHAR * | An informational message on the error or warning. This field is formatted as described in the "Diagnostic Messages" section of Chapter 15: Diagnostics. There is no maximum length to the diagnostic message text.
SQL_DIAG_NATIVE | SQLINTEGER | A driver/data source–specific native error code. If there is no native error code, the driver returns 0.
SQL_DIAG_ROW_NUMBER | SQLINTEGER | This field contains the row number in the rowset, or the parameter number in the set of parameters, with which the status record is associated. Row numbers and parameter numbers start with 1. This field has the value SQL_NO_ROW_NUMBER if this status record is not associated with a row number or parameter number. If the driver cannot determine the row number or parameter number that this record is associated with, this field has the value SQL_ROW_NUMBER_UNKNOWN. The contents of this field are defined only for statement handles.
SQL_DIAG_SERVER_NAME | SQLCHAR * | A string that indicates the server name that the diagnostic record relates to. It is the same as the value returned for a call to SQLGetInfo with the SQL_DATA_SOURCE_NAME option. For diagnostic data structures associated with the environment handle and for diagnostics that do not relate to any server, this field is a zero-length string.
SQL_DIAG_SQLSTATE | SQLCHAR * | A five-character SQLSTATE diagnostic code. For more information, see "SQLSTATEs" in Chapter 15: Diagnostics.
SQL_DIAG_SUBCLASS_ORIGIN | SQLCHAR * | A string with the same format and valid values as SQL_DIAG_CLASS_ORIGIN, that identifies the defining portion of the subclass portion of the SQLSTATE code. The ODBC-specific SQLSTATES for which "ODBC 3.0" is returned include the following: 01S00, 01S01, 01S02, 01S06, 01S07, 07S01, 08S01, 21S01, 21S02, 25S01, 25S02, 25S03, 42S01, 42S02, 42S11, 42S12, 42S21, 42S22, HY095, HY097, HY098, HY099, HY100, HY101, HY105, HY107, HY109, HY110, HY111, HYT00, HYT01, IM001, IM002, IM003, IM004, IM005, IM006, IM007, IM008, IM010, IM011, IM012.

Values of the Dynamic Function Fields

The following table describes the values of SQL_DIAG_DYNAMIC_FUNCTION and SQL_DIAG_DYNAMIC_FUNCTION_CODE that apply to each type of SQL statement executed by a call to SQLExecute or SQLExecDirect. The driver can add driver-defined values to those listed.

SQL statement executed | Value of SQL_DIAG_DYNAMIC_FUNCTION | Value of SQL_DIAG_DYNAMIC_FUNCTION_CODE
alter-domain- statement | "ALTER DOMAIN" | SQL_DIAG_ALTER_DOMAIN
alter-table- statement | "ALTER TABLE" | SQL_DIAG_ALTER_TABLE
assertion- definition | "CREATE ASSERTION" | SQL_DIAG_CREATE_ASSERTION
character-set- definition | "CREATE CHARACTER SET" | SQL_DIAG_CREATE_CHARACTER_SET
collation- definition | "CREATE COLLATION" | SQL_DIAG_CREATE_COLLATION
create-index- statement | "CREATE INDEX" | SQL_DIAG_CREATE_INDEX
create-table- statement | "CREATE TABLE" | SQL_DIAG_CREATE_TABLE
create-view- statement | "CREATE VIEW" | SQL_DIAG_CREATE_VIEW
cursor- specification | "SELECT CURSOR" | SQL_DIAG_SELECT_CURSOR
delete- statement- positioned | "DYNAMIC DELETE CURSOR" | SQL_DIAG_DYNAMIC_DELETE_CURSOR
delete- statement- searched | "DELETE WHERE" | SQL_DIAG_DELETE_WHERE
domain- definition | "CREATE DOMAIN" | SQL_DIAG_CREATE_DOMAIN
drop-assertion- statement | "DROP ASSERTION" | SQL_DIAG_DROP_ASSERTION
drop-character- set-stmt | "DROP CHARACTER SET" | SQL_DIAG_DROP_CHARACTER_SET
drop-collation- statement | "DROP COLLATION" | SQL_DIAG_DROP_COLLATION
drop-domain- statement | "DROP DOMAIN" | SQL_DIAG_DROP_DOMAIN
drop-index- statement | "DROP INDEX" | SQL_DIAG_DROP_INDEX
drop-schema- statement | "DROP SCHEMA" | SQL_DIAG_DROP_SCHEMA
drop-table- statement | "DROP TABLE" | SQL_DIAG_DROP_TABLE
drop-translation- statement | "DROP TRANSLATION" | SQL_DIAG_DROP_TRANSLATION
drop-view- statement | "DROP VIEW" | SQL_DIAG_DROP_VIEW
grant-statement | "GRANT" | SQL_DIAG_GRANT
insert-statement | "INSERT" | SQL_DIAG_INSERT
ODBC- procedure- extension | "CALL" | SQL_DIAG_ CALL
revoke- statement | "REVOKE" | SQL_DIAG_REVOKE
schema- definition | "CREATE SCHEMA" | SQL_DIAG_CREATE_SCHEMA
translation- definition | "CREATE TRANSLATION" | SQL_DIAG_CREATE_TRANSLATION
update- statement- positioned | "DYNAMIC UPDATE CURSOR" | SQL_DIAG_DYNAMIC_UPDATE_CURSOR
update- statement- searched | "UPDATE WHERE" | SQL_DIAG_UPDATE_WHERE
Unknown | empty string | SQL_DIAG_UNKNOWN_STATEMENT

Sequence of Status Records

Status records are placed in a sequence based on row number and the type of the diagnostic. The Driver Manager determines the final order in which to return status records that it generates. The driver determines the final order in which to return status records that it generates.

If diagnostic records are posted by both the Driver Manager and the driver, the Driver Manager is responsible for ordering them.

If there are two or more status records, the sequence of the records is determined first by row number. The following rules apply to determining the sequence of diagnostic records by row:

  • Records that do not correspond to any row appear in front of records that correspond to a particular row, because SQL_NO_ROW_NUMBER is defined to be –1.
  • Records for which the row number is unknown appear in front of all other records, because SQL_ROW_NUMBER_UNKNOWN is defined to be –2.
  • For all records that pertain to specific rows, records are sorted by the value in the SQL_DIAG_ROW_NUMBER field. All errors and warnings of the first row affected are listed, then all errors and warnings of the next row affected, and so on.

Note The ODBC 3.x Driver Manager does not order status records in the diagnostic queue if SQLSTATE 01S01 (Error in row) is returned by an ODBC 2.x driver or if SQLSTATE 01S01 (Error in row) is returned by an ODBC 3.x driver when SQLExtendedFetch is called or SQLSetPos is called on a cursor that has been positioned with SQLExtendedFetch.

Within each row, or for all those records that do not correspond to a row or for which the row number is unknown, or for all those records with a row number equal to SQL_NO_ROW_NUMBER, the first record listed is determined using a set of sorting rules. After the first record, the order of the other records affecting a row is undefined. An application cannot assume that errors precede warnings after the first record. Applications should scan the entire diagnostic data structure to obtain complete information on an unsuccessful call to a function.

The following rules are used to determine the first record within a row. The record with the highest rank is the first record. The source of a record (Driver Manager, driver, gateway, and so on) is not considered when ranking records.

  • Errors. Status records that describe errors have the highest rank. The following rules are applied to sort errors:
    • Records that indicate a transaction failure or possible transaction failure outrank all other records.
    • If two or more records describe the same error condition, then SQLSTATEs defined by the X/Open CLI specification (classes 03 through HZ) outrank ODBC- and driver-defined SQLSTATEs.
  • Implementation-defined No Data values. Status records that describe driver-defined No Data values (class 02) have the second highest rank.
  • Warnings. Status records that describe warnings (class 01) have the lowest rank. If two or more records describe the same warning condition, then warning SQLSTATEs defined by the X/Open CLI specification outrank ODBC-defined and driver-defined SQLSTATEs.

Related Functions

For information about | See
Obtaining multiple fields of a diagnostic data structure | SQLGetDiagRec


SQLGetDiagRec

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLGetDiagRec returns the current values of multiple fields of a diagnostic record that contains error, warning, and status information. Unlike SQLGetDiagField, which returns one diagnostic field per call, SQLGetDiagRec returns several commonly used fields of a diagnostic record, including the SQLSTATE, the native error code, and the diagnostic message text.

Syntax

SQLRETURN SQLGetDiagRec(
     SQLSMALLINT     HandleType,
     SQLHANDLE     Handle,
     SQLSMALLINT     RecNumber,
     SQLCHAR *     Sqlstate,
     SQLINTEGER *     NativeErrorPtr,
     SQLCHAR *     MessageText,
     SQLSMALLINT     BufferLength,
     SQLSMALLINT *     TextLengthPtr);

Arguments

HandleType
[Input]
A handle type identifier that describes the type of handle for which diagnostics are required. Must be one of the following:

SQL_HANDLE_ENV
SQL_HANDLE_DBC
SQL_HANDLE_STMT
SQL_HANDLE_DESC

Handle
[Input]
A handle for the diagnostic data structure, of the type indicated by HandleType. If HandleType is SQL_HANDLE_ENV, Handle can be either a shared or an unshared environment handle.
RecNumber
[Input]
Indicates the status record from which the application seeks information. Status records are numbered from 1.
SQLState
[Output]
Pointer to a buffer in which to return a five-character SQLSTATE code pertaining to the diagnostic record RecNumber. The first two characters indicate the class; the next three indicate the subclass. This information is contained in the SQL_DIAG_SQLSTATE diagnostic field. For more information, see "SQLSTATEs" in Chapter 15: Diagnostics.
NativeErrorPtr
[Output]
Pointer to a buffer in which to return the native error code, specific to the data source. This information is contained in the SQL_DIAG_NATIVE diagnostic field.
MessageText
[Output]
Pointer to a buffer in which to return the diagnostic message text string. This information is contained in the SQL_DIAG_MESSAGE_TEXT diagnostic field. For the format of the string, see the "Diagnostic Messages" section of Chapter 15: Diagnostics.
BufferLength
[Input]
Length of the *MessageText buffer in characters. There is no maximum length of the diagnostic message text.
TextLengthPtr
[Output]
Pointer to a buffer in which to return the total number of bytes (excluding the number of bytes required for the null-termination character) available to return in *MessageText. If the number of bytes available to return is greater than BufferLength, the diagnostic message text in *MessageText is truncated to BufferLength minus the length of a null-termination character.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

SQLGetDiagRec does not post diagnostic records for itself. It uses the following return values to report the outcome of its own execution:

  • SQL_SUCCESS: The function successfully returned diagnostic information.
  • SQL_SUCCESS_WITH_INFO: The *MessageText buffer was too small to hold the requested diagnostic message. No diagnostic records were generated. To determine that a truncation occurred, the application must compare BufferLength to the actual number of bytes available, which is written to *StringLengthPtr.
  • SQL_INVALID_HANDLE: The handle indicated by HandleType and Handle was not a valid handle.
  • SQL_ERROR: One of the following occurred:
    • RecNumber was negative or 0.
    • BufferLength was less than zero.
  • SQL_NO_DATA: RecNumber was greater than the number of diagnostic records that existed for the handle specified in Handle. The function also returns SQL_NO_DATA for any positive RecNumber if there are no diagnostic records for Handle.

Comments

An application typically calls SQLGetDiagRec when a previous call to an ODBC function has returned SQL_SUCCESS or SQL_SUCCESS_WITH_INFO. However, because any ODBC function can post zero or more diagnostic records each time it is called, an application can call SQLGetDiagRec after any ODBC function call. An application can call SQLGetDiagRec multiple times to return some or all of the records in the diagnostic data structure. ODBC imposes no limit to the number of diagnostic records that can be stored at any one time.

SQLGetDiagRec cannot be used to return fields from the header of the diagnostic data structure. (The RecNumber argument must be greater than 0.) The application should call SQLGetDiagField for this purpose.

SQLGetDiagRec retrieves only the diagnostic information most recently associated with the handle specified in the Handle argument. If the application calls another ODBC function, except SQLGetDiagRec, SQLGetDiagField, or SQLError, any diagnostic information from the previous calls on the same handle is lost.

An application can scan all diagnostic records by looping, incrementing RecNumber, as long as SQLGetDiagRec returns SQL_SUCCESS. Calls to SQLGetDiagRec are nondestructive to the header and record fields. The application can call SQLGetDiagRec again at a later time to retrieve a field from a record as long as no other function, except SQLGetDiagRec, SQLGetDiagField, or SQLError, has been called in the interim. The application can also retrieve a count of the total number of diagnostic records available by calling SQLGetDiagField to retrieve the value of the SQL_DIAG_NUMBER field, and then calling SQLGetDiagRec that many times.

For a description of the fields of the diagnostic data structure, see SQLGetDiagField. For more information, see "Using SQLGetDiagRec and SQLGetDiagField" and "Implementing SQLGetDiagRec and SQLGetDiagField" in Chapter 15: Diagnostics.

HandleType Argument

Each handle type can have diagnostic information associated with it. The HandleType argument denotes the handle type of the Handle argument.

Some header and record fields cannot be returned for environment, connection, statement, and descriptor handles. Those handles for which a field is not applicable are indicated in the "Header Fields" and "Record Fields" sections in SQLGetDiagField.

A call to SQLGetDiagRec will return SQL_INVALID_HANDLE if HandleType is SQL_HANDLE_SENV, which denotes a shared environment handle. However, if HandleType is SQL_HANDLE_ENV, Handle can be either a shared or an unshared environment handle.

Related Functions

For information about | See
Obtaining a field of a diagnostic record or a field of the diagnostic header | SQLGetDiagField


SQLGetEnvAttr

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLGetEnvAttr returns the current setting of an environment attribute.

Syntax

SQLRETURN SQLGetEnvAttr(
     SQLHENV     EnvironmentHandle,
     SQLINTEGER     Attribute,
     SQLPOINTER     ValuePtr,
     SQLINTEGER     BufferLength,
     SQLINTEGER *     StringLengthPtr);

Arguments

EnvironmentHandle
[Input]
Environment handle.
Attribute
[Input]
Attribute to retrieve.
ValuePtr
[Output]
Pointer to a buffer in which to return the current value of the attribute specified by Attribute.
BufferLength
[Input]
If ValuePtr points to a character string, this argument should be the length of *ValuePtr. If *ValuePtr is an integer, BufferLength is ignored. If *ValuePtr is a Unicode string (when calling SQLGetEnvAttrW), the BufferLength argument must be an even number. If the attribute value is not a character string, BufferLength is unused.
StringLengthPtr
[Output]
A pointer to a buffer in which to return the total number of bytes (excluding the null-termination character) available to return in *ValuePtr. If ValuePtr is a null pointer, no length is returned. If the attribute value is a character string and the number of bytes available to return is greater than or equal to BufferLength, the data in *ValuePtr is truncated to BufferLength minus the length of a null-termination character and is null-terminated by the driver.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLGetEnvAttr returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_ENV and a Handle of EnvironmentHandle. The following table lists the SQLSTATE values commonly returned by SQLGetEnvAttr and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | The data returned in *ValuePtr was truncated to be BufferLength minus the null-termination character. The length of the untruncated string value is returned in *StringLengthPtr. (Function returns SQL_SUCCESS_WITH_INFO.)
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY092 | Invalid attribute/option identifier | The value specified for the argument Attribute was not valid for the version of ODBC supported by the driver.
HYC00 | Optional feature not implemented | The value specified for the argument Attribute was a valid ODBC environment attribute for the version of ODBC supported by the driver but was not supported by the driver.
IM001 | Driver does not support this function | (DM) The driver corresponding to the EnvironmentHandle does not support the function.

Comments

For a list of attributes, see SQLSetEnvAttr. There are no driver-specific environment attributes. If Attribute specifies an attribute that returns a string, ValuePtr must be a pointer to a buffer in which to return the string. The maximum length of the string, including the null-termination byte, will be BufferLength bytes.

SQLGetEnvAttr can be called at any time between the allocation and the freeing of an environment handle. All environment attributes successfully set by the application for the environment persist until SQLFreeHandle is called on the EnvironmentHandle with a HandleType of SQL_HANDLE_ENV. More than one environment handle can be allocated simultaneously in ODBC 3.x. An environment attribute on one environment is not affected when another environment has been allocated.

Note The SQL_ATTR_OUTPUT_NTS environment attribute is supported by standards-compliant applications. When SQLGetEnvAttr is called, the ODBC 3.x Driver Manager always returns SQL_TRUE for this attribute. SQL_ATTR_OUTPUT_NTS can be set to SQL_TRUE only by a call to SQLSetEnvAttr.

Related Functions

For information about | See
Returning the setting of a connection attribute | SQLGetConnectAttr
Returning the setting of a statement attribute | SQLGetStmtAttr
Setting a connection attribute | SQLSetConnectAttr
Setting an environment attribute | SQLSetEnvAttr
Setting a statement attribute | SQLSetStmtAttr


SQLGetFunctions

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLGetFunctions returns information about whether a driver supports a specific ODBC function. This function is implemented in the Driver Manager; it can also be implemented in drivers. If a driver implements SQLGetFunctions, the Driver Manager calls the function in the driver. Otherwise, it executes the function itself.

Syntax

SQLRETURN SQLGetFunctions(
     SQLHDBC     ConnectionHandle,
     SQLUSMALLINT     FunctionId,
     SQLUSMALLINT *     SupportedPtr);

Arguments

ConnectionHandle
[Input]
Connection handle.
FunctionId
[Input]
A #define value that identifies the ODBC function of interest; SQL_API_ODBC3_ALL_FUNCTIONS or SQL_API_ALL_FUNCTIONS. SQL_API_ODBC3_ALL_FUNCTIONS is used by an ODBC 3.x application to determine support of ODBC 3.x and earlier functions. SQL_API_ALL_FUNCTIONS is used by an ODBC 2.x application to determine support of ODBC 2.x and earlier functions.

For a list of #define values that identify ODBC functions, see the tables in "Comments."

SupportedPtr
[Output]
If FunctionId identifies a single ODBC function, SupportedPtr points to a single SQLUSMALLINT value that is SQL_TRUE if the specified function is supported by the driver, and SQL_FALSE if it is not supported.

If FunctionId is SQL_API_ODBC3_ALL_FUNCTIONS, SupportedPtr points to a SQLSMALLINT array with a number of elements equal to SQL_API_ODBC3_ALL_FUNCTIONS_SIZE. This array is treated by the Driver Manager as a 4,000-bit bitmap that can be used to determine whether an ODBC 3.x or earlier function is supported. The SQL_FUNC_EXISTS macro is called to determine function support. (See "Comments.") An ODBC 3.x application can call SQLGetFunctions with SQL_API_ODBC3_ALL_FUNCTIONS against either an ODBC 3.x or ODBC 2.x driver.

If FunctionId is SQL_API_ALL_FUNCTIONS, SupportedPtr points to an SQLUSMALLINT array of 100 elements. The array is indexed by #define values used by FunctionId to identify each ODBC function; some elements of the array are unused and reserved for future use. An element is SQL_TRUE if it identifies an ODBC 2.x or earlier function supported by the driver. It is SQL_FALSE if it identifies an ODBC function not supported by the driver or does not identify an ODBC function.

The arrays returned in *SupportedPtr use zero-based indexing.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLGetFunctions returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_DBC and a Handle of ConnectionHandle. The following table lists the SQLSTATE values commonly returned by SQLGetFunctions and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY010 | Function sequence error | (DM) SQLGetFunctions was called before SQLConnect, SQLBrowseConnect, or SQLDriverConnect. (DM) SQLBrowseConnect was called for the ConnectionHandle and returned SQL_NEED_DATA. This function was called before SQLBrowseConnect returned SQL_SUCCESS_WITH_INFO or SQL_SUCCESS.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY095 | Function type out of range | (DM) An invalid FunctionId value was specified.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.

Comments

SQLGetFunctions always returns that SQLGetFunctions, SQLDataSources, and SQLDrivers are supported. It does this because these functions are implemented in the Driver Manager. The Driver Manager will map an ANSI function to the corresponding Unicode function if the Unicode function exists and will map a Unicode function to the corresponding ANSI function if the ANSI function exists. For information about how applications use SQLGetFunctions, see "Interface Conformance Levels" in Chapter 4: ODBC Fundamentals.

The following is a list of valid values for FunctionId for functions that conform to the ISO 92 standards–compliance level:

SQL_API_SQLALLOCHANDLE | SQL_API_SQLGETDESCFIELD
SQL_API_SQLBINDCOL | SQL_API_SQLGETDESCREC
SQL_API_SQLCANCEL | SQL_API_SQLGETDIAGFIELD
SQL_API_SQLCLOSECURSOR | SQL_API_SQLGETDIAGREC
SQL_API_SQLCOLATTRIBUTE | SQL_API_SQLGETENVATTR
SQL_API_SQLCONNECT | SQL_API_SQLGETFUNCTIONS
SQL_API_SQLCOPYDESC | SQL_API_SQLGETINFO
SQL_API_SQLDATASOURCES | SQL_API_SQLGETSTMTATTR
SQL_API_SQLDESCRIBECOL | SQL_API_SQLGETTYPEINFO
SQL_API_SQLDISCONNECT | SQL_API_SQLNUMRESULTCOLS
SQL_API_SQLDRIVERS | SQL_API_SQLPARAMDATA
SQL_API_SQLENDTRAN | SQL_API_SQLPREPARE
SQL_API_SQLEXECDIRECT | SQL_API_SQLPUTDATA
SQL_API_SQLEXECUTE | SQL_API_SQLROWCOUNT
SQL_API_SQLFETCH | SQL_API_SQLSETCONNECTATTR
SQL_API_SQLFETCHSCROLL | SQL_API_SQLSETCURSORNAME
SQL_API_SQLFREEHANDLE | SQL_API_SQLSETDESCFIELD
SQL_API_SQLFREESTMT | SQL_API_SQLSETDESCREC
SQL_API_SQLGETCONNECTATTR | SQL_API_SQLSETENVATTR
SQL_API_SQLGETCURSORNAME | SQL_API_SQLSETSTMTATTR
SQL_API_SQLGETDATA |

The following is a list of valid values for FunctionId for functions conforming to the X/Open standards–compliance level:

SQL_API_SQLCOLUMNS | SQL_API_SQLSTATISTICS
SQL_API_SQLSPECIALCOLUMNS | SQL_API_SQLTABLES

The following is a list of valid values for FunctionId for functions conforming to the ODBC standards–compliance level.

SQL_API_SQLBINDPARAMETER | SQL_API_SQLNATIVESQL
SQL_API_SQLBROWSECONNECT | SQL_API_SQLNUMPARAMS
SQL_API_SQLBULKOPERATIONS[1] | SQL_API_SQLPRIMARYKEYS
SQL_API_SQLCOLUMNPRIVILEGES | SQL_API_SQLPROCEDURECOLUMNS
SQL_API_SQLDESCRIBEPARAM | SQL_API_SQLPROCEDURES
SQL_API_SQLDRIVERCONNECT | SQL_API_SQLSETPOS
SQL_API_SQLFOREIGNKEYS | SQL_API_SQLTABLEPRIVILEGES
SQL_API_SQLMORERESULTS |

[1] When working with an ODBC 2.x driver, SQLBulkOperations will be returned as supported only if both of the following are true: the ODBC 2.x driver supports SQLSetPos, and the information type SQL_POS_OPERATIONS returns the SQL_POS_ADD bit as set.

SQL_FUNC_EXISTS Macro

The SQL_FUNC_EXISTS(SupportedPtr, FunctionID) macro is used to determine support of ODBC 3.x or earlier functions after SQLGetFunctions has been called with a FunctionId argument of SQL_API_ODBC3_ALL_FUNCTIONS. The application calls SQL_FUNC_EXISTS with the SupportedPtr argument set to the SupportedPtr passed in SQLGetFunctions, and with the FunctionID argument set to the #define for the function. SQL_FUNC_EXISTS returns SQL_TRUE if the function is supported, and SQL_FALSE otherwise.

Note When working with an ODBC 2.x driver, the ODBC 3.x Driver Manager will return SQL_TRUE for SQLAllocHandle and SQLFreeHandle because SQLAllocHandle is mapped to SQLAllocEnv, SQLAllocConnect, or SQLAllocStmt, and because SQLFreeHandle is mapped to SQLFreeEnv, SQLFreeConnect, or SQLFreeStmt. SQLAllocHandle or SQLFreeHandle with a HandleType argument of SQL_HANDLE_DESC is not supported, however, even though SQL_TRUE is returned for the functions, because there is no ODBC 2.x function to map to in this case.

Code Example

The following two examples show how an application uses SQLGetFunctions to determine if a driver supports SQLTables, SQLColumns, and SQLStatistics. If the driver does not support these functions, the application disconnects from the driver. The first example calls SQLGetFunctions once for each function.

SQLUSMALLINT TablesExists, ColumnsExists, StatisticsExists;

SQLGetFunctions(hdbc, SQL_API_SQLTABLES, &TablesExists);
SQLGetFunctions(hdbc, SQL_API_SQLCOLUMNS, &ColumnsExists);
SQLGetFunctions(hdbc, SQL_API_SQLSTATISTICS, &StatisticsExists);

if (TablesExists && ColumnsExists && StatisticsExists) {

   /* Continue with application */

}

SQLDisconnect(hdbc);

The second example calls SQLGetFunctions a single time and passes it an array in which SQLGetFunctions returns information about all ODBC functions.

#define FUNCTIONS 100

SQLUSMALLINT fExists[FUNCTIONS];

SQLGetFunctions(hdbc, SQL_API_ALL_FUNCTIONS, fExists);

if (fExists[SQL_API_SQLTABLES] &&
   fExists[SQL_API_SQLCOLUMNS] &&
   fExists[SQL_API_SQLSTATISTICS]) {

   /* Continue with application */

}

SQLDisconnect(hdbc);

Related Functions

For information about | See
Returning the setting of a connection attribute | SQLGetConnectAttr
Returning information about a driver or data source | SQLGetInfo
Returning the setting of a statement attribute | SQLGetStmtAttr


SQLGetInfo

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLGetInfo returns general information about the driver and data source associated with a connection.

Syntax

SQLRETURN SQLGetInfo(
     SQLHDBC     ConnectionHandle,
     SQLUSMALLINT     InfoType,
     SQLPOINTER     InfoValuePtr,
     SQLSMALLINT     BufferLength,
     SQLSMALLINT *     StringLengthPtr);

Arguments

ConnectionHandle
[Input]
Connection handle.
InfoType
[Input]
Type of information.
InfoValuePtr
[Output]
Pointer to a buffer in which to return the information. Depending on the InfoType requested, the information returned will be one of the following: a null-terminated character string, an SQLUSMALLINT value, an SQLUINTEGER bitmask, an SQLUINTEGER flag, or a SQLUINTEGER binary value.

If the InfoType argument is SQL_DRIVER_HDESC or SQL_DRIVER_HSTMT, the InfoValuePtr argument is both input and output. (See the SQL_DRIVER_HDESC or SQL_DRIVER_HSTMT descriptors later in this function description for more information.)

BufferLength
[Input]
Length of the *InfoValuePtr buffer. If the value in *InfoValuePtr is not a character string or if InfoValuePtr is a null pointer, the BufferLength argument is ignored. The driver assumes that the size of *InfoValuePtr is SQLUSMALLINT or SQLUINTEGER, based on the InfoType. If *InfoValuePtr is a Unicode string (when calling SQLGetInfoW), the BufferLength argument must be an even number; if not, SQLSTATE HY090 (Invalid string or buffer length) is returned.
StringLengthPtr
[Output]
Pointer to a buffer in which to return the total number of bytes (excluding the null-termination character for character data) available to return in *InfoValuePtr.

For character data, if the number of bytes available to return is greater than or equal to BufferLength, the information in *InfoValuePtr is truncated to BufferLength bytes minus the length of a null-termination character and is null-terminated by the driver.

For all other types of data, the value of BufferLength is ignored and the driver assumes the size of *InfoValuePtr is SQLUSMALLINT or SQLUINTEGER, depending on the InfoType.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLGetInfo returns either SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_DBC and a Handle of ConnectionHandle. The following table lists the SQLSTATE values commonly returned by SQLGetInfo and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | The buffer *InfoValuePtr was not large enough to return all of the requested information, so the information was truncated. The length of the requested information in its untruncated form is returned in *StringLengthPtr. (Function returns SQL_SUCCESS_WITH_INFO.)
08003 | Connection does not exist | (DM) The type of information requested in InfoType requires an open connection. Of the information types reserved by ODBC, only SQL_ODBC_VER can be returned without an open connection.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY024 | Invalid attribute value | (DM) The InfoType argument was SQL_DRIVER_HSTMT, and the value pointed to by InfoValuePtr was not a valid statement handle. (DM) The InfoType argument was SQL_DRIVER_HDESC, and the value pointed to by InfoValuePtr was not a valid descriptor handle.
HY090 | Invalid string or buffer length | (DM) The value specified for argument BufferLength was less than 0. (DM) The value specified for BufferLength was an odd number, and *InfoValuePtr was of a Unicode data type.
HY096 | Information type out of range | The value specified for the argument InfoType was not valid for the version of ODBC supported by the driver.
HYC00 | Optional field not implemented | The value specified for the argument InfoType was a driver-specific value that is not supported by the driver.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver corresponding to the ConnectionHandle does not support the function.

Comments

The currently defined information types are shown in "Information Types," later in this section; it is expected that more will be defined to take advantage of different data sources. A range of information types is reserved by ODBC; driver developers must reserve values for their own driver-specific use from X/Open. SQLGetInfo performs no Unicode conversion or thunking (see Appendix A of the ODBC Programmer's Reference) for driver-defined InfoTypes. For more information, see "Driver-Specific Data Types, Descriptor Types, Information Types, Diagnostic Types, and Attributes" in Chapter 17: Programming Considerations. The format of the information returned in *InfoValuePtr depends on the InfoType requested. SQLGetInfo will return information in one of five different formats:

  • A null-terminated character string
  • An SQLUSMALLINT value
  • An SQLUINTEGER bitmask
  • An SQLUINTEGER value
  • A SQLUINTEGER binary value

The format of each of the following information types is noted in the type's description. The application must cast the value returned in *InfoValuePtr accordingly. For an example of how an application could retrieve data from a SQLUINTEGER bitmask, see "Code Example."

A driver must return a value for each of the information types defined in the tables below. If an information type does not apply to the driver or data source, the driver returns one of the values listed in the following table.

Format of *InfoValuePtr | Returned value
Character string ("Y" or "N") | "N"
Character string (not "Y" or "N") | Empty string
SQLUSMALLINT | 0
SQLUINTEGER bitmask or SQLUINTEGER binary value | 0L

For example, if a data source does not support procedures, SQLGetInfo returns the values listed in the following table for the values of InfoType that are related to procedures.

InfoType | Returned value
SQL_PROCEDURES | "N"
SQL_ACCESSIBLE_PROCEDURES | "N"
SQL_MAX_PROCEDURE_NAME_LEN | 0
SQL_PROCEDURE_TERM | Empty string

SQLGetInfo returns SQLSTATE HY096 (Invalid argument value) for values of InfoType that are in the range of information types reserved for use by ODBC but are not defined by the version of ODBC supported by the driver. To determine what version of ODBC a driver conforms to, an application calls SQLGetInfo with the SQL_DRIVER_ODBC_VER information type. SQLGetInfo returns SQLSTATE HYC00 (Optional feature not implemented) for values of InfoType that are in the range of information types reserved for driver-specific use but are not supported by the driver.

All calls to SQLGetInfo require an open connection, except when the InfoType is SQL_ODBC_VER, which returns the version of the Driver Manager.

Information Types

This section lists the information types supported by SQLGetInfo. Information types are grouped categorically and listed alphabetically. Information types that were added or renamed for ODBC 3.x are also listed.

Driver Information

The following values of the InfoType argument return information about the ODBC driver, such as the number of active statements, the data source name, and the interface standards compliance level:

SQL_ACTIVE_ENVIRONMENTS | SQL_GETDATA_EXTENSIONS
SQL_ASYNC_MODE | SQL_INFO_SCHEMA_VIEWS
SQL_BATCH_ROW_COUNT | SQL_KEYSET_CURSOR_ATTRIBUTES1
SQL_BATCH_SUPPORT | SQL_KEYSET_CURSOR_ATTRIBUTES2
SQL_DATA_SOURCE_NAME | SQL_MAX_ASYNC_CONCURRENT_STATEMENTS
SQL_DRIVER_HDBC | SQL_MAX_CONCURRENT_ACTIVITIES
SQL_DRIVER_HDESC | SQL_MAX_DRIVER_CONNECTIONS
SQL_DRIVER_HENV | SQL_ODBC_INTERFACE_CONFORMANCE
SQL_DRIVER_HLIB | SQL_ODBC_STANDARD_CLI_CONFORMANCE
SQL_DRIVER_HSTMT | SQL_ODBC_VER
SQL_DRIVER_NAME | SQL_PARAM_ARRAY_ROW_COUNTS
SQL_DRIVER_ODBC_VER | SQL_PARAM_ARRAY_SELECTS
SQL_DRIVER_VER | SQL_ROW_UPDATES
SQL_DYNAMIC_CURSOR_ATTRIBUTES1 | SQL_SEARCH_PATTERN_ESCAPE
SQL_DYNAMIC_CURSOR_ATTRIBUTES2 | SQL_SERVER_NAME
SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 | SQL_STATIC_CURSOR_ATTRIBUTES1
SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 | SQL_STATIC_CURSOR_ATTRIBUTES2
SQL_FILE_USAGE |

DBMS Product Information

The following values of the InfoType argument return information about the DBMS product, such as the DBMS name and version:

SQL_DATABASE_NAME
SQL_DBMS_NAME
SQL_DBMS_VER

Data Source Information

The following values of the InfoType argument return information about the data source, such as cursor characteristics and transaction capabilities:

SQL_ACCESSIBLE_PROCEDURES | SQL_MULT_RESULT_SETS
SQL_ACCESSIBLE_TABLES | SQL_MULTIPLE_ACTIVE_TXN
SQL_BOOKMARK_PERSISTENCE | SQL_NEED_LONG_DATA_LEN
SQL_CATALOG_TERM | SQL_NULL_COLLATION
SQL_COLLATION_SEQ | SQL_PROCEDURE_TERM
SQL_CONCAT_NULL_BEHAVIOR | SQL_SCHEMA_TERM
SQL_CURSOR_COMMIT_BEHAVIOR | SQL_SCROLL_OPTIONS
SQL_CURSOR_ROLLBACK_BEHAVIOR | SQL_TABLE_TERM
SQL_CURSOR_SENSITIVITY | SQL_TXN_CAPABLE
SQL_DATA_SOURCE_READ_ONLY | SQL_TXN_ISOLATION_OPTION
SQL_DEFAULT_TXN_ISOLATION | SQL_USER_NAME
SQL_DESCRIBE_PARAMETER |

Supported SQL

The following values of the InfoType argument return information about the SQL statements supported by the data source. The SQL syntax of each feature described by these information types is the SQL-92 syntax. These information types do not exhaustively describe the entire SQL-92 grammar. Instead, they describe those parts of the grammar for which data sources commonly offer different levels of support. Specifically, most of the DDL statements in SQL-92 are covered.

Applications should determine the general level of supported grammar from the SQL_SQL_CONFORMANCE information type and use the other information types to determine variations from the stated standards compliance level.

SQL_AGGREGATE_FUNCTIONS | SQL_DROP_TABLE
SQL_ALTER_DOMAIN | SQL_DROP_TRANSLATION
SQL_ALTER_SCHEMA | SQL_DROP_VIEW
SQL_ALTER_TABLE | SQL_EXPRESSIONS_IN_ORDERBY
SQL_ANSI_SQL_DATETIME_LITERALS | SQL_GROUP_BY
SQL_CATALOG_LOCATION | SQL_IDENTIFIER_CASE
SQL_CATALOG_NAME | SQL_IDENTIFIER_QUOTE_CHAR
SQL_CATALOG_NAME_SEPARATOR | SQL_INDEX_KEYWORDS
SQL_CATALOG_USAGE | SQL_INSERT_STATEMENT
SQL_COLUMN_ALIAS | SQL_INTEGRITY
SQL_CORRELATION_NAME | SQL_KEYWORDS
SQL_CREATE_ASSERTION | SQL_LIKE_ESCAPE_CLAUSE
SQL_CREATE_CHARACTER_SET | SQL_NON_NULLABLE_COLUMNS
SQL_CREATE_COLLATION | SQL_SQL_CONFORMANCE
SQL_CREATE_DOMAIN | SQL_OJ_CAPABILITIES
SQL_CREATE_SCHEMA | SQL_ORDER_BY_COLUMNS_IN_SELECT
SQL_CREATE_TABLE | SQL_OUTER_JOINS
SQL_CREATE_TRANSLATION | SQL_PROCEDURES
SQL_DDL_INDEX | SQL_QUOTED_IDENTIFIER_CASE
SQL_DROP_ASSERTION | SQL_SCHEMA_USAGE
SQL_DROP_CHARACTER_SET | SQL_SPECIAL_CHARACTERS
SQL_DROP_COLLATION | SQL_SUBQUERIES
SQL_DROP_DOMAIN | SQL_UNION
SQL_DROP_SCHEMA |

SQL Limits

The following values of the InfoType argument return information about the limits applied to identifiers and clauses in SQL statements, such as the maximum lengths of identifiers and the maximum number of columns in a select list. Limitations can be imposed by either the driver or the data source.

SQL_MAX_BINARY_LITERAL_LEN | SQL_MAX_IDENTIFIER_LEN
SQL_MAX_CATALOG_NAME_LEN | SQL_MAX_INDEX_SIZE
SQL_MAX_CHAR_LITERAL_LEN | SQL_MAX_PROCEDURE_NAME_LEN
SQL_MAX_COLUMN_NAME_LEN | SQL_MAX_ROW_SIZE
SQL_MAX_COLUMNS_IN_GROUP_BY | SQL_MAX_ROW_SIZE_INCLUDES_LONG
SQL_MAX_COLUMNS_IN_INDEX | SQL_MAX_SCHEMA_NAME_LEN
SQL_MAX_COLUMNS_IN_ORDER_BY | SQL_MAX_STATEMENT_LEN
SQL_MAX_COLUMNS_IN_SELECT | SQL_MAX_TABLE_NAME_LEN
SQL_MAX_COLUMNS_IN_TABLE | SQL_MAX_TABLES_IN_SELECT
SQL_MAX_CURSOR_NAME_LEN | SQL_MAX_USER_NAME_LEN

Scalar Function Information

The following values of the InfoType argument return information about the scalar functions supported by the data source and the driver. For more information about scalar functions, see Appendix E: Scalar Functions.

SQL_CONVERT_FUNCTIONS | SQL_TIMEDATE_ADD_INTERVALS
SQL_NUMERIC_FUNCTIONS | SQL_TIMEDATE_DIFF_INTERVALS
SQL_STRING_FUNCTIONS | SQL_TIMEDATE_FUNCTIONS
SQL_SYSTEM_FUNCTIONS |

Conversion Information

The following values of the InfoType argument return a list of the SQL data types to which the data source can convert the specified SQL data type with the CONVERT scalar function:

SQL_CONVERT_BIGINT | SQL_CONVERT_LONGVARBINARY
SQL_CONVERT_BINARY | SQL_CONVERT_LONGVARCHAR
SQL_CONVERT_BIT | SQL_CONVERT_NUMERIC
SQL_CONVERT_CHAR | SQL_CONVERT_REAL
SQL_CONVERT_DATE | SQL_CONVERT_SMALLINT
SQL_CONVERT_DECIMAL | SQL_CONVERT_TIME
SQL_CONVERT_DOUBLE | SQL_CONVERT_TIMESTAMP
SQL_CONVERT_FLOAT | SQL_CONVERT_TINYINT
SQL_CONVERT_INTEGER | SQL_CONVERT_VARBINARY
SQL_CONVERT_INTERVAL_YEAR_MONTH | SQL_CONVERT_VARCHAR
SQL_CONVERT_INTERVAL_DAY_TIME |

Information Types Added for ODBC 3.x

The following values of the InfoType argument have been added for ODBC 3.x:

SQL_ACTIVE_ENVIRONMENTS | SQL_DROP_ASSERTION
SQL_AGGREGATE_FUNCTIONS | SQL_DROP_CHARACTER_SET
SQL_ALTER_DOMAIN | SQL_DROP_COLLATION
SQL_ALTER_SCHEMA | SQL_DROP_DOMAIN
SQL_ANSI_SQL_DATETIME_LITERALS | SQL_DROP_SCHEMA
SQL_ASYNC_MODE | SQL_DROP_TABLE
SQL_BATCH_ROW_COUNT | SQL_DROP_TRANSLATION
SQL_BATCH_SUPPORT | SQL_DROP_VIEW
SQL_CATALOG_NAME | SQL_DYNAMIC_CURSOR_ATTRIBUTES1
SQL_COLLATION_SEQ | SQL_DYNAMIC_CURSOR_ATTRIBUTES2
SQL_CONVERT_INTERVAL_YEAR_MONTH | SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1
SQL_CONVERT_INTERVAL_DAY_TIME | SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2
SQL_CREATE_ASSERTION | SQL_INFO_SCHEMA_VIEWS
SQL_CREATE_CHARACTER_SET | SQL_INSERT_STATEMENT
SQL_CREATE_COLLATION | SQL_KEYSET_CURSOR_ATTRIBUTES1
SQL_CREATE_DOMAIN | SQL_KEYSET_CURSOR_ATTRIBUTES2
SQL_CREATE_SCHEMA | SQL_MAX_ASYNC_CONCURRENT_STATEMENTS
SQL_CREATE_TABLE | SQL_MAX_IDENTIFIER_LEN
SQL_CREATE_TRANSLATION | SQL_PARAM_ARRAY_ROW_COUNTS
SQL_CURSOR_SENSITIVITY | SQL_PARAM_ARRAY_SELECTS
SQL_DDL_INDEX | SQL_STATIC_CURSOR_ATTRIBUTES1
SQL_DESCRIBE_PARAMETER | SQL_STATIC_CURSOR_ATTRIBUTES2
SQL_DM_VER | SQL_XOPEN_CLI_YEAR
SQL_DRIVER_HDESC |

Information Types Renamed for ODBC 3.x

The following values of the InfoType argument have been renamed for ODBC 3.x.

ODBC 2.0 InfoType | ODBC 3.x InfoType
SQL_ACTIVE_CONNECTIONS | SQL_MAX_DRIVER_CONNECTIONS
SQL_ACTIVE_STATEMENTS | SQL_MAX_CONCURRENT_ACTIVITIES
SQL_MAX_OWNER_NAME_LEN | SQL_MAX_SCHEMA_NAME_LEN
SQL_MAX_QUALIFIER_NAME_LEN | SQL_MAX_CATALOG_NAME_LEN
SQL_ODBC_SQL_OPT_IEF | SQL_INTEGRITY
SQL_OWNER_TERM | SQL_SCHEMA_TERM
SQL_OWNER_USAGE | SQL_SCHEMA_USAGE
SQL_QUALIFIER_LOCATION | SQL_CATALOG_LOCATION
SQL_QUALIFIER_NAME_SEPARATOR | SQL_CATALOG_NAME_SEPARATOR
SQL_QUALIFIER_TERM | SQL_CATALOG_TERM
SQL_QUALIFIER_USAGE | SQL_CATALOG_USAGE

Information Types Deprecated in ODBC 3.x

The following values of the InfoType argument have been deprecated in ODBC 3.x. ODBC 3.x drivers must continue to support these information types for backward compatibility with ODBC 2.x applications. (For more information on these types, see "SQLGetInfo Support" in Appendix G: Driver Guidelines for Backward Compatibility.)

SQL_FETCH_DIRECTION | SQL_POS_OPERATIONS
SQL_LOCK_TYPES | SQL_POSITIONED_STATEMENTS
SQL_ODBC_API_CONFORMANCE | SQL_SCROLL_CONCURRENCY
SQL_ODBC_SQL_CONFORMANCE | SQL_STATIC_SENSITIVITY

Information Type Descriptions

The following table alphabetically lists each information type, the version of ODBC in which it was introduced, and its description.

InfoType Returns
SQL_ACCESSIBLE_PROCEDURES
(ODBC 1.0) 		A character string: "Y" if the user can execute all procedures returned by SQLProcedures; "N" if there may be procedures returned that the user cannot execute.
SQL_ACCESSIBLE_TABLES
(ODBC 1.0) 		A character string: "Y" if the user is guaranteed SELECT privileges to all tables returned by SQLTables; "N" if there may be tables returned that the user cannot access.
SQL_ACTIVE_ENVIRONMENTS
(ODBC 3.0) 		An SQLUSMALLINT value specifying the maximum number of active environments that the driver can support. If there is no specified limit or the limit is unknown, this value is set to zero.
SQL_AGGREGATE_FUNCTIONS
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating support for aggregation functions:

SQL_AF_ALL
SQL_AF_AVG
SQL_AF_COUNT
SQL_AF_DISTINCT
SQL_AF_MAX
SQL_AF_MIN
SQL_AF_SUM

An SQL-92 Entry level–conformant driver will always return all of these options as supported.
SQL_ALTER_DOMAIN
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the ALTER DOMAIN statement, as defined in SQL-92, supported by the data source. An SQL-92 Full level–compliant driver will always return all of the bitmasks. A return value of "0" means that the ALTER DOMAIN statement is not supported.

The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.

The following bitmasks are used to determine which clauses are supported:

SQL_AD_ADD_DOMAIN_CONSTRAINT = Adding a domain constraint is supported (Full level)

SQL_AD_ADD_DOMAIN_DEFAULT = <alter domain> <set domain default clause> is supported (Full level)

SQL_AD_CONSTRAINT_NAME_DEFINITION = <constraint name definition clause> is supported for naming domain constraint (Intermediate level)

SQL_AD_DROP_DOMAIN_CONSTRAINT = <drop domain constraint clause> is supported (Full level)

SQL_AD_DROP_DOMAIN_DEFAULT = <alter domain> <drop domain default clause> is supported (Full level)

The following bits specify the supported <constraint attributes> if <add domain constraint> is supported (the SQL_AD_ADD_DOMAIN_CONSTRAINT bit is set):

SQL_AD_ADD_CONSTRAINT_DEFERRABLE (Full level)
SQL_AD_ADD_CONSTRAINT_NON_DEFERRABLE (Full level)
SQL_AD_ADD_CONSTRAINT_INITIALLY_DEFERRED (Full level)
SQL_AD_ADD_CONSTRAINT_INITIALLY_IMMEDIATE (Full level)
SQL_ALTER_TABLE
(ODBC 2.0) 		An SQLUINTEGER bitmask enumerating the clauses in the ALTER TABLE statement supported by the data source.

The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.

The following bitmasks are used to determine which clauses are supported:

SQL_AT_ADD_COLUMN_COLLATION = <add column> clause is supported, with facility to specify column collation (Full level) (ODBC 3.0)

SQL_AT_ADD_COLUMN_DEFAULT = <add column> clause is supported, with facility to specify column defaults (FIPS Transitional level) (ODBC 3.0)

SQL_AT_ADD_COLUMN_SINGLE = <add column> is supported (FIPS Transitional level) (ODBC 3.0)

SQL_AT_ADD_CONSTRAINT = <add column> clause is supported, with facility to specify column constraints (FIPS Transitional level) (ODBC 3.0)

SQL_AT_ADD_TABLE_CONSTRAINT = <add table constraint> clause is supported (FIPS Transitional level) (ODBC 3.0)

SQL_AT_CONSTRAINT_NAME_DEFINITION = <constraint name definition> is supported for naming column and table constraints (Intermediate level) (ODBC 3.0)

SQL_AT_DROP_COLUMN_CASCADE = <drop column> CASCADE is supported (FIPS Transitional level) (ODBC 3.0)

SQL_AT_DROP_COLUMN_DEFAULT = <alter column> <drop column default clause> is supported (Intermediate level) (ODBC 3.0)

SQL_AT_DROP_COLUMN_RESTRICT = <drop column> RESTRICT is supported (FIPS Transitional level) (ODBC 3.0)

SQL_AT_DROP_TABLE_CONSTRAINT_CASCADE (ODBC 3.0)

SQL_AT_DROP_TABLE_CONSTRAINT_RESTRICT = <drop column> RESTRICT is supported (FIPS Transitional level) (ODBC 3.0)

SQL_AT_SET_COLUMN_DEFAULT = <alter column> <set column default clause> is supported (Intermediate level) (ODBC 3.0)

The following bits specify the support <constraint attributes> if specifying column or table constraints is supported (the SQL_AT_ADD_CONSTRAINT bit is set):

SQL_AT_CONSTRAINT_INITIALLY_DEFERRED (Full level) (ODBC 3.0)
SQL_AT_CONSTRAINT_INITIALLY_IMMEDIATE (Full level) (ODBC 3.0)
SQL_AT_CONSTRAINT_DEFERRABLE (Full level) (ODBC 3.0)
SQL_AT_CONSTRAINT_NON_DEFERRABLE (Full level) (ODBC 3.0)
SQL_ASYNC_MODE
(ODBC 3.0) 		An SQLUINTEGER value indicating the level of asynchronous support in the driver:

SQL_AM_CONNECTION = Connection level asynchronous execution is supported. Either all statement handles associated with a given connection handle are in asynchronous mode or all are in synchronous mode. A statement handle on a connection cannot be in asynchronous mode while another statement handle on the same connection is in synchronous mode, and vice versa.

SQL_AM_STATEMENT = Statement level asynchronous execution is supported. Some statement handles associated with a connection handle can be in asynchronous mode, while other statement handles on the same connection are in synchronous mode.

SQL_AM_NONE = Asynchronous mode is not supported.
SQL_BATCH_ROW_COUNT
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the behavior of the driver with respect to the availability of row counts. The following bitmasks are used in conjunction with the information type:

SQL_BRC_ROLLED_UP = Row counts for consecutive INSERT, DELETE, or UPDATE statements are rolled up into one. If this bit is not set, then row counts are available for each individual statement.

SQL_BRC_PROCEDURES = Row counts, if any, are available when a batch is executed in a stored procedure. If row counts are available, they can be rolled up or individually available, depending on the SQL_BRC_ROLLED_UP bit.

SQL_BRC_EXPLICIT = Row counts, if any, are available when a batch is executed directly by calling SQLExecute or SQLExecDirect. If row counts are available, they can be rolled up or individually available, depending on the SQL_BRC_ROLLED_UP bit.
SQL_BATCH_SUPPORT
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the driver's support for batches. The following bitmasks are used to determine which level is supported:

SQL_BS_SELECT_EXPLICIT = The driver supports explicit batches that can have result-set generating statements.

SQL_BS_ROW_COUNT_EXPLICIT = The driver supports explicit batches that can have row-count generating statements.

SQL_BS_SELECT_PROC = The driver supports explicit procedures that can have result-set generating statements.

SQL_BS_ROW_COUNT_PROC = The driver supports explicit procedures that can have row-count generating statements.
SQL_BOOKMARK_PERSISTENCE
(ODBC 2.0) 		An SQLUINTEGER bitmask enumerating the operations through which bookmarks persist.

The following bitmasks are used in conjunction with the flag to determine through which options bookmarks persist:

SQL_BP_CLOSE = Bookmarks are valid after an application calls SQLFreeStmt with the SQL_CLOSE option, or SQLCloseCursor to close the cursor associated with a statement.

SQL_BP_DELETE = The bookmark for a row is valid after that row has been deleted.

SQL_BP_DROP = Bookmarks are valid after an application calls SQLFreeHandle with a HandleType of SQL_HANDLE_STMT to drop a statement.

SQL_BP_TRANSACTION = Bookmarks are valid after an application commits or rolls back a transaction.

SQL_BP_UPDATE = The bookmark for a row is valid after any column in that row has been updated, including key columns.

SQL_BP_OTHER_HSTMT = A bookmark associated with one statement can be used with another statement. Unless SQL_BP_CLOSE or SQL_BP_DROP is specified, the cursor on the first statement must be open.
SQL_CATALOG_LOCATION
(ODBC 2.0) 		An SQLUSMALLINT value indicating the position of the catalog in a qualified table name:

SQL_CL_START
SQL_CL_END

For example, an Xbase driver returns SQL_CL_START because the directory (catalog) name is at the start of the table name, as in \EMPDATA\EMP.DBF. An ORACLE Server driver returns SQL_CL_END because the catalog is at the end of the table name, as in ADMIN.EMP@EMPDATA.

An SQL-92 Full level–conformant driver will always return SQL_CL_START. A value of 0 is returned if catalogs are not supported by the data source. To find out whether catalogs are supported, an application calls SQLGetInfo with the SQL_CATALOG_NAME information type.

This InfoType has been renamed for ODBC 3.0 from the ODBC 2.0 InfoType SQL_QUALIFIER_LOCATION.
SQL_CATALOG_NAME
(ODBC 3.0) 		A character string: "Y" if the server supports catalog names, or "N" if it does not.

An SQL-92 Full level–conformant driver will always return "Y".
SQL_CATALOG_NAME_SEPARATOR
(ODBC 1.0) 		A character string: the character or characters that the data source defines as the separator between a catalog name and the qualified name element that follows or precedes it.

An empty string is returned if catalogs are not supported by the data source. To find out whether catalogs are supported, an application calls SQLGetInfo with the SQL_CATALOG_NAME information type. An SQL-92 Full level–conformant driver will always return ".".

This InfoType has been renamed for ODBC 3.0 from the ODBC 2.0 InfoType SQL_QUALIFIER_NAME_SEPARATOR.
SQL_CATALOG_TERM
(ODBC 1.0) 		A character string with the data source vendor's name for a catalog; for example, "database" or "directory". This string can be in upper, lower, or mixed case.

An empty string is returned if catalogs are not supported by the data source. To find out whether catalogs are supported, an application calls SQLGetInfo with the SQL_CATALOG_NAME information type. An SQL-92 Full level–conformant driver will always return "catalog".

This InfoType has been renamed for ODBC 3.0 from the ODBC 2.0 InfoType SQL_QUALIFIER_TERM.
SQL_CATALOG_USAGE
(ODBC 2.0) 		An SQLUINTEGER bitmask enumerating the statements in which catalogs can be used.

The following bitmasks are used to determine where catalogs can be used:

SQL_CU_DML_STATEMENTS = Catalogs are supported in all Data Manipulation Language statements: SELECT, INSERT, UPDATE, DELETE, and if supported, SELECT FOR UPDATE and positioned update and delete statements.

SQL_CU_PROCEDURE_INVOCATION = Catalogs are supported in the ODBC procedure invocation statement.

SQL_CU_TABLE_DEFINITION = Catalogs are supported in all table definition statements: CREATE TABLE, CREATE VIEW, ALTER TABLE, DROP TABLE, and DROP VIEW.

SQL_CU_INDEX_DEFINITION = Catalogs are supported in all index definition statements: CREATE INDEX and DROP INDEX.

SQL_CU_PRIVILEGE_DEFINITION = Catalogs are supported in all privilege definition statements: GRANT and REVOKE.

A value of 0 is returned if catalogs are not supported by the data source. To find out whether catalogs are supported, an application calls SQLGetInfo with the SQL_CATALOG_NAME information type. An SQL-92 Full level–conformant driver will always return a bitmask with all of these bits set.

This InfoType has been renamed for ODBC 3.0 from the ODBC 2.0 InfoType SQL_QUALIFIER_USAGE.
SQL_COLLATION_SEQ
(ODBC 3.0) 		The name of the collation sequence. This is a character string that indicates the name of the default collation for the default character set for this server (for example, 'ISO 8859-1' or EBCDIC). If this is unknown, an empty string will be returned. An SQL-92 Full level–conformant driver will always return a non-empty string.
SQL_COLUMN_ALIAS
(ODBC 2.0) 		A character string: "Y" if the data source supports column aliases; otherwise, "N".

A column alias is an alternate name that can be specified for a column in the select list by using an AS clause. An SQL-92 Entry level–conformant driver will always return "Y".
SQL_CONCAT_NULL_BEHAVIOR
(ODBC 1.0) 		An SQLUSMALLINT value indicating how the data source handles the concatenation of NULL valued character data type columns with non-NULL valued character data type columns:

SQL_CB_NULL = Result is NULL valued.

SQL_CB_NON_NULL = Result is concatenation of non-NULL valued column or columns.

An SQL-92 Entry level–conformant driver will always return SQL_CB_NULL.
SQL_CONVERT_BIGINT
SQL_CONVERT_BINARY
SQL_CONVERT_BIT
SQL_CONVERT_CHAR
SQL_CONVERT_GUID
SQL_CONVERT_DATE
SQL_CONVERT_DECIMAL
SQL_CONVERT_DOUBLE
SQL_CONVERT_FLOAT
SQL_CONVERT_INTEGER
SQL_CONVERT_INTERVAL_YEAR_MONTH
SQL_CONVERT_INTERVAL_DAY_TIME
SQL_CONVERT_LONGVARBINARY
SQL_CONVERT_LONGVARCHAR
SQL_CONVERT_NUMERIC
SQL_CONVERT_REAL
SQL_CONVERT_SMALLINT
SQL_CONVERT_TIME
SQL_CONVERT_TIMESTAMP
SQL_CONVERT_TINYINT
SQL_CONVERT_VARBINARY
SQL_CONVERT_VARCHAR
(ODBC 1.0) 		An SQLUINTEGER bitmask. The bitmask indicates the conversions supported by the data source with the CONVERT scalar function for data of the type named in the InfoType. If the bitmask equals zero, the data source does not support any conversions from data of the named type, including conversion to the same data type.

For example, to find out if a data source supports the conversion of SQL_INTEGER data to the SQL_BIGINT data type, an application calls SQLGetInfo with the InfoType of SQL_CONVERT_INTEGER. The application performs an AND operation with the returned bitmask and SQL_CVT_BIGINT. If the resulting value is nonzero, the conversion is supported.

The following bitmasks are used to determine which conversions are supported:

SQL_CVT_BIGINT (ODBC 1.0)
SQL_CVT_BINARY (ODBC 1.0)
SQL_CVT_BIT (ODBC 1.0)
SQL_CVT_GUID (ODBC 3.5)
SQL_CVT_CHAR (ODBC 1.0)
SQL_CVT_DATE (ODBC 1.0)
SQL_CVT_DECIMAL (ODBC 1.0)
SQL_CVT_DOUBLE (ODBC 1.0)
SQL_CVT_FLOAT (ODBC 1.0)
SQL_CVT_INTEGER (ODBC 1.0)
SQL_CVT_INTERVAL_YEAR_MONTH (ODBC 3.0)
SQL_CVT_INTERVAL_DAY_TIME (ODBC 3.0)
SQL_CVT_LONGVARBINARY (ODBC 1.0)
SQL_CVT_LONGVARCHAR (ODBC 1.0)
SQL_CVT_NUMERIC (ODBC 1.0)
SQL_CVT_REAL ODBC 1.0)
SQL_CVT_SMALLINT (ODBC 1.0)
SQL_CVT_TIME (ODBC 1.0)
SQL_CVT_TIMESTAMP (ODBC 1.0)
SQL_CVT_TINYINT (ODBC 1.0)
SQL_CVT_VARBINARY (ODBC 1.0)
SQL_CVT_VARCHAR (ODBC 1.0)
SQL_CONVERT_FUNCTIONS
(ODBC 1.0) 		An SQLUINTEGER bitmask enumerating the scalar conversion functions supported by the driver and associated data source.

The following bitmask is used to determine which conversion functions are supported:

SQL_FN_CVT_CAST
SQL_FN_CVT_CONVERT
SQL_CORRELATION_NAME
(ODBC 1.0) 		An SQLUSMALLINT value indicating whether table correlation names are supported:

SQL_CN_NONE = Correlation names are not supported.

SQL_CN_DIFFERENT = Correlation names are supported but must differ from the names of the tables they represent.

SQL_CN_ANY = Correlation names are supported and can be any valid user-defined name.

An SQL-92 Entry level–conformant driver will always return SQL_CN_ANY.
SQL_CREATE_ASSERTION
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the CREATE ASSERTION statement, as defined in SQL-92, supported by the data source.

The following bitmasks are used to determine which clauses are supported:

SQL_CA_CREATE_ASSERTION

The following bits specify the supported constraint attribute if the ability to specify constraint attributes explicitly is supported (see the SQL_ALTER_TABLE and SQL_CREATE_TABLE information types):

SQL_CA_CONSTRAINT_INITIALLY_DEFERRED
SQL_CA_CONSTRAINT_INITIALLY_IMMEDIATE
SQL_CA_CONSTRAINT_DEFERRABLE
SQL_CA_CONSTRAINT_NON_DEFERRABLE

An SQL-92 Full level–conformant driver will always return all of these options as supported. A return value of "0" means that the CREATE ASSERTION statement is not supported.
SQL_CREATE_CHARACTER_SET
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the CREATE CHARACTER SET statement, as defined in SQL-92, supported by the data source.

The following bitmasks are used to determine which clauses are supported:

SQL_CCS_CREATE_CHARACTER_SET
SQL_CCS_COLLATE_CLAUSE
SQL_CCS_LIMITED_COLLATION

An SQL-92 Full level–conformant driver will always return all of these options as supported. A return value of "0" means that the CREATE CHARACTER SET statement is not supported.
SQL_CREATE_COLLATION
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the CREATE COLLATION statement, as defined in SQL-92, supported by the data source.

The following bitmask is used to determine which clauses are supported:

SQL_CCOL_CREATE_COLLATION

An SQL-92 Full level–conformant driver will always return this option as supported. A return value of "0" means that the CREATE COLLATION statement is not supported.
SQL_CREATE_DOMAIN
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the CREATE DOMAIN statement, as defined in SQL-92, supported by the data source.

The following bitmasks are used to determine which clauses are supported:

SQL_CDO_CREATE_DOMAIN = The CREATE DOMAIN statement is supported (Intermediate level).

SQL_CDO_CONSTRAINT_NAME_DEFINITION = <constraint name definition> is supported for naming domain constraints (Intermediate level).

The following bits specify the ability to create column constraints:
SQL_CDO_DEFAULT = Specifying domain constraints is supported (Intermediate level)
SQL_CDO_CONSTRAINT = Specifying domain defaults is supported (Intermediate level)
SQL_CDO_COLLATION = Specifying domain collation is supported (Full level)

The following bits specify the supported constraint attributes if specifying domain constraints is supported (SQL_CDO_DEFAULT is set):

SQL_CDO_CONSTRAINT_INITIALLY_DEFERRED (Full level)
SQL_CDO_CONSTRAINT_INITIALLY_IMMEDIATE (Full level)
SQL_CDO_CONSTRAINT_DEFERRABLE (Full level)
SQL_CDO_CONSTRAINT_NON_DEFERRABLE (Full level)

A return value of "0" means that the CREATE DOMAIN statement is not supported.
SQL_CREATE_SCHEMA
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the CREATE SCHEMA statement, as defined in SQL-92, supported by the data source.

The following bitmasks are used to determine which clauses are supported:

SQL_CS_CREATE_SCHEMA
SQL_CS_AUTHORIZATION
SQL_CS_DEFAULT_CHARACTER_SET

An SQL-92 Intermediate level–conformant driver will always return the SQL_CS_CREATE_SCHEMA and SQL_CS_AUTHORIZATION options as supported. These must also be supported at the SQL-92 Entry level, but not necessarily as SQL statements. An SQL-92 Full level–conformant driver will always return all of these options as supported.
SQL_CREATE_TABLE
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the CREATE TABLE statement, as defined in SQL-92, supported by the data source.

The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.

The following bitmasks are used to determine which clauses are supported:

SQL_CT_CREATE_TABLE = The CREATE TABLE statement is supported. (Entry level)

SQL_CT_TABLE_CONSTRAINT = Specifying table constraints is supported (FIPS Transitional level)

SQL_CT_CONSTRAINT_NAME_DEFINITION = The <constraint name definition> clause is supported for naming column and table constraints (Intermediate level)

The following bits specify the ability to create temporary tables:

SQL_CT_COMMIT_PRESERVE = Deleted rows are preserved on commit. (Full level)
SQL_CT_COMMIT_DELETE = Deleted rows are deleted on commit. (Full level)
SQL_CT_GLOBAL_TEMPORARY = Global temporary tables can be created. (Full level)
SQL_CT_LOCAL_TEMPORARY = Local temporary tables can be created. (Full level)

The following bits specify the ability to create column constraints:

SQL_CT_COLUMN_CONSTRAINT = Specifying column constraints is supported (FIPS Transitional level)
SQL_CT_COLUMN_DEFAULT = Specifying column defaults is supported (FIPS Transitional level)
SQL_CT_COLUMN_COLLATION = Specifying column collation is supported (Full level)

The following bits specify the supported constraint attributes if specifying column or table constraints is supported:

SQL_CT_CONSTRAINT_INITIALLY_DEFERRED (Full level)
SQL_CT_CONSTRAINT_INITIALLY_IMMEDIATE (Full level)
SQL_CT_CONSTRAINT_DEFERRABLE (Full level)
SQL_CT_CONSTRAINT_NON_DEFERRABLE (Full level)
SQL_CREATE_TRANSLATION
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the CREATE TRANSLATION statement, as defined in SQL-92, supported by the data source.

The following bitmask is used to determine which clauses are supported:

SQL_CTR_CREATE_TRANSLATION

An SQL-92 Full level–conformant driver will always return these options as supported. A return value of "0" means that the CREATE TRANSLATION statement is not supported.
SQL_CREATE_VIEW
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the CREATE VIEW statement, as defined in SQL-92, supported by the data source.

The following bitmasks are used to determine which clauses are supported:

SQL_CV_CREATE_VIEW
SQL_CV_CHECK_OPTION
SQL_CV_CASCADED
SQL_CV_LOCAL

A return value of "0" means that the CREATE VIEW statement is not supported.

An SQL-92 Entry level–conformant driver will always return the SQL_CV_CREATE_VIEW and SQL_CV_CHECK_OPTION options as supported.

An SQL-92 Full level–conformant driver will always return all of these options as supported.
SQL_CURSOR_COMMIT_BEHAVIOR
(ODBC 1.0) 		An SQLUSMALLINT value indicating how a COMMIT operation affects cursors and prepared statements in the data source:

SQL_CB_DELETE = Close cursors and delete prepared statements. To use the cursor again, the application must reprepare and reexecute the statement.

SQL_CB_CLOSE = Close cursors. For prepared statements, the application can call SQLExecute on the statement without calling SQLPrepare again.

SQL_CB_PRESERVE = Preserve cursors in the same position as before the COMMIT operation. The application can continue to fetch data, or it can close the cursor and reexecute the statement without repreparing it.
SQL_CURSOR_ROLLBACK_BEHAVIOR
(ODBC 1.0) 		An SQLUSMALLINT value indicating how a ROLLBACK operation affects cursors and prepared statements in the data source:

SQL_CB_DELETE = Close cursors and delete prepared statements. To use the cursor again, the application must reprepare and reexecute the statement.

SQL_CB_CLOSE = Close cursors. For prepared statements, the application can call SQLExecute on the statement without calling SQLPrepare again.

SQL_CB_PRESERVE = Preserve cursors in the same position as before the ROLLBACK operation. The application can continue to fetch data, or it can close the cursor and reexecute the statement without repreparing it.
SQL_CURSOR_ROLLBACK_SQL_CURSOR_SENSITIVITY
(ODBC 3.0) 		An SQLUINTEGER value indicating the support for cursor sensitivity:

SQL_INSENSITIVE = All cursors on the statement handle show the result set without reflecting any changes made to it by any other cursor within the same transaction.

SQL_UNSPECIFIED = It is unspecified whether cursors on the statement handle make visible the changes made to a result set by another cursor within the same transaction. Cursors on the statement handle may make visible none, some, or all such changes.

SQL_SENSITIVE = Cursors are sensitive to changes made by other cursors within the same transaction.

An SQL-92 Entry level–conformant driver will always return the SQL_UNSPECIFIED option as supported.

An SQL-92 Full level–conformant driver will always return the SQL_INSENSITIVE option as supported.
SQL_DATA_SOURCE_NAME
(ODBC 1.0) 		A character string with the data source name used during connection. If the application called SQLConnect, this is the value of the szDSN argument. If the application called SQLDriverConnect or SQLBrowseConnect, this is the value of the DSN keyword in the connection string passed to the driver. If the connection string did not contain the DSN keyword (such as when it contains the DRIVER keyword), this is an empty string.
SQL_DATA_SOURCE_READ_ONLY
(ODBC 1.0) 		A character string. "Y" if the data source is set to READ ONLY mode, "N" if it is otherwise.

This characteristic pertains only to the data source itself; it is not a characteristic of the driver that enables access to the data source. A driver that is read/write can be used with a data source that is read-only. If a driver is read-only, all of its data sources must be read-only and must return SQL_DATA_SOURCE_READ_ONLY.
SQL_DATABASE_NAME
(ODBC 1.0) 		A character string with the name of the current database in use, if the data source defines a named object called "database".

Note In ODBC 3.x, the value returned for this InfoType can also be returned by calling SQLGetConnectAttr with an Attribute argument of SQL_ATTR_CURRENT_CATALOG.
SQL_DATETIME_LITERALS
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the SQL-92 datetime literals supported by the data source. Note that these are the datetime literals listed in the SQL-92 specification and are separate from the datetime literal escape clauses defined by ODBC. For more information about the ODBC datetime literal escape clauses, see "Date, Time, Timestamp, and Datetime Interval Literals" in Chapter 8: SQL Statements.

A FIPS Transitional level–conformant driver will always return the "1" value in the bitmask for the bits listed below. A value of "0" means that SQL-92 datetime literals are not supported.

The following bitmasks are used to determine which literals are supported:

SQL_DL_SQL92_DATE
SQL_DL_SQL92_TIME
SQL_DL_SQL92_TIMESTAMP
SQL_DL_SQL92_INTERVAL_YEAR
SQL_DL_SQL92_INTERVAL_MONTH
SQL_DL_SQL92_INTERVAL_DAY
SQL_DL_SQL92_INTERVAL_HOUR
SQL_DL_SQL92_INTERVAL_MINUTE
SQL_DL_SQL92_INTERVAL_SECOND
SQL_DL_SQL92_INTERVAL_YEAR_TO_MONTH
SQL_DL_SQL92_INTERVAL_DAY_TO_HOUR

SQL_DL_SQL92_INTERVAL_DAY_TO_MINUTE
SQL_DL_SQL92_INTERVAL_DAY_TO_SECOND
SQL_DL_SQL92_INTERVAL_HOUR_TO_MINUTE
SQL_DL_SQL92_INTERVAL_HOUR_TO_SECOND
SQL_DL_SQL92_INTERVAL_MINUTE_TO_SECOND
SQL_DBMS_NAME
(ODBC 1.0) 		A character string with the name of the DBMS product accessed by the driver.
SQL_DBMS_VER
(ODBC 1.0) 		A character string indicating the version of the DBMS product accessed by the driver. 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 driver must render the DBMS product version in this form but can also append the DBMS product-specific version as well. For example, "04.01.0000 Rdb 4.1".
SQL_DDL_INDEX
(ODBC 3.0) 		An SQLUINTEGER value that indicates support for creation and dropping of indexes:

SQL_DI_CREATE_INDEX
SQL_DI_DROP_INDEX

SQL_DEFAULT_TXN_ISOLATION
(ODBC 1.0) 		An SQLUINTEGER value that indicates the default transaction isolation level supported by the driver or data source, or zero if the data source does not support transactions. The following terms are used to define transaction isolation levels:

Dirty Read Transaction 1 changes a row. Transaction 2 reads the changed row before transaction 1 commits the change. If transaction 1 rolls back the change, transaction 2 will have read a row that is considered to have never existed.

Nonrepeatable Read Transaction 1 reads a row. Transaction 2 updates or deletes that row and commits this change. If transaction 1 attempts to reread the row, it will receive different row values or discover that the row has been deleted.

Phantom Transaction 1 reads a set of rows that satisfy some search criteria. Transaction 2 generates one or more rows (through either inserts or updates) that match the search criteria. If transaction 1 reexecutes the statement that reads the rows, it receives a different set of rows.

If the data source supports transactions, the driver returns one of the following bitmasks:

SQL_TXN_READ_UNCOMMITTED = Dirty reads, nonrepeatable reads, and phantoms are possible.

SQL_TXN_READ_COMMITTED = Dirty reads are not possible. Nonrepeatable reads and phantoms are possible.

SQL_TXN_REPEATABLE_READ = Dirty reads and nonrepeatable reads are not possible. Phantoms are possible.

SQL_TXN_SERIALIZABLE = Transactions are serializable. Serializable transactions do not allow dirty reads, nonrepeatable reads, or phantoms.
SQL_DESCRIBE_PARAMETER
(ODBC 3.0) 		A character string: "Y" if parameters can be described; "N", if not.

An SQL-92 Full level–conformant driver will usually return "Y" because it will support the DESCRIBE INPUT statement. Because this does not directly specify the underlying SQL support, however, describing parameters might not be supported, even in a SQL-92 Full level–conformant driver.
SQL_DM_VER
(ODBC 3.0) 		A character string with the version of the Driver Manager. The version is of the form ##.##.####.####, where:

The first set of two digits is the major ODBC version, as given by the constant SQL_SPEC_MAJOR.

The second set of two digits is the minor ODBC version, as given by the constant SQL_SPEC_MINOR.

The third set of four digits is the Driver Manager major build number.

The last set of four digits is the Driver Manager minor build number.
SQL_DRIVER_HDBC
SQL_DRIVER_HENV
(ODBC 1.0) 		An SQLUINTEGER value, the driver's environment handle or connection handle, determined by the argument InfoType.

These information types are implemented by the Driver Manager alone.
SQL_DRIVER_HDESC
(ODBC 3.0) 		An SQLUINTEGER value, the driver's descriptor handle determined by the Driver Manager's descriptor handle, which must be passed on input in *InfoValuePtr from the application. In this case, InfoValuePtr is both an input and output argument. The input descriptor handle passed in *InfoValuePtr must have been either explicitly or implicitly allocated on the ConnectionHandle.

The application should make a copy of the Driver Manager's descriptor handle before calling SQLGetInfo with this information type, to ensure that the handle is not overwritten on output.

This information type is implemented by the Driver Manager alone.
SQL_DRIVER_HLIB
(ODBC 2.0) 		An SQLUINTEGER value, the hinst from the load library returned to the Driver Manager when it loaded the driver DLL (on a Microsoft® Windows® platform) or equivalent on a non-Windows platform. The handle is valid only for the connection handle specified in the call to SQLGetInfo.

This information type is implemented by the Driver Manager alone.
SQL_DRIVER_HSTMT
(ODBC 1.0) 		An SQLUINTEGER value, the driver's statement handle determined by the Driver Manager statement handle, which must be passed on input in *InfoValuePtr from the application. In this case, InfoValuePtr is both an input and an output argument. The input statement handle passed in *InfoValuePtr must have been allocated on the argument ConnectionHandle.

The application should make a copy of the Driver Manager's statement handle before calling SQLGetInfo with this information type, to ensure that the handle is not overwritten on output.

This information type is implemented by the Driver Manager alone.
SQL_DRIVER_NAME
(ODBC 1.0) 		A character string with the file name of the driver used to access the data source.
SQL_DRIVER_ODBC_VER
(ODBC 2.0) 		A character string with the version of ODBC that the driver supports. The version is of the form ##.##, where the first two digits are the major version and the next two digits are the minor version. SQL_SPEC_MAJOR and SQL_SPEC_MINOR define the major and minor version numbers. For the version of ODBC described in this manual, these are 3 and 0, and the driver should return "03.00".
SQL_DRIVER_VER
(ODBC 1.0) 		A character string with the version of the driver and optionally, a description of the driver. At a minimum, 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.
SQL_DROP_ASSERTION
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the DROP ASSERTION statement, as defined in SQL-92, supported by the data source.

The following bitmask is used to determine which clauses are supported:

SQL_DA_DROP_ASSERTION

An SQL-92 Full level–conformant driver will always return this option as supported.
SQL_DROP_CHARACTER_SET
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the DROP CHARACTER SET statement, as defined in SQL-92, supported by the data source.

The following bitmask is used to determine which clauses are supported:

SQL_DCS_DROP_CHARACTER_SET

An SQL-92 Full level–conformant driver will always return this option as supported.
SQL_DROP_COLLATION
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the DROP COLLATION statement, as defined in SQL-92, supported by the data source.

The following bitmask is used to determine which clauses are supported:

SQL_DC_DROP_COLLATION

An SQL-92 Full level–conformant driver will always return this option as supported.
SQL_DROP_DOMAIN
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the DROP DOMAIN statement, as defined in SQL-92, supported by the data source.

The following bitmasks are used to determine which clauses are supported:

SQL_DD_DROP_DOMAIN
SQL_DD_CASCADE
SQL_DD_RESTRICT

An SQL-92 Intermediate level–conformant driver will always return all of these options as supported.
SQL_DROP_SCHEMA
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the DROP SCHEMA statement, as defined in SQL-92, supported by the data source.

The following bitmasks are used to determine which clauses are supported:

SQL_DS_DROP_SCHEMA
SQL_DS_CASCADE
SQL_DS_RESTRICT

An SQL-92 Intermediate level–conformant driver will always return all of these options as supported.
SQL_DROP_TABLE
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the DROP TABLE statement, as defined in SQL-92, supported by the data source.

The following bitmasks are used to determine which clauses are supported:

SQL_DT_DROP_TABLE
SQL_DT_CASCADE
SQL_DT_RESTRICT

An FIPS Transitional level–conformant driver will always return all of these options as supported.
SQL_DROP_TRANSLATION
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the DROP TRANSLATION statement, as defined in SQL-92, supported by the data source.

The following bitmask is used to determine which clauses are supported:

SQL_DTR_DROP_TRANSLATION

An SQL-92 Full level–conformant driver will always return this option as supported.
SQL_DROP_VIEW
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses in the DROP VIEW statement, as defined in SQL-92, supported by the data source.

The following bitmasks are used to determine which clauses are supported:

SQL_DV_DROP_VIEW
SQL_DV_CASCADE
SQL_DV_RESTRICT

An FIPS Transitional level–conformant driver will always return all of these options as supported.
SQL_DYNAMIC_CURSOR_ATTRIBUTES1
(ODBC 3.0) 		An SQLUINTEGER bitmask that describes the attributes of a dynamic cursor that are supported by the driver. This bitmask contains the first subset of attributes; for the second subset, see SQL_DYNAMIC_CURSOR_ATTRIBUTES2.

The following bitmasks are used to determine which attributes are supported:

SQL_CA1_NEXT = A FetchOrientation argument of SQL_FETCH_NEXT is supported in a call to SQLFetchScroll when the cursor is a dynamic cursor.

SQL_CA1_ABSOLUTE = FetchOrientation arguments of SQL_FETCH_FIRST, SQL_FETCH_LAST, and SQL_FETCH_ABSOLUTE are supported in a call to SQLFetchScroll when the cursor is a dynamic cursor. (The rowset that will be fetched is independent of the current cursor position.)

SQL_CA1_RELATIVE = FetchOrientation arguments of SQL_FETCH_PRIOR and SQL_FETCH_RELATIVE are supported in a call to SQLFetchScroll when the cursor is a dynamic cursor. (The rowset that will be fetched is dependent on the current cursor position. Note that this is separated from SQL_FETCH_NEXT because in a forward-only cursor, only SQL_FETCH_NEXT is supported.)

SQL_CA1_BOOKMARK = A FetchOrientation argument of SQL_FETCH_BOOKMARK is supported in a call to SQLFetchScroll when the cursor is a dynamic cursor.

SQL_CA1_LOCK_EXCLUSIVE = A LockType argument of SQL_LOCK_EXCLUSIVE is supported in a call to SQLSetPos when the cursor is a dynamic cursor.

SQL_CA1_LOCK_NO_CHANGE = A LockType argument of SQL_LOCK_NO_CHANGE is supported in a call to SQLSetPos when the cursor is a dynamic cursor.

SQL_CA1_LOCK_UNLOCK = A LockType argument of SQL_LOCK_UNLOCK is supported in a call to SQLSetPos when the cursor is a dynamic cursor.

SQL_CA1_POS_POSITION = An Operation argument of SQL_POSITION is supported in a call to SQLSetPos when the cursor is a dynamic cursor.

SQL_CA1_POS_UPDATE = An Operation argument of SQL_UPDATE is supported in a call to SQLSetPos when the cursor is a dynamic cursor.

SQL_CA1_POS_DELETE = An Operation argument of SQL_DELETE is supported in a call to SQLSetPos when the cursor is a dynamic cursor.

SQL_CA1_POS_REFRESH = An Operation argument of SQL_REFRESH is supported in a call to SQLSetPos when the cursor is a dynamic cursor.

SQL_CA1_POSITIONED_UPDATE = An UPDATE WHERE CURRENT OF SQL statement is supported when the cursor is a dynamic cursor. (An SQL-92 Entry level–conformant driver will always return this option as supported.)

SQL_CA1_POSITIONED_DELETE = A DELETE WHERE CURRENT OF SQL statement is supported when the cursor is a dynamic cursor. (An SQL-92 Entry level–conformant driver will always return this option as supported.)

SQL_CA1_SELECT_FOR_UPDATE = A SELECT FOR UPDATE SQL statement is supported when the cursor is a dynamic cursor. (An SQL-92 Entry level–conformant driver will always return this option as supported.)

SQL_CA1_BULK_ADD = An Operation argument of SQL_ADD is supported in a call to SQLBulkOperations when the cursor is a dynamic cursor.

SQL_CA1_BULK_UPDATE_BY_BOOKMARK = An Operation argument of SQL_UPDATE_BY_BOOKMARK is supported in a call to SQLBulkOperations when the cursor is a dynamic cursor.

SQL_CA1_BULK_DELETE_BY_BOOKMARK = An Operation argument of SQL_DELETE_BY_BOOKMARK is supported in a call to SQLBulkOperations when the cursor is a dynamic cursor.

SQL_CA1_BULK_FETCH_BY_BOOKMARK = An Operation argument of SQL_FETCH_BY_BOOKMARK is supported in a call to SQLBulkOperations when the cursor is a dynamic cursor.

An SQL-92 Intermediate level–conformant driver will usually return the SQL_CA1_NEXT, SQL_CA1_ABSOLUTE, and SQL_CA1_RELATIVE options as supported, because it supports scrollable cursors through the embedded SQL FETCH statement. Because this does not directly determine the underlying SQL support, however, scrollable cursors may not be supported, even for an SQL-92 Intermediate level–conformant driver.
SQL_DYNAMIC_CURSOR_ATTRIBUTES2
(ODBC 3.0) 		An SQLUINTEGER bitmask that describes the attributes of a dynamic cursor that are supported by the driver. This bitmask contains the second subset of attributes; for the first subset, see SQL_DYNAMIC_CURSOR_ATTRIBUTES1.

The following bitmasks are used to determine which attributes are supported:

SQL_CA2_READ_ONLY_CONCURRENCY = A read-only dynamic cursor, in which no updates are allowed, is supported. (The SQL_ATTR_CONCURRENCY statement attribute can be SQL_CONCUR_READ_ONLY for a dynamic cursor).

SQL_CA2_LOCK_CONCURRENCY = A dynamic cursor that uses the lowest level of locking sufficient to ensure that the row can be updated is supported. (The SQL_ATTR_CONCURRENCY statement attribute can be SQL_CONCUR_LOCK for a dynamic cursor.) These locks must be consistent with the transaction isolation level set by the SQL_ATTR_TXN_ISOLATION connection attribute.

SQL_CA2_OPT_ROWVER_CONCURRENCY = A dynamic cursor that uses the optimistic concurrency control comparing row versions is supported. (The SQL_ATTR_CONCURRENCY statement attribute can be SQL_CONCUR_ROWVER for a dynamic cursor.)

SQL_CA2_OPT_VALUES_CONCURRENCY = A dynamic cursor that uses the optimistic concurrency control comparing values is supported. (The SQL_ATTR_CONCURRENCY statement attribute can be SQL_CONCUR_VALUES for a dynamic cursor.)

SQL_CA2_SENSITIVITY_ADDITIONS = Added rows are visible to a dynamic cursor; the cursor can scroll to those rows. (Where these rows are added to the cursor is driver-dependent.)

SQL_CA2_SENSITIVITY_DELETIONS = Deleted rows are no longer available to a dynamic cursor, and do not leave a "hole" in the result set; after the dynamic cursor scrolls from a deleted row, it cannot return to that row.

SQL_CA2_SENSITIVITY_UPDATES = Updates to rows are visible to a dynamic cursor; if the dynamic cursor scrolls from and returns to an updated row, the data returned by the cursor is the updated data, not the original data.

SQL_CA2_MAX_ROWS_SELECT = The SQL_ATTR_MAX_ROWS statement attribute affects SELECT statements when the cursor is a dynamic cursor.

SQL_CA2_MAX_ROWS_INSERT = The SQL_ATTR_MAX_ROWS statement attribute affects INSERT statements when the cursor is a dynamic cursor.

SQL_CA2_MAX_ROWS_DELETE = The SQL_ATTR_MAX_ROWS statement attribute affects DELETE statements when the cursor is a dynamic cursor.

SQL_CA2_MAX_ROWS_UPDATE = The SQL_ATTR_MAX_ROWS statement attribute affects UPDATE statements when the cursor is a dynamic cursor.

SQL_CA2_MAX_ROWS_CATALOG = The SQL_ATTR_MAX_ROWS statement attribute affects CATALOG result sets when the cursor is a dynamic cursor.

SQL_CA2_MAX_ROWS_AFFECTS_ALL = The SQL_ATTR_MAX_ROWS statement attribute affects SELECT, INSERT, DELETE, and UPDATE statements, and CATALOG result sets, when the cursor is a dynamic cursor.

SQL_CA2_CRC_EXACT = The exact row count is available in the SQL_DIAG_CURSOR_ROW_COUNT diagnostic field when the cursor is a dynamic cursor.

SQL_CA2_CRC_APPROXIMATE = An approximate row count is available in the SQL_DIAG_CURSOR_ROW_COUNT diagnostic field when the cursor is a dynamic cursor.

SQL_CA2_SIMULATE_NON_UNIQUE = The driver does not guarantee that simulated positioned update or delete statements will affect only one row when the cursor is a dynamic cursor; it is the application's responsibility to guarantee this. (If a statement affects more than one row, SQLExecute or SQLExecDirect returns SQLSTATE 01001 [Cursor operation conflict].) To set this behavior, the application calls SQLSetStmtAttr with the SQL_ATTR_SIMULATE_CURSOR attribute set to SQL_SC_NON_UNIQUE.

SQL_CA2_SIMULATE_TRY_UNIQUE = The driver attempts to guarantee that simulated positioned update or delete statements will affect only one row when the cursor is a dynamic cursor. The driver always executes such statements, even if they might affect more than one row, such as when there is no unique key. (If a statement affects more than one row, SQLExecute or SQLExecDirect returns SQLSTATE 01001 [Cursor operation conflict].) To set this behavior, the application calls SQLSetStmtAttr with the SQL_ATTR_SIMULATE_CURSOR attribute set to SQL_SC_TRY_UNIQUE.

SQL_CA2_SIMULATE_UNIQUE = The driver guarantees that simulated positioned update or delete statements will affect only one row when the cursor is a dynamic cursor. If the driver cannot guarantee this for a given statement, SQLExecDirect or SQLPrepare return SQLSTATE 01001 (Cursor operation conflict). To set this behavior, the application calls SQLSetStmtAttr with the SQL_ATTR_SIMULATE_CURSOR attribute set to SQL_SC_UNIQUE.
SQL_EXPRESSIONS_IN_ORDERBY
(ODBC 1.0) 		A character string: "Y" if the data source supports expressions in the ORDER BY list; "N" if it does not.
SQL_FILE_USAGE
(ODBC 2.0) 		An SQLUSMALLINT value indicating how a single-tier driver directly treats files in a data source:

SQL_FILE_NOT_SUPPORTED = The driver is not a single-tier driver. For example, an ORACLE driver is a two-tier driver.

SQL_FILE_TABLE = A single-tier driver treats files in a data source as tables. For example, an Xbase driver treats each Xbase file as a table.

SQL_FILE_CATALOG = A single-tier driver treats files in a data source as a catalog. For example, a Microsoft® Access driver treats each Microsoft Access file as a complete database.

An application might use this to determine how users will select data. For example, Xbase users often think of data as stored in files, while ORACLE and MicrosoftAccess users generally think of data as stored in tables.

When a user selects an Xbase data source, the application could display the Windows File Open common dialog box; when the user selects a Microsoft Access or ORACLE data source, the application could display a custom Select Table dialog box.
SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1
(ODBC 3.0) 		An SQLUINTEGER bitmask that describes the attributes of a forward-only cursor that are supported by the driver. This bitmask contains the first subset of attributes; for the second subset, see SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2.

The following bitmasks are used to determine which attributes are supported:

SQL_CA1_NEXT
SQL_CA1_LOCK_EXCLUSIVE
SQL_CA1_LOCK_NO_CHANGE
SQL_CA1_LOCK_UNLOCK
SQL_CA1_POS_POSITION
SQL_CA1_POS_UPDATE
SQL_CA1_POS_DELETE
SQL_CA1_POS_REFRESH
SQL_CA1_POSITIONED_UPDATE
SQL_CA1_POSITIONED_DELETE
SQL_CA1_SELECT_FOR_UPDATE
SQL_CA1_BULK_ADD
SQL_CA1_BULK_UPDATE_BY_BOOKMARK
SQL_CA1_BULK_DELETE_BY_BOOKMARK
SQL_CA1_BULK_FETCH_BY_BOOKMARK

For descriptions of these bitmasks, see SQL_DYNAMIC_CURSOR_ATTRIBUTES1 (and substitute "forward-only cursor" for "dynamic cursor" in the descriptions).

SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2
(ODBC 3.0) 		An SQLUINTEGER bitmask that describes the attributes of a forward-only cursor that are supported by the driver. This bitmask contains the second subset of attributes; for the first subset, see SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1.

The following bitmasks are used to determine which attributes are supported:

SQL_CA2_READ_ONLY_CONCURRENCY
SQL_CA2_LOCK_CONCURRENCY
SQL_CA2_OPT_ROWVER_CONCURRENCY
SQL_CA2_OPT_VALUES_CONCURRENCY
SQL_CA2_SENSITIVITY_ADDITIONS
SQL_CA2_SENSITIVITY_DELETIONS
SQL_CA2_SENSITIVITY_UPDATES
SQL_CA2_MAX_ROWS_SELECT
SQL_CA2_MAX_ROWS_INSERT
SQL_CA2_MAX_ROWS_DELETE
SQL_CA2_MAX_ROWS_UPDATE
SQL_CA2_MAX_ROWS_CATALOG
SQL_CA2_MAX_ROWS_AFFECTS_ALL
SQL_CA2_CRC_EXACT
SQL_CA2_CRC_APPROXIMATE
SQL_CA2_SIMULATE_NON_UNIQUE
SQL_CA2_SIMULATE_TRY_UNIQUE
SQL_CA2_SIMULATE_UNIQUE

For descriptions of these bitmasks, see SQL_DYNAMIC_CURSOR_ATTRIBUTES2 (and substitute "forward-only cursor" for "dynamic cursor" in the descriptions).
SQL_GETDATA_EXTENSIONS
(ODBC 2.0) 		An SQLUINTEGER bitmask enumerating extensions to SQLGetData.

The following bitmasks are used in conjunction with the flag to determine what common extensions the driver supports for SQLGetData:

SQL_GD_ANY_COLUMN = SQLGetData can be called for any unbound column, including those before the last bound column. Note that the columns must be called in order of ascending column number unless SQL_GD_ANY_ORDER is also returned.

SQL_GD_ANY_ORDER = SQLGetData can be called for unbound columns in any order. Note that SQLGetData can be called only for columns after the last bound column unless SQL_GD_ANY_COLUMN is also returned.

SQL_GD_BLOCK = SQLGetData can be called for an unbound column in any row in a block (where the rowset size is greater than 1) of data after positioning to that row with SQLSetPos.

SQL_GD_BOUND = SQLGetData can be called for bound columns as well as unbound columns. A driver cannot return this value unless it also returns SQL_GD_ANY_COLUMN.

SQLGetData is required to return data only from unbound columns that occur after the last bound column, are called in order of increasing column number, and are not in a row in a block of rows.

If a driver supports bookmarks (either fixed-length or variable-length), it must support calling SQLGetData on column 0. This support is required regardless of what the driver returns for a call to SQLGetInfo with the SQL_GETDATA_EXTENSIONS InfoType.
SQL_GROUP_BY
(ODBC 2.0) 		An SQLUSMALLINT value specifying the relationship between the columns in the GROUP BY clause and the nonaggregated columns in the select list:

SQL_GB_COLLATE = A COLLATE clause can be specified at the end of each grouping column. (ODBC 3.0)

SQL_GB_NOT_SUPPORTED = GROUP BY clauses are not supported. (ODBC 2.0)

SQL_GB_GROUP_BY_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. (ODBC 2.0)

SQL_GB_GROUP_BY_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. (ODBC 2.0)

SQL_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. (ODBC 2.0)

An SQL-92 Entry level–conformant driver will always return the SQL_GB_GROUP_BY_EQUALS_SELECT option as supported. An SQL-92 Full level–conformant driver will always return the SQL_GB_COLLATE option as supported. If none of the options is supported, the GROUP BY clause is not supported by the data source.
SQL_IDENTIFIER_CASE
(ODBC 1.0) 		An SQLUSMALLINT value as follows:

SQL_IC_UPPER = Identifiers in SQL are not case-sensitive and are stored in uppercase in system catalog.

SQL_IC_LOWER = Identifiers in SQL are not case-sensitive and are stored in lowercase in system catalog.

SQL_IC_SENSITIVE = Identifiers in SQL are case-sensitive and are stored in mixed case in system catalog.

SQL_IC_MIXED = Identifiers in SQL are not case-sensitive and are stored in mixed case in system catalog.

Because identifiers in SQL-92 are never case-sensitive, a driver that conforms strictly to SQL-92 (any level) will never return the SQL_IC_SENSITIVE option as supported.
SQL_IDENTIFIER_QUOTE_CHAR
(ODBC 1.0) 		The character string used as the starting and ending delimiter of a quoted (delimited) identifier in SQL statements. (Identifiers passed as arguments to ODBC functions do not need to be quoted.) If the data source does not support quoted identifiers, a blank is returned.

This character string can also be used for quoting catalog function arguments when the connection attribute SQL_ATTR_METADATA_ID is set to SQL_TRUE.

Because the identifier quote character in SQL-92 is the double quotation mark ("), a driver that conforms strictly to SQL-92 will always return the double quotation mark character.
SQL_INDEX_KEYWORDS
(ODBC 3.0) 		An SQLUINTEGER bitmask that enumerates keywords in the CREATE INDEX statement that are supported by the driver:

SQL_IK_NONE = None of the keywords is supported.

SQL_IK_ASC = ASC keyword is supported.

SQL_IK_DESC = DESC keyword is supported.

SQL_IK_ALL = All keywords are supported.

To see if the CREATE INDEX statement is supported, an application calls SQLGetInfo with the SQL_DLL_INDEX information type.
SQL_INFO_SCHEMA_VIEWS
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the views in the INFORMATION_SCHEMA that are supported by the driver. The views in, and the contents of, INFORMATION_SCHEMA are as defined in SQL-92.

The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.

The following bitmasks are used to determine which views are supported:

SQL_ISV_ASSERTIONS = Identifies the catalog's assertions that are owned by a given user. (Full level)

SQL_ISV_CHARACTER_SETS = Identifies the catalog's character sets that are accessible to a given user. (Intermediate level)

SQL_ISV_CHECK_CONSTRAINTS = Identifies the CHECK constraints that are owned by a given user. (Intermediate level)

SQL_ISV_COLLATIONS = Identifies the character collations for the catalog that are accessible to a given user. (Full level)

SQL_ISV_COLUMN_DOMAIN_USAGE = Identifies columns for the catalog that are dependent on domains defined in the catalog and are owned by a given user. (Intermediate level)

SQL_ISV_COLUMN_PRIVILEGES = Identifies the privileges on columns of persistent tables that are available to or granted by a given user. (FIPS Transitional level)

SQL_ISV_COLUMNS = Identifies the columns of persistent tables that are accessible to a given user. (FIPS Transitional level)

SQL_ISV_CONSTRAINT_COLUMN_USAGE = Similar to CONSTRAINT_TABLE_USAGE view, columns are identified for the various constraints that are owned by a given user. (Intermediate level)

SQL_ISV_CONSTRAINT_TABLE_USAGE = Identifies the tables that are used by constraints (referential, unique, and assertions), and are owned by a given user. (Intermediate level)

SQL_ISV_DOMAIN_CONSTRAINTS = Identifies the domain constraints (of the domains in the catalog) that are accessible to a given user. (Intermediate level)

SQL_ISV_DOMAINS = Identifies the domains defined in a catalog that are accessible to the user. (Intermediate level)

SQL_ISV_KEY_COLUMN_USAGE = Identifies columns defined in the catalog that are constrained as keys by a given user. (Intermediate level)

SQL_ISV_REFERENTIAL_CONSTRAINTS = Identifies the referential constraints that are owned by a given user. (Intermediate level)

SQL_ISV_SCHEMATA = Identifies the schemas that are owned by a given user. (Intermediate level)

SQL_ISV_SQL_LANGUAGES = Identifies the SQL conformance levels, options, and dialects supported by the SQL implementation. (Intermediate level)

SQL_ISV_TABLE_CONSTRAINTS = Identifies the table constraints that are owned by a given user. (Intermediate level)

SQL_ISV_TABLE_PRIVILEGES = Identifies the privileges on persistent tables that are available to or granted by a given user. (FIPS Transitional level)

SQL_ISV_TABLES = Identifies the persistent tables defined in a catalog that are accessible to a given user. (FIPS Transitional level)

SQL_ISV_TRANSLATIONS = Identifies character translations for the catalog that are accessible to a given user. (Full level)

SQL_ISV_USAGE_PRIVILEGES = Identifies the USAGE privileges on catalog objects that are available to or owned by a given user. (FIPS Transitional level)

SQL_ISV_VIEW_COLUMN_USAGE = Identifies the columns on which the catalog's views that are owned by a given user are dependent. (Intermediate level)

SQL_ISV_VIEW_TABLE_USAGE = Identifies the tables on which the catalog's views that are owned by a given user are dependent. (Intermediate level)

SQL_ISV_VIEWS = Identifies the viewed tables defined in this catalog that are accessible to a given user. (FIPS Transitional level)
SQL_INSERT_STATEMENT
(ODBC 3.0) 		An SQLUINTEGER bitmask that indicates support for INSERT statements:

SQL_IS_INSERT_LITERALS

SQL_IS_INSERT_SEARCHED

SQL_IS_SELECT_INTO

An SQL-92 Entry level–conformant driver will always return all of these options as supported.
SQL_INTEGRITY
(ODBC 1.0) 		A character string: "Y" if the data source supports the Integrity Enhancement Facility; "N" if it does not.

This InfoType has been renamed for ODBC 3.0 from the ODBC 2.0 InfoType SQL_ODBC_SQL_OPT_IEF.
SQL_KEYSET_CURSOR_ATTRIBUTES1
(ODBC 3.0) 		An SQLUINTEGER bitmask that describes the attributes of a keyset cursor that are supported by the driver. This bitmask contains the first subset of attributes; for the second subset, see SQL_KEYSET_CURSOR_ATTRIBUTES2.

The following bitmasks are used to determine which attributes are supported:

SQL_CA1_NEXT
SQL_CA1_ABSOLUTE
SQL_CA1_RELATIVE
SQL_CA1_BOOKMARK
SQL_CA1_LOCK_EXCLUSIVE
SQL_CA1_LOCK_NO_CHANGE
SQL_CA1_LOCK_UNLOCK
SQL_CA1_POS_POSITION
SQL_CA1_POS_UPDATE
SQL_CA1_POS_DELETE
SQL_CA1_POS_REFRESH
SQL_CA1_POSITIONED_UPDATE
SQL_CA1_POSITIONED_DELETE
SQL_CA1_SELECT_FOR_UPDATE
SQL_CA1_BULK_ADD
SQL_CA1_BULK_UPDATE_BY_BOOKMARK
SQL_CA1_BULK_DELETE_BY_BOOKMARK
SQL_CA1_BULK_FETCH_BY_BOOKMARK

For descriptions of these bitmasks, see SQL_DYNAMIC_CURSOR_ATTRIBUTES1 (and substitute "keyset-driven cursor" for "dynamic cursor" in the descriptions).

An SQL-92 Intermediate level–conformant driver will usually return the SQL_CA1_NEXT, SQL_CA1_ABSOLUTE, and SQL_CA1_RELATIVE options as supported, because the driver supports scrollable cursors through the embedded SQL FETCH statement. Because this does not directly determine the underlying SQL support, however, scrollable cursors may not be supported, even for an SQL-92 Intermediate level–conformant driver.
SQL_KEYSET_CURSOR_ATTRIBUTES2
(ODBC 3.0) 		An SQLUINTEGER bitmask that describes the attributes of a keyset cursor that are supported by the driver. This bitmask contains the second subset of attributes; for the first subset, see SQL_KEYSET_CURSOR_ATTRIBUTES1.

The following bitmasks are used to determine which attributes are supported:

SQL_CA2_READ_ONLY_CONCURRENCY
SQL_CA2_LOCK_CONCURRENCY
SQL_CA2_OPT_ROWVER_CONCURRENCY
SQL_CA2_OPT_VALUES_CONCURRENCY
SQL_CA2_SENSITIVITY_ADDITIONS
SQL_CA2_SENSITIVITY_DELETIONS
SQL_CA2_SENSITIVITY_UPDATES
SQL_CA2_MAX_ROWS_SELECT
SQL_CA2_MAX_ROWS_INSERT
SQL_CA2_MAX_ROWS_DELETE
SQL_CA2_MAX_ROWS_UPDATE
SQL_CA2_MAX_ROWS_CATALOG
SQL_CA2_MAX_ROWS_AFFECTS_ALL
SQL_CA2_CRC_EXACT
SQL_CA2_CRC_APPROXIMATE
SQL_CA2_SIMULATE_NON_UNIQUE
SQL_CA2_SIMULATE_TRY_UNIQUE
SQL_CA2_SIMULATE_UNIQUE

For descriptions of these bitmasks, see SQL_DYNAMIC_CURSOR_ATTRIBUTES1 (and substitute "keyset-driven cursor" for "dynamic cursor" in the descriptions).
SQL_KEYWORDS
(ODBC 2.0) 		A character string containing a comma-separated list of all data source–specific keywords. This list does not contain keywords specific to ODBC or keywords used by both the data source and ODBC. This list represents all the reserved keywords; interoperable applications should not use these words in object names.

For a list of ODBC keywords, see "List of Reserved Keywords" in Appendix C, "SQL Grammar." The #define value SQL_ODBC_KEYWORDS contains a comma-separated list of ODBC keywords.
SQL_LIKE_ESCAPE_CLAUSE
(ODBC 2.0) 		A character string: "Y" if the data source supports an escape character for the percent character (%) and underscore character (_) in a LIKE predicate and the driver supports the ODBC syntax for defining a LIKE predicate escape character; "N" otherwise.
SQL_MAX_ASYNC_CONCURRENT_STATEMENTS
(ODBC 3.0) 		An SQLUINTEGER value specifying the maximum number of active concurrent statements in asynchronous mode that the driver can support on a given connection. If there is no specific limit or the limit is unknown, this value is zero.
SQL_MAX_BINARY_LITERAL_LEN
(ODBC 2.0) 		An SQLUINTEGER value specifying the maximum length (number of hexadecimal characters, excluding the literal prefix and suffix returned by SQLGetTypeInfo) of a binary literal in an SQL statement. For example, the binary literal 0xFFAA has a length of 4. If there is no maximum length or the length is unknown, this value is set to zero.
SQL_MAX_CATALOG_NAME_LEN
(ODBC 1.0) 		An SQLUSMALLINT value specifying the maximum length of a catalog name in the data source. If there is no maximum length or the length is unknown, this value is set to zero.

An FIPS Full level–conformant driver will return at least 128.

This InfoType has been renamed for ODBC 3.0 from the ODBC 2.0 InfoType SQL_MAX_QUALIFIER_NAME_LEN.
SQL_MAX_CHAR_LITERAL_LEN
(ODBC 2.0) 		An SQLUINTEGER value specifying the maximum length (number of characters, excluding the literal prefix and suffix returned by SQLGetTypeInfo) of a character literal in an SQL statement. If there is no maximum length or the length is unknown, this value is set to zero.
SQL_MAX_COLUMN_NAME_LEN
(ODBC 1.0) 		An SQLUSMALLINT value specifying the maximum length of a column name in the data source. If there is no maximum length or the length is unknown, this value is set to zero.

An FIPS Entry level–conformant driver will return at least 18. An FIPS Intermediate level–conformant driver will return at least 128.
SQL_MAX_COLUMNS_IN_GROUP_BY
(ODBC 2.0) 		An SQLUSMALLINT value specifying the maximum number of columns allowed in a GROUP BY clause. If there is no specified limit or the limit is unknown, this value is set to zero.

An FIPS Entry level–conformant driver will return at least 6. An FIPS Intermediate level–conformant driver will return at least 15.
SQL_MAX_COLUMNS_IN_INDEX
(ODBC 2.0) 		An SQLUSMALLINT value specifying the maximum number of columns allowed in an index. If there is no specified limit or the limit is unknown, this value is set to zero.
SQL_MAX_COLUMNS_IN_ORDER_BY
(ODBC 2.0) 		An SQLUSMALLINT value specifying the maximum number of columns allowed in an ORDER BY clause. If there is no specified limit or the limit is unknown, this value is set to zero.

An FIPS Entry level–conformant driver will return at least 6. An FIPS Intermediate level–conformant driver will return at least 15.
SQL_MAX_COLUMNS_IN_SELECT
(ODBC 2.0) 		An SQLUSMALLINT value specifying the maximum number of columns allowed in a select list. If there is no specified limit or the limit is unknown, this value is set to zero.

An FIPS Entry level–conformant driver will return at least 100. An FIPS Intermediate level–conformant driver will return at least 250.
SQL_MAX_COLUMNS_IN_TABLE
(ODBC 2.0) 		An SQLUSMALLINT value specifying the maximum number of columns allowed in a table. If there is no specified limit or the limit is unknown, this value is set to zero.

An FIPS Entry level–conformant driver will return at least 100. An FIPS Intermediate level–conformant driver will return at least 250.
SQL_MAX_CONCURRENT_ACTIVITIES
(ODBC 1.0) 		An SQLUSMALLINT value specifying the maximum number of active statements that the driver can support for a connection. A statement is defined as active if it has results pending, with the term "results" meaning rows from a SELECT operation or rows affected by an INSERT, UPDATE, or DELETE operation (such as a row count), or if it is in a NEED_DATA state. This value can reflect a limitation imposed by either the driver or the data source. If there is no specified limit or the limit is unknown, this value is set to zero.

This InfoType has been renamed for ODBC 3.0 from the ODBC 2.0 InfoType SQL_ACTIVE_STATEMENTS.
SQL_MAX_CURSOR_NAME_LEN
(ODBC 1.0) 		An SQLUSMALLINT value specifying the maximum length of a cursor name in the data source. If there is no maximum length or the length is unknown, this value is set to zero.

An FIPS Entry level–conformant driver will return at least 18. An FIPS Intermediate level–conformant driver will return at least 128.
SQL_MAX_DRIVER_CONNECTIONS
(ODBC 1.0) 		An SQLUSMALLINT value specifying the maximum number of active connections that the driver can support for an environment. This value can reflect a limitation imposed by either the driver or the data source. If there is no specified limit or the limit is unknown, this value is set to zero.

This InfoType has been renamed for ODBC 3.0 from the ODBC 2.0 InfoType SQL_ACTIVE_CONNECTIONS.
SQL_MAX_IDENTIFIER_LEN
(ODBC 3.0) 		An SQLUSMALLINT that indicates the maximum size in characters that the data source supports for user-defined names.

An FIPS Entry level–conformant driver will return at least 18. An FIPS Intermediate level–conformant driver will return at least 128.
SQL_MAX_INDEX_SIZE
(ODBC 2.0) 		An SQLUINTEGER value specifying the maximum number of bytes allowed in the combined fields of an index. If there is no specified limit or the limit is unknown, this value is set to zero.
SQL_MAX_PROCEDURE_NAME_LEN
(ODBC 1.0) 		An SQLUSMALLINT value specifying the maximum length of a procedure name in the data source. If there is no maximum length or the length is unknown, this value is set to zero.
SQL_MAX_ROW_SIZE
(ODBC 2.0) 		An SQLUINTEGER value specifying 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.

An FIPS Entry level–conformant driver will return at least 2,000. An FIPS Intermediate level–conformant driver will return at least 8,000.
SQL_MAX_ROW_SIZE_INCLUDES_LONG
(ODBC 3.0) 		A character string: "Y" if the maximum row size returned for the SQL_MAX_ROW_SIZE information type includes the length of all SQL_LONGVARCHAR and SQL_LONGVARBINARY columns in the row; "N" otherwise.
SQL_MAX_SCHEMA_NAME_LEN
(ODBC 1.0) 		An SQLUSMALLINT value specifying the maximum length of a schema name in the data source. If there is no maximum length or the length is unknown, this value is set to zero.

An FIPS Entry level–conformant driver will return at least 18. An FIPS Intermediate level–conformant driver will return at least 128.

This InfoType has been renamed for ODBC 3.0 from the ODBC 2.0 InfoType SQL_MAX_OWNER_NAME_LEN.
SQL_MAX_STATEMENT_LEN
(ODBC 2.0) 		An SQLUINTEGER value specifying the maximum length (number of characters, including white space) of an SQL statement. If there is no maximum length or the length is unknown, this value is set to zero.
SQL_MAX_TABLE_NAME_LEN
(ODBC 1.0) 		An SQLUSMALLINT value specifying the maximum length of a table name in the data source. If there is no maximum length or the length is unknown, this value is set to zero.

An FIPS Entry level–conformant driver will return at least 18. An FIPS Intermediate level–conformant driver will return at least 128.
SQL_MAX_TABLES_IN_SELECT
(ODBC 2.0) 		An SQLUSMALLINT value specifying 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.

An FIPS Entry level–conformant driver will return at least 15. An FIPS Intermediate level–conformant driver will return at least 50.
SQL_MAX_USER_NAME_LEN
(ODBC 2.0) 		An SQLUSMALLINT value specifying the maximum length of a user name in the data source. If there is no maximum length or the length is unknown, this value is set to zero.
SQL_MULT_RESULT_SETS
(ODBC 1.0) 		A character string: "Y" if the data source supports multiple result sets, "N" if it does not.

For more information on multiple result sets, see "Multiple Results" in Chapter 11: Retrieving Results (Advanced).
SQL_MULTIPLE_ACTIVE_TXN
(ODBC 1.0) 		A character string: "Y" if the driver supports more than one active transaction at the same time, "N" if only one transaction can be active at any time.

The information returned for this information type does not apply in the case of distributed transactions.
SQL_NEED_LONG_DATA_LEN
(ODBC 2.0) 		A character string: "Y" if the data source needs the length of a long data value (the data type is SQL_LONGVARCHAR, SQL_LONGVARBINARY, or a long data source–specific data type) before that value is sent to the data source, "N" if it does not. For more information, see SQLBindParameter and SQLSetPos.
SQL_NON_NULLABLE_COLUMNS
(ODBC 1.0) 		An SQLUSMALLINT value specifying whether the data source supports NOT NULL in columns:

SQL_NNC_NULL = All columns must be nullable.

SQL_NNC_NON_NULL = Columns cannot be nullable. (The data source supports the NOT NULL column constraint in CREATE TABLE statements.)

An SQL-92 Entry level–conformant driver will return SQL_NNC_NON_NULL.
SQL_NULL_COLLATION
(ODBC 2.0) 		An SQLUSMALLINT value specifying where NULLs are sorted in a result set:

SQL_NC_END = NULLs are sorted at the end of the result set, regardless of the ASC or DESC keywords.

SQL_NC_HIGH = NULLs are sorted at the high end of the result set, depending on the ASC or DESC keywords.

SQL_NC_LOW = NULLs are sorted at the low end of the result set, depending on the ASC or DESC keywords.

SQL_NC_START = NULLs are sorted at the start of the result set, regardless of the ASC or DESC keywords.
SQL_NUMERIC_FUNCTIONS
(ODBC 1.0)

The information type was introduced in ODBC 1.0; each bitmask is labeled with the version in which it was introduced.

 An SQLUINTEGER bitmask enumerating the scalar numeric functions supported by the driver and associated data source.

The following bitmasks are used to determine which numeric functions are supported:

SQL_FN_NUM_ABS (ODBC 1.0)
SQL_FN_NUM_ACOS (ODBC 1.0)
SQL_FN_NUM_ASIN (ODBC 1.0)
SQL_FN_NUM_ATAN (ODBC 1.0)
SQL_FN_NUM_ATAN2 (ODBC 1.0)
SQL_FN_NUM_CEILING (ODBC 1.0)
SQL_FN_NUM_COS (ODBC 1.0)
SQL_FN_NUM_COT (ODBC 1.0)
SQL_FN_NUM_DEGREES (ODBC 2.0)
SQL_FN_NUM_EXP (ODBC 1.0)
SQL_FN_NUM_FLOOR (ODBC 1.0)
SQL_FN_NUM_LOG (ODBC 1.0)
SQL_FN_NUM_LOG10 (ODBC 2.0)
SQL_FN_NUM_MOD (ODBC 1.0)
SQL_FN_NUM_PI (ODBC 1.0)
SQL_FN_NUM_POWER (ODBC 2.0)
SQL_FN_NUM_RADIANS (ODBC 2.0)
SQL_FN_NUM_RAND (ODBC 1.0)
SQL_FN_NUM_ROUND (ODBC 2.0)
SQL_FN_NUM_SIGN (ODBC 1.0)
SQL_FN_NUM_SIN (ODBC 1.0)
SQL_FN_NUM_SQRT (ODBC 1.0)
SQL_FN_NUM_TAN (ODBC 1.0)
SQL_FN_NUM_TRUNCATE (ODBC 2.0)
SQL_ODBC_INTERFACE_CONFORMANCE
(ODBC 3.0) 		An SQLUINTEGER value indicating the level of the ODBC 3.x interface that the driver conforms to.

SQL_OIC_CORE: The minimum level that all ODBC drivers are expected to conform to. This level includes basic interface elements such as connection functions, functions for preparing and executing an SQL statement, basic result set metadata functions, basic catalog functions, and so on.

SQL_OIC_LEVEL1: A level including the core standards compliance level functionality, plus scrollable cursors, bookmarks, positioned updates and deletes, and so on.

SQL_OIC_LEVEL2: A level including level 1 standards compliance level functionality, plus advanced features such as sensitive cursors; update, delete, and refresh by bookmarks; stored procedure support; catalog functions for primary and foreign keys; multicatalog support; and so on.

For more information, see "Interface Conformance Levels" in Chapter 4: ODBC Fundamentals.
SQL_ODBC_VER
(ODBC 1.0) 		A character string with the version of ODBC to which the Driver Manager conforms. The version is of the form ##.##.0000, where the first two digits are the major version and the next two digits are the minor version. This is implemented solely in the Driver Manager.
SQL_OJ_CAPABILITIES
(ODBC 2.01) 		An SQLUINTEGER bitmask enumerating the types of outer joins supported by the driver and data source. The following bitmasks are used to determine which types are supported:

SQL_OJ_LEFT = Left outer joins are supported.

SQL_OJ_RIGHT = Right outer joins are supported.

SQL_OJ_FULL = Full outer joins are supported.

SQL_OJ_NESTED = Nested outer joins are supported.

SQL_OJ_NOT_ORDERED = The column names in the ON clause of the outer join do not have to be in the same order as their respective table names in the OUTER JOIN clause.

SQL_OJ_INNER = The inner table (the right table in a left outer join or the left table in a right outer join) can also be used in an inner join. This does not apply to full outer joins, which do not have an inner table.

SQL_OJ_ALL_COMPARISON_OPS = The comparison operator in the ON clause can be any of the ODBC comparison operators. If this bit is not set, only the equals (=) comparison operator can be used in outer joins.

If none of these options is returned as supported, no outer join clause is supported.

For information on the support of relational join operators in a SELECT statement, as defined by SQL-92, see SQL_SQL92_RELATIONAL_JOIN_OPERATORS.
SQL_ORDER_BY_COLUMNS_IN_SELECT
(ODBC 2.0) 		A character string: "Y" if the columns in the ORDER BY clause must be in the select list; otherwise, "N".
SQL_PARAM_ARRAY_ROW_COUNTS
(ODBC 3.0) 		An SQLUINTEGER enumerating the driver's properties regarding the availability of row counts in a parameterized execution. Has the following values:

SQL_PARC_BATCH = Individual row counts are available for each set of parameters. This is conceptually equivalent to the driver generating a batch of SQL statements, one for each parameter set in the array. Extended error information can be retrieved by using the SQL_PARAM_STATUS_PTR descriptor field.

SQL_PARC_NO_BATCH = There is only one row count available, which is the cumulative row count resulting from the execution of the statement for the entire array of parameters. This is conceptually equivalent to treating the statement along with the entire parameter array as one atomic unit. Errors are handled the same as if one statement were executed.
SQL_PARAM_ARRAY_SELECTS
(ODBC 3.0) 		An SQLUINTEGER enumerating the driver's properties regarding the availability of result sets in a parameterized execution. Has the following values:

SQL_PAS_BATCH = There is one result set available per set of parameters. This is conceptually equivalent to the driver generating a batch of SQL statements, one for each parameter set in the array.

SQL_PAS_NO_BATCH = There is only one result set available, which represents the cumulative result set resulting from the execution of the statement for the entire array of parameters. This is conceptually equivalent to treating the statement along with the entire parameter array as one atomic unit.

SQL_PAS_NO_SELECT = A driver does not allow a result-set generating statement to be executed with an array of parameters.
SQL_PROCEDURE_TERM
(ODBC 1.0) 		A character string with the data source vendor's name for a procedure; for example, "database procedure", "stored procedure", "procedure", "package", or "stored query".
SQL_PROCEDURES
(ODBC 1.0) 		A character string: "Y" if the data source supports procedures and the driver supports the ODBC procedure invocation syntax; "N" otherwise.
SQL_POS_OPERATIONS (ODBC 2.0) An SQLINTEGER bitmask enumerating the support operations in SQLSetPos.

The following bitmasks are used in conjunction with the flag to determine which options are supported.

SQL_POS_POSITION (ODBC 2.0) SQL_POS_REFRESH (ODBC 2.0) SQL_POS_UPDATE (ODBC 2.0) SQL_POS_DELETE (ODBC 2.0) SQL_POS_ADD (ODBC 2.0)

SQL_QUOTED_IDENTIFIER_CASE
(ODBC 2.0) 		An SQLUSMALLINT value as follows:

SQL_IC_UPPER = Quoted identifiers in SQL are not case-sensitive and are stored in uppercase in the system catalog.

SQL_IC_LOWER = Quoted identifiers in SQL are not case-sensitive and are stored in lowercase in the system catalog.

SQL_IC_SENSITIVE = Quoted identifiers in SQL are case-sensitive and are stored in mixed case in the system catalog. (In an SQL-92–compliant database, quoted identifiers are always case-sensitive.)

SQL_IC_MIXED = Quoted identifiers in SQL are not case-sensitive and are stored in mixed case in the system catalog.

An SQL-92 Entry level–conformant driver will always return SQL_IC_SENSITIVE.
SQL_ROW_UPDATES
(ODBC 1.0) 		A character string: "Y" if a keyset-driven or mixed cursor maintains row versions or values for all fetched rows and therefore can detect any updates made to a row by any user since the row was last fetched. (This applies only to updates, not to deletions or insertions.) The driver can return the SQL_ROW_UPDATED flag to the row status array when SQLFetchScroll is called. Otherwise, "N".
SQL_SCHEMA_TERM
(ODBC 1.0) 		A character string with the data source vendor's name for an schema; for example, "owner", "Authorization ID", or "Schema".

The character string can be returned in upper, lower, or mixed case.

An SQL-92 Entry level–conformant driver will always return "schema".

This InfoType has been renamed for ODBC 3.0 from the ODBC 2.0 InfoType SQL_OWNER_TERM.
SQL_SCHEMA_USAGE
(ODBC 2.0) 		An SQLUINTEGER bitmask enumerating the statements in which schemas can be used:

SQL_SU_DML_STATEMENTS = Schemas are supported in all Data Manipulation Language statements: SELECT, INSERT, UPDATE, DELETE, and if supported, SELECT FOR UPDATE and positioned update and delete statements.

SQL_SU_PROCEDURE_INVOCATION = Schemas are supported in the ODBC procedure invocation statement.

SQL_SU_TABLE_DEFINITION = Schemas are supported in all table definition statements: CREATE TABLE, CREATE VIEW, ALTER TABLE, DROP TABLE, and DROP VIEW.

SQL_SU_INDEX_DEFINITION = Schemas are supported in all index definition statements: CREATE INDEX and DROP INDEX.

SQL_SU_PRIVILEGE_DEFINITION = Schemas are supported in all privilege definition statements: GRANT and REVOKE.

An SQL-92 Entry level–conformant driver will always return the SQL_SU_DML_STATEMENTS, SQL_SU_TABLE_DEFINITION, and SQL_SU_PRIVILEGE_DEFINITION options, as supported.

This InfoType has been renamed for ODBC 3.0 from the ODBC 2.0 InfoType SQL_OWNER_USAGE.
SQL_SCROLL_OPTIONS
(ODBC 1.0)

The information type was introduced in ODBC 1.0; each bitmask is labeled with the version in which it was introduced.

 An SQLUINTEGER bitmask enumerating the scroll options supported for scrollable cursors.

The following bitmasks are used to determine which options are supported:

SQL_SO_FORWARD_ONLY = The cursor only scrolls forward. (ODBC 1.0)

SQL_SO_STATIC = The data in the result set is static. (ODBC 2.0)

SQL_SO_KEYSET_DRIVEN = The driver saves and uses the keys for every row in the result set. (ODBC 1.0)

SQL_SO_DYNAMIC = The driver keeps the keys for every row in the rowset (the keyset size is the same as the rowset size). (ODBC 1.0)

SQL_SO_MIXED = The driver keeps the keys for every row in the keyset, and the keyset size is greater than the rowset size. The cursor is keyset-driven inside the keyset and dynamic outside the keyset. (ODBC 1.0)

For information about scrollable cursors, see "Scrollable Cursors" in Chapter 11: Retrieving Results (Advanced)
SQL_SEARCH_PATTERN_ESCAPE
(ODBC 1.0) 		A character string specifying what the driver supports as an escape character that permits the use of the pattern match metacharacters underscore (_) and percent sign (%) as valid characters in search patterns. This escape character applies only for those catalog function arguments that support search strings. If this string is empty, the driver does not support a search-pattern escape character.

Because this information type does not indicate general support of the escape character in the LIKE predicate, SQL-92 does not include requirements for this character string.

This InfoType is limited to catalog functions. For a description of the use of the escape character in search pattern strings, see "Pattern Value Arguments" in Chapter 7: Catalog Functions.
SQL_SERVER_NAME
(ODBC 1.0) 		A character string with the actual data source–specific server name; useful when a data source name is used during SQLConnect, SQLDriverConnect, and SQLBrowseConnect.
SQL_SPECIAL_CHARACTERS
(ODBC 2.0) 		A character string containing all special characters (that is, all characters except a through z, A through Z, 0 through 9, and underscore) that can be used in an identifier name, such as a table name, column column name, or index name, on the data source. For example, "#$^". If an identifier contains one or more of these characters, the identifier must be a delimited identifier.
SQL_SQL_CONFORMANCE
(ODBC 3.0) 		An SQLUINTEGER value indicating the level of SQL-92 supported by the driver:

SQL_SC_SQL92_ENTRY = Entry level SQL-92 compliant.

SQL_SC_FIPS127_2_TRANSITIONAL = FIPS 127-2 transitional level compliant.

SQL_SC_SQL92_FULL = Full level SQL-92 compliant.

SQL_SC_ SQL92_INTERMEDIATE = Intermediate level SQL-92 compliant.
SQL_SQL92_DATETIME_FUNCTIONS
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the datetime scalar functions that are supported by the driver and the associated data source, as defined in SQL-92.

The following bitmasks are used to determine which datetime functions are supported:

SQL_SDF_CURRENT_DATE
SQL_SDF_CURRENT_TIME
SQL_SDF_CURRENT_TIMESTAMP
SQL_SQL92_FOREIGN_KEY_DELETE_RULE
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the rules supported for a foreign key in a DELETE statement, as defined in SQL-92.

The following bitmasks are used to determine which clauses are supported by the data source:

SQL_SFKD_CASCADE
SQL_SFKD_NO_ACTION
SQL_SFKD_SET_DEFAULT
SQL_SFKD_SET_NULL

An FIPS Transitional level–conformant driver will always return all of these options as supported.
SQL_SQL92_FOREIGN_KEY_UPDATE_RULE
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the rules supported for a foreign key in an UPDATE statement, as defined in SQL-92.

The following bitmasks are used to determine which clauses are supported by the data source:

SQL_SFKU_CASCADE
SQL_SFKU_NO_ACTION
SQL_SFKU_SET_DEFAULT
SQL_SFKU_SET_NULL

An SQL-92 Full level–conformant driver will always return all of these options as supported.
SQL_SQL92_GRANT
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses supported in the GRANT statement, as defined in SQL-92.

The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.

The following bitmasks are used to determine which clauses are supported by the data source:

SQL_SG_DELETE_TABLE (Entry level)
SQL_SG_INSERT_COLUMN (Intermediate level)
SQL_SG_INSERT_TABLE (Entry level)
SQL_SG_REFERENCES_TABLE (Entry level)
SQL_SG_REFERENCES_COLUMN (Entry level)
SQL_SG_SELECT_TABLE (Entry level)
SQL_SG_UPDATE_COLUMN (Entry level)
SQL_SG_UPDATE_TABLE (Entry level)
SQL_SG_USAGE_ON_DOMAIN (FIPS Transitional level)
SQL_SG_USAGE_ON_CHARACTER_SET (FIPS Transitional level)
SQL_SG_USAGE_ON_COLLATION (FIPS Transitional level)
SQL_SG_USAGE_ON_TRANSLATION (FIPS Transitional level)
SQL_SG_WITH_GRANT_OPTION (Entry level)
SQL_SQL92_NUMERIC_VALUE_FUNCTIONS
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the numeric value scalar functions that are supported by the driver and the associated data source, as defined in SQL-92.

The following bitmasks are used to determine which numeric functions are supported:

SQL_SNVF_BIT_LENGTH
SQL_SNVF_CHAR_LENGTH
SQL_SNVF_CHARACTER_LENGTH
SQL_SNVF_EXTRACT
SQL_SNVF_OCTET_LENGTH
SQL_SNVF_POSITION
SQL_SQL92_PREDICATES
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the predicates supported in a SELECT statement, as defined in SQL-92.

The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.

The following bitmasks are used to determine which options are supported by the data source:

SQL_SP_BETWEEN (Entry level)
SQL_SP_COMPARISON (Entry level)
SQL_SP_EXISTS (Entry level)
SQL_SP_IN (Entry level)
SQL_SP_ISNOTNULL (Entry level)
SQL_SP_ISNULL (Entry level)
SQL_SP_LIKE (Entry level)
SQL_SP_MATCH_FULL (Full level)
SQL_SP_MATCH_PARTIAL(Full level)
SQL_SP_MATCH_UNIQUE_FULL (Full level)
SQL_SP_MATCH_UNIQUE_PARTIAL (Full level)
SQL_SP_OVERLAPS (FIPS Transitional level)
SQL_SP_QUANTIFIED_COMPARISON (Entry level)
SQL_SP_UNIQUE (Entry level)
SQL_SQL92_RELATIONAL_JOIN_OPERATORS
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the relational join operators supported in a SELECT statement, as defined in SQL-92.

The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.

The following bitmasks are used to determine which options are supported by the data source:

SQL_SRJO_CORRESPONDING_CLAUSE (Intermediate level)
SQL_SRJO_CROSS_JOIN (Full level)
SQL_SRJO_EXCEPT_JOIN (Intermediate level)
SQL_SRJO_FULL_OUTER_JOIN (Intermediate level)
SQL_SRJO_INNER_JOIN (FIPS Transitional level)
SQL_SRJO_INTERSECT_JOIN (Intermediate level)
SQL_SRJO_LEFT_OUTER_JOIN (FIPS Transitional level)
SQL_SRJO_NATURAL_JOIN (FIPS Transitional level)
SQL_SRJO_RIGHT_OUTER_JOIN (FIPS Transitional level)
SQL_SRJO_UNION_JOIN (Full level)

SQL_SRJO_INNER_JOIN indicates support for the INNER JOIN syntax, not for the inner join capability. Support for the INNER JOIN syntax is FIPS TRANSITIONAL, while support for the inner join capability is ENTRY.
SQL_SQL92_REVOKE
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the clauses supported in the REVOKE statement, as defined in SQL-92, supported by the data source.

The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.

The following bitmasks are used to determine which clauses are supported by the data source:

SQL_SR_CASCADE (FIPS Transitional level)
SQL_SR_DELETE_TABLE (Entry level)
SQL_SR_GRANT_OPTION_FOR (Intermediate level)
SQL_SR_INSERT_COLUMN (Intermediate level)
SQL_SR_INSERT_TABLE (Entry level)
SQL_SR_REFERENCES_COLUMN (Entry level)
SQL_SR_REFERENCES_TABLE (Entry level)
SQL_SR_RESTRICT (FIPS Transitional level)
SQL_SR_SELECT_TABLE (Entry level)
SQL_SR_UPDATE_COLUMN (Entry level)
SQL_SR_UPDATE_TABLE (Entry level)
SQL_SR_USAGE_ON_DOMAIN (FIPS Transitional level)
SQL_SR_USAGE_ON_CHARACTER_SET (FIPS Transitional level)
SQL_SR_USAGE_ON_COLLATION (FIPS Transitional level)
SQL_SR_USAGE_ON_TRANSLATION (FIPS Transitional level)
SQL_SQL92_ROW_VALUE_CONSTRUCTOR
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the row value constructor expressions supported in a SELECT statement, as defined in SQL-92. The following bitmasks are used to determine which options are supported by the data source:

SQL_SRVC_VALUE_EXPRESSION
SQL_SRVC_NULL
SQL_SRVC_DEFAULT
SQL_SRVC_ROW_SUBQUERY
SQL_SQL92_STRING_FUNCTIONS
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the string scalar functions that are supported by the driver and the associated data source, as defined in SQL-92.

The following bitmasks are used to determine which string functions are supported:

SQL_SSF_CONVERT
SQL_SSF_LOWER
SQL_SSF_UPPER
SQL_SSF_SUBSTRING
SQL_SSF_TRANSLATE
SQL_SSF_TRIM_BOTH
SQL_SSF_TRIM_LEADING
SQL_SSF_TRIM_TRAILING
SQL_SQL92_VALUE_EXPRESSIONS
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the value expressions supported, as defined in SQL-92.

The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.

The following bitmasks are used to determine which options are supported by the data source:

SQL_SVE_CASE (Intermediate level)
SQL_SVE_CAST (FIPS Transitional level)
SQL_SVE_COALESCE (Intermediate level)
SQL_SVE_NULLIF (Intermediate level)
SQL_STANDARD_CLI_CONFORMANCE
(ODBC 3.0) 		An SQLUINTEGER bitmask enumerating the CLI standard or standards to which the driver conforms. The following bitmasks are used to determine which levels the driver conforms to:

SQL_SCC_XOPEN_CLI_VERSION1: The driver conforms to the X/Open CLI version 1.

SQL_SCC_ISO92_CLI: The driver conforms to the ISO 92 CLI.
SQL_STATIC_CURSOR_ATTRIBUTES1
(ODBC 3.0) 		An SQLUINTEGER bitmask that describes the attributes of a static cursor that are supported by the driver. This bitmask contains the first subset of attributes; for the second subset, see SQL_STATIC_CURSOR_ATTRIBUTES2.

The following bitmasks are used to determine which attributes are supported:

SQL_CA1_NEXT
SQL_CA1_ABSOLUTE
SQL_CA1_RELATIVE
SQL_CA1_BOOKMARK
SQL_CA1_LOCK_NO_CHANGE
SQL_CA1_LOCK_EXCLUSIVE
SQL_CA1_LOCK_UNLOCK
SQL_CA1_POS_POSITION
SQL_CA1_POS_UPDATE
SQL_CA1_POS_DELETE
SQL_CA1_POS_REFRESH
SQL_CA1_POSITIONED_UPDATE
SQL_CA1_POSITIONED_DELETE
SQL_CA1_SELECT_FOR_UPDATE
SQL_CA1_BULK_ADD
SQL_CA1_BULK_UPDATE_BY_BOOKMARK
SQL_CA1_BULK_DELETE_BY_BOOKMARK
SQL_CA1_BULK_FETCH_BY_BOOKMARK

For descriptions of these bitmasks, see SQL_DYNAMIC_CURSOR_ATTRIBUTES1 (and substitute "static cursor" for "dynamic cursor" in the descriptions).

An SQL-92 Intermediate level–conformant driver will usually return the SQL_CA1_NEXT, SQL_CA1_ABSOLUTE, and SQL_CA1_RELATIVE options as supported, because the driver supports scrollable cursors through the embedded SQL FETCH statement. Because this does not directly determine the underlying SQL support, however, scrollable cursors may not be supported, even for an SQL-92 Intermediate level–conformant driver.
SQL_STATIC_CURSOR_ATTRIBUTES2
(ODBC 3.0) 		An SQLUINTEGER bitmask that describes the attributes of a static cursor that are supported by the driver. This bitmask contains the second subset of attributes; for the first subset, see SQL_STATIC_CURSOR_ATTRIBUTES1.

The following bitmasks are used to determine which attributes are supported:

SQL_CA2_READ_ONLY_CONCURRENCY
SQL_CA2_LOCK_CONCURRENCY
SQL_CA2_OPT_ROWVER_CONCURRENCY
SQL_CA2_OPT_VALUES_CONCURRENCY
SQL_CA2_SENSITIVITY_ADDITIONS
SQL_CA2_SENSITIVITY_DELETIONS
SQL_CA2_SENSITIVITY_UPDATES
SQL_CA2_MAX_ROWS_SELECT
SQL_CA2_MAX_ROWS_INSERT
SQL_CA2_MAX_ROWS_DELETE
SQL_CA2_MAX_ROWS_UPDATE
SQL_CA2_MAX_ROWS_CATALOG
SQL_CA2_MAX_ROWS_AFFECTS_ALL
SQL_CA2_CRC_EXACT
SQL_CA2_CRC_APPROXIMATE
SQL_CA2_SIMULATE_NON_UNIQUE
SQL_CA2_SIMULATE_TRY_UNIQUE
SQL_CA2_SIMULATE_UNIQUE

For descriptions of these bitmasks, see SQL_DYNAMIC_CURSOR_ATTRIBUTES2 (and substitute "static cursor" for "dynamic cursor" in the descriptions).
SQL_STRING_FUNCTIONS
(ODBC 1.0)

The information type was introduced in ODBC 1.0; each bitmask is labeled with the version in which it was introduced.

 An SQLUINTEGER bitmask enumerating the scalar string functions supported by the driver and associated data source.

The following bitmasks are used to determine which string functions are supported:

SQL_FN_STR_ASCII (ODBC 1.0)
SQL_FN_STR_BIT_LENGTH (ODBC 3.0)
SQL_FN_STR_CHAR (ODBC 1.0)
SQL_FN_STR_CHAR_
LENGTH (ODBC 3.0)
SQL_FN_STR_CHARACTER_
LENGTH (ODBC 3.0)
SQL_FN_STR_CONCAT (ODBC 1.0)
SQL_FN_STR_DIFFERENCE (ODBC 2.0)
SQL_FN_STR_INSERT (ODBC 1.0)
SQL_FN_STR_LCASE (ODBC 1.0)
SQL_FN_STR_LEFT (ODBC 1.0)
SQL_FN_STR_LENGTH (ODBC 1.0)
SQL_FN_STR_LOCATE (ODBC 1.0)
SQL_FN_STR_LTRIM (ODBC 1.0)
SQL_FN_STR_OCTET_
LENGTH (ODBC 3.0)
SQL_FN_STR_POSITION (ODBC 3.0)
SQL_FN_STR_REPEAT (ODBC 1.0)
SQL_FN_STR_REPLACE (ODBC 1.0)
SQL_FN_STR_RIGHT (ODBC 1.0)
SQL_FN_STR_RTRIM (ODBC 1.0)
SQL_FN_STR_SOUNDEX (ODBC 2.0)
SQL_FN_STR_SPACE (ODBC 2.0)
SQL_FN_STR_SUBSTRING (ODBC 1.0)
SQL_FN_STR_UCASE (ODBC 1.0)

If an application can call the LOCATE scalar function with the string_exp1, string_exp2, and start arguments, the driver returns the SQL_FN_STR_LOCATE bitmask. If an application can call the LOCATE scalar function with only the string_exp1 and string_exp2 arguments, the driver returns the SQL_FN_STR_LOCATE_2 bitmask. Drivers that fully support the LOCATE scalar function return both bitmasks.

(For more information, see String Functions in Appendix E, "Scalar Functions.")
SQL_SUBQUERIES
(ODBC 2.0) 		An SQLUINTEGER bitmask enumerating the predicates that support subqueries:

SQL_SQ_CORRELATED_SUBQUERIES
SQL_SQ_COMPARISON
SQL_SQ_EXISTS
SQL_SQ_IN
SQL_SQ_QUANTIFIED

The SQL_SQ_CORRELATED_SUBQUERIES bitmask indicates that all predicates that support subqueries support correlated subqueries.

An SQL-92 Entry level–conformant driver will always return a bitmask in which all of these bits are set.
SQL_SYSTEM_FUNCTIONS
(ODBC 1.0) 		An SQLUINTEGER bitmask enumerating the scalar system functions supported by the driver and associated data source.

The following bitmasks are used to determine which system functions are supported:

SQL_FN_SYS_DBNAME
SQL_FN_SYS_IFNULL
SQL_FN_SYS_USERNAME
SQL_TABLE_TERM
(ODBC 1.0) 		A character string with the data source vendor's name for a table; for example, "table" or "file".

This character string can be in upper, lower, or mixed case.

An SQL-92 Entry level–conformant driver will always return "table".
SQL_TIMEDATE_ADD_INTERVALS
(ODBC 2.0) 		An SQLUINTEGER bitmask enumerating the timestamp intervals supported by the driver and associated data source for the TIMESTAMPADD scalar function.

The following bitmasks are used to determine which intervals are supported:

SQL_FN_TSI_FRAC_SECOND
SQL_FN_TSI_SECOND
SQL_FN_TSI_MINUTE
SQL_FN_TSI_HOUR
SQL_FN_TSI_DAY
SQL_FN_TSI_WEEK
SQL_FN_TSI_MONTH
SQL_FN_TSI_QUARTER
SQL_FN_TSI_YEAR

An FIPS Transitional level–conformant driver will always return a bitmask in which all of these bits are set.
SQL_TIMEDATE_DIFF_INTERVALS
(ODBC 2.0) 		An SQLUINTEGER bitmask enumerating the timestamp intervals supported by the driver and associated data source for the TIMESTAMPDIFF scalar function.

The following bitmasks are used to determine which intervals are supported:

SQL_FN_TSI_FRAC_SECOND
SQL_FN_TSI_SECOND
SQL_FN_TSI_MINUTE
SQL_FN_TSI_HOUR
SQL_FN_TSI_DAY
SQL_FN_TSI_WEEK
SQL_FN_TSI_MONTH
SQL_FN_TSI_QUARTER
SQL_FN_TSI_YEAR

An FIPS Transitional level–conformant driver will always return a bitmask in which all of these bits are set.
SQL_TIMEDATE_FUNCTIONS
(ODBC 1.0)

The information type was introduced in ODBC 1.0; each bitmask is labeled with the version in which it was introduced.

 An SQLUINTEGER bitmask enumerating the scalar date and time functions supported by the driver and associated data source.

The following bitmasks are used to determine which date and time functions are supported:

SQL_FN_TD_CURRENT_DATE ODBC 3.0)
SQL_FN_TD_CURRENT_TIME (ODBC 3.0)
SQL_FN_TD_CURRENT_TIMESTAMP (ODBC 3.0)
SQL_FN_TD_CURDATE (ODBC 1.0)
SQL_FN_TD_CURTIME (ODBC 1.0)
SQL_FN_TD_DAYNAME (ODBC 2.0)
SQL_FN_TD_DAYOFMONTH (ODBC 1.0)
SQL_FN_TD_DAYOFWEEK (ODBC 1.0)
SQL_FN_TD_DAYOFYEAR (ODBC 1.0)
SQL_FN_TD_EXTRACT (ODBC 3.0)
SQL_FN_TD_HOUR (ODBC 1.0)
SQL_FN_TD_MINUTE (ODBC 1.0)
SQL_FN_TD_MONTH (ODBC 1.0)
SQL_FN_TD_MONTHNAME (ODBC 2.0)
SQL_FN_TD_NOW (ODBC 1.0)
SQL_FN_TD_QUARTER (ODBC 1.0)
SQL_FN_TD_SECOND (ODBC 1.0)
SQL_FN_TD_TIMESTAMPADD (ODBC 2.0)
SQL_FN_TD_TIMESTAMPDIFF (ODBC 2.0)
SQL_FN_TD_WEEK (ODBC 1.0)
SQL_FN_TD_YEAR (ODBC 1.0)
SQL_TXN_CAPABLE
(ODBC 1.0)

The information type was introduced in ODBC 1.0; each return value is labeled with the version in which it was introduced.

 An SQLUSMALLINT value describing the transaction support in the driver or data source:

SQL_TC_NONE = Transactions not supported. (ODBC 1.0)

SQL_TC_DML = Transactions can contain only Data Manipulation Language (DML) statements (SELECT, INSERT, UPDATE, DELETE). Data Definition Language (DDL) statements encountered in a transaction cause an error. (ODBC 1.0)

SQL_TC_DDL_COMMIT = Transactions can contain only DML statements. DDL statements (CREATE TABLE, DROP INDEX, and so on) encountered in a transaction cause the transaction to be committed. (ODBC 2.0)

SQL_TC_DDL_IGNORE = Transactions can contain only DML statements. DDL statements encountered in a transaction are ignored. (ODBC 2.0)

SQL_TC_ALL = Transactions can contain DDL statements and DML statements in any order. (ODBC 1.0)

(Because support of transactions is mandatory in SQL-92, an SQL-92 conformant driver [any level] will never return SQL_TC_NONE.)
SQL_TXN_ISOLATION_OPTION
(ODBC 1.0) 		An SQLUINTEGER bitmask enumerating the transaction isolation levels available from the driver or data source.

The following bitmasks are used in conjunction with the flag to determine which options are supported:

SQL_TXN_READ_UNCOMMITTED
SQL_TXN_READ_COMMITTED
SQL_TXN_REPEATABLE_READ
SQL_TXN_SERIALIZABLE

For descriptions of these isolation levels, see the description of SQL_DEFAULT_TXN_ISOLATION.

To set the transaction isolation level, an application calls SQLSetConnectAttr to set the SQL_ATTR_TXN_ISOLATION attribute. For more information, see SQLSetConnectAttr.

An SQL-92 Entry level–conformant driver will always return SQL_TXN_SERIALIZABLE as supported. A FIPS Transitional level–conformant driver will always return all of these options as supported.
SQL_UNION
(ODBC 2.0) 		An SQLUINTEGER bitmask enumerating the support for the UNION clause:

SQL_U_UNION = The data source supports the UNION clause.

SQL_U_UNION_ALL = The data source supports the ALL keyword in the UNION clause. (SQLGetInfo returns both SQL_U_UNION and SQL_U_UNION_ALL in this case.)

An SQL-92 Entry level–conformant driver will always return both of these options as supported.
SQL_USER_NAME
(ODBC 1.0) 		A character string with the name used in a particular database, which can be different from the login name.
SQL_XOPEN_CLI_YEAR
(ODBC 3.0) 		A character string that indicates the year of publication of the X/Open specification with which the version of the ODBC Driver Manager fully complies.

Code Example

SQLGetInfo returns lists of supported options as an SQLUINTEGER bitmask in *InfoValuePtr. The bitmask for each option is used in conjunction with the flag to determine whether the option is supported.

For example, an application could use the following code to determine whether the SUBSTRING scalar function is supported by the driver associated with the connection:

SQLUINTEGER    fFuncs;

SQLGetInfo(hdbc,
   SQL_STRING_FUNCTIONS,
   (SQLPOINTER)&fFuncs,
   sizeof(fFuncs),
   NULL);

if (fFuncs & SQL_FN_STR_SUBSTRING)   /* SUBSTRING supported */
      ;
else                                 /* SUBSTRING not supported */
      ;

Related Functions

For information about | See
Returning the setting of a connection attribute | SQLGetConnectAttr
Determining whether a driver supports a function | SQLGetFunctions
Returning the setting of a statement attribute | SQLGetStmtAttr
Returning information about a data source's data types | SQLGetTypeInfo


SQLGetStmtAttr

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLGetStmtAttr returns the current setting of a statement attribute.

Note For more information about what the Driver Manager maps this function to when an ODBC 3.x application is working with an ODBC 2.x driver, see "Mapping Replacement Functions for Backward Compatibility of Applications" in Chapter 17: Programming Considerations.

Syntax

SQLRETURN SQLGetStmtAttr(
     SQLHSTMT     StatementHandle,
     SQLINTEGER     Attribute,
     SQLPOINTER     ValuePtr,
     SQLINTEGER     BufferLength,
     SQLINTEGER *     StringLengthPtr);

Arguments

StatementHandle
[Input]
Statement handle.
Attribute
[Input]
Attribute to retrieve.
ValuePtr
[Output]
Pointer to a buffer in which to return the value of the attribute specified in Attribute.
BufferLength
[Input]
If Attribute is an ODBC-defined attribute and ValuePtr points to a character string or a binary buffer, this argument should be the length of *ValuePtr. If Attribute is an ODBC-defined attribute and *ValuePtr is an integer, BufferLength is ignored. If the value returned in *ValuePtr is a Unicode string (when calling SQLGetStmtAttrW), the BufferLength argument must be an even number

If Attribute is a driver-defined attribute, the application indicates the nature of the attribute to the Driver Manager by setting the BufferLength argument. BufferLength can have the following values:

  • If *ValuePtr is a pointer to a character string, then BufferLength is the length of the string or SQL_NTS.
  • If *ValuePtr is a pointer to a binary buffer, then the application places the result of the SQL_LEN_BINARY_ATTR(length) macro in BufferLength. This places a negative value in BufferLength.
  • If *ValuePtr is a pointer to a value other than a character string or binary string, then BufferLength should have the value SQL_IS_POINTER.
  • If *ValuePtr is contains a fixed-length data type, then BufferLength is either SQL_IS_INTEGER or SQL_IS_UINTEGER, as appropriate.
StringLengthPtr
[Output]
A pointer to a buffer in which to return the total number of bytes (excluding the null-termination character) available to return in *ValuePtr. If ValuePtr is a null pointer, no length is returned. If the attribute value is a character string, and the number of bytes available to return is greater than or equal to BufferLength, the data in *ValuePtr is truncated to BufferLength minus the length of a null-termination character and is null-terminated by the driver.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLGetStmtAttr returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLGetStmtAttr and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | The data returned in *ValuePtr was truncated to be BufferLength minus the length of a null-termination character. The length of the untruncated string value is returned in *StringLengthPtr. (Function returns SQL_SUCCESS_WITH_INFO.)
24000 | Invalid cursor state | The argument Attribute was SQL_ATTR_ROW_NUMBER and the cursor was not open, or the cursor was positioned before the start of the result set or after the end of the result set.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the argument MessageText describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY010 | Function sequence error | (DM) An asynchronously executing function was called for the StatementHandle and was still executing when this function was called. (DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) *ValuePtr is a character string, and BufferLength was less than zero, but not equal to SQL_NTS.
HY092 | Invalid attribute/option identifier | The value specified for the argument Attribute was not valid for the version of ODBC supported by the driver.
HY109 | Invalid cursor position | The Attribute argument was SQL_ATTR_ROW_NUMBER and the row had been deleted or could not be fetched.
HYC00 | Optional feature not implemented | The value specified for the argument Attribute was a valid ODBC statement attribute for the version of ODBC supported by the driver, but was not supported by the driver.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver corresponding to the StatementHandle does not support the function.

Comments

For general information about statement attributes, see "Statement Attributes" in Chapter 9: Executing Statements.

A call to SQLGetStmtAttr returns in *ValuePtr the value of the statement attribute specified in Attribute. That value can either be a 32-bit value or a null-terminated character string. If the value is a null-terminated string, the application specifies the maximum length of that string in the BufferLength argument, and the driver returns the length of that string in the *StringLengthPtr buffer. If the value is a 32-bit value, the BufferLength and StringLengthPtr arguments are not used.

To allow applications calling SQLGetStmtAttr to work with ODBC 2.x drivers, a call to SQLGetStmtAttr is mapped in the Driver Manager to SQLGetStmtOption.

The following statement attributes are read-only, so can be retrieved by SQLGetStmtAttr, but not set by SQLSetStmtAttr:

  • SQL_ATTR_IMP_PARAM_DESC
  • SQL_ATTR_IMP_ROW_DESC
  • SQL_ATTR_ROW_NUMBER

For a list of attributes that can be set and retrieved, see SQLSetStmtAttr.

Related Functions

For information about | See
Returning the setting of a connection attribute | SQLGetConnectAttr
Setting a connection attribute | SQLSetConnectAttr
Setting a statement attribute | SQLSetStmtAttr


SQLGetStmtOption

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

In ODBC 3.x, the ODBC 2.0 function SQLGetStmtOption has been replaced by SQLGetStmtAttr. For more information, see SQLGetStmtAttr.

Note For more information about what the Driver Manager maps this function to when an ODBC 2.x application is working with an ODBC 3.x driver, see "Mapping Deprecated Functions" in Appendix G: Driver Guidelines for Backward Compatibility.


SQLGetTypeInfo

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLGetTypeInfo returns information about data types supported by the data source. The driver returns the information in the form of an SQL result set. The data types are intended for use in Data Definition Language (DDL) statements.

Important Applications must use the type names returned in the TYPE_NAME column of the SQLGetTypeInfo result set in ALTER TABLE and CREATE TABLE statements. SQLGetTypeInfo may return more than one row with the same value in the DATA_TYPE column.

Syntax

SQLRETURN SQLGetTypeInfo(
     SQLHSTMT     StatementHandle,
     SQLSMALLINT     DataType);

Arguments

StatementHandle
[Input]
Statement handle for the result set.
DataType
[Input]
The SQL data type. This must be one of the values in the "SQL Data Types" section of Appendix D: Data Types, or a driver-specific SQL data type. SQL_ALL_TYPES specifies that information about all data types should be returned.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLGetTypeInfo returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLGetTypeInfo and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01S02 | Option value changed | A specified statement attribute was invalid because of implementation working conditions, so a similar value was temporarily substituted. (Call SQLGetStmtAttr to determine the temporarily substituted value.) The substitute value is valid for the StatementHandle until the cursor is closed. The statement attributes that can be changed are: SQL_ATTR_CONCURRENCY, SQL_ATTR_CURSOR_TYPE, SQL_ATTR_KEYSET_SIZE, SQL_ATTR_MAX_LENGTH, SQL_ATTR_MAX_ROWS, SQL_ATTR_QUERY_TIMEOUT, and SQL_ATTR_SIMULATE_CURSOR. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
24000 | Invalid cursor state | A cursor was open on the StatementHandle, and SQLFetch or SQLFetchScroll had been called. This error is returned by the Driver Manager if SQLFetch or SQLFetchScroll has not returned SQL_NO_DATA, and is returned by the driver if SQLFetch or SQLFetchScroll has returned SQL_NO_DATA. A result set was open on the StatementHandle, but SQLFetch or SQLFetchScroll had not been called.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function and the state of the transaction cannot be determined.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY004 | Invalid SQL data type | The value specified for the argument DataType was neither a valid ODBC SQL data type identifier nor a driver-specific data type identifier supported by the driver.
HY008 | Operation canceled | Asynchronous processing was enabled for the StatementHandle, then the function was called and, before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle. The function was called and, before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY010 | Function sequence error | (DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called. (DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HYC00 | Optional feature not implemented | The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source. The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.
HYT00 | Timeout expired | The query timeout period expired before the data source returned the result set. The timeout period is set through SQLSetStmtAttr, SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver corresponding to the StatementHandle does not support the function.

Comments

SQLGetTypeInfo returns the results as a standard result set, ordered by DATA_TYPE and then by how closely the data type maps to the corresponding ODBC SQL data type. Data types defined by the data source take precedence over user-defined data types. Consequently, the sort order is not necessarily consistent but can be generalized as DATA_TYPE first, followed by TYPE_NAME, both ascending. For example, suppose that a data source defined INTEGER and COUNTER data types, where COUNTER is auto-incrementing, and that a user-defined data type WHOLENUM has also been defined. These would be returned in the order INTEGER, WHOLENUM, and COUNTER, because WHOLENUM maps closely to the ODBC SQL data type SQL_INTEGER, while the auto-incrementing data type, even though supported by the data source, does not map closely to an ODBC SQL data type. For information about how this information might be used, see "DDL Statements" in Chapter 8: SQL Statements.

If the DataType argument specifies a data type which is valid for the version of ODBC supported by the driver, but is not supported by the driver, then it will return an empty result set.

Note For more information about the general use, arguments, and returned data of ODBC catalog functions, see Chapter 7: Catalog Functions.

The following columns have been renamed for ODBC 3.x. The column name changes do not affect backward compatibility because applications bind by column number.

ODBC 2.0 column | ODBC 3.x column
PRECISION | COLUMN_SIZE
MONEY | FIXED_PREC_SCALE
AUTO_INCREMENT | AUTO_UNIQUE_VALUE

The following columns have been added to the results set returned by SQLGetTypeInfo for ODBC 3.x:

  • SQL_DATA_TYPE
  • INTERVAL_PRECISION
  • SQL_DATETIME_SUB
  • NUM_PREC_RADIX

The following table lists the columns in the result set. Additional columns beyond column 19 (INTERVAL_PRECISION) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "Data Returned by Catalog Functions" in Chapter 7: Catalog Functions.

Note SQLGetTypeInfo might not return all data types. For example, a driver might not return user-defined data types. Applications can use any valid data type, regardless of whether it is returned by SQLGetTypeInfo.

The data types returned by SQLGetTypeInfo are those supported by the data source. They are intended for use in Data Definition Language (DDL) statements. Drivers can return result-set data using data types other than the types returned by SQLGetTypeInfo. In creating the result set for a catalog function, the driver might use a data type that is not supported by the data source.

Column name 		Column
number
Data type
Comments
TYPE_NAME
(ODBC 2.0) 		1 Varchar
not NULL 		Data source–dependent data-type name; for example, "CHAR()", "VARCHAR()", "MONEY", "LONG VARBINARY", or "CHAR ( ) FOR BIT DATA". Applications must use this name in CREATE TABLE and ALTER TABLE statements.
DATA_TYPE
(ODBC 2.0) 		2 Smallint
not NULL 		SQL data type. This can be an ODBC SQL data type or a driver-specific SQL data type. For datetime or interval data types, this column returns the concise data type (such as SQL_TYPE_TIME or SQL_INTERVAL_YEAR_TO_MONTH). For a list of valid ODBC SQL data types, see "SQL Data Types" in Appendix D: Data Types. For information about driver-specific SQL data types, see the driver’s documentation.
COLUMN_SIZE
(ODBC 2.0) 		3 Integer The maximum column size that the server supports for this data type. For numeric data, this is the maximum precision. For string data, this is the length in characters. For datetime data types, this is the length in characters of the string representation (assuming the maximum allowed precision of the fractional seconds component). NULL is returned for data types where column size is not applicable. For interval data types, this is the number of characters in the character representation of the interval literal (as defined by the interval leading precision; see "Interval Data Type Length" in Appendix D: Data Types).

For more information on column size, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" in Appendix D: Data Types.
LITERAL_PREFIX
(ODBC 2.0) 		4 Varchar Character or characters used to prefix a literal; for example, a single quotation mark (') for character data types or 0x for binary data types; NULL is returned for data types where a literal prefix is not applicable.
LITERAL_SUFFIX
(ODBC 2.0) 		5 Varchar Character or characters used to terminate a literal; for example, a single quotation mark (') for character data types; NULL is returned for data types where a literal suffix is not applicable.
CREATE_PARAMS
(ODBC 2.0) 		6 Varchar A list of keywords, separated by commas, corresponding to each parameter that the application may specify in parentheses when using the name that is returned in the TYPE_NAME field. The keywords in the list can be any of the following: length, precision, or scale. They appear in the order that the syntax requires them to be used. For example, CREATE_PARAMS for DECIMAL would be "precision,scale"; CREATE_PARAMS for VARCHAR would equal "length." NULL is returned if there are no parameters for the data type definition; for example, INTEGER.

The driver supplies the CREATE_PARAMS text in the language of the country where it is used.
NULLABLE
(ODBC 2.0) 		7 Smallint
not NULL 		Whether the data type accepts a NULL value:

SQL_NO_NULLS if the data type does not accept NULL values.

SQL_NULLABLE if the data type accepts NULL values.

SQL_NULLABLE_UNKNOWN if it is not known whether the column accepts NULL values.
CASE_SENSITIVE
(ODBC 2.0) 		8 Smallint
not NULL 		Whether a character data type is case-sensitive in collations and comparisons:

SQL_TRUE if the data type is a character data type and is case-sensitive.

SQL_FALSE if the data type is not a character data type or is not case-sensitive.
SEARCHABLE
(ODBC 2.0) 		9 Smallint
not NULL 		How the data type is used in a WHERE clause:

SQL_PRED_NONE if the column cannot be used in a WHERE clause. (This is the same as the SQL_UNSEARCHABLE value in ODBC 2.x.)

SQL_PRED_CHAR if the column can be used in a WHERE clause, but only with the LIKE predicate. (This is the same as the SQL_LIKE_ONLY value in ODBC 2.x.)

SQL_PRED_BASIC if the column can be used in a WHERE clause with all the comparison operators except LIKE (comparison, quantified comparison, BETWEEN, DISTINCT, IN, MATCH, and UNIQUE). (This is the same as the SQL_ALL_EXCEPT_LIKE value in ODBC 2.x.)

SQL_SEARCHABLE if the column can be used in a WHERE clause with any comparison operator.
UNSIGNED_ATTRIBUTE
(ODBC 2.0) 		10 Smallint Whether the data type is unsigned:

SQL_TRUE if the data type is unsigned.

SQL_FALSE if the data type is signed.

NULL is returned if the attribute is not applicable to the data type or the data type is not numeric.
FIXED_PREC_SCALE
(ODBC 2.0) 		11 Smallint
not NULL 		Whether the data type has predefined fixed precision and scale (which are data source–specific), such as a money data type:

SQL_TRUE if it has predefined fixed precision and scale.

SQL_FALSE if it does not have predefined fixed precision and scale.
AUTO_UNIQUE_VALUE
(ODBC 2.0) 		12 Smallint Whether the data type is autoincrementing:

SQL_TRUE if the data type is autoincrementing.

SQL_FALSE if the data type is not autoincrementing.

NULL is returned if the attribute is not applicable to the data type or the data type is not numeric.

An application can insert values into a column having this attribute, but typically cannot update the values in the column.

When an insert is made into an auto-increment column, a unique value is inserted into the column at insert time. The increment is not defined, but is data source–specific. An application should not assume that an auto-increment column starts at any particular point or increments by any particular value.
LOCAL_TYPE_NAME
(ODBC 2.0) 		13 Varchar Localized version of the data source–dependent name of the data type. NULL is returned if a localized name is not supported by the data source. This name is intended for display only, such as in dialog boxes.
MINIMUM_SCALE
(ODBC 2.0) 		14 Smallint The minimum scale of the data type on the data source. If a data type has a fixed scale, the MINIMUM_SCALE and MAXIMUM_SCALE columns both contain this value. For example, an SQL_TYPE_TIMESTAMP column might have a fixed scale for fractional seconds. NULL is returned where scale is not applicable. For more information, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" in Appendix D: Data Types.
MAXIMUM_SCALE
(ODBC 2.0) 		15 Smallint The maximum scale of the data type on the data source. NULL is returned where scale is not applicable. If the maximum scale is not defined separately on the data source, but is instead defined to be the same as the maximum precision, this column contains the same value as the COLUMN_SIZE column. For more information, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" in Appendix D: Data Types.
SQL_DATA_TYPE
(ODBC 3.0) 		16 Smallint NOT NULL The value of the SQL data type as it appears in the SQL_DESC_TYPE field of the descriptor. This column is the same as the DATA_TYPE column, except for interval and datetime data types.

For interval and datetime data types, the SQL_DATA_TYPE field in the result set will return SQL_INTERVAL or SQL_DATETIME, and the SQL_DATETIME_SUB field will return the subcode for the specific interval or datetime data type. (See Appendix D: Data Types.)

SQL_DATETIME_SUB
(ODBC 3.0) 		17 Smallint When the value of SQL_DATA_TYPE is SQL_DATETIME or SQL_INTERVAL, this column contains the datetime/interval subcode. For data types other than datetime and interval, this field is NULL.

For interval or datetime data types, the SQL_DATA_TYPE field in the result set will return SQL_INTERVAL or SQL_DATETIME, and the SQL_DATETIME_SUB field will return the subcode for the specific interval or datetime data type. (See Appendix D: Data Types.)
NUM_PREC_RADIX
(ODBC 3.0) 		18 Integer If the data type is an approximate numeric type, this column contains the value 2 to indicate that COLUMN_SIZE specifies a number of bits. For exact numeric types, this column contains the value 10 to indicate that COLUMN_SIZE specifies a number of decimal digits. Otherwise, this column is NULL.
INTERVAL_PRECISION
(ODBC 3.0) 		19 Smallint If the data type is an interval data type, then this column contains the value of the interval leading precision. (See "Interval Data Type Precision" in Appendix D: Data Types.) Otherwise, this column is NULL.

Attribute information can apply to data types or to specific columns in a result set. SQLGetTypeInfo returns information about attributes associated with data types; SQLColAttribute returns information about attributes associated with columns in a result set.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Returning information about a column in a result set | SQLColAttribute
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Fetching a single row or a block of data in a forward-only direction | SQLFetch
Returning information about a driver or data source | SQLGetInfo


SQLMoreResults

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ODBC

Summary

SQLMoreResults determines whether more results are available on a statement containing SELECT, UPDATE, INSERT, or DELETE statements and, if so, initializes processing for those results.

Syntax

SQLRETURN SQLMoreResults(
     SQLHSTMT     StatementHandle);

Arguments

StatementHandle
[Input]
Statement handle.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_NO_DATA, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLMoreResults returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLMoreResults and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01S02 | Option value has changed | The value of a statement attribute changed as the batch was being processed. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function and the state of the transaction cannot be determined.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY008 | Operation canceled | Asynchronous processing was enabled for the StatementHandle. The function was called and, before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle. The function was called and, before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY010 | Function sequence error | (DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called. (DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

SELECT statements return result sets. UPDATE, INSERT, and DELETE statements return a count of affected rows. If any of these statements are batched, submitted with arrays of parameters (numbered in increasing parameter order, in the order that they appear in the batch), or in procedures, they can return multiple result sets or row counts. For information about batches of statements and arrays of parameters, see "Batches of SQL Statements" and "Arrays of Parameter Values" in Chapter 9: Executing Statements.

After executing the batch, the application is positioned on the first result set. The application can call SQLBindCol, SQLBulkOperations, SQLFetch, SQLGetData, SQLFetchScroll, SQLSetPos, and all the metadata functions, on the first or any subsequent result sets, just as it would if there were just a single result set. Once it is done with the first result set, the application calls SQLMoreResults to move to the next result set. If another result set or count is available, SQLMoreResults returns SQL_SUCCESS and initializes the result set or count for additional processing. If any row count–generating statements appear in between result set–generating statements, they can be stepped over by calling SQLMoreResults. After calling SQLMoreResults for UPDATE, INSERT, or DELETE statements, an application can call SQLRowCount.

If there was a current result set with unfetched rows, SQLMoreResults discards that result set and makes the next result set or count available. If all results have been processed, SQLMoreResults returns SQL_NO_DATA. For some drivers, output parameters and return values are not available until all result sets and row counts have been processed. For such drivers, output parameters and return values become available when SQLMoreResults returns SQL_NO_DATA.

Any bindings that were established for the previous result set still remain valid. If the column structures are different for this result set, then calling SQLFetch or SQLFetchScroll may result in an error or truncation. To prevent this, the application has to call SQLBindCol to explicitly rebind as appropriate (or do so by setting descriptor fields). Alternatively, the application can call SQLFreeStmt with an Option of SQL_UNBIND to unbind all the column buffers.

The values of statement attributes, such as cursor type, cursor concurrency, keyset size, or maximum length, may change as the application navigates through the batch by calls to SQLMoreResults. If this happens, SQLMoreResults will return SQL_SUCCESS_WITH_INFO and SQLSTATE 01S02 (Option value has changed).

Calling SQLCloseCursor, or SQLFreeStmt with an Option of SQL_CLOSE, discards all the result sets and row counts that were available as a result of the execution of the batch. The statement handle returns to either the allocated or prepared state. Calling SQLCancel to cancel an asynchronously executing function when a batch has been executed and the statement handle is in the executed, cursor-positioned, or asynchronous state results in all the results sets and row counts generated by the batch being discarded if the cancel call was successful. The statement then returns to the prepared or allocated state.

If a batch of statements or a procedure mixes other SQL statements with SELECT, UPDATE, INSERT, and DELETE statements, these other statements do not affect SQLMoreResults.

For more information, see "Multiple Results" in Chapter 11: Retrieving Results (Advanced).

If a searched update or delete statement in a batch of statements does not affect any rows at the data source, SQLMoreResults returns SQL_SUCCESS. This is different from the case of a searched update or delete statement that is executed through SQLExecDirect, SQLExecute, or SQLParamData, which returns SQL_NO_DATA if it does not affect any rows at the data source. If an application calls SQLRowCount to retrieve the row count after a call to SQLMoreResults has not affected any rows, SQLRowCount will return SQL_NO_DATA.

For additional information about the valid sequencing of result-processing functions, see Appendix B: ODBC State Transition Tables.

Availability of Row Counts

When a batch contains multiple consecutive row count–generating statements, it is possible that these row counts are rolled up into just one row count. For example, if a batch has five insert statements, then certain data sources are capable of returning five individual row counts. Certain other data sources return only one row count that represents the sum of the five individual row counts.

When a batch contains a combination of result set–generating and row count–generating statements, row counts may or may not be available at all. The behavior of the driver with respect to the availability of row counts is enumerated in the SQL_BATCH_ROW_COUNT information type available through a call to SQLGetInfo. For example, suppose that the batch contains a SELECT, followed by two INSERTs and another SELECT. Then the following cases are possible:

  • The row counts corresponding to the two INSERT statements are not available at all. The first call to SQLMoreResults will position you on the result set of the second SELECT statement.
  • The row counts corresponding to the two INSERT statements are available individually. (A call to SQLGetInfo does not return the SQL_BRC_ROLLED_UP bit for the SQL_BATCH_ROW_COUNT information type.) The first call to SQLMoreResults will position you on the row count of the first INSERT, and the second call will position you on the row count of the second INSERT. The third call to SQLMoreResults will position you on the result set of the second SELECT statement.
  • The row counts corresponding to the two INSERTs are rolled up into one single row count that is available. (A call to SQLGetInfo returns the SQL_BRC_ROLLED_UP bit for the SQL_BATCH_ROW_COUNT information type.) The first call to SQLMoreResults will position you on the rolled-up row count, and the second call to SQLMoreResults will position you on the result set of the second SELECT.

Certain drivers make row counts available only for explicit batches and not for stored procedures.

Related Functions

For information about | See
Canceling statement processing | SQLCancel
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Fetching a single row or a block of data in a forward-only direction | SQLFetch
Fetching part or all of a column of data | SQLGetData


SQLNativeSql

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ODBC

Summary

SQLNativeSql returns the SQL string as modified by the driver. SQLNativeSql does not execute the SQL statement.

Syntax

SQLRETURN SQLNativeSql(
     SQLHDBC     ConnectionHandle,
     SQLCHAR *     InStatementText,
     SQLINTEGER     TextLength1,
     SQLCHAR *     OutStatementText,
     SQLINTEGER     BufferLength,
     SQLINTEGER *     TextLength2Ptr);

Arguments

ConnectionHandle
[Input]
Connection handle.
InStatementText
[Input]
SQL text string to be translated.
TextLength1
[Input]
Length of *InStatementText text string.
OutStatementText
[Output]
Pointer to a buffer in which to return the translated SQL string.
BufferLength
[Input]
Length of the *OutStatementText buffer. If the value returned in *InStatementText is a Unicode string (when calling SQLNativeSqlW), the BufferLength argument must be an even number.
TextLength2Ptr
[Output]
Pointer to a buffer in which to return the total number of bytes (excluding the null-termination byte) available to return in *OutStatementText. If the number of bytes available to return is greater than or equal to BufferLength, the translated SQL string in *OutStatementText is truncated to BufferLength minus the length of a null-termination character.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLNativeSql returns either SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_DBC and a Handle of ConnectionHandle. The following table lists the SQLSTATE values commonly returned by SQLNativeSql and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | The buffer *OutStatementText was not large enough to return the entire SQL string, so the SQL string was truncated. The length of the untruncated SQL string is returned in *TextLength2Ptr. (Function returns SQL_SUCCESS_WITH_INFO.)
08003 | Connection does not exist | The ConnectionHandle was not in a connected state.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
22007 | Invalid datetime format | *InStatementText contained an escape clause with an invalid date, time, or timestamp value.
24000 | Invalid cursor state | The cursor referred to in the statement was positioned before the start of the result set or after the end of the result set. This error may not be returned by a driver having a native DBMS cursor implementation.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY009 | Invalid use of null pointer | (DM) *InStatementText was a null pointer.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) The argument TextLength1 was less than 0, but not equal to SQL_NTS.
 |  | (DM) The argument BufferLength was less than 0 and the argument OutStatementText was not a null pointer.
HY109 | Invalid cursor position | The current row of the cursor had been deleted or had not been fetched. This error may not be returned by a driver having a native DBMS cursor implementation.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the ConnectionHandle does not support the function.

Comments

The following are examples of what SQLNativeSql might return for the following input SQL string containing the scalar function CONVERT. Assume that the column empid is of type INTEGER in the data source:

SELECT { fn CONVERT (empid, SQL_SMALLINT) } FROM employee

A driver for Microsoft SQL Server might return the following translated SQL string:

SELECT convert (smallint, empid) FROM employee

A driver for ORACLE Server might return the following translated SQL string:

SELECT to_number (empid) FROM employee

A driver for Ingres might return the following translated SQL string:

SELECT int2 (empid) FROM employee

For more information, see "Direct Execution" and "Prepared Execution" in Chapter 9: Executing Statements.

Related Functions

None.


SQLNumParams

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLNumParams returns the number of parameters in an SQL statement.

Syntax

SQLRETURN SQLNumParams(
     SQLHSTMT     StatementHandle,
     SQLSMALLINT *     ParameterCountPtr);

Arguments

StatementHandle
[Input]
Statement handle.
ParameterCountPtr
[Output]
Pointer to a buffer in which to return the number of parameters in the statement.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLNumParams returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLNumParams and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called and, before it completed execution, SQLCancel was called on the StatementHandle; the function was then called again on the StatementHandle.

The function was called and, before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY010 Function sequence error (DM) The function was called prior to calling SQLPrepare or SQLExecDirect for the StatementHandle.

(DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

SQLNumParams can be called only after SQLPrepare has been called.

If the statement associated with StatementHandle does not contain parameters, SQLNumParams sets *ParameterCountPtr to 0.

The number of parameters returned by SQLNumParams is the same value as the SQL_DESC_COUNT field of the IPD.

For more information, see "Describing Parameters" in Chapter 9: Executing Statements.

Related Functions

For information about | See
Binding a buffer to a parameter | SQLBindParameter
Returning information about a parameter in a statement | SQLDescribeParam


SQLNumResultCols

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLNumResultCols returns the number of columns in a result set.

Syntax

SQLRETURN SQLNumResultCols(
     SQLHSTMT     StatementHandle,
     SQLSMALLINT *     ColumnCountPtr);

Arguments

StatementHandle
[Input]
Statement handle.
ColumnCountPtr
[Output]
Pointer to a buffer in which to return the number of columns in the result set. This count does not include a bound bookmark column.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLNumResultCols returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLNumResultCols and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle; the function was then called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY010 Function sequence error (DM) The function was called prior to calling SQLPrepare or SQLExecDirect for the StatementHandle.

(DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

SQLNumResultCols can return any SQLSTATE that can be returned by SQLPrepare or SQLExecute when called after SQLPrepare and before SQLExecute, depending on when the data source evaluates the SQL statement associated with the statement.

Comments

SQLNumResultCols can be called successfully only when the statement is in the prepared, executed, or positioned state.

If the statement associated with StatementHandle does not return columns, SQLNumResultCols sets *ColumnCountPtr to 0.

The number of columns returned by SQLNumResultCols is the same value as the SQL_DESC_COUNT field of the IRD.

For more information, see "Was a Result Set Created?" and "How is Metadata Used?" in Chapter 10: Retrieving Results (Basic).

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Returning information about a column in a result set | SQLColAttribute
Returning information about a column in a result set | SQLDescribeCol
Executing an SQL statement | SQLExecDirect
Executing a prepared SQL statement | SQLExecute
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Fetching a single row or a block of data in a forward-only direction | SQLFetch
Fetching part or all of a column of data | SQLGetData
Preparing an SQL statement for execution | SQLPrepare
Setting cursor scrolling options | SQLSetStmtAttr


SQLParamData

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLParamData is used in conjunction with SQLPutData to supply parameter data at statement execution time.

Syntax

SQLRETURN SQLParamData(
     SQLHSTMT     StatementHandle,
     SQLPOINTER *     ValuePtrPtr);

Arguments

StatementHandle
[Input]
Statement handle.
ValuePtrPtr
[Output]
Pointer to a buffer in which to return the address of the ParameterValuePtr buffer specified in SQLBindParameter (for parameter data) or the address of the TargetValuePtr buffer specified in SQLBindCol (for column data), as contained in the SQL_DESC_DATA_PTR descriptor record field.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NEED_DATA, SQL_NO_DATA, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLParamData returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLParamData and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
07006 Restricted data type attribute violation The data value identified by the ValueType argument in SQLBindParameter for the bound parameter could not be converted to the data type identified by the ParameterType argument in SQLBindParameter.

The data value returned for a parameter bound as SQL_PARAM_INPUT_OUTPUT or SQL_PARAM_OUTPUT could not be converted to the data type identified by the ValueType argument in SQLBindParameter.

(If the data values for one or more rows could not be converted, but one or more rows were successfully returned, this function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
22026 String data, length mismatch The SQL_NEED_LONG_DATA_LEN information type in SQLGetInfo was "Y", and less data was sent for a long parameter (the data type was SQL_LONGVARCHAR, SQL_LONGVARBINARY, or a long data source–specific data type) than was specified with the StrLen_or_IndPtr argument in SQLBindParameter.

The SQL_NEED_LONG_DATA_LEN information type in SQLGetInfo was "Y", and less data was sent for a long column (the data type was SQL_LONGVARCHAR, SQL_LONGVARBINARY, or a long data source–specific data type) than was specified in the length buffer corresponding to a column in a row of data that was added or updated with SQLBulkOperations or updated with SQLSetPos.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle; the function was then called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY010 Function sequence error (DM) The previous function call was not a call to SQLExecDirect, SQLExecute, SQLBulkOperations, or SQLSetPos where the return code was SQL_NEED_DATA, or the previous function call was a call to SQLPutData.

The previous function call was a call to SQLParamData.

(DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. SQLCancel was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver that corresponds to the StatementHandle does not support the function.

If SQLParamData is called while sending data for a parameter in an SQL statement, it can return any SQLSTATE that can be returned by the function called to execute the statement (SQLExecute or SQLExecDirect). If it is called while sending data for a column being updated or added with SQLBulkOperations or being updated with SQLSetPos, it can return any SQLSTATE that can be returned by SQLBulkOperations or SQLSetPos.

Comments

SQLParamData can be called to supply data-at-execution data for two uses: parameter data to be used in a call to SQLExecute or SQLExecDirect, or column data to be used when a row is updated or added by a call to SQLBulkOperations or updated by a call to SQLSetPos. At execution time, SQLParamData returns to the application an indicator of which data the driver requires.

When an application calls SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos, the driver returns SQL_NEED_DATA if it needs data-at-execution data. An application then calls SQLParamData to determine which data to send. If the driver requires parameter data, the driver returns in the *ValuePtrPtr output buffer the value that the application placed in the rowset buffer. The application can use this value to determine which parameter data the driver is requesting. If the driver requires column data, the driver returns in the *ValuePtrPtr buffer the address that the column was originally bound to, in the following manner:

Bound Address + Binding Offset + ((Row Number – 1) x Element Size)

where the variables are defined as indicated in the following table.

Variable | Description
Bound Address | The address specified with the TargetValuePtr argument in SQLBindCol.
Binding Offset | The value stored at the address specified with the SQL_ATTR_ROW_BIND_OFFSET_PTR statement attribute.
Row Number | The 1-based number of the row in the rowset. For single-row fetches, which are the default, this is 1.
Element Size | The value of the SQL_ATTR_ROW_BIND_TYPE statement attribute for both data and length/indicator buffers.

It also returns SQL_NEED_DATA, which is an indicator to the application that it should call SQLPutData to send the data.

The application calls SQLPutData as many times as necessary to send the data-at-execution data for the column or parameter. After all of the data has been sent for the column or parameter, the application calls SQLParamData again. If SQLParamData again returns SQL_NEED_DATA, data needs to be sent for another parameter or column, so the application again calls SQLPutData. If all data-at-execution data has been sent for all parameters or columns, then SQLParamData returns SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the value in *ValuePtrPtr is undefined, and the SQL statement can be executed or the SQLBulkOperations or SQLSetPos call can be processed.

If SQLParamData supplies parameter data for a searched update or delete statement that does not affect any rows at the data source, the call to SQLParamData returns SQL_NO_DATA.

For more information on how data-at-execution parameter data is passed at statement execution time, see "Passing Parameter Values" in SQLBindParameter and "Sending Long Data" in Chapter 9: Executing Statements. For more information on how data-at-execution column data is updated or added, see the section "Using SQLSetPos" in SQLSetPos, "Performing Bulk Updates Using Bookmarks" in SQLBulkOperations, and "Long Data and SQLSetPos and SQLBulkOperations" in Chapter 12: Updating Data.

Code Example

See SQLPutData.

Related Functions

For information about | See
Binding a buffer to a parameter | SQLBindParameter
Canceling statement processing | SQLCancel
Returning information about a parameter in a statement | SQLDescribeParam
Executing an SQL statement | SQLExecDirect
Executing a prepared SQL statement | SQLExecute
Sending parameter data at execution time | SQLPutData


SQLParamOptions

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

The ODBC 2.0 function SQLParamOptions has been replaced in ODBC 3.x by calls to SQLSetStmtAttr.

Note For more information about what the Driver Manager maps this function to when an ODBC 2.x application is working with an ODBC 3.x driver, see "Mapping Deprecated Functions" in Appendix G: Driver Guidelines for Backward Compatibility.


SQLPrepare

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLPrepare prepares an SQL string for execution.

Syntax

SQLRETURN SQLPrepare(
     SQLHSTMT     StatementHandle,
     SQLCHAR *     StatementText,
     SQLINTEGER     TextLength);

Arguments

StatementHandle
[Input]
Statement handle.
StatementText
[Input]
SQL text string.
TextLength
[Input]
Length of *StatementText.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLPrepare returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLPrepare and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01S02 Option value changed A specified statement attribute was invalid because of implementation working conditions, so a similar value was temporarily substituted. (SQLGetStmtAttr can be called to determine what the temporarily substituted value is.) The substitute value is valid for the StatementHandle until the cursor is closed. The statement attributes that can be changed are: SQL_ATTR_CONCURRENCY
SQL_ATTR_CURSOR_TYPE
SQL_ATTR_KEYSET_SIZE
SQL_ATTR_MAX_LENGTH
SQL_ATTR_MAX_ROWS
SQL_ATTR_QUERY_TIMEOUT
SQL_ATTR_SIMULATE_CURSOR

(Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
21S01 | Insert value list does not match column list | *StatementText contained an INSERT statement, and the number of values to be inserted did not match the degree of the derived table.
21S02 | Degree of derived table does not match column list | *StatementText contained a CREATE VIEW statement, and the number of names specified is not the same degree as the derived table defined by the query specification.
22018 | Invalid character value for cast specification | *StatementText contained an SQL statement that contained a literal or parameter, and the value was incompatible with the data type of the associated table column.
22019 | Invalid escape character | The argument StatementText contained a LIKE predicate with an ESCAPE in the WHERE clause, and the length of the escape character following ESCAPE was not equal to 1.
22025 | Invalid escape sequence | The argument StatementText contained "LIKE pattern value ESCAPE escape character" in the WHERE clause, and the character following the escape character in the pattern value was neither "%" nor "_".
24000 Invalid cursor state (DM) A cursor was open on the StatementHandle, and SQLFetch or SQLFetchScroll had been called.

A cursor was open on the StatementHandle, but SQLFetch or SQLFetchScroll had not been called.
34000 | Invalid cursor name | *StatementText contained a positioned DELETE or a positioned UPDATE, and the cursor referenced by the statement being prepared was not open.
3D000 | Invalid catalog name | The catalog name specified in StatementText was invalid.
3F000 | Invalid schema name | The schema name specified in StatementText was invalid.
42000 Syntax error or access violation *StatementText contained an SQL statement that was not preparable or contained a syntax error.

*StatementText contained a statement for which the user did not have the required privileges.
42S01 | Base table or view already exists | *StatementText contained a CREATE TABLE or CREATE VIEW statement, and the table name or view name specified already exists.
42S02 Base table or view not found *StatementText contained a DROP TABLE or a DROP VIEW statement, and the specified table name or view name did not exist.

*StatementText contained an ALTER TABLE statement, and the specified table name did not exist.

*StatementText contained a CREATE VIEW statement, and a table name or view name defined by the query specification did not exist.

*StatementText contained a CREATE INDEX statement, and the specified table name did not exist.

*StatementText contained a GRANT or REVOKE statement, and the specified table name or view name did not exist.

*StatementText contained a SELECT statement, and a specified table name or view name did not exist.

*StatementText contained a DELETE, INSERT, or UPDATE statement, and the specified table name did not exist.

*StatementText contained a CREATE TABLE statement, and a table specified in a constraint (referencing a table other than the one being created) did not exist.
42S11 | Index already exists | *StatementText contained a CREATE INDEX statement, and the specified index name already existed.
42S12 | Index not found | *StatementText contained a DROP INDEX statement, and the specified index name did not exist.
42S21 | Column already exists | *StatementText contained an ALTER TABLE statement, and the column specified in the ADD clause is not unique or identifies an existing column in the base table.
42S22 Column not found *StatementText contained a CREATE INDEX statement, and one or more of the column names specified in the column list did not exist.

*StatementText contained a GRANT or REVOKE statement, and a specified column name did not exist.

*StatementText contained a SELECT, DELETE, INSERT, or UPDATE statement, and a specified column name did not exist.

*StatementText contained a CREATE TABLE statement, and a column specified in a constraint (referencing a table other than the one being created) did not exist.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle, and then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY009 | Invalid use of null pointer | (DM) StatementText was a null pointer.
HY010 Function sequence error (DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) The argument TextLength was less than or equal to 0 but not equal to SQL_NTS.
HYC00 Optional feature not implemented The concurrency setting was invalid for the type of cursor defined.

The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.
HYT00 | Timeout expired | The timeout period expired before the data source returned the result set. The timeout period is set through SQLSetStmtAttr, SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

The application calls SQLPrepare to send an SQL statement to the data source for preparation. For more information about prepared execution, see "Prepared Execution" in Chapter 9: Executing Statements. The application can include one or more parameter markers in the SQL statement. To include a parameter marker, the application embeds a question mark (?) into the SQL string at the appropriate position. For information about parameters, see "Statement Parameters" in Chapter 9: Executing Statements.

Note If an application uses SQLPrepare to prepare and SQLExecute to submit a COMMIT or ROLLBACK statement, it will not be interoperable between DBMS products. To commit or roll back a transaction, call SQLEndTran.

The driver can modify the statement to use the form of SQL used by the data source and then submit it to the data source for preparation. In particular, the driver modifies the escape sequences used to define SQL syntax for certain features. (For a description of SQL statement grammar, see "Escape Sequences in ODBC" in Chapter 8: SQL Statements, and Appendix C: SQL Grammar.) For the driver, a statement handle is similar to a statement identifier in embedded SQL code. If the data source supports statement identifiers, the driver can send a statement identifier and parameter values to the data source.

Once a statement is prepared, the application uses the statement handle to refer to the statement in later function calls. The prepared statement associated with the statement handle can be reexecuted by calling SQLExecute until the application frees the statement with a call to SQLFreeStmt with the SQL_DROP option or until the statement handle is used in a call to SQLPrepare, SQLExecDirect, or one of the catalog functions (SQLColumns, SQLTables, and so on). Once the application prepares a statement, it can request information about the format of the result set. For some implementations, calling SQLDescribeCol or SQLDescribeParam after SQLPrepare might not be as efficient as calling the function after SQLExecute or SQLExecDirect.

Some drivers cannot return syntax errors or access violations when the application calls SQLPrepare. A driver can handle syntax errors and access violations, only syntax errors, or neither syntax errors nor access violations. Therefore, an application must be able to handle these conditions when calling subsequent related functions such as SQLNumResultCols, SQLDescribeCol, SQLColAttribute, and SQLExecute.

Depending on the capabilities of the driver and data source, parameter information (such as data types) might be checked when the statement is prepared (if all parameters have been bound) or when it is executed (if all parameters have not been bound). For maximum interoperability, an application should unbind all parameters that applied to an old SQL statement before preparing a new SQL statement on the same statement. This prevents errors that are due to old parameter information being applied to the new statement.

Important Committing a transaction, either by explicitly calling SQLEndTran or by working in autocommit mode, can cause the data source to delete the access plans for all statements on a connection. For more information, see the SQL_CURSOR_COMMIT_BEHAVIOR and SQL_CURSOR_ROLLBACK_BEHAVIOR information types in SQLGetInfo and "Effect of Transactions on Cursors and Prepared Statements" in Chapter 14: Transactions.

Code Example

See SQLBindParameter, SQLPutData, and SQLSetPos.

Related Functions

For information about | See
Allocating a statement handle | SQLAllocHandle
Binding a buffer to a column in a result set | SQLBindCol
Binding a buffer to a parameter | SQLBindParameter
Canceling statement processing | SQLCancel
Executing a commit or rollback operation | SQLEndTran
Executing an SQL statement | SQLExecDirect
Executing a prepared SQL statement | SQLExecute
Returning the number of rows affected by a statement | SQLRowCount
Setting a cursor name | SQLSetCursorName


SQLPrimaryKeys

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ODBC

Summary

SQLPrimaryKeys returns the column names that make up the primary key for a table. The driver returns the information as a result set. This function does not support returning primary keys from multiple tables in a single call.

Syntax

SQLRETURN SQLPrimaryKeys(
     SQLHSTMT     StatementHandle,
     SQLCHAR *     CatalogName,
     SQLSMALLINT     NameLength1,
     SQLCHAR *     SchemaName,
     SQLSMALLINT     NameLength2,
     SQLCHAR *     TableName,
     SQLSMALLINT     NameLength3);

Arguments

StatementHandle
[Input]
Statement handle.
CatalogName
[Input]
Catalog name. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have catalogs. CatalogName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, CatalogName is treated as an identifier and its case is not significant. If it is SQL_FALSE, CatalogName is an ordinary argument; it is treated literally, and its case is significant. For more information, see "Arguments in Catalog Functions" in Chapter 7: Catalog Functions.

NameLength1
[Input]
Length in bytes of *CatalogName.
SchemaName
[Input]
Schema name. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have schemas. SchemaName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, SchemaName is treated as an identifier and its case is not significant. If it is SQL_FALSE, SchemaName is an ordinary argument; it is treated literally, and its case is not significant.

NameLength2
[Input]
Length in bytes of *SchemaName.
TableName
[Input]
Table name. This argument cannot be a null pointer. TableName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, TableName is treated as an identifier and its case is not significant. If it is SQL_FALSE, TableName is an ordinary argument; it is treated literally, and its case is not significant.

NameLength3
[Input]
Length in bytes of *TableName.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLPrimaryKeys returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLPrimaryKeys and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
24000 Invalid cursor state (DM) A cursor was open on the StatementHandle, and SQLFetch or SQLFetchScroll had been called.

A cursor was open on the StatementHandle, but SQLFetch or SQLFetchScroll had not been called.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY009 Invalid use of null pointer (DM) The TableName argument was a null pointer.

The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, the CatalogName argument was a null pointer, and SQLGetInfo with the SQL_CATALOG_NAME information type returns that catalog names are supported.

(DM) The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, and the SchemaName argument was a null pointer.
HY010 Function sequence error (DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 Invalid string or buffer length (DM) The value of one of the name length arguments was less than 0 but not equal to SQL_NTS, and the associated name argument is not a null pointer.

The value of one of the name length arguments exceeded the maximum length value for the corresponding name.
HYC00 Optional feature not implemented A catalog was specified, and the driver or data source does not support catalogs.

A schema was specified and the driver or data source does not support schemas.

The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source.

The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.
HYT00 | Timeout expired | The timeout period expired before the data source returned the requested result set. The timeout period is set through SQLSetStmtAttr, SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

SQLPrimaryKeys returns the results as a standard result set, ordered by TABLE_CAT, TABLE_SCHEM, TABLE_NAME, and KEY_SEQ. For information about how this information might be used, see "Uses of Catalog Data" in Chapter 7: Catalog Functions.

The following columns have been renamed for ODBC 3.x. The column name changes do not affect backward compatibility because applications bind by column number.

ODBC 2.0 column | ODBC 3.x column
TABLE_QUALIFIER | TABLE_CAT
TABLE_OWNER | TABLE_SCHEM

To determine the actual lengths of the TABLE_CAT, TABLE_SCHEM, TABLE_NAME, and COLUMN_NAME columns, call SQLGetInfo with the SQL_MAX_CATALOG_NAME_LEN, SQL_MAX_SCHEMA_NAME_LEN, SQL_MAX_TABLE_NAME_LEN, and SQL_MAX_COLUMN_NAME_LEN options.

Note For more information about the general use, arguments, and returned data of ODBC catalog functions, see Chapter 7: Catalog Functions.

The following table lists the columns in the result set. Additional columns beyond column 6 (PK_NAME) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "Data Returned by Catalog Functions" in Chapter 7: Catalog Functions.

Column name | Column number | Data type | Comments
TABLE_CAT (ODBC 1.0) | 1 | Varchar | Primary key table catalog name; NULL if not applicable to the data source. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have catalogs.
TABLE_SCHEM (ODBC 1.0) | 2 | Varchar | Primary key table schema name; NULL if not applicable to the data source. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have schemas.
TABLE_NAME (ODBC 1.0) | 3 | Varchar not NULL | Primary key table name.
COLUMN_NAME (ODBC 1.0) | 4 | Varchar not NULL | Primary key column name. The driver returns an empty string for a column that does not have a name.
KEY_SEQ (ODBC 1.0) | 5 | Smallint not NULL | Column sequence number in key (starting with 1).
PK_NAME (ODBC 2.0) | 6 | Varchar | Primary key name. NULL if not applicable to the data source.

Code Example

See SQLForeignKeys.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Fetching a single row or a block of data in a forward-only direction | SQLFetch
Returning the columns of foreign keys | SQLForeignKeys
Returning table statistics and indexes | SQLStatistics


SQLProcedureColumns

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ODBC

Summary

SQLProcedureColumns returns the list of input and output parameters, as well as the columns that make up the result set for the specified procedures. The driver returns the information as a result set on the specified statement.

Syntax

SQLRETURN SQLProcedureColumns(
     SQLHSTMT     StatementHandle,
     SQLCHAR *     CatalogName,
     SQLSMALLINT     NameLength1,
     SQLCHAR *     SchemaName,
     SQLSMALLINT     NameLength2,
     SQLCHAR *     ProcName,
     SQLSMALLINT     NameLength3,
     SQLCHAR *     ColumnName,
     SQLSMALLINT     NameLength4);

Arguments

StatementHandle
[Input]
Statement handle.
CatalogName
[Input]
Procedure catalog name. If a driver supports catalogs for some procedures but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those procedures that do not have catalogs. CatalogName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, CatalogName is treated as an identifier and its case is not significant. If it is SQL_FALSE, CatalogName is an ordinary argument; it is treated literally, and its case is significant. For more information, see "Arguments in Catalog Functions" in Chapter 7: Catalog Functions.

NameLength1
[Input]
Length of *CatalogName.
SchemaName
[Input]
String search pattern for procedure schema names. If a driver supports schemas for some procedures but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those procedures that do not have schemas.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, SchemaName is treated as an identifier and its case is not significant. If it is SQL_FALSE, SchemaName is a pattern value argument; it is treated literally, and its case is significant.

NameLength2
[Input]
Length of *SchemaName.
ProcName
[Input]
String search pattern for procedure names.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, ProcName is treated as an identifier and its case is not significant. If it is SQL_FALSE, ProcName is a pattern value argument; it is treated literally, and its case is significant.

NameLength3
[Input]
Length of *ProcName.
ColumnName
[Input]
String search pattern for column names.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, ColumnName is treated as an identifier and its case is not significant. If it is SQL_FALSE, ColumnName is a pattern value argument; it is treated literally, and its case is significant.

NameLength4
[Input]
Length of *ColumnName.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLProcedureColumns returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLProcedureColumns and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
24000 Invalid cursor state A cursor was open on the StatementHandle, and SQLFetch or SQLFetchScroll had been called. This error is returned by the Driver Manager if SQLFetch or SQLFetchScroll has not returned SQL_NO_DATA, and is returned by the driver if SQLFetch or SQLFetchScroll has returned SQL_NO_DATA.

A cursor was open on the StatementHandle, but SQLFetch or SQLFetchScroll had not been called.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLError in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY009 Invalid use of null pointer The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, the CatalogName argument was a null pointer, and the SQL_CATALOG_NAME InfoType returns that catalog names are supported.

(DM) The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, and the SchemaName, ProcName, or ColumnName argument was a null pointer.
HY010 Function sequence error (DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY090 Invalid string or buffer length (DM) The value of one of the name length arguments was less than 0 but not equal to SQL_NTS.

The value of one of the name length arguments exceeded the maximum length value for the corresponding catalog, schema, procedure, or column name.
HYC00 Optional feature not implemented A procedure catalog was specified, and the driver or data source does not support catalogs.

A procedure schema was specified, and the driver or data source does not support schemas.

A string search pattern was specified for the procedure schema, procedure name, or column name, and the data source does not support search patterns for one or more of those arguments.

The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source.

The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.
HYT00 | Timeout expired | The timeout period expired before the data source returned the result set. The timeout period is set through SQLSetStmtAttr, SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

This function is typically used before statement execution to retrieve information about procedure parameters and the columns that make up the result set or sets returned by the procedure, if any. For more information, see "Procedures" in Chapter 9: Executing Statements.

Note SQLProcedureColumns might not return all columns used by a procedure. For example, a driver might return only information about the parameters used by a procedure and not the columns in a result set it generates.

The SchemaName, ProcName, and ColumnName arguments accept search patterns. For more information about valid search patterns, see "Pattern Value Arguments" in Chapter 7: Catalog Functions.

Note For more information about the general use, arguments, and returned data of ODBC catalog functions, see Chapter 7: Catalog Functions.

SQLProcedureColumns returns the results as a standard result set, ordered by PROCEDURE_CAT, PROCEDURE_SCHEM, PROCEDURE_NAME, and COLUMN_TYPE. Column names are returned for each procedure in the following order: the name of the return value, the names of each parameter in the procedure invocation (in call order), and then the names of each column in the result set returned by the procedure (in column order).

Applications should bind driver-specific columns relative to the end of the result set. For more information, see "Data Returned by Catalog Functions" in Chapter 7: Catalog Functions.

To determine the actual lengths of the PROCEDURE_CAT, PROCEDURE_SCHEM, PROCEDURE_NAME, and COLUMN_NAME columns, an application can call SQLGetInfo with the SQL_MAX_CATALOG_NAME_LEN, SQL_MAX_SCHEMA_NAME_LEN, SQL_MAX_PROCEDURE_NAME_LEN, and SQL_MAX_COLUMN_NAME_LEN options.

The following columns have been renamed for ODBC 3.x. The column name changes do not affect backward compatibility because applications bind by column number.

ODBC 2.0 column | ODBC 3.x column
PROCEDURE_QUALIFIER | PROCEDURE_CAT
PROCEDURE _OWNER | PROCEDURE_SCHEM
PRECISION | COLUMN_SIZE
LENGTH | BUFFER_LENGTH
SCALE | DECIMAL_DIGITS
RADIX | NUM_PREC_RADIX

The following columns have been added to the results set returned by SQLProcedureColumns for ODBC 3.x:

  • COLUMN_DEF
  • DATETIME_CODE
  • CHAR_OCTET_LENGTH
  • ORDINAL_POSITION
  • IS_NULLABLE

The following table lists the columns in the result set. Additional columns beyond column 19 (IS_NULLABLE) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "Data Returned by Catalog Functions" in Chapter 7: Catalog Functions.

Column name 		Column number
Data type
Comments
PROCEDURE_CAT
(ODBC 2.0) 		1 Varchar Procedure catalog name; NULL if not applicable to the data source. If a driver supports catalogs for some procedures but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those procedures that do not have catalogs.
PROCEDURE_SCHEM
(ODBC 2.0) 		2 Varchar Procedure schema name; NULL if not applicable to the data source. If a driver supports schemas for some procedures but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those procedures that do not have schemas.
PROCEDURE_NAME
(ODBC 2.0) 		3 Varchar
not NULL 		Procedure name. An empty string is returned for a procedure that does not have a name.
COLUMN_NAME
(ODBC 2.0) 		4 Varchar
not NULL 		Procedure column name. The driver returns an empty string for a procedure column that does not have a name.
COLUMN_TYPE
(ODBC 2.0) 		5 Smallint
not NULL 		Defines the procedure column as a parameter or a result set column:

SQL_PARAM_TYPE_UNKNOWN: The procedure column is a parameter whose type is unknown. (ODBC 1.0)

SQL_PARAM_INPUT: The procedure column is an input parameter. (ODBC 1.0)

SQL_PARAM_INPUT_OUTPUT: The procedure column is an input/output parameter. (ODBC 1.0)

SQL_PARAM_OUTPUT: The procedure column is an output parameter. (ODBC 2.0)

SQL_RETURN_VALUE: The procedure column is the return value of the procedure. (ODBC 2.0)

SQL_RESULT_COL: The procedure column is a result set column. (ODBC 1.0)
DATA_TYPE
(ODBC 2.0) 		6 Smallint
not NULL 		SQL data type. This can be an ODBC SQL data type or a driver-specific SQL data type. For datetime and interval data types, this column returns the concise data types (for example, SQL_TYPE_TIME or SQL_INTERVAL_YEAR_TO_MONTH). For a list of valid ODBC SQL data types, see "SQL Data Types" in Appendix D: Data Types. For information about driver-specific SQL data types, see the driver's documentation.
TYPE_NAME
(ODBC 2.0) 		7 Varchar
not NULL 		Data source–dependent data type name; for example, "CHAR", "VARCHAR", "MONEY", "LONG VARBINARY", or "CHAR ( ) FOR BIT DATA".
COLUMN_SIZE
(ODBC 2.0) 		8 Integer The column size of the procedure column on the data source. NULL is returned for data types where column size is not applicable. For more information concerning precision, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size," in Appendix D: Data Types.
BUFFER_LENGTH
(ODBC 2.0) 		9 Integer The length in bytes of data transferred on an SQLGetData or SQLFetch operation if SQL_C_DEFAULT is specified. For numeric data, this size may be different than the size of the data stored on the data source. For more information, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size," in Appendix D: Data Types.
DECIMAL_DIGITS
(ODBC 2.0) 		10 Smallint The decimal digits of the procedure column on the data source. NULL is returned for data types where decimal digits is not applicable. For more information concerning decimal digits, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size," in Appendix D: Data Types.
NUM_PREC_RADIX
(ODBC 2.0) 		11 Smallint For numeric data types, either 10 or 2.

If 10, the values in COLUMN_SIZE and DECIMAL_DIGITS give the number of decimal digits allowed for the column. For example, a DECIMAL(12,5) column would return a NUM_PREC_RADIX of 10, a COLUMN_SIZE of 12, and a DECIMAL_DIGITS of 5; a FLOAT column could return a NUM_PREC_RADIX of 10, a COLUMN_SIZE of 15, and a DECIMAL_DIGITS of NULL.

If 2, the values in COLUMN_SIZE and DECIMAL_DIGITS give the number of bits allowed in the column. For example, a FLOAT column could return a NUM_PREC_RADIX of 2, a COLUMN_SIZE of 53, and a DECIMAL_DIGITS of NULL.

NULL is returned for data types where NUM_PREC_RADIX is not applicable.

NULLABLE
(ODBC 2.0) 		12 Smallint
not NULL 		Whether the procedure column accepts a NULL value:

SQL_NO_NULLS: The procedure column does not accept NULL values.

SQL_NULLABLE: The procedure column accepts NULL values.

SQL_NULLABLE_UNKNOWN: It is not known if the procedure column accepts NULL values.
REMARKS
(ODBC 2.0) 		13 Varchar A description of the procedure column.
COLUMN_DEF
(ODBC 3.0) 		14 Varchar The default value of the column.

If NULL was specified as the default value, this column is the word NULL, not enclosed in quotation marks. If the default value cannot be represented without truncation, this column contains TRUNCATED, with no enclosing single quotation marks. If no default value was specified, this column is NULL.

The value of COLUMN_DEF can be used in generating a new column definition, except when it contains the value TRUNCATED.
SQL_DATA_TYPE
(ODBC 3.0) 		15 Smallint not NULL The value of the SQL data type as it appears in the SQL_DESC_TYPE field of the descriptor. This column is the same as the DATA_TYPE column, except for datetime and interval data types.

For datetime and interval data types, the SQL_DATA_TYPE field in the result set will return SQL_INTERVAL or SQL_DATETIME, and the SQL_DATETIME_SUB field will return the subcode for the specific interval or datetime data type. (See Appendix D: Data Types.)
SQL_DATETIME_SUB
(ODBC 3.0) 		16 Smallint The subtype code for datetime and interval data types. For other data types, this column returns a NULL.
CHAR_OCTET_LENGTH
(ODBC 3.0) 		17 Integer The maximum length in bytes of a character or binary data type column. For all other data types, this column returns a NULL.
ORDINAL_POSITION
(ODBC 3.0) 		18 Integer not NULL For input and output parameters, the ordinal position of the parameter in the procedure definition (in increasing parameter order, starting at 1). For a return value (if any), 0 is returned. For result-set columns, the ordinal position of the column in the result set, with the first column in the result set being number 1. If there are multiple result sets, column ordinal positions are returned in a driver-specific manner.
IS_NULLABLE
(ODBC 3.0) 		19 Varchar "NO" if the column does not include NULLs.

"YES" if the column can include NULLs.

This column returns a zero-length string if nullability is unknown.

ISO rules are followed to determine nullability. An ISO SQL–compliant DBMS cannot return an empty string.

The value returned for this column is different from the value returned for the NULLABLE column. (See the description of the NULLABLE column.)

Code Example

See "Procedure Calls" in Chapter 8: SQL Statements.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Fetching a single row or a block of data in a forward-only direction | SQLFetch
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Returning a list of procedures in a data source | SQLProcedures


SQLProcedures

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ODBC

Summary

SQLProcedures returns the list of procedure names stored in a specific data source. Procedure is a generic term used to describe an executable object, or a named entity that can be invoked using input and output parameters. For more information on procedures, see the "Procedures" section in Chapter 9: Executing Statements.

Syntax

SQLRETURN SQLProcedures(
     SQLHSTMT     StatementHandle,
     SQLCHAR *     CatalogName,
     SQLSMALLINT     NameLength1,
     SQLCHAR *     SchemaName,
     SQLSMALLINT     NameLength2,
     SQLCHAR *     ProcName,
     SQLSMALLINT     NameLength3);

Arguments

StatementHandle
[Input]
Statement handle.
CatalogName
[Input]
Procedure catalog. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have catalogs. CatalogName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, CatalogName is treated as an identifier and its case is not significant. If it is SQL_FALSE, CatalogName is an ordinary argument; it is treated literally, and its case is significant. For more information, see "Arguments in Catalog Functions" in Chapter 7: Catalog Functions.

NameLength1
[Input]
Length in bytes of *CatalogName.
SchemaName
[Input]
String search pattern for procedure schema names. If a driver supports schemas for some procedures but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those procedures that do not have schemas.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, SchemaName is treated as an identifier and its case is not significant. If it is SQL_FALSE, SchemaName is a pattern value argument; it is treated literally, and its case is significant.

NameLength2
[Input]
Length in bytes of *SchemaName.
ProcName
[Input]
String search pattern for procedure names.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, ProcName is treated as an identifier and its case is not significant. If it is SQL_FALSE, ProcName is a pattern value argument; it is treated literally, and its case is significant.

NameLength3
[Input]
Length in bytes of *ProcName.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLProcedures returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLProcedures and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
24000 Invalid cursor state A cursor was open on the StatementHandle, and SQLFetch or SQLFetchScroll had been called. This error is returned by the Driver Manager if SQLFetch or SQLFetchScroll has not returned SQL_NO_DATA, and is returned by the driver if SQLFetch or SQLFetchScroll has returned SQL_NO_DATA.

A cursor was open on the StatementHandle, but SQLFetch or SQLFetchScroll had not been called.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 Memory allocation
error 		The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY009 Invalid use of null pointer The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, the CatalogName argument was a null pointer, and the SQL_CATALOG_NAME InfoType returns that catalog names are supported.

(DM) The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, and the SchemaName or ProcName argument was a null pointer.
HY010 Function sequence error (DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 Invalid string or buffer length (DM) The value of one of the name length arguments was less than 0 but not equal to SQL_NTS.

The value of one of the name length arguments exceeded the maximum length value for the corresponding name.

HYC00 Optional feature not implemented A procedure catalog was specified, and the driver or data source does not support catalogs.

A procedure schema was specified, and the driver or data source does not support schemas.

A string search pattern was specified for the procedure schema or procedure name, and the data source does not support search patterns for one or more of those arguments.

The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source.

The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.
HYT00 | Timeout expired | The query timeout period expired before the data source returned the requested result set. The timeout period is set through SQLSetStmtAttr, SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support this function.

Comments

SQLProcedures lists all procedures in the requested range. A user may or may not have permission to execute any of these procedures. To check accessibility, an application can call SQLGetInfo and check the SQL_ACCESSIBLE_PROCEDURES information value. Otherwise, the application must be able to handle a situation where the user selects a procedure that it cannot execute. For information about how this information might be used, see "Procedures" in Chapter 9: Executing Statements.

Note For more information about the general use, arguments, and returned data of ODBC catalog functions, see Chapter 7: Catalog Functions.

SQLProcedures returns the results as a standard result set, ordered by PROCEDURE_CAT, PROCEDURE_SCHEMA, and PROCEDURE_NAME.

Note SQLProcedures might not return all procedures. Applications can use any valid procedure, regardless of whether it is returned by SQLProcedures.

The following columns have been renamed for ODBC 3.x. The column name changes do not affect backward compatibility because applications bind by column number.

ODBC 2.0 column | ODBC 3.x column
PROCEDURE_QUALIFIER | PROCEDURE_CAT
PROCEDURE _OWNER | PROCEDURE _SCHEM

To determine the actual lengths of the PROCEDURE_CAT, PROCEDURE_SCHEM, and PROCEDURE_NAME columns, an application can call SQLGetInfo with the SQL_MAX_CATALOG_NAME_LEN, SQL_MAX_SCHEMA_NAME_LEN, and SQL_MAX_PROCEDURE_NAME_LEN options.

The following table lists the columns in the result set. Additional columns beyond column 8 (PROCEDURE_TYPE) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "Data Returned by Catalog Functions" in Chapter 7: Catalog Functions.

Column name | Column number | Data type | Comments
PROCEDURE_CAT (ODBC 2.0) | 1 | Varchar | Procedure catalog identifier; NULL if not applicable to the data source. If a driver supports catalogs for some procedures but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those procedures that do not have catalogs.
PROCEDURE_SCHEM (ODBC 2.0) | 2 | Varchar | Procedure schema identifier; NULL if not applicable to the data source. If a driver supports schemas for some procedures but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those procedures that do not have schemas.
PROCEDURE_NAME (ODBC 2.0) | 3 | Varchar not NULL | Procedure identifier.
NUM_INPUT_PARAMS (ODBC 2.0) | 4 | N/A | Reserved for future use. Applications should not rely on the data returned in these result columns.
NUM_OUTPUT_PARAMS (ODBC 2.0) | 5 | N/A | Reserved for future use. Applications should not rely on the data returned in these result columns.
NUM_RESULT_SETS (ODBC 2.0) | 6 | N/A | Reserved for future use. Applications should not rely on the data returned in these result columns.
REMARKS (ODBC 2.0) | 7 | Varchar | A description of the procedure.
PROCEDURE_TYPE (ODBC 2.0) | 8 | Smallint | Defines the procedure type: SQL_PT_UNKNOWN: It cannot be determined whether the procedure returns a value. SQL_PT_PROCEDURE: The returned object is a procedure; that is, it does not have a return value. SQL_PT_FUNCTION: The returned object is a function; that is, it has a return value.

The SchemaName and ProcName arguments accept search patterns. For more information about valid search patterns, see "Pattern Value Arguments" in Chapter 7: Catalog Functions.

Code Example

See "Procedure Calls" in Chapter 8: SQL Statements.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Fetching a single row or a block of data in a forward-only direction | SQLFetch
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Returning information about a driver or data source | SQLGetInfo
Returning the parameters and result set columns of a procedure | SQLProcedureColumns
Syntax for invoking stored procedures | Chapter 8: Executing Statements


SQLPutData

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLPutData allows an application to send data for a parameter or column to the driver at statement execution time. This function can be used to send character or binary data values in parts to a column with a character, binary, or data source–specific data type (for example, parameters of the SQL_LONGVARBINARY or SQL_LONGVARCHAR types). SQLPutData supports binding to a Unicode C data type, even if the underlying driver does not support Unicode data.

Syntax

SQLRETURN SQLPutData(
     SQLHSTMT     StatementHandle,
     SQLPOINTER     DataPtr,
     SQLINTEGER     StrLen_or_Ind);

Arguments

StatementHandle
[Input]
Statement handle.
DataPtr
[Input]
Pointer to a buffer containing the actual data for the parameter or column. The data must be in the C data type specified in the ValueType argument of SQLBindParameter (for parameter data) or the TargetType argument of SQLBindCol (for column data).
StrLen_or_Ind
[Input]
Length of *DataPtr. Specifies the amount of data sent in a call to SQLPutData. The amount of data can vary with each call for a given parameter or column. StrLen_or_Ind is ignored unless it meets one of the following conditions:
  • StrLen_or_Ind is SQL_NTS, SQL_NULL_DATA, or SQL_DEFAULT_PARAM.
  • The C data type specified in SQLBindParameter or SQLBindCol is SQL_C_CHAR or SQL_C_BINARY.
  • The C data type is SQL_C_DEFAULT, and the default C data type for the specified SQL data type is SQL_C_CHAR or SQL_C_BINARY.

For all other types of C data, if StrLen_or_Ind is not SQL_NULL_DATA or SQL_DEFAULT_PARAM, the driver assumes that the size of the *DataPtr buffer is the size of the C data type specified with ValueType or TargetType and sends the entire data value. For more information, see "Converting Data from C to SQL Data Types" in Appendix D: Data Types.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLPutData returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLPutData and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | String or binary data returned for an output parameter resulted in the truncation of nonblank character or non-NULL binary data. If it was a string value, it was right-truncated. (Function returns SQL_SUCCESS_WITH_INFO.)
07006 Restricted data type
attribute violation 		The data value identified by the ValueType argument in SQLBindParameter for the bound parameter could not be converted to the data type identified by the ParameterType argument in SQLBindParameter.
07S01 | Invalid use of default parameter | A parameter value, set with SQLBindParameter, was SQL_DEFAULT_PARAM, and the corresponding parameter did not have a default value.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
22001 String data, right truncation The assignment of a character or binary value to a column resulted in the truncation of nonblank (character) or non-null (binary) characters or bytes.

The SQL_NEED_LONG_DATA_LEN information type in SQLGetInfo was "Y", and more data was sent for a long parameter (the data type was SQL_LONGVARCHAR, SQL_LONGVARBINARY, or a long data source–specific data type) than was specified with the StrLen_or_IndPtr argument in SQLBindParameter.

The SQL_NEED_LONG_DATA_LEN information type in SQLGetInfo was "Y", and more data was sent for a long column (the data type was SQL_LONGVARCHAR, SQL_LONGVARBINARY, or a long data source–specific data type) than was specified in the length buffer corresponding to a column in a row of data that was added or updated with SQLBulkOperations or updated with SQLSetPos.
22003 Numeric value out of range The data sent for a bound numeric parameter or column caused the whole (as opposed to fractional) part of the number to be truncated when assigned to the associated table column.

Returning a numeric value (as numeric or string) for one or more input/output or output parameters would have caused the whole (as opposed to fractional) part of the number to be truncated.
22007 Invalid datetime format The data sent for a parameter or column that was bound to a date, time, or timestamp structure was, respectively, an invalid date, time, or timestamp.

An input/output or output parameter was bound to a date, time, or timestamp C structure, and a value in the returned parameter was, respectively, an invalid date, time, or timestamp. (Function returns SQL_SUCCESS_WITH_INFO.)
22008 | Datetime field overflow | A datetime expression computed for an input/output or output parameter resulted in a date, time, or timestamp C structure that was invalid.
22012 | Division by zero | An arithmetic expression calculated for an input/output or output parameter resulted in division by zero.
22015 Interval field overflow The data sent for an exact numeric or interval column or parameter to an interval SQL data type caused a loss of significant digits.

Data was sent for an interval column or parameter with more than one field, was converted to a numeric data type, and had no representation in the numeric data type.

The data sent for column or parameter data was assigned to an interval SQL type, and there was no representation of the value of the C type in the interval SQL type.

The data sent for an exact numeric or interval C column or parameter to an interval C type caused a loss of significant digits.

The data sent for column or parameter data was assigned to an interval C structure, and there was no representation of the data in the interval data structure.
22018 Invalid character value for cast specification The C type was an exact or approximate numeric, a datetime, or an interval data type; the SQL type of the column was a character data type; and the value in the column or parameter was not a valid literal of the bound C type.

The SQL type was an exact or approximate numeric, a datetime, or an interval data type; the C type was SQL_C_CHAR; and the value in the column or parameter was not a valid literal of the bound SQL type.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY009 | Invalid use of null pointer | (DM) The argument DataPtr was a null pointer, and the argument StrLen_or_Ind was not 0, SQL_DEFAULT_PARAM, or SQL_NULL_DATA.
HY010 Function sequence error (DM) The previous function call was not a call to SQLPutData or SQLParamData.

(DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY019 | Non-character and non-binary data sent in pieces | SQLPutData was called more than once for a parameter or column, and it was not being used to send character C data to a column with a character, binary, or data source–specific data type or to send binary C data to a column with a character, binary, or data source–specific data type.
HY020 | Attempt to concatenate a null value | SQLPutData was called more than once since the call that returned SQL_NEED_DATA, and in one of those calls, the StrLen_or_Ind argument contained SQL_NULL_DATA or SQL_DEFAULT_PARAM.
HY090 | Invalid string or buffer length | The argument DataPtr was not a null pointer, and the argument StrLen_or_Ind was less than 0 but not equal to SQL_NTS or SQL_NULL_DATA.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

If SQLPutData is called while sending data for a parameter in an SQL statement, it can return any SQLSTATE that can be returned by the function called to execute the statement (SQLExecute or SQLExecDirect). If it is called while sending data for a column being updated or added with SQLBulkOperations or being updated with SQLSetPos, it can return any SQLSTATE that can be returned by SQLBulkOperations or SQLSetPos.

Comments

SQLPutData can be called to supply data-at-execution data for two uses: parameter data to be used in a call to SQLExecute or SQLExecDirect, or column data to be used when a row is updated or added by a call to SQLBulkOperations or is updated by a call to SQLSetPos.

When an application calls SQLParamData to determine which data it should send, the driver returns an indicator that the application can use to determine which parameter data to send or where column data can be found. It also returns SQL_NEED_DATA, which is an indicator to the application that it should call SQLPutData to send the data. In the DataPtr argument to SQLPutData, the application passes a pointer to the buffer containing the actual data for the parameter or column.

When the driver returns SQL_SUCCESS for SQLPutData, the application calls SQLParamData again. SQLParamData returns SQL_NEED_DATA if more data needs to be sent, in which case the application calls SQLPutData again. It returns SQL_SUCCESS if all data-at-execution data has been sent. The application then calls SQLParamData again. If the driver returns SQL_NEED_DATA and another indicator in *ValuePtrPtr, it requires data for another parameter or column and SQLPutData is called again. If the driver returns SQL_SUCCESS, then all data-at-execution data has been sent and the SQL statement can be executed or the SQLBulkOperations or SQLSetPos call can be processed.

For more information on how data-at-execution parameter data is passed at statement execution time, see "Passing Parameter Values" in SQLBindParameter and "Sending Long Data" in Chapter 9: Executing Statements. For more information on how data-at-execution column data is updated or added, see the section "Using SQLSetPos" in SQLSetPos, "Performing Bulk Updates Using Bookmarks" in SQLBulkOperations, and "Long Data and SQLSetPos and SQLBulkOperations" in Chapter 12: Updating Data.

Note An application can use SQLPutData to send data in parts only when sending character C data to a column with a character, binary, or data source–specific data type or when sending binary C data to a column with a character, binary, or data source–specific data type. If SQLPutData is called more than once under any other conditions, it returns SQL_ERROR and SQLSTATE HY019 (Non-character and non-binary data sent in pieces).

Code Example

In the following example, an application prepares an SQL statement to insert data into the PICTURES table. The statement contains parameters for the PARTID and PICTURE columns. For each parameter, the application calls SQLBindParameter to specify the C and SQL data types of the parameter. It also specifies that the data for the second parameter will be passed at execution time and passes the value 2 for later retrieval by SQLParamData. This value will identify the parameter that is being processed.

The application calls GetNextID to get the next available part ID number. It then calls SQLExecute to execute the statement. SQLExecute returns SQL_NEED_DATA when it needs data for the second parameter. The application calls SQLParamData to retrieve the value it stored with SQLBindParameter; it uses this value to determine which parameter to send data for. For each parameter, the application calls InitUserData to initialize the data routine. It repeatedly calls GetUserData and SQLPutData to get and send the parameter data. Finally, it calls SQLParamData to indicate it has sent all the data for the parameter, at which point it returns SQL_SUCCESS.

For the second parameter, InitUserData calls a routine to prompt the user for the name of a file containing a bitmap photo of the part and opens the file. GetUserData retrieves the next MAX_DATA_LEN bytes of photo data from the file. After it has retrieved all the photo data, it closes the photo file.

Note that some application routines are omitted for clarity.

#define MAX_DATA_LEN 1024
SQLINTEGER     cbPartID = 0, cbPhotoParam, cbData;
SQLUINTEGER    sPartID;
               szPhotoFile;
SQLPOINTER     pToken, InitValue;
SQLCHAR        Data[MAX_DATA_LEN];
SQLRETURN      retcode;
SQLHSTMT       hstmt;

retcode = SQLPrepare(hstmt,
         "INSERT INTO PICTURES (PARTID, PICTURE) VALUES
         (?, ?)", SQL_NTS);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {

   /* Bind the parameters. For parameter 2, pass */
   /* the parameter number in ParameterValuePtr instead of a buffer */
   /* address. */

   SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG,
                  SQL_INTEGER, 0, 0, &sPartID, 0, &cbPartID);
   SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT,
                  SQL_C_BINARY, SQL_LONGVARBINARY,
                  0, 0, (SQLPOINTER) 2, 0, &cbPhotoParam);

   /* Set values so data for parameter 2 will be */
   /* passed at execution. Note that the length parameter in */
   /* the macro SQL_LEN_DATA_AT_EXEC is 0. This assumes that */
   /* the driver returns "N" for the SQL_NEED_LONG_DATA_LEN */
   /* information type in SQLGetInfo. */

   cbPhotoParam = SQL_LEN_DATA_AT_EXEC(0);

   sPartID = GetNextID();   /* Get next available employee ID */
                   /* number. */

   retcode = SQLExecute(hstmt);

   /* For data-at-execution parameters, call SQLParamData to */
   /* get the parameter number set by SQLBindParameter. */
   /* Call InitUserData. Call GetUserData and SQLPutData */
   /* repeatedly to get and put all data for the parameter. */
   /* Call SQLParamData to finish processing this parameter */

   while (retcode == SQL_NEED_DATA) {
      retcode = SQLParamData(hstmt, &pToken);
      if (retcode == SQL_NEED_DATA) {
         InitUserData((SQLSMALLINT)pToken, InitValue);
         while (GetUserData(InitValue, (SQLSMALLINT)pToken, Data,
                            &cbData))
            SQLPutData(hstmt, Data, cbData);
      }
   }
}

VOID InitUserData(sParam, InitValue)
SQLPOINTER InitValue;
{
   SQLCHAR szPhotoFile[MAX_FILE_NAME_LEN];

   /* Prompt user for bitmap file containing employee */
   /* photo. OpenPhotoFile opens the file and returns the */
   /* file handle. */

   PromptPhotoFileName(szPhotoFile);
   OpenPhotoFile(szPhotoFile, (FILE *)InitValue);
   break;
   }

BOOL GetUserData(InitValue, sParam, Data, cbData)
SQLPOINTER     InitValue;
SQLCHAR *      Data;
SQLINTEGER *   cbData;
BOOL           Done;

{

   /* GetNextPhotoData returns the next piece of photo */
   /* data and the number of bytes of data returned */
   /* (up to MAX_DATA_LEN). */

   Done = GetNextPhotoData((FILE *)InitValue, Data,
         MAX_DATA_LEN, &cbData);
   if (Done) {
      ClosePhotoFile((FILE *)InitValue);
      return (TRUE);
   }
   return (FALSE);
   }

Related Functions

For information about | See
Binding a buffer to a parameter | SQLBindParameter
Canceling statement processing | SQLCancel
Executing an SQL statement | SQLExecDirect
Executing a prepared SQL statement | SQLExecute
Returning the next parameter to send data for | SQLParamData


SQLRowCount

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLRowCount returns the number of rows affected by an UPDATE, INSERT, or DELETE statement; an SQL_ADD, SQL_UPDATE_BY_BOOKMARK, or SQL_DELETE_BY_BOOKMARK operation in SQLBulkOperations; or an SQL_UPDATE or SQL_DELETE operation in SQLSetPos.

Syntax

SQLRETURN SQLRowCount(
     SQLHSTMT     StatementHandle,
     SQLINTEGER *     RowCountPtr);

Arguments

StatementHandle
[Input]
Statement handle.
RowCountPtr
[Output]
Points to a buffer in which to return a row count. For UPDATE, INSERT, and DELETE statements, for the SQL_ADD, SQL_UPDATE_BY_BOOKMARK, and SQL_DELETE_BY_BOOKMARK operations in SQLBulkOperations, and for the SQL_UPDATE or SQL_DELETE operations in SQLSetPos, the value returned in *RowCountPtr is either the number of rows affected by the request or –1 if the number of affected rows is not available.

When SQLExecute, SQLExecDirect, SQLBulkOperations, SQLSetPos, or SQLMoreResults is called, the SQL_DIAG_ROW_COUNT field of the diagnostic data structure is set to the row count, and the row count is cached in an implementation-dependent way. SQLRowCount returns the cached row count value. The cached row count value is valid until the statement handle is set back to the prepared or allocated state, the statement is reexecuted, or SQLCloseCursor is called. Note that if a function has been called since the SQL_DIAG_ROW_COUNT field was set, the value returned by SQLRowCount might be different from the value in the SQL_DIAG_ROW_COUNT field because the SQL_DIAG_ROW_COUNT field is reset to 0 by any function call.

For other statements and functions, the driver may define the value returned in *RowCountPtr. For example, some data sources may be able to return the number of rows returned by a SELECT statement or a catalog function before fetching the rows.

Note Many data sources cannot return the number of rows in a result set before fetching them; for maximum interoperability, applications should not rely on this behavior.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLRowCount returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLRowCount and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY010 Function sequence error (DM) The function was called prior to calling SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos for the StatementHandle.

(DM) An asynchronously executing function was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

If the last SQL statement executed on the statement handle was not an UPDATE, INSERT, or DELETE statement or if the Operation argument in the previous call to SQLBulkOperations was not SQL_ADD, SQL_UPDATE_BY_BOOKMARK, or SQL_DELETE_BY_BOOKMARK, or if the Operation argument in the previous call to SQLSetPos was not SQL_UPDATE or SQL_DELETE, the value of *RowCountPtr is driver-defined. For more information, see "Determining the Number of Affected Rows" in Chapter 12: Updating Data.

Related Functions

For information about | See
Executing an SQL statement | SQLExecDirect
Executing a prepared SQL statement | SQLExecute


SQLSetConnectAttr

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLSetConnectAttr sets attributes that govern aspects of connections.

Note For more information about what the Driver Manager maps this function to when an ODBC 3.x application is working with an ODBC 2.x driver, see "Mapping Replacement Functions for Backward Compatibility of Applications" in Chapter 17: Programming Considerations.

Syntax

SQLRETURN SQLSetConnectAttr(
     SQLHDBC     ConnectionHandle,
     SQLINTEGER     Attribute,
     SQLPOINTER     ValuePtr,
     SQLINTEGER     StringLength);

Arguments

ConnectionHandle
[Input]
Connection handle.
Attribute
[Input]
Attribute to set, listed in "Comments."
ValuePtr
[Input]
Pointer to the value to be associated with Attribute. Depending on the value of Attribute, ValuePtr will be a 32-bit unsigned integer value or will point to a null-terminated character string. Note that if the Attribute argument is a driver-specific value, the value in ValuePtr may be a signed integer.
StringLength
[Input]
If Attribute is an ODBC-defined attribute and ValuePtr points to a character string or a binary buffer, this argument should be the length of *ValuePtr. If Attribute is an ODBC-defined attribute and ValuePtr is an integer, StringLength is ignored.

If Attribute is a driver-defined attribute, the application indicates the nature of the attribute to the Driver Manager by setting the StringLength argument. StringLength can have the following values:

  • If ValuePtr is a pointer to a character string, then StringLength is the length of the string or SQL_NTS.
  • If ValuePtr is a pointer to a binary buffer, then the application places the result of the SQL_LEN_BINARY_ATTR(length) macro in StringLength. This places a negative value in StringLength.
  • If ValuePtr is a pointer to a value other than a character string or a binary string, then StringLength should have the value SQL_IS_POINTER.
  • If ValuePtr contains a fixed-length value, then StringLength is either SQL_IS_INTEGER or SQL_IS_UINTEGER, as appropriate.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLSetConnectAttr returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_DBC and a Handle of ConnectionHandle. The following table lists the SQLSTATE values commonly returned by SQLSetConnectAttr and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

The driver can return SQL_SUCCESS_WITH_INFO to provide information about the result of setting an option.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01S02 | Option value changed | The driver did not support the value specified in ValuePtr and substituted a similar value. (Function returns SQL_SUCCESS_WITH_INFO.)
08002 | Connection name in use | The Attribute argument was SQL_ATTR_ODBC_CURSORS, and the driver was already connected to the data source.
08003 | Connection does not exist | (DM) An Attribute value was specified that required an open connection, but the ConnectionHandle was not in a connected state.
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
24000 | Invalid cursor state | The Attribute argument was SQL_ATTR_CURRENT_CATALOG, and a result set was pending.
3D000 | Invalid catalog name | The Attribute argument was SQL_CURRENT_CATALOG, and the specified catalog name was invalid.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY009 | Invalid use of null pointer | The Attribute argument identified a connection attribute that required a string value, and the ValuePtr argument was a null pointer.
HY010 Function sequence error (DM) An asynchronously executing function was called for a StatementHandle associated with the ConnectionHandle and was still executing when SQLSetConnectAttr was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for a StatementHandle associated with the ConnectionHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.

(DM) SQLBrowseConnect was called for the ConnectionHandle and returned SQL_NEED_DATA. This function was called before SQLBrowseConnect returned SQL_SUCCESS_WITH_INFO or SQL_SUCCESS.
HY011 | Attribute cannot be set now | The Attribute argument was SQL_ATTR_TXN_ISOLATION, and a transaction was open.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY024 Invalid attribute value Given the specified Attribute value, an invalid value was specified in ValuePtr. (The Driver Manager returns this SQLSTATE only for connection and statement attributes that accept a discrete set of values, such as SQL_ATTR_ACCESS_MODE or SQL_ATTR_ASYNC_ENABLE. For all other connection and statement attributes, the driver must verify the value specified in ValuePtr.)

The Attribute argument was SQL_ATTR_TRACEFILE or SQL_ATTR_TRANSLATE_LIB, and ValuePtr was an empty string.
HY090 | Invalid string or buffer length | (DM) *ValuePtr is a character string, and the StringLength argument was less than 0 but was not SQL_NTS.
HY092 Invalid attribute/option identifier (DM) The value specified for the argument Attribute was not valid for the version of ODBC supported by the driver.

(DM) The value specified for the argument Attribute was a read-only attribute.
HYC00 | Optional feature not implemented | The value specified for the argument Attribute was a valid ODBC connection or statement attribute for the version of ODBC supported by the driver but was not supported by the driver.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the ConnectionHandle does not support the function.
IM009 | Unable to load translation DLL | The driver was unable to load the translation DLL that was specified for the connection. This error can be returned only when Attribute is SQL_ATTR_TRANSLATE_LIB.

When Attribute is a statement attribute, SQLSetConnectAttr can return any SQLSTATEs returned by SQLSetStmtAttr.

Comments

For general information about connection attributes, see "Connection Attributes" in Chapter 6: Connecting to a Data Source or Driver.

The currently defined attributes and the version of ODBC in which they were introduced are shown in the table later in this section; it is expected that more attributes will be defined to take advantage of different data sources. A range of attributes is reserved by ODBC; driver developers must reserve values for their own driver-specific use from X/Open.

Note The ability to set statement attributes at the connection level by calling SQLSetConnectAttr has been deprecated in ODBC 3.x. ODBC 3.x applications should never set statement attributes at the connection level. ODBC 3.x statement attributes cannot be set at the connection level, with the exception of the SQL_ATTR_METADATA_ID and SQL_ATTR_ASYNC_ENABLE attributes, which are both connection attributes and statement attributes and can be set at either the connection level or the statement level.

ODBC 3.x drivers need only support this functionality if they should work with ODBC 2.x applications that set ODBC 2.x statement options at the connection level. For more information, see "SQLSetConnectOption Mapping" in Appendix G: Driver Guidelines for Backward Compatibility.

An application can call SQLSetConnectAttr at any time between the time the connection is allocated and freed. All connection and statement attributes successfully set by the application for the connection persist until SQLFreeHandle is called on the connection. For example, if an application calls SQLSetConnectAttr before connecting to a data source, the attribute persists even if SQLSetConnectAttr fails in the driver when the application connects to the data source; if an application sets a driver-specific attribute, the attribute persists even if the application connects to a different driver on the connection.

Some connection attributes can be set only before a connection has been made; others can be set only after a connection has been made. The following table indicates those connection attributes that must be set either before or after a connection has been made. Either indicates that the attribute can be set either before or after connection.

Attribute | Set before or after connection?
SQL_ATTR_ACCESS_MODE | Either[1]
SQL_ATTR_ASYNC_ENABLE | Either[2]
SQL_ATTR_AUTOCOMMIT | Either
SQL_ATTR_CONNECTION_TIMEOUT | Either
SQL_ATTR_CURRENT_CATALOG | Either[1]
SQL_ATTR_LOGIN_TIMEOUT | Before
SQL_ATTR_METADATA_ID | Either
SQL_ATTR_ODBC_CURSORS | Before
SQL_ATTR_PACKET_SIZE | Before
SQL_ATTR_QUIET_MODE | Either
SQL_ATTR_TRACE | Either
SQL_ATTR_TRACEFILE | Either
SQL_ATTR_TRANSLATE_LIB | After
SQL_ATTR_TRANSLATE_OPTION | After
SQL_ATTR_TXN_ISOLATION | Either[3]

[1] SQL_ATTR_ACCESS_MODE and SQL_ATTR_CURRENT_CATALOG can be set before or after connecting, depending on the driver. However, interoperable applications set them before connecting because some drivers do not support changing these after connecting.

[2] SQL_ATTR_ASYNC_ENABLE must be set before there is an active statement.

[3] SQL_ATTR_TXN_ISOLATION can be set only if there are no open transactions on the connection. Some connection attributes support substitution of a similar value if the data source does not support the value specified in *ValuePtr. In such cases, the driver returns SQL_SUCCESS_WITH_INFO and SQLSTATE 01S02 (Option value changed). For example, if Attribute is SQL_ATTR_PACKET_SIZE and *ValuePtr exceeds the maximum packet size, the driver substitutes the maximum size. To determine the substituted value, an application calls SQLGetConnectAttr.

The format of information set in the *ValuePtr buffer depends on the specified Attribute. SQLSetConnectAttr will accept attribute information in one of two different formats: a null-terminated character string or a 32-bit integer value. The format of each is noted in the attribute's description. Character strings pointed to by the ValuePtr argument of SQLSetConnectAttr have a length of StringLength bytes.

The StringLength argument is ignored if the length is defined by the attribute, as is the case for all attributes introduced in ODBC 2.x or earlier.

Attribute ValuePtr contents
SQL_ATTR_ACCESS_MODE
(ODBC 1.0) 		An SQLUINTEGER value. SQL_MODE_READ_ONLY is used by the driver or data source as an indicator that the connection is not required to support SQL statements that cause updates to occur. This mode can be used to optimize locking strategies, transaction management, or other areas as appropriate to the driver or data source. The driver is not required to prevent such statements from being submitted to the data source. The behavior of the driver and data source when asked to process SQL statements that are not read-only during a read-only connection is implementation-defined. SQL_MODE_READ_WRITE is the default.
SQL_ATTR_ASYNC_ENABLE
(ODBC 3.0) 		An SQLUINTEGER value that specifies whether a function called with a statement on the specified connection is executed asynchronously:

SQL_ASYNC_ENABLE_OFF = Off (the default)
SQL_ASYNC_ENABLE_ON = On

Setting SQL_ASYNC_ENABLE_ON enables asynchronous execution for all future statement handles allocated on this connection. It is driver-defined whether this enables asynchronous execution for existing statement handles associated with this connection. An error is returned if asynchronous execution is enabled while there is an active statement on the connection.

This attribute can be set whether SQLGetInfo with the SQL_ASYNC_MODE information type returns SQL_AM_CONNECTION or SQL_AM_STATEMENT.

After a function has been called asynchronously, only the original function, SQLAllocHandle, SQLCancel, SQLGetDiagField, or SQLGetDiagRec can be called on the statement or the connection associated with StatementHandle, until the original function returns a code other than SQL_STILL_EXECUTING. Any other function called on StatementHandle or the connection associated with StatementHandle returns SQL_ERROR with an SQLSTATE of HY010 (Function sequence error). Functions can be called on other statements. For more information, see "Asynchronous Execution" in Chapter 9: Executing Statements.

In general, applications should execute functions asynchronously only on single-thread operating systems. On multithread operating systems, applications should execute functions on separate threads rather than executing them asynchronously on the same thread. Drivers that operate only on multithread operating systems do not need to support asynchronous execution.

The following functions can be executed asynchronously:

SQLBulkOperations
SQLColAttribute
SQLColumnPrivileges
SQLColumns
SQLCopyDesc
SQLDescribeCol
SQLDescribeParam
SQLExecDirect
SQLExecute
SQLFetch
SQLFetchScroll
SQLForeignKeys
SQLGetData
SQLGetDescField[1]
SQLGetDescRec[1]
SQLGetDiagField
SQLGetDiagRec
SQLGetTypeInfo
SQLMoreResults
SQLNumParams
SQLNumResultCols
SQLParamData
SQLPrepare
SQLPrimaryKeys
SQLProcedureColumns
SQLProcedures
SQLPutData
SQLSetPos
SQLSpecialColumns
SQLStatistics
SQLTablePrivileges
SQLTables
SQL_ATTR_AUTO_IPD
(ODBC 3.0) 		A read-only SQLUINTEGER value that specifies whether automatic population of the IPD after a call to SQLPrepare is supported:

SQL_TRUE = Automatic population of the IPD after a call to SQLPrepare is supported by the driver.

SQL_FALSE = Automatic population of the IPD after a call to SQLPrepare is not supported by the driver. Servers that do not support prepared statements will not be able to populate the IPD automatically.

If SQL_TRUE is returned for the SQL_ATTR_AUTO_IPD connection attribute, the statement attribute SQL_ATTR_ENABLE_AUTO_IPD can be set to turn automatic population of the IPD on or off. If SQL_ATTR_AUTO_IPD is SQL_FALSE, SQL_ATTR_ENABLE_AUTO_IPD cannot be set to SQL_TRUE. The default value of SQL_ATTR_ENABLE_AUTO_IPD is equal to the value of SQL_ATTR_AUTO_IPD.

This connection attribute can be returned by SQLGetConnectAttr but cannot be set by SQLSetConnectAttr.
SQL_ATTR_AUTOCOMMIT
(ODBC 1.0) 		An SQLUINTEGER value that specifies whether to use autocommit or manual-commit mode:

SQL_AUTOCOMMIT_OFF = The driver uses manual-commit mode, and the application must explicitly commit or roll back transactions with SQLEndTran.

SQL_AUTOCOMMIT_ON = The driver uses autocommit mode. Each statement is committed immediately after it is executed. This is the default. Any open transactions on the connection are committed when SQL_ATTR_AUTOCOMMIT is set to SQL_AUTOCOMMIT_ON to change from manual-commit mode to autocommit mode.

For more information, see "Commit Mode" in Chapter 14: Transactions.

Important Some data sources delete the access plans and close the cursors for all statements on a connection each time a statement is committed; autocommit mode can cause this to happen after each nonquery statement is executed or when the cursor is closed for a query. For more information, see the SQL_CURSOR_COMMIT_BEHAVIOR and SQL_CURSOR_ROLLBACK_BEHAVIOR information types in SQLGetInfo and "Effect of Transactions on Cursors and Prepared Statements" in Chapter 14: Transactions.

When a batch is executed in autocommit mode, two things are possible. The entire batch can be treated as an autocommitable unit, or each statement in a batch is treated as an autocommitable unit. Certain data sources can support both these behaviors and may provide a way of choosing one or the other. It is driver-defined whether a batch is treated as an autocommitable unit or whether each individual statement within the batch is autocommitable.
SQL_ATTR_CONNECTION_DEAD

(ODBC 3.5)

 An SQLUINTERGER value that indicates the state of the connection. If SQL_CD_TRUE, the connection has been lost. If SQL_CD_FALSE, the connection is still active.
SQL_ATTR_CONNECTION_TIMEOUT
(ODBC 3.0) 		An SQLUINTEGER value corresponding to the number of seconds to wait for any request on the connection to complete before returning to the application. The driver should return SQLSTATE HYT00 (Timeout expired) anytime that it is possible to time out in a situation not associated with query execution or login.

If ValuePtr is equal to 0 (the default), there is no timeout.
SQL_ATTR_CURRENT_CATALOG
(ODBC 2.0) 		A character string containing the name of the catalog to be used by the data source. For example, in SQL Server, the catalog is a database, so the driver sends a USE database statement to the data source, where database is the database specified in *ValuePtr. For a single-tier driver, the catalog might be a directory, so the driver changes its current directory to the directory specified in *ValuePtr.
SQL_ATTR_LOGIN_TIMEOUT
(ODBC 1.0) 		An SQLUINTEGER value corresponding to the number of seconds to wait for a login request to complete before returning to the application. The default is driver-dependent. If ValuePtr is 0, the timeout is disabled and a connection attempt will wait indefinitely.

If the specified timeout exceeds the maximum login timeout in the data source, the driver substitutes that value and returns SQLSTATE 01S02 (Option value changed).
SQL_ATTR_METADATA_ID
(ODBC 3.0) 		An SQLUINTEGER value that determines how the string arguments of catalog functions are treated.

If SQL_TRUE, the string argument of catalog functions are treated as identifiers. The case is not significant. For nondelimited strings, the driver removes any trailing spaces and the string is folded to uppercase. For delimited strings, the driver removes any leading or trailing spaces and takes literally whatever is between the delimiters. If one of these arguments is set to a null pointer, the function returns SQL_ERROR and SQLSTATE HY009 (Invalid use of null pointer).

If SQL_FALSE, the string arguments of catalog functions are not treated as identifiers. The case is significant. They can either contain a string search pattern or not, depending on the argument.

The default value is SQL_FALSE.

The TableType argument of SQLTables, which takes a list of values, is not affected by this attribute.

SQL_ATTR_METADATA_ID can also be set on the statement level. (It is the only connection attribute that is also a statement attribute.)

For more information, see "Arguments in Catalog Functions" in Chapter 7: Catalog Functions.
SQL_ATTR_ODBC_CURSORS
(ODBC 2.0) 		An SQLUINTEGER value specifying how the Driver Manager uses the ODBC cursor library:

SQL_CUR_USE_IF_NEEDED = The Driver Manager uses the ODBC cursor library only if it is needed. If the driver supports the SQL_FETCH_PRIOR option in SQLFetchScroll, the Driver Manager uses the scrolling capabilities of the driver. Otherwise, it uses the ODBC cursor library.

SQL_CUR_USE_ODBC = The Driver Manager uses the ODBC cursor library.

SQL_CUR_USE_DRIVER = The Driver Manager uses the scrolling capabilities of the driver. This is the default setting.

For more information about the ODBC cursor library, see Appendix F: ODBC Cursor Library.
SQL_ATTR_PACKET_SIZE
(ODBC 2.0) 		An SQLUINTEGER value specifying the network packet size in bytes.

Note Many data sources either do not support this option or only can return but not set the network packet size.

If the specified size exceeds the maximum packet size or is smaller than the minimum packet size, the driver substitutes that value and returns SQLSTATE 01S02 (Option value changed).

If the application sets packet size after a connection has already been made, the driver will return SQLSTATE HY011 (Attribute cannot be set now).
SQL_ATTR_QUIET_MODE
(ODBC 2.0) 		A 32-bit window handle (hwnd).

If the window handle is a null pointer, the driver does not display any dialog boxes.

If the window handle is not a null pointer, it should be the parent window handle of the application. This is the default. The driver uses this handle to display dialog boxes.

Note The SQL_ATTR_QUIET_MODE connection attribute does not apply to dialog boxes displayed by SQLDriverConnect.
SQL_ATTR_TRACE
(ODBC 1.0) 		An SQLUINTEGER value telling the Driver Manager whether to perform tracing:

SQL_OPT_TRACE_OFF = Tracing off (the default)

SQL_OPT_TRACE_ON = Tracing on

When tracing is on, the Driver Manager writes each ODBC function call to the trace file.

Note When tracing is on, the Driver Manager can return SQLSTATE IM013 (Trace file error) from any function.

An application specifies a trace file with the SQL_ATTR_TRACEFILE option. If the file already exists, the Driver Manager appends to the file. Otherwise, it creates the file. If tracing is on and no trace file has been specified, the Driver Manager writes to the file SQL.LOG in the root directory.

An application can set the variable ODBCSharedTraceFlag to enable tracing dynamically. Tracing is then enabled for all ODBC applications currently running. If an application turns tracing off, it is turned off only for that application.

If the Trace keyword in the system information is set to 1 when an application calls SQLAllocHandle with a HandleType of SQL_HANDLE_ENV, tracing is enabled for all handles. It is enabled only for the application that called SQLAllocHandle.

Calling SQLSetConnectAttr with an Attribute of SQL_ATTR_TRACE does not require that the ConnectionHandle argument be valid and will not return SQL_ERROR if ConnectionHandle is NULL. This attribute applies to all connections.
SQL_ATTR_TRACEFILE
(ODBC 1.0) 		A null-terminated character string containing the name of the trace file.

The default value of the SQL_ATTR_TRACEFILE attribute is specified with the TraceFile keyword in the system information. For more information, see "ODBC Subkey" in Chapter 19: Configuring Data Sources.

Calling SQLSetConnectAttr with an Attribute of SQL_ATTR_ TRACEFILE does not require the ConnectionHandle argument to be valid and will not return SQL_ERROR if ConnectionHandle is invalid. This attribute applies to all connections.
SQL_ATTR_TRANSLATE_LIB
(ODBC 1.0) 		A null-terminated character string containing the name of a library containing the functions SQLDriverToDataSource and SQLDataSourceToDriver that the driver accesses to perform tasks such as character set translation. This option may be specified only if the driver has connected to the data source. The setting of this attribute will persist across connections. For more information about translating data, see "Translation DLLs" in Chapter 17: Programming Considerations, and Chapter 24: Translation DLL Function Reference.
SQL_ATTR_TRANSLATE_OPTION
(ODBC 1.0) 		A 32-bit flag value that is passed to the translation DLL. This attribute can be specified only if the driver has connected to the data source. For information about translating data, see "Translation DLLs" in Chapter 17: Programming Considerations.
SQL_ATTR_TXN_ISOLATION
(ODBC 1.0) 		A 32-bit bitmask that sets the transaction isolation level for the current connection. An application must call SQLEndTran to commit or roll back all open transactions on a connection, before calling SQLSetConnectAttr with this option.

The valid values for ValuePtr can be determined by calling SQLGetInfo with InfoType equal to SQL_TXN_ISOLATION_OPTIONS.

For a description of transaction isolation levels, see the description of the SQL_DEFAULT_TXN_ISOLATION information type in SQLGetInfo and "Transaction Isolation Levels" in Chapter 14: Transactions.

[1] These functions can be called asynchronously only if the descriptor is an implementation descriptor, not an application descriptor.

Code Example

See SQLConnect.

Related Functions

For information about | See
Allocating a handle | SQLAllocHandle
Returning the setting of a connection attribute | SQLGetConnectAttr


SQLSetConnectOption

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

In ODBC 3.x, the ODBC 2.0 function SQLSetConnectOption has been replaced by SQLSetConnectAttr. For more information, see SQLSetConnectAttr.

Note For more information about what the Driver Manager maps this function to when an ODBC 2.x application is working with an ODBC 3.x driver, see "Mapping Deprecated Functions" in Appendix G: Driver Guidelines for Backward Compatibility.


SQLSetCursorName

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLSetCursorName associates a cursor name with an active statement. If an application does not call SQLSetCursorName, the driver generates cursor names as needed for SQL statement processing.

Syntax

SQLRETURN SQLSetCursorName(
     SQLHSTMT      StatementHandle,
     SQLCHAR *     CursorName,
     SQLSMALLINT     NameLength);

Arguments

StatementHandle
[Input]
Statement handle.
CursorName
[Input]
Cursor name. For efficient processing, the cursor name should not include any leading or trailing spaces in the cursor name, and if the cursor name includes a delimited identifier, the delimiter should be positioned as the first character in the cursor name.
NameLength
[Input]
Length of *CursorName.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLSetCursorName returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLSetCursorName and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01004 | String data, right truncated | The cursor name exceeded the maximum limit, so only the maximum allowable number of characters was used.
24000 | Invalid cursor state | The statement corresponding to StatementHandle was already in an executed or cursor-positioned state.
34000 | Invalid cursor name | The cursor name specified in *CursorName was invalid because it exceeded the maximum length as defined by the driver, or it started with "SQLCUR" or "SQL_CUR."
3C000 | Duplicate cursor name | The cursor name specified in *CursorName already exists.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY009 | Invalid use of null pointer | (DM) The argument CursorName was a null pointer.
HY010 | Function sequence error | (DM) An asynchronously executing function was called for the StatementHandle and was still executing when this function was called. (DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 | Invalid string or buffer length | (DM) The argument NameLength was less than 0 but not equal to SQL_NTS.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

Cursor names are used only in positioned update and delete statements (for example, UPDATE table-name ...WHERE CURRENT OF cursor-name). For more information, see "Positioned Update and Delete Statements" in Chapter 12: Updating Data. If the application does not call SQLSetCursorName to define a cursor name, on execution of a query statement the driver generates a name that begins with the letters SQL_CUR and does not exceed 18 characters in length.

All cursor names within the connection must be unique. The maximum length of a cursor name is defined by the driver. For maximum interoperability, it is recommended that applications limit cursor names to no more than 18 characters. In ODBC 3.x, if a cursor name is a quoted identifier, it is treated in a case-sensitive manner and it can contain characters that the syntax of SQL would not permit or would treat specially, such as blanks or reserved keywords. If a cursor name must be treated in a case-sensitive manner, it must be passed as a quoted identifier.

A cursor name that is set either explicitly or implicitly remains set until the statement with which it is associated is dropped, using SQLFreeHandle. SQLSetCursorName can be called to rename a cursor on a statement as long as the cursor is in an allocated or prepared state.

Code Example

In the following example, an application uses SQLSetCursorName to set a cursor name for a statement. It then uses that statement to retrieve results from the CUSTOMERS table. Finally, it performs a positioned update to change the phone number of John Smith. Note that the application uses different statement handles for the SELECT and UPDATE statements.

For another code example, see SQLSetPos.

#define NAME_LEN 50
#define PHONE_LEN 10

SQLHSTMT     hstmtSelect,
SQLHSTMT     hstmtUpdate;
SQLRETURN    retcode;
SQLHDBC      hdbc;
SQLCHAR      szName[NAME_LEN], szPhone[PHONE_LEN];
SQLINTEGER   cbName, cbPhone;

/* Allocate the statements and set the cursor name. */

SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmtSelect);
SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmtUpdate);
SQLSetCursorName(hstmtSelect, "C1", SQL_NTS);

/* SELECT the result set and bind its columns to local buffers. */

SQLExecDirect(hstmtSelect,
      "SELECT NAME, PHONE FROM CUSTOMERS",
      SQL_NTS);
SQLBindCol(hstmtSelect, 1, SQL_C_CHAR, szName, NAME_LEN, &cbName);
SQLBindCol(hstmtSelect, 2, SQL_C_CHAR, szPhone, PHONE_LEN, &cbPhone);

/* Read through the result set until the cursor is */
/* positioned on the row for John Smith. */

do
 retcode = SQLFetch(hstmtSelect);
while ((retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) &&
   (strcmp(szName, "Smith, John") != 0));

/* Perform a positioned update of John Smith's name. */

if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
   SQLExecDirect(hstmtUpdate,
   "UPDATE EMPLOYEE SET PHONE=\"2064890154\" WHERE CURRENT OF C1",
   SQL_NTS);
}

Related Functions

For information about | See
Executing an SQL statement | SQLExecDirect
Executing a prepared SQL statement | SQLExecute
Returning a cursor name | SQLGetCursorName
Setting cursor scrolling options | SQLSetScrollOptions


SQLSetDescField

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLSetDescField sets the value of a single field of a descriptor record.

Syntax

SQLRETURN SQLSetDescField(
     SQLHDESC     DescriptorHandle,
     SQLSMALLINT     RecNumber,
     SQLSMALLINT     FieldIdentifier,
     SQLPOINTER     ValuePtr,
     SQLINTEGER     BufferLength);

Arguments

DescriptorHandle
[Input]
Descriptor handle.
RecNumber
[Input]
Indicates the descriptor record containing the field that the application seeks to set. Descriptor records are numbered from 0, with record number 0 being the bookmark record. The RecNumber argument is ignored for header fields.
FieldIdentifier
[Input]
Indicates the field of the descriptor whose value is to be set. For more information, see "FieldIdentifier Argument" in the "Comments" section.
ValuePtr
[Input]
Pointer to a buffer containing the descriptor information, or a 4-byte value. The data type depends on the value of FieldIdentifier. If ValuePtr is a 4-byte value, either all four of the bytes are used or just two of the four are used, depending on the value of the FieldIdentifier argument.
BufferLength
[Input]
If FieldIdentifier is an ODBC-defined field and ValuePtr points to a character string or a binary buffer, this argument should be the length of *ValuePtr. If FieldIdentifier is an ODBC-defined field and ValuePtr is an integer, BufferLength is ignored.

If FieldIdentifier is a driver-defined field, the application indicates the nature of the field to the Driver Manager by setting the BufferLength argument. BufferLength can have the following values:

  • If ValuePtr is a pointer to a character string, then BufferLength is the length of the string or SQL_NTS.
  • If ValuePtr is a pointer to a binary buffer, then the application places the result of the SQL_LEN_BINARY_ATTR(length) macro in BufferLength. This places a negative value in BufferLength.
  • If ValuePtr is a pointer to a value other than a character string or a binary string, then BufferLength should have the value SQL_IS_POINTER.
  • If ValuePtr contains a fixed-length value, then BufferLength is either SQL_IS_INTEGER, SQL_IS_UINTEGER, SQL_IS_SMALLINT, or SQL_IS_USMALLINT, as appropriate.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLSetDescField returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_DESC and a Handle of DescriptorHandle. The following table lists the SQLSTATE values commonly returned by SQLSetDescField and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01S02 | Option value changed | The driver did not support the value specified in *ValuePtr (if ValuePtr was a pointer) or the value in ValuePtr (if ValuePtr was a 4-byte value), or *ValuePtr was invalid because of implementation working conditions, so the driver substituted a similar value. (Function returns SQL_SUCCESS_WITH_INFO.)
07009 Invalid descriptor index The FieldIdentifier argument was a record field, the RecNumber argument was 0, and the DescriptorHandle argument referred to an IPD handle.

The RecNumber argument was less than 0, and the DescriptorHandle argument referred to an ARD or an APD.

The RecNumber argument was greater than the maximum number of columns or parameters that the data source can support, and the DescriptorHandle argument referred to an APD or ARD.

(DM) The FieldIdentifier argument was SQL_DESC_COUNT, and *ValuePtr argument was less than 0.

The RecNumber argument was equal to 0, and the DescriptorHandle argument referred to an implicitly allocated APD. (This error does not occur with an explicitly allocated application descriptor, because it is not known whether an explicitly allocated application descriptor is an APD or ARD until execute time.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
22001 String data, right
truncated 		The FieldIdentifier argument was SQL_DESC_NAME, and the BufferLength argument was a value larger than SQL_MAX_IDENTIFIER_LEN.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 Memory allocation
error 		The driver was unable to allocate memory required to support execution or completion of the function.
HY010 Function sequence error (DM) The DescriptorHandle was associated with a StatementHandle for which an asynchronously executing function (not this one) was called and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle with which the DescriptorHandle was associated and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY016 | Cannot modify an implementation row descriptor | The DescriptorHandle argument was associated with an IRD, and the FieldIdentifier argument was not SQL_DESC_ARRAY_STATUS_PTR or SQL_DESC_ROWS_PROCESSED_PTR.
HY021 Inconsistent descriptor information The SQL_DESC_TYPE and SQL_DESC_DATETIME_INTERVAL_CODE fields do not form a valid ODBC SQL type or a valid driver-specific SQL type (for IPDs) or a valid ODBC C type (for APDs or ARDs).

Descriptor information checked during a consistency check was not consistent. (See "Consistency Check" in SQLSetDescRec.)
HY090 Invalid string or buffer length (DM) *ValuePtr is a character string, and BufferLength was less than zero but was not equal to SQL_NTS.

(DM) The driver was an ODBC 2.x driver, the descriptor was an ARD, the ColumnNumber argument was set to 0, and the value specified for the argument BufferLength was not equal to 4.

HY091 Invalid descriptor field identifier The value specified for the FieldIdentifier argument was not an ODBC-defined field and was not an implementation-defined value.

The FieldIdentifier argument was invalid for the DescriptorHandle argument.

The FieldIdentifier argument was a read-only, ODBC-defined field.
HY092 Invalid attribute/option identifier The value in *ValuePtr was not valid for the FieldIdentifier argument.

The FieldIdentifier argument was SQL_DESC_UNNAMED, and ValuePtr was SQL_NAMED.
HY105 | Invalid parameter type | (DM) The value specified for the SQL_DESC_PARAMETER_TYPE field was invalid. (For more information, see the "InputOutputType Argument" section in SQLBindParameter.)
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the DescriptorHandle does not support the function.

Comments

An application can call SQLSetDescField to set any descriptor field one at a time. One call to SQLSetDescField sets a single field in a single descriptor. This function can be called to set any field in any descriptor type, provided the field can be set. (See the table later in this section.)

Note If a call to SQLSetDescField fails, the contents of the descriptor record identified by the RecNumber argument are undefined.

Other functions can be called to set multiple descriptor fields with a single call of the function. The SQLSetDescRec function sets a variety of fields that affect the data type and buffer bound to a column or parameter (the SQL_DESC_TYPE, SQL_DESC_DATETIME_INTERVAL_CODE, SQL_DESC_OCTET_LENGTH, SQL_DESC_PRECISION, SQL_DESC_SCALE, SQL_DESC_DATA_PTR, SQL_DESC_OCTET_LENGTH_PTR, and SQL_DESC_INDICATOR_PTR fields). SQLBindCol or SQLBindParameter can be used to make a complete specification for the binding of a column or parameter. These functions set a specific group of descriptor fields with one function call.

SQLSetDescField can be called to change the binding buffers by adding an offset to the binding pointers (SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, or SQL_DESC_OCTET_LENGTH_PTR). This changes the binding buffers without calling SQLBindCol or SQLBindParameter, which allows an application to change SQL_DESC_DATA_PTR without changing other fields, such as SQL_DESC_DATA_TYPE.

If an application calls SQLSetDescField to set any field other than SQL_DESC_COUNT or the deferred fields SQL_DESC_DATA_PTR, SQL_DESC_OCTET_LENGTH_PTR, or SQL_DESC_INDICATOR_PTR, the record becomes unbound.

Descriptor header fields are set by calling SQLSetDescField with the appropriate FieldIdentifier. Many header fields are also statement attributes, so they can also be set by a call to SQLSetStmtAttr. This allows applications to set a descriptor field without first obtaining a descriptor handle. When SQLSetDescField is called to set a header field, the RecNumber argument is ignored.

A RecNumber of 0 is used to set bookmark fields.

Note The statement attribute SQL_ATTR_USE_BOOKMARKS should always be set before calling SQLSetDescField to set bookmark fields. While this is not mandatory, it is strongly recommended.

Sequence of Setting Descriptor Fields

When setting descriptor fields by calling SQLSetDescField, the application must follow a specific sequence:

  1. The application must first set the SQL_DESC_TYPE, SQL_DESC_CONCISE_TYPE, or SQL_DESC_DATETIME_INTERVAL_CODE field.
  2. After one of these fields has been set, the application can set an attribute of a data type, and the driver sets data type attribute fields to the appropriate default values for the data type. Automatic defaulting of type attribute fields ensures that the descriptor is always ready to use once the application has specified a data type. If the application explicitly sets a data type attribute, it is overriding the default attribute.
  3. After one of the fields listed in step 1 has been set, and data type attributes have been set, the application can set SQL_DESC_DATA_PTR. This prompts a consistency check of descriptor fields. If the application changes the data type or attributes after setting the SQL_DESC_DATA_PTR field, the driver sets SQL_DESC_DATA_PTR to a null pointer, unbinding the record. This forces the application to complete the proper steps in sequence, before the descriptor record is usable.

Initialization of Descriptor Fields

When a descriptor is allocated, the fields in the descriptor can be initialized to a default value, be initialized without a default value, or be undefined for the type of descriptor. The following tables indicate the initialization of each field for each type of descriptor, with "D" indicating that the field is initialized with a default, and "ND" indicating that the field is initialized without a default. If a number is shown, the default value of the field is that number. The tables also indicate whether a field is read/write (R/W) or read-only (R).

The fields of an IRD have a default value only after the statement has been prepared or executed and the IRD has been populated, not when the statement handle or descriptor has been allocated. Until the IRD has been populated, any attempt to gain access to a field of an IRD will return an error.

Some descriptor fields are defined for one or more, but not all, of the descriptor types (ARDs and IRDs, and APDs and IPDs). When a field is undefined for a type of descriptor, it is not needed by any of the functions that use that descriptor.

The fields that can be accessed by SQLGetDescField cannot necessarily be set by SQLSetDescField. Fields that can be set by SQLSetDescField are listed in the following tables.

The initialization of header fields is outlined in the table that follows.

Header field name | Type | R/W | Default
SQL_DESC_ALLOC_TYPE | SQLSMALLINT | ARD: R APD: R IRD: R IPD: R | ARD: SQL_DESC_ALLOC_AUTO for implicit or SQL_DESC_ALLOC_USER for explicit APD: SQL_DESC_ALLOC_AUTO for implicit or SQL_DESC_ALLOC_USER for explicit IRD: SQL_DESC_ALLOC_AUTO IPD: SQL_DESC_ALLOC_AUTO
SQL_DESC_ARRAY_SIZE | SQLUINTEGER | ARD: R/W APD: R/W IRD: Unused IPD: Unused | ARD:[1] APD:[1] IRD: Unused IPD: Unused
SQL_DESC_ARRAY_STATUS_PTR | SQLUSMALLINT* | ARD: R/W APD: R/W IRD: R/W IPD: R/W | ARD: Null ptr APD: Null ptr IRD: Null ptr IPD: Null ptr
SQL_DESC_BIND_OFFSET_PTR | SQLINTEGER* | ARD: R/W APD: R/W IRD: Unused IPD: Unused | ARD: Null ptr APD: Null ptr IRD: Unused IPD: Unused
SQL_DESC_BIND_TYPE | SQLINTEGER | ARD: R/W APD: R/W IRD: Unused IPD: Unused | ARD: SQL_BIND_BY_COLUMN APD: SQL_BIND_BY_COLUMN IRD: Unused IPD: Unused
SQL_DESC_COUNT | SQLSMALLINT | ARD: R/W APD: R/W IRD: R IPD: R/W | ARD: 0 APD: 0 IRD: D IPD: 0
SQL_DESC_ROWS_PROCESSED_PTR | SQLUINTEGER* | ARD: Unused APD: Unused IRD: R/W IPD: R/W | ARD: Unused APD: Unused IRD: Null ptr IPD: Null ptr

[1] These fields are defined only when the IPD is automatically populated by the driver. If not, they are undefined. If an application attempts to set these fields, SQLSTATE HY091 (Invalid descriptor field identifier) will be returned.

The initialization of record fields is as shown in the following table.

Record field name | Type | R/W | Default
SQL_DESC_AUTO_UNIQUE_VALUE | SQLINTEGER | ARD: Unused APD: Unused IRD: R IPD: Unused | ARD: Unused APD: Unused IRD: D IPD: Unused
SQL_DESC_BASE_COLUMN_NAME | SQLCHAR * | ARD: Unused APD: Unused IRD: R IPD: Unused | ARD: Unused APD: Unused IRD: D IPD: Unused
SQL_DESC_BASE_TABLE_NAME | SQLCHAR * | ARD: Unused APD: Unused IRD: R IPD: Unused | ARD: Unused APD: Unused IRD: D IPD: Unused
SQL_DESC_CASE_SENSITIVE | SQLINTEGER | ARD: Unused APD: Unused IRD: R IPD: R | ARD: Unused APD: Unused IRD: D IPD: D[1]
SQL_DESC_CATALOG_NAME | SQLCHAR * | ARD: Unused APD: Unused IRD: R IPD: Unused | ARD: Unused APD: Unused IRD: D IPD: Unused
SQL_DESC_CONCISE_TYPE | SQLSMALLINT | ARD: R/W APD: R/W IRD: R IPD: R/W | ARD: SQL_C_ DEFAULT APD: SQL_C_ DEFAULT IRD: D IPD: ND
SQL_DESC_DATA_PTR | SQLPOINTER | ARD: R/W APD: R/W IRD: Unused IPD: Unused | ARD: Null ptr APD: Null ptr IRD: Unused IPD: Unused[2]
SQL_DESC_DATETIME_INTERVAL_CODE | SQLSMALLINT | ARD: R/W APD: R/W IRD: R IPD: R/W | ARD: ND APD: ND IRD: D IPD: ND
SQL_DESC_DATETIME_INTERVAL_PRECISION | SQLINTEGER | ARD: R/W APD: R/W IRD: R IPD: R/W | ARD: ND APD: ND IRD: D IPD: ND
SQL_DESC_DISPLAY_SIZE | SQLINTEGER | ARD: Unused APD: Unused IRD: R IPD: Unused | ARD: Unused APD: Unused IRD: D IPD: Unused
SQL_DESC_FIXED_PREC_SCALE | SQLSMALLINT | ARD: Unused APD: Unused IRD: R IPD: R | ARD: Unused APD: Unused IRD: D IPD: D[1]
SQL_DESC_INDICATOR_PTR | SQLINTEGER * | ARD: R/W APD: R/W IRD: Unused IPD: Unused | ARD: Null ptr APD: Null ptr IRD: Unused IPD: Unused
SQL_DESC_LABEL | SQLCHAR * | ARD: Unused APD: Unused IRD: R IPD: Unused | ARD: Unused APD: Unused IRD: D IPD: Unused
SQL_DESC_LENGTH | SQLUINTEGER | ARD: R/W APD: R/W IRD: R IPD: R/W | ARD: ND APD: ND IRD: D IPD: ND
SQL_DESC_LITERAL_PREFIX | SQLCHAR * | ARD: Unused APD: Unused IRD: R IPD: Unused | ARD: Unused APD: Unused IRD: D IPD: Unused
SQL_DESC_LITERAL_SUFFIX | SQLCHAR * | ARD: Unused APD: Unused IRD: R IPD: Unused | ARD: Unused APD: Unused IRD: D IPD: Unused
SQL_DESC_LOCAL_TYPE_NAME | SQLCHAR * | ARD: Unused APD: Unused IRD: R IPD: R | ARD: Unused APD: Unused IRD: D IPD: D[1]
SQL_DESC_NAME | SQLCHAR * | ARD: Unused APD: Unused IRD: R IPD: R/W | ARD: ND APD: ND IRD: D IPD: ND
SQL_DESC_NULLABLE | SQLSMALLINT | ARD: Unused APD: Unused IRD: R IPD: R | ARD: ND APD: ND IRD: D IPD: ND
SQL_DESC_NUM_PREC_RADIX | SQLINTEGER | ARD: R/W APD: R/W IRD: R IPD: R/W | ARD: ND APD: ND IRD: D IPD: ND
SQL_DESC_OCTET_LENGTH | SQLINTEGER | ARD: R/W APD: R/W IRD: R IPD: R/W | ARD: ND APD: ND IRD: D IPD: ND
SQL_DESC_OCTET_LENGTH_PTR | SQLINTEGER * | ARD: R/W APD: R/W IRD: Unused IPD: Unused | ARD: Null ptr APD: Null ptr IRD: Unused IPD: Unused
SQL_DESC_PARAMETER_TYPE | SQLSMALLINT | ARD: Unused APD: Unused IRD: Unused IPD: R/W | ARD: Unused APD: Unused IRD: Unused IPD: D=SQL_PARAM_INPUT
SQL_DESC_PRECISION | SQLSMALLINT | ARD: R/W APD: R/W IRD: R IPD: R/W | ARD: ND APD: ND IRD: D IPD: ND
SQL_DESC_ROWVER | SQLSMALLINT | ARD: Unused APD: Unused IRD: R IPD: R | ARD: Unused APD: Unused IRD: ND IPD: ND
SQL_DESC_SCALE | SQLSMALLINT | ARD: R/W APD: R/W IRD: R IPD: R/W | ARD: ND APD: ND IRD: D IPD: ND
SQL_DESC_SCHEMA_NAME | SQLCHAR * | ARD: Unused APD: Unused IRD: R IPD: Unused | ARD: Unused APD: Unused IRD: D IPD: Unused
SQL_DESC_SEARCHABLE | SQLSMALLINT | ARD: Unused APD: Unused IRD: R IPD: Unused | ARD: Unused APD: Unused IRD: D IPD: Unused
SQL_DESC_TABLE_NAME | SQLCHAR * | ARD: Unused APD: Unused IRD: R IPD: Unused | ARD: Unused APD: Unused IRD: D IPD: Unused
SQL_DESC_TYPE | SQLSMALLINT | ARD: R/W APD: R/W IRD: R IPD: R/W | ARD: SQL_C_DEFAULT APD: SQL_C_DEFAULT IRD: D IPD: ND
SQL_DESC_TYPE_NAME | SQLCHAR * | ARD: Unused APD: Unused IRD: R IPD: R | ARD: Unused APD: Unused IRD: D IPD: D[1]
SQL_DESC_UNNAMED | SQLSMALLINT | ARD: Unused APD: Unused IRD: R IPD: R/W | ARD: ND APD: ND IRD: D IPD: ND
SQL_DESC_UNSIGNED | SQLSMALLINT | ARD: Unused APD: Unused IRD: R IPD: R | ARD: Unused APD: Unused IRD: D IPD: D[1]
SQL_DESC_UPDATABLE | SQLSMALLINT | ARD: Unused APD: Unused IRD: R IPD: Unused | ARD: Unused APD: Unused IRD: D IPD: Unused

[1] These fields are defined only when the IPD is automatically populated by the driver. If not, they are undefined. If an application attempts to set these fields, SQLSTATE HY091 (Invalid descriptor field identifier) will be returned.

[2] The SQL_DESC_DATA_PTR field in the IPD can be set to force a consistency check. In a subsequent call to SQLGetDescField or SQLGetDescRec, the driver is not required to return the value that SQL_DESC_DATA_PTR was set to.

FieldIdentifier Argument

The FieldIdentifier argument indicates the descriptor field to be set. A descriptor contains the descriptor header, consisting of the header fields described in the next section, "Header Fields," and zero or more descriptor records, consisting of the record fields described in the section following the "Header Fields" section.

Header Fields

Each descriptor has a header consisting of the following fields:

SQL_DESC_ALLOC_TYPE [All]
This read-only SQLSMALLINT header field specifies whether the descriptor was allocated automatically by the driver or explicitly by the application. The application can obtain, but not modify, this field. The field is set to SQL_DESC_ALLOC_AUTO by the driver if the descriptor was automatically allocated by the driver. It is set to SQL_DESC_ALLOC_USER by the driver if the descriptor was explicitly allocated by the application.
SQL_DESC_ARRAY_SIZE [Application descriptors]
In ARDs, this SQLUINTEGER header field specifies the number of rows in the rowset. This is the number of rows to be returned by a call to SQLFetch or SQLFetchScroll or to be operated on by a call to SQLBulkOperations or SQLSetPos.

In APDs, this SQLUINTEGER header field specifies the number of values for each parameter.

The default value of this field is 1. If SQL_DESC_ARRAY_SIZE is greater than 1, SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR of the APD or ARD point to arrays. The cardinality of each array is equal to the value of this field.

This field in the ARD can also be set by calling SQLSetStmtAttr with the SQL_ATTR_ROW_ARRAY_SIZE attribute. This field in the APD can also be set by calling SQLSetStmtAttr with the SQL_ATTR_PARAMSET_SIZE attribute.

SQL_DESC_ARRAY_STATUS_PTR [All]
For each descriptor type, this SQLUSMALLINT * header field points to an array of SQLUSMALLINT values. These arrays are named as follows: row status array (IRD), parameter status array (IPD), row operation array (ARD), and parameter operation array (APD).

In the IRD, this header field points to a row status array containing status values after a call to SQLBulkOperations, SQLFetch, SQLFetchScroll, or SQLSetPos. The array has as many elements as there are rows in the rowset. The application must allocate an array of SQLUSMALLINTs and set this field to point to the array. The field is set to a null pointer by default. The driver will populate the array—unless the SQL_DESC_ARRAY_STATUS_PTR field is set to a null pointer, in which case no status values are generated and the array is not populated.

Caution Driver behavior is undefined if the application sets the elements of the row status array pointed to by the SQL_DESC_ARRAY_STATUS_PTR field of the IRD.

The array is initially populated by a call to SQLBulkOperations, SQLFetch, SQLFetchScroll, or SQLSetPos. If the call did not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the array pointed to by this field are undefined. The elements in the array can contain the following values:

  • SQL_ROW_SUCCESS: The row was successfully fetched and has not changed since it was last fetched.
  • SQL_ROW_SUCCESS_WITH_INFO: The row was successfully fetched and has not changed since it was last fetched. However, a warning was returned about the row.
  • SQL_ROW_ERROR: An error occurred while fetching the row.
  • SQL_ROW_UPDATED: The row was successfully fetched and has been updated since it was last fetched. If the row is fetched again, its status is SQL_ROW_SUCCESS.
  • SQL_ROW_DELETED: The row has been deleted since it was last fetched.
  • SQL_ROW_ADDED: The row was inserted by SQLBulkOperations. If the row is fetched again, its status is SQL_ROW_SUCCESS.
  • SQL_ROW_NOROW: The rowset overlapped the end of the result set, and no row was returned that corresponded to this element of the row status array.

This field in the IRD can also be set by calling SQLSetStmtAttr with the SQL_ATTR_ROW_STATUS_PTR attribute.

The SQL_DESC_ARRAY_STATUS_PTR field of the IRD is valid only after SQL_SUCCESS or SQL_SUCCESS_WITH_INFO has been returned. If the return code is not one of these, the location pointed to by SQL_DESC_ROWS_PROCESSED_PTR is undefined.

In the IPD, this header field points to a parameter status array containing status information for each set of parameter values after a call to SQLExecute or SQLExecDirect. If the call to SQLExecute or SQLExecDirect did not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the array pointed to by this field are undefined. The application must allocate an array of SQLUSMALLINTs and set this field to point to the array. The driver will populate the array—unless the SQL_DESC_ARRAY_STATUS_PTR field is set to a null pointer, in which case no status values are generated and the array is not populated. The elements in the array can contain the following values:

  • SQL_PARAM_SUCCESS: The SQL statement was successfully executed for this set of parameters.
  • SQL_PARAM_SUCCESS_WITH_INFO: The SQL statement was successfully executed for this set of parameters; however, warning information is available in the diagnostics data structure.
  • SQL_PARAM_ERROR: An error occurred in processing this set of parameters. Additional error information is available in the diagnostics data structure.
  • SQL_PARAM_UNUSED: This parameter set was unused, possibly due to the fact that some previous parameter set caused an error that aborted further processing, or because SQL_PARAM_IGNORE was set for that set of parameters in the array specified by the SQL_DESC_ARRAY_STATUS_PTR field of the APD.
  • SQL_PARAM_DIAG_UNAVAILABLE: Diagnostic information is not available. An example of this is when the driver treats arrays of parameters as a monolithic unit and so does not generate this level of error information.

This field in the IPD can also be set by calling SQLSetStmtAttr with the SQL_ATTR_PARAM_STATUS_PTR attribute.

In the ARD, this header field points to a row operation array of values that can be set by the application to indicate whether this row is to be ignored for SQLSetPos operations. The elements in the array can contain the following values:

  • SQL_ROW_PROCEED: The row is included in the bulk operation using SQLSetPos. (This setting does not guarantee that the operation will occur on the row. If the row has the status SQL_ROW_ERROR in the IRD row status array, the driver might not be able to perform the operation in the row.)
  • SQL_ROW_IGNORE: The row is excluded from the bulk operation using SQLSetPos.

If no elements of the array are set, all rows are included in the bulk operation. If the value in the SQL_DESC_ARRAY_STATUS_PTR field of the ARD is a null pointer, all rows are included in the bulk operation; the interpretation is the same as if the pointer pointed to a valid array and all elements of the array were SQL_ROW_PROCEED. If an element in the array is set to SQL_ROW_IGNORE, the value in the row status array for the ignored row is not changed.

This field in the ARD can also be set by calling SQLSetStmtAttr with the SQL_ATTR_ROW_OPERATION_PTR attribute.

In the APD, this header field points to a parameter operation array of values that can be set by the application to indicate whether this set of parameters is to be ignored when SQLExecute or SQLExecDirect is called. The elements in the array can contain the following values:

  • SQL_PARAM_PROCEED: The set of parameters is included in the SQLExecute or SQLExecDirect call.
  • SQL_PARAM_IGNORE: The set of parameters is excluded from the SQLExecute or SQLExecDirect call.

If no elements of the array are set, all sets of parameters in the array are used in the SQLExecute or SQLExecDirect calls. If the value in the SQL_DESC_ARRAY_STATUS_PTR field of the APD is a null pointer, all sets of parameters are used; the interpretation is the same as if the pointer pointed to a valid array and all elements of the array were SQL_PARAM_PROCEED.

This field in the APD can also be set by calling SQLSetStmtAttr with the SQL_ATTR_PARAM_OPERATION_PTR attribute.

SQL_DESC_BIND_OFFSET_PTR [Application descriptors]
This SQLINTEGER * header field points to the binding offset. It is set to a null pointer by default. If this field is not a null pointer, the driver dereferences the pointer and adds the dereferenced value to each of the deferred fields that has a non-null value in the descriptor record (SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR) at fetch time and uses the new pointer values when binding.

The binding offset is always added directly to the values in the SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR fields. If the offset is changed to a different value, the new value is still added directly to the value in each descriptor field. The new offset is not added to the field value plus any earlier offset.

This field is a deferred field: It is not used at the time it is set but is used at a later time by the driver when it needs to determine addresses for data buffers.

This field in the ARD can also be set by calling SQLSetStmtAttr with the SQL_ATTR_ROW_BIND_OFFSET_PTR attribute. This field in the ARD can also be set by calling SQLSetStmtAttr with the SQL_ATTR_PARAM_BIND_OFFSET_PTR attribute.

For more information, see the description of row-wise binding in SQLFetchScroll and SQLBindParameter.

SQL_DESC_BIND_TYPE [Application descriptors]
This SQLUINTEGER header field sets the binding orientation to be used for binding either columns or parameters.

In ARDs, this field specifies the binding orientation when SQLFetchScroll or SQLFetch is called on the associated statement handle.

To select column-wise binding for columns, this field is set to SQL_BIND_BY_COLUMN (the default).

This field in the ARD can also be set by calling SQLSetStmtAttr with the SQL_ATTR_ROW_BIND_TYPE Attribute.

In APDs, this field specifies the binding orientation to be used for dynamic parameters.

To select column-wise binding for parameters, this field is set to SQL_BIND_BY_COLUMN (the default).

This field in the APD can also be set by calling SQLSetStmtAttr with the SQL_ATTR_PARAM_BIND_TYPE Attribute.

SQL_DESC_COUNT [All]
This SQLSMALLINT header field specifies the 1-based index of the highest-numbered record that contains data. When the driver sets the data structure for the descriptor, it must also set the SQL_DESC_COUNT field to show how many records are significant. When an application allocates an instance of this data structure, it does not have to specify how many records to reserve room for. As the application specifies the contents of the records, the driver takes any required action to ensure that the descriptor handle refers to a data structure of the adequate size.

SQL_DESC_COUNT is not a count of all data columns that are bound (if the field is in an ARD) or of all parameters that are bound (if the field is in an APD), but the number of the highest-numbered record. If the highest-numbered column or parameter is unbound, then SQL_DESC_COUNT is changed to the number of the next highest-numbered column or parameter. If a column or a parameter with a number that is less than the number of the highest-numbered column is unbound (by calling SQLBindCol with the TargetValuePtr argument set to a null pointer, or SQLBindParameter with the ParameterValuePtr argument set to a null pointer), SQL_DESC_COUNT is not changed. If additional columns or parameters are bound with numbers greater than the highest-numbered record that contains data, the driver automatically increases the value in the SQL_DESC_COUNT field. If all columns are unbound by calling SQLFreeStmt with the SQL_UNBIND option, the SQL_DESC_COUNT fields in the ARD and IRD are set to 0. If SQLFreeStmt is called with the SQL_RESET_PARAMS option, the SQL_DESC_COUNT fields in the APD and IPD are set to 0.

The value in SQL_DESC_COUNT can be set explicitly by an application by calling SQLSetDescField. If the value in SQL_DESC_COUNT is explicitly decreased, all records with numbers greater than the new value in SQL_DESC_COUNT are effectively removed. If the value in SQL_DESC_COUNT is explicitly set to 0 and the field is in an ARD, all data buffers except a bound bookmark column are released.

The record count in this field of an ARD does not include a bound bookmark column. The only way to unbind a bookmark column is to set the SQL_DESC_DATA_PTR field to a null pointer.

SQL_DESC_ROWS_PROCESSED_PTR [Implementation descriptors]
In an IRD, this SQLUINTEGER * header field points to a buffer containing the number of rows fetched after a call to SQLFetch or SQLFetchScroll, or the number of rows affected in a bulk operation performed by a call to SQLBulkOperations or SQLSetPos, including error rows.

In an IPD, this SQLUINTEGER * header field points to a buffer containing the number of sets of parameters that have been processed, including error sets. No number will be returned if this is a null pointer.

SQL_DESC_ROWS_PROCESSED_PTR is valid only after SQL_SUCCESS or SQL_SUCCESS_WITH_INFO has been returned after a call to SQLFetch or SQLFetchScroll (for an IRD field) or SQLExecute, SQLExecDirect, or SQLParamData (for an IPD field). If the call that fills in the buffer pointed to by this field does not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the buffer are undefined, unless it returns SQL_NO_DATA, in which case the value in the buffer is set to 0.

This field in the ARD can also be set by calling SQLSetStmtAttr with the SQL_ATTR_ROWS_FETCHED_PTR attribute. This field in the APD can also be set by calling SQLSetStmtAttr with the SQL_ATTR_PARAMS_PROCESSED_PTR attribute.

The buffer pointed to by this field is allocated by the application. It is a deferred output buffer that is set by the driver. It is set to a null pointer by default.

Record Fields

Each descriptor contains one or more records consisting of fields that define either column data or dynamic parameters, depending on the type of descriptor. Each record is a complete definition of a single column or parameter.

SQL_DESC_AUTO_UNIQUE_VALUE [IRDs]
This read-only SQLINTEGER record field contains SQL_TRUE if the column is an auto-incrementing column, or SQL_FALSE if the column is not an auto-incrementing column. This field is read-only, but the underlying auto-incrementing column is not necessarily read-only.
SQL_DESC_BASE_COLUMN_NAME [IRDs]
This read-only SQLCHAR * record field contains the base column name for the result set column. If a base column name does not exist (as in the case of columns that are expressions), this variable contains an empty string.
SQL_DESC_BASE_TABLE_NAME [IRDs]
This read-only SQLCHAR * record field contains the base table name for the result set column. If a base table name cannot be defined or is not applicable, this variable contains an empty string.
SQL_DESC_CASE_SENSITIVE [Implementation descriptors]
This read-only SQLINTEGER record field contains SQL_TRUE if the column or parameter is treated as case-sensitive for collations and comparisons, or SQL_FALSE if the column is not treated as case-sensitive for collations and comparisons or if it is a noncharacter column.
SQL_DESC_CATALOG_NAME [IRDs]
This read-only SQLCHAR * record field contains the catalog for the base table that contains the column. The return value is driver-dependent if the column is an expression or if the column is part of a view. If the data source does not support catalogs or the catalog cannot be determined, this variable contains an empty string.
SQL_DESC_CONCISE_TYPE [All]
This SQLSMALLINT header field specifies the concise data type for all data types, including the datetime and interval data types.

The values in the SQL_DESC_CONCISE_TYPE, SQL_DESC_TYPE, and SQL_DESC_DATETIME_INTERVAL_CODE fields are interdependent. Each time one of the fields is set, the other must also be set. SQL_DESC_CONCISE_TYPE can be set by a call to SQLBindCol or SQLBindParameter, or SQLSetDescField. SQL_DESC_TYPE can be set by a call to SQLSetDescField or SQLSetDescRec.

If SQL_DESC_CONCISE_TYPE is set to a concise data type other than an interval or datetime data type, the SQL_DESC_TYPE field is set to the same value and the SQL_DESC_DATETIME_INTERVAL_CODE field is set to 0.

If SQL_DESC_CONCISE_TYPE is set to the concise datetime or interval data type, the SQL_DESC_TYPE field is set to the corresponding verbose type (SQL_DATETIME or SQL_INTERVAL) and the SQL_DESC_DATETIME_INTERVAL_CODE field is set to the appropriate subcode.

SQL_DESC_DATA_PTR [Application descriptors and IPDs]
This SQLPOINTER record field points to a variable that will contain the parameter value (for APDs) or the column value (for ARDs). This field is a deferred field. It is not used at the time it is set but is used at a later time by the driver to retrieve data.

The column specified by the SQL_DESC_DATA_PTR field of the ARD is unbound if the TargetValuePtr argument in a call to SQLBindCol is a null pointer or if the SQL_DESC_DATA_PTR field in the ARD is set by a call to SQLSetDescField or SQLSetDescRec to a null pointer. Other fields are not affected if the SQL_DESC_DATA_PTR field is set to a null pointer.

If the call to SQLFetch or SQLFetchScroll that fills in the buffer pointed to by this field did not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the buffer are undefined.

Whenever the SQL_DESC_DATA_PTR field of an APD, ARD, or IPD is set, the driver checks that the value in the SQL_DESC_TYPE field contains one of the valid ODBC C data types or a driver-specific data type, and that all other fields affecting the data types are consistent. Prompting a consistency check is the only use of the SQL_DESC_DATA_PTR field of an IPD. Specifically, if an application sets the SQL_DESC_DATA_PTR field of an IPD and later calls SQLGetDescField on this field, it is not necessarily returned the value that it had set. For more information, see "Consistency Checks" in SQLSetDescRec.

SQL_DESC_DATETIME_INTERVAL_CODE [All]
This SQLSMALLINT record field contains the subcode for the specific datetime or interval data type when the SQL_DESC_TYPE field is SQL_DATETIME or SQL_INTERVAL. This is true for both SQL and C data types. The code consists of the data type name with "CODE" substituted for either "TYPE" or "C_TYPE" (for datetime types), or "CODE" substituted for "INTERVAL" or "C_INTERVAL" (for interval types).

If SQL_DESC_TYPE and SQL_DESC_CONCISE_TYPE in an application descriptor are set to SQL_C_DEFAULT and the descriptor is not associated with a statement handle, the contents of SQL_DESC_DATETIME_INTERVAL_CODE are undefined.

This field can be set for the datetime data types listed in the following table.

Datetime types | DATETIME_INTERVAL_CODE
SQL_TYPE_DATE/SQL_C_TYPE_DATE | SQL_CODE_DATE
SQL_TYPE_TIME/SQL_C_TYPE_TIME | SQL_CODE_TIME
SQL_TYPE_TIMESTAMP/
SQL_C_TYPE_TIMESTAMP 		SQL_CODE_TIMESTAMP

This field can be set for the interval data types listed in the following table.

Interval type DATETIME_INTERVAL_CODE
SQL_INTERVAL_DAY/
SQL_C_INTERVAL_DAY 		SQL_CODE_DAY
SQL_INTERVAL_DAY_TO_HOUR/
SQL_C_INTERVAL_DAY_TO_HOUR 		SQL_CODE_DAY_TO_HOUR
SQL_INTERVAL_DAY_TO_MINUTE/
SQL_C_INTERVAL_DAY_TO_MINUTE 		SQL_CODE_DAY_TO_MINUTE
SQL_INTERVAL_DAY_TO_SECOND/
SQL_C_INTERVAL_DAY_TO_SECOND 		SQL_CODE_DAY_TO_SECOND
SQL_INTERVAL_HOUR/
SQL_C_INTERVAL_HOUR 		SQL_CODE_HOUR
SQL_INTERVAL_HOUR_TO_MINUTE/
SQL_C_INTERVAL_HOUR_TO_MINUTE 		SQL_CODE_HOUR_TO_MINUTE
SQL_INTERVAL_HOUR_TO_SECOND/
SQL_C_INTERVAL_HOUR_TO_SECOND 		SQL_CODE_HOUR_TO_SECOND
SQL_INTERVAL_MINUTE/
SQL_C_INTERVAL_MINUTE 		SQL_CODE_MINUTE
SQL_INTERVAL_MINUTE_TO_SECOND/
SQL_C_INTERVAL_MINUTE_TO_SECOND 		SQL_CODE_MINUTE_TO_SECOND
SQL_INTERVAL_MONTH/
SQL_C_INTERVAL_MONTH 		SQL_CODE_MONTH
SQL_INTERVAL_SECOND/
SQL_C_INTERVAL_SECOND 		SQL_CODE_SECOND
SQL_INTERVAL_YEAR/
SQL_C_INTERVAL_YEAR 		SQL_CODE_YEAR
SQL_INTERVAL_YEAR_TO_MONTH/
SQL_C_INTERVAL_YEAR_TO_MONTH 		SQL_CODE_YEAR_TO_MONTH

For more information about the data intervals and this field, see "Data Type Identifiers and Descriptors" in Appendix D: Data Types.

SQL_DESC_DATETIME_INTERVAL_PRECISION [All]
This SQLINTEGER record field contains the interval leading precision if the SQL_DESC_TYPE field is SQL_INTERVAL. When the SQL_DESC_DATETIME_INTERVAL_CODE field is set to an interval data type, this field is set to the default interval leading precision.
SQL_DESC_DISPLAY_SIZE [IRDs]
This read-only SQLINTEGER record field contains the maximum number of characters required to display the data from the column.
SQL_DESC_FIXED_PREC_SCALE [Implementation descriptors]
This read-only SQLSMALLINT record field is set to SQL_TRUE if the column is an exact numeric column and has a fixed precision and nonzero scale, or to SQL_FALSE if the column is not an exact numeric column with a fixed precision and scale.
SQL_DESC_INDICATOR_PTR [Application descriptors]
In ARDs, this SQLINTEGER * record field points to the indicator variable. This variable contains SQL_NULL_DATA if the column value is a NULL. For APDs, the indicator variable is set to SQL_NULL_DATA to specify NULL dynamic arguments. Otherwise, the variable is zero (unless the values in SQL_DESC_INDICATOR_PTR and SQL_DESC_OCTET_LENGTH_PTR are the same pointer).

If the SQL_DESC_INDICATOR_PTR field in an ARD is a null pointer, the driver is prevented from returning information about whether the column is NULL or not. If the column is NULL and SQL_DESC_INDICATOR_PTR is a null pointer, SQLSTATE 22002 (Indicator variable required but not supplied) is returned when the driver attempts to populate the buffer after a call to SQLFetch or SQLFetchScroll. If the call to SQLFetch or SQLFetchScroll did not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the buffer are undefined.

The SQL_DESC_INDICATOR_PTR field determines whether the field pointed to by SQL_DESC_OCTET_LENGTH_PTR is set. If the data value for a column is NULL, the driver sets the indicator variable to SQL_NULL_DATA. The field pointed to by SQL_DESC_OCTET_LENGTH_PTR is then not set. If a NULL value is not encountered during the fetch, the buffer pointed to by SQL_DESC_INDICATOR_PTR is set to zero and the buffer pointed to by SQL_DESC_OCTET_LENGTH_PTR is set to the length of the data.

If the SQL_DESC_INDICATOR_PTR field in an APD is a null pointer, the application cannot use this descriptor record to specify NULL arguments.

This field is a deferred field: It is not used at the time it is set but is used at a later time by the driver to indicate nullability (for ARDs) or to determine nullability (for APDs).

SQL_DESC_LABEL [IRDs]
This read-only SQLCHAR * record field contains the column label or title. If the column does not have a label, this variable contains the column name. If the column is unnamed and unlabeled, this variable contains an empty string.
SQL_DESC_LENGTH [All]
This SQLUINTEGER record field is either the maximum or actual length of a character string in characters or a binary data type in bytes. It is the maximum length for a fixed-length data type, or the actual length for a variable-length data type. Its value always excludes the null-termination character that ends the character string. For values whose type is SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TYPE_TIMESTAMP, or one of the SQL interval data types, this field has the length in characters of the character string representation of the datetime or interval value.

The value in this field may be different from the value for "length" as defined in ODBC 2.x. For more information, see Appendix D: Data Types.

SQL_DESC_LITERAL_PREFIX [IRDs]
This read-only SQLCHAR * record field contains the character or characters that the driver recognizes as a prefix for a literal of this data type. This variable contains an empty string for a data type for which a literal prefix is not applicable.
SQL_DESC_LITERAL_SUFFIX [IRDs]
This read-only SQLCHAR * record field contains the character or characters that the driver recognizes as a suffix for a literal of this data type. This variable contains an empty string for a data type for which a literal suffix is not applicable.
SQL_DESC_LOCAL_TYPE_NAME [Implementation descriptors]
This read-only SQLCHAR * record field contains any localized (native language) name for the data type that may be different from the regular name of the data type. If there is no localized name, an empty string is returned. This field is for display purposes only.
SQL_DESC_NAME [Implementation descriptors]
This SQLCHAR * record field in a row descriptor contains the column alias, if it applies. If the column alias does not apply, the column name is returned. In either case, the driver sets the SQL_DESC_UNNAMED field to SQL_NAMED when it sets the SQL_DESC_NAME field. If there is no column name or a column alias, the driver returns an empty string in the SQL_DESC_NAME field and sets the SQL_DESC_UNNAMED field to SQL_UNNAMED.

An application can set the SQL_DESC_NAME field of an IPD to a parameter name or alias to specify stored procedure parameters by name. (For more information, see "Binding Parameters by Name (Named Parameters)" in Chapter 9: Executing Statements.) The SQL_DESC_NAME field of an IRD is a read-only field; SQLSTATE HY091 (Invalid descriptor field identifier) will be returned if an application attempts to set it.

In IPDs, this field is undefined if the driver does not support named parameters. If the driver supports named parameters and is capable of describing parameters, the parameter name is returned in this field.

SQL_DESC_NULLABLE [Implementation descriptors]
In IRDs, this read-only SQLSMALLINT record field is SQL_NULLABLE if the column can have NULL values, SQL_NO_NULLS if the column does not have NULL values, or SQL_NULLABLE_UNKNOWN if it is not known whether the column accepts NULL values. This field pertains to the result set column, not the base column.

In IPDs, this field is always set to SQL_NULLABLE because dynamic parameters are always nullable and cannot be set by an application.

SQL_DESC_NUM_PREC_RADIX [All]
This SQLINTEGER field contains a value of 2 if the data type in the SQL_DESC_TYPE field is an approximate numeric data type, because the SQL_DESC_PRECISION field contains the number of bits. This field contains a value of 10 if the data type in the SQL_DESC_TYPE field is an exact numeric data type, because the SQL_DESC_PRECISION field contains the number of decimal digits. This field is set to 0 for all non-numeric data types.
SQL_DESC_OCTET_LENGTH [All]
This SQLINTEGER record field contains the length, in bytes, of a character string or binary data type. For fixed-length character or binary types, this is the actual length in bytes. For variable-length character or binary types, this is the maximum length in bytes. This value always excludes space for the null-termination character for implementation descriptors and always includes space for the null-termination character for application descriptors. For application data, this field contains the size of the buffer. For APDs, this field is defined only for output or input/output parameters.
SQL_DESC_OCTET_LENGTH_PTR [Application descriptors]
This SQLINTEGER * record field points to a variable that will contain the total length in bytes of a dynamic argument (for parameter descriptors) or of a bound column value (for row descriptors).

For an APD, this value is ignored for all arguments except character string and binary; if this field points to SQL_NTS, the dynamic argument must be null-terminated. To indicate that a bound parameter will be a data-at-execution parameter, an application sets this field in the appropriate record of the APD to a variable that, at execute time, will contain the value SQL_DATA_AT_EXEC or the result of the SQL_LEN_DATA_AT_EXEC macro. If there is more than one such field, SQL_DESC_DATA_PTR can be set to a value uniquely identifying the parameter to help the application determine which parameter is being requested.

If the OCTET_LENGTH_PTR field of an ARD is a null pointer, the driver does not return length information for the column. If the SQL_DESC_OCTET_LENGTH_PTR field of an APD is a null pointer, the driver assumes that character strings and binary values are null-terminated. (Binary values should not be null-terminated but should be given a length to avoid truncation.)

If the call to SQLFetch or SQLFetchScroll that fills in the buffer pointed to by this field did not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the buffer are undefined. This field is a deferred field. It is not used at the time it is set but is used at a later time by the driver to determine or indicate the octet length of the data.

SQL_DESC_PARAMETER_TYPE [IPDs]
This SQLSMALLINT record field is set to SQL_PARAM_INPUT for an input parameter, SQL_PARAM_INPUT_OUTPUT for an input/output parameter, or SQL_PARAM_OUTPUT for an output parameter. It is set to SQL_PARAM_INPUT by default.

For an IPD, the field is set to SQL_PARAM_INPUT by default if the IPD is not automatically populated by the driver (the SQL_ATTR_ENABLE_AUTO_IPD statement attribute is SQL_FALSE). An application should set this field in the IPD for parameters that are not input parameters.

SQL_DESC_PRECISION [All]
This SQLSMALLINT record field contains the number of digits for an exact numeric type, the number of bits in the mantissa (binary precision) for an approximate numeric type, or the numbers of digits in the fractional seconds component for the SQL_TYPE_TIME, SQL_TYPE_TIMESTAMP, or SQL_INTERVAL_SECOND data type. This field is undefined for all other data types.

The value in this field may be different from the value for "precision" as defined in ODBC 2.x. For more information, see Appendix D: Data Types.

SQL_DESC_ROWVER [Implementation descriptors]
This SQLSMALLINT record field indicates whether a column is automatically modified by the DBMS when a row is updated (for example, a column of the type "timestamp" in SQL Server). The value of this record field is set to SQL_TRUE if the column is a row versioning column, and to SQL_FALSE otherwise. This column attribute is similar to calling SQLSpecialColumns with IdentifierType of SQL_ROWVER to determine whether a column is automatically updated.
SQL_DESC_SCALE [All]
This SQLSMALLINT record field contains the defined scale for decimal and numeric data types. The field is undefined for all other data types.

The value in this field may be different from the value for "scale" as defined in ODBC 2.x. For more information, see Appendix D: Data Types.

SQL_DESC_SCHEMA_NAME [IRDs]
This read-only SQLCHAR * record field contains the schema name of the base table that contains the column. The return value is driver-dependent if the column is an expression or if the column is part of a view. If the data source does not support schemas or the schema name cannot be determined, this variable contains an empty string.
SQL_DESC_SEARCHABLE [IRDs]
This read-only SQLSMALLINT record field is set to one of the following values:
  • SQL_PRED_NONE if the column cannot be used in a WHERE clause. (This is the same as the SQL_UNSEARCHABLE value in ODBC 2.x.)
  • SQL_PRED_CHAR if the column can be used in a WHERE clause but only with the LIKE predicate. (This is the same as the SQL_LIKE_ONLY value in ODBC 2.x.)
  • SQL_PRED_BASIC if the column can be used in a WHERE clause with all the comparison operators except LIKE. (This is the same as the SQL_EXCEPT_LIKE value in ODBC 2.x.)
  • SQL_PRED_SEARCHABLE if the column can be used in a WHERE clause with any comparison operator.
SQL_DESC_TABLE_NAME [IRDs]
This read-only SQLCHAR * record field contains the name of the base table that contains this column. The return value is driver-dependent if the column is an expression or if the column is part of a view.
SQL_DESC_TYPE [All]
This SQLSMALLINT record field specifies the concise SQL or C data type for all data types except datetime and interval data types. For the datetime and interval data types, this field specifies the verbose data type, which is SQL_DATETIME or SQL_INTERVAL.

Whenever this field contains SQL_DATETIME or SQL_INTERVAL, the SQL_DESC_DATETIME_INTERVAL_CODE field must contain the appropriate subcode for the concise type. For datetime data types, SQL_DESC_TYPE contains SQL_DATETIME, and the SQL_DESC_DATETIME_INTERVAL_CODE field contains a subcode for the specific datetime data type. For interval data types, SQL_DESC_TYPE contains SQL_INTERVAL and the SQL_DESC_DATETIME_INTERVAL_CODE field contains a subcode for the specific interval data type.

The values in the SQL_DESC_TYPE and SQL_DESC_CONCISE_TYPE fields are interdependent. Each time one of the fields is set, the other must also be set. SQL_DESC_TYPE can be set by a call to SQLSetDescField or SQLSetDescRec. SQL_DESC_CONCISE_TYPE can be set by a call to SQLBindCol or SQLBindParameter, or SQLSetDescField.

If SQL_DESC_TYPE is set to a concise data type other than an interval or datetime data type, the SQL_DESC_CONCISE_TYPE field is set to the same value and the SQL_DESC_DATETIME_INTERVAL_CODE field is set to 0.

If SQL_DESC_TYPE is set to the verbose datetime or interval data type (SQL_DATETIME or SQL_INTERVAL) and the SQL_DESC_DATETIME_INTERVAL_CODE field is set to the appropriate subcode, the SQL_DESC_CONCISE TYPE field is set to the corresponding concise type. Trying to set SQL_DESC_TYPE to one of the concise datetime or interval types will return SQLSTATE HY021 (Inconsistent descriptor information).

When the SQL_DESC_TYPE field is set by a call to SQLBindCol, SQLBindParameter, or SQLSetDescField, the following fields are set to the following default values, as shown in the table below. The values of the remaining fields of the same record are undefined.

Value of SQL_DESC_TYPE | Other fields implicitly set
SQL_CHAR, SQL_VARCHAR, SQL_C_CHAR, SQL_C_VARCHAR | SQL_DESC_LENGTH is set to 1. SQL_DESC_PRECISION is set to 0.
SQL_DATETIME | When SQL_DESC_DATETIME_INTERVAL_CODE is set to SQL_CODE_DATE or SQL_CODE_TIME, SQL_DESC_PRECISION is set to 0. When it is set to SQL_DESC_TIMESTAMP, SQL_DESC_PRECISION is set to 6.
SQL_DECIMAL, SQL_NUMERIC,
SQL_C_NUMERIC 		SQL_DESC_SCALE is set to 0. SQL_DESC_PRECISION is set to the implementation-defined precision for the respective data type.
SQL_FLOAT, SQL_C_FLOAT | SQL_DESC_PRECISION is set to the implementation-defined default precision for SQL_FLOAT.
SQL_INTERVAL | When SQL_DESC_DATETIME_INTERVAL_CODE is set to an interval data type, SQL_DESC_DATETIME_INTERVAL_PRECISION is set to 2 (the default interval leading precision). When the interval has a seconds component, SQL_DESC_PRECISION is set to 6 (the default interval seconds precision).

When an application calls SQLSetDescField to set fields of a descriptor rather than calling SQLSetDescRec, the application must first declare the data type. When it does, the other fields indicated in the previous table are implicitly set. If any of the values implicitly set are unacceptable, the application can then call SQLSetDescField or SQLSetDescRec to set the unacceptable value explicitly.

SQL_DESC_TYPE_NAME [Implementation descriptors]
This read-only SQLCHAR * record field contains the data source–dependent type name (for example, "CHAR", "VARCHAR", and so on). If the data type name is unknown, this variable contains an empty string.
SQL_DESC_UNNAMED [Implementation descriptors]
This SQLSMALLINT record field in a row descriptor is set by the driver to either SQL_NAMED or SQL_UNNAMED when it sets the SQL_DESC_NAME field. If the SQL_DESC_NAME field contains a column alias or if the column alias does not apply, the driver sets the SQL_DESC_UNNAMED field to SQL_NAMED. If an application sets the SQL_DESC_NAME field of an IPD to a parameter name or alias, the driver sets the SQL_DESC_UNNAMED field of the IPD to SQL_NAMED. If there is no column name or a column alias, the driver sets the SQL_DESC_UNNAMED field to SQL_UNNAMED.

An application can set the SQL_DESC_UNNAMED field of an IPD to SQL_UNNAMED. A driver returns SQLSTATE HY091 (Invalid descriptor field identifier) if an application attempts to set the SQL_DESC_UNNAMED field of an IPD to SQL_NAMED. The SQL_DESC_UNNAMED field of an IRD is read-only; SQLSTATE HY091 (Invalid descriptor field identifier) will be returned if an application attempts to set it.

SQL_DESC_UNSIGNED [Implementation descriptors]
This read-only SQLSMALLINT record field is set to SQL_TRUE if the column type is unsigned or non-numeric, or SQL_FALSE if the column type is signed.
SQL_DESC_UPDATABLE [IRDs]
This read-only SQLSMALLINT record field is set to one of the following values:
  • SQL_ATTR_READ_ONLY if the result set column is read-only.
  • SQL_ATTR_WRITE if the result set column is read-write.
  • SQL_ATTR_READWRITE_UNKNOWN if it is not known whether the result set column is updatable or not.

SQL_DESC_UPDATABLE describes the updatability of the column in the result set, not the column in the base table. The updatability of the column in the base table on which this result set column is based may be different than the value in this field. Whether a column is updatable can be based on the data type, user privileges, and the definition of the result set itself. If it is unclear whether a column is updatable, SQL_ATTR_READWRITE_UNKNOWN should be returned.

Consistency Checks

A consistency check is performed by the driver automatically whenever an application passes in a value for the SQL_DESC_DATA_PTR field of the ARD, APD, or IPD. If any of the fields is inconsistent with other fields, SQLSetDescField will return SQLSTATE HY021 (Inconsistent descriptor information). For more information, see "Consistency Check" in SQLSetDescRec.

Related Functions

For information about | See
Binding a column | SQLBindCol
Binding a parameter | SQLBindParameter
Getting a descriptor field | SQLGetDescField
Getting multiple descriptor fields | SQLGetDescRec
Setting multiple descriptor fields | SQLSetDescRec


SQLSetDescRec

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

The SQLSetDescRec function sets multiple descriptor fields that affect the data type and buffer bound to a column or parameter data.

Syntax

SQLRETURN SQLSetDescRec(
     SQLHDESC     DescriptorHandle,
     SQLSMALLINT     RecNumber,
     SQLSMALLINT     Type,
     SQLSMALLINT     SubType,
     SQLINTEGER     Length,
     SQLSMALLINT     Precision,
     SQLSMALLINT     Scale,
     SQLPOINTER     DataPtr,
     SQLINTEGER *     StringLengthPtr,
     SQLINTEGER *     IndicatorPtr);

Arguments

DescriptorHandle
[Input]
Descriptor handle. This must not be an IRD handle.
RecNumber
[Input]
Indicates the descriptor record that contains the fields to be set. Descriptor records are numbered from 0, with record number 0 being the bookmark record. This argument must be equal to or greater than 0. If RecNumber is greater than the value of SQL_DESC_COUNT, SQL_DESC_COUNT is changed to the value of RecNumber.
Type
[Input]
The value to which to set the SQL_DESC_TYPE field for the descriptor record.
SubType
[Input]
For records whose type is SQL_DATETIME or SQL_INTERVAL, this is the value to which to set the SQL_DESC_DATETIME_INTERVAL_CODE field.
Length
[Input]
The value to which to set the SQL_DESC_OCTET_LENGTH field for the descriptor record.
Precision
[Input]
The value to which to set the SQL_DESC_PRECISION field for the descriptor record.
Scale
[Input]
The value to which to set the SQL_DESC_SCALE field for the descriptor record.
DataPtr
[Deferred Input or Output]
The value to which to set the SQL_DESC_DATA_PTR field for the descriptor record. DataPtr can be set to a null pointer.

The DataPtr argument can be set to a null pointer to set the SQL_DESC_DATA_PTR field to a null pointer. If the handle in the DescriptorHandle argument is associated with an ARD, this unbinds the column.

StringLengthPtr
[Deferred Input or Output]
The value to which to set the SQL_DESC_OCTET_LENGTH_PTR field for the descriptor record. StringLengthPtr can be set to a null pointer to set the SQL_DESC_OCTET_LENGTH_PTR field to a null pointer.
IndicatorPtr
[Deferred Input or Output]
The value to which to set the SQL_DESC_INDICATOR_PTR field for the descriptor record. IndicatorPtr can be set to a null pointer to set the SQL_DESC_INDICATOR_PTR field to a null pointer.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLSetDescRec returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_DESC and a Handle of DescriptorHandle. The following table lists the SQLSTATE values commonly returned by SQLSetDescRec and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
07009 Invalid descriptor index The RecNumber argument was set to 0, and the DescriptorHandle referred to an IPD handle.

The RecNumber argument was less than 0.

The RecNumber argument was greater than the maximum number of columns or parameters that the data source can support, and the DescriptorHandle argument was an APD, IPD, or ARD.

The RecNumber argument was equal to 0, and the DescriptorHandle argument referred to an implicitly allocated APD. (This error does not occur with an explicitly allocated application descriptor because it is not known whether an explicitly allocated application descriptor is an APD or ARD until execute time.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 Memory allocation
error 		The driver was unable to allocate memory required to support execution or completion of the function.
HY010 Function sequence error (DM) The DescriptorHandle was associated with a StatementHandle for which an asynchronously executing function (not this one) was called and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle with which the DescriptorHandle was associated and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY016 | Cannot modify an implementation row descriptor | The DescriptorHandle argument was associated with an IRD.
HY021 Inconsistent descriptor information The Type field, or any other field associated with the SQL_DESC_TYPE field in the descriptor, was not valid or consistent.

Descriptor information checked during a consistency check was not consistent. (See "Consistency Checks," later in this section.)
HY090 | Invalid string or buffer length | (DM) The driver was an ODBC 2.x driver, the descriptor was an ARD, the ColumnNumber argument was set to 0, and the value specified for the argument BufferLength was not equal to 4.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the DescriptorHandle does not support the function.

Comments

An application can call SQLSetDescRec to set the following fields for a single column or parameter:

  • SQL_DESC_TYPE
  • SQL_DESC_DATETIME_INTERVAL_CODE (for records whose type is SQL_DATETIME or SQL_INTERVAL)
  • SQL_DESC_OCTET_LENGTH
  • SQL_DESC_PRECISION
  • SQL_DESC_SCALE
  • SQL_DESC_DATA_PTR
  • SQL_DESC_OCTET_LENGTH_PTR
  • SQL_DESC_INDICATOR_PTR

Note If a call to SQLSetDescRec fails, the contents of the descriptor record identified by the RecNumber argument are undefined.

When binding a column or parameter, SQLSetDescRec allows you to change multiple fields affecting the binding without calling SQLBindCol or SQLBindParameter or making multiple calls to SQLSetDescField. SQLSetDescRec can set fields on a descriptor not currently associated with a statement. Note that SQLBindParameter sets more fields than SQLSetDescRec, can set fields on both an APD and an IPD in one call, and does not require a descriptor handle.

Note The statement attribute SQL_ATTR_USE_BOOKMARKS should always be set before calling SQLSetDescRec with a RecNumber argument of 0 to set bookmark fields. While this is not mandatory, it is strongly recommended.

Consistency Checks

A consistency check is performed by the driver automatically whenever an application sets the SQL_DESC_DATA_PTR field of an APD, ARD, or IPD. If any of the fields is inconsistent with other fields, SQLSetDescRec will return SQLSTATE HY021 (Inconsistent descriptor information).

Whenever an application sets the SQL_DESC_DATA_PTR field of an APD, ARD, or IPD, the driver checks that the value of the SQL_DESC_TYPE field and the values applicable to that SQL_DESC_TYPE field are valid and consistent. This check is always performed when SQLBindParameter or SQLBindCol is called or when SQLSetDescRec is called for an APD, ARD, or IPD. This consistency check includes the following checks on descriptor fields:

  • The SQL_DESC_TYPE field must be one of the valid ODBC C or SQL types or a driver-specific SQL type. The SQL_DESC_CONCISE_TYPE field must be one of the valid ODBC C or SQL types or a driver-specific C or SQL type, including the concise datetime and interval types.
  • If the SQL_DESC_TYPE record field is SQL_DATETIME or SQL_INTERVAL, the SQL_DESC_DATETIME_INTERVAL_CODE field must be one of the valid datetime or interval codes. (See the description of the SQL_DESC_DATETIME_INTERVAL_CODE field in SQLSetDescField.)
  • If the SQL_DESC_TYPE field indicates a numeric type, the SQL_DESC_PRECISION and SQL_DESC_SCALE fields are verified to be valid.
  • If the SQL_DESC_CONCISE_TYPE field is a time or timestamp data type, an interval type with a seconds component, or one of the interval data types with a time component, the SQL_DESC_PRECISION field is verified to be a valid seconds precision.
  • If the SQL_DESC_CONCISE_TYPE is an interval data type, the SQL_DESC_DATETIME_INTERVAL_PRECISION field is verified to be a valid interval leading precision value.

The SQL_DESC_DATA_PTR field of an IPD is not normally set; however, an application can do so to force a consistency check of IPD fields. A consistency check cannot be performed on an IRD. The value that the SQL_DESC_DATA_PTR field of the IPD is set to is not actually stored and cannot be retrieved by a call to SQLGetDescField or SQLGetDescRec; the setting is made only to force the consistency check.

Related Functions

For information about | See
Binding a column | SQLBindCol
Binding a parameter | SQLBindParameter
Getting a single descriptor field | SQLGetDescField
Getting multiple descriptor fields | SQLGetDescRec
Setting single descriptor fields | SQLSetDescField


SQLSetEnvAttr

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLSetEnvAttr sets attributes that govern aspects of environments.

Syntax

SQLRETURN SQLSetEnvAttr(
     SQLHENV     EnvironmentHandle,
     SQLINTEGER     Attribute,
     SQLPOINTER     ValuePtr,
     SQLINTEGER     StringLength);

Arguments

EnvironmentHandle
[Input]
Environment handle.
Attribute
[Input]
Attribute to set, listed in "Comments."
ValuePtr
[Input]
Pointer to the value to be associated with Attribute. Depending on the value of Attribute, ValuePtr will be a 32-bit integer value or point to a null-terminated character string.
StringLength
[Input] If ValuePtr points to a character string or a binary buffer, this argument should be the length of *ValuePtr. If ValuePtr is an integer, StringLength is ignored.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLSetEnvAttr returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_ENV and a Handle of EnvironmentHandle. The following table lists the SQLSTATE values commonly returned by SQLSetEnvAttr and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise. If a driver does not support an environment attribute, the error can be returned only during connect time.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01S02 | Option value changed | The driver did not support the value specified in ValuePtr and substituted a similar value. (Function returns SQL_SUCCESS_WITH_INFO.)
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY009 | Invalid use of null pointer | The Attribute argument identified an environment attribute that required a string value, and the ValuePtr argument was a null pointer.
HY010 | Function sequence error | (DM) A connection handle has been allocated on EnvironmentHandle.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY024 | Invalid attribute value | Given the specified Attribute value, an invalid value was specified in ValuePtr.
HY090 | Invalid string or buffer length | The StringLength argument was less than 0 but was not SQL_NTS.
HY092 | Invalid attribute/option identifier | (DM) The value specified for the argument Attribute was not valid for the version of ODBC supported by the driver.
HYC00 | Optional feature not implemented | The value specified for the argument Attribute was a valid ODBC environment attribute for the version of ODBC supported by the driver, but was not supported by the driver. (DM) The Attribute argument was SQL_ATTR_OUTPUT_NTS, and ValuePtr was SQL_FALSE.

Comments

An application can call SQLSetEnvAttr only if no connection handle is allocated on the environment. All environment attributes successfully set by the application for the environment persist until SQLFreeHandle is called on the environment. More than one environment handle can be allocated simultaneously in ODBC 3.x.

The format of information set through ValuePtr depends on the specified Attribute. SQLSetEnvAttr will accept attribute information in one of two different formats: a null-terminated character string or a 32-bit integer value. The format of each is noted in the attribute's description.

There are no driver-specific environment attributes.

Connection attributes cannot be set by a call to SQLSetEnvAttr. Attempting to do so will return SQLSTATE HY092 (Invalid attribute/option identifier).

Attribute ValuePtr contents
SQL_ATTR_CONNECTION_POOLING
(ODBC 3.0) 		A 32-bit SQLUINTEGER value that enables or disables connection pooling at the environment level. The following values are used:

SQL_CP_OFF = Connection pooling is turned off. This is the default.

SQL_CP_ONE_PER_DRIVER = A single connection pool is supported for each driver. Every connection in a pool is associated with one driver.

SQL_CP_ONE_PER_HENV = A single connection pool is supported for each environment. Every connection in a pool is associated with one environment.

Connection pooling is enabled by calling SQLSetEnvAttr to set the SQL_ATTR_CONNECTION_POOLING attribute to SQL_CP_ONE_PER_DRIVER or SQL_CP_ONE_PER_HENV. This call must be made before the application allocates the shared environment for which connection pooling is to be enabled. The environment handle in the call to SQLSetEnvAttr is set to null, which makes SQL_ATTR_CONNECTION_POOLING a process-level attribute. After connection pooling is enabled, the application then allocates an implicit shared environment by calling SQLAllocHandle with the InputHandle argument set to SQL_HANDLE_ENV.

After connection pooling has been enabled and a shared environment has been selected for an application, SQL_ATTR_CONNECTION_POOLING cannot be reset for that environment, because SQLSetEnvAttr is called with a null environment handle when setting this attribute. If this attribute is set while connection pooling is already enabled on a shared environment, the attribute affects only shared environments that are allocated subsequently.

For more information, see "ODBC Connection Pooling" in Chapter 6: Connecting to a Data Source or Driver.
SQL_ATTR_CP_MATCH
(ODBC 3.0) 		A 32-bit SQLUINTEGER value that determines how a connection is chosen from a connection pool. When SQLConnect or SQLDriverConnect is called, the Driver Manager determines which connection is reused from the pool. The Driver Manager attempts to match the connection options in the call and the connection attributes set by the application to the keywords and connection attributes of the connections in the pool. The value of this attribute determines the level of precision of the matching criteria.

The following values are used to set the value of this attribute:

SQL_CP_STRICT_MATCH = Only connections that exactly match the connection options in the call and the connection attributes set by the application are reused. This is the default.

SQL_CP_RELAXED_MATCH = Connections with matching connection string keywords can be used. Keywords must match, but not all connection attributes must match.

For more information on how the Driver Manager performs the match in connecting to a pooled connection, see SQLConnect. For more information on connection pooling, see "ODBC Connection Pooling" in Chapter 6: Connecting to a Data Source or Driver.
SQL_ATTR_ODBC_VERSION
(ODBC 3.0) 		A 32-bit integer that determines whether certain functionality exhibits ODBC 2.x behavior or ODBC 3.x behavior. The following values are used to set the value of this attribute:

SQL_OV_ODBC3 = The Driver Manager and driver exhibit the following ODBC 3.x behavior:

  • The driver returns and expects ODBC 3.x codes for date, time, and timestamp.
  • The driver returns ODBC 3.x SQLSTATE codes when SQLError, SQLGetDiagField, or SQLGetDiagRec is called.
  • The CatalogName argument in a call to SQLTables accepts a search pattern.

SQL_OV_ODBC2 = The Driver Manager and driver exhibit the following ODBC 2.x behavior. This is especially useful for an ODBC 2.x application working with an ODBC 3.x driver.

  • The driver returns and expects ODBC 2.x codes for date, time, and timestamp.
  • The driver returns ODBC 2.x SQLSTATE codes when SQLError, SQLGetDiagField, or SQLGetDiagRec is called.
  • The CatalogName argument in a call to SQLTables does not accept a search pattern.

An application must set this environment attribute before calling any function that has an SQLHENV argument, or the call will return SQLSTATE HY010 (Function sequence error). It is driver-specific whether or not additional behaviors exist for these environmental flags.

  • For more information, see "Declaring the Application's ODBC Version" in Chapter 6: Connecting to a Data Source or Driver and "Behavioral Changes" in Chapter 17: Programming Considerations.
SQL_ATTR_OUTPUT_NTS
(ODBC 3.0) 		A 32-bit integer that determines how the driver returns string data. If SQL_TRUE, the driver returns string data null-terminated. If SQL_FALSE, the driver does not return string data null-terminated.

This attribute defaults to SQL_TRUE. A call to SQLSetEnvAttr to set it to SQL_TRUE returns SQL_SUCCESS. A call to SQLSetEnvAttr to set it to SQL_FALSE returns SQL_ERROR and SQLSTATE HYC00 (Optional feature not implemented).

Related Functions

For information about | See
Allocating a handle | SQLAllocHandle
Returning the setting of an environment attribute | SQLGetEnvAttr


SQLSetParam

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

In ODBC 2.0, the ODBC 1.0 function SQLSetParam has been replaced by SQLBindParameter. For more information, see SQLBindParameter.

Note For more information about what the Driver Manager maps this function to when an ODBC 2.x application is working with an ODBC 3.x driver, see "Mapping Deprecated Functions" in Appendix G: Driver Guidelines for Backward Compatibility.


SQLSetPos

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ODBC

Summary

SQLSetPos sets the cursor position in a rowset and allows an application to refresh data in the rowset or to update or delete data in the result set.

Syntax

SQLRETURN SQLSetPos(
     SQLHSTMT     StatementHandle,
     SQLUSMALLINT     RowNumber,
     SQLUSMALLINT     Operation,
     SQLUSMALLINT     LockType);

Arguments

StatementHandle
[Input]
Statement handle.
RowNumber
[Input]
Position of the row in the rowset on which to perform the operation specified with the Operation argument. If RowNumber is 0, the operation applies to every row in the rowset.

For additional information, see "Comments."

Operation
[Input]
Operation to perform:

SQL_POSITION
SQL_REFRESH
SQL_UPDATE
SQL_DELETE

Note The SQL_ADD value for the Operation argument has been deprecated for ODBC 3.x. ODBC 3.x drivers will need to support SQL_ADD for backward compatibility. This functionality has been replaced by a call to SQLBulkOperations with an Operation of SQL_ADD. When an ODBC 3.x application works with an ODBC 2.x driver, the Driver Manager maps a call to SQLBulkOperations with an Operation of SQL_ADD to SQLSetPos with an Operation of SQL_ADD.

For more information, see "Comments."

LockType
[Input]
Specifies how to lock the row after performing the operation specified in the Operation argument.

SQL_LOCK_NO_CHANGE
SQL_LOCK_EXCLUSIVE
SQL_LOCK_UNLOCK

For more information, see "Comments."

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NEED_DATA, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLSetPos returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLSetPos and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

For all those SQLSTATEs that can return SQL_SUCCESS_WITH_INFO or SQL_ERROR (except 01xxx SQLSTATEs), SQL_SUCCESS_WITH_INFO is returned if an error occurs on one or more, but not all, rows of a multirow operation, and SQL_ERROR is returned if an error occurs on a single-row operation.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01001 Cursor operation conflict The Operation argument was SQL_DELETE or SQL_UPDATE, and no rows or more than one row were deleted or updated. (For more information about updates to more than one row, see the description of the SQL_ATTR_SIMULATE_CURSOR Attribute in SQLSetStmtAttr.) (Function returns SQL_SUCCESS_WITH_INFO.)

The Operation argument was SQL_DELETE or SQL_UPDATE, and the operation failed because of optimistic concurrency. (Function returns SQL_SUCCESS_WITH_INFO.)

01004 | String data right truncation | The Operation argument was SQL_REFRESH, and string or binary data returned for a column or columns with a data type of SQL_C_CHAR or SQL_C_BINARY resulted in the truncation of nonblank character or non-NULL binary data.
01S01 Error in row The RowNumber argument was 0, and an error occurred in one or more rows while performing the operation specified with the Operation argument.

(SQL_SUCCESS_WITH_INFO is returned if an error occurs on one or more, but not all, rows of a multirow operation, and SQL_ERROR is returned if an error occurs on a single-row operation.)

(This SQLSTATE is returned only when SQLSetPos is called after SQLExtendedFetch, if the driver is an ODBC 2.x driver and the cursor library is not used.)
01S07 Fractional truncation The Operation argument was SQL_REFRESH, the data type of the application buffer was not SQL_C_CHAR or SQL_C_BINARY, and the data returned to application buffers for one or more columns was truncated. For numeric data types, the fractional part of the number was truncated. For time, timestamp, and interval data types containing a time component, the fractional portion of the time was truncated.

(Function returns SQL_SUCCESS_WITH_INFO.)
07006 | Restricted data type attribute violation | The data value of a column in the result set could not be converted to the data type specified by TargetType in the call to SQLBindCol.
07009 | Invalid descriptor index | The argument Operation was SQL_REFRESH or SQL_UPDATE, and a column was bound with a column number greater than the number of columns in the result set.
21S02 | Degree of derived table does not match column list | The argument Operation was SQL_UPDATE, and no columns were updatable because all columns were either unbound, read-only, or the value in the bound length/indicator buffer was SQL_COLUMN_IGNORE.
22001 | String data, right truncation | The Operation argument was SQL_UPDATE, and the assignment of a character or binary value to a column resulted in the truncation of nonblank (for characters) or non-null (for binary) characters or bytes.
22003 Numeric value out of range The argument Operation was SQL_UPDATE, and the assignment of a numeric value to a column in the result set caused the whole (as opposed to fractional) part of the number to be truncated.

The argument Operation was SQL_REFRESH, and returning the numeric value for one or more bound columns would have caused a loss of significant digits.
22007 Invalid datetime format The argument Operation was SQL_UPDATE, and the assignment of a date or timestamp value to a column in the result set caused the year, month, or day field to be out of range.

The argument Operation was SQL_REFRESH, and returning the date or timestamp value for one or more bound columns would have caused the year, month, or day field to be out of range.
22008 Date/time field
overflow 		The Operation argument was SQL_UPDATE, and the performance of datetime arithmetic on data being sent to a column in the result set resulted in a datetime field (the year, month, day, hour, minute, or second field) of the result being outside the permissible range of values for the field, or being invalid based on the Gregorian calendar's natural rules for datetimes.

The Operation argument was SQL_REFRESH, and the performance of datetime arithmetic on data being retrieved from the result set resulted in a datetime field (the year, month, day, hour, minute, or second field) of the result being outside the permissible range of values for the field, or being invalid based on the Gregorian calendar's natural rules for datetimes.
22015 Interval field overflow The Operation argument was SQL_UPDATE, and the assignment of an exact numeric or interval C type to an interval SQL data type caused a loss of significant digits.

The Operation argument was SQL_UPDATE; when assigning to an interval SQL type, there was no representation of the value of the C type in the interval SQL type.

The Operation argument was SQL_REFRESH, and assigning from an exact numeric or interval SQL type to an interval C type caused a loss of significant digits in the leading field.

The Operation argument was SQL_ REFRESH; when assigning to an interval C type, there was no representation of the value of the SQL type in the interval C type.

22018 Invalid character value for cast specification The Operation argument was SQL_REFRESH; the C type was an exact or approximate numeric, a datetime, or an interval data type; the SQL type of the column was a character data type; and the value in the column was not a valid literal of the bound C type.

The argument Operation was SQL_UPDATE; the SQL type was an exact or approximate numeric, a datetime, or an interval data type; the C type was SQL_C_CHAR; and the value in the column was not a valid literal of the bound SQL type.

23000 | Integrity constraint violation | The argument Operation was SQL_DELETE or SQL_UPDATE, and an integrity constraint was violated.
24000 Invalid cursor state The StatementHandle was in an executed state, but no result set was associated with the StatementHandle.

(DM) A cursor was open on the StatementHandle, but SQLFetch or SQLFetchScroll had not been called.

A cursor was open on the StatementHandle, and SQLFetch or SQLFetchScroll had been called, but the cursor was positioned before the start of the result set or after the end of the result set.

The argument Operation was SQL_DELETE, SQL_REFRESH, or SQL_UPDATE, and the cursor was positioned before the start of the result set or after the end of the result set.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
42000 Syntax error or access violation The driver was unable to lock the row as needed to perform the operation requested in the argument Operation.

The driver was unable to lock the row as requested in the argument LockType.
44000 | WITH CHECK OPTION violation | The Operation argument was SQL_UPDATE, and the update was performed on a viewed table or a table derived from the viewed table which was created by specifying WITH CHECK OPTION, such that one or more rows affected by the update will no longer be present in the viewed table.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 Memory allocation
error 		The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle, and then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY010 Function sequence error (DM) The specified StatementHandle was not in an executed state. The function was called without first calling SQLExecDirect, SQLExecute, or a catalog function.

(DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.

(DM) The driver was an ODBC 2.x driver, and SQLSetPos was called for a StatementHandle after SQLFetch was called.
HY011 | Attribute cannot be set now | (DM) The driver was an ODBC 2.x driver; the SQL_ATTR_ROW_STATUS_PTR statement attribute was set; then SQLSetPos was called before SQLFetch, SQLFetchScroll, or SQLExtendedFetch was called.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 Invalid string or buffer length The Operation argument was SQL_UPDATE, a data value was a null pointer, and the column length value was not 0, SQL_DATA_AT_EXEC, SQL_COLUMN_IGNORE, SQL_NULL_DATA, or less than or equal to SQL_LEN_DATA_AT_EXEC_OFFSET.

The Operation argument was SQL_UPDATE; a data value was not a null pointer; the C data type was SQL_C_BINARY or SQL_C_CHAR; and the column length value was less than 0 but not equal to SQL_DATA_AT_EXEC, SQL_COLUMN_IGNORE, SQL_NTS, or SQL_NULL_DATA, or less than or equal to SQL_LEN_DATA_AT_EXEC_OFFSET.

The value in a length/indicator buffer was SQL_DATA_AT_EXEC; the SQL type was either SQL_LONGVARCHAR, SQL_LONGVARBINARY, or a long data source–specific data type; and the SQL_NEED_LONG_DATA_LEN information type in SQLGetInfo was "Y".
HY092 Invalid attribute identifier (DM) The value specified for the Operation argument was invalid.

(DM) The value specified for the LockType argument was invalid.

The Operation argument was SQL_UPDATE or SQL_DELETE, and the SQL_ATTR_CONCURRENCY statement attribute was SQL_ATTR_CONCUR_READ_ONLY.
HY107 | Row value out of range | The value specified for the argument RowNumber was greater than the number of rows in the rowset.
HY109 Invalid cursor position The cursor associated with the StatementHandle was defined as forward-only, so the cursor could not be positioned within the rowset. See the description for the SQL_ATTR_CURSOR_TYPE attribute in SQLSetStmtAttr.

The Operation argument was SQL_UPDATE, SQL_DELETE, or SQL_REFRESH, and the row identified by the RowNumber argument had been deleted or had not been fetched.

(DM) The RowNumber argument was 0, and the Operation argument was SQL_POSITION.

SQLSetPos was called after SQLBulkOperations was called and before SQLFetchScroll or SQLFetch was called.
HYC00 | Optional feature not implemented | The driver or data source does not support the operation requested in the Operation argument or the LockType argument.
HYT00 | Timeout expired | The query timeout period expired before the data source returned the result set. The timeout period is set through SQLSetStmtAttr with an Attribute of SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

Caution For information on the statement states that SQLSetPos can be called in and what it needs to do for compatibility with ODBC 2.x applications, see the "Block Cursors, Scrollable Cursors, and Backward Compatibility" section in Appendix G: Driver Guidelines for Backward Compatibility.

RowNumber Argument

The RowNumber argument specifies the number of the row in the rowset on which to perform the operation specified by the Operation argument. If RowNumber is 0, the operation applies to every row in the rowset. RowNumber must be a value from 0 to the number of rows in the rowset.

Note In the C language, arrays are 0-based and the RowNumber argument is 1-based. For example, to update the fifth row of the rowset, an application modifies the rowset buffers at array index 4 but specifies a RowNumber of 5.

All operations position the cursor on the row specified by RowNumber. The following operations require a cursor position:

  • Positioned update and delete statements.
  • Calls to SQLGetData.
  • Calls to SQLSetPos with the SQL_DELETE, SQL_REFRESH, and SQL_UPDATE options.

For example, if RowNumber is 2 for a call to SQLSetPos with an Operation of SQL_DELETE, the cursor is positioned on the second row of the rowset and that row is deleted. The entry in the implementation row status array (pointed to by the SQL_ATTR_ROW_STATUS_PTR statement attribute) for the second row is changed to SQL_ROW_DELETED.

An application can specify a cursor position when it calls SQLSetPos. Generally, it calls SQLSetPos with the SQL_POSITION or SQL_REFRESH operation to position the cursor before executing a positioned update or delete statement or calling SQLGetData.

Operation Argument

The Operation argument supports the following operations. To determine which options are supported by a data source, an application calls SQLGetInfo with the SQL_DYNAMIC_CURSOR_ATTRIBUTES1, SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1, SQL_KEYSET_CURSOR_ATTRIBUTES1, or SQL_STATIC_CURSOR_ATTRIBUTES1 information type (depending on the type of the cursor).

Operation
argument
Operation
SQL_POSITION The driver positions the cursor on the row specified by RowNumber.

The contents of the row status array pointed to by the SQL_ATTR_ROW_OPERATION_PTR statement attribute are ignored for the SQL_POSITION Operation.
SQL_REFRESH The driver positions the cursor on the row specified by RowNumber and refreshes data in the rowset buffers for that row. For more information about how the driver returns data in the rowset buffers, see the descriptions of row-wise and column-wise binding in SQLBindCol.

SQLSetPos with an Operation of SQL_REFRESH updates the status and content of the rows within the current fetched rowset. This includes refreshing the bookmarks. Because the data in the buffers is refreshed but not refetched, the membership in the rowset is fixed. This is different from the refresh performed by a call to SQLFetchScroll with a FetchOrientation of SQL_FETCH_RELATIVE and a RowNumber equal to 0, which refetches the rowset from the result set so that it can show added data and remove deleted data if those operations are supported by the driver and the cursor.

A successful refresh with SQLSetPos will not change a row status of SQL_ROW_DELETED. Deleted rows within the rowset will continue to be marked as deleted until the next fetch. The rows will disappear at the next fetch if the cursor supports packing (in which a subsequent SQLFetch or SQLFetchScroll does not return deleted rows).

Added rows do not appear when a refresh with SQLSetPos is performed. This behavior is different from SQLFetchScroll with a FetchType of SQL_FETCH_RELATIVE and a RowNumber equal to 0, which also refreshes the current rowset but will show added records or pack deleted records if these operations are supported by the cursor.

A successful refresh with SQLSetPos will change a row status of SQL_ROW_ADDED to SQL_ROW_SUCCESS (if the row status array exists).

A successful refresh with SQLSetPos will change a row status of SQL_ROW_UPDATED to the row's new status (if the row status array exists).

If an error occurs in a SQLSetPos operation on a row, the row status is set to SQL_ROW_ERROR (if the row status array exists).

For a cursor opened with an SQL_ATTR_CONCURRENCY statement attribute of SQL_CONCUR_ROWVER or SQL_CONCUR_VALUES, a refresh with SQLSetPos might update the optimistic concurrency values used by the data source to detect that the row has changed. If this occurs, the row versions or values used to ensure cursor concurrency are updated whenever the rowset buffers are refreshed from the server. This occurs for each row that is refreshed.

The contents of the row status array pointed to by the SQL_ATTR_ROW_OPERATION_PTR statement attribute are ignored for the SQL_REFRESH Operation.
SQL_UPDATE The driver positions the cursor on the row specified by RowNumber and updates the underlying row of data with the values in the rowset buffers (the TargetValuePtr argument in SQLBindCol). It retrieves the lengths of the data from the length/indicator buffers (the StrLen_or_IndPtr argument in SQLBindCol). If the length of any column is SQL_COLUMN_IGNORE, the column is not updated. After updating the row, the driver changes the corresponding element of the row status array to SQL_ROW_UPDATED or SQL_ROW_SUCCESS_WITH_INFO (if the row status array exists).

It is driver-defined what the behavior is if SQLSetPos with an Operation argument of SQL_UPDATE is called on a cursor that contains duplicate columns. The driver can return a driver-defined SQLSTATE, can update the first column that appears in the result set, or perform other driver-defined behavior.

The row operation array pointed to by the SQL_ATTR_ROW_OPERATION_PTR statement attribute can be used to indicate that a row in the current rowset should be ignored during a bulk update. For more information, see "Status and Operation Arrays" later in this function reference.
SQL_DELETE The driver positions the cursor on the row specified by RowNumber and deletes the underlying row of data. It changes the corresponding element of the row status array to SQL_ROW_DELETED. After the row has been deleted, the following are not valid for the row: positioned update and delete statements, calls to SQLGetData, and calls to SQLSetPos with Operation set to anything except SQL_POSITION. For drivers that support packing, the row is deleted from the cursor when new data is retrieved from the data source.

Whether the row remains visible depends on the cursor type. For example, deleted rows are visible to static and keyset-driven cursors but invisible to dynamic cursors.

The row operation array pointed to by the SQL_ATTR_ROW_OPERATION_PTR statement attribute can be used to indicate that a row in the current rowset should be ignored during a bulk delete. For more information, see "Status and Operation Arrays" later in this function reference.

LockType Argument

The LockType argument provides a way for applications to control concurrency. In most cases, data sources that support concurrency levels and transactions will support only the SQL_LOCK_NO_CHANGE value of the LockType argument. The LockType argument is generally used only for file-based support.

The LockType argument specifies the lock state of the row after SQLSetPos has been executed. If the driver is unable to lock the row either to perform the requested operation or to satisfy the LockType argument, it returns SQL_ERROR and SQLSTATE 42000 (Syntax error or access violation).

Although the LockType argument is specified for a single statement, the lock accords the same privileges to all statements on the connection. In particular, a lock that is acquired by one statement on a connection can be unlocked by a different statement on the same connection.

A row locked through SQLSetPos remains locked until the application calls SQLSetPos for the row with LockType set to SQL_LOCK_UNLOCK, or until the application calls SQLFreeHandle for the statement or SQLFreeStmt with the SQL_CLOSE option. For a driver that supports transactions, a row locked through SQLSetPos is unlocked when the application calls SQLEndTran to commit or roll back a transaction on the connection (if a cursor is closed when a transaction is committed or rolled back, as indicated by the SQL_CURSOR_COMMIT_BEHAVIOR and SQL_CURSOR_ROLLBACK_BEHAVIOR information types returned by SQLGetInfo).

The LockType argument supports the following types of locks. To determine which locks are supported by a data source, an application calls SQLGetInfo with the SQL_DYNAMIC_CURSOR_ATTRIBUTES1, SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1, SQL_KEYSET_CURSOR_ATTRIBUTES1, or SQL_STATIC_CURSOR_ATTRIBUTES1 information type (depending on the type of the cursor).

LockType argument | Lock type
SQL_LOCK_NO_CHANGE | The driver or data source ensures that the row is in the same locked or unlocked state as it was before SQLSetPos was called. This value of LockType allows data sources that do not support explicit row-level locking to use whatever locking is required by the current concurrency and transaction isolation levels.
SQL_LOCK_EXCLUSIVE | The driver or data source locks the row exclusively. A statement on a different connection or in a different application cannot be used to acquire any locks on the row.
SQL_LOCK_UNLOCK | The driver or data source unlocks the row.

If a driver supports SQL_LOCK_EXCLUSIVE but does not support SQL_LOCK_UNLOCK, a row that is locked will remain locked until one of the function calls described in the previous paragraph occurs.

If a driver supports SQL_LOCK_EXCLUSIVE but does not support SQL_LOCK_UNLOCK, a row that is locked will remain locked until the application calls SQLFreeHandle for the statement or SQLFreeStmt with the SQL_CLOSE option. If the driver supports transactions and closes the cursor upon committing or rolling back the transaction, the application calls SQLEndTran.

For the update and delete operations in SQLSetPos, the application uses the LockType argument as follows:

  • To guarantee that a row does not change after it is retrieved, an application calls SQLSetPos with Operation set to SQL_REFRESH and LockType set to SQL_LOCK_EXCLUSIVE.
  • 69If the application sets LockType to SQL_LOCK_NO_CHANGE, the driver guarantees that an update or delete operation will succeed only if the application specified SQL_CONCUR_LOCK for the SQL_ATTR_CONCURRENCY statement attribute.
  • If the application specifies SQL_CONCUR_ROWVER or SQL_CONCUR_VALUES for the SQL_ATTR_CONCURRENCY statement attribute, the driver compares row versions or values and rejects the operation if the row has changed since the application fetched the row.
  • If the application specifies SQL_CONCUR_READ_ONLY for the SQL_ATTR_CONCURRENCY statement attribute, the driver rejects any update or delete operation.

For more information about the SQL_ATTR_CONCURRENCY statement attribute, see SQLSetStmtAttr.

Status and Operation Arrays

The following status and operation arrays are used when calling SQLSetPos:

  • The row status array (as pointed to by the SQL_DESC_ARRAY_STATUS_PTR field in the IRD and the SQL_ATTR_ROW_STATUS_ARRAY statement attribute) contains status values for each row of data in the rowset. The driver sets the status values in this array after a call to SQLFetch, SQLFetchScroll, SQLBulkOperations, or SQLSetPos. This array is pointed to by the SQL_ATTR_ROW_STATUS_PTR statement attribute.
  • The row operation array (as pointed to by the SQL_DESC_ARRAY_STATUS_PTR field in the ARD and the SQL_ATTR_ROW_OPERATION_ARRAY statement attribute) contains a value for each row in the rowset that indicates whether a call to SQLSetPos for a bulk operation is ignored or performed. Each element in the array is set to either SQL_ROW_PROCEED (the default) or SQL_ROW_IGNORE. This array is pointed to by the SQL_ATTR_ROW_OPERATION_PTR statement attribute.

The number of elements in the status and operation arrays must equal the number of rows in the rowset (as defined by the SQL_ATTR_ROW_ARRAY_SIZE statement attribute).

For information about the row status array, see SQLFetch. For information about the row operation array, see "Ignoring a Row in a Bulk Operation," later in this section.

Using SQLSetPos

Before an application calls SQLSetPos, it must perform the following sequence of steps:

  1. If the application will call SQLSetPos with Operation set to SQL_UPDATE, call SQLBindCol (or SQLSetDescRec) for each column to specify its data type and bind buffers for the column's data and length.
  2. If the application will call SQLSetPos with Operation set to SQL_DELETE or SQL_UPDATE, call SQLColAttribute to make sure that the columns to be deleted or updated are updatable.
  3. Call SQLExecDirect, SQLExecute, or a catalog function to create a result set.
  4. Call SQLFetch or SQLFetchScroll to retrieve the data.

For more information about using SQLSetPos, see "Updating Data with SQLSetPos" in Chapter 12: Updating Data.

Deleting Data Using SQLSetPos

  • To delete data with SQLSetPos, an application calls SQLSetPos with RowNumber set to the number of the row to delete and Operation set to SQL_DELETE.

After the data has been deleted, the driver changes the value in the implementation row status array for the appropriate row to SQL_ROW_DELETED (or SQL_ROW_ERROR).

Updating Data Using SQLSetPos

An application can pass the value for a column either in the bound data buffer or with one or more calls to SQLPutData. Columns whose data is passed with SQLPutData are known as data-at-execution columns. These are commonly used to send data for SQL_LONGVARBINARY and SQL_LONGVARCHAR columns and can be mixed with other columns.

To update data with SQLSetPos, an application:

  1. Places values in the data and length/indicator buffers bound with SQLBindCol:
    • For normal columns, the application places the new column value in the *TargetValuePtr buffer and the length of that value in the *StrLen_or_IndPtr buffer. If the row should not be updated, the application places SQL_ROW_IGNORE in that row's element of the row operation array.
    • For data-at-execution columns, the application places an application-defined value, such as the column number, in the *TargetValuePtr buffer. The value can be used later to identify the column.

      The application places the result of the SQL_LEN_DATA_AT_EXEC(length) macro in the *StrLen_or_IndPtr buffer. If the SQL data type of the column is SQL_LONGVARBINARY, SQL_LONGVARCHAR, or a long data source–specific data type and the driver returns "Y" for the SQL_NEED_LONG_DATA_LEN information type in SQLGetInfo, length is the number of bytes of data to be sent for the parameter; otherwise, it must be a non-negative value and is ignored.

  2. Calls SQLSetPos with the Operation argument set to SQL_UPDATE to update the row of data.
    • If there are no data-at-execution columns, the process is complete.
    • If there are any data-at-execution columns, the function returns SQL_NEED_DATA and proceeds to step 3.
  3. Calls SQLParamData to retrieve the address of the *TargetValuePtr buffer for the first data-at-execution column to be processed. SQLParamData returns SQL_NEED_DATA. The application retrieves the application-defined value from the *TargetValuePtr buffer.

    Note Although data-at-execution parameters are similar to data-at-execution columns, the value returned by SQLParamData is different for each.

    Data-at-execution parameters are parameters in an SQL statement for which data will be sent with SQLPutData when the statement is executed with SQLExecDirect or SQLExecute. They are bound with SQLBindParameter or by setting descriptors with SQLSetDescRec. The value returned by SQLParamData is a 32-bit value passed to SQLBindParameter in the ParameterValuePtr argument.

    Data-at-execution columns are columns in a rowset for which data will be sent with SQLPutData when a row is updated with SQLSetPos. They are bound with SQLBindCol. The value returned by SQLParamData is the address of the row in the *TargetValuePtr buffer that is being processed.

  4. Calls SQLPutData one or more times to send data for the column. More than one call is needed if all the data values cannot be returned in the *TargetValuePtr buffer specified in SQLPutData; multiple calls to SQLPutData for the same column are allowed only when sending character C data to a column with a character, binary, or data source–specific data type or when sending binary C data to a column with a character, binary, or data source–specific data type.
  5. Calls SQLParamData again to signal that all data has been sent for the column.
    • If there are more data-at-execution columns, SQLParamData returns SQL_NEED_DATA and the address of the TargetValuePtr buffer for the next data-at-execution column to be processed. The application repeats steps 4 and 5.
    • If there are no more data-at-execution columns, the process is complete. If the statement was executed successfully, SQLParamData returns SQL_SUCCESS or SQL_SUCCESS_WITH_INFO; if the execution failed, it returns SQL_ERROR. At this point, SQLParamData can return any SQLSTATE that can be returned by SQLSetPos.

If data has been updated, the driver changes the value in the implementation row status array for the appropriate row to SQL_ROW_UPDATED.

If the operation is canceled or an error occurs in SQLParamData or SQLPutData, after SQLSetPos returns SQL_NEED_DATA and before data is sent for all data-at-execution columns, the application can call only SQLCancel, SQLGetDiagField, SQLGetDiagRec, SQLGetFunctions, SQLParamData, or SQLPutData for the statement or the connection associated with the statement. If it calls any other function for the statement or the connection associated with the statement, the function returns SQL_ERROR and SQLSTATE HY010 (Function sequence error).

If the application calls SQLCancel while the driver still needs data for data-at-execution columns, the driver cancels the operation. The application can then call SQLSetPos again; canceling does not affect the cursor state or the current cursor position.

When the SELECT-list of the query specification associated with the cursor contains more than one reference to the same column, whether an error is generated or the driver ignores the duplicated references and performs the requested operations is driver-defined.

Performing Bulk Operations

If the RowNumber argument is 0, the driver performs the operation specified in the Operation argument for every row in the rowset that has a value of SQL_ROW_PROCEED in its field in the row operation array pointed to by SQL_ATTR_ROW_OPERATION_PTR statement attribute. This is a valid value of the RowNumber argument for an Operation argument of SQL_DELETE, SQL_REFRESH, or SQL_UPDATE, but not SQL_POSITION. SQLSetPos with an Operation of SQL_POSITION and a RowNumber equal to 0 will return SQLSTATE HY109 (Invalid cursor position).

If an error occurs that pertains to the entire rowset, such as SQLSTATE HYT00 (Timeout expired), the driver returns SQL_ERROR and the appropriate SQLSTATE. The contents of the rowset buffers are undefined, and the cursor position is unchanged.

If an error occurs that pertains to a single row, the driver:

  • Sets the element for the row in the row status array pointed to by the SQL_ATTR_ROW_STATUS_PTR statement attribute to SQL_ROW_ERROR.
  • Posts one or more additional SQLSTATEs for the error in the error queue and sets the SQL_DIAG_ROW_NUMBER field in the diagnostic data structure.

After it has processed the error or warning, if the driver completes the operation for the remaining rows in the rowset, it returns SQL_SUCCESS_WITH_INFO. Thus, for each row that returned an error, the error queue contains zero or more additional SQLSTATEs. If the driver stops the operation after it has processed the error or warning, it returns SQL_ERROR.

If the driver returns any warnings, such as SQLSTATE 01004 (Data truncated), it returns warnings that apply to the entire rowset or to unknown rows in the rowset before it returns the error information that applies to specific rows. It returns warnings for specific rows along with any other error information about those rows.

If RowNumber is equal to 0 and Operation is SQL_UPDATE, SQL_REFRESH, or SQL_DELETE, the number of rows that SQLSetPos operates on is pointed to by the SQL_ATTR_ROWS_FETCHED_PTR statement attribute.

If RowNumber is equal to 0 and Operation is SQL_DELETE, SQL_REFRESH, or SQL_UPDATE, the current row after the operation is the same as the current row before the operation.

Ignoring a Row in a Bulk Operation

The row operation array can be used to indicate that a row in the current rowset should be ignored during a bulk operation using SQLSetPos. To direct the driver to ignore one or more rows during a bulk operation, an application should perform the following steps:

  1. Call SQLSetStmtAttr to set the SQL_ATTR_ROW_OPERATION_PTR statement attribute to point to an array of SQLUSMALLINTs. This field can also be set by calling SQLSetDescField to set the SQL_DESC_ARRAY_STATUS_PTR header field of the ARD, which requires that an application obtains the descriptor handle.
  2. Set each element of the row operation array to one of two values:
    • SQL_ROW_IGNORE, to indicate that the row is excluded for the bulk operation.
    • SQL_ROW_PROCEED, to indicate that the row is included in the bulk operation. (This is the default value.)
  3. Call SQLSetPos to perform the bulk operation.

The following rules apply to the row operation array:

  • SQL_ROW_IGNORE and SQL_ROW_PROCEED affect only bulk operations using SQLSetPos with an Operation of SQL_DELETE or SQL_UPDATE. They do not affect calls to SQLSetPos with an Operation of SQL_REFRESH or SQL_POSITION.
  • The pointer is set to null by default.
  • If the pointer is null, all rows are updated as if all elements were set to SQL_ROW_PROCEED.
  • Setting an element to SQL_ROW_PROCEED does not guarantee that the operation will occur on that particular row. For example, if a certain row in the rowset has the status SQL_ROW_ERROR, the driver might not be able to update that row regardless of whether the application specified SQL_ROW_PROCEED. An application must always check the row status array to see whether the operation was successful.
  • SQL_ROW_PROCEED is defined as 0 in the header file. An application can initialize the row operation array to 0 in order to process all rows.
  • If element number "n" in the row operation array is set to SQL_ROW_IGNORE and SQLSetPos is called to perform a bulk update or delete operation, the nth row in the rowset remains unchanged after the call to SQLSetPos.
  • An application should automatically set a read-only column to SQL_ROW_IGNORE.

Ignoring a Column in a Bulk Operation

To avoid unnecessary processing diagnostics generated by attempted updates to one or more read-only columns, an application can set the value in the bound length/indicator buffer to SQL_COLUMN_IGNORE. For more information, see SQLBindCol.

Code Example

In the following example, an application allows a user to browse the ORDERS table and update order status. The cursor is keyset-driven with a rowset size of 20 and uses optimistic concurrency control comparing row versions. After each rowset is fetched, the application prints it and allows the user to select and update the status of an order. The application uses SQLSetPos to position the cursor on the selected row and performs a positioned update of the row. (Error handling is omitted for clarity.)

#define ROWS 20
#define STATUS_LEN 6

SQLCHAR        szStatus[ROWS][STATUS_LEN], szReply[3];
SQLINTEGER     cbStatus[ROWS], cbOrderID;
SQLUSMALLINT   rgfRowStatus[ROWS];
SQLUINTEGER    sOrderID, crow = ROWS, irow;
SQLHSTMT       hstmtS, hstmtU;

SQLSetStmtAttr(hstmtS, SQL_ATTR_CONCURRENCY, (SQLPOINTER) SQL_CONCUR_ROWVER, 0);
SQLSetStmtAttr(hstmtS, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER) SQL_CURSOR_KEYSET_DRIVEN, 0);
SQLSetStmtAttr(hstmtS, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER) ROWS, 0);
SQLSetStmtAttr(hstmtS, SQL_ATTR_ROW_STATUS_PTR, (SQLPOINTER) rgfRowStatus, 0);
SQLSetCursorName(hstmtS, "C1", SQL_NTS);
SQLExecDirect(hstmtS, "SELECT ORDERID, STATUS FROM ORDERS ", SQL_NTS);

SQLBindCol(hstmtS, 1, SQL_C_ULONG, &sOrderID, 0, &cbOrderID);
SQLBindCol(hstmtS, 2, SQL_C_CHAR, szStatus, STATUS_LEN, &cbStatus);

while ((retcode == SQLFetchScroll(hstmtS, SQL_FETCH_NEXT, 0)) != SQL_ERROR) {
   if (retcode == SQL_NO_DATA_FOUND)
      break;
   for (irow = 0; irow < crow; irow++) {
      if (rgfRowStatus[irow] != SQL_ROW_DELETED)
         printf("%2d %5d %*s\n", irow+1, sOrderID, NAME_LEN-1, szStatus[irow]);
   }
   while (TRUE) {
      printf("\nRow number to update?");
      gets(szReply);
      irow = atoi(szReply);
      if (irow > 0 && irow <= crow) {
         printf("\nNew status?");
         gets(szStatus[irow-1]);
         SQLSetPos(hstmtS, irow, SQL_POSITION, SQL_LOCK_NO_CHANGE);
         SQLPrepare(hstmtU,
          "UPDATE ORDERS SET STATUS=? WHERE CURRENT OF C1", SQL_NTS);
         SQLBindParameter(hstmtU, 1, SQL_PARAM_INPUT,
            SQL_C_CHAR, SQL_CHAR,
            STATUS_LEN, 0, szStatus[irow], 0, NULL);
         SQLExecute(hstmtU);
      } else if (irow == 0) {
         break;
      }
   }
}

For more examples, see "Positioned Update and Delete Statements" and "Updating Rows in the Rowset with SQLSetPos" in Chapter 12: Updating Data.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Performing bulk operations that do not relate to the block cursor position | SQLBulkOperations
Canceling statement processing | SQLCancel
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Getting a single field of a descriptor | SQLGetDescField
Getting multiple fields of a descriptor | SQLGetDescRec
Setting a single field of a descriptor | SQLSetDescField
Setting multiple fields of a descriptor | SQLSetDescRec
Setting a statement attribute | SQLSetStmtAttr


SQLSetScrollOptions

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

In ODBC 3.x, the ODBC 2.0 function SQLSetScrollOptions has been replaced by calls to SQLGetInfo and SQLSetStmtAttr.

Note For more information about what the Driver Manager maps this function to when an ODBC 2.x application is working with an ODBC 3.x driver, see "Mapping Deprecated Functions" in Appendix G: Driver Guidelines for Backward Compatibility.

Note When the Driver Manager maps SQLSetScrollOptions for an application working with an ODBC 3.x driver that does not support SQLSetScrollOptions, the Driver Manager sets the SQL_ROWSET_SIZE statement option, not the SQL_ATTR_ROW_ARRAY_SIZE statement attribute, to the RowsetSize argument in SQLSetScrollOption. As a result, SQLSetScrollOptions cannot be used by an application when fetching multiple rows by a call to SQLFetch or SQLFetchScroll. It can be used only when fetching multiple rows by a call to SQLExtendedFetch.


SQLSetStmtAttr

Conformance

Version Introduced: ODBC 3.0
Standards Compliance: ISO 92

Summary

SQLSetStmtAttr sets attributes related to a statement.

Note For more information about what the Driver Manager maps this function to when an ODBC 3.x application is working with an ODBC 2.x driver, see "Mapping Replacement Functions for Backward Compatibility of Applications" in Chapter 17: Programming Considerations.

Syntax

SQLRETURN SQLSetStmtAttr(
     SQLHSTMT     StatementHandle,
     SQLINTEGER     Attribute,
     SQLPOINTER     ValuePtr,
     SQLINTEGER     StringLength);

Arguments

StatementHandle
[Input]
Statement handle.
Attribute
[Input]
Option to set, listed in "Comments."
ValuePtr
[Input]
Pointer to the value to be associated with Attribute. Depending on the value of Attribute, ValuePtr will be a 32-bit unsigned integer value or a pointer to a null-terminated character string, a binary buffer, or a driver-defined value. If the Attribute argument is a driver-specific value, ValuePtr may be a signed integer.
StringLength
[Input]
If Attribute is an ODBC-defined attribute and ValuePtr points to a character string or a binary buffer, this argument should be the length of *ValuePtr. If Attribute is an ODBC-defined attribute and ValuePtr is an integer, StringLength is ignored.

If Attribute is a driver-defined attribute, the application indicates the nature of the attribute to the Driver Manager by setting the StringLength argument. StringLength can have the following values:

  • If ValuePtr is a pointer to a character string, then StringLength is the length of the string or SQL_NTS.
  • If ValuePtr is a pointer to a binary buffer, then the application places the result of the SQL_LEN_BINARY_ATTR(length) macro in StringLength. This places a negative value in StringLength.
  • If ValuePtr is a pointer to a value other than a character string or a binary string, then StringLength should have the value SQL_IS_POINTER.
  • If ValuePtr contains a fixed-length value, then StringLength is either SQL_IS_INTEGER or SQL_IS_UINTEGER, as appropriate.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLSetStmtAttr returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLSetStmtAttr and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
01S02 Option value changed The driver did not support the value specified in ValuePtr, or the value specified in ValuePtr was invalid because of implementation working conditions, so the driver substituted a similar value. (SQLGetStmtAttr can be called to determine the temporarily substituted value.) The substitute value is valid for the StatementHandle until the cursor is closed, at which point the statement attribute reverts to its previous value. The statement attributes that can be changed are:

SQL_ ATTR_CONCURRENCY
SQL_ ATTR_CURSOR_TYPE
SQL_ ATTR_KEYSET_SIZE
SQL_ ATTR_MAX_LENGTH
SQL_ ATTR_MAX_ROWS
SQL_ ATTR_QUERY_TIMEOUT
SQL_ATTR_ROW_ARRAY_SIZE
SQL_ ATTR_SIMULATE_CURSOR

(Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
24000 | Invalid cursor state | The Attribute was SQL_ATTR_CONCURRENCY, SQL_ATTR_CURSOR_TYPE, SQL_ATTR_SIMULATE_CURSOR, or SQL_ATTR_USE_BOOKMARKS, and the cursor was open.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 Memory allocation
error 		The driver was unable to allocate memory required to support execution or completion of the function.
HY009 | Invalid use of null pointer | The Attribute argument identified a statement attribute that required a string attribute, and the ValuePtr argument was a null pointer.
HY010 Function sequence error (DM) An asynchronously executing function was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY011 | Attribute cannot be set now | The Attribute was SQL_ATTR_CONCURRENCY, SQL_ ATTR_CURSOR_TYPE, SQL_ ATTR_SIMULATE_CURSOR, or SQL_ ATTR_USE_BOOKMARKS, and the statement was prepared.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY017 Invalid use of an automatically allocated descriptor handle (DM) The Attribute argument was SQL_ATTR_IMP_ROW_DESC or SQL_ATTR_IMP_PARAM_DESC.

(DM) The Attribute argument was SQL_ATTR_APP_ROW_DESC or SQL_ATTR_APP_PARAM_DESC, and the value in ValuePtr was an implicitly allocated descriptor handle other than the handle originally allocated for the ARD or APD.
HY024 Invalid attribute value Given the specified Attribute value, an invalid value was specified in ValuePtr. (The Driver Manager returns this SQLSTATE only for connection and statement attributes that accept a discrete set of values, such as SQL_ATTR_ACCESS_MODE or SQL_ ATTR_ASYNC_ENABLE. For all other connection and statement attributes, the driver must verify the value specified in ValuePtr.)

The Attribute argument was SQL_ATTR_APP_ROW_DESC or SQL_ATTR_APP_PARAM_DESC, and ValuePtr was an explicitly allocated descriptor handle that is not on the same connection as the StatementHandle argument.
HY090 | Invalid string or buffer length | (DM) *ValuePtr is a character string, and the StringLength argument was less than 0 but was not SQL_NTS.
HY092 Invalid attribute/option identifier (DM) The value specified for the argument Attribute was not valid for the version of ODBC supported by the driver.

(DM) The value specified for the argument Attribute was a read-only attribute.
HYC00 Optional feature not implemented The value specified for the argument Attribute was a valid ODBC statement attribute for the version of ODBC supported by the driver but was not supported by the driver.

The Attribute argument was SQL_ATTR_ASYNC_ENABLE, and a call to SQLGetInfo with an InfoType of SQL_ASYNC_MODE returns SQL_AM_CONNECTION.

The Attribute argument was SQL_ATTR_ENABLE_AUTO_IPD, and the value of the connection attribute SQL_ATTR_AUTO_IPD was SQL_FALSE.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

Statement attributes for a statement remain in effect until they are changed by another call to SQLSetStmtAttr or until the statement is dropped by calling SQLFreeHandle. Calling SQLFreeStmt with the SQL_CLOSE, SQL_UNBIND, or SQL_RESET_PARAMS option does not reset statement attributes.

Some statement attributes support substitution of a similar value if the data source does not support the value specified in ValuePtr. In such cases, the driver returns SQL_SUCCESS_WITH_INFO and SQLSTATE 01S02 (Option value changed). For example, if Attribute is SQL_ATTR_CONCURRENCY and ValuePtr is SQL_CONCUR_ROWVER, and if the data source does not support this, the driver substitutes SQL_CONCUR_VALUES and returns SQL_SUCCESS_WITH_INFO. To determine the substituted value, an application calls SQLGetStmtAttr.

The format of information set with ValuePtr depends on the specified Attribute. SQLSetStmtAttr accepts attribute information in one of two different formats: a character string or a 32-bit integer value. The format of each is noted in the attribute's description. This format applies to the information returned for each attribute in SQLGetStmtAttr. Character strings pointed to by the ValuePtr argument of SQLSetStmtAttr have a length of StringLength.

Note The ability to set statement attributes at the connection level by calling SQLSetConnectAttr has been deprecated in ODBC 3.x. ODBC 3.x applications should never set statement attributes at the connection level. ODBC 3.x statement attributes cannot be set at the connection level, with the exception of the SQL_ATTR_METADATA_ID and SQL_ATTR_ASYNC_ENABLE attributes, which are both connection attributes and statement attributes, and can be set at either the connection level or the statement level.

ODBC 3.x drivers need only support this functionality if they should work with ODBC 2.x applications that set ODBC 2.x statement options at the connection level. For more information, see "Setting Statement Options on the Connection Level" under "SQLSetConnectOption Mapping" in Appendix G: Driver Guidelines for Backward Compatibility.

Statement Attributes That Set Descriptor Fields

Many statement attributes correspond to a header field of a descriptor. Setting these attributes actually results in the setting of the descriptor fields. Setting fields by a call to SQLSetStmtAttr rather than to SQLSetDescField has the advantage that a descriptor handle does not have to be obtained for the function call.

Caution Calling SQLSetStmtAttr for one statement can affect other statements. This occurs when the APD or ARD associated with the statement is explicitly allocated and is also associated with other statements. Because SQLSetStmtAttr modifies the APD or ARD, the modifications apply to all statements with which this descriptor is associated. If this is not the required behavior, the application should dissociate this descriptor from the other statements (by calling SQLSetStmtAttr to set the SQL_ATTR_APP_ROW_DESC or SQL_ATTR_APP_PARAM_DESC field to a different descriptor handle) before calling SQLSetStmtAttr again.

When a descriptor field is set as a result of the corresponding statement attribute being set, the field is set only for the applicable descriptors that are currently associated with the statement identified by the StatementHandle argument, and the attribute setting does not affect any descriptors that may be associated with that statement in the future. When a descriptor field that is also a statement attribute is set by a call to SQLSetDescField, the corresponding statement attribute is set. If an explicitly allocated descriptor is dissociated from a statement, a statement attribute that corresponds to a header field will revert to the value of the field in the implicitly allocated descriptor.

When a statement is allocated (see SQLAllocHandle), four descriptor handles are automatically allocated and associated with the statement. Explicitly allocated descriptor handles can be associated with the statement by calling SQLAllocHandle with an fHandleType of SQL_HANDLE_DESC to allocate a descriptor handle and then calling SQLSetStmtAttr to associate the descriptor handle with the statement.

The statement attributes in the following table correspond to descriptor header fields.

Statement attribute | Header field | Desc.
SQL_ATTR_PARAM_BIND_OFFSET_PTR | SQL_DESC_BIND_OFFSET_PTR | APD
SQL_ATTR_PARAM_BIND_TYPE | SQL_DESC_BIND_TYPE | APD
SQL_ATTR_PARAM_OPERATION_PTR | SQL_DESC_ARRAY_STATUS_PTR | APD
SQL_ATTR_PARAM_STATUS_PTR | SQL_DESC_ARRAY_STATUS_PTR | IPD
SQL_ATTR_PARAMS_PROCESSED_PTR | SQL_DESC_ROWS_PROCESSED_PTR | IPD
SQL_ATTR_PARAMSET_SIZE | SQL_DESC_ARRAY_SIZE | APD
SQL_ATTR_ROW_ARRAY_SIZE | SQL_DESC_ARRAY_SIZE | ARD
SQL_ATTR_ROW_BIND_OFFSET_PTR | SQL_DESC_BIND_OFFSET_PTR | ARD
SQL_ATTR_ROW_BIND_TYPE | SQL_DESC_BIND_TYPE | ARD
SQL_ATTR_ROW_OPERATION_PTR | SQL_DESC_ARRAY_STATUS_PTR | ARD
SQL_ATTR_ROW_STATUS_PTR | SQL_DESC_ARRAY_STATUS_PTR | IRD
SQL_ATTR_ROWS_FETCHED_PTR | SQL_DESC_ROWS_PROCESSED_PTR | IRD

Statement Attributes

The currently defined attributes and the version of ODBC in which they were introduced are shown in the following table; it is expected that more attributes will be defined by drivers to take advantage of different data sources. A range of attributes is reserved by ODBC; driver developers must reserve values for their own driver-specific use from X/Open. For more information, see "Driver-Specific Data Types, Descriptor Types, Information Types, Diagnostic Types, and Attributes" in Chapter 17: Programming Considerations.

Attribute ValuePtr contents
SQL_ATTR_APP_PARAM_DESC
(ODBC 3.0) 		The handle to the APD for subsequent calls to SQLExecute and SQLExecDirect on the statement handle. The initial value of this attribute is the descriptor implicitly allocated when the statement was initially allocated. If the value of this attribute is set to SQL_NULL_DESC or the handle originally allocated for the descriptor, an explicitly allocated APD handle that was previously associated with the statement handle is dissociated from it and the statement handle reverts to the implicitly allocated APD handle.

This attribute cannot be set to a descriptor handle that was implicitly allocated for another statement or to another descriptor handle that was implicitly set on the same statement; implicitly allocated descriptor handles cannot be associated with more than one statement or descriptor handle.
SQL_ATTR_APP_ROW_DESC
(ODBC 3.0) 		The handle to the ARD for subsequent fetches on the statement handle. The initial value of this attribute is the descriptor implicitly allocated when the statement was initially allocated. If the value of this attribute is set to SQL_NULL_DESC or the handle originally allocated for the descriptor, an explicitly allocated ARD handle that was previously associated with the statement handle is dissociated from it and the statement handle reverts to the implicitly allocated ARD handle.

This attribute cannot be set to a descriptor handle that was implicitly allocated for another statement or to another descriptor handle that was implicitly set on the same statement; implicitly allocated descriptor handles cannot be associated with more than one statement or descriptor handle.
SQL_ATTR_ASYNC_ENABLE
(ODBC 1.0) 		An SQLUINTEGER value that specifies whether a function called with the specified statement is executed asynchronously:

SQL_ASYNC_ENABLE_OFF = Off (the default)
SQL_ASYNC_ENABLE_ON = On

Once a function has been called asynchronously, only the original function, SQLCancel, SQLGetDiagField, or SQLGetDiagRec can be called on the statement, and only the original function, SQLAllocHandle (with a HandleType of SQL_HANDLE_STMT), SQLGetDiagField, SQLGetDiagRec, or SQLGetFunctions can be called on the connection associated with the statement, until the original function returns a code other than SQL_STILL_EXECUTING. Any other function called on the statement or the connection associated with the statement returns SQL_ERROR with an SQLSTATE of HY010 (Function sequence error). Functions can be called on other statements. For more information, see "Asynchronous Execution" in Chapter 9: Executing Statements.

For drivers with statement level asynchronous execution support, the statement attribute SQL_ATTR_ASYNC_ENABLE may be set. Its initial value is the same as the value of the connection level attribute with the same name at the time the statement handle was allocated.

For drivers with connection-level, asynchronous-execution support, the statement attribute SQL_ATTR_ASYNC_ENABLE is read-only. Its value is the same as the value of the connection level attribute with the same name at the time the statement handle was allocated. Calling SQLSetStmtAttr to set SQL_ATTR_ASYNC_ENABLE when the SQL_ASYNC_MODE InfoType returns SQL_AM_CONNECTION returns SQLSTATE HYC00 (Optional feature not implemented). (See SQLSetConnectAttr for more information.)

As a standard practice, applications should execute functions asynchronously only on single-thread operating systems. On multithread operating systems, applications should execute functions on separate threads rather than executing them asynchronously on the same thread. No functionality is lost if drivers that operate only on multithread operating systems do not need to support asynchronous execution.

The following functions can be executed asynchronously:

SQLBulkOperations
SQLColAttribute
SQLColumnPrivileges
SQLColumns
SQLCopyDesc
SQLDescribeCol
SQLDescribeParam
SQLExecDirect
SQLExecute
SQLFetch
SQLFetchScroll
SQLForeignKeys
SQLGetData
SQLGetDescField[1]
SQLGetDescRec[1]
SQLGetDiagField
SQLGetDiagRec
SQLGetTypeInfo
SQLMoreResults
SQLNumParams
SQLNumResultCols
SQLParamData
SQLPrepare
SQLPrimaryKeys
SQLProcedureColumns
SQLProcedures
SQLPutData
SQLSetPos
SQLSpecialColumns
SQLStatistics
SQLTablePrivileges
SQLTables
SQL_ATTR_CONCURRENCY
(ODBC 2.0) 		An SQLUINTEGER value that specifies the cursor concurrency:

SQL_CONCUR_READ_ONLY = Cursor is read-only. No updates are allowed.

SQL_CONCUR_LOCK = Cursor uses the lowest level of locking sufficient to ensure that the row can be updated.

SQL_CONCUR_ROWVER = Cursor uses optimistic concurrency control, comparing row versions such as SQLBase ROWID or Sybase TIMESTAMP.

SQL_CONCUR_VALUES = Cursor uses optimistic concurrency control, comparing values.

The default value for SQL_ATTR_CONCURRENCY is SQL_CONCUR_READ_ONLY.

This attribute cannot be specified for an open cursor. For more information, see "Concurrency Types" in Chapter 14: Transactions.

If the SQL_ATTR_CURSOR_TYPE Attribute is changed to a type that does not support the current value of SQL_ATTR_CONCURRENCY, the value of SQL_ATTR_CONCURRENCY will be changed at execution time, and a warning issued when SQLExecDirect or SQLPrepare is called.

If the driver supports the SELECT FOR UPDATE statement and such a statement is executed while the value of SQL_ATTR_CONCURRENCY is set to SQL_CONCUR_READ_ONLY, an error will be returned. If the value of SQL_ATTR_CONCURRENCY is changed to a value that the driver supports for some value of SQL_ATTR_CURSOR_TYPE but not for the current value of SQL_ATTR_CURSOR_TYPE, the value of SQL_ATTR_CURSOR_TYPE will be changed at execution time and SQLSTATE 01S02 (Option value changed) is issued when SQLExecDirect or SQLPrepare is called.

If the specified concurrency is not supported by the data source, the driver substitutes a different concurrency and returns SQLSTATE 01S02 (Option value changed). For SQL_CONCUR_VALUES, the driver substitutes SQL_CONCUR_ROWVER, and vice versa. For SQL_CONCUR_LOCK, the driver substitutes, in order, SQL_CONCUR_ROWVER or SQL_CONCUR_VALUES. The validity of the substituted value is not checked until execution time.

For more information about the relationship between SQL_ATTR_CONCURRENCY and the other cursor attributes, see "Cursor Characteristics and Cursor Type" in Chapter 11: Retrieving Results (Advanced).
SQL_ATTR_CURSOR_SCROLLABLE
(ODBC 3.0) 		An SQLUINTEGER value that specifies the level of support that the application requires. Setting this attribute affects subsequent calls to SQLExecDirect and SQLExecute.

SQL_NONSCROLLABLE = Scrollable cursors are not required on the statement handle. If the application calls SQLFetchScroll on this handle, the only valid value of FetchOrientation is SQL_FETCH_NEXT. This is the default.

SQL_SCROLLABLE = Scrollable cursors are required on the statement handle. When calling SQLFetchScroll, the application may specify any valid value of FetchOrientation, achieving cursor positioning in modes other than the sequential mode.

For more information about scrollable cursors, see "Scrollable Cursors" in Chapter 11: Retrieving Results (Advanced). For more information about the relationship between SQL_ATTR_CURSOR_SCROLLABLE and the other cursor attributes, see "Cursor Characteristics and Cursor Type" in Chapter 11: Retrieving Results (Advanced).
SQL_ATTR_CURSOR_SENSITIVITY
(ODBC 3.0) 		An SQLUINTEGER value that specifies whether cursors on the statement handle make visible the changes made to a result set by another cursor. Setting this attribute affects subsequent calls to SQLExecDirect and SQLExecute. An application can read back the value of this attribute to obtain its initial state or its state as most recently set by the application.

SQL_UNSPECIFIED = It is unspecified what the cursor type is and whether cursors on the statement handle make visible the changes made to a result set by another cursor. Cursors on the statement handle may make visible none, some, or all such changes. This is the default.

SQL_INSENSITIVE = All cursors on the statement handle show the result set without reflecting any changes made to it by any other cursor. Insensitive cursors are read-only. This corresponds to a static cursor, which has a concurrency that is read-only.

SQL_SENSITIVE = All cursors on the statement handle make visible all changes made to a result set by another cursor.

For more information about the relationship between SQL_ATTR_CURSOR_SENSITIVITY and the other cursor attributes, see "Cursor Characteristics and Cursor Type" in Chapter 11: Retrieving Results (Advanced).
SQL_ATTR_CURSOR_TYPE
(ODBC 2.0) 		An SQLUINTEGER value that specifies the cursor type:

SQL_CURSOR_FORWARD_ONLY = The cursor only scrolls forward.

SQL_CURSOR_STATIC = The data in the result set is static.

SQL_CURSOR_KEYSET_DRIVEN = The driver saves and uses the keys for the number of rows specified in the SQL_ATTR_KEYSET_SIZE statement attribute.

SQL_CURSOR_DYNAMIC = The driver saves and uses only the keys for the rows in the rowset.

The default value is SQL_CURSOR_FORWARD_ONLY. This attribute cannot be specified after the SQL statement has been prepared.

If the specified cursor type is not supported by the data source, the driver substitutes a different cursor type and returns SQLSTATE 01S02 (Option value changed). For a mixed or dynamic cursor, the driver substitutes, in order, a keyset-driven or static cursor. For a keyset-driven cursor, the driver substitutes a static cursor.

For more information about scrollable cursor types, see "Scrollable Cursor Types" in Chapter 11: Retrieving Results (Advanced). For more information about the relationship between SQL_ATTR_CURSOR_TYPE and the other cursor attributes, see "Cursor Characteristics and Cursor Type" in Chapter 11: Retrieving Results (Advanced).
SQL_ATTR_ENABLE_AUTO_IPD
(ODBC 3.0) 		An SQLUINTEGER value that specifies whether automatic population of the IPD is performed:

SQL_TRUE = Turns on automatic population of the IPD after a call to SQLPrepare. SQL_FALSE = Turns off automatic population of the IPD after a call to SQLPrepare. (An application can still obtain IPD field information by calling SQLDescribeParam, if supported.) The default value of the statement attribute SQL_ATTR_ENABLE_AUTO_IPD is SQL_FALSE. For more information, see "Automatic Population of the IPD" in Chapter 13: Descriptors.
SQL_ATTR_FETCH_BOOKMARK_PTR
(ODBC 3.0) 		A pointer that points to a binary bookmark value. When SQLFetchScroll is called with fFetchOrientation equal to SQL_FETCH_BOOKMARK, the driver picks up the bookmark value from this field. This field defaults to a null pointer. For more information, see "Scrolling by Bookmark" in Chapter 11: Retrieving Results (Advanced).

The value pointed to by this field is not used for delete by bookmark, update by bookmark, or fetch by bookmark operations in SQLBulkOperations, which use bookmarks cached in rowset buffers.
SQL_ATTR_IMP_PARAM_DESC
(ODBC 3.0) 		The handle to the IPD. The value of this attribute is the descriptor allocated when the statement was initially allocated. The application cannot set this attribute.

This attribute can be retrieved by a call to SQLGetStmtAttr but not set by a call to SQLSetStmtAttr.
SQL_ATTR_IMP_ROW_DESC
(ODBC 3.0) 		The handle to the IRD. The value of this attribute is the descriptor allocated when the statement was initially allocated. The application cannot set this attribute.

This attribute can be retrieved by a call to SQLGetStmtAttr but not set by a call to SQLSetStmtAttr.
SQL_ATTR_KEYSET_SIZE
(ODBC 2.0) 		An SQLUINTEGER that specifies the number of rows in the keyset for a keyset-driven cursor. If the keyset size is 0 (the default), the cursor is fully keyset-driven. If the keyset size is greater than 0, the cursor is mixed (keyset-driven within the keyset and dynamic outside of the keyset). The default keyset size is 0. For more information about keyset-driven cursors, see "Keyset-Driven Cursors" in Chapter 11: Retrieving Results (Advanced).

If the specified size exceeds the maximum keyset size, the driver substitutes that size and returns SQLSTATE 01S02 (Option value changed).

SQLFetch or SQLFetchScroll returns an error if the keyset size is greater than 0 and less than the rowset size.
SQL_ATTR_MAX_LENGTH
(ODBC 1.0) 		An SQLUINTEGER value that specifies the maximum amount of data that the driver returns from a character or binary column. If ValuePtr is less than the length of the available data, SQLFetch or SQLGetData truncates the data and returns SQL_SUCCESS. If ValuePtr is 0 (the default), the driver attempts to return all available data.

If the specified length is less than the minimum amount of data that the data source can return or greater than the maximum amount of data that the data source can return, the driver substitutes that value and returns SQLSTATE 01S02 (Option value changed).

The value of this attribute can be set on an open cursor; however, the setting might not take effect immediately, in which case the driver will return SQLSTATE 01S02 (Option value changed) and reset the attribute to its original value.

This attribute is intended to reduce network traffic and should be supported only when the data source (as opposed to the driver) in a multiple-tier driver can implement it. This mechanism should not be used by applications to truncate data; to truncate data received, an application should specify the maximum buffer length in the BufferLength argument in SQLBindCol or SQLGetData.
SQL_ATTR_MAX_ROWS
(ODBC 1.0) 		An SQLUINTEGER value corresponding to the maximum number of rows to return to the application for a SELECT statement. If *ValuePtr equals 0 (the default), the driver returns all rows.

This attribute is intended to reduce network traffic. Conceptually, it is applied when the result set is created and limits the result set to the first ValuePtr rows. If the number of rows in the result set is greater than ValuePtr, the result set is truncated.

SQL_ATTR_MAX_ROWS applies to all result sets on the Statement, including those returned by catalog functions. SQL_ATTR_MAX_ROWS establishes a maximum for the value of the cursor row count.

A driver should not emulate SQL_ATTR_MAX_ROWS behavior for SQLFetch or SQLFetchScroll (if result set size limitations cannot be implemented at the data source) if it cannot guarantee that SQL_ATTR_MAX_ROWS will be implemented properly.

It is driver-defined whether SQL_ATTR_MAX_ROWS applies to statements other than SELECT statements (such as catalog functions).

The value of this attribute can be set on an open cursor; however, the setting might not take effect immediately, in which case the driver will return SQLSTATE 01S02 (Option value changed) and reset the attribute to its original value.
SQL_ATTR_METADATA_ID
(ODBC 3.0) 		An SQLUINTEGER value that determines how the string arguments of catalog functions are treated.

If SQL_TRUE, the string argument of catalog functions are treated as identifiers. The case is not significant. For nondelimited strings, the driver removes any trailing spaces and the string is folded to uppercase. For delimited strings, the driver removes any leading or trailing spaces and takes whatever is between the delimiters literally. If one of these arguments is set to a null pointer, the function returns SQL_ERROR and SQLSTATE HY009 (Invalid use of null pointer).

If SQL_FALSE, the string arguments of catalog functions are not treated as identifiers. The case is significant. They can either contain a string search pattern or not, depending on the argument.

The default value is SQL_FALSE.

The TableType argument of SQLTables, which takes a list of values, is not affected by this attribute.

SQL_ATTR_METADATA_ID can also be set on the connection level. (It and SQL_ATTR_ASYNC_ENABLE are the only statement attributes that are also connection attributes.)

For more information, see "Arguments in Catalog Functions" in Chapter 7: Catalog Functions.
SQL_ATTR_NOSCAN
(ODBC 1.0) 		An SQLUINTEGER value that indicates whether the driver should scan SQL strings for escape sequences:

SQL_NOSCAN_OFF = The driver scans SQL strings for escape sequences (the default).

SQL_NOSCAN_ON = The driver does not scan SQL strings for escape sequences. Instead, the driver sends the statement directly to the data source.

For more information, see "Escape Sequences in ODBC" in Chapter 8: SQL Statements.
SQL_ATTR_PARAM_BIND_OFFSET_PTR
(ODBC 3.0) 		An SQLUINTEGER * value that points to an offset added to pointers to change binding of dynamic parameters. If this field is non-null, the driver dereferences the pointer, adds the dereferenced value to each of the deferred fields in the descriptor record (SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR), and uses the new pointer values when binding. It is set to null by default.

The bind offset is always added directly to the SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR fields. If the offset is changed to a different value, the new value is still added directly to the value in the descriptor field. The new offset is not added to the field value plus any earlier offsets.
SQL_ATTR_PARAM_BIND_OFFSET_PTR
(ODBC 3.0) (continued) 		For more information, see "Parameter Binding Offsets" in Chapter 9: Executing Statements.

Setting this statement attribute sets the SQL_DESC_BIND_OFFSET_PTR field in the APD header.
SQL_ATTR_PARAM_BIND_TYPE
(ODBC 3.0) 		An SQLUINTEGER value that indicates the binding orientation to be used for dynamic parameters.

This field is set to SQL_PARAM_BIND_BY_COLUMN (the default) to select column-wise binding.

To select row-wise binding, this field is set to the length of the structure or an instance of a buffer that will be bound to a set of dynamic parameters. This length must include space for all of the bound parameters and any padding of the structure or buffer to ensure that when the address of a bound parameter is incremented with the specified length, the result will point to the beginning of the same parameter in the next set of parameters. When using the sizeof operator in ANSI C, this behavior is guaranteed.

For more information, see "Binding Arrays of Parameters" in Chapter 9: Executing Statements.

Setting this statement attribute sets the SQL_DESC_ BIND_TYPE field in the APD header.
SQL_ATTR_PARAM_OPERATION_PTR
(ODBC 3.0) 		An SQLUSMALLINT * value that points to an array of SQLUSMALLINT values used to ignore a parameter during execution of an SQL statement. Each value is set to either SQL_PARAM_PROCEED (for the parameter to be executed) or SQL_PARAM_IGNORE (for the parameter to be ignored).

A set of parameters can be ignored during processing by setting the status value in the array pointed to by SQL_DESC_ARRAY_STATUS_PTR in the APD to SQL_PARAM_IGNORE. A set of parameters is processed if its status value is set to SQL_PARAM_PROCEED or if no elements in the array are set.

This statement attribute can be set to a null pointer, in which case the driver does not return parameter status values. This attribute can be set at any time, but the new value is not used until the next time SQLExecDirect or SQLExecute is called.

For more information, see "Using Arrays of Parameters" in Chapter 9: Executing Statements.

Setting this statement attribute sets the SQL_DESC_ARRAY_STATUS_PTR field in the APD header.
SQL_ATTR_PARAM_STATUS_PTR
(ODBC 3.0) 		An SQLUSMALLINT * value that points to an array of SQLUSMALLINT values containing status information for each row of parameter values after a call to SQLExecute or SQLExecDirect. This field is required only if PARAMSET_SIZE is greater than 1.

The status values can contain the following values:

SQL_PARAM_SUCCESS: The SQL statement was successfully executed for this set of parameters.

SQL_PARAM_SUCCESS_WITH_INFO: The SQL statement was successfully executed for this set of parameters; however, warning information is available in the diagnostics data structure.

SQL_PARAM_ERROR: There was an error in processing this set of parameters. Additional error information is available in the diagnostics data structure.

SQL_PARAM_UNUSED: This parameter set was unused, possibly due to the fact that some previous parameter set caused an error that aborted further processing, or because SQL_PARAM_IGNORE was set for that set of parameters in the array specified by the SQL_ATTR_PARAM_OPERATION_PTR.

SQL_PARAM_DIAG_UNAVAILABLE: The driver treats arrays of parameters as a monolithic unit and so does not generate this level of error information.

This statement attribute can be set to a null pointer, in which case the driver does not return parameter status values. This attribute can be set at any time, but the new value is not used until the next time SQLExecute or SQLExecDirect is called. Note that setting this attribute can affect the output parameter behavior implemented by the driver.

For more information, see "Using Arrays of Parameters" in Chapter 9: Executing Statements.

Setting this statement attribute sets the SQL_DESC_ARRAY_STATUS_PTR field in the IPD header.
SQL_ATTR_PARAMS_PROCESSED_PTR
(ODBC 3.0) 		An SQLUINTEGER * record field that points to a buffer in which to return the number of sets of parameters that have been processed, including error sets. No number will be returned if this is a null pointer.

Setting this statement attribute sets the SQL_DESC_ROWS_PROCESSED_PTR field in the IPD header.

If the call to SQLExecDirect or SQLExecute that fills in the buffer pointed to by this attribute does not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the buffer are undefined.

For more information, see "Using Arrays of Parameters" in Chapter 9: Executing Statements.
SQL_ATTR_PARAMSET_SIZE
(ODBC 3.0) 		An SQLUINTEGER value that specifies the number of values for each parameter. If SQL_ATTR_PARAMSET_SIZE is greater than 1, SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR of the APD point to arrays. The cardinality of each array is equal to the value of this field.

For more information, see "Using Arrays of Parameters" in Chapter 9: Executing Statements.

Setting this statement attribute sets the SQL_DESC_ARRAY_SIZE field in the APD header.
SQL_ATTR_QUERY_TIMEOUT
(ODBC 1.0) 		An SQLUINTEGER value corresponding to the number of seconds to wait for an SQL statement to execute before returning to the application. If ValuePtr is equal to 0 (default), there is no timeout.

If the specified timeout exceeds the maximum timeout in the data source or is smaller than the minimum timeout, SQLSetStmtAttr substitutes that value and returns SQLSTATE 01S02 (Option value changed).

Note that the application need not call SQLCloseCursor to reuse the statement if a SELECT statement timed out.

The query timeout set in this statement attribute is valid in both synchronous and asynchronous modes.
SQL_ATTR_RETRIEVE_DATA
(ODBC 2.0) 		An SQLUINTEGER value:

SQL_RD_ON = SQLFetchScroll and, in ODBC 3.x, SQLFetch retrieve data after it positions the cursor to the specified location. This is the default.

SQL_RD_OFF = SQLFetchScroll and, in ODBC 3.x, SQLFetch do not retrieve data after it positions the cursor.

By setting SQL_RETRIEVE_DATA to SQL_RD_OFF, an application can verify that a row exists or retrieve a bookmark for the row without incurring the overhead of retrieving rows. For more information, see "Scrolling and Fetching Rows" in Chapter 11: Retrieving Results (Advanced).

The value of this attribute can be set on an open cursor; however, the setting might not take effect immediately, in which case the driver will return SQLSTATE 01S02 (Option value changed) and reset the attribute to its original value.
SQL_ATTR_ROW_ARRAY_SIZE
(ODBC 3.0) 		An SQLUINTEGER value that specifies the number of rows returned by each call to SQLFetch or SQLFetchScroll. It is also the number of rows in a bookmark array used in a bulk bookmark operation in SQLBulkOperations. The default value is 1.

If the specified rowset size exceeds the maximum rowset size supported by the data source, the driver substitutes that value and returns SQLSTATE 01S02 (Option value changed).

For more information, see "Rowset Size" in Chapter 11: Retrieving Results (Advanced).

Setting this statement attribute sets the SQL_DESC_ARRAY_SIZE field in the ARD header.
SQL_ATTR_ROW_BIND_OFFSET_PTR
(ODBC 3.0) 		An SQLUINTEGER * value that points to an offset added to pointers to change binding of column data. If this field is non-null, the driver dereferences the pointer, adds the dereferenced value to each of the deferred fields in the descriptor record (SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR), and uses the new pointer values when binding. It is set to null by default.

Setting this statement attribute sets the SQL_DESC_BIND_OFFSET_PTR field in the ARD header.
SQL_ATTR_ROW_BIND_TYPE
(ODBC 1.0) 		An SQLUINTEGER value that sets the binding orientation to be used when SQLFetch or SQLFetchScroll is called on the associated statement. Column-wise binding is selected by setting the value to SQL_BIND_BY_COLUMN. Row-wise binding is selected by setting the value to the length of a structure or an instance of a buffer into which result columns will be bound.

If a length is specified, it must include space for all of the bound columns and any padding of the structure or buffer to ensure that when the address of a bound column is incremented with the specified length, the result will point to the beginning of the same column in the next row. When using the sizeof operator with structures or unions in ANSI C, this behavior is guaranteed.

Column-wise binding is the default binding orientation for SQLFetch and SQLFetchScroll.

For more information, see "Binding Columns for Use with Block Cursors" in Chapter 11: Retrieving Results (Advanced).

Setting this statement attribute sets the SQL_DESC_BIND_TYPE field in the ARD header.
SQL_ATTR_ROW_NUMBER
(ODBC 2.0) 		An SQLUINTEGER value that is the number of the current row in the entire result set. If the number of the current row cannot be determined or there is no current row, the driver returns 0.

This attribute can be retrieved by a call to SQLGetStmtAttr but not set by a call to SQLSetStmtAttr.
SQL_ATTR_ROW_OPERATION_PTR
(ODBC 3.0) 		An SQLUSMALLINT * value that points to an array of SQLUSMALLINT values used to ignore a row during a bulk operation using SQLSetPos. Each value is set to either SQL_ROW_PROCEED (for the row to be included in the bulk operation) or SQL_ROW_IGNORE (for the row to be excluded from the bulk operation). (Rows cannot be ignored by using this array during calls to SQLBulkOperations.)

This statement attribute can be set to a null pointer, in which case the driver does not return row status values. This attribute can be set at any time, but the new value is not used until the next time SQLSetPos is called.

For more information, see "Updating Rows in the Rowset with SQLSetPos" and "Deleting Rows in the Rowset with SQLSetPos" in Chapter 12: Updating Data.

Setting this statement attribute sets the SQL_DESC_ARRAY_STATUS_PTR field in the ARD.
SQL_ATTR_ROW_STATUS_PTR
(ODBC 3.0) 		An SQLUSMALLINT * value that points to an array of SQLUSMALLINT values containing row status values after a call to SQLFetch or SQLFetchScroll. The array has as many elements as there are rows in the rowset.

This statement attribute can be set to a null pointer, in which case the driver does not return row status values. This attribute can be set at any time, but the new value is not used until the next time SQLBulkOperations, SQLFetch, SQLFetchScroll, or SQLSetPos is called.

For more information, see "Number of Rows Fetched and Status" in Chapter 11: Retrieving Results (Advanced).

Setting this statement attribute sets the SQL_DESC_ARRAY_STATUS_PTR field in the IRD header.

This attribute is mapped by an ODBC 2.x driver to the rgbRowStatus array in a call to SQLExtendedFetch.
SQL_ATTR_ROWS_FETCHED_PTR
(ODBC 3.0) 		An SQLUINTEGER * value that points to a buffer in which to return the number of rows fetched after a call to SQLFetch or SQLFetchScroll; the number of rows affected by a bulk operation performed by a call to SQLSetPos with an Operation argument of SQL_REFRESH; or the number of rows affected by a bulk operation performed by SQLBulkOperations. This number includes error rows.

For more information, see "Number of Rows Fetched and Status" in Chapter 11: Retrieving Results (Advanced).

Setting this statement attribute sets the SQL_DESC_ROWS_PROCESSED_PTR field in the IRD header.

If the call to SQLFetch or SQLFetchScroll that fills in the buffer pointed to by this attribute does not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the buffer are undefined.
SQL_ATTR_SIMULATE_CURSOR
(ODBC 2.0) 		An SQLUINTEGER value that specifies whether drivers that simulate positioned update and delete statements guarantee that such statements affect only one single row.

To simulate positioned update and delete statements, most drivers construct a searched UPDATE or DELETE statement containing a WHERE clause that specifies the value of each column in the current row. Unless these columns make up a unique key, such a statement can affect more than one row.

To guarantee that such statements affect only one row, the driver determines the columns in a unique key and adds these columns to the result set. If an application guarantees that the columns in the result set make up a unique key, the driver is not required to do so. This may reduce execution time.

SQL_SC_NON_UNIQUE = The driver does not guarantee that simulated positioned update or delete statements will affect only one row; it is the application's responsibility to do so. If a statement affects more than one row, SQLExecute, SQLExecDirect, or SQLSetPos returns SQLSTATE 01001 (Cursor operation conflict).

SQL_SC_TRY_UNIQUE = The driver attempts to guarantee that simulated positioned update or delete statements affect only one row. The driver always executes such statements, even if they might affect more than one row, such as when there is no unique key. If a statement affects more than one row, SQLExecute, SQLExecDirect, or SQLSetPos returns SQLSTATE 01001 (Cursor operation conflict).

SQL_SC_UNIQUE = The driver guarantees that simulated positioned update or delete statements affect only one row. If the driver cannot guarantee this for a given statement, SQLExecDirect or SQLPrepare returns an error.

If the data source provides native SQL support for positioned update and delete statements and the driver does not simulate cursors, SQL_SUCCESS is returned when SQL_SC_UNIQUE is requested for SQL_SIMULATE_CURSOR. SQL_SUCCESS_WITH_INFO is returned if SQL_SC_TRY_UNIQUE or SQL_SC_NON_UNIQUE is requested. If the data source provides the SQL_SC_TRY_UNIQUE level of support and the driver does not, SQL_SUCCESS is returned for SQL_SC_TRY_UNIQUE and SQL_SUCCESS_WITH_INFO is returned for SQL_SC_NON_UNIQUE.

If the specified cursor simulation type is not supported by the data source, the driver substitutes a different simulation type and returns SQLSTATE 01S02 (Option value changed). For SQL_SC_UNIQUE, the driver substitutes, in order, SQL_SC_TRY_UNIQUE or SQL_SC_NON_UNIQUE. For SQL_SC_TRY_UNIQUE, the driver substitutes SQL_SC_NON_UNIQUE.

For more information, see "Simulating Positioned Update and Delete Statements" in Chapter 12: Updating Data.
SQL_ATTR_USE_BOOKMARKS
(ODBC 2.0) 		An SQLUINTEGER value that specifies whether an application will use bookmarks with a cursor:

SQL_UB_OFF = Off (the default)

SQL_UB_VARIABLE = An application will use bookmarks with a cursor, and the driver will provide variable-length bookmarks if they are supported. SQL_UB_FIXED is deprecated in ODBC 3.x. ODBC 3.x applications should always use variable-length bookmarks, even when working with ODBC 2.x drivers (which supported only 4-byte, fixed-length bookmarks). This is because a fixed-length bookmark is just a special case of a variable-length bookmark. When working with an ODBC 2.x driver, the Driver Manager maps SQL_UB_VARIABLE to SQL_UB_FIXED.

To use bookmarks with a cursor, the application must specify this attribute with the SQL_UB_VARIABLE value before opening the cursor.

For more information, see "Retrieving Bookmarks" in Chapter 11: Retrieving Results (Advanced).

[1] These functions can be called asynchronously only if the descriptor is an implementation descriptor, not an application descriptor.

See "Column-Wise Binding" and "Row-Wise Binding" in Chapter 11: Retrieving Results (Advanced).

Related Functions

For information about | See
Canceling statement processing | SQLCancel
Returning the setting of a connection attribute | SQLGetConnectAttr
Returning the setting of a statement attribute | SQLGetStmtAttr
Setting a connection attribute | SQLSetConnectAttr
Setting a single field of the descriptor | SQLSetDescField


SQLSetStmtOption

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

In ODBC 3.x, the ODBC 2.0 function SQLSetStmtOption has been replaced by SQLSetStmtAttr. For more information, see SQLSetStmtAttr.

Note For more information about what the Driver Manager maps this function to when an ODBC 2.x application is working with an ODBC 3.x driver, see "Mapping Deprecated Functions" in Appendix G: Driver Guidelines for Backward Compatibility.


SQLSpecialColumns

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: X/Open

Summary

SQLSpecialColumns retrieves the following information about columns within a specified table:

  • The optimal set of columns that uniquely identifies a row in the table.
  • Columns that are automatically updated when any value in the row is updated by a transaction.

Syntax

SQLRETURN SQLSpecialColumns(
     SQLHSTMT     StatementHandle,
     SQLSMALLINT     IdentifierType,
     SQLCHAR *     CatalogName,
     SQLSMALLINT     NameLength1,
     SQLCHAR *     SchemaName,
     SQLSMALLINT     NameLength2,
     SQLCHAR *     TableName,
     SQLSMALLINT     NameLength3,
     SQLSMALLINT     Scope,
     SQLSMALLINT     Nullable);

Arguments

StatementHandle
[Input]
Statement handle.
IdentifierType
[Input]
Type of column to return. Must be one of the following values:

SQL_BEST_ROWID: Returns the optimal column or set of columns that, by retrieving values from the column or columns, allows any row in the specified table to be uniquely identified. A column can be either a pseudo-column specifically designed for this purpose (as in Oracle ROWID or Ingres TID) or the column or columns of any unique index for the table.

SQL_ROWVER: Returns the column or columns in the specified table, if any, that are automatically updated by the data source when any value in the row is updated by any transaction (as in SQLBase ROWID or Sybase TIMESTAMP).

CatalogName
[Input]
Catalog name for the table. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have catalogs. CatalogName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, CatalogName is treated as an identifier and its case is not significant. If it is SQL_FALSE, CatalogName is an ordinary argument; it is treated literally, and its case is significant. For more information, see "Arguments in Catalog Functions" in Chapter 7: Catalog Functions.

NameLength1
[Input]
Length of *CatalogName.
SchemaName
[Input]
Schema name for the table. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have schemas. SchemaName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, SchemaName is treated as an identifier and its case is not significant. If it is SQL_FALSE, SchemaName is an ordinary argument; it is treated literally, and its case is significant.

NameLength2
[Input]
Length of *SchemaName.
TableName
[Input]
Table name. This argument cannot be a null pointer. TableName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, TableName is treated as an identifier and its case is not significant. If it is SQL_FALSE, TableName is an ordinary argument; it is treated literally, and its case is significant.

NameLength3
[Input]
Length of *TableName.
Scope
[Input]
Minimum required scope of the rowid. The returned rowid may be of greater scope. Must be one of the following:

SQL_SCOPE_CURROW: The rowid is guaranteed to be valid only while positioned on that row. A later reselect using rowid may not return a row if the row was updated or deleted by another transaction.

SQL_SCOPE_TRANSACTION: The rowid is guaranteed to be valid for the duration of the current transaction.

SQL_SCOPE_SESSION: The rowid is guaranteed to be valid for the duration of the session (across transaction boundaries).

Nullable
[Input]
Determines whether to return special columns that can have a NULL value. Must be one of the following:

SQL_NO_NULLS: Exclude special columns that can have NULL values. Some drivers cannot support SQL_NO_NULLS, and these drivers will return an empty result set if SQL_NO_NULLS was specified. Applications should be prepared for this case and request SQL_NO_NULLS only if it is absolutely required.

SQL_NULLABLE: Return special columns even if they can have NULL values.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLSpecialColumns returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLSpecialColumns and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
24000 Invalid cursor state A cursor was open on the StatementHandle, and SQLFetch or SQLFetchScroll had been called. This error is returned by the Driver Manager if SQLFetch or SQLFetchScroll has not returned SQL_NO_DATA and is returned by the driver if SQLFetch or SQLFetchScroll has returned SQL_NO_DATA.

A cursor was open on the StatementHandle, but SQLFetch or SQLFetchScroll had not been called.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 Memory allocation
error 		The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY009 Invalid use of null pointer The TableName argument was a null pointer.

The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, the CatalogName argument was a null pointer, and the SQL_CATALOG_NAME InfoType returns that catalog names are supported.

(DM) The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, and the SchemaName argument was a null pointer.
HY010 Function sequence error (DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 Invalid string or buffer length (DM) The value of one of the length arguments was less than 0 but not equal to SQL_NTS.

The value of one of the length arguments exceeded the maximum length value for the corresponding name. The maximum length of each name can be obtained by calling SQLGetInfo with the InfoType values: SQL_MAX_CATALOG_NAME_LEN, SQL_MAX_SCHEMA_NAME_LEN, or SQL_MAX_TABLE_NAME_LEN.
HY097 | Column type out of range | (DM) An invalid IdentifierType value was specified.
HY098 | Scope type out of range | (DM) An invalid Scope value was specified.
HY099 | Nullable type out of range | (DM) An invalid Nullable value was specified.
HYC00 Optional feature not implemented A catalog was specified, and the driver or data source does not support catalogs.

A schema was specified, and the driver or data source does not support schemas.

The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source.

The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.
HYT00 | Timeout expired | The query timeout period expired before the data source returned the requested result set. The timeout period is set through SQLSetStmtAttr, SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

When the IdentifierType argument is SQL_BEST_ROWID, SQLSpecialColumns returns the column or columns that uniquely identify each row in the table. These columns can always be used in a select-list or WHERE clause. SQLColumns, which is used to return a variety of information on the columns of a table, does not necessarily return the columns that uniquely identify each row, or columns that are automatically updated when any value in the row is updated by a transaction. For example, SQLColumns might not return the Oracle pseudo-column ROWID. This is why SQLSpecialColumns is used to return these columns. For more information, see "Uses of Catalog Data" in Chapter 7: Catalog Functions.

Note For more information about the general use, arguments, and returned data of ODBC catalog functions, see Chapter 7: Catalog Functions.

If there are no columns that uniquely identify each row in the table, SQLSpecialColumns returns a rowset with no rows; a subsequent call to SQLFetch or SQLFetchScroll on the statement returns SQL_NO_DATA.

If the IdentifierType, Scope, or Nullable arguments specify characteristics that are not supported by the data source, SQLSpecialColumns returns an empty result set.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, the CatalogName, SchemaName, and TableName arguments are treated as identifiers, so they cannot be set to a null pointer in certain situations. (For more information, see "Arguments in Catalog Functions" in Chapter 7: Catalog Functions.)

SQLSpecialColumns returns the results as a standard result set, ordered by SCOPE.

The following columns have been renamed for ODBC 3.x. The column name changes do not affect backward compatibility because applications bind by column number.

ODBC 2.0 column | ODBC 3.x column
PRECISION | COLUMN_SIZE
LENGTH | BUFFER_LENGTH
SCALE | DECIMAL_DIGITS

To determine the actual length of the COLUMN_NAME column, an application can call SQLGetInfo with the SQL_MAX_COLUMN_NAME_LEN option.

The following table lists the columns in the result set. Additional columns beyond column 8 (PSEUDO_COLUMN) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "Data Returned by Catalog Functions" in Chapter 7: Catalog Functions.

Column name | Column number | Data type | Comments
SCOPE (ODBC 1.0) | 1 | Smallint | Actual scope of the rowid. Contains one of the following values: SQL_SCOPE_CURROW SQL_SCOPE_TRANSACTION SQL_SCOPE_SESSION NULL is returned when IdentifierType is SQL_ROWVER. For a description of each value, see the description of Scope in "Syntax," earlier in this section.
COLUMN_NAME (ODBC 1.0) | 2 | Varchar not NULL | Column name. The driver returns an empty string for a column that does not have a name.
DATA_TYPE (ODBC 1.0) | 3 | Smallint not NULL | SQL data type. This can be an ODBC SQL data type or a driver-specific SQL data type. For a list of valid ODBC SQL data types, see "SQL Data Types" in Appendix D: Data Types. For information about driver-specific SQL data types, see the driver's documentation.
TYPE_NAME (ODBC 1.0) | 4 | Varchar not NULL | Data source–dependent data type name; for example, "CHAR", "VARCHAR", "MONEY", "LONG VARBINARY", or "CHAR ( ) FOR BIT DATA".
COLUMN_SIZE (ODBC 1.0) | 5 | Integer | The size of the column on the data source. For more information concerning column size, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size," in Appendix D: Data Types.
BUFFER_LENGTH (ODBC 1.0) | 6 | Integer | The length in bytes of data transferred on an SQLGetData or SQLFetch operation if SQL_C_DEFAULT is specified. For numeric data, this size may be different than the size of the data stored on the data source. This value is the same as the COLUMN_SIZE column for character or binary data. For more information, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size," in Appendix D: Data Types.
DECIMAL_DIGITS (ODBC 1.0) | 7 | Smallint | The decimal digits of the column on the data source. NULL is returned for data types where decimal digits are not applicable. For more information concerning decimal digits, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size," in Appendix D: Data Types.
PSEUDO_COLUMN (ODBC 2.0) | 8 | Smallint | Indicates whether the column is a pseudo-column, such as Oracle ROWID: SQL_PC_UNKNOWN SQL_PC_NOT_PSEUDO SQL_PC_PSEUDO Note For maximum interoperability, pseudo-columns should not be quoted with the identifier quote character returned by SQLGetInfo.

After the application retrieves values for SQL_BEST_ROWID, the application can use these values to reselect that row within the defined scope. The SELECT statement is guaranteed to return either no rows or one row.

If an application reselects a row based on the rowid column or columns and the row is not found, the application can assume that the row was deleted or the rowid columns were modified. The opposite is not true: even if the rowid has not changed, the other columns in the row may have changed.

Columns returned for column type SQL_BEST_ROWID are useful for applications that need to scroll forward and back within a result set to retrieve the most recent data from a set of rows. The column or columns of the rowid are guaranteed not to change while positioned on that row.

The column or columns of the rowid may remain valid even when the cursor is not positioned on the row; the application can determine this by checking the SCOPE column in the result set.

Columns returned for column type SQL_ROWVER are useful for applications that need the ability to check whether any columns in a given row have been updated while the row was reselected using the rowid. For example, after reselecting a row using rowid, the application can compare the previous values in the SQL_ROWVER columns to the ones just fetched. If the value in a SQL_ROWVER column differs from the previous value, the application can alert the user that data on the display has changed.

Code Example

For a code example of a similar function, see SQLColumns.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Returning the columns in a table or tables | SQLColumns
Fetching a single row or a block of data in a forward-only direction | SQLFetch
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Returning the columns of a primary key | SQLPrimaryKeys


SQLStatistics

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ISO 92

Summary

SQLStatistics retrieves a list of statistics about a single table and the indexes associated with the table. The driver returns the information as a result set.

Syntax

SQLRETURN SQLStatistics(
     SQLHSTMT     StatementHandle,
     SQLCHAR *     CatalogName,
     SQLSMALLINT     NameLength1,
     SQLCHAR *     SchemaName,
     SQLSMALLINT     NameLength2,
     SQLCHAR *     TableName,
     SQLSMALLINT     NameLength3,
     SQLUSMALLINT     Unique,
     SQLUSMALLINT     Reserved);

Arguments

StatementHandle
[Input]
Statement handle.
CatalogName
[Input]
Catalog name. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have catalogs. CatalogName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, CatalogName is treated as an identifier and its case is not significant. If it is SQL_FALSE, CatalogName is an ordinary argument; it is treated literally, and its case is significant. For more information, see "Arguments in Catalog Functions" in Chapter 7: Catalog Functions.

NameLength1
[Input]
Length of *CatalogName.
SchemaName
[Input]
Schema name. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have schemas. SchemaName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, SchemaName is treated as an identifier and its case is not significant. If it is SQL_FALSE, SchemaName is an ordinary argument; it is treated literally, and its case is significant.

NameLength2
[Input]
Length of *SchemaName.
TableName
[Input]
Table name. This argument cannot be a null pointer. SchemaName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, TableName is treated as an identifier and its case is not significant. If it is SQL_FALSE, TableName is an ordinary argument; it is treated literally, and its case is significant.

NameLength3
[Input]
Length of *TableName.
Unique
[Input]
Type of index: SQL_INDEX_UNIQUE or SQL_INDEX_ALL.
Reserved
[Input]
Indicates the importance of the CARDINALITY and PAGES columns in the result set. The following options affect the return of the CARDINALITY and PAGES columns only; index information is returned even if CARDINALITY and PAGES are not returned.

SQL_ENSURE requests that the driver unconditionally retrieve the statistics. (Drivers that conform only to the X/Open standard and do not support ODBC extensions will not be able to support SQL_ENSURE.)

SQL_QUICK requests that the driver retrieve the CARDINALITY and PAGES only if they are readily available from the server. In this case, the driver does not ensure that the values are current. (Applications that are written to the X/Open standard will always get SQL_QUICK behavior from ODBC 3.x-compliant drivers.)

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLStatistics returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLStatistics and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
24000 Invalid cursor state A cursor was open on the StatementHandle, and SQLFetch or SQLFetchScroll had been called. This error is returned by the Driver Manager if SQLFetch or SQLFetchScroll has not returned SQL_NO_DATA and is returned by the driver if SQLFetch or SQLFetchScroll has returned SQL_NO_DATA.

A cursor was open on the StatementHandle, but SQLFetch or SQLFetchScroll had not been called.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 | Memory allocation error | The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle, and then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY009 Invalid use of null pointer The TableName argument was a null pointer.

The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, the CatalogName argument was a null pointer, and the SQL_CATALOG_NAME InfoType returns that catalog names are supported.

(DM) The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, and the SchemaName argument was a null pointer.
HY010 Function sequence error (DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 Invalid string or buffer length (DM) The value of one of the name length arguments was less than 0 but not equal to SQL_NTS.

The value of one of the name length arguments exceeded the maximum length value for the corresponding name.
HY100 | Uniqueness option type out of range | (DM) An invalid Unique value was specified.
HY101 | Accuracy option type out of range | (DM) An invalid Reserved value was specified.
HYC00 Optional feature not implemented A catalog was specified, and the driver or data source does not support catalogs.

A schema was specified, and the driver or data source does not support schemas.

The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source.

The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.
HYT00 | Timeout expired | The query timeout period expired before the data source returned the requested result set. The timeout period is set through SQLSetStmtAttr, SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

SQLStatistics returns information about a single table as a standard result set, ordered by NON_UNIQUE, TYPE, INDEX_QUALIFIER, INDEX_NAME, and ORDINAL_POSITION. The result set combines statistics information (in the CARDINALITY and PAGES columns of the result set) for the table with information about each index. For information about how this information might be used, see "Uses of Catalog Data" in Chapter 7: Catalog Functions.

To determine the actual lengths of the TABLE_CAT, TABLE_SCHEM, TABLE_NAME, and COLUMN_NAME columns, an application can call SQLGetInfo with the SQL_MAX_CATALOG_NAME_LEN, SQL_MAX_SCHEMA_NAME_LEN, SQL_MAX_TABLE_NAME_LEN, and SQL_MAX_COLUMN_NAME_LEN options.

Note For more information about the general use, arguments, and returned data of ODBC catalog functions, see Chapter 7: Catalog Functions.

The following columns have been renamed for ODBC 3.x. The column name changes do not affect backward compatibility because applications bind by column number.

ODBC 2.0 column | ODBC 3.x column
TABLE_QUALIFIER | TABLE_CAT
TABLE_OWNER | TABLE_SCHEM
SEQ_IN_INDEX | ORDINAL_POSITION
COLLATION | ASC_OR_DESC

The following table lists the columns in the result set. Additional columns beyond column 13 (FILTER_CONDITION) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "Data Returned by Catalog Functions" in Chapter 7: Catalog Functions.

Column name | Column number | Data type | Comments
TABLE_CAT (ODBC 1.0) | 1 | Varchar | Catalog name of the table to which the statistic or index applies; NULL if not applicable to the data source. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have catalogs.
TABLE_SCHEM (ODBC 1.0) | 2 | Varchar | Schema name of the table to which the statistic or index applies; NULL if not applicable to the data source. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have schemas.
TABLE_NAME (ODBC 1.0) | 3 | Varchar not NULL | Table name of the table to which the statistic or index applies.
NON_UNIQUE (ODBC 1.0) | 4 | Smallint | Indicates whether the index prohibits duplicate values: SQL_TRUE if the index values can be nonunique. SQL_FALSE if the index values must be unique. NULL is returned if TYPE is SQL_TABLE_STAT.
INDEX_QUALIFIER (ODBC 1.0) | 5 | Varchar | The identifier that is used to qualify the index name doing a DROP INDEX; NULL is returned if an index qualifier is not supported by the data source or if TYPE is SQL_TABLE_STAT. If a non-null value is returned in this column, it must be used to qualify the index name on a DROP INDEX statement; otherwise, the TABLE_SCHEM should be used to qualify the index name.
INDEX_NAME (ODBC 1.0) | 6 | Varchar | Index name; NULL is returned if TYPE is SQL_TABLE_STAT.
TYPE (ODBC 1.0) | 7 | Smallint not NULL | Type of information being returned: SQL_TABLE_STAT indicates a statistic for the table (in the CARDINALITY or PAGES column). SQL_INDEX_BTREE indicates a B-Tree index. SQL_INDEX_CLUSTERED indicates a clustered index. SQL_INDEX_CONTENT indicates a content index. SQL_INDEX_HASHED indicates a hashed index. SQL_INDEX_OTHER indicates another type of index.
ORDINAL_POSITION (ODBC 1.0) | 8 | Smallint | Column sequence number in index (starting with 1); NULL is returned if TYPE is SQL_TABLE_STAT.
COLUMN_NAME (ODBC 1.0) | 9 | Varchar | Column name. If the column is based on an expression, such as SALARY + BENEFITS, the expression is returned; if the expression cannot be determined, an empty string is returned. NULL is returned if TYPE is SQL_TABLE_STAT.
ASC_OR_DESC (ODBC 1.0) | 10 | Char(1) | Sort sequence for the column: "A" for ascending; "D" for descending; NULL is returned if column sort sequence is not supported by the data source or if TYPE is SQL_TABLE_STAT.
CARDINALITY (ODBC 1.0) | 11 | Integer | Cardinality of table or index; number of rows in table if TYPE is SQL_TABLE_STAT; number of unique values in the index if TYPE is not SQL_TABLE_STAT; NULL is returned if the value is not available from the data source.
PAGES (ODBC 1.0) | 12 | Integer | Number of pages used to store the index or table; number of pages for the table if TYPE is SQL_TABLE_STAT; number of pages for the index if TYPE is not SQL_TABLE_STAT; NULL is returned if the value is not available from the data source or if not applicable to the data source.
FILTER_CONDITION (ODBC 2.0) | 13 | Varchar | If the index is a filtered index, this is the filter condition, such as SALARY > 30000; if the filter condition cannot be determined, this is an empty string. NULL if the index is not a filtered index, it cannot be determined whether the index is a filtered index, or TYPE is SQL_TABLE_STAT.

If the row in the result set corresponds to a table, the driver sets TYPE to SQL_TABLE_STAT and sets NON_UNIQUE, INDEX_QUALIFIER, INDEX_NAME, ORDINAL_POSITION, COLUMN_NAME, and ASC_OR_DESC to NULL. If CARDINALITY or PAGES are not available from the data source, the driver sets them to NULL.

Code Example

For a code example of a similar function, see SQLColumns.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Fetching a single row or a block of data in a forward-only direction. | SQLFetch
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Returning the columns of foreign keys | SQLForeignKeys
Returning the columns of a primary key | SQLPrimaryKeys


SQLTablePrivileges

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: ODBC

Summary

SQLTablePrivileges returns a list of tables and the privileges associated with each table. The driver returns the information as a result set on the specified statement.

Syntax

SQLRETURN SQLTablePrivileges(
     SQLHSTMT     StatementHandle,
     SQLCHAR *     CatalogName,
     SQLSMALLINT     NameLength1,
     SQLCHAR *     SchemaName,
     SQLSMALLINT     NameLength2,
     SQLCHAR *     TableName,
     SQLSMALLINT     NameLength3);

Arguments

StatementHandle
[Input]
Statement handle.
CatalogName
[Input]
Table catalog. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have catalogs. CatalogName cannot contain a string search pattern.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, CatalogName is treated as an identifier and its case is not significant. If it is SQL_FALSE, CatalogName is an ordinary argument; it is treated literally, and its case is significant. For more information, see "Arguments in Catalog Functions" in Chapter 7: Catalog Functions.

NameLength1
[Input]
Length of *CatalogName.
SchemaName
[Input]
String search pattern for schema names. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have schemas.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, SchemaName is treated as an identifier and its case is not significant. If it is SQL_FALSE, SchemaName is a pattern value argument; it is treated literally, and its case is significant.

NameLength2
[Input]
Length of *SchemaName.
TableName
[Input]
String search pattern for table names.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, TableName is treated as an identifier and its case is not significant. If it is SQL_FALSE, TableName is a pattern value argument; it is treated literally, and its case is significant.

NameLength3
[Input]
Length of *TableName.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLTablePrivileges returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLTablePrivileges and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
24000 Invalid cursor state A cursor was open on the StatementHandle, and SQLFetch or SQLFetchScroll had been called. This error is returned by the Driver Manager if SQLFetch or SQLFetchScroll has not returned SQL_NO_DATA and is returned by the driver if SQLFetch or SQLFetchScroll has returned SQL_NO_DATA.

A cursor was open on the StatementHandle, but SQLFetch or SQLFetchScroll had not been called.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 Memory allocation
error 		The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY009 Invalid use of null pointer The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, the CatalogName argument was a null pointer, and the SQL_CATALOG_NAME InfoType returns that catalog names are supported.

(DM) The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, and the SchemaName or TableName argument was a null pointer.
HY010 Function sequence
error 		(DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 Invalid string or buffer length (DM) The value of one of the name length arguments was less than 0 but not equal to SQL_NTS.

The value of one of the name length arguments exceeded the maximum length value for the corresponding qualifier or name.
HYC00 Optional feature not implemented A catalog was specified, and the driver or data source does not support catalogs.

A schema was specified, and the driver or data source does not support schemas.

A string search pattern was specified for the table schema, table name, or column name, and the data source does not support search patterns for one or more of those arguments.

The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source.

The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.
HYT00 | Timeout expired | The query timeout period expired before the data source returned the result set. The timeout period is set through SQLSetStmtAttr, SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

The SchemaName and TableName arguments accept search patterns. For more information about valid search patterns, see "Pattern Value Arguments" in Chapter 7: Catalog Functions.

SQLTablePrivileges returns the results as a standard result set, ordered by TABLE_CAT, TABLE_SCHEM, TABLE_NAME, PRIVILEGE, and GRANTEE.

To determine the actual lengths of the TABLE_CAT, TABLE_SCHEM, and TABLE_NAME columns, an application can call SQLGetInfo with the SQL_MAX_CATALOG_NAME_LEN, SQL_MAX_SCHEMA_NAME_LEN, and SQL_MAX_TABLE_NAME_LEN options.

Note For more information about the general use, arguments, and returned data of ODBC catalog functions, see Chapter 7: Catalog Functions.

The following columns have been renamed for ODBC 3.x. The column name changes do not affect backward compatibility because applications bind by column number.

ODBC 2.0 column | ODBC 3.x column
TABLE_QUALIFIER | TABLE_CAT
TABLE_OWNER | TABLE_SCHEM

The following table lists the columns in the result set. Additional columns beyond column 7 (IS_GRANTABLE) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "Data Returned by Catalog Functions" in Chapter 7: Catalog Functions.

Column name 		Column number
Data type
Comments
TABLE_CAT
(ODBC 1.0) 		1 Varchar Catalog name; NULL if not applicable to the data source. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have catalogs.
TABLE_SCHEM
(ODBC 1.0) 		2 Varchar Schema name; NULL if not applicable to the data source. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have schemas.
TABLE_NAME
(ODBC 1.0) 		3 Varchar
not NULL 		Table name.
GRANTOR
(ODBC 1.0) 		4 Varchar Name of the user who granted the privilege; NULL if not applicable to the data source.

For all rows in which the value in the GRANTEE column is the owner of the object, the GRANTOR column will be "_SYSTEM".
GRANTEE
(ODBC 1.0) 		5 Varchar
not NULL 		Name of the user to whom the privilege was granted.
PRIVILEGE
(ODBC 1.0) 		6 Varchar
not NULL 		The table privilege. May be one of the following or a data source–specific privilege.

SELECT: The grantee is permitted to retrieve data for one or more columns of the table.

INSERT: The grantee is permitted to insert new rows containing data for one or more columns into the table.

UPDATE: The grantee is permitted to update the data in one or more columns of the table.

DELETE: The grantee is permitted to delete rows of data from the table.

REFERENCES: The grantee is permitted to refer to one or more columns of the table within a constraint (for example, a unique, referential, or table check constraint).

The scope of action permitted the grantee by a given table privilege is data source–dependent. For example, the UPDATE privilege might permit the grantee to update all columns in a table on one data source and only those columns for which the grantor has the UPDATE privilege on another data source.
IS_GRANTABLE
(ODBC 1.0) 		7 Varchar Indicates whether the grantee is permitted to grant the privilege to other users; "YES", "NO", or NULL if unknown or not applicable to the data source.

A privilege is either grantable or not grantable but not both. The result set returned by SQLColumnPrivileges will never contain two rows for which all columns except the IS_GRANTABLE column contain the same value.

Code Example

For a code example of a similar function, see SQLColumns.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Returning privileges for a column or columns | SQLColumnPrivileges
Returning the columns in a table or tables | SQLColumns
Fetching a single row or a block of data in a forward-only direction | SQLFetch
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Returning table statistics and indexes | SQLStatistics
Returning a list of tables in a data source | SQLTables


SQLTables

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: X/Open

Summary

SQLTables returns the list of table, catalog, or schema names, and table types, stored in a specific data source. The driver returns the information as a result set.

Syntax

SQLRETURN SQLTables(
     SQLHSTMT     StatementHandle,
     SQLCHAR *     CatalogName,
     SQLSMALLINT     NameLength1,
     SQLCHAR *     SchemaName,
     SQLSMALLINT     NameLength2,
     SQLCHAR *     TableName,
     SQLSMALLINT     NameLength3,
     SQLCHAR *     TableType,
     SQLSMALLINT     NameLength4);

Arguments

StatementHandle
[Input]
Statement handle for retrieved results.
CatalogName
[Input]
Catalog name. The CatalogName argument accepts search patterns if the SQL_ODBC_VERSION environment attribute is SQL_OV_ODBC3; it does not accept search patterns if SQL_OV_ODBC2 is set. If a driver supports catalogs for some tables but not for others, such as when a driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have catalogs.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, CatalogName is treated as an identifier and its case is not significant. If it is SQL_FALSE, CatalogName is a pattern value argument; it is treated literally, and its case is significant. For more information, see "Arguments in Catalog Functions" in Chapter 7: Catalog Functions.

NameLength1
[Input]
Length of *CatalogName.
SchemaName
[Input]
String search pattern for schema names. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have schemas.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, SchemaName is treated as an identifier and its case is not significant. If it is SQL_FALSE, SchemaName is a pattern value argument; it is treated literally, and its case is significant.

NameLength2
[Input]
Length of *SchemaName.
TableName
[Input]
String search pattern for table names.

If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, TableName is treated as an identifier and its case is not significant. If it is SQL_FALSE, TableName is a pattern value argument; it is treated literally, and its case is significant.

NameLength3
[Input]
Length of *TableName.
TableType
[Input]
List of table types to match.

Note that the SQL_ATTR_METADATA_ID statement attribute has no effect upon the TableType argument. TableType is a value list argument, no matter what the setting of SQL_ATTR_METADATA_ID.

NameLength4
[Input]
Length of *TableType.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.

Diagnostics

When SQLTables returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling SQLGetDiagRec with a HandleType of SQL_HANDLE_STMT and a Handle of StatementHandle. The following table lists the SQLSTATE values commonly returned by SQLTables and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.

SQLSTATE | Error | Description
01000 | General warning | Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)
08S01 | Communication link failure | The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.
24000 Invalid cursor state A cursor was open on the StatementHandle, and SQLFetch or SQLFetchScroll had been called. This error is returned by the Driver Manager if SQLFetch or SQLFetchScroll has not returned SQL_NO_DATA and is returned by the driver if SQLFetch or SQLFetchScroll has returned SQL_NO_DATA.

A cursor was open on the StatementHandle, but SQLFetch or SQLFetchScroll had not been called.
40001 | Serialization failure | The transaction was rolled back due to a resource deadlock with another transaction.
40003 | Statement completion unknown | The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.
HY000 | General error | An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the *MessageText buffer describes the error and its cause.
HY001 Memory allocation
error 		The driver was unable to allocate memory required to support execution or completion of the function.
HY008 Operation canceled Asynchronous processing was enabled for the StatementHandle. The function was called, and before it completed execution, SQLCancel was called on the StatementHandle. Then the function was called again on the StatementHandle.

The function was called, and before it completed execution, SQLCancel was called on the StatementHandle from a different thread in a multithread application.
HY009 Invalid use of null pointer The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, the CatalogName argument was a null pointer, and the SQL_CATALOG_NAME InfoType returns that catalog names are supported.

(DM) The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, and the SchemaName or TableName argument was a null pointer.
HY010 Function sequence error (DM) An asynchronously executing function (not this one) was called for the StatementHandle and was still executing when this function was called.

(DM) SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos was called for the StatementHandle and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.
HY013 | Memory management error | The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.
HY090 Invalid string or buffer length (DM) The value of one of the length arguments was less than 0 but not equal to SQL_NTS.

The value of one of the name length arguments exceeded the maximum length value for the corresponding name.
HYC00 Optional feature not implemented A catalog was specified, and the driver or data source does not support catalogs.

A schema was specified, and the driver or data source does not support schemas.

A string search pattern was specified for the catalog name, table schema, or table name, and the data source does not support search patterns for one or more of those arguments.

The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source.

The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.
HYT00 | Timeout expired | The query timeout period expired before the data source returned the requested result set. The timeout period is set through SQLSetStmtAttr, SQL_ATTR_QUERY_TIMEOUT.
HYT01 | Connection timeout expired | The connection timeout period expired before the data source responded to the request. The connection timeout period is set through SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 | Driver does not support this function | (DM) The driver associated with the StatementHandle does not support the function.

Comments

SQLTables lists all tables in the requested range. A user may or may not have SELECT privileges to any of these tables. To check accessibility, an application can:

  • Call SQLGetInfo and check the SQL_ACCESSIBLE_TABLES information type.
  • Call SQLTablePrivileges to check the privileges for each table.

Otherwise, the application must be able to handle a situation where the user selects a table for which SELECT privileges are not granted.

The SchemaName and TableName arguments accept search patterns. The CatalogName argument accepts search patterns if the SQL_ODBC_VERSION environment attribute is SQL_OV_ODBC3; it does not accept search patterns if SQL_OV_ODBC2 is set. If SQL_OV_ODBC3 is set, an ODBC 3.x driver will require that wildcard characters in the CatalogName argument be escaped to be treated literally. For more information about valid search patterns, see "Pattern Value Arguments" in Chapter 7: Catalog Functions.

Note For more information about the general use, arguments, and returned data of ODBC catalog functions, see Chapter 7: Catalog Functions.

To support enumeration of catalogs, schemas, and table types, the following special semantics are defined for the CatalogName, SchemaName, TableName, and TableType arguments of SQLTables:

  • If CatalogName is SQL_ALL_CATALOGS and SchemaName and TableName are empty strings, the result set contains a list of valid catalogs for the data source. (All columns except the TABLE_CAT column contain NULLs.)
  • If SchemaName is SQL_ALL_SCHEMAS and CatalogName and TableName are empty strings, the result set contains a list of valid schemas for the data source. (All columns except the TABLE_SCHEM column contain NULLs.)
  • If TableType is SQL_ALL_TABLE_TYPES and CatalogName, SchemaName, and TableName are empty strings, the result set contains a list of valid table types for the data source. (All columns except the TABLE_TYPE column contain NULLs.)

If TableType is not an empty string, it must contain a list of comma-separated values for the types of interest; each value may be enclosed in single quotation marks (') or unquoted—for example, 'TABLE', 'VIEW' or TABLE, VIEW. An application should always specify the table type in uppercase; the driver should convert the table type to whatever case is needed by the data source. If the data source does not support a specified table type, SQLTables does not return any results for that type.

SQLTables returns the results as a standard result set, ordered by TABLE_TYPE, TABLE_CAT, TABLE_SCHEM, and TABLE_NAME. For information about how this information might be used, see "Uses of Catalog Data" in Chapter 7: Catalog Functions.

To determine the actual lengths of the TABLE_CAT, TABLE_SCHEM, and TABLE_NAME columns, an application can call SQLGetInfo with the SQL_MAX_CATALOG_NAME_LEN, SQL_MAX_SCHEMA_NAME_LEN, and SQL_MAX_TABLE_NAME_LEN information types.

The following columns have been renamed for ODBC 3.x. The column name changes do not affect backward compatibility because applications bind by column number.

ODBC 2.0 column | ODBC 3.x column
TABLE_QUALIFIER | TABLE_CAT
TABLE_OWNER | TABLE_SCHEM

The following table lists the columns in the result set. Additional columns beyond column 5 (REMARKS) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "Data Returned by Catalog Functions" in Chapter 7: Catalog Functions.

Column name | Column number | Data type | Comments
TABLE_CAT (ODBC 1.0) | 1 | Varchar | Catalog name; NULL if not applicable to the data source. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have catalogs.
TABLE_SCHEM (ODBC 1.0) | 2 | Varchar | Schema name; NULL if not applicable to the data source. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have schemas.
TABLE_NAME (ODBC 1.0) | 3 | Varchar | Table name.
TABLE_TYPE (ODBC 1.0) | 4 | Varchar | Table type name; one of the following: "TABLE", "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM", or a data source–specific type name. The meanings of "ALIAS" and "SYNONYM" are driver-specific.
REMARKS (ODBC 1.0) | 5 | Varchar | A description of the table.

Code Example

For a code example of a similar function, see SQLColumns.

Related Functions

For information about | See
Binding a buffer to a column in a result set | SQLBindCol
Canceling statement processing | SQLCancel
Returning privileges for a column or columns | SQLColumnPrivileges
Returning the columns in a table or tables | SQLColumns
Fetching a single row or a block of data in a forward-only direction | SQLFetch
Fetching a block of data or scrolling through a result set | SQLFetchScroll
Returning table statistics and indexes | SQLStatistics
Returning privileges for a table or tables | SQLTablePrivileges


SQLTransact

Conformance

Version Introduced: ODBC 1.0
Standards Compliance: Deprecated

Summary

In ODBC 3.x, the ODBC 2.x function SQLTransact has been replaced by SQLEndTran. For more information, see SQLEndTran.

Note For more information about what the Driver Manager maps this function to when an ODBC 2.x application is working with an ODBC 3.x driver, see "Mapping Deprecated Functions" in Appendix G: Driver Guidelines for Backward Compatibility.


Chapter 22: Setup DLL API Reference

This chapter describes the syntax of the driver setup DLL API, which consists of two functions (ConfigDriver and ConfigDSN). ConfigDriver and ConfigDSN can be either in the driver DLL or in a separate setup DLL.

In addition, this chapter describes the syntax of the translator setup DLL API, which consists of a single function (ConfigTranslator). ConfigTranslator can be either in the translator DLL or in a separate setup DLL.

Each function is labeled with the version of ODBC in which it was introduced.


ConfigDriver

Conformance

Version Introduced: ODBC 2.5

Summary

ConfigDriver allows a setup program to perform install and uninstall functions without requiring the program to call ConfigDSN. This function will perform driver-specific functions such as creating driver-specific system information and performing DSN conversions during installation, as well as cleaning up system information modifications during uninstall. This function is exposed by the driver setup DLL or a separate setup DLL.

Syntax

BOOL ConfigDriver(
     HWND     hwndParent,
     WORD     fRequest,
     LPCSTR     lpszDriver,
     LPCSTR     lpszArgs,
     LPSTR     lpszMsg,
     WORD     cbMsgMax,
     WORD *     pcbMsgOut);

Arguments

hwndParent
[Input]
Parent window handle. The function will not display any dialog boxes if the handle is null.
fRequest
[Input]
Type of request. The fRequest argument must contain one of the following values:

ODBC_INSTALL_DRIVER: Install a new driver.

ODBC_REMOVE_DRIVER: Remove a driver.

This option can also be driver-specific, in which case the fRequest argument for the first option must start from ODBC_CONFIG_DRIVER_MAX+1. The fRequest argument for any additional option must also start from a value greater than ODBC_CONFIG_DRIVER_MAX+1.

lpszDriver
[Input]
The name of the driver as registered in the Odbcinst.ini key of the system information.
lpszArgs
[Input]
A null-terminated string containing arguments for a driver-specific fRequest.
lpszMsg
[Output]
A null-terminated string containing an output message from the driver setup.
cbMsgMax
[Input]
Length of lpszMsg.
pcbMsgOut
[Output]
Total number of bytes available to return in lpszMsg.

If the number of bytes available to return is greater than or equal to cbMsgMax, the output message in lpszMsg is truncated to cbMsgMax minus the null-termination character. The pcbMsgOut argument can be a null pointer.

Returns

The function returns TRUE if it is successful, FALSE if it fails.

Diagnostics

When ConfigDriver returns FALSE, an associated *pfErrorCode value is posted to the installer error buffer by a call to SQLPostInstallerError and can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_INVALID_HWND | Invalid window handle | The hwndParent argument was invalid.
ODBC_ERROR_INVALID_REQUEST_TYPE | Invalid type of request | The fRequest argument was not one of the following: ODBC_INSTALL_DRIVER ODBC_REMOVE_DRIVER The driver-specific option was less than or equal to ODBC_CONFIG_DRIVER_MAX.
ODBC_ERROR_INVALID_NAME | Invalid driver or translator name | The lpszDriver argument was invalid. It could not be found in the registry.
ODBC_ERROR_REQUEST_FAILED | Request failed | Could not perform the operation requested by the fRequest argument.
ODBC_ERROR_DRIVER_SPECIFIC | Driver- or translator-specific error | A driver-specific error for which there is no defined ODBC installer error. The SzError argument in a call to the SQLPostInstallerError function should contain the driver-specific error message.

Comments

Driver-Specific Options

An application can request driver-specific features exposed by the driver by using the fRequest argument. The fRequest for the first option will be ODBC_CONFIG_DRIVER_MAX plus 1, and additional options will be incremented by 1 from that value. Any arguments required by the driver for that function should be provided in a null-terminated string passed in the lpszArgs argument. Drivers providing such functionality should maintain a table of driver-specific options. The options should be fully documented in driver documentation. Application writers who use driver-specific options should be aware that this will make the application less interoperable.

Messages

A driver setup routine can send a text message to an application as a null-terminated string in the lpszMsg buffer. The message will be truncated to cbMsgMax minus the null-termination character by the ConfigDriver function if it is greater than or equal to cbMsgMax characters.


ConfigDSN

Conformance

Version Introduced: ODBC 1.0

Summary

ConfigDSN adds, modifies, or deletes data sources from the system information. It may prompt the user for connection information. It can be in the driver DLL or a separate setup DLL.

Syntax

BOOL ConfigDSN(
     HWND     hwndParent,
     WORD     fRequest,
     LPCSTR     lpszDriver,
     LPCSTR     lpszAttributes);

Arguments

hwndParent
[Input]
Parent window handle. The function will not display any dialog boxes if the handle is null.
fRequest
[Input]
Type of request. The fRequest argument must contain one of the following values:

ODBC_ADD_DSN: Add a new data source.
ODBC_CONFIG_DSN: Configure (modify) an existing data source.
ODBC_REMOVE_DSN: Remove an existing data source.

lpszDriver
[Input]
Driver description (usually the name of the associated DBMS) presented to users instead of the physical driver name.
lpszAttributes
[Input]
List of attributes in the form of keyword-value pairs. For information about the list structure, see "Comments."

Returns

The function returns TRUE if it is successful, FALSE if it fails.

Diagnostics

When ConfigDSN returns FALSE, an associated *pfErrorCode value is posted to the installer error buffer by a call to SQLPostInstallerError and can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_INVALID_HWND | Invalid window handle | The hwndParent argument was invalid.
ODBC_ERROR_INVALID_KEYWORD_VALUE | Invalid keyword-value pairs | The lpszAttributes argument contained a syntax error.
ODBC_ERROR_INVALID_NAME | Invalid driver or translator name | The lpszDriver argument was invalid. It could not be found in the registry.
ODBC_ERROR_INVALID_REQUEST_TYPE | Invalid type of request | The fRequest argument was not one of the following: ODBC_ADD_DSN ODBC_CONFIG_DSN ODBC_REMOVE_DSN
ODBC_ERROR_REQUEST_FAILED | Request failed | Could not perform the operation requested by the fRequest argument.
ODBC_ERROR_DRIVER_SPECIFIC | Driver- or translator-specific error | A driver-specific error for which there is no defined ODBC installer error. The SzError argument in a call to the SQLPostInstallerError function should contain the driver-specific error message.

Comments

ConfigDSN receives connection information from the installer DLL as a list of attributes in the form of keyword-value pairs. Each pair is terminated with a null byte, and the entire list is terminated with a null byte. (That is, two null bytes mark the end of the list.) Spaces are not allowed around the equal sign in the keyword-value pair. ConfigDSN can accept keywords that are not valid keywords for SQLBrowseConnect and SQLDriverConnect. ConfigDSN does not necessarily support all keywords that are valid keywords for SQLBrowseConnect and SQLDriverConnect. (ConfigDSN does not accept the DRIVER keyword.) The keywords used by the ConfigDSN function must support all the options required to re-create the data source using the AUTO setup feature of the installer. When the uses of the ConfigDSN values and the connection string values are the same, the same keywords should be used.

As in SQLBrowseConnect and SQLDriverConnect, the keywords and their values should not contain the []{}(),;?*=!@ characters, and the value of the DSN keyword cannot consist only of blanks. Because of the registry grammar, keywords and data source names cannot contain the backslash (\) character.

ConfigDSN should call SQLValidDSN to check the length of the data source name and to verify that no invalid characters are included in the name. If the data source name is longer than SQL_MAX_DSN_LENGTH or includes invalid characters, SQLValidDSN returns an error and ConfigDSN returns an error. The length of the data source name is also checked by SQLWriteDSNToIni.

For example, to configure a data source that requires a user ID, password, and database name, a setup application might pass the following keyword-value pairs:

DSN=Personnel Data\0UID=Smith\0PWD=Sesame\0DATABASE=Personnel\0\0

For more information about these keywords, see SQLDriverConnect and each driver's documentation.

To display a dialog box, hwndParent must not be null.

Adding a Data Source

If a data source name is passed to ConfigDSN in lpszAttributes, ConfigDSN checks that the name is valid. If the data source name matches an existing data source name and hwndParent is null, ConfigDSN overwrites the existing name. If it matches an existing name and hwndParent is not null, ConfigDSN prompts the user to overwrite the existing name.

If lpszAttributes contains enough information to connect to a data source, ConfigDSN can add the data source or display a dialog box with which the user can change the connection information. If lpszAttributes does not contain enough information to connect to a data source, ConfigDSN must determine the necessary information; if hwndParent is not null, it displays a dialog box to retrieve the information from the user.

If ConfigDSN displays a dialog box, it must display any connection information passed to it in lpszAttributes. In particular, if a data source name was passed to it, ConfigDSN displays that name but does not allow the user to change it. ConfigDSN can supply default values for connection information not passed to it in lpszAttributes.

If ConfigDSN cannot get complete connection information for a data source, it returns FALSE.

If ConfigDSN can get complete connection information for a data source, it calls SQLWriteDSNToIni in the installer DLL to add the new data source specification to the Odbc.ini file (or registry). SQLWriteDSNToIni adds the data source name to the [ODBC Data Sources] section, creates the data source specification section, and adds the DRIVER keyword with the driver description as its value. ConfigDSN calls SQLWritePrivateProfileString in the installer DLL to add any additional keywords and values used by the driver.

Modifying a Data Source

To modify a data source, a data source name must be passed to ConfigDSN in lpszAttributes. ConfigDSN checks that the data source name is in the Odbc.ini file (or registry).

If hwndParent is null, ConfigDSN uses the information in lpszAttributes to modify the information in the Odbc.ini file (or registry). If hwndParent is not null, ConfigDSN displays a dialog box using the information in lpszAttributes; for information not in lpszAttributes, it uses information from the system information. The user can modify the information before ConfigDSN stores it in the system information.

If the data source name was changed, ConfigDSN first calls SQLRemoveDSNFromIni in the installer DLL to remove the existing data source specification from the Odbc.ini file (or registry). It then follows the steps in the preceding section to add the new data source specification. If the data source name was not changed, ConfigDSN calls SQLWritePrivateProfileString in the installer DLL to make any other changes. ConfigDSN may not delete or change the value of the Driver keyword.

Deleting a Data Source

To delete a data source, a data source name must be passed to ConfigDSN in lpszAttributes. ConfigDSN checks that the data source name is in the Odbc.ini file (or registry). It then calls SQLRemoveDSNFromIni in the installer DLL to remove the data source.

Related Functions

For information about | See
Adding, modifying, or removing a data source | SQLConfigDataSource
Getting a value from the Odbc.ini file or the registry | SQLGetPrivateProfileString
Removing the default data source | SQLRemoveDefaultDataSource
Removing a data source name from Odbc.ini (or registry) | SQLRemoveDSNFromIni
Adding a data source name to Odbc.ini (or registry) | SQLWriteDSNToIni
Writing a value to the Odbc.ini file or the registry | SQLWritePrivateProfileString


ConfigTranslator

Conformance

Version Introduced: ODBC 2.0

Summary

ConfigTranslator returns a default translation option for a translator. It can be in the translator DLL or a separate setup DLL.

Syntax

BOOL ConfigTranslator(
     HWND     hwndParent,
     DWORD *     pvOption);

Arguments

hwndParent
[Input]
Parent window handle. The function will not display any dialog boxes if the handle is null.
pvOption
[Output]
A 32-bit translation option.

Returns

The function returns TRUE if it is successful, FALSE if it fails.

Diagnostics

When ConfigTranslator returns FALSE, an associated *pfErrorCode value is posted to the installer error buffer by a call to SQLPostInstallerError and can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_INVALID_HWND | Invalid window handle | The hwndParent argument was invalid or NULL.
ODBC_ERROR_DRIVER_SPECIFIC | Driver- or translator-specific error | A driver-specific error for which there is no defined ODBC installer error. The SzError argument in a call to the SQLPostInstallerError function should contain the driver-specific error message.
ODBC_ERROR_INVALID_OPTION | Invalid translation option | The pvOption argument contained an invalid value.

Comments

If the translator supports only a single translation option, ConfigTranslator returns TRUE and sets pvOption to the 32-bit option. Otherwise, it determines the default translation option to use. ConfigTranslator can display a dialog box with which a user selects a default translation option.

Related Functions

For information about | See
Getting a translation option | SQLGetConnectAttr
Selecting a translator | SQLGetTranslator
Setting a translation option | SQLSetConnectAttr


Chapter 23: Installer DLL API Reference

This chapter describes the syntax of the functions in the installer DLL API. The installer DLL API consists of 20 functions. Three of these functions, SQLGetTranslator, SQLRemoveDSNFromIni, and SQLWriteDSNToIni, are called only by setup DLLs. The other functions are called by the setup and administration programs.

Each function is labeled with the version of ODBC in which it was introduced.


SQLConfigDataSource

Conformance

Version Introduced: ODBC 1.0

Summary

SQLConfigDataSource adds, modifies, or deletes data sources.

Syntax

BOOL SQLConfigDataSource(
     HWND      hwndParent,
     WORD      fRequest,
     LPCSTR      lpszDriver,
     LPCSTR      lpszAttributes);

Arguments

hwndParent
[Input]
Parent window handle. The function will not display any dialog boxes if the handle is null.
fRequest
[Input]
Type of request. The fRequest argument must contain one of the following values:

ODBC_ADD_DSN: Add a new user data source.

ODBC_CONFIG_DSN: Configure (modify) an existing user data source.

ODBC_REMOVE_DSN: Remove an existing user data source.

ODBC_ADD_SYS_DSN: Add a new system data source.

ODBC_CONFIG_SYS_DSN: Modify an existing system data source.

ODBC_REMOVE_SYS_DSN: Remove an existing system data source.

ODBC_REMOVE_DEFAULT_DSN: Remove the default data source specification section from the system information. (It also removes the default driver specification section from the Odbcinst.ini entry in the system information. This fRequest performs the same function as the deprecated SQLRemoveDefaultDataSource function.) When this option is specified, all of the other parameters in the call to SQLConfigDataSource should be NULLs; if they are not NULL, they will be ignored.

lpszDriver
[Input]
Driver description (usually the name of the associated DBMS) presented to users instead of the physical driver name.
lpszAttributes
[Input]
List of attributes in the form of keyword-value pairs. For more information, see ConfigDSN in Chapter 22: Setup DLL Function Reference.

Returns

The function returns TRUE if it is successful, FALSE if it fails. If no entry exists in the system information when this function is called, the function returns FALSE.

Diagnostics

When SQLConfigDataSource returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_INVALID_HWND | Invalid window handle | The hwndParent argument was invalid or NULL.
ODBC_ERROR_INVALID_REQUEST_TYPE | Invalid type of request | The fRequest argument was not one of the following: ODBC_ADD_DSN ODBC_CONFIG_DSN ODBC_REMOVE_DSN ODBC_ADD_SYS_DSN ODBC_CONFIG_SYS_DSN ODBC_REMOVE_SYS_DSN ODBC_REMOVE_DEFAULT_DSN
ODBC_ERROR_INVALID_NAME | Invalid driver or translator name | The lpszDriver argument was invalid. It could not be found in the registry.
ODBC_ERROR_INVALID_KEYWORD_VALUE | Invalid keyword-value pairs | The lpszAttributes argument contained a syntax error.
ODBC_ERROR_REQUEST_FAILED | Request failed | The installer could not perform the operation requested by the fRequest argument. The call to ConfigDSN failed.
ODBC_ERROR_LOAD_LIBRARY_FAILED | Could not load the driver or translator setup library | The driver setup library could not be loaded.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Comments

SQLConfigDataSource uses the value of lpszDriver to read the full path of the setup DLL for the driver from the system information. It loads the DLL and calls ConfigDSN with the same arguments that were passed to it.

SQLConfigDataSource returns FALSE if it is unable to find or load the setup DLL or if the user cancels the dialog box. Otherwise, it returns the status it received from ConfigDSN.

SQLConfigDataSource maps the System DSN fRequests to the User DSN fRequests (ODBC_ADD_SYS_DSN to ODBC_ADD_DSN, ODBC_CONFIG_SYS_DSN to ODBC_CONFIG_DSN, and ODBC_REMOVE_SYS_DSN to ODBC_REMOVE_DSN). To distinguish user and System DSNs, SQLConfigDataSource sets the installer configuration mode according to the following table. Prior to returning, SQLConfigDataSource resets configuration mode to BOTHDSN.

fRequest | Configuration mode
ODBC_ADD_DSN | USERDSN_ONLY
ODBC_CONFIG_DSN | USERDSN_ONLY
ODBC_REMOVE_DSN | USERDSN_ONLY
ODBC_ADD_SYS_DSN | SYSTEMDSN_ONLY
ODBC_CONFIG_SYS_DSN | SYSTEMDSN_ONLY
ODBC_REMOVE_SYS_DSN | SYSTEMDSN_ONLY

Related Functions

For information about | See
Adding, modifying, or removing a data source | ConfigDSN (in the setup DLL)
Removing a data source name from the system information | SQLRemoveDSNFromIni
Adding a data source name to the system information | SQLWriteDSNToIni


SQLConfigDriver

Conformance

Version Introduced: ODBC 2.5

Summary

SQLConfigDriver loads the appropriate driver setup DLL and calls the ConfigDriver function.

Syntax

BOOL SQLConfigDriver(
     HWND      hwndParent,
     WORD      fRequest,
     LPCSTR      lpszDriver,
     LPCSTR      lpszArgs,
     LPSTR      lpszMsg,
     WORD      cbMsgMax,
     WORD *      pcbMsgOut);

Arguments

hwndParent
[Input]
Parent window handle. The function will not display any dialog boxes if the handle is null.
fRequest
[Input]
Type of request. fRequest must contain one of the following values:

ODBC_CONFIG_DRIVER: Changes the connection pooling timeout used by the driver.

ODBC_INSTALL_DRIVER: Installs a new driver.

ODBC_REMOVE_DRIVER: Removes an existing driver.

This option can also be driver-specific, in which case the fRequest for the first option must start from ODBC_CONFIG_DRIVER_MAX+1. The fRequest for any additional option must also start from a value greater than ODBC_CONFIG_DRIVER_MAX+1.

lpszDriver
[Input]
The name of the driver as registered in the system information.
lpszArgs
[Input]
A null-terminated string containing arguments for a driver-specific fRequest.
lpszMsg
[Output]
A null-terminated string containing an output message from the driver setup.
cbMsgMax
[Input]
Length of lpszMsg.
pcbMsgOut
[Output]
Total number of bytes available to return in lpszMsg. If the number of bytes available to return is greater than or equal to cbMsgMax, the output message in lpszMsg is truncated to cbMsgMax minus the null-termination character. The pcbMsgOut argument can be a null pointer.

Returns

The function returns TRUE if it is successful, FALSE if it fails.

Diagnostics

When SQLConfigDriver returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_INVALID_BUFF_LEN | Invalid buffer length | The lpszMsg argument was invalid.
ODBC_ERROR_INVALID_HWND | Invalid window handle | The hwndParent argument was invalid.
ODBC_ERROR_INVALID_REQUEST_TYPE | Invalid type of request | The fRequest argument was not one of the following: ODBC_INSTALL_DRIVER ODBC_REMOVE_DRIVER The fRequest argument was a driver-specific option that was less than or equal to ODBC_CONFIG_DRIVER_MAX.
ODBC_ERROR_INVALID_NAME | Invalid driver or translator name | The lpszDriver argument was invalid. It could not be found in the registry.
ODBC_ERROR_INVALID_KEYWORD_VALUE | Invalid keyword-value pairs | The lpszAttributes argument contained a syntax error.
ODBC_ERROR_REQUEST_FAILED | Request failed | The installer could not perform the operation requested by the fRequest argument. The call to ConfigDriver failed.
ODBC_ERROR_LOAD_LIBRARY_FAILED | Could not load the driver or translator setup library | The driver setup library could not be loaded.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Comments

SQLConfigDriver allows an application to call a driver's ConfigDriver routine without having to know the name and load the driver-specific setup DLL. A setup program calls this function after the driver setup DLL has been installed. The calling program should be aware that this function might not be available for all drivers. In such a case, the calling program should continue without error.

Driver-Specific Options

An application can request driver-specific features exposed by the driver by using the fRequest argument. The fRequest for the first option will be ODBC_CONFIG_DRIVER_MAX+1, and additional options will be incremented by 1 from that value. Any arguments required by the driver for that function should be provided in a null-terminated string passed in the lpszArgs argument. Drivers providing such functionality should maintain a table of driver-specific options. The options should be fully documented in driver documentation. Application writers who make use of driver-specific options should be aware that this use will make the application less interoperable.

Setting Connection Pooling Timeout

Connection pooling timeout properties can be set when setting the configuration of the driver. SQLConfigDriver is called with an fRequest of ODBC_CONFIG_DRIVER and lpszArgs set to CPTimeout. CPTimeout determines the amount of time that a connection can remain in the connection pool without being used. When the timeout expires, the connection is closed and removed from the pool. The default timeout is 60 seconds.

When SQLConfigDriver is called with fRequest set to ODBC_INSTALL_DRIVER or ODBC_REMOVE_DRIVER, the Driver Manager loads the appropriate driver setup DLL and calls the ConfigDriver function. When SQLConfigDriver is called with an fRequest of ODBC_CONFIG_DRIVER, all processing is performed in the ODBC installer, so the driver setup DLL does not need to be loaded.

Messages

A driver setup routine can send a text message to an application as null-terminated strings in the lpszMsg buffer. The message will be truncated to cbMsgMax minus the null-termination character by the ConfigDriver function if it is greater than or equal to cbMsgMax characters.

Related Functions

For information about | See
Adding, modifying, or removing a driver | ConfigDriver (in the setup DLL)
Removing the default data source | SQLRemoveDefaultDataSource


SQLCreateDataSource

Conformance

Version Introduced: ODBC 2.0

Summary

SQLCreateDataSource displays a dialog box with which the user can add a data source.

Syntax

BOOL SQLCreateDataSource(
     HWND     hwnd,
     LPSTR     lpszDS);

Arguments

hwnd
[Input]
Parent window handle.
lpszDS
[Input]
Data source name. lpszDS can be a null pointer or an empty string.

Returns

SQLCreateDataSource returns TRUE if the data source is created. Otherwise, it returns FALSE.

Diagnostics

When SQLCreateDataSource returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_INVALID_HWND | Invalid window handle | The hwnd argument was invalid or NULL.
ODBC_ERROR_INVALID_DSN | Invalid DSN | The lpszDS argument contained a string that was invalid for a DSN.
ODBC_ERROR_REQUEST_FAILED | Request failed | The call to ConfigDSN with the ODBC_ADD_DSN option failed.
ODBC_ERROR_LOAD_LIBRARY_FAILED | Could not load the driver or translator setup library | The driver setup library could not be loaded.
ODBC_ERROR_USER_CANCELED | User canceled operation | User canceled creation of a new data source.
ODBC_ERROR_CREATE_DSN_FAILED | Could not create the requested DSN | Could not connect to the database; the call to SQLDriverConnect for a File DSN did not return a successful connection. Could not write to the file.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Comments

If hwnd is null, SQLCreateDataSource returns FALSE. Otherwise, it displays the Create New Data Source dialog box with a wizard page for choosing the type of data source to be set up, as shown in the following illustration.

The default option is File Data Source. When a data source has been chosen and Next clicked, the following wizard page containing a list of installed drivers is displayed.

If Cancel is clicked, the dialog box disappears and SQLCreateDataSource returns FALSE with the error code of ODBC_ERROR_USER_CANCELED. If either the User Data Source or System Data Source option was selected, the Advanced button is unavailable.

When the Next button is clicked, one of the following will occur, depending on which type of data source was selected:

  • If File Data Source was selected, a wizard page is displayed for the user to enter a file name.
  • If either User Data Source or System Data Source was selected, a wizard page displaying the type of data source and driver is displayed for review, and when Finish is clicked, the data source is set up.

If Advanced is clicked from the Create New Data Source wizard page, a wizard page is displayed for the user to enter driver-specific information. In the text box of this dialog box, type the driver and keywords separated by returns, as shown in the following illustration.

Additional driver-specific keywords can be found under the description of SQLDriverConnect. All but DSN are allowed.

The default for the Verify This Connection option is TRUE. This default applies whether or not this wizard page is activated. If OK is clicked, the string specified in the text box and the Verify This Connection option value are cached. (If the Close button or Cancel is clicked, any newly entered driver-specific information is lost because the string specified in the text box and the Verify This Connection option value are not cached.)

If File Data Source was selected in the first wizard page, then after a driver has been selected and the keyword values have been entered in the Advanced wizard page, the user is prompted to enter a file name. Click Browse to search for a file name, in which case the default directory in the Browse box is specified by a combination of the path specified by CommonFileDir in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion and "ODBC\DataSources". (If CommonFileDir was "C:\Program Files\Common Files", the default directory would be "C:\Program Files\Common Files\ODBC\Data Sources".)

When a file name has been entered and Next is clicked, the file name entered is checked for validity against the standard file-naming rules of the operating system. If the file name is invalid, an error message box notifies the user that an invalid file name was entered. After the user acknowledges the message box, the focus is returned to the wizard page in which the file name is entered. If the file name is valid, a wizard page showing the selected keyword-value pairs is displayed for review, as shown in the following illustration.

If Finish is clicked and File Data Source was selected as the data source type, and if the Verify this connection option is TRUE, SQLDriverConnect is called with the SAVEFILE and DRIVER keywords. The DriverCompletion argument is set to SQL_DRIVER_COMPLETE. The file name for the SAVEFILE keyword is the name that was entered or chosen, and the driver name for the DRIVER keyword is the name that was chosen. If a driver-specific connection string was specified in the Advanced wizard page, that string is appended after the DRIVER keyword.

If SQLDriverConnect returns SQL_SUCCESS, the Driver Manager has created the File DSN. SQLCreateDataSource returns TRUE. If SQLDriverConnect does not return SQL_SUCCESS, a warning message box indicates that a connection could not be made to the data source. A DSN with minimal connection information can still be created. This message box allows the user to either cancel or continue with the File DSN creation.

If the user chooses to continue creating the DSN, this process continues as if the Verify this connection option were set to FALSE. If the user chooses to cancel, FALSE is returned for SQLCreateDataSource with an error code of ODBC_ERROR_CREATE_DSN_FAILED.

If File Data Source was selected as the data source type and the Verify this connection option is FALSE, a File DSN is created with the DRIVER keyword and user-specified connect string (if any) from the Advanced wizard page. If the file creation was successful, TRUE is returned for SQLCreateDataSource. If the file creation was not successful, an error message box notifies the user with whatever error was returned from the operating system. FALSE is returned for SQLCreateDataSource with an error code of ODBC_ERROR_CREATE_DSN_FAILED. For more information on file data sources, see "Connecting Using File Data Sources" in Chapter 6: Connecting to a Data Source or Driver, or see SQLDriverConnect.

If User or System Data Source was selected as the data source type, ConfigDSN in the driver setup library is called with the ODBC_ADD_DSN fRequest. For more information, see ConfigDSN in Chapter 22: Setup DLL Function Reference.

Related Functions

For information about | See
Managing data sources | SQLManageDataSources


SQLGetConfigMode

Conformance

Version Introduced: ODBC 3.0

Summary

SQLGetConfigMode retrieves the configuration mode that indicates where the Odbc.ini entry listing DSN values is in the system information.

Syntax

BOOL SQLGetConfigMode(
     UWORD *     pwConfigMode);

Arguments

pwConfigMode
[Output]
Pointer to the buffer containing the configuration mode. (See "Comments.") The value in *pwConfigMode can be:

ODBC_USER_DSN
ODBC_SYSTEM_DSN
ODBC_BOTH_DSN

Returns

The function returns TRUE if it is successful, FALSE if it fails.

Diagnostics

When SQLGetConfigMode returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Comments

This function is used to determine where the Odbc.ini entry listing DSN values is in the system information. If *pwConfigMode is ODBC_USER_DSN, the DSN is a User DSN and the function reads from the Odbc.ini entry in HKEY_CURRENT_USER. If it is ODBC_SYSTEM_DSN, the DSN is a System DSN and the function reads from the Odbc.ini entry in HKEY_LOCAL_MACHINE. If it is ODBC_BOTH_DSN, HKEY_CURRENT_USER is tried, and if it fails, HKEY_LOCAL_MACHINE is used.

By default, SQLGetConfigMode returns ODBC_BOTH_DSN. When a User DSN or a System DSN is created by a call to SQLConfigDataSource, the function sets the configuration mode to ODBC_USER_DSN or ODBC_SYSTEM_DSN to distinguish user and System DSNs while modifying a DSN. Prior to returning, SQLConfigDataSource resets the configuration mode to ODBC_BOTH_DSN.

Related Functions

For information about | See
Setting the configuration mode | SQLSetConfigMode


SQLGetInstalledDrivers

Conformance

Version Introduced: ODBC 1.0

Summary

SQLGetInstalledDrivers reads the [ODBC Drivers] section of the system information and returns a list of descriptions of the installed drivers.

Syntax

BOOL SQLGetInstalledDrivers(
     LPSTR     lpszBuf,
     WORD     cbBufMax,
     WORD *     pcbBufOut);

Arguments

lpszBuf
[Output]
List of descriptions of the installed drivers. For information about the list structure, see "Comments."
cbBufMax
[Input]
Length of lpszBuf.
pcbBufOut
[Output]
Total number of bytes (excluding the null-termination byte) returned in lpszBuf. If the number of bytes available to return is greater than or equal to cbBufMax, the list of driver descriptions in lpszBuf is truncated to cbBufMax minus the null-termination character. The pcbBufOut argument can be a null pointer.

Returns

The function returns TRUE if it is successful, FALSE if it fails.

Diagnostics

When SQLGetInstalledDrivers returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_INVALID_BUFF_LEN | Invalid buffer length | The lpszBuf argument was NULL or invalid, or the cbBufMax argument was less than or equal to 0.
ODBC_ERROR_COMPONENT_NOT_FOUND | Component not found in registry | The installer could not find the [ODBC Drivers] section in the registry.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Comments

Each driver description is terminated with a null byte, and the entire list is terminated with a null byte. (That is, two null bytes mark the end of the list.) If the allocated buffer is not large enough to hold the entire list, the list is truncated without error. An error is returned if a null pointer is passed in as lpszBuf.

Related Functions

For information about | See
Returning driver descriptions and attributes | SQLDrivers


SQLGetPrivateProfileString

Conformance

Version Introduced: ODBC 2.0

Summary

SQLGetPrivateProfileString gets a list of names of values or data corresponding to a value of the system information.

Syntax

int SQLGetPrivateProfileString(
     LPCSTR     lpszSection,
     LPCSTR     lpszEntry,
     LPCSTR     lpszDefault,
     LPCSTR     RetBuffer,
     INT     cbRetBuffer,
     LPCSTR     lpszFilename);

Arguments

lpszSection
[Input]
Points to a null-terminated string that specifies the section containing the key name. If this argument is NULL, the function copies all section names in the file to the supplied buffer.
lpszEntry
[Input]
Points to the null-terminated string containing the key name whose associated string is to be retrieved. If this argument is NULL, all key names in the section specified by the lpszSection argument are copied to the buffer specified by the RetBuffer argument.
lpszDefault
[Input]
Points to a null-terminated string that specifies the default value for the given key if the key cannot be found in the initialization file. This argument cannot be NULL.
RetBuffer
[Output]
Points to the buffer that receives the retrieved string.
cbRetBuffer
[Input]
Specifies the size, in characters, of the buffer pointed to by the RetBuffer argument.
lpszFilename
[Output]
Points to a null-terminated string that names the initialization file. If this argument does not contain a full path to the file, the default directory is searched.

Returns

SQLGetPrivateProfileString returns an integer value that indicates the number of characters read.

Diagnostics

When a call to SQLGetPrivateProfileString fails, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Comments

SQLGetPrivateProfileString is provided as a simple way to port drivers and driver setup DLLs from Microsoft® Windows® to Microsoft Windows NT®/Windows 2000. Calls to GetPrivateProfileString that retrieve a profile string from the Odbc.ini file should be replaced with calls to SQLGetPrivateProfileString. SQLGetPrivateProfileString calls functions in the Win32® API to retrieve the requested names of values or data corresponding to a value of the Odbc.ini subkey of the system information.

The configuration mode (as set by SQLSetConfigMode) indicates where the Odbc.ini entry listing DSN values is in the system information. If the DSN is a User DSN (the configuration mode is USERDSN_ONLY), the function reads from the Odbc.ini entry in HKEY_CURRENT_USER. If the DSN is a System DSN (SYSTEMDSN_ONLY), the function reads from the Odbc.ini entry in HKEY_LOCAL_MACHINE. If the configuration mode is BOTHDSN, HKEY_CURRENT_USER is tried, and if it fails, HKEY_LOCAL_MACHINE is used.

Related Functions

For information about | See
Writing a value to the system information | SQLWritePrivateProfileString


SQLGetTranslator

Conformance

Version Introduced: ODBC 2.0

Summary

SQLGetTranslator displays a dialog box from which a user can select a translator.

Syntax

BOOL SQLGetTranslator(
     HWND     hwndParent,
     LPSTR     lpszName,
     WORD     cbNameMax,
     WORD *     pcbNameOut,
     LPSTR     lpszPath,
     WORD     cbPathMax,
     WORD *     pcbPathOut,
     DWORD *     pvOption);

Arguments

hwndParent
[Input]
Parent window handle.
lpszName
[Input/Output]
Name of the translator from the system information.
cbNameMax
[Input]
Maximum length of the lpszName buffer.
pcbNameOut
[Input/Output]
Total number of bytes (excluding the null-termination byte) passed or returned in lpszName. If the number of bytes available to return is greater than or equal to cbNameMax, the translator name in lpszName is truncated to cbNameMax minus the null-termination character. The pcbNameOut argument can be a null pointer.
lpszPath
[Output]
Full path of the translation DLL.
cbPathMax
[Input]
Maximum length of the lpszPath buffer.
pcbPathOut
[Output]
Total number of bytes (excluding the null-termination byte) returned in lpszPath. If the number of bytes available to return is greater than or equal to cbPathMax, the translation DLL path in lpszPath is truncated to cbPathMax minus the null-termination character. The pcbPathOut argument can be a null pointer.
pvOption
[Output]
32-bit translation option.

Returns

The function returns TRUE if it is successful, FALSE if it fails or if the user cancels the dialog box.

Diagnostics

When SQLGetTranslator returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_INVALID_BUFF_LEN | Invalid buffer length | The cbNameMax or cbPathMax argument was less than or equal to 0.
ODBC_ERROR_INVALID_HWND | Invalid window handle | The hwndParent argument was invalid or NULL.
ODBC_ERROR_INVALID_NAME | Invalid driver or translator name | The lpszName argument was invalid. It could not be found in the registry.
ODBC_ERROR_LOAD_LIBRARY_FAILED | Could not load the driver or translator setup library | The translator library could not be loaded.
ODBC_ERROR_INVALID_OPTION | Invalid transaction option | The pvOption argument contained an invalid value.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Comments

If hwndParent is null or if lpszName, lpszPath, or pvOption is a null pointer, SQLGetTranslator returns FALSE. Otherwise, it displays the list of installed translators in the following dialog box.

If lpszName contains a valid translator name, it is selected. Otherwise, <No Translator> is selected.

If the user chooses <No Translator>, the contents of lpszName, lpszPath, and pvOption are not touched. SQLGetTranslator sets pcbNameOut and pcbPathOut to 0 and returns TRUE.

If the user chooses a translator, SQLGetTranslator calls ConfigTranslator in the translator's setup DLL. If ConfigTranslator returns FALSE, SQLGetTranslator returns to its dialog box. If ConfigTranslator returns TRUE, SQLGetTranslator returns TRUE, along with the selected translator name, path, and translation option.

Related Functions

For information about | See
Configuring a translator | ConfigTranslator
Getting a translation attribute | SQLGetConnectAttr
Setting a translation attribute | SQLSetConnectAttr


SQLInstallDriverEx

Conformance

Version Introduced: ODBC 3.0

Summary

SQLInstallDriverEx adds information about the driver to the Odbcinst.ini entry in the system information and increments the driver's UsageCount by 1. However, if a version of the driver already exists but the UsageCount value for the driver does not exist, the new UsageCount value is set to 2.

This function does not actually copy any files. It is the responsibility of the calling program to copy the driver's files to the target directory properly.

Syntax

BOOL SQLInstallDriverEx(
     LPCSTR     lpszDriver,
     LPCSTR     lpszPathIn,
     LPSTR     lpszPathOut,
     WORD     cbPathOutMax,
     WORD *     pcbPathOut,
     WORD     fRequest,
     LPDWORD     lpdwUsageCount);

Arguments

lpszDriver
[Input]
The driver description (usually the name of the associated DBMS) presented to users instead of the physical driver name. The lpszDriver argument must contain a list of keyword-value pairs describing the driver.

For more information, see "Comments."

lpszPathIn
[Input]
Full path of the target directory of the installation, or a null pointer. If lpszPathIn is a null pointer, the drivers will be installed in the system directory.
lpszPathOut
[Output]
Path of the target directory where the driver should be installed. If the driver has not previously been installed, lpszPathOut should be the same as lpszPathIn. If the driver was previously installed, lpszPathOut is the path of the previous installation.
cbPathOutMax
[Input]
Length of lpszPathOut.
pcbPathOut
[Output]
Total number of bytes (excluding the null-termination character) available to return in lpszPathOut. If the number of bytes available to return is greater than or equal to cbPathOutMax, the output path in lpszPathOut is truncated to cbPathOutMax minus the null-termination character. The pcbPathOut argument can be a null pointer.
fRequest
[Input]
Type of request. The fRequest argument must contain one of the following values:

ODBC_INSTALL_INQUIRY: Inquire about where a driver can be installed.

ODBC_INSTALL_COMPLETE: Complete the installation request.

lpdwUsageCount
[Output]
The usage count of the driver after this function has been called.

Returns

The function returns TRUE if it is successful, FALSE if it fails.

Diagnostics

When SQLInstallDriverEx returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_INVALID_BUFF_LEN | Invalid buffer length | The lpszPathOut argument was not large enough to contain the output path. The buffer contains the truncated path. The cbPathOutMax argument was 0, and fRequest was ODBC_INSTALL_COMPLETE.
ODBC_ERROR_INVALID_REQUEST_TYPE | Invalid type of request | The fRequest argument was not one of the following: ODBC_INSTALL_INQUIRY ODBC_INSTALL_COMPLETE
ODBC_ERROR_INVALID_KEYWORD_VALUE | Invalid keyword-value pairs | The lpszDriver argument contained a syntax error.
ODBC_ERROR_INVALID_PATH | Invalid install path | The lpszPathIn argument contained an invalid path.
ODBC_ERROR_LOAD_LIBRARY_FAILED | Could not load the driver or translator setup library | The driver setup library could not be loaded.
ODBC_ERROR_INVALID_PARAM_SEQUENCE | Invalid parameter sequence | The lpszDriver argument did not contain a list of keyword-value pairs.
ODBC_ERROR_USAGE_UPDATE_FAILED | Could not increment or decrement the component usage count | The installer failed to increment the driver's usage count.

Comments

The lpszDriver argument is a list of attributes in the form of keyword-value pairs. Each pair is terminated with a null byte, and the entire list is terminated with a null byte. (That is, two null bytes mark the end of the list.) The format of this list is as follows:

driver-desc\0Driver=driver-DLL-filename\0[Setup=setup-DLL-filename\0]
[driver-attr-keyword1=value1\0][driver-attr-keyword2=value2\0]...\0

where \0 is a null byte and driver-attr-keywordn is any driver attribute keyword described in "Driver Keyword Sections" in Chapter 18: Installing ODBC Components. The keywords must appear in the specified order. For example, suppose a driver for formatted text files has separate driver and setup DLLs and can use files with the .txt and .csv extensions. The lpszDriver argument for this driver might be as follows:

Text\0Driver=TEXT.DLL\0Setup=TXTSETUP.DLL\0FileUsage=1\0
FileExtns=*.txt,*.csv\0\0

Suppose that a driver for SQL Server does not have a separate setup DLL and does not have any driver attribute keywords. The lpszDriver argument for this driver might be as follows:

SQL Server\0Driver=SQLSRVR.DLL\0\0

After SQLInstallDriverEx retrieves information about the driver from the lpszDriver argument, it adds the driver description to the [ODBC Drivers] section of the Odbcinst.ini entry in the system information. It then creates a section titled with the driver's description and adds the full paths of the driver DLL and the setup DLL. Finally, it returns the path of the target directory of the installation but does not copy the driver files to it. The calling program must actually copy the driver files to the target directory.

SQLInstallDriverEx increments the component usage count for the installed driver by 1. If a version of the driver already exists but the component usage count for the driver does not exist, the new component usage count value is set to 2.

The application setup program is responsible for physically copying the driver file and maintaining the file usage count. If the driver file has not previously been installed, the application setup program must copy the file in the lpszPathIn path and create the file usage count. If the file has previously been installed, the setup program merely increments the file usage count and returns the path of the prior installation in the lpszPathOut argument.

Note For more information about component usage counts and file usage counts, see "Usage Counting" in Chapter 18: Installing ODBC Components.

If an older version of the driver file was previously installed by the application, the driver should be uninstalled and then reinstalled, so that the driver component usage count is valid. SQLConfigDriver (with an fRequest of ODBC_REMOVE_DRIVER) should first be called, and then SQLRemoveDriver should be called to decrement the component usage count. SQLInstallDriverEx should then be called to reinstall the driver, incrementing the component usage count. The application setup program must replace the old file with the new file. The file usage count will remain the same, and any other application that used the older version file will now use the newer version.

Note If the driver was previously installed and SQLInstallDriverEx is called to install the driver in a different directory, the function will return TRUE, but lpszPathOut will include the directory where the driver was already installed. It will not include the directory entered in the lpszDriver argument.

The length of the path in lpszPathOut in SQLInstallDriverEx allows for a two-phase install process, so an application can determine what cbPathOutMax should be by calling SQLInstallDriverEx with an fRequest of ODBC_INSTALL_INQUIRY mode. This will return the total number of bytes available in the pcbPathOut buffer. SQLInstallDriverEx can then be called with an fRequest of ODBC_INSTALL_COMPLETE and the cbPathOutMax argument set to the value in the pcbPathOut buffer, plus the null-termination character.

If you choose not to use the two-phase model for SQLInstallDriverEx, you must set cbPathOutMax, which defines the size of the storage for the path of the target directory, to the value _MAX_PATH, as defined in Stdlib.h, to prevent truncation.

When fRequest is ODBC_INSTALL_COMPLETE, SQLInstallDriverEx does not allow lpszPathOut to be NULL (or cbPathOutMax to be 0). If fRequest is ODBC_INSTALL_COMPLETE, FALSE is returned when the number of bytes available to return is greater than or equal to cbPathOutMax, with the result that truncation occurs.

After SQLInstallDriverEx has been called and the application setup program has copied the driver file (if necessary), the driver setup DLL must call SQLConfigDriver to set the configuration for the driver.

Related Functions

For information about | See
Installing the Driver Manager | SQLInstallDriverManager


SQLInstallDriverManager

Conformance

Version Introduced: ODBC 1.0

Summary

SQLInstallDriverManager returns the path of the target directory for the installation of the ODBC core components. The calling program must actually copy the Driver Manager's files to the target directory.

Syntax

BOOL SQLInstallDriverManager(
     LPSTR     lpszPath,
     WORD     cbPathMax,
     WORD *     pcbPathOut);

Arguments

lpszPath
[Output]
Path of the target directory of the installation.
cbPathMax
[Input]
Length of lpszPath. This must be at least _MAX_PATH bytes.
pcbPathOut
[Output]
Total number of bytes (excluding the null-termination byte) returned in lpszPath. If the number of bytes available to return is greater than or equal to cbPathMax, the path in lpszPath is truncated to cbPathMax minus the null-termination character. The pcbPathOut argument can be a null pointer.

Returns

The function returns TRUE if it is successful, FALSE if it fails.

Diagnostics

When SQLInstallDriverManager returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_INVALID_BUFF_LEN | Invalid buffer length | The lpszPath argument was not large enough to contain the output path. The buffer contains the truncated path. The cbPathMax argument was less than _MAX_PATH.
ODBC_ERROR_USAGE_UPDATE_FAILED | Could not increment or decrement the component usage count | The installer failed to increment the ODBC core component usage count.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Comments

SQLInstallDriverManager is called to return the path for ODBC core components and increment the component usage count in the system information. If a version of the Driver Manager already exists but the component usage count for the driver does not exist, the new component usage count value is set to 2.

The application setup program is responsible for physically copying the core component files and maintaining the file usage counts. If a core component file has not previously been installed, the application setup program must copy the file, and create the file usage count. If the file has previously been installed, the setup program merely increments the file usage count.

If an older version of the Driver Manager was previously installed by the application setup program, the core components should be uninstalled and then reinstalled, so that the core component usage count is valid. SQLRemoveDriverManager should first be called to decrement the component usage count. SQLInstallDriverManager should then be called to increment the component usage count. The application setup program must replace the old core component files with the new files. The file usage counts will remain the same, and other applications that used the older version core component files will now use the newer version files.

In a fresh install of the ODBC core components, drivers, and translators, the application setup program should call the following functions in sequence: SQLInstallDriverManager, SQLInstallDriverEx, SQLConfigDriver (with an fRequest of ODBC_INSTALL_DRIVER), and then SQLInstallTranslatorEx. In an uninstall of the core components, drivers, and translators, the application setup program should call the following functions in sequence: SQLRemoveTranslator, SQLRemoveDriver, and then SQLRemoveDriverManager. These functions must be called in this sequence. In an upgrade of all components, all the uninstall functions should be called in sequence and then all the install functions should be called in sequence.

Related Functions

For information about | See
Adding, modifying, or removing a driver | SQLConfigDriver
Installing a driver | SQLInstallDriverEx
Installing a translator | SQLInstallTranslatorEx
Removing a driver | SQLRemoveDriver
Removing the Driver Manager | SQLRemoveDriverManager
Removing a translator | SQLRemoveTranslator


SQLInstallerError

Conformance

Version Introduced: ODBC 3.0

Summary

SQLInstallerError returns error or status information for the ODBC installer functions.

Syntax

RETCODE SQLInstallerError(
     WORD     iError,
     DWORD *     pfErrorCode,
     LPSTR     lpszErrorMsg,
     WORD     cbErrorMsgMax,
     WORD *     pcbErrorMsg);

Arguments

iError
[Input]
Error record number. Valid numbers are from 1 through 8.
pfErrorCode
[Output]
Installer error code. (For more information, see "Comments.")
lpszErrorMsg
[Output]
Pointer to storage for the error message text.
cbErrorMsgMax
[Input]
Maximum length of the szErrorMsg buffer. This must be less than or equal to SQL_MAX_MESSAGE_LENGTH minus the null-termination character.
cbErrorMsgMax
[Input]
Maximum length of the szErrorMsg buffer. This must be less than or equal to SQL_MAX_MESSAGE_LENGTH minus the null-termination character.
pcbErrorMsg
[Output]
Pointer to the total number of bytes (excluding the null-termination character) available to return in lpszErrorMsg. If the number of bytes available to return is greater than or equal to cbErrorMsgMax, the error message text in lpszErrorMsg is truncated to cbErrorMsgMax minus the null-termination character bytes. The pcbErrorMsg argument can be a null pointer.

Returns

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA, or SQL_ERROR.

Diagnostics

SQLInstallerError does not post error values for itself. SQLInstallerError returns SQL_NO_DATA when it is unable to retrieve any error information (in which case pfErrorCode is undefined). If SQLInstallerError cannot access error values for any reason that would normally return SQL_ERROR, SQLInstallerError returns SQL_ERROR but does not post any error values. If either the lpszErrorMsg argument is NULL or the cbErrorMsgMax is less than or equal to 0, this function returns SQL_ERROR. If the buffer for the error message is too short, SQLInstallerError returns SQL_SUCCESS_WITH_INFO and returns the correct pfErrorCode value for SQLInstallerError.

To determine whether a truncation occurred in the error message, an application can compare the value in the cbErrorMsgMax argument to the actual length of the message text written to the pcbErrorMsg argument. If truncation does occur, the correct buffer length should be allocated for lpszErrorMsg and SQLInstallerError should be called again with the corresponding iError record.

Comments

An application calls SQLInstallerError when a previous call to the ODBC installer function returns FALSE. ODBC installer and driver or translator setup functions post zero or more errors only when the function fails (returns FALSE); therefore, an application calls SQLInstallerError only after an ODBC installer function fails.

The ODBC installer error queue is flushed each time a new installer function is called. Therefore, an application cannot expect to retrieve errors for functions other than from the last installer function call.

To retrieve multiple errors for a function call, an application calls SQLInstallerError multiple times.

When there is no additional information, SQLInstallerError returns SQL_NO_DATA, the pfErrorCode argument is undefined, the pcbErrorMsg argument equals 0, and the lpszErrorMsg argument contains a single null-termination character (unless the cbErrorMsgMax argument is equal to 0).


SQLInstallTranslator

Conformance

Version Introduced: ODBC 2.5, Deprecated

Summary

In ODBC 3.0, SQLInstallTranslator has been replaced by SQLInstallTranslatorEx. Calls to SQLInstallTranslator will be mapped to SQLInstallTranslatorEx. For more information, see SQLInstallTranslatorEx.

SQLInstallTranslator will return FALSE if an application calls it in the ODBC 3.x Driver Manager with the lpszInfFile argument set to a value other than NULL. The Odbc.inf file used in ODBC 2.x is no longer supported in ODBC 3.x, even for backward compatibility.


SQLInstallTranslatorEx

Conformance

Version Introduced: ODBC 3.0

Summary

SQLInstallTranslatorEx adds information about a translator to the Odbcinst.ini section of the system information.

Syntax

BOOL SQLInstallTranslatorEx(
     LPCSTR     lpszTranslator,
     LPCSTR     lpszPathIn,
     LPSTR     lpszPathOut,
     WORD     cbPathOutMax,
     WORD *     pcbPathOut,
     WORD     fRequest,
     LPDWORD     lpdwUsageCount);

Arguments

lpszTranslator
[Input]
This must contain a list of keyword-value pairs describing the translator. The Translator and Setup keywords must be included in the lpszTranslator string. The translation DLL is listed with the Translator keyword, and the translator setup DLL is listed with the Setup keyword. Each pair is terminated with a NULL byte, and the entire list is terminated with a NULL byte. (That is, two NULL bytes mark the end of the list.) The format of lpszTranslator is as follows:
translator-desc
\0Translator=translator-DLL-filename\0[Setup=setup-DLL-filename\0]\0
lpszPathIn
[Input]
Full path of where the translator is to be installed or a null pointer. If lpszPath is a null pointer, the translators will be installed in the System directory.
lpszPathOut
[Output]
The path of the target directory where the translator should be installed. If the translator has never been installed, lpszPathOut is the same as lpszPathIn. If there exists a prior installation of the translator, lpszPathOut is the path of the prior installation.
cbPathOutMax
[Input]
Length of lpszPathOut.
pcbPathOut
[Output]
Total number of bytes available to return in lpszPathOut. If the number of bytes available to return is greater than or equal to cbPathOutMax, the output path in lpszPathOut is truncated to pcbPathOutMax minus the null-termination character. The pcbPathOut argument can be a null pointer.
fRequest
[Input]
Type of request. fRequest must contain one of the following values:

ODBC_INSTALL_INQUIRY: Inquire about where a translator can be installed.

ODBC_INSTALL_COMPLETE: Complete the installation request.

lpdwUsageCount
[Output]
The usage count of the translator after this function has been called.

Returns

The function returns TRUE if it is successful, FALSE if it fails.

Diagnostics

When SQLInstallTranslatorEx returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_INVALID_BUFF_LEN | Invalid buffer length | The lpszPathOut argument was not large enough to contain the output path. The buffer contains the truncated path. The cbPathOutMax argument was 0, and the fRequest argument was ODBC_INSTALL_COMPLETE.
ODBC_ERROR_INVALID_REQUEST_TYPE | Invalid type of request | The fRequest argument was not one of the following: ODBC_INSTALL_INQUIRY ODBC_INSTALL_COMPLETE
ODBC_ERROR_INVALID_KEYWORD_VALUE | Invalid keyword-value pairs | The lpszTranslator argument contained a syntax error.
ODBC_ERROR_INVALID_PATH | Invalid install path | The lpszPathIn argument contained an invalid path.
ODBC_ERROR_INVALID_PARAM_SEQUENCE | Invalid parameter sequence | The lpszTranslator argument did not contain a list of keyword-value pairs.
ODBC_ERROR_USAGE_UPDATE_FAILED | Could not increment or decrement the registry's component usage count | The installer failed to increment the translator's usage count.

Comments

SQLInstallTranslatorEx provides a mechanism to install just the translator. This function does not actually copy any files. The calling program is responsible for copying the translator files.

SQLInstallTranslatorEx increments the component usage count for the installed translator by 1. If a version of the translator already exists but the component usage count for the translator does not exist, the new component usage count value is set to 2.

The application setup program is responsible for physically copying the translator file and maintaining the file usage count. If the translator file has not previously been installed, the application setup program must copy the file or files and create the file or files usage count. If the file has previously been installed, the setup program simply increments the file usage count.

If an older version of the translator was previously installed by the application, the translator should be uninstalled and then reinstalled, so that the translator component usage count is valid. SQLRemoveTranslator should be called to decrement the component usage count, and then SQLInstallTranslatorEx should be called to increment the component usage count. The application setup program must replace the old file or files with the new file. The file usage count will remain the same, and other applications that used the older version file will now use the newer version.

The length of the path in lpszPathOut in SQLInstallTranslatorEx allows for a two-phase install process, so an application can determine what cbPathOutMax should be by calling SQLInstallTranslatorEx with an fRequest of ODBC_INSTALL_INQUIRY mode. This will return the total number of bytes available in the pcbPathOut buffer. SQLInstallTranslatorEx can then be called with an fRequest of ODBC_INSTALL_COMPLETE and the cbPathOutMax argument set to the value in the pcbPathOut buffer, plus the null-termination character.

If you choose not to use the two-phase model for SQLInstallTranslatorEx, you must set cbPathOutMax, which defines the size of the storage for the path of the target directory, to the value _MAX_PATH, as defined in Stdlib.h, to prevent truncation.

When fRequest is ODBC_INSTALL_COMPLETE, SQLInstallTranslatorEx does not allow lpszPathOut to be NULL (or cbPathOutMax to be 0). If fRequest is ODBC_INSTALL_COMPLETE, FALSE is returned when the number of bytes available to return is greater than or equal to cbPathOutMax, with the result that truncation occurs.

Related Functions

For information about | See
Returning a default translation option | ConfigTranslator
Selecting translators | SQLGetTranslator
Removing translators | SQLRemoveTranslator


SQLManageDataSources

Conformance

Version Introduced: ODBC 2.0

Summary

SQLManageDataSources displays a dialog box with which users can set up, add, and delete data sources in the system information.

Syntax

BOOL SQLManageDataSources(
     HWND     hwnd);

Arguments

hwnd
[Input]
Parent window handle.

Returns

SQLManageDataSources returns FALSE if hwnd is not a valid window handle. Otherwise, it returns TRUE.

Diagnostics

When SQLManageDataSources returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_REQUEST_FAILED | Request failed | The call to ConfigDSN failed.
ODBC_ERROR_INVALID__HWND | Invalid window handle | The hwnd argument was invalid or NULL.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Managing Data Sources

SQLManageDataSources initially displays the ODBC Data Source Administrator dialog box, as shown in the following illustration.

The dialog box displays the data sources listed in the system information under three tabs: User DSN, System DSN, and File DSN. If the user double-clicks a data source or selects a data source and clicks Configure, SQLManageDataSources calls ConfigDSN in the setup DLL with the ODBC_CONFIG_DSN option.

If the user clicks Add, SQLManageDataSources displays the Create New Data Source dialog box, shown in the following illustration.

The dialog box displays a list of installed drivers. If the user double-clicks a driver or selects a driver and clicks OK, SQLManageDataSources calls ConfigDSN in the setup DLL and passes it the ODBC_ADD_DSN option.

If the user selects a data source and clicks Remove, SQLManageDataSources asks whether the user wants to delete the data source. If the user clicks Yes, SQLManageDataSources calls ConfigDSN in the setup DLL with the ODBC_REMOVE_DSN option.

The Create New Data Source dialog box is used to add or delete a user data source, a system data source, or a file data source.

User DSNs

DSNs created for individual users will be called User DSNs, to distinguish them from System DSNs. User DSNs are registered as follows in the system information:

HKEY_CURRENT_USER
SOFTWARE
ODBC
Odbc.ini

System DSNs

The Create New Data Source dialog box allows you to add a system data source to your local computer or delete one, or to set the configuration for a system data source.

A data source set up with a system data source name (DSN) can be used by more than one user on the same machine. It can also be used by a systemwide service, which can then gain access to the data source even if no user is logged on to the machine.

A System DSN is registered in the HKEY_LOCAL_MACHINE entry in the system information rather than in the HKEY_CURRENT_USER entry. It is not tied to one user who logs on with his or her particular user name and password but can be used by any user of that machine or by an automatic systemwide service. The System DSN is, however, tied to one machine. It does not support the capability of using remote DSNs between machines. System DSNs are registered as follows in the system information:

HKEY_LOCAL_MACHINE
SOFTWARE
ODBC
Odbc.ini

File DSNs

A file data source does not have a data source name, as does a machine data source, and is not registered to any one user or machine. The connection information for that data source is contained in a .dsn file that can be copied to any machine. A file data source can be shareable, in which case the .dsn file resides on a network and can be used simultaneously by multiple users on the network as long as the user has the appropriate driver installed. A file data source can also be unshareable, in which case it can be used only on a single machine.

For more information on file data sources, see "Connecting Using File Data Sources" in Chapter 6: Connecting to a Data Source or Driver, or see SQLDriverConnect.

Managing Drivers

If the user clicks the Drivers tab in the ODBC Data Source Administrator dialog box, SQLManageDataSources displays a list of ODBC drivers installed on the system, as well as information about the drivers. The date displayed is the creation date of the driver, as shown in the following illustration.

Tracing Options

If the user clicks the Tracing tab in the ODBC Data Source Administrator dialog box, SQLManageDataSources displays tracing options, as shown in the following illustration.

If the user clicks Start Tracing Now and then clicks OK, SQLManageDataSources enables tracing manually for all applications currently running on the machine.

If the user specifies the name of a trace file in the Log file Path text box and then clicks OK, SQLManageDataSources sets the TraceFile keyword in the [ODBC] section of the system information to the specified name.

If the user clicks Start Visual Studio Analyzer and then clicks OK, Visual Studio Analyzer is enabled. It remains enabled until Stop Visual Studio Analyzer is clicked.

For more information on tracing, see "Tracing" in Chapter 17: Programming Considerations. For more information about the Trace, TraceAutoStop, and TraceFile keywords, see "ODBC Subkey" in Chapter 19: Configuring Data Sources.

Related Functions

For information about | See
Creating data sources | SQLCreateDataSource


SQLPostInstallerError

Conformance

Version Introduced: ODBC 3.0

Summary

SQLPostInstallerError provides a mechanism for a driver or translator setup library to report errors for the ConfigDriver, ConfigDSN, and ConfigTranslator functions to the installer error queue. Applications do not use this API; they use SQLInstallerError to retrieve the error.

Syntax

RETCODE SQLPostInstallerError(
     DWORD     fErrorCode,
     LPSTR     szErrorMsg);

Arguments

fErrorCode
[Input]
Installer error code.
szErrorMsg
[Input]
Error message text.

Returns

SQL_SUCCESS or SQL_ERROR.

Diagnostics

SQLPostInstallerError does not post error values for itself. If the error was successfully posted to the installer error queue (retrievable using SQLInstallerError), SQL_SUCCESS is returned. SQL_ERROR will be returned if the value in the dwErrorCode argument is not one of the specified installer error codes.

Related Functions

For information about | See
Adding, modifying, or removing a driver | ConfigDriver
Adding, modifying, or removing data sources | ConfigDSN
Setting a translation option | ConfigTranslator


SQLReadFileDSN

Conformance

Version Introduced: ODBC 3.0

Summary

SQLReadFileDSN reads information from a File DSN.

Syntax

BOOL SQLReadFileDSN(
     LPCSTR     lpszFileName,
     LPCSTR     lpszAppName,
     LPCSTR     lpszKeyName,
     LPSTR     lpszString,
     WORD     cbString,
     WORD *     pcbString);

Arguments

lpszFileName
[Input]
Pointer to the data buffer containing the name of the .dsn file. A .dsn extension is appended to all file names that do not already have a .dsn extension. The value in *lpszFileName must be a null-terminated string.
lpszAppName
[Input]
Pointer to the data buffer containing the name of the application. This is "ODBC" for the ODBC section. The value in *lpszAppName must be a null-terminated string.
lpszKeyName
[Input]
Pointer to the data buffer containing the name of the key to be read. See "Comments" for reserved keywords. The value in *lpszAppName must be a null-terminated string.
lpszString
[Output]
Pointer to the data buffer containing the string associated with the key to be read.

If *lpszFileName is a valid .dsn file name but the lpszAppName argument is a null pointer and the lpszKeyName argument is a null pointer, then *lpszString contains a list of valid applications. If *lpszFileName is a valid .dsn file name and *lpszAppName is a valid application name, but the lpszKeyName argument is a null pointer, then *lpszString contains a list of valid reserved keywords in the appropriate section of the DSN file, delimited by semicolons. If *lpszFileName is a valid .dsn file name but *lpszAppName is a null pointer and the lpszKeyName argument is a null pointer, then *lpszString contains a list of the sections in the DSN file, delimited by semicolons.

cbString
[Input]
Length of the *lpszString buffer.
pcbString
[Output]
Total number of bytes available to return in *lpszString. If the number of bytes available to return is greater than or equal to cbString, the output string in *lpszString is truncated to cbString minus the null-termination character. The pcbString argument can be a null pointer.

Returns

The function returns TRUE if it is successful, FALSE if it fails.

Diagnostics

When SQLReadFileDSN returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_INVALID_BUFF_LEN | Invalid buffer length | The lpszString argument was NULL. The cbString argument was less than or equal to 0.
ODBC_ERROR_INVALID_PATH | Invalid install path | The path of the file name specified in the lpszFileName argument was invalid.
ODBC_ERROR_INVALID_REQUEST_TYPE | Invalid type of request | The lpszAppName argument was NULL, while the lpszKeyName argument was valid.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.
ODBC_ERROR_OUTPUT_STRING_TRUNCATED | Output string truncated | The string returned in *lpszString was truncated because the value in cbString was less than or equal to the value in *pcbString.
ODBC_ERROR_REQUEST_FAILED | Request failed | The keyword did not exist in the file DSN.

Comments

ODBC reserves the section name [ODBC] in which to store the connection information. The reserved keywords for this section are the same as those reserved for a connect string in SQLDriverConnect. (For more information, see the SQLDriverConnect function description.)

Applications can use these reserved keywords to read the information in a File DSN. If an applications wants to find out the DSN-less connection string associated with a File DSN, it can call SQLReadFileDSN for any of the reserved connection string keywords in the [ODBC] section. The full connection string passed in a DSN-less connection is a combination of all of the keywords (reserved and driver-specific) in the [ODBC] section.

Related Functions

For information about | See
Writing information to a File DSN | SQLWriteFileDSN


SQLRemoveDefaultDataSource

Conformance

Version Introduced: ODBC 1.0, Deprecated

Summary

In ODBC 3.0, the SQLRemoveDefaultDataSource function has been replaced by a call to SQLConfigDataSource with an fRequest argument of ODBC_REMOVE_DEFAULT_DSN. If an ODBC 2.x installation program calls this function, the ODBC installer will map it to the following SQLConfigDataSource call:

SQLConfigDataSource (NULL, ODBC_REMOVE_DEFAULT_DSN, NULL, NULL)


SQLRemoveDriver

Conformance

Version Introduced: ODBC 3.0

Summary

SQLRemoveDriver changes or removes information about the driver from the Odbcinst.ini entry in the system information.

Syntax

BOOL SQLRemoveDriver(
     LPCSTR     lpszDriver,
     BOOL     fRemoveDSN,
     LPDWORD     lpdwUsageCount);

Arguments

lpszDriver
[Input]
The name of the driver as registered in the Odbcinst.ini key of the system information.
fRemoveDSN
[Input]
The valid values are:

TRUE: Remove DSNs associated with the driver specified in lpszDriver.
FALSE: Do not remove DSNs associated with the driver specified in lpszDriver.

lpdwUsageCount
[Output]
The usage count of the driver after this function has been called.

Returns

The function returns TRUE if it is successful, FALSE if it fails. If no entry exists in the system information when this function is called, the function returns FALSE.

Diagnostics

When SQLRemoveDriver returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_COMPONENT_NOT_FOUND | Component not found in registry | The installer could not remove the driver information because it either did not exist in the registry or could not be found in the registry.
ODBC_ERROR_INVALID_NAME | Invalid driver or translator name | The lpszDriver argument was invalid.
ODBC_ERROR_USAGE_UPDATE_FAILED | Could not increment or decrement the component usage count | The installer failed to decrement the usage count of the driver.
ODBC_ERROR_REQUEST_FAILED | Request failed | The fRemoveDSN argument was TRUE; however, one or more DSNs could not be removed. The call to SQLConfigDriver with the ODBC_REMOVE_DRIVER request failed.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Comments

SQLRemoveDriver complements the SQLInstallDriverEx function and updates the component usage count in the system information. This function should be called only from a setup application.

SQLRemoveDriver will decrement the component usage count value by 1. If the component usage count goes to 0, the following will occur:

  1. The SQLConfigDriver function with the ODBC_REMOVE_DRIVER option will be called. If the fRemoveDSN option is set to TRUE, the ConfigDSN function calls SQLRemoveDSNFromIni to remove all the data sources associated with the driver specified in lpszDriver. If the fRemoveDSN option is set to FALSE, the data sources will not be deleted.
  2. The driver entry in the system information will be removed. The driver entry is in the following system information location, under the driver name:

    HKEY_LOCAL_MACHINE
       SOFTWARE
          ODBC
             Odbcinst.ini

SQLRemoveDriver does not actually remove any files. The calling program is responsible for deleting files and maintaining the file usage count. Only after both the component usage count and the file usage count have reached zero is a file physically deleted. Some files in a component can be deleted, and others not deleted, depending on whether the files are used by other applications that have incremented the file usage count.

SQLRemoveDriver is also called as part of an upgrade process. If an application detects that it has to perform an upgrade and it has previously installed the driver, the driver should be removed and then reinstalled. SQLRemoveDriver should first be called to decrement the component usage count, and then SQLInstallDriverEx should be called to increment the component usage count. The application setup program must replace the old files with the new files. The file usage count will remain the same, and other applications that use the older version files will now use the newer version.

Related Functions

For information about | See
Adding, modifying, or removing a driver | ConfigDriver (in the Setup DLL)
Adding, modifying, or removing a driver | SQLConfigDriver
Installing a driver | SQLInstallDriverEx


SQLRemoveDriverManager

Conformance

Version Introduced: ODBC 3.0

Summary

SQLRemoveDriverManager changes or removes information about the ODBC core components from the Odbcinst.ini entry in the system information.

Syntax

BOOL SQLRemoveDriverManager(
     LPDWORD     pdwUsageCount);

Arguments

pdwUsageCount
[Output]
The usage count of the Driver Manager after this function has been called.

Returns

The function returns TRUE if it is successful, FALSE if it fails. If no entry exists in the system information when this function is called, the function returns FALSE.

Diagnostics

When SQLRemoveDriverManager returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_COMPONENT_NOT_FOUND | Component not found in registry | The installer could not remove the Driver Manager information because it either did not exist in the registry or could not be found in the registry.
ODBC_ERROR_USAGE_UPDATE_FAILED | Could not increment or decrement the component usage count | The installer failed to decrement the usage count of the Driver Manager.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Comments

SQLRemoveDriverManager complements the SQLInstallDriverManager function, and updates the component usage count in the system information. This function should be called only from a setup application.

SQLRemoveDriverManager will decrement the core component usage count by 1. If the component usage count goes to 0, the entry system information will be removed. The core component entry is in the following location in the system information, under the title "ODBC Core":

HKEY_LOCAL_MACHINE
   SOFTWARE
      ODBC
         Odbcinst.ini

Caution An application should not physically remove Driver Manager files when the component usage count and the file usage count reach zero.

SQLRemoveDriverManager does not actually remove any files. The calling program is responsible for deleting files and maintaining the file usage counts. Driver Manager files should not, however, be removed when both the component usage count and the file usage count have reached zero, because these files may be used by other applications that have not incremented the file usage count.

SQLRemoveDriverManager is called as part of the Uninstall process. ODBC core components (which include the Driver Manager, Cursor Library, Installer, Language Library, Administrator, thunking files, and so on) are uninstalled as a whole. The following files are not removed when SQLRemoveDriverManager is called as part of the Uninstall process:

ODBC32DLL | ODBCCP32.DLL
ODBCCR32.DLL | ODBC16GT.DLL
ODBCCU32.DLL | ODBC32GT.DLL
ODBCINT.DLL | DS16GT.DLL
ODBCTRAC.DLL | DS32GT.DLL
MSVCRT40.DLL | ODBCAD32.EXE
ODBCCP32.CPL |

SQLRemoveDriverManager is also called as part of an upgrade process. If an application detects that it has to perform an upgrade and it has previously installed the driver, the driver should be removed and then reinstalled.

SQLRemoveDriverManager should first be called to decrement the component usage count. SQLInstallDriverEx should then be called to increment the component usage count. The application setup program must replace the old core component files with the new files. The file usage counts will remain the same, and other applications that use the older version core component files will now use the newer version files.

Related Functions

For information about | See
Installing a Driver Manager | SQLInstallDriverManager


SQLRemoveDSNFromIni

Conformance

Version Introduced: ODBC 1.0

Summary

SQLRemoveDSNFromIni removes a data source from the system information.

Syntax

BOOL SQLRemoveDSNFromIni(
     LPCSTR     lpszDSN);

Arguments

lpszDSN
[Input]
Name of the data source to remove.

Returns

The function returns TRUE if it removes the data source or the data source was not in the Odbc.ini file. It returns FALSE if it fails to remove the data source.

Diagnostics

When SQLRemoveDSNFromIni returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_INVALID_DSN | Invalid DSN | The lpszDSN argument was invalid.
ODBC_ERROR_REQUEST_FAILED | Request failed | The installer could not remove the DSN info from the registry.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Comments

SQLRemoveDSNFromIni removes the data source name from the [ODBC Data Sources] section of the system information. It also removes the data source specification section from the system information.

This function should be called only from a driver setup library.

Related Functions

For information about | See
Adding, modifying, or removing a data source | ConfigDSN
Adding, modifying, or removing a data source | SQLConfigDataSource
Removing the default data source | SQLRemoveDefaultDataSource
Adding a data source name to the system information | SQLWriteDSNToIni


SQLRemoveTranslator

Conformance

Version Introduced: ODBC 3.0

Summary

SQLRemoveTranslator removes information about a translator from the Odbcinst.ini section of the system information and decrements the translator's component usage count by 1.

Syntax

BOOL SQLRemoveTranslator(
     LPCSTR     lpszTranslator,
     LPDWORD     lpdwUsageCount);

Arguments

lpszTranslator
[Input]
The name of the translator as registered in the Odbcinst.ini key of the system information.
lpdwUsageCount
[Output]
The usage count of the translator after this function has been called.

Returns

The function returns TRUE if it is successful, FALSE if it fails. If no entry exists in the system information when this function is called, the function returns FALSE.

Diagnostics

When SQLRemoveTranslator returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_COMPONENT_NOT_FOUND | Component not found in registry | The installer could not remove the translator information because it either did not exist in the registry or could not be found in the registry.
ODBC_ERROR_INVALID_NAME | Invalid driver or translator name | The lpszTranslator argument was invalid.
ODBC_ERROR_USAGE_UPDATE_FAILED | Could not increment or decrement the component usage count | The installer failed to decrement the usage count of the driver.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Comments

SQLRemoveTranslator complements the SQLInstallTranslatorEx function and updates the component usage count in the system information. This function should be called only from a setup application.

SQLRemoveTranslator will decrement the component usage count by 1. If the component usage count goes to 0, the translator entry in the system information will be removed. The translator entry is in the following location in the system information, under the translator name:

HKEY_LOCAL_MACHINE
   SOFTWARE
      ODBC
         Odbcinst.ini

SQLRemoveTranslator does not actually remove any files. The calling program is responsible for deleting files, and maintaining the file usage count. Only after both the component usage count and the file usage count have reached zero is a file physically deleted. Some files in a component can be deleted, and others not deleted, depending on whether the files are used by other applications that have incremented the file usage count.

SQLRemoveTranslator is also called as part of an upgrade process. If an application detects that it has to perform an upgrade and it has previously installed the driver, the driver should be removed and then reinstalled. SQLRemoveTranslator should first be called to decrement the component usage count, and then SQLInstallTranslatorEx should be called to increment the component usage count. The application setup program must physically replace the old files with the new files. The file usage count will remain the same, and other applications that use the older version files will now use the newer version.

Related Functions

For information about | See
Installing a translator | SQLInstallTranslatorEx


SQLSetConfigMode

Conformance

Version Introduced: ODBC 3.0

Summary

SQLSetConfigMode sets the configuration mode that indicates where the Odbc.ini entry listing DSN values is in the system information.

Syntax

BOOL SQLSetConfigMode(
     UWORD     wConfigMode);

Arguments

wConfigMode
[Input]
The installer configuration mode (see "Comments"). The value in wConfigMode can be:

ODBC_USER_DSN
ODBC_SYSTEM_DSN
ODBC_BOTH_DSN

Returns

The function returns TRUE if it is successful, FALSE if it fails.

Diagnostics

When SQLSetConfigMode returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_INVALID_PARAM_SEQUENCE | Invalid parameter sequence | The wConfigMode argument did not contain ODBC_USER_DSN, ODBC_SYSTEM_DSN, or ODBC_BOTH_DSN.

Comments

This function is used to set where the Odbc.ini entry listing DSN values is in the system information. If wConfigMode is ODBC_USER_DSN, the DSN is a User DSN and the function reads from the Odbc.ini entry in HKEY_CURRENT_USER. If it is ODBC_SYSTEM_DSN, the DSN is a System DSN and the function reads from the Odbc.ini entry in HKEY_LOCAL_MACHINE. If it is ODBC_BOTH_DSN, HKEY_CURRENT_USER is tried, and if it fails, then HKEY_LOCAL_MACHINE is used.

This function does not affect SQLCreateDataSource and SQLDriverConnect. The configuration mode has to be set when a driver reads from the registry by calling SQLGetPrivateProfileString or writes to the registry by calling SQLWritePrivateProfileString. Calls to SQLGetPrivateProfileString and SQLWritePrivateProfileString use the configuration mode to know which part of the registry to operate on.

Caution SQLSetConfigMode should be called only when necessary; if the mode is improperly set, the ODBC Installer may fail to function properly.

SQLSetConfigMode makes a direct registry modification of the configuration mode. This is apart from the process of modifying the configuration mode by a call to SQLConfigDataSource. A call to SQLConfigDataSource sets the configuration mode to distinguish user and System DSNs when modifying a DSN. Prior to returning, SQLConfigDataSource resets the configuration mode to BOTHDSN.

Related Functions

For information about | See
Creating a data source | SQLCreateDataSource
Connecting to a data source using a connection string or dialog box | SQLDriverConnect
Retrieving the configuration mode | SQLGetConfigMode


SQLValidDSN

Conformance

Version Introduced: ODBC 2.0

Summary

SQLValidDSN checks the length and validity of the data source name before the name is added to the system information.

Syntax

BOOL SQLValidDSN(
     LPCSTR     lpszDSN);

Arguments

lpszDSN
[Input]
Data source name to be checked.

Returns

The function returns TRUE if the data source name is valid. It returns FALSE if the data source name is invalid or the function call failed.

Diagnostics

When SQLValidDSN returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. A *pfErrorCode is returned only if the function call failed, not if FALSE was returned because the data source name is invalid. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Comments

SQLValidDSN is called by a driver's ConfigDSN to check the length of the data source name and the validity of the individual characters in the data source name. It checks whether the length of the name is greater than SQL_MAX_DSN_LENGTH, as defined in Sqlext.h. (The length of the data source name is also checked by SQLWriteDSNToIni.) SQLValidDSN checks whether any of the following invalid characters are included in the data source name:

[ ] { } ( ) , ; ? * = ! @ \

Related Functions

For information about | See
Adding, modifying, or removing a data source | ConfigDSN (in the Setup DLL)
Adding, modifying, or removing a data source | SQLConfigDataSource
Writing a data source name to the system information | SQLWriteDSNToIni


SQLWriteDSNToIni

Conformance

Version Introduced: ODBC 1.0

Summary

SQLWriteDSNToIni adds a data source to the system information.

Syntax

BOOL SQLWriteDSNToIni(
     LPCSTR     lpszDSN,
     LPCSTR     lpszDriver);

Arguments

lpszDSN
[Input]
Name of the data source to add.
lpszDriver
[Input]
Driver description (usually the name of the associated DBMS) presented to users instead of the physical driver name.

Returns

The function returns TRUE if it is successful, FALSE if it fails.

Diagnostics

When SQLWriteDSNToIni returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_INVALID_DSN | Invalid DSN | The lpszDSN argument contained a string that was invalid for a DSN.
ODBC_ERROR_INVALID_NAME | Invalid driver or translator name | The lpszDriver argument was invalid.
ODBC_ERROR_REQUEST_FAILED | Request failed | The installer failed to create a DSN in the registry.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Comments

SQLWriteDSNToIni adds the data source to the [ODBC Data Sources] section of the system information. It then creates a specification section for the data source and adds a single keyword (Driver) with the name of the driver DLL as its value. If the data source specification section already exists, SQLWriteDSNToIni removes the old section before creating the new one.

The caller of this function must add any driver-specific keywords and values to the data source specification section of the system information.

If the name of the data source is Default, SQLWriteDSNToIni also creates the default driver specification section in the system information.

This function should be called only from a setup DLL.

Related Functions

For information about | See
Adding, modifying, or removing a data source | ConfigDSN (in the Setup DLL)
Adding, modifying, or removing a data source | SQLConfigDataSource
Removing a data source name from the system information | SQLRemoveDSNFromIni


SQLWriteFileDSN

Conformance

Version Introduced: ODBC 3.0

Summary

SQLWriteFileDSN writes information to a File DSN.

Syntax

BOOL SQLWriteFileDSN(
     LPCSTR     lpszFileName,
     LPCSTR     lpszAppName,
     LPCSTR     lpszKeyName,
     LPCSTR     lpszString);

Arguments

lpszFileName
[Input]
Pointer to the name of the File DSN. A DSN extension is appended to all file names that do not already have a DSN extension.
lpszAppName
[Input]
Pointer to the name of the application. This is "ODBC" for the ODBC section.
lpszKeyName
[Input]
Pointer to the name of the key to be read. See "Comments" for reserved keywords.
lpszString
[Output]
Pointed to the string associated with the key to be written. The maximum length of the string pointed to by this argument is 32,767 bytes.

Returns

The function returns TRUE if it is successful, FALSE if it fails.

Diagnostics

When SQLWriteFileDSN returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_INVALID_PATH | Invalid install path | The path of the file name specified in the lpszFileName argument was invalid.
ODBC_ERROR_INVALID_REQUEST_TYPE | Invalid type of request | The lpszAppName, lpszKeyName, or lpszString argument was NULL.

Comments

ODBC reserves the section name [ODBC] in which to store the connection information. The reserved keywords for this section are the same as those reserved for a connect string in SQLDriverConnect. (For more information, see the SQLDriverConnect function description.)

Applications can use these reserved keywords to write information directly to a File DSN. If an application wants to create or modify the DSN-less connection string associated with a File DSN, it can call SQLWriteFileDSN for any of the reserved connection string keywords in the [ODBC] section.

If the lpszString argument is a null pointer, the keyword pointed to by the lpszKeyName argument will be deleted from the .dsn file. If the lpszString and lpszKeyName arguments are both null pointers, the section pointed to by the lpszAppName argument will be deleted from the .dsn file.

Related Functions

For information about | See
Reading information from File DSNs | SQLReadFileDSN


SQLWritePrivateProfileString

Conformance

Version Introduced: ODBC 2.0

Summary

SQLWritePrivateProfileString writes a value name and data to the Odbc.ini subkey of the system information.

Syntax

BOOL SQLWritePrivateProfileString(
     LPCSTR     lpszSection,
     LPCSTR     lpszEntry,
     LPCSTR     lpszString,
     LPCSTR     lpszFilename);

Arguments

lpszSection
[Input]
Points to a null-terminated string containing the name of the section to which the string will be copied. If the section does not exist, it is created. The name of the section is case-independent; the string can be any combination of uppercase and lowercase letters.
lpszEntry
[Input]
Points to a null-terminated string containing the name of the key to be associated with a string. If the key does not exist in the specified section, it is created. If this argument is NULL, the entire section, including all entries within the section, is deleted.
lpszString
[Input]
Points to a null-terminated string to be written to the file. If this argument is NULL, the key pointed to by the lpszEntry argument is deleted.
lpszFilename
[Output]
Points to a null-terminated string that names the initialization file.

Returns

The function returns TRUE if it is successful, FALSE if it fails.

Diagnostics

When SQLWritePrivateProfileString returns FALSE, an associated *pfErrorCode value can be obtained by calling SQLInstallerError. The following table lists the *pfErrorCode values that can be returned by SQLInstallerError and explains each one in the context of this function.

*pfErrorCode | Error | Description
ODBC_ERROR_GENERAL_ERR | General installer error | An error occurred for which there was no specific installer error.
ODBC_ERROR_REQUEST_FAILED | Request failed | The requested system information could not be written.
ODBC_ERROR_OUT_OF_MEM | Out of memory | The installer could not perform the function because of a lack of memory.

Comments

SQLWritePrivateProfileString is provided as a simple way to port drivers and driver setup DLLs from Microsoft® Windows® to Microsoft Windows NT®/Windows 2000. Calls to WritePrivateProfileString that write a profile string to the Odbc.ini file should be replaced with calls to SQLWritePrivateProfileString. SQLWritePrivateProfileString calls functions in the Win32® API to add the specified value name and data to the Odbc.ini subkey of the system information.

A configuration mode indicates where the Odbc.ini entry listing DSN values is in the system information. If the DSN is a User DSN (the state variable is USERDSN_ONLY), the function writes to the Odbc.ini entry in HKEY_CURRENT_USER. If the DSN is a System DSN (SYSTEMDSN_ONLY), the function writes to the Odbc.ini entry in HKEY_LOCAL_MACHINE. If the state variable is BOTHDSN, HKEY_CURRENT_USER is tried, and if it fails, HKEY_LOCAL_MACHINE is used.

Related Functions

For information about | See
Getting a value from the system information | SQLGetPrivateProfileString


Chapter 24: Translation DLL Function Reference

This chapter describes the syntax of the translation DLL API, which consists of two functions: SQLDriverToDataSource and SQLDataSourceToDriver. These functions must be included in the DLL that performs translation for the driver.


SQLDataSourceToDriver

SQLDataSourceToDriver supports translations for ODBC drivers. This function is not called by ODBC-enabled applications; applications request translation through SQLSetConnectAttr. The driver associated with the ConnectionHandle specified in SQLSetConnectAttr calls the specified DLL to perform translations of all data flowing from the data source to the driver. A default translation DLL can be specified in the ODBC initialization file.

Syntax

BOOL SQLDataSourceToDriver(
     UDWORD      fOption,
     SWORD      fSqlType,
     PTR      rgbValueIn,
     SDWORD      cbValueIn,
     PTR      rgbValueOut,
     SDWORD      cbValueOutMax,
     SDWORD *      pcbValueOut,
     UCHAR *      szErrorMsg,
     SWORD     cbErrorMsgMax,
     SWORD *      pcbErrorMsg);

Arguments

fOption
[Input]
Option value.
fSqlType
[Input]
The SQL data type. This argument tells the driver how to convert rgbValueIn into a form acceptable by the application. For a list of valid SQL data types, see the "SQL Data Types" section in Appendix D: Data Types.
rgbValueIn
[Input]
Value to translate.
cbValueIn
[Input]
Length of rgbValueIn.
rgbValueOut
[Output]
Result of the translation.

Note The translation DLL does not null-terminate this value.

cbValueOutMax
[Input]
Length of rgbValueOut.
pcbValueOut
[Output]
The total number of bytes (excluding the null-termination byte) available to return in rgbValueOut.

For character or binary data, if this is greater than or equal to cbValueOutMax, the data in rgbValueOut is truncated to cbValueOutMax bytes.

For all other data types, the value of cbValueOutMax is ignored and the translation DLL assumes that the size of rgbValueOut is the size of the default C data type of the SQL data type specified with fSqlType.

The pcbValueOut argument can be a null pointer.

szErrorMsg
[Output]
Pointer to storage for an error message. This is an empty string unless the translation failed.
cbErrorMsgMax
[Input]
Length of szErrorMsg.
pcbErrorMsg
[Output]
Pointer to the total number of bytes (excluding the null-termination byte) available to return in szErrorMsg. If this is greater than or equal to cbErrorMsg, the data in szErrorMsg is truncated to cbErrorMsgMax minus the null-termination character. The pcbErrorMsg argument can be a null pointer.

Returns

TRUE if the translation was successful, FALSE if the translation failed.

Comments

The driver calls SQLDataSourceToDriver to translate all data (result set data, table names, row counts, error messages, and so on) passing from the data source to the driver. The translation DLL might not translate some data, depending on the data's type and the purpose of the translation DLL; for example, a DLL that translates character data from one code page to another ignores all numeric and binary data.

The value of fOption is set to the value of vParam specified by calling SQLSetConnectAttr with the SQL_ATTR_TRANSLATE_OPTION attribute. It is a 32-bit value that has a specific meaning for a given translation DLL. For example, it could specify a certain character set translation.

If the same buffer is specified for rgbValueIn and rgbValueOut, the translation of data in the buffer will be performed in place.

Although cbValueIn, cbValueOutMax, and pcbValueOut are of the type SDWORD, SQLDataSourceToDriver does not necessarily support huge pointers.

If SQLDataSourceToDriver returns FALSE, data truncation might have occurred during translation. If pcbValueOut (the number of bytes available to return in the output buffer) is greater than cbValueOutMax (the length of the output buffer), then truncation occurred. The driver must determine whether the truncation was acceptable. If truncation did not occur, the SQLDataSourceToDriver returned FALSE due to another error. In either case, a specific error message is returned in szErrorMsg.

For more information about translating data, see "Translation DLLs" in Chapter 17: Programming Considerations.

Related Functions

For information about | See
Translating data being sent to the data source | SQLDriverToDataSource
Returning the setting of a connection attribute | SQLGetConnectAttr
Setting a connection attribute | SQLSetConnectAttr


SQLDriverToDataSource

SQLDriverToDataSource supports translations for ODBC drivers. This function is not called by ODBC-enabled applications; applications request translation through SQLSetConnectAttr. The driver associated with the ConnectionHandle specified in SQLSetConnectAttr calls the specified DLL to perform translations of all data flowing from the driver to the data source. A default translation DLL can be specified in the ODBC initialization file.

Syntax

BOOL SQLDriverToDataSource(
     UDWORD      fOption,
     SWORD      fSqlType,
     PTR      rgbValueIn,
     SDWORD      cbValueIn,
     PTR      rgbValueOut,
     SDWORD      cbValueOutMax,
     SDWORD *      pcbValueOut,
     UCHAR *      szErrorMsg,
     SWORD      cbErrorMsgMax,
     SWORD *      pcbErrorMsg);

Arguments

fOption
[Input]
Option value.
fSqlType
[Input]
The ODBC SQL data type. This argument tells the driver how to convert rgbValueIn into a form acceptable by the data source. For a list of valid SQL data types, see the "SQL Data Types" section in Appendix D: Data Types.
rgbValueIn
[Input]
Value to translate.
cbValueIn
[Input]
Length of rgbValueIn.
rgbValueOut
[Output]
Result of the translation.

Note The translation DLL does not null-terminate this value.

cbValueOutMax
[Input]
Length of rgbValueOut.
pcbValueOut
[Output]
The total number of bytes (excluding the null-termination byte) available to return in rgbValueOut.

For character or binary data, if this is greater than or equal to cbValueOutMax, the data in rgbValueOut is truncated to cbValueOutMax bytes.

For all other data types, the value of cbValueOutMax is ignored and the translation DLL assumes that the size of rgbValueOut is the size of the default C data type of the SQL data type specified with fSqlType.

The pcbValueOut argument can be a null pointer.

szErrorMsg
[Output]
Pointer to storage for an error message. This is an empty string unless the translation failed.
cbErrorMsgMax
[Input]
Length of szErrorMsg.
pcbErrorMsg
[Output]
Pointer to the total number of bytes (excluding the null-termination byte) available to return in szErrorMsg. If this is greater than or equal to cbErrorMsg, the data in szErrorMsg is truncated to cbErrorMsgMax minus the null-termination character. The pcbErrorMsg argument can be a null pointer.

Returns

TRUE if the translation was successful, FALSE if the translation failed.

Comments

The driver calls SQLDriverToDataSource to translate all data (SQL statements, parameters, and so on) passing from the driver to the data source. The translation DLL might not translate some data, depending on the data's type and the purpose of the translation DLL. For example, a DLL that translates character data from one code page to another ignores all numeric and binary data.

The value of fOption is set to the value of vParam specified by calling SQLSetConnectAttr with the SQL_ATTR_TRANSLATE_OPTION attribute. It is a 32-bit value that has a specific meaning for a given translation DLL. For example, it could specify a certain character set translation.

If the same buffer is specified for rgbValueIn and rgbValueOut, the translation of data in the buffer will be performed in place.

Although cbValueIn, cbValueOutMax, and pcbValueOut are of the type SDWORD, SQLDriverToDataSource does not necessarily support huge pointers.

If SQLDriverToDataSource returns FALSE, data truncation might have occurred during translation. If pcbValueOut (the number of bytes available to return in the output buffer) is greater than cbValueOutMax (the length of the output buffer), then truncation occurred. The driver must determine whether or not the truncation was acceptable. If truncation did not occur, the SQLDriverToDataSource returned FALSE due to another error. In either case, a specific error message is returned in szErrorMsg.

For more information about translating data, see "Translation DLLs" in Chapter 17: Programming Considerations.

Related Functions

For information about | See
Translating data returned from the data source | SQLDataSourceToDriver
Returning the setting of a connection attribute | SQLGetConnectAttr
Setting a connection attribute | SQLSetConnectAttr


Appendixes

Technical details relating to the Microsoft ODBC interface are contained in the following appendixes:

  • Appendix A: ODBC Error Codes
  • Appendix B: ODBC State Transition Tables
  • Appendix C: SQL Grammar
  • Appendix D: Data Types
  • Appendix E: Scalar Functions
  • Appendix F: ODBC Cursor Library
  • Appendix G: Driver Guidelines for Backward Compatibility


Appendix A: ODBC Error Codes

SQLGetDiagRec or SQLGetDiagField returns SQLSTATE values as defined by X/Open Data Management: Structured Query Language (SQL), Version 2 (March 1995). SQLSTATE values are strings that contain five characters. The following table lists SQLSTATE values that a driver can return for SQLGetDiagRec.

The character string value returned for an SQLSTATE consists of a two-character class value followed by a three-character subclass value. A class value of "01" indicates a warning and is accompanied by a return code of SQL_SUCCESS_WITH_INFO. Class values other than "01," except for the class "IM," indicate an error and are accompanied by a return value of SQL_ERROR. The class "IM" is specific to warnings and errors that derive from the implementation of ODBC itself. The subclass value "000" in any class indicates that there is no subclass for that SQLSTATE. The assignment of class and subclass values is defined by SQL-92.

Note Although successful execution of a function is normally indicated by a return value of SQL_SUCCESS, the SQLSTATE 00000 also indicates success.

SQLSTATE | Error | Can be returned from
01000 | General warning | All ODBC functions except: SQLError SQLGetDiagField SQLGetDiagRec
01001 | Cursor operation conflict | SQLExecDirect SQLExecute SQLParamData SQLSetPos
01002 | Disconnect error | SQLDisconnect
01003 | NULL value eliminated in set function | SQLExecDirect SQLExecute SQLParamData
01004 | String data, right-truncated | SQLBrowseConnect SQLBulkOperations SQLColAttribute SQLDataSources SQLDescribeCol SQLDriverConnect SQLDrivers SQLExecDirect SQLExecute SQLExtendedFetch SQLFetch SQLFetchScroll SQLGetConnectAttr SQLGetCursorName SQLGetData SQLGetDescField SQLGetDescRec SQLGetEnvAttr SQLGetInfo SQLGetStmtAttr SQLNativeSql SQLParamData SQLPutData SQLSetCursorName
01006 | Privilege not revoked | SQLExecDirect SQLExecute SQLParamData
01007 | Privilege not granted | SQLExecDirect SQLExecute SQLParamData
01S00 | Invalid connection string attribute | SQLBrowseConnect SQLDriverConnect
01S01 | Error in row | SQLBulkOperations SQLExtendedFetch SQLSetPos
01S02 | Option value changed | SQLBrowseConnect SQLConnect SQLDriverConnect SQLExecDirect SQLExecute SQLParamData SQLPrepare SQLSetConnectAttr SQLSetDescField SQLSetEnvAttr SQLSetStmtAttr
01S06 | Attempt to fetch before the result set returned the first rowset | SQLExtendedFetch SQLFetchScroll
01S07 | Fractional truncation | SQLBulkOperations SQLExecDirect SQLExecute SQLExtendedFetch SQLFetch SQLFetchScroll SQLGetData SQLParamData SQLSetPos
01S08 | Error saving File DSN | SQLDriverConnect
01S09 | Invalid keyword | SQLDriverConnect
07001 | Wrong number of parameters | SQLExecDirect SQLExecute
07002 | COUNT field incorrect | SQLExecDirect SQLExecute SQLParamData
07005 | Prepared statement not a cursor-specification | SQLColAttribute SQLDescribeCol
07006 | Restricted data type attribute violation | SQLBindCol SQLBindParameter SQLBulkOperations SQLExecDirect SQLExecute SQLExtendedFetch SQLFetch SQLFetchScroll SQLGetData SQLParamData SQLPutData SQLSetPos
07009 | Invalid descriptor index | SQLBindCol SQLBindParameter SQLBulkOperations SQLColAttribute SQLDescribeCol SQLDescribeParam SQLFetch SQLFetchScroll SQLGetData SQLGetDescField SQLGetDescRec SQLParamData SQLSetDescField SQLSetDescRec SQLSetPos
07S01 | Invalid use of default parameter | SQLExecDirect SQLExecute SQLParamData SQLPutData
08001 | Client unable to establish connection | SQLBrowseConnect SQLConnect SQLDriverConnect
08002 | Connection name in use | SQLBrowseConnect SQLConnect SQLDriverConnect SQLSetConnectAttr
08003 | Connection does not exist | SQLAllocHandle SQLDisconnect SQLEndTran SQLGetConnectAttr SQLGetInfo SQLNativeSql SQLSetConnectAttr
08004 | Server rejected the connection | SQLBrowseConnect SQLConnect SQLDriverConnect
08007 | Connection failure during transaction | SQLEndTran
08S01 | Communication link failure | SQLBrowseConnect SQLColumnPrivileges SQLColumns SQLConnect SQLCopyDesc SQLDescribeCol SQLDescribeParam SQLDriverConnect SQLExecDirect SQLExecute SQLExtendedFetch SQLFetch SQLFetchScroll SQLForeignKeys SQLGetConnectAttr SQLGetData SQLGetDescField SQLGetDescRec SQLGetFunctions SQLGetInfo SQLGetTypeInfo SQLMoreResults SQLNativeSql SQLNumParams SQLNumResultCols SQLParamData SQLPrepare SQLPrimaryKeys SQLProcedureColumns SQLProcedures SQLPutData SQLSetConnectAttr SQLSetDescField SQLSetDescRec SQLSetEnvAttr SQLSetStmtAttr SQLSpecialColumns SQLStatistics SQLTablePrivileges SQLTables
21S01 | Insert value list does not match column list | SQLExecDirect SQLPrepare
21S02 | Degree of derived table does not match column list | SQLBulkOperations SQLExecDirect SQLExecute SQLParamData SQLPrepare SQLSetPos
22001 | String data, right-truncated | SQLBulkOperations SQLExecDirect SQLExecute SQLFetch SQLFetchScroll SQLParamData SQLPutData SQLSetDescField SQLSetPos
22002 | Indicator variable required but not supplied | SQLExecDirect SQLExecute SQLExtendedFetch SQLFetch SQLFetchScroll SQLGetData SQLParamData
22003 | Numeric value out of range | SQLBulkOperations SQLExecDirect SQLExecute SQLExtendedFetch SQLFetch SQLFetchScroll SQLGetData SQLGetInfo SQLParamData SQLPutData SQLSetPos
22007 | Invalid datetime format | SQLBulkOperations SQLExecDirect SQLExecute SQLExtendedFetch SQLFetch SQLFetchScroll SQLGetData SQLParamData SQLPutData SQLSetPos
22008 | Datetime field overflow | SQLBulkOperations SQLExecDirect SQLExecute SQLParamData SQLPutData
22012 | Division by zero | SQLExecDirect SQLExecute SQLExtendedFetch SQLFetch SQLFetchScroll SQLGetData SQLParamData SQLPutData
22015 | Interval field overflow | SQLBulkOperations SQLExecDirect SQLExecute SQLExtendedFetch SQLFetch SQLFetchScroll SQLGetData SQLParamData SQLPutData SQLSetPos
22018 | Invalid character value for cast specification | SQLBulkOperations SQLExecDirect SQLExecute SQLExtendedFetch SQLFetch SQLFetchScroll SQLGetData SQLParamData SQLPutData SQLSetPos
22019 | Invalid escape character | SQLExecDirect SQLExecute SQLPrepare
22025 | Invalid escape sequence | SQLExecDirect SQLExecute SQLPrepare
22026 | String data, length mismatch | SQLParamData
23000 | Integrity constraint violation | SQLBulkOperations SQLExecDirect SQLExecute SQLParamData SQLSetPos
24000 | Invalid cursor state | SQLBulkOperations SQLCloseCursor SQLColumnPrivileges SQLColumns SQLExecDirect SQLExecute SQLExtendedFetch SQLFetch SQLFetchScroll SQLForeignKeys SQLGetData SQLGetStmtAttr SQLGetTypeInfo SQLNativeSql SQLPrepare SQLPrimaryKeys SQLProcedureColumns SQLProcedures SQLSetConnectAttr SQLSetCursorName SQLSetPos SQLSetStmtAttr SQLSpecialColumns SQLStatistics SQLTablePrivileges SQLTables
25000 | Invalid transaction state | SQLDisconnect
25S01 | Transaction state | SQLEndTran
25S02 | Transaction is still active | SQLEndTran
25S03 | Transaction is rolled back | SQLEndTran
28000 | Invalid authorization specification | SQLBrowseConnect SQLConnect SQLDriverConnect
34000 | Invalid cursor name | SQLExecDirect SQLPrepare SQLSetCursorName
3C000 | Duplicate cursor name | SQLSetCursorName
3D000 | Invalid catalog name | SQLExecDirect SQLPrepare SQLSetConnectAttr
3F000 | Invalid schema name | SQLExecDirect SQLPrepare
40001 | Serialization failure | SQLBulkOperations SQLColumnPrivileges SQLColumns SQLEndTran SQLExecDirect SQLExecute SQLFetch SQLFetchScroll SQLForeignKeys SQLGetTypeInfo SQLMoreResults SQLParamData SQLPrimaryKeys SQLProcedureColumns SQLProcedures SQLSetPos SQLSpecialColumns SQLStatistics SQLTablePrivileges SQLTables
40002 | Integrity constraint violation | SQLEndTran
40003 | Statement completion unknown | SQLBulkOperations SQLColumnPrivileges SQLColumns SQLExecDirect SQLExecute SQLFetch SQLFetchScroll SQLForeignKeys SQLGetTypeInfo SQLMoreResults SQLPrimaryKeys SQLProcedureColumns SQLProcedures SQLParamData SQLSetPos SQLSpecialColumns SQLStatistics SQLTablePrivileges SQLTables
42000 | Syntax error or access violation | SQLBulkOperations SQLExecDirect SQLExecute SQLParamData SQLPrepare SQLSetPos
42S01 | Base table or view already exists | SQLExecDirect SQLPrepare
42S02 | Base table or view not found | SQLExecDirect SQLPrepare
42S11 | Index already exists | SQLExecDirect SQLPrepare
42S12 | Index not found | SQLExecDirect SQLPrepare
42S21 | Column already exists | SQLExecDirect SQLPrepare
42S22 | Column not found | SQLExecDirect SQLPrepare
44000 | WITH CHECK OPTION violation | SQLBulkOperations SQLExecDirect SQLExecute SQLParamData SQLSetPos
HY000 | General error | All ODBC functions except: SQLError SQLGetDiagField SQLGetDiagRec
HY001 | Memory allocation error | All ODBC functions except: SQLError SQLGetDiagField SQLGetDiagRec
HY003 | Invalid application buffer type | SQLBindCol SQLBindParameter SQLGetData
HY004 | Invalid SQL data type | SQLBindParameter SQLGetTypeInfo
HY007 | Associated statement is not prepared | SQLCopyDesc SQLGetDescField SQLGetDescRec
HY008 | Operation canceled | All ODBC functions that can be processed asynchronously: SQLBulkOperations SQLColAttribute SQLColumnPrivileges SQLColumns SQLDescribeCol SQLDescribeParam SQLExecDirect SQLExecute SQLExtendedFetch SQLFetch SQLFetchScroll SQLForeignKeys SQLGetData SQLGetTypeInfo SQLMoreResults SQLNumParams SQLNumResultCols SQLParamData SQLPrepare SQLPrimaryKeys SQLProcedureColumns SQLProcedures SQLPutData SQLSetPos SQLSpecialColumns SQLStatistics SQLTablePrivileges SQLTables
HY009 | Invalid use of null pointer | SQLAllocHandle SQLBindParameter SQLBulkOperations SQLColumnPrivileges SQLColumns SQLExecDirect SQLForeignKeys SQLGetCursorName SQLGetData SQLGetFunctions SQLNativeSql SQLPrepare SQLPrimaryKeys SQLProcedureColumns SQLProcedures SQLPutData SQLSetConnectAttr SQLSetCursorName SQLSetEnvAttr SQLSetStmtAttr SQLSpecialColumns SQLStatistics SQLTablePrivileges SQLTables
HY010 | Function sequence error | SQLAllocHandle SQLBindCol SQLBindParameter SQLBulkOperations SQLCloseCursor SQLColAttribute SQLColumnPrivileges SQLColumns SQLCopyDesc SQLDescribeCol SQLDescribeParam SQLDisconnect SQLEndTran SQLExecDirect SQLExecute SQLExtendedFetch SQLFetch SQLFetchScroll SQLForeignKeys SQLFreeHandle SQLFreeStmt SQLGetConnectAttr SQLGetCursorName SQLGetData SQLGetDescField SQLGetDescRec SQLGetFunctions SQLGetStmtAttr SQLGetTypeInfo SQLMoreResults SQLNumParams SQLNumResultCols SQLParamData SQLPrepare SQLPrimaryKeys SQLProcedureColumns SQLProcedures SQLPutData SQLRowCount SQLSetConnectAttr SQLSetCursorName SQLSetDescField SQLSetEnvAttr SQLSetDescRec SQLSetPos SQLSetStmtAttr SQLSpecialColumns SQLStatistics SQLTablePrivileges SQLTables
HY011 | Attribute cannot be set now | SQLBulkOperations SQLParamData SQLSetConnectAttr SQLSetPos SQLSetStmtAttr
HY012 | Invalid transaction operation code | SQLEndTran
HY013 | Memory management error | All ODBC functions except: SQLGetDiagField SQLGetDiagRec
HY014 | Limit on the number of handles exceeded | SQLAllocHandle
HY015 | No cursor name available | SQLGetCursorName
HY016 | Cannot modify an implementation row descriptor | SQLCopyDesc SQLSetDescField SQLSetDescRec
HY017 | Invalid use of an automatically allocated descriptor handle | SQLFreeHandle SQLSetStmtAttr
HY018 | Server declined cancel request | SQLCancel
HY019 | Non-character and non-binary data sent in pieces | SQLPutData
HY020 | Attempt to concatenate a null value | SQLPutData
HY021 | Inconsistent descriptor information | SQLBindParameter SQLCopyDesc SQLGetDescField SQLSetDescField SQLSetDescRec
HY024 | Invalid attribute value | SQLSetConnectAttr SQLSetEnvAttr SQLSetStmtAttr
HY090 | Invalid string or buffer length | SQLBindCol SQLBindParameter SQLBrowseConnect SQLBulkOperations SQLColAttribute SQLColumnPrivileges SQLColumns SQLConnect SQLDataSources SQLDescribeCol SQLDriverConnect SQLDrivers SQLExecDirect SQLExecute SQLFetch SQLFetchScroll SQLForeignKeys SQLGetConnectAttr SQLGetCursorName SQLGetData SQLGetDescField SQLGetInfo SQLGetStmtAttr SQLNativeSql SQLParamData SQLPrepare SQLPrimaryKeys SQLProcedureColumns SQLProcedures SQLPutData SQLSetConnectAttr SQLSetCursorName SQLSetDescField SQLSetDescRec SQLSetEnvAttr SQLSetStmtAttr SQLSetPos SQLSpecialColumns SQLStatistics SQLTablePrivileges SQLTables
HY091 | Invalid descriptor field identifier | SQLColAttribute SQLGetDescField SQLSetDescField
HY092 | Invalid attribute/option identifier | SQLAllocHandle SQLBulkOperations SQLCopyDesc SQLDriverConnect SQLEndTran SQLFreeStmt SQLGetConnectAttr SQLGetEnvAttr SQLGetStmtAttr SQLParamData SQLSetConnectAttr SQLSetDescField SQLSetEnvAttr SQLSetPos SQLSetStmtAttr
HY095 | Function type out of range | SQLGetFunctions
HY096 | Invalid information type | SQLGetInfo
HY097 | Column type out of range | SQLSpecialColumns
HY098 | Scope type out of range | SQLSpecialColumns
HY099 | Nullable type out of range | SQLSpecialColumns
HY100 | Uniqueness option type out of range | SQLStatistics
HY101 | Accuracy option type out of range | SQLStatistics
HY103 | Invalid retrieval code | SQLDataSources SQLDrivers
HY104 | Invalid precision or scale value | SQLBindParameter
HY105 | Invalid parameter type | SQLBindParameter SQLExecDirect SQLExecute SQLParamData SQLSetDescField
HY106 | Fetch type out of range | SQLExtendedFetch SQLFetchScroll
HY107 | Row value out of range | SQLExtendedFetch SQLFetch SQLFetchScroll SQLSetPos
HY109 | Invalid cursor position | SQLExecDirect SQLExecute SQLGetData SQLGetStmtAttr SQLNativeSql SQLParamData SQLSetPos
HY110 | Invalid driver completion | SQLDriverConnect
HY111 | Invalid bookmark value | SQLExtendedFetch SQLFetchScroll
HYC00 | Optional feature not implemented | SQLBindCol SQLBindParameter SQLBulkOperations SQLColAttribute SQLColumnPrivileges SQLColumns SQLDriverConnect SQLEndTran SQLExecDirect SQLExecute SQLExtendedFetch SQLFetch SQLFetchScroll SQLForeignKeys SQLGetConnectAttr SQLGetData SQLGetEnvAttr SQLGetInfo SQLGetStmtAttr SQLGetTypeInfo SQLParamData SQLPrepare SQLPrimaryKeys SQLProcedureColumns SQLProcedures SQLSetConnectAttr SQLSetEnvAttr SQLSetPos SQLSetStmtAttr SQLSpecialColumns SQLStatistics SQLTablePrivileges SQLTables
HYT00 | Timeout expired | SQLBrowseConnect SQLBulkOperations SQLColumnPrivileges SQLColumns SQLConnect SQLDriverConnect SQLExecDirect SQLExecute SQLExtendedFetch SQLForeignKeys SQLGetTypeInfo SQLParamData SQLPrepare SQLPrimaryKeys SQLProcedureColumns SQLProcedures SQLSetPos SQLSpecialColumns SQLStatistics SQLTablePrivileges SQLTables
HYT01 | Connection timeout expired | All ODBC functions except: SQLDrivers SQLDataSources SQLGetEnvAttr SQLSetEnvAttr
IM001 | Driver does not support this function | All ODBC functions except: SQLAllocHandle SQLDataSources SQLDrivers SQLFreeHandle SQLGetFunctions
IM002 | Data source name not found and no default driver specified | SQLBrowseConnect SQLConnect SQLDriverConnect
IM003 | Specified driver could not be loaded | SQLBrowseConnect SQLConnect SQLDriverConnect
IM004 | Driver's SQLAllocHandle on SQL_HANDLE_ENV failed | SQLBrowseConnect SQLConnect SQLDriverConnect
IM005 | Driver's SQLAllocHandle on SQL_HANDLE_DBC failed | SQLBrowseConnect SQLConnect SQLDriverConnect
IM006 | Driver's SQLSetConnectAttr failed | SQLBrowseConnect SQLConnect SQLDriverConnect
IM007 | No data source or driver specified; dialog prohibited | SQLDriverConnect
IM008 | Dialog failed | SQLDriverConnect
IM009 | Unable to load translation DLL | SQLBrowseConnect SQLConnect SQLDriverConnect SQLSetConnectAttr
IM010 | Data source name too long | SQLBrowseConnect SQLConnect SQLDriverConnect
IM011 | Driver name too long | SQLBrowseConnect SQLDriverConnect
IM012 | DRIVER keyword syntax error | SQLBrowseConnect SQLDriverConnect
IM013 | Trace file error | All ODBC functions
IM014 | Invalid name of File DSN | SQLDriverConnect
IM015 | Corrupt file data source | SQLDriverConnect


Appendix B: ODBC State Transition Tables

The tables in this appendix show how ODBC functions cause transitions of the environment, connection, statement, and descriptor states. The state of the environment, connection, statement, or descriptor usually dictates when functions that use the corresponding type of handle (environment, connection, statement, or descriptor) can be called. The environment, connection, statement, and descriptor states overlap roughly as shown in the following illustrations. For example, the exact overlap of connection states C5 and C6 and statement states S1 through S12 is data source–dependent, since transactions begin at different times on different data sources, and descriptor state D1i (implicitly allocated descriptor) depends on the state of the statement with which the descriptor is associated, while state D1e (explicitly allocated descriptor) is independent of the state of any statement. For a description of each state, see "Environment Transitions," "Connection Transitions," "Statement Transitions," and "Descriptor Transitions," later in this appendix.

The environment and connection states overlap as follows:

The connection and statement states overlap as follows:

The statement and descriptor states overlap as follows:

The connection and descriptor states overlap as follows:

Each entry in a transition table can be one of the following values:

  • -- —The state is unchanged after executing the function.
  • En, Cn, Sn, or Dn—The environment, connection, statement, or descriptor state moves to the specified state.
  • (IH)—An invalid handle was passed to the function. If the handle was a null handle or was a valid handle of the wrong type—for example, a connection handle was passed when a statement handle was required—the function returns SQL_INVALID_HANDLE; otherwise the behavior is undefined and probably fatal. This error is shown only when it is the only possible outcome of calling the function in the specified state. This error does not change the state and is always detected by the Driver Manager, as indicated by the parentheses.
  • NS—Next State. The statement transition is the same as if the statement had not gone through the asynchronous states. For example, suppose a statement that creates a result set enters state S11 from state S1 because SQLExecDirect returned SQL_STILL_EXECUTING. The NS notation in state S11 means that the transitions for the statement are the same as those for a statement in state S1 that creates a result set. If SQLExecDirect returns an error, the statement remains in state S1; if it succeeds, the statement moves to state S5; if it needs data, the statement moves to state S8; and if it is still executing, it remains in state S11.
  • XXXXX or (XXXXX)—An SQLSTATE that is related to the transition table; SQLSTATEs detected by the Driver Manager are enclosed in parentheses. The function returned SQL_ERROR and the specified SQLSTATE, but the state does not change. For example, if SQLExecute is called before SQLPrepare, it returns SQLSTATE HY010 (Function sequence error).

Note The tables do not show errors unrelated to the transition tables that do not change the state. For example, when SQLAllocHandle is called in environment state E1 and returns SQLSTATE HY001 (Memory allocation error), the environment remains in state E1; this is not shown in the environment transition table for SQLAllocHandle.

If the environment, connection, statement, or descriptor can move to more than one state, each possible state is shown and one or more footnotes explain the conditions under which each transition takes place. The following footnotes may appear in any table.

Footnote | Meaning
b | Before or after. The cursor was positioned before the start of the result set or after the end of the result set.
c | Current function. The current function was executing asynchronously.
d | Need data. The function returned SQL_NEED_DATA.
e | Error. The function returned SQL_ERROR.
i | Invalid row. The cursor was positioned on a row in the result set and either the row had been deleted or an error had occurred in an operation on the row. If the row status array existed, the value in the row status array for the row was SQL_ROW_DELETED or SQL_ROW_ERROR. (The row status array is pointed to by the SQL_ATTR_ROW_STATUS_PTR statement attribute.)
nf | Not found. The function returned SQL_NO_DATA. This does not apply when SQLExecDirect, SQLExecute, or SQLParamData returns SQL_NO_DATA after executing a searched update or delete statement.
np | Not prepared. The statement was not prepared.
nr | No results. The statement will not or did not create a result set.
o | Other function. Another function was executing asynchronously.
p | Prepared. The statement was prepared.
r | Results. The statement will or did create a (possibly empty) result set.
s | Success. The function returned SQL_SUCCESS_WITH_INFO or SQL_SUCCESS.
v | Valid row. The cursor was positioned on a row in the result set, and the row had been successfully inserted, successfully updated, or another operation on the row had been successfully completed. If the row status array existed, the value in the row status array for the row was SQL_ROW_ADDED, SQL_ROW_SUCCESS, or SQL_ROW_UPDATED. (The row status array is pointed to by the SQL_ATTR_ROW_STATUS_PTR statement attribute.)
x | Executing. The function returned SQL_STILL_EXECUTING.

SQLFreeHandle

In this example, the row in the environment state transition table for SQLFreeHandle when HandleType is SQL_HANDLE_ENV is as follows.

E0 Unallocated | E1 Allocated | E2 Connection
(IH) | E0 | (HY010)

If SQLFreeHandle is called in environment state E0 with HandleType set to SQL_HANDLE_ENV, the Driver Manager returns SQL_INVALID_HANDLE. If it is called in state E1 with HandleType set to SQL_HANDLE_ENV, the environment moves to state E0 if the function succeeds and remains in state E1 if the function fails. If it is called in state E2 with HandleType set to SQL_HANDLE_ENV, the Driver Manager always returns SQL_ERROR and SQLSTATE HY010 (Function sequence error) and the environment remains in state E2.

To understand the state transition tables, it is necessary to understand which item (environment, connection, statement, or descriptor) they refer to. Suppose a function accepts the handle of an item of type X. The X state transition table for that function describes how calling the function, with the handle of an item of type X, affects that item. For example, SQLDisconnect accepts a connection handle. The connection state transition table for SQLDisconnect describes how SQLDisconnect affects the state of the connection for which it is called.

Suppose a function accepts the handle of an item of type Y, where Y is not equal to X. The X state transition table for that function describes how calling the function, with a handle of type X that is associated with the item of type Y, affects the item of type Y. For example, the statement state transition table for SQLDisconnect describes how SQLDisconnect affects the state of a statement when called with the handle of the connection with which the statement is associated.


Environment Transitions

ODBC environments have the following three states.

State | Description
E0 | Unallocated environment
E1 | Allocated environment, unallocated connection
E2 | Allocated environment, allocated connection

The following tables show how each ODBC function affects the environment state.

SQLAllocHandle

E0 Unallocated | E1 Allocated | E2 Connection
E1[1] | --[4] | --[4]
(IH)[2] | E2[5] (HY010)[6] | --[4]
(IH)[3] | (IH) | --[4]

[1] This row shows transitions when HandleType was SQL_HANDLE_ENV.

[2] This row shows transitions when HandleType was SQL_HANDLE_DBC.

[3] This row shows transitions when HandleType was SQL_HANDLE_STMT or SQL_HANDLE_DESC.

[4] Calling SQLAllocHandle with OutputHandlePtr pointing to a valid handle overwrites that handle. This might be an application programming error.

[5] The SQL_ATTR_ODBC_VERSION environment attribute had been set on the environment.

[6] The SQL_ATTR_ODBC_VERSION environment attribute had not been set on the environment.

SQLDataSources and SQLDrivers

E0 Unallocated | E1 Allocated | E2 Connection
(IH) | --[1] (HY010)[2] | --[1] (HY010)[2]

[1] The SQL_ATTR_ODBC_VERSION environment attribute had been set on the environment.

[2] The SQL_ATTR_ODBC_VERSION environment attribute had not been set on the environment.

SQLEndTran

E0 Unallocated | E1 Allocated | E2 Connection
(IH)[1] | --[3] (HY010)[4] | --[3] (HY010)[4]
(IH)[2] | (IH) | --

[1] This row shows transitions when HandleType was SQL_HANDLE_ENV.

[2] This row shows transitions when HandleType was SQL_HANDLE_DBC.

[3] The SQL_ATTR_ODBC_VERSION environment attribute had been set on the environment.

[4] The SQL_ATTR_ODBC_VERSION environment attribute had not been set on the environment.

SQLFreeHandle

E0 Unallocated | E1 Allocated | E2 Connection
(IH)[1] | E0 | (HY010)
(IH)[2] | (IH) | --[4] E1[5]
(IH)[3] | (IH) | --

[1] This row shows transitions when HandleType was SQL_HANDLE_ENV.

[2] This row shows transitions when HandleType was SQL_HANDLE_DBC.

[3] This row shows transitions when HandleType was SQL_HANDLE_STMT or SQL_HANDLE_DESC.

[4] There were other allocated connection handles.

[5] The connection handle specified in Handle was the only allocated connection handle.

SQLGetDiagField and SQLGetDiagRec

E0 Unallocated | E1 Allocated | E2 Connection
(IH)[1] | -- | --
(IH)[2] | (IH) | --

[1] This row shows transitions when HandleType was SQL_HANDLE_ENV.

[2] This row shows transitions when HandleType was SQL_HANDLE_DBC, SQL_HANDLE_STMT, or SQL_HANDLE_DESC.

SQLGetEnvAttr

E0 Unallocated | E1 Allocated | E2 Connection
(IH) | --[1] (HY010)[2] | --

[1] The SQL_ATTR_ODBC_VERSION environment attribute had been set on the environment.

[2] The SQL_ATTR_ODBC_VERSION environment attribute had not been set on the environment.

SQLSetEnvAttr

E0 Unallocated | E1 Allocated | E2 Connection
(IH) | --[1] (HY010)[2] | (HY011)

[1] The SQL_ATTR_ODBC_VERSION environment attribute had been set on the environment.

[2] The Attribute argument was not SQL_ATTR_ODBC_VERSION, and the SQL_ATTR_ODBC_VERSION environment attribute had not been set on the environment.

All Other ODBC Functions

E0 Unallocated | E1 Allocated | E2 Connection
(IH) | (IH) | --


Connection Transitions

ODBC connections have the following states.

State | Description
C0 | Unallocated environment, unallocated connection
C1 | Allocated environment, unallocated connection
C2 | Allocated environment, allocated connection
C3 | Connection function needs data
C4 | Connected connection
C5 | Connected connection, allocated statement
C6 | Connected connection, transaction in progress. It is possible for a connection to be in state C6 with no statements allocated on the connection. For example, suppose the connection is in manual commit mode and is in state C4. If a statement is allocated, executed (starting a transaction), and then freed, the transaction remains active but there are no statements on the connection.

The following tables show how each ODBC function affects the connection state.

SQLAllocHandle

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
C1[1] | --[5] | --[5] | --[5] | --[5] | --[5] | --[5]
(IH)[2] | C2 | --[5] | --[5] | --[5] | --[5] | --[5]
(IH)[3] | (IH) | (08003) | (08003) | C5 | --[5] | --[5]
(IH)[4] | (IH) | (08003) | (08003) | --[5] | --[5] | --[5]

[1] This row shows transitions when HandleType was SQL_HANDLE_ENV.

[2] This row shows transitions when HandleType was SQL_HANDLE_DBC.

[3] This row shows transitions when HandleType was SQL_HANDLE_STMT.

[4] This row shows transitions when HandleType was SQL_HANDLE_DESC.

[5] Calling SQLAllocHandle with OutputHandlePtr pointing to a valid handle overwrites that handle without regard for the previous contents ofthat handle, and might cause problems for ODBC drivers. It is incorrect ODBC application programming to call SQLAllocHandle twice with the same application variable defined for *OutputHandlePtr without calling SQLFreeHandle to free the handle before reallocating it. Overwriting ODBC handles in such a manner can lead to inconsistent behavior or errors on the part of ODBC drivers.

SQLBrowseConnect

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | C3 [d] C4 [s] | -- [d] C2 [e] C4 [s] | (08002) | (08002) | (08002)

SQLCloseCursor

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | (IH) | (IH) | (IH) | -- | --[1] C5[2]

[1] The connection was in manual-commit mode.

[2] The connection was in auto-commit mode.

SQLColumnPrivileges, SQLColumns, SQLForeignKeys, SQLGetTypeInfo, SQLPrimaryKeys, SQLProcedureColumns, SQLProcedures, SQLSpecialColumns, SQLStatistics, SQLTablePrivileges, and SQLTables

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | (IH) | (IH) | (IH) | --[1] C6[2] | --

[1] The connection was in auto-commit mode, or the data source did not begin a transaction.

[2] The connection was in manual-commit mode, and the data source began a transaction.

SQLColumns: See SQLColumnPrivileges

SQLConnect

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | C4 | (08002) | (08002) | (08002) | (08002)

SQLCopyDesc, SQLGetDescField, SQLGetDescRec, SQLSetDescField, and SQLSetDescRec

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | (IH) | (IH) | --[1] | -- | --

[1] In this state, the only descriptors available to the application are explicitly allocated descriptors.

SQLDataSources and SQLDrivers

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | -- | -- | -- | -- | -- | --

SQLDisconnect

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | (08003) | C2 | C2 | C2 | 25000

SQLDriverConnect

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | C4 s -- n[f] | (08002) | (08002) | (08002) | (08002)

SQLDrivers: See SQLDataSources

SQLEndTran

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH)[1] | --[3] | --[3] | --[3] | -- | -- | --[4] or ([5], [6], and [8]) C4[5] and [7] C5[5], [6], and [9]
(IH)[2] | (IH) | (08003) | (08003) | -- | -- | C5

[1] This row shows transitions when HandleType was SQL_HANDLE_ENV.

[2] This row shows transitions when HandleType was SQL_HANDLE_DBC.

[3] Because the connection is not in a connected state, it is unaffected by the transaction.

[4] The commit or rollback failed on the connection. The function returns SQL_ERROR in this case.

[5] The commit or rollback succeeded on the connection. The function returns SQL_ERROR if the commit or rollback failed on another connection, or the function returns SQL_SUCCESS if the commit or rollback succeeded on all connections.

[6] There was at least one statement allocated on the connection.

[7] There were no statements allocated on the connection.

[8] The connection had at least one statement for which there was an open cursor, and the data source preserves cursors when transactions are committed or rolled back, whichever applies (depending on whether CompletionType was SQL_COMMIT or SQL_ROLLBACK). For more information, see the SQL_CURSOR_COMMIT_BEHAVIOR and SQL_CURSOR_ROLLBACK_BEHAVIOR attributes in SQLGetInfo.

[9] If the connection had any statements for which there were open cursors, the cursors were not preserved when the transaction was committed or rolled back.

SQLExecDirect and SQLExecute

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | (IH) | (IH) | (IH) | --[1] C6[2] C6[3] | --

[1] The connection was in auto-commit mode, and the statement executed was not a cursor specification (such as a SELECT statement); or the connection was in manual-commit mode, and the statement executed did not begin a transaction.

[2] The connection was in auto-commit mode, and the statement executed was a cursor specification (such as a SELECT statement).

[3] The connection was in manual-commit mode, and the data source began a transaction.

SQLExecute: See SQLExecDirect

SQLForeignKeys: See SQLColumnPrivileges

SQLFreeHandle

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH)[1] | C0 | (HY010) | (HY010) | (HY010) | (HY010) | (HY010)
(IH)[2] | (IH) | (C1) | (HY010) | (HY010) | (HY010) | (HY010)
(IH)[3] | (IH) | (IH) | (IH) | (IH) | C4[5] --[6] | --[7] C4[5] and [8] C5[6] and [8]
(IH)[4] | (IH) | (IH) | (IH) | -- | -- | --

[1] This row shows transitions when HandleType was SQL_HANDLE_ENV.

[2] This row shows transitions when HandleType was SQL_HANDLE_DBC.

[3] This row shows transitions when HandleType was SQL_HANDLE_STMT.

[4] This row shows transitions when HandleType was SQL_HANDLE_DESC.

[5] There was only one statement allocated on the connection.

[6] There were multiple statements allocated on the connection.

[7] The connection was in manual-commit mode.

[8] The connection was in auto-commit mode.

SQLFreeStmt

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH)[1] | (IH) | (IH) | (IH) | (IH) | -- | C5[3] --[4]
(IH)[2] | (IH) | (IH) | (IH) | (IH) | -- | --

[1] This row shows transactions when the Option argument is SQL_CLOSE.

[2] This row shows transactions when the Option argument is SQL_UNBIND or SQL_RESET_PARAMS.

[3] The connection was in auto-commit mode, and no cursors were open on any statements except this one.

[4] The connection was in manual-commit mode, or it was in auto-commit mode and a cursor was open on at least one other statement.

SQLGetConnectAttr

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | --[1] (08003)[2] | (HY010) | -- | -- | --

[1] The Attribute argument was SQL_ATTR_ACCESS_MODE, SQL_ATTR_AUTOCOMMIT, SQL_ATTR_LOGIN_TIMEOUT, SQL_ATTR_ODBC_CURSORS, SQL_ATTR_TRACE, or SQL_ATTR_TRACEFILE, or a value had been set for the connection attribute.

[2] The Attribute argument was not SQL_ATTR_ACCESS_MODE, SQL_ATTR_AUTOCOMMIT, SQL_ATTR_LOGIN_TIMEOUT, SQL_ATTR_ODBC_CURSORS, SQL_ATTR_TRACE, or SQL_ATTR_TRACEFILE, and a value had not been set for the connection attribute.

SQLGetDiagField and SQLGetDiagRec

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH)[1] | -- | -- | -- | -- | -- | --
(IH)[2] | (IH) | -- | -- | -- | -- | --
(IH)[3] | (IH) | (IH) | (IH) | (IH) | -- | --
(IH)[4] | (IH) | (IH) | (IH) | -- | -- | --

[1] This row shows transitions when HandleType was SQL_HANDLE_ENV.

[2] This row shows transitions when HandleType was SQL_HANDLE_DBC.

[3] This row shows transitions when HandleType was SQL_HANDLE_STMT.

[4] This row shows transitions when HandleType was SQL_HANDLE_DESC.

SQLGetEnvAttr

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | -- | -- | -- | -- | -- | --

SQLGetFunctions

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | (HY010) | (HY010) | -- | -- | --

SQLGetInfo

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | --[1] (08003)[2] | (08003) | -- | -- | --

[1] The InfoType argument was SQL_ODBC_VER.

[2] The InfoType argument was not SQL_ODBC_VER.

SQLGetTypeInfo: See SQLColumnPrivileges

SQLMoreResults

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | (IH) | (IH) | (IH) | --[1] C6[2] | --[3] C5[1]

[1] The connection was in auto-commit mode, and the call to SQLMoreResults has not initialized the processing of a result set of a cursor specification.

[2] The connection was in auto-commit mode, and the call to SQLMoreResults has initialized the processing of a result set of a cursor specification.

[3] The connection was in manual-commit mode.

SQLNativeSql

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | (08003) | (08003) | -- | -- | --

SQLPrepare

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | (IH) | (IH) | (IH) | --[1] C6[2] | --

[1] The connection was in auto-commit mode, or the data source did not begin a transaction.

[2] The connection was in manual–commit mode, and the data source began a transaction.

SQLPrimaryKeys: See SQLColumnPrivileges

SQLProcedureColumns: See SQLColumnPrivileges

SQLProcedures: See SQLColumnPrivileges

SQLSetConnectAttr

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | --[1] (08003)[2] | (HY010) | --[3] (08002)[4] HY011[5] | --[3] (08002)[4] HY011[5] | --[3] and [6] C5[8] (08002)[4] HY011[5] or [7]

[1] The Attribute argument was not SQL_ATTR_TRANSLATE_LIB or SQL_ATTR_TRANSLATE_OPTION.

[2] The Attribute argument was SQL_ATTR_TRANSLATE_LIB or SQL_ATTR_TRANSLATE_OPTION.

[3] The Attribute argument was not SQL_ATTR_ODBC_CURSORS or SQL_ATTR_PACKET_SIZE.

[4] The Attribute argument was SQL_ATTR_ODBC_CURSORS.

[5] The Attribute argument was SQL_ATTR_PACKET_SIZE.

[6] The Attribute argument was not SQL_ATTR_AUTOCOMMIT, or the Attribute argument was SQL_ATTR_AUTOCOMMIT and setting this attribute did not commit the transaction.

[7] The Attribute argument was SQL_ATTR_TXN_ISOLATION.

[8] The Attribute argument was SQL_ATTR_AUTOCOMMIT, and setting this attribute committed the transaction.

SQLSetEnvAttr

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | -- | -- | (HY010) | -- | -- | --

SQLSpecialColumns: See SQLColumnPrivileges

SQLStatistics: See SQLColumnPrivileges

SQLTablePrivileges: See SQLColumnPrivileges

SQLTables: See SQLColumnPrivileges

All Other ODBC Functions

C0 No Env. | C1 Unallocated | C2 Allocated | C3 Need Data | C4 Connected | C5 Statement | C6 Transaction
(IH) | (IH) | (IH) | (IH) | (IH) | -- | --


Statement Transitions

ODBC statements have the following states.

State | Description
S0 | Unallocated statement. (The connection state must be C4, C5, or C6. For more information, see "Connection Transitions," earlier in this appendix.)
S1 | Allocated statement.
S2 | Prepared statement. No result set will be created.
S3 | Prepared statement. A (possibly empty) result set will be created.
S4 | Statement executed and no result set was created.
S5 | Statement executed and a (possibly empty) result set was created. The cursor is open and positioned before the first row of the result set.
S6 | Cursor positioned with SQLFetch or SQLFetchScroll.
S7 | Cursor positioned with SQLExtendedFetch.
S8 | Function needs data. SQLParamData has not been called.
S9 | Function needs data. SQLPutData has not been called.
S10 | Function needs data. SQLPutData has been called.
S11 | Still executing. A statement is left in this state after a function that is executed asynchronously returns SQL_STILL_EXECUTING. A statement is temporarily in this state while any function that accepts a statement handle is executing. Temporary residence in state S11 is not shown in any state tables except the state table for SQLCancel. While a statement is temporarily in state S11, the function can be canceled by calling SQLCancel from another thread.
S12 | Asynchronous execution canceled. In S12, an application must call the canceled function until it returns a value other than SQL_STILL_EXECUTING. The function was canceled successfully only if the function returns SQL_ERROR and SQLSTATE HY008 (Operation canceled). If it returns any other value, such as SQL_SUCCESS, the cancel operation failed and the function executed normally.

States S2 and S3 are known as the prepared states, states S5 through S7 as the cursor states, states S8 through S10 as the need data states, and states S11 and S12 as the asynchronous states. In each of these groups, the transitions are shown separately only when they are different for each state in the group; in most cases, the transitions for each state in each a group are the same.

The following tables show how each ODBC function affects the statement state.

SQLAllocHandle

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
--[1], [5], [6] | --[5] | --[5] | --[5] | --[5] | --[5] | --[5]
--[2], [5] | --[5] | --[5] | --[5] | --[5] | --[5] | --[5]
S1[3] | --[5] | --[5] | --[5] | --[5] | --[5] | --[5]
--[4], [5] | --[5] | --[5] | --[5] | --[5] | --[5] | --[5]

[1] This row shows transitions when HandleType was SQL_HANDLE_ENV.

[2] This row shows transitions when HandleType was SQL_HANDLE_DBC.

[3] This row shows transitions when HandleType was SQL_HANDLE_STMT.

[4] This row shows transitions when HandleType was SQL_HANDLE_DESC.

[5] Calling SQLAllocHandle with OutputHandlePtr pointing to a valid handle overwrites that handle without regard for the previous contents to that handle and might cause problems for ODBC drivers. It is incorrect ODBC application programming to call SQLAllocHandle twice with the same application variable defined for *OutputHandlePtr without calling SQLFreeHandle to free the handle before reallocating it. Overwriting ODBC handles in such a manner might lead to inconsistent behavior or errors on the part of ODBC drivers.

SQLBindCol

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | -- | -- | -- | -- | (HY010) | (HY010)

SQLBindParameter

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | -- | -- | -- | -- | (HY010) | (HY010)

SQLBrowseConnect, SQLConnect, and SQLDriverConnect

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(08002) | (08002) | (08002) | (08002) | (08002) | (08002) | (08002)

SQLBulkOperations

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | (HY010) | (HY010) | 24000 | See next table | (HY010) | NS [c] (HY010) o

SQLBulkOperations (Cursor States)

S5 Opened | S6 SQLFetch or SQLFetchScroll | S7 SQLExtendedFetch
-- [s] S8 [d] S11 [x] | -- [s] S8 [d] S11 [x] | (HY010)

SQLCancel

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | -- | -- | -- | -- | S1[1] S2 [nr] and [2] S3 [r] and [2] S5[3] and [5] S6([3] or [4]) and [6] S7[4] and [7] | See next table

[1] SQLExecDirect returned SQL_NEED_DATA.

[2] SQLExecute returned SQL_NEED_DATA.

[3] SQLBulkOperations returned SQL_NEED_DATA.

[4] SQLSetPos returned SQL_NEED_DATA.

[5] SQLFetch, SQLFetchScroll, or SQLExtendedFetch had not been called.

[6] SQLFetch or SQLFetchScroll had been called.

[7] SQLExtendedFetch had been called.

SQLCancel (Asynchronous States)

S11 Still executing | S12 Asynch canceled
NS[1] S12[2] | S12

[1] The statement was temporarily in state S11 while a function was executing. SQLCancel was called from a different thread.

[2] The statement was in state S11 because a function called asynchronously returned SQL_STILL_EXECUTING.

SQLCloseCursor

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | 24000 | 24000 | 24000 | S1 [np] S3 [p] | (HY010) | (HY010)

SQLColAttribute

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | (HY010) | See next table | 24000 | -- [s] S11 [x] | (HY010) | NS [c] (HY010) o

SQLColAttribute (Prepared States)

S2 No Results | S3 Results
--[1] 07005[2] | -- [s] S11 x

[1] FieldIdentifier was SQL_DESC_COUNT.

[2] FieldIdentifier was not SQL_DESC_COUNT.

SQLColumnPrivileges, SQLColumns, SQLForeignKeys, SQLGetTypeInfo, SQLPrimaryKeys, SQLProcedureColumns, SQLProcedures, SQLSpecialColumns, SQLStatistics, SQLTablePrivileges, and SQLTables

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | S5 [s] S11 [x] | S1 [e] S5 [s] S11 [x] | S1 [e] and [1] S5 [s] and [1] S11 [x] and [1] 24000[2] | See next table | (HY010) | NS [c] (HY010) o

[1] The current result is the last or only result, or there are no current results. For more information about multiple results, see "Multiple Results" in Chapter 11: Retrieving Results (Advanced).

[2] The current result is not the last result.

SQLColumnPrivileges, SQLColumns, SQLForeignKeys, SQLGetTypeInfo, SQLPrimaryKeys, SQLProcedureColumns, SQLProcedures, SQLSpecialColumns, SQLStatistics, SQLTablePrivileges, and SQLTables (Cursor States)

S5 Opened | S6 SQLFetch or SQLFetchScroll | S7 SQLExtendedFetch
24000 | (24000)[1] | (24000)

[1] This error is returned by the Driver Manager if SQLFetch or SQLFetchScroll has not returned SQL_NO_DATA and is returned by the driver if SQLFetch or SQLFetchScroll has returned SQL_NO_DATA.

SQLColumns: See SQLColumnPrivileges

SQLConnect: See SQLBrowseConnect

SQLCopyDesc

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH)[1] | -- | -- | -- | -- | (HY010) | NS [c] and [3] (HY010) [o] or [4]
(IH)[2] | (HY010) | See next table | 24000 | -- [s] S11 x | (HY010) | NS [c] and [3] (HY010) [o] or [4]

[1] This row shows transitions when the SourceDescHandle argument was an ARD, APD, or IPD.

[2] This row shows transitions when the SourceDescHandle argument was an IRD.

[3] The SourceDescHandle and TargetDescHandle arguments were the same as in the SQLCopyDesc function that is running asynchronously.

[4] Either the SourceDescHandle argument or the TargetDescHandle argument (or both) were different than in the SQLCopyDesc function that is running asynchronously.

SQLCopyDesc (Prepared States)

S2 No Results | S3 Results
24000[1] | -- [s] S11 [x]

[1] This row shows transitions when the SourceDescHandle argument was an IRD.

SQLDataSources and SQLDrivers

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
-- | -- | -- | -- | -- | -- | --

SQLDescribeCol

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | (HY010) | See next table | 24000 | -- [s] S11 [x] | (HY010) | NS [c] (HY010) o

SQLDescribeCol (Prepared States)

S2 No Results | S3 Results
07005 | -- [s] S11 [x]

SQLDescribeParam

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | (HY010) | -- [s] S11 [x] | (HY010) | (HY010) | (HY010) | NS [c] (HY010) [o]

SQLDisconnect

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
--[1] | S0[1] | S0[1] | S0[1] | S0[1] | (HY010) | (HY010)

[1] Calling SQLDisconnect frees all statements associated with the connection. Furthermore, this returns the connection state to C2; the connection state must be C4 before the statement state is S0.

SQLDriverConnect: See SQLBrowseConnect

SQLDrivers: See SQLDataSources

SQLEndTran

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
-- | -- | --[2] or [3] S1[1] | --[3] S1 [np] and ([1] or [2]) S1 [p] and [1] S2 [p] and [2] | --[3] S1 [np] and ([1] or [2]) S1 [p] and [1] S3 [p] and [2] | (HY010) | (HY010)

[1] The CompletionType argument is SQL_COMMIT and SQLGetInfo returns SQL_CB_DELETE for the SQL_CURSOR_COMMIT_BEHAVIOR information type, or the CompletionType argument is SQL_ROLLBACK and SQLGetInfo returns SQL_CB_DELETE for the SQL_CURSOR_ROLLBACK_BEHAVIOR information type.

[2] The CompletionType argument is SQL_COMMIT and SQLGetInfo returns SQL_CB_CLOSE for the SQL_CURSOR_COMMIT_BEHAVIOR information type, or the CompletionType argument is SQL_ROLLBACK and SQLGetInfo returns SQL_CB_CLOSE for the SQL_CURSOR_ROLLBACK_BEHAVIOR information type.

[3] The CompletionType argument is SQL_COMMIT and SQLGetInfo returns SQL_CB_PRESERVE for the SQL_CURSOR_COMMIT_BEHAVIOR information type, or the CompletionType argument is SQL_ROLLBACK and SQLGetInfo returns SQL_CB_PRESERVE for the SQL_CURSOR_ROLLBACK_BEHAVIOR information type.

SQLExecDirect

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | S4 [s] and [nr] S5 [s] and [r] S8 [d] S11 [x] | -- [e] and [1] S1 [e] and [2] S4 [s] and [nr] S5 [s] and [r] S8 [d] S11 [x] | -- [e], [1], and [3] S1 [e], [2], and [3] S4 [s], [nr], and [3] S5 [s], [r], and [3] S8 [d] and [3] S11 [x] and [3] 24000 [4] | See next table | (HY010) | NS [c] (HY010) [o]

[1] The error was returned by the Driver Manager.

[2] The error was not returned by the Driver Manager.

[3] The current result is the last or only result, or there are no current results. For more information about multiple results, see "Multiple Results" in Chapter 11: Retrieving Results (Advanced).

[4] The current result is not the last result.

SQLExecDirect (Cursor States)

S5 Opened | S6 SQLFetch or SQLFetchScroll | S7 SQLExtendedFetch
24000 | (24000) [1] | (24000)

[1] This error is returned by the Driver Manager if SQLFetch or SQLFetchScroll has not returned SQL_NO_DATA, and is returned by the driver if SQLFetch or SQLFetchScroll has returned SQL_NO_DATA.

SQLExecute

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | (HY010) | See next table | S2 [e], p, and [1] S4 [s], [p], [nr], and [1] S5 [s], [p], [r], and [1] S8 [d], [p], and [1] S11 [x], [p], and [1] 24000 [p] and [2] (HY010) [np] | See cursor states table | (HY010) | NS [c] (HY010) [o]

[1] The current result is the last or only result, or there are no current results. For more information about multiple results, see "Multiple Results" in Chapter 11: Retrieving Results (Advanced).

[2] The current result is not the last result.

SQLExecute (Prepared States)

S2 No Results | S3 Results
S4 [s] S8 [d] S11 [x] | S5 [s] S8 [d] S11 [x]

SQLExecute (Cursor States)

S5 Opened | S6 SQLFetch or SQLFetchScroll | S7 SQLExtendedFetch
24000 [p] (HY010) [np] | (24000) [p], [1] (HY010) [np] | (24000) [p] (HY010) [np]

[1] This error is returned by the Driver Manager if SQLFetch or SQLFetchScroll has not returned SQL_NO_DATA, and is returned by the driver if SQLFetch or SQLFetchScroll has returned SQL_NO_DATA.

SQLExtendedFetch

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | (S1010) | (S1010) | 24000 | See next table | (S1010) | NS [c] (S1010) [o]

SQLExtendedFetch (Cursor States)

S5 Opened | S6 SQLFetch or SQLFetchScroll | S7 SQLExtendedFetch
S7 [s] or [nf] S11 [x] | (S1010) | -- [s] or [nf] S11 [x]

SQLFetch and SQLFetchScroll

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | (HY010) | (HY010) | 24000 | See next table | (HY010) | NS [c] (HY010) [o]

SQLFetch and SQLFetchScroll (Cursor states)

S5 Opened | S6 SQLFetch or SQLFetchScroll | S7 SQLExtendedFetch
S6 [s] or [nf] S11 [x] | -- [s] or [nf] S11 [x] | (HY010)

SQLForeignKeys: See SQLColumnPrivileges

SQLFreeHandle

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
-- [1] | (HY010) | (HY010) | (HY010) | (HY010) | (HY010) | (HY010)
(IH) [2] | S0 | S0 | S0 | S0 | (HY010) | (HY010)
-- [3] | -- | -- | -- | -- | -- | --

[1] This row shows transitions when HandleType was SQL_HANDLE_ENV or SQL_HANDLE_DBC.

[2] This row shows transitions when HandleType was SQL_HANDLE_STMT.

[3] This row shows transitions when HandleType was SQL_HANDLE_DESC and the descriptor was explicitly allocated.

SQLFreeStmt

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) [1] | -- | -- | S1 [np] S2 [p] | S1 [np] S3 [p] | (HY010) | (HY010)
(IH) [2] | -- | -- | -- | -- | (HY010) | (HY010)

[1] This row shows transitions when Option was SQL_CLOSE.

[2] This row shows transitions when Option was SQL_UNBIND or SQL_RESET_PARAMS. If the Option argument was SQL_DROP and the underlying driver is an ODBC 3.x driver, the Driver Manager maps this to a call to SQLFreeHandle with HandleType set to SQL_HANDLE_STMT. For more information, see the transition table for SQLFreeHandle.

SQLGetConnectAttr

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
-- | -- | -- | -- | -- | -- | --

SQLGetCursorName

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | -- | -- | -- | -- | (HY010) | (HY010)

SQLGetData

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | (HY010) | (HY010) | (24000) | See next table | (HY010) | NS [c] (HY010) [o]

SQLGetData (Cursor States)

S5 Opened | S6 SQLFetch or SQLFetchScroll | S7 SQLExtendedFetch
(24000) | -- [s] or [nf] S11 [x] 24000 [b] HY109 [i] | -- [s] or [nf] S11 [x] 24000 [b] HY109 [i]

SQLGetDescField and SQLGetDescRec

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | -- [1] or [2] (HY010) [3] | See next table | -- [1] or [2] 24000 [3] | -- [1], [2], or [3] S11 [3] and [x] | (HY010) | NS [c] or [4] (HY010) [o] and [5]

[1] The DescriptorHandle argument was an APD or ARD.

[2] The DescriptorHandle argument was an IPD.

[3] The DescriptorHandle argument was an IRD.

[4] The DescriptorHandle argument was the same as the DescriptorHandle argument in the SQLGetDescField or SQLGetDescRec function that is running asynchronously.

[5] The DescriptorHandle argument was different than the DescriptorHandle argument in the SQLGetDescField or SQLGetDescRec function that is running asynchronously.

SQLGetDescField and SQLGetDescRec (Prepared States)

S2 No Results | S3 Results
--[1], [2], or [3] S11[2] and [x] | --[1], [2], or [3] S11 [x]

[1] The DescriptorHandle argument was an APD or ARD.

[2] The DescriptorHandle argument was an IPD.

[3] The DescriptorHandle argument was an IRD. Note that these functions always return SQL_NO_DATA in state S2 when DescriptorHandle was an IRD.

SQLGetDiagField and SQLGetDiagRec

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
--[1] | -- | -- | -- | -- | -- | --
(IH)[2] | --[3] | --[3] | -- | -- | --[3] | --[3]

[1] This row shows transitions when HandleType was SQL_HANDLE_ENV, SQL_HANDLE_DBC, or SQL_HANDLE_DESC.

[2] This row shows transitions when HandleType was SQL_HANDLE_STMT.

[3] SQLGetDiagField always returns an error in this state when DiagIdentifier is SQL_DIAG_ROW_COUNT.

SQLGetEnvAttr

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
-- | -- | -- | -- | -- | -- | --

SQLGetFunctions

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
-- | -- | -- | -- | -- | -- | --

SQLGetInfo

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
-- | -- | -- | -- | -- | -- | --

SQLGetStmtAttr

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | --[1] (24000)[2] | --[1] (24000)[2] | --[1] (24000)[2] | See next table | (HY010) | (HY010)

[1] The statement attribute was not SQL_ATTR_ROW_NUMBER.

[2] The statement attribute was SQL_ATTR_ROW_NUMBER.

SQLGetStmtAttr (Cursor States)

S5 Opened | S6 SQLFetch or SQLFetchScroll | S7 SQLExtendedFetch
--[1] (24000)[2] | --[1] or ([v] and [2]) 24000 [b] and [2] HY109 [i] and [2] | -- [i] or ([v] and [2]) 24000 [b] and [2] HY109[1] and [2]

[1] The Attribute argument was not SQL_ATTR_ROW_NUMBER.

[2] The Attribute argument was SQL_ATTR_ROW_NUMBER.

SQLGetTypeInfo: See SQLColumnPrivileges

SQLMoreResults

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | --[1] | --[1] | -- [s] and [2] S1 [nf], [np], and [4] S2 [nf], [p], and [4] S5 [s] and [3] S11 [x] | S1 [nf], [np], and [4] S3 [nf], [p] and [4] S4 [s] and [2] S5 [s] and [3] S11 [x] | (HY010) | NS [c] (HY010) [o]

[1] The function always returns SQL_NO_DATA in this state.

[2] The next result is a row count.

[3] The next result is a result set.

[4] The current result is the last result.

SQLNativeSql

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
-- | -- | -- | -- | -- | -- | --

SQLNumParams

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | (HY010) | -- [s] S11 [x] | -- [s] S11 [x] | -- [s] S11 [x] | (HY010) | NS [c] (HY010) [o]

SQLNumResultCols

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | (HY010) | -- [s] S11 [x] | -- [s] S11 [x] | -- [s] S11 [x] | (HY010) | NS [c] (HY010) [o]

SQLParamData

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | (HY010) | (HY010) | (HY010) | (HY010) | See next table | NS [c] (HY010) [o]

SQLParamData (Need Data States)

S8 Need Data | S9 Must Put | S10 Can Put
S1 [e] and [1] S2 [e], [nr], and [2] S3 [e], [r], and [2] S5 [e] and [4] S6 [e] and [5] S7 [e] and [3] S9 [d] S11 [x] | HY010 | S1 [e] and [1] S2 [e], [nr], and [2] S3 [e], [r], and [2] S4 [s], [nr], and ([1] or [2]) S5 [s], [r], and ([1] or [2]) S5 ([s] or [e]) and [4] S6 ([s] or [e]) and [5] S7 ([s] or [e]) and [3] S9 [d] S11 [x]

[1] SQLExecDirect returned SQL_NEED_DATA.

[2] SQLExecute returned SQL_NEED_DATA.

[3] SQLSetPos had been called from state S7 and returned SQL_NEED_DATA.

[4] SQLBulkOperations had been called from state S5 and returned SQL_NEED_DATA.

[5] SQLSetPos or SQLBulkOperations had been called from state S6 and returned SQL_NEED_DATA.

SQLPrepare

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | S2 [s] and [nr] S3 [s] and [r] S11 [x] | -- [s] or ([e] and [1]) S1 [e] and [2] S11 [x] | S1 [e] and [3] S2 [s], [nr], and [3] S3 [s], [r], and [3] S11 [x] and [3] 24000[4] | See next table | (HY010) | NS [c] (HY010) [o]

[1] The preparation fails for a reason other than validating the statement (the SQLSTATE was HY009 [Invalid argument value] or HY090 [Invalid string or buffer length]).

[2] The preparation fails while validating the statement (the SQLSTATE was not HY009 [Invalid argument value] or HY090 [Invalid string or buffer length]).

[3] The current result is the last or only result, or there are no current results. For more information about multiple results, see "Multiple Results" in Chapter 11: Retrieving Results (Advanced).

[4] The current result is not the last result.

SQLPrepare (Cursor States)

S5 Opened | S6 SQLFetch or SQLFetchScroll | S7 SQLExtendedFetch
24000 | (24000) | (24000)

SQLPrimaryKeys: See SQLColumnPrivileges

SQLProcedureColumns: See SQLColumnPrivileges

SQLProcedures: See SQLColumnPrivileges

SQLPutData

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | (HY010) | (HY010) | (HY010) | (HY010) | See next table | NS [c] (HY010) [o]

SQLPutData (Need Data States)

S8 Need Data | S9 Must Put | S10 Can Put
HY010 | S1 [e] and [1] S2 [e], [nr], and [2] S3 [e], [r], and [2] S5 [e] and [4] S6 [e] and [5] S7 [e] and [3] S10 [s] S11 [x] | -- [s] S1 [e] and [1] S2 [e], [nr], and [2] S3 [e], [r], and [2] S5 [e] and [4] S6 [e] and [5] S7 [e] and [3] S11 [x] HY011[6]

[1] SQLExecDirect returned SQL_NEED_DATA.

[2] SQLExecute returned SQL_NEED_DATA.

[3] SQLSetPos had been called from state S7 and returned SQL_NEED_DATA.

[4] SQLBulkOperations had been called from state S5 and returned SQL_NEED_DATA.

[5] SQLSetPos or SQLBulkOperations had been called from state S6 and returned SQL_NEED_DATA.

[6] One or more calls to SQLPutData for a single parameter returned SQL_SUCCESS, and then a call to SQLPutData was made for the same parameter with StrLen_or_Ind set to SQL_NULL_DATA.

SQLRowCount

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | (HY010) | (HY010) | -- | -- | (HY010) | (HY010)

SQLSetConnectAttr

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
--[1] | -- | -- | -- | --[2] 24000[3] | (HY010) | (HY010)

[1] This row shows transitions when Attribute was a connection attribute. For transitions when Attribute was a statement attribute, see the statement transition table for SQLSetStmtAttr.

[2] The Attribute argument was not SQL_ATTR_CURRENT_CATALOG.

[3] The Attribute argument was SQL_ATTR_CURRENT_CATALOG.

SQLSetCursorName

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | -- | -- | (24000) | (24000) | (HY010) | (HY010)

SQLSetDescField and SQLSetDescRec

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH)[1] | -- | -- | -- | -- | (HY010) | (HY010)

[1] This row shows transitions where the DescriptorHandle argument is an ARD, APD, IPD, or (for SQLSetDescField) an IRD when the FieldIdentifier argument is SQL_DESC_ARRAY_STATUS_PTR or SQL_DESC_ROWS_PROCESSED_PTR. It is an error to call SQLSetDescField for an IRD when FieldIdentifier is any other value.

SQLSetEnvAttr

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(HY011) | (HY011) | (HY011) | (HY011) | (HY011) | (HY011) | (HY011)

SQLSetPos

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | (HY010) | (HY010) | (24000) | See next table | (HY010) | NS [c] (HY010) [o]

SQLSetPos (Cursor States)

S5 Opened | S6 SQLFetch or SQLFetchScroll | S7 SQLExtendedFetch
(24000) | -- [s] S8 [d] S11 [x] 24000 [b] HY109 [i] | -- [s] S8 [d] S11 [x] 24000 [b] HY109 [i]

SQLSetStmtAttr

S0 Unallocated | S1 Allocated | S2–S3 Prepared | S4 Executed | S5–S7 Cursor | S8–S10 Need Data | S11–S12 Async
(IH) | -- | --[1] (HY011)[2] | --[1] (24000)[2] | --[1] (24000)[2] | (HY010) [np] or [1] (HY011) [p] and [2] | (HY010) [np] or [1] (HY011) [p] and [2]

[1] The Attribute argument was not SQL_ATTR_CONCURRENCY, SQL_ATTR_CURSOR_TYPE, SQL_ATTR_SIMULATE_CURSOR, SQL_ATTR_USE_BOOKMARKS, SQL_ATTR_CURSOR_SCROLLABLE, or SQL_ATTR_CURSOR_SENSITIVITY.

[2] The Attribute argument was SQL_ATTR_CONCURRENCY, SQL_ATTR_CURSOR_TYPE, SQL_ATTR_SIMULATE_CURSOR, SQL_ATTR_USE_BOOKMARKS, SQL_ATTR_CURSOR_SCROLLABLE, or SQL_ATTR_CURSOR_SENSITIVITY.

SQLSpecialColumns: See SQLColumnPrivileges

SQLStatistics: See SQLColumnPrivileges

SQLTablePrivileges: See SQLColumnPrivileges

SQLTables: See SQLColumnPrivileges


Descriptor Transitions

ODBC descriptors have the following three states.

State | Description
D0 | Unallocated descriptor
D1i | Implicitly allocated descriptor
D1e | Explicitly allocated descriptor

The following tables show how each ODBC function affects the descriptor state.

SQLAllocHandle

D0 Unallocated | D1i Implicit | D1e Explicit
D1i[1] | -- | --
D1e[2] | -- | --

[1] This row shows transitions when HandleType was SQL_HANDLE_STMT.

[2] This row shows transitions when HandleType was SQL_HANDLE_DESC.

SQLCopyDesc

D0 Unallocated | D1i Implicit | D1e Explicit
(IH) | -- | --

SQLFreeHandle

D0 Unallocated | D1i Implicit | D1e Explicit
--[1] | D0 | --
(IH)[2] | (HY017) | D0

[1] This row shows transitions when HandleType was SQL_HANDLE_STMT.

[2] This row shows transitions when HandleType was SQL_HANDLE_DESC.

SQLGetDescField and SQLGetDescRec

D0 Unallocated | D1i Implicit | D1e Explicit
(IH) | -- | --

SQLSetDescField and SQLSetDescRec

D0 Unallocated | D1i Implicit | D1e Explicit
(IH)[1] | -- | --

[1] This row shows transitions when DescriptorHandle was the handle of an ARD, APD, or IPD, or (for SQLSetDescField) when DescriptorHandle was the handle of an IRD and FieldIdentifier was SQL_DESC_ARRAY_STATUS_PTR or SQL_DESC_ROWS_PROCESSED_PTR.

All Other ODBC Functions

D0 Unallocated | D1i Implicit | D1e Explicit
-- | -- | --


Appendix C: SQL Grammar

This appendix includes the following sections:

  • SQL Minimum Grammar—Describes the minimum SQL syntax that an ODBC driver must support.
  • ODBC Escape Sequences—Provides syntax for the escape sequences that are used in ODBC.
  • Literals in ODBC—Provides syntax for interval and numeric literals in ODBC.
  • List of Reserved Keywords—Lists the keywords that are reserved for use in ODBC function calls.


SQL Minimum Grammar

This section describes the minimum SQL syntax that an ODBC driver must support. The syntax described in this section is a subset of the Entry level syntax of SQL-92.

An application can use any of the syntax in this section and be assured that any ODBC-compliant driver will support that syntax. To determine whether additional features of SQL-92 not in this section are supported, the application should call SQLGetInfo with the SQL_SQL_CONFORMANCE information type. Even if the driver does not conform to any SQL-92 conformance level, an application can still use the syntax described in this section. If a driver conforms to an SQL-92 level, on the other hand, it supports all syntax included in that level. This includes the syntax in this section because the minimum grammar described here is a pure subset of the lowest SQL-92 conformance level. Once the application knows the SQL-92 level supported, it can determine whether a higher-level feature is supported (if any) by calling SQLGetInfo with the individual information type corresponding to that feature.

Drivers that work only with read-only data sources might not support those parts of the grammar included in this section that deal with changing data. An application can determine if a data source is read-only by calling SQLGetInfo with the SQL_DATA_SOURCE_READ_ONLY information type.

Statement

create-table-statement ::=
CREATE TABLE base-table-name
(column-identifier data-type [,column-identifier data-type]…)

Important As a data-type in a create-table-statement, applications must use a data type from the TYPE_NAME column of the result set returned by SQLGetTypeInfo.

delete-statement-searched ::=
DELETE FROM table-name [WHERE search-condition]

drop-table-statement ::=
DROP TABLE base-table-name

insert-statement ::=
INSERT INTO table-name [( column-identifier [, column-identifier]...)]
VALUES (insert-value[, insert-value]... )

select-statement ::=
SELECT [ALL | DISTINCT] select-list
FROM table-reference-list
[WHERE search-condition]
[order-by-clause]

statement ::= create-table-statement
| delete-statement-searched
| drop-table-statement
| insert-statement
| select-statement
| update-statement-searched

update-statement-searched
UPDATE table-name
SET column-identifier = {expression | NULL }
[, column-identifier = {expression | NULL}]...
[WHERE search-condition]


Elements Used in SQL Statements

The following elements are used in the SQL statements listed previously.

Element

base-table-identifier ::= user-defined-name

base-table-name ::= base-table-identifier

boolean-factor ::= [NOT] boolean-primary

boolean-primary ::= comparison-predicate | ( search-condition )

boolean-term ::= boolean-factor [AND boolean-term]

character-string-literal ::= ''{character}…''
(character is any character in the character set of the driver/data source. To include a single literal quote character ('') in a character-string-literal, use two literal quote characters [''''].)

column-identifier ::= user-defined-name

column-name ::= [table-name.]column-identifier

comparison-operator ::= < | > | <= | >= | = | <>

comparison-predicate ::= expression comparison-operator expression

data-type ::= character-string-type
(character-string-type is any data type for which the ""DATA_TYPE"" column in the result set returned by SQLGetTypeInfo is either SQL_CHAR or SQL_VARCHAR.)

digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

dynamic-parameter ::= ?

expression ::= term | expression {+|–} term

factor ::= [+|–]primary

insert-value ::=
dynamic-parameter
| literal
| NULL
| USER

letter ::= lower-case-letter | upper-case-letter

literal ::= character-string-literal

lower-case-letter ::= a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | u | v | w | x | y | z

order-by-clause ::= ORDER BY sort-specification [, sort-specification]...

primary ::= column-name
| dynamic-parameter
| literal
| ( expression )

search-condition ::= boolean-term [OR search-condition]

select-list ::= * | select-sublist [, select-sublist]...
(select-list cannot contain parameters.)

select-sublist ::= expression

sort-specification ::= {unsigned-integer | column-name} [ASC | DESC]

table-identifier ::= user-defined-name

table-name ::= table-identifier

table-reference ::= table-name

table-reference-list ::= table-reference [,table-reference]…

term ::= factor | term {*|/} factor

unsigned-integer ::= {digit}

upper-case-letter ::= A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

user-defined-name ::= letter[digit | letter | _]...


Data Type Support

ODBC drivers must support at least one of SQL_CHAR and SQL_VARCHAR. Support for other data types is determined by the driver's or data source's SQL-92 conformance level. An application should call SQLGetTypeInfo to determine the data types supported by the driver.

For more information on data types, see Appendix D: Data Types.


Parameter Data Types

Even though each parameter specified with SQLBindParameter is defined using an SQL data type, the parameters in an SQL statement have no intrinsic data type. Therefore, parameter markers can be included in an SQL statement only if their data types can be inferred from another operand in the statement. For example, in an arithmetic expression such as ? + COLUMN1, the data type of the parameter can be inferred from the data type of the named column represented by COLUMN1. An application cannot use a parameter marker if the data type cannot be determined.

The following table describes how a data type is determined for several types of parameters, in accordance with SQL-92. For a more comprehensive specification on inferring the parameter type when other SQL clauses are used, see the SQL-92 specification.

Location of parameter | Assumed data type
One operand of a binary arithmetic or comparison operator | Same as the other operand
The first operand in a BETWEEN clause | Same as the second operand
The second or third operand in a BETWEEN clause | Same as the first operand
An expression used with IN | Same as the first value or the result column of the subquery
A value used with IN | Same as the expression or the first value if there is a parameter marker in the expression
A pattern value used with LIKE | VARCHAR
An update value used with UPDATE | Same as the update column


Parameter Markers

In accordance with the SQL-92 specification, an application cannot place parameter markers in the following locations. For a more comprehensive list, see the SQL-92 specification.

  • In a SELECT list
  • As both expressions in a comparison-predicate
  • As both operands of a binary operator
  • As both the first and second operands of a BETWEEN operation
  • As both the first and third operands of a BETWEEN operation
  • As both the expression and the first value of an IN operation
  • As the operand of a unary + or – operation
  • As the argument of a set-function-reference

For more information about parameter markers, see the SQL-92 specification. For more information about parameters, see "Statement Parameters" in Chapter 9: Executing Statements.


ODBC Escape Sequences

The following escape sequences are used in ODBC. The grammar in this section uses some elements defined in the "Elements Used in SQL Statements" section.


Date, Time, and Timestamp Escape Sequences

ODBC defines escape sequences for date, time, and timestamp literals. The syntax of these escape sequences is as follows:

{d 'value'}
{t 'value'}
{ts 'value'}

In BNF notation, the syntax is as follows:

ODBC-date-time-escape ::=
     ODBC-date-escape
     | ODBC-time-escape
     | ODBC-timestamp-escape
ODBC-date-escape ::=
     ODBC-esc-initiator d 'date-value' ODBC-esc-terminator
ODBC-time-escape ::=
     ODBC-esc-initiator t 'time-value' ODBC-esc-terminator
ODBC-timestamp-escape ::=
     ODBC-esc-initiator ts 'timestamp-value' ODBC-esc-terminator
ODBC-esc-initiator ::= {
ODBC-esc-terminator ::= }
date-value ::=
     years-value date-separator months-value date-separator days-value
time-value ::=
     hours-value time-separator minutes-value time-separator seconds-value
timestamp-value ::= date-value timestamp-separator time-value
date-separator ::= -
time-separator ::= :
timestamp-separator ::=
     (The blank character)
years-value ::= digit digit digit digit
months-value ::= digit digit
days-value ::= digit digit
hours-value ::= digit digit
minutes-value ::= digit digit
seconds-value ::= digit digit[.digit…]

The date, time, and timestamp literal escape sequences are supported if the date, time, and timestamp data types are supported by the data source. An application should call SQLGetTypeInfo to determine whether these data types are supported.


GUID Escape Sequences

ODBC uses escape sequences for GUID literals. The syntax of this escape sequence is as follows:

{guid 'nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn'}

In BNF notation, the syntax is as follows:

ODBC-guid-escape ::=
 ODBC-esc-initiator guid 'guid-value' ODBC-esc-terminator

ODBC-esc-initiator ::= {

ODBC-esc-terminator ::= }

guid-value ::= clock-low-value guid-separator clock-middle-value guid-separator clock-high-value guid-separator clock-seq-value guid-separator node-value

guid-separator ::= -

clock-low-value ::= hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit

clock-middle-value ::= hex_digit hex_digit hex_digit hex_digit

clock-high-value ::= hex_digit hex_digit hex_digit hex_digit

clock-seq-value ::= hex_digit hex_digit hex_digit hex_digit

clock-node-value ::= hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit

hex_digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F

The GUID literal escape sequence is supported if the GUID data type is supported by the data source. An application should call SQLGetTypeInfo to determine whether this data type is supported.


Interval Escape Sequences

ODBC uses escape sequences for interval literals. The syntax of this escape sequence is as follows:

{interval-literal}

For the BNF syntax of interval-literal, see the "Interval Literal Syntax" section later in this appendix.

The interval literal escape sequence is supported if the interval data types are supported by the data source. An application should call SQLGetTypeInfo to determine whether these data types are supported.


LIKE Escape Sequence

ODBC uses escape sequences for the LIKE clause. The syntax of this escape sequence is as follows:

{escape 'escape-character'}

In BNF notation, the syntax is as follows:

ODBC-like-escape ::=
ODBC-esc-initiator escape 'escape-character' ODBC-esc-terminator

escape-character ::= character

ODBC-esc-initiator ::= {

ODBC-esc-terminator ::= }

To determine if the driver supports the LIKE escape sequence, an application can call SQLGetInfo with the SQL_LIKE_ESCAPE_CLAUSE information type.


Outer Join Escape Sequence

ODBC uses escape sequences for outer joins. The syntax of this escape sequence is as follows:

{oj outer-join}

In BNF notation, the syntax is as follows:

ODBC-outer-join-escape ::=
ODBC-esc-initiator oj outer-join ODBC-esc-terminator

outer-join ::= table-name [correlation-name] {LEFT | RIGHT | FULL}
OUTER JOIN{table-name [correlation-name] | outer-join} ON search-
condition

correlation-name ::= user-defined-name

ODBC-esc-initiator ::= {

ODBC-esc-terminator ::= }

To determine which parts of this statement are supported, an application calls SQLGetInfo with the SQL_OJ_CAPABILITIES information type. For outer joins, search-condition must contain only the join condition between the specified table-names.


Procedure Call Escape Sequence

ODBC uses escape sequences for procedure calls. The syntax of this escape sequence is as follows:

{[?=]call procedure-name[([parameter][,[parameter]]...)]}

In BNF notation, the syntax is as follows:

ODBC-procedure-escape ::=
| ODBC-esc-initiator [?=] call procedure ODBC-esc-terminator

procedure ::= procedure-name | procedure-name (procedure-parameter-list)

procedure-identifier ::= user-defined-name

procedure-name ::= procedure-identifier
| owner-name.procedure-identifier
| catalog-name catalog-separator procedure-identifier
| catalog-name catalog-separator [owner-name].procedure-identifier
(The third syntax is valid only if the data source does not support owners.)

owner-name ::= user-defined-name

catalog-name ::= user-defined-name

catalog-separator ::= {implementation-defined}
(The catalog separator is returned through SQLGetInfo with the SQL_CATALOG_NAME_SEPARATOR information option.)

procedure-parameter-list ::= procedure-parameter
| procedure-parameter, procedure-parameter-list

procedure-parameter ::= dynamic-parameter | literal | empty-string

empty-string ::=

ODBC-esc-initiator ::= {

ODBC-esc-terminator ::= }
(If a procedure parameter is an empty string, the procedure uses the default value for that parameter.)

To determine whether the data source supports procedures and the driver supports the ODBC procedure invocation syntax, an application can call SQLGetInfo with the SQL_PROCEDURES information type.


Scalar Function Escape Sequence

ODBC uses escape sequences for scalar functions. The syntax of this escape sequence is as follows:

{fn scalar-function}

In BNF notation, the syntax is as follows:

ODBC-scalar-function-escape ::=
ODBC-esc-initiator fn scalar-function ODBC-esc-terminator

scalar-function ::= function-name (argument-list)
(The definitions for the nonterminals function-name and function-name (argument-list) are derived from the list of scalar functions in Appendix E: Scalar Functions.)

ODBC-esc-initiator ::= {

ODBC-esc-terminator ::= }

To determine whether the data source supports procedures and the driver supports the ODBC procedure invocation syntax, an application can call SQLGetInfo. For more information, see Appendix E: Scalar Functions.


Literals in ODBC

The syntax in the following sections is used for interval and numeric literals in ODBC. This syntax is provided here as an aid to driver writers when conversions are performed from a character string type to a numeric or interval type, or from a numeric or interval type to a character string type. For more information, see the "Interval Literals" and "Numeric Literals" sections in Appendix D: Data Types.


Interval Literal Syntax

The following syntax is used for interval literals in ODBC.

interval-literal ::= INTERVAL [+|-] interval-string interval-qualifier

interval-string ::= quote { year-month-literal | day-time-literal } quote

year-month-literal ::= years-value | [years-value -] months-value

day-time-literal ::= day-time-interval | time-interval

day-time-interval ::= days-value [hours-value [:minutes-value[:seconds-value]]]

time-interval ::= hours-value [:minutes-value [:seconds-value ] ]
| minutes-value [:seconds-value ]
| seconds-value

years-value ::= datetime-value

months-value ::= datetime-value

days-value ::= datetime-value

hours-value ::= datetime-value

minutes-value ::= datetime-value

seconds-value ::= seconds-integer-value [.[seconds-fraction] ]

seconds-integer-value ::= unsigned-integer

seconds-fraction ::= unsigned-integer

datetime-value ::= unsigned-integer

interval-qualifier ::= start-field TO end-field | single-datetime-field

start-field ::= non-second-datetime-field [(interval-leading-field-precision )]

end-field ::= non-second-datetime-field
| SECOND[(interval-fractional-seconds-precision)]

single-datetime-field ::= non-second-datetime-field [(interval-leading-field-precision)] | SECOND[(interval-leading-field-precision [, (interval-fractional-seconds-precision)]

datetime-field ::= non-second-datetime-field | SECOND

non-second-datetime-field ::= YEAR | MONTH | DAY | HOUR | MINUTE

interval-fractional-seconds-precision ::= unsigned-integer

interval-leading-field-precision ::= unsigned-integer

quote ::= '

unsigned-integer ::= digit…


Numeric Literal Syntax

The following syntax is used for numeric literals in ODBC:

numeric-literal ::= signed-numeric-literal | unsigned-numeric-literal

signed-numeric-literal ::= [sign] unsigned-numeric-literal

unsigned-numeric-literal ::= exact-numeric-literal | approximate-numeric-literal

exact-numeric-literal ::= unsigned-integer [period[unsigned-integer]] |
period unsigned-integer

sign ::= plus-sign | minus-sign

approximate-numeric-literal ::= mantissa E exponent

mantissa ::= exact-numeric-literal

exponent ::= signed-integer

signed-integer ::= [sign] unsigned-integer

unsigned-integer ::= digit...

plus-sign ::= +

minus-sign ::= -

digit ::= 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0

period ::= .


List of Reserved Keywords

The following words are reserved for use in ODBC function calls. These words do not constrain the minimum SQL grammar; however, to ensure compatibility with drivers that support the core SQL grammar, applications should avoid using any of these keywords. The #define value SQL_ODBC_KEYWORDS contains a comma-separated list of these keywords.

ABSOLUTE | IS
ACTION | ISOLATION
ADA | JOIN
ADD | KEY
ALL | LANGUAGE
ALLOCATE | LAST
ALTER | LEADING
AND | LEFT
ANY | LEVEL
ARE | LIKE
AS | LOCAL
ASC | LOWER
ASSERTION | MATCH
AT | MAX
AUTHORIZATION | MIN
AVG | MINUTE
BEGIN | MODULE
BETWEEN | MONTH
BIT | NAMES
BIT_LENGTH | NATIONAL
BOTH | NATURAL
BY | NCHAR
CASCADE | NEXT
CASCADED | NO
CASE | NONE
CAST | NOT
CATALOG | NULL
CHAR | NULLIF
CHAR_LENGTH | NUMERIC
CHARACTER | OCTET_LENGTH
CHARACTER_LENGTH | OF
CHECK | ON
CLOSE | ONLY
COALESCE | OPEN
COLLATE | OPTION
COLLATION | OR
COLUMN | ORDER
COMMIT | OUTER
CONNECT | OUTPUT
CONNECTION | OVERLAPS
CONSTRAINT | PAD
CONSTRAINTS | PARTIAL
CONTINUE | PASCAL
CONVERT | POSITION
CORRESPONDING | PRECISION
COUNT | PREPARE
CREATE | PRESERVE
CROSS | PRIMARY
CURRENT | PRIOR
CURRENT_DATE | PRIVILEGES
CURRENT_TIME | PROCEDURE
CURRENT_TIMESTAMP | PUBLIC
CURRENT_USER | READ
CURSOR | REAL
DATE | REFERENCES
DAY | RELATIVE
DEALLOCATE | RESTRICT
DEC | REVOKE
DECIMAL | RIGHT
DECLARE | ROLLBACK
DEFAULT | ROWS
DEFERRABLE | SCHEMA
DEFERRED | SCROLL
DELETE | SECOND
DESC | SECTION
DESCRIBE | SELECT
DESCRIPTOR | SESSION
DIAGNOSTICS | SESSION_USER
DISCONNECT | SET
DISTINCT | SIZE
DOMAIN | SMALLINT
DOUBLE | SOME
DROP | SPACE
ELSE | SQL
END | SQLCA
END-EXEC | SQLCODE
ESCAPE | SQLERROR
EXCEPT | SQLSTATE
EXCEPTION | SQLWARNING
EXEC | SUBSTRING
EXECUTE | SUM
EXISTS | SYSTEM_USER
EXTERNAL | TABLE
EXTRACT | TEMPORARY
FALSE | THEN
FETCH | TIME
FIRST | TIMESTAMP
FLOAT | TIMEZONE_HOUR
FOR | TIMEZONE_MINUTE
FOREIGN | TO
FORTRAN | TRAILING
FOUND | TRANSACTION
FROM | TRANSLATE
FULL | TRANSLATION
GET | TRIM
GLOBAL | TRUE
GO | UNION
GOTO | UNIQUE
GRANT | UNKNOWN
GROUP | UPDATE
HAVING | UPPER
HOUR | USAGE
IDENTITY | USER
IMMEDIATE | USING
IN | VALUE
INCLUDE | VALUES
INDEX | VARCHAR
INDICATOR | VARYING
INITIALLY | VIEW
INNER | WHEN
INPUT | WHENEVER
INSENSITIVE | WHERE
INSERT | WITH
INT | WORK
INTEGER | WRITE
INTERSECT | YEAR
INTERVAL | ZONE
INTO |


Appendix D: Data Types

ODBC defines two sets of data types: SQL data types and C data types. SQL data types indicate the data type of data stored at the data source. C data types indicate the data type of data stored in application buffers.

SQL data types are defined by each DBMS in accordance with the SQL-92 standard. For each SQL data type specified in the SQL-92 standard, ODBC defines a type identifier, which is a #define value that is passed as an argument in ODBC functions or returned in the metadata of a result set. The only SQL-92 data types not supported by ODBC are BIT (the ODBC SQL_BIT type has different characteristics), BIT_VARYING, TIME_WITH_TIMEZONE, TIMESTAMP_WITH_TIMEZONE, and NATIONAL_CHARACTER. Drivers are responsible for mapping data source–specific SQL data types to ODBC SQL data type identifiers and driver-specific SQL data type identifiers. The SQL data type is specified in the SQL_DESC_CONCISE_TYPE field of an implementation descriptor.

ODBC defines the C data types and their corresponding ODBC type identifiers. An application specifies the C data type of the buffer that will receive result set data by passing the appropriate C type identifier in the TargetType argument in a call to SQLBindCol or SQLGetData. It specifies the C type of the buffer containing a statement parameter by passing the appropriate C type identifier in the ValueType argument in a call to SQLBindParameter. The C data type is specified in the SQL_DESC_CONCISE_TYPE field of an application descriptor.

Note There are no driver-specific C data types.

Each SQL data type corresponds to an ODBC C data type. Before returning data from the data source, the driver converts it to the specified C data type. Before sending data to the data source, the driver converts it from the specified C data type.

This appendix includes the following sections:

  • Using Data Type Identifiers
  • SQL Data Types
  • C Data Types
  • Data Type Identifiers and Descriptors
  • Pseudo-Type Identifiers
  • Transferring Data in Its Binary Form
  • Guidelines for Interval and Numeric Data Types
  • Constraints of the Gregorian Calendar
  • Column Size, Decimal Digits, Transfer Octet Length, and Display Size
  • Converting Data from SQL to C Data Types
  • Converting Data from C to SQL Data Types

For an explanation of ODBC data types, see "Data Types in ODBC" in Chapter 4: ODBC Fundamentals. For information about driver-specific SQL data types, see the driver's documentation.


Using Data Type Identifiers

Applications use data type identifiers in two ways: to describe their buffers to the driver, and to retrieve metadata about the result set from the driver so that they can determine what type of C buffers to use to store the data. Applications call the following functions to perform these tasks:

  • SQLBindParameter, SQLBindCol, and SQLGetData—to describe the C data type of application buffers.
  • SQLBindParameter—to describe the SQL data type of dynamic parameters.
  • SQLColAttribute and SQLDescribeCol—to retrieve the SQL data types of result set columns.
  • SQLDescribeParameter—to retrieve the SQL data types of parameters.
  • SQLColumns, SQLProcedureColumns, and SQLSpecialColumns—to retrieve the SQL data types of various schema information
  • SQLGetTypeInfo—to retrieve a list of supported data types

Data type identifiers are stored in the SQL_DESC_CONCISE_TYPE field of a descriptor. The descriptor functions SQLSetDescField and SQLSetDescRec can be used with the appropriate types to perform the tasks listed in the previous list. For more information, see SQLSetDescField.


SQL Data Types

Each DBMS defines its own SQL types. Each ODBC driver exposes only those SQL data types that the associated DBMS defines. How a driver maps DBMS SQL types to the ODBC-defined SQL type identifiers and how a driver maps DBMS SQL types to its own driver-specific SQL type identifiers are returned through a call to SQLGetTypeInfo. A driver also returns the SQL data types when describing the data types of columns and parameters through calls to SQLColAttribute, SQLColumns, SQLDescribeCol, SQLDescribeParam, SQLProcedureColumns, and SQLSpecialColumns.

Note The SQL data types are contained in the SQL_DESC_ CONCISE_TYPE, SQL_DESC_TYPE, and SQL_DESC_DATETIME_INTERVAL_CODE fields of the implementation descriptors. Characteristics of the SQL data types are contained in the SQL_DESC_PRECISION, SQL_DESC_SCALE, SQL_DESC_LENGTH, and SQL_DESC_OCTET_LENGTH fields of the implementation descriptors. For more information, see "Data Type Identifiers and Descriptors" later in this appendix.

A given driver and data source do not necessarily support all of the SQL data types defined in this appendix. A driver's support for SQL data types depends on the level of SQL-92 that the driver conforms to. To determine the level of SQL-92 grammar supported by the driver, an application calls SQLGetInfo with the SQL_SQL_CONFORMANCE information type. Furthermore, a given driver and data source may support additional, driver-specific SQL data types. To determine which data types a driver supports, an application calls SQLGetTypeInfo. For information about driver-specific SQL data types, see the driver's documentation. For information about the data types in a specific data source, see the documentation for that data source.

Important The tables throughout this appendix are only guidelines and show commonly used names, ranges, and limits of SQL data types. A given data source might support only some of the listed data types, and the characteristics of the supported data types can differ from those listed.

The following table lists valid SQL type identifiers for all SQL data types. The table also lists the name and description of the corresponding data type from SQL-92 (if one exists).

SQL type identifier[1] | Typical SQL data type[2] | Typical type description
SQL_CHAR | CHAR(n) | Character string of fixed string length n.
SQL_VARCHAR | VARCHAR(n) | Variable-length character string with a maximum string length n.
SQL_LONGVARCHAR | LONG VARCHAR | Variable length character data. Maximum length is data source–dependent.[9]
SQL_WCHAR | WCHAR(n) | Unicode character string of fixed string length n
SQL_WVARCHAR | VARWCHAR(n) | Unicode variable-length character string with a maximum string length n
SQL_WLONGVARCHAR | LONGWVARCHAR | Unicode variable-length character data. Maximum length is data source–dependent
SQL_DECIMAL | DECIMAL(p,s) | Signed, exact, numeric value with a precision of at least p and scale s. (The maximum precision is driver-defined.) (1 <= p <= 15; s <= p).[4]
SQL_NUMERIC | NUMERIC(p,s) | Signed, exact, numeric value with a precision p and scale s (1 <= p <= 15; s <= p).[4]
SQL_SMALLINT | SMALLINT | Exact numeric value with precision 5 and scale 0 (signed: –32,768 <= n <= 32,767, unsigned: 0 <= n <= 65,535)[3].
SQL_INTEGER | INTEGER | Exact numeric value with precision 10 and scale 0 (signed: –2[31] <= n <= 2[31] – 1, unsigned: 0 <= n <= 2[32] – 1)[3].
SQL_REAL | REAL | Signed, approximate, numeric value with a binary precision 24 (zero or absolute value 10[–38] to 10[38]).
SQL_FLOAT | FLOAT(p) | Signed, approximate, numeric value with a binary precision of at least p. (The maximum precision is driver-defined.)[5]
SQL_DOUBLE | DOUBLE PRECISION | Signed, approximate, numeric value with a binary precision 53 (zero or absolute value 10[–308] to 10[308]).
SQL_BIT | BIT | Single bit binary data.[8]
SQL_TINYINT | TINYINT | Exact numeric value with precision 3 and scale 0 (signed: –128 <= n <= 127, unsigned: 0 <= n <= 255)[3].
SQL_BIGINT | BIGINT | Exact numeric value with precision 19 (if signed) or 20 (if unsigned) and scale 0 (signed: –2[63] <= n <= 2[63] – 1, unsigned: 0 <= n <= 2[64] – 1)[3],[9].
SQL_BINARY | BINARY(n) | Binary data of fixed length n.[9]
SQL_VARBINARY | VARBINARY(n) | Variable length binary data of maximum length n. The maximum is set by the user.[9]
SQL_LONGVARBINARY | LONG VARBINARY | Variable length binary data. Maximum length is data source–dependent.[9]
SQL_TYPE_DATE[6] | DATE | Year, month, and day fields, conforming to the rules of the Gregorian calendar. (See "Constraints of the Gregorian Calendar," later in this appendix.)
SQL_TYPE_TIME[6] | TIME(p) | Hour, minute, and second fields, with valid values for hours of 00 to 23, valid values for minutes of 00 to 59, and valid values for seconds of 00 to 61. Precision p indicates the seconds precision.
SQL_TYPE_TIMESTAMP[6] | TIMESTAMP(p) | Year, month, day, hour, minute, and second fields, with valid values as defined for the DATE and TIME data types.
SQL_INTERVAL_MONTH[7] | INTERVAL MONTH(p) | Number of months between two dates; p is the interval leading precision.
SQL_INTERVAL_YEAR[7] | INTERVAL YEAR(p) | Number of years between two dates; p is the interval leading precision.
SQL_INTERVAL_YEAR_TO_MONTH[7] | INTERVAL YEAR(p) TO MONTH | Number of years and months between two dates; p is the interval leading precision.
SQL_INTERVAL_DAY[7] | INTERVAL DAY(p) | Number of days between two dates; p is the interval leading precision.
SQL_INTERVAL_HOUR[7] | INTERVAL HOUR(p) | Number of hours between two date/times; p is the interval leading precision.
SQL_INTERVAL_MINUTE[7] | INTERVAL MINUTE(p) | Number of minutes between two date/times; p is the interval leading precision.
SQL_INTERVAL_SECOND[7] | INTERVAL SECOND(p,q) | Number of seconds between two date/times; p is the interval leading precision and q is the interval seconds precision.
SQL_INTERVAL_DAY_TO_HOUR[7] | INTERVAL DAY(p) TO HOUR | Number of days/hours between two date/times; p is the interval leading precision.
SQL_INTERVAL_DAY_TO_MINUTE[7] | INTERVAL DAY(p) TO MINUTE | Number of days/hours/minutes between two date/times; p is the interval leading precision.
SQL_INTERVAL_DAY_TO_SECOND[7] | INTERVAL DAY(p) TO SECOND(q) | Number of days/hours/minutes/seconds between two date/times; p is the interval leading precision and q is the interval seconds precision.
SQL_INTERVAL_HOUR_TO_MINUTE[7] | INTERVAL HOUR(p) TO MINUTE | Number of hours/minutes between two date/times; p is the interval leading precision.
SQL_INTERVAL_HOUR_TO_SECOND[7] | INTERVAL HOUR(p) TO SECOND(q) | Number of hours/minutes/seconds between two date/times; p is the interval leading precision and q is the interval seconds precision.
SQL_INTERVAL_MINUTE_TO_SECOND[7] | INTERVAL MINUTE(p) TO SECOND(q) | Number of minutes/seconds between two date/times; p is the interval leading precision and q is the interval seconds precision.
SQL_GUID | GUID | Fixed length Globally Unique Identifier.

[1] This is the value returned in the DATA_TYPE column by a call to SQLGetTypeInfo.

[2] This is the value returned in the NAME and CREATE PARAMS column by a call to SQLGetTypeInfo. The NAME column returns the designation—for example, CHAR—while the CREATE PARAMS column returns a comma-separated list of creation parameters such as precision, scale, and length.

[3] An application uses SQLGetTypeInfo or SQLColAttribute to determine if a particular data type or a particular column in a result set is unsigned.

[4] SQL_DECIMAL and SQL_NUMERIC data types differ only in their precision. The precision of a DECIMAL(p,s) is an implementation-defined decimal precision that is no less than p, while the precision of a NUMERIC(p,s) is exactly equal to p.

[5] Depending on the implementation, the precision of SQL_FLOAT can be either 24 or 53: if it is 24, the SQL_FLOAT data type is the same as SQL_REAL; if it is 53, the SQL_FLOAT data type is the same as SQL_DOUBLE.

[6] In ODBC 3.x, the SQL date, time, and timestamp data types are SQL_TYPE_DATE, SQL_TYPE_TIME, and SQL_TYPE_TIMESTAMP, respectively; in ODBC 2.x, the data types are SQL_DATE, SQL_TIME, and SQL_TIMESTAMP.

[7] For more information on the interval SQL data types, see the "Interval Data Types" section, later in this appendix.

[8] The SQL_BIT data type has different characteristics than the BIT type in SQL-92.

[9] This data type has no corresponding data type in SQL-92.


Example SQLGetTypeInfo Result Set

An application calls SQLGetTypeInfo to determine which data types are supported by a data source and the characteristics of those data types. The following tables show a sample result set returned by SQLGetTypeInfo for a data source that supports SQL_CHAR, SQL_LONGVARCHAR, SQL_DECIMAL, SQL_REAL, SQL_DATETIME, SQL_INTERVAL_YEAR, and SQL_INTERVAL_DAY_TO_SECOND.

TYPE_NAME | DATA_TYPE | COLUMN_SIZE | LITERAL_PREFIX | LITERAL_SUFFIX | CREATE_PARAMS | NULLABLE
"char" | SQL_CHAR | 255 | "'" | "'" | "length" | SQL_TRUE
"text" | SQL_LONGVARCHAR | 2147483647 | "'" | "'" | <Null> | SQL_TRUE
"decimal" | SQL_DECIMAL | 28 | <Null> | <Null> | "precision, scale" | SQL_TRUE
"real" | SQL_REAL | 7 | <Null> | <Null> | <Null> | SQL_TRUE
"datetime" | SQL_TYPE_TIMESTAMP | 23 | "'" | "'" | <Null> | SQL_TRUE
"INTERVAL YEAR() TO YEAR" | SQL_INTERVAL_YEAR | 9 | "'" | "'" | "precision" | SQL_TRUE
"INTERVAL DAY() TO FRACTION(5)" | SQL_INTERVAL_DAY_TO_SECOND | 24 | "'" | "'" | "precision" | SQL_TRUE
DATA_TYPE | CASE_SENSITIVE | SEARCHABLE | UNSIGNED_ATTRIBUTE | FIXED_PREC_SCALE | AUTO_UNIQUE_VALUE | LOCAL_TYPE_NAME
SQL_CHAR | SQL_FALSE | SQL_SEARCHABLE | <Null> | SQL_FALSE | <Null> | "char"
SQL_LONGVARCHAR | SQL_FALSE | SQL_PRED_CHAR | <Null> | SQL_FALSE | <Null> | "text"
SQL_DECIMAL | SQL_FALSE | SQL_PRED_BASIC | SQL_FALSE | SQL_FALSE | SQL_FALSE | "decimal"
SQL_REAL | SQL_FALSE | SQL_PRED_BASIC | SQL_FALSE | SQL_FALSE | SQL_FALSE | "real"
SQL_TYPE_TIMESTAMP | SQL_FALSE | SQL_SEARCHABLE | <Null> | SQL_FALSE | <Null> | "datetime"
SQL_INTERVAL_YEAR | SQL_FALSE | SQL_SEARCHABLE | <Null> | SQL_FALSE | <Null> | "INTERVAL YEAR() TO YEAR"
SQL_INTERVAL_DAY_TO_SECOND | SQL_FALSE | SQL_PRED_BASIC | <Null> | SQL_FALSE | <Null> | "INTERVAL DAY() TO FRACTION(5)"
DATA_TYPE | MINIMUM_SCALE | MAXIMUM_SCALE | SQL_DATA_TYPE | SQL_DATETIME_SUB | NUM_PREC_RADIX | INTERVAL_PRECISION
SQL_CHAR | <Null> | <Null> | SQL_CHAR | <Null> | <Null> | <Null>
SQL_LONGVARCHAR | <Null> | <Null> | SQL_LONGVARCHAR | <Null> | <Null> | <Null>
SQL_DECIMAL | 0 | 28 | SQL_DECIMAL | <Null> | 10 | <Null>
SQL_REAL | <Null> | <Null> | SQL_REAL | <Null> | 10 | <Null>
SQL_TYPE_TIMESTAMP | 3 | 3 | SQL_DATETIME | SQL_CODE_TIMESTAMP | <Null> | 12
SQL_INTERVAL_YEAR | 0 | 0 | SQL_INTERVAL | SQL_CODE_INTERVALYEAR | <Null> | 9
SQL_INTERVAL_DAY_TO_SECOND | 5 | 5 | SQL_INTERVAL | SQL_CODE_INTERVALDAY_TO_SECOND | <Null> | 9


C Data Types

ODBC C data types indicate the data type of C buffers used to store data in the application.

All drivers must support all C data types. This is required because all drivers must support all C types to which SQL types that they support can be converted, and all drivers support at least one character SQL type. Because the character SQL type can be converted to and from all C types, all drivers must support all C types.

The C data type is specified in the SQLBindCol and SQLGetData functions with the TargetType argument and in the SQLBindParameter function with the ValueType argument. It can also be specified by calling SQLSetDescField to set the SQL_DESC_CONCISE_TYPE field of an ARD or APD, or by calling SQLSetDescRec with the Type argument (and the SubType argument if needed) and the DescriptorHandle argument set to the handle of an ARD or APD.

The following table lists valid type identifiers for the C data types. The table also lists the ODBC C data type that corresponds to each identifier and the definition of this data type.

C type identifier | ODBC C typedef | C type
SQL_C_CHAR | SQLCHAR * | unsigned char *
SQL_C_SSHORT[j] | SQLSMALLINT | short int
SQL_C_USHORT[j] | SQLUSMALLINT | unsigned short int
SQL_C_SLONG[j] | SQLINTEGER | long int
SQL_C_ULONG[j] | SQLUINTEGER | unsigned long int
SQL_C_FLOAT | SQLREAL | float
SQL_C_DOUBLE | SQLDOUBLE, SQLFLOAT | double
SQL_C_BIT | SQLCHAR | unsigned char
SQL_C_STINYINT[j] | SQLSCHAR | signed char
SQL_C_UTINYINT[j] | SQLCHAR | unsigned char
SQL_C_SBIGINT | SQLBIGINT | _int64[h]
SQL_C_UBIGINT | SQLUBIGINT | unsigned _int64[h]
SQL_C_BINARY | SQLCHAR * | unsigned char *
SQL_C_BOOKMARK[i] | BOOKMARK | unsigned long int[d]
SQL_C_VARBOOKMARK | SQLCHAR * | unsigned char *
SQL_C_TYPE_DATE[c] SQL_DATE_STRUCT struct tagDATE_STRUCT {
SQLSMALLINT year;
SQLUSMALLINT month;
SQLUSMALLINT day;
} DATE_STRUCT;[a]
SQL_C_TYPE_TIME[c] SQL_TIME_STRUCT struct tagTIME_STRUCT {
SQLUSMALLINT hour;
SQLUSMALLINT minute;
SQLUSMALLINT second;
} TIME_STRUCT;[a]
SQL_C_TYPE_TIMESTAMP[c] SQL_TIMESTAMP_STRUCT struct tagTIMESTAMP_STRUCT {
SQLSMALLINT year;
SQLUSMALLINT month;
SQLUSMALLINT day;
SQLUSMALLINT hour;
SQLUSMALLINT minute;
SQLUSMALLINT second;
SQLUINTEGER fraction;[b]
} TIMESTAMP_STRUCT;[a]
SQL_C_NUMERIC SQL_NUMERIC_STRUCT struct tagSQL_NUMERIC_STRUCT {
SQLCHAR precision;
SQLSCHAR scale;
SQLCHAR sign[g];
SQLCHAR
val[SQL_MAX_NUMERIC_L EN];[e], [f]
} SQL_NUMERIC_STRUCT;
SQL_C_GUID SQLGUID struct tagSQLGUID {
DWORD Data1;
WORD Data2;
WORD Data3;
BYTE Data4[8];
} SQLGUID;[k]
All C interval data types | SQL_INTERVAL_STRUCT | See the "C Interval Structure" section, later in this appendix.

[a] The values of the year, month, day, hour, minute, and second fields in the datetime C data types must conform to the constraints of the Gregorian calendar. (See "Constraints of the Gregorian Calendar" later in this appendix.)

[b] The value of the fraction field is the number of billionths of a second and ranges from 0 through 999,999,999 (1 less than 1 billion). For example, the value of the fraction field for a half-second is 500,000,000, for a thousandth of a second (one millisecond) is 1,000,000, for a millionth of a second (one microsecond) is 1,000, and for a billionth of a second (one nanosecond) is 1.

[c] In ODBC 2.x, the C date, time, and timestamp data types are SQL_C_DATE, SQL_C_TIME, and SQL_C_TIMESTAMP.

[d] ODBC 3.x applications should use SQL_C_VARBOOKMARK, not SQL_C_BOOKMARK. When an ODBC 3.x application works with an ODBC 2.x driver, the ODBC 3.x Driver Manager will map SQL_C_VARBOOKMARK to SQL_C_BOOKMARK.

[e] A number is stored in the val field of the SQL_NUMERIC_STRUCT structure as a scaled integer, in little endian mode (the leftmost byte being the least-significant byte). For example, the number 10.001 base 10, with a scale of 4, is scaled to an integer of 100010. Because this is 186AA in hexadecimal format, the value in SQL_NUMERIC_STRUCT would be "AA 86 01 00 00 … 00", with the number of bytes defined by the SQL_MAX_NUMERIC_LEN #define.

[f] The precision and scale fields of the SQL_C_NUMERIC data type are never used for input from an application, only for output from the driver to the application. When the driver writes a numeric value into the SQL_NUMERIC_STRUCT, it will use its own driver-specific default as the value for the precision field, and it will use the value in the SQL_DESC_SCALE field of the application descriptor (which defaults to 0) for the scale field. An application can provide its own values for precision and scale by setting the SQL_DESC_PRECISION and SQL_DESC_SCALE fields of the application descriptor.

[g] The sign field is 1 if positive, 0 if negative.

[h] _int64 might not be supplied by some compilers.

[i] _SQL_C_BOOKMARK has been deprecated in ODBC 3.x.

[j] _SQL_C_SHORT, SQL_C_LONG, and SQL_C_TINYINT have been replaced in ODBC by signed and unsigned types: SQL_C_SSHORT and SQL_C_USHORT, SQL_C_SLONG and SQL_C_ULONG, and SQL_C_STINYINT and SQL_C_UTINYINT. An ODBC 3.x driver that should work with ODBC 2.x applications should support SQL_C_SHORT, SQL_C_LONG, and SQL_C_TINYINT, because when they are called, the Driver Manager passes them through to the driver.

[k] SQL_C_GUID can be converted only to SQL_CHAR or SQL_WCHAR.


64-Bit Integer Structures

The C type for the SQL_C_SBIGINT and SQL_C_UBIGINT data type identifiers on Microsoft C compilers is _int64. When a compiler other than a Microsoft® C compiler is used, the C type might be different. If the compiler supports 64-bit integers natively, the driver or application should define ODBCINT64 to be the native 64-bit integer type. If the compiler does not support 64-bit integers natively, an application or driver can define the following structures to ensure that it has access to this data:

typedef struct{
SQLUINTEGER dwLowWord;
SQLUINTEGER dwHighWord;
} SQLUBIGINT

typedef struct{
SQLUINTEGER dwLowWord;
SQLINTEGER sdwHighWord;
} SQLBIGINT

These structures should be aligned to an 8-byte boundary because a 64-bit integer is aligned to the 8-byte boundary.


Data Type Identifiers and Descriptors

The data types listed in the "SQL Data Types" and "C Data Types" sections earlier in this appendix are "concise" data types: Each identifier refers to a single data type. There is a one-to-one correspondence between the identifier and the data type. Descriptors, however, do not in all cases use a single value to identify data types. In some cases, they use a "verbose" data type and a type subcode. For all data types except datetime and interval data types, the verbose type identifier is the same as the concise type identifier and the value in SQL_DESC_DATETIME_INTERVAL_CODE is equal to 0. For datetime and interval data types, however, a verbose type (SQL_DATETIME or SQL_INTERVAL) is stored in SQL_DESC_TYPE, a concise type is stored in SQL_DESC_CONCISE_TYPE, and a subcode for each concise type is stored in SQL_DESC_DATETIME_INTERVAL_CODE. Setting one of these fields affects the others. For more information about these fields, see the SQLSetDescField function description.

When the SQL_DESC_TYPE or SQL_DESC_CONCISE_TYPE field is set for some data types, the SQL_DESC_DATETIME_INTERVAL_PRECISION, SQL_DESC_LENGTH, SQL_DESC_PRECISION, and SQL_DESC_SCALE fields are automatically set to default values, as applicable for the data type. For more information, see the description of the SQL_DESC_TYPE field in SQLSetDescField. If any of the default values set is not appropriate, the application should explicitly set the descriptor field through a call to SQLSetDescField.

The following table shows the concise type identifier, verbose type identifier, and type subcode for each datetime and interval SQL and C type identifier. As this table indicates, for datetime and interval data types, the SQL_DESC_TYPE and SQL_DESC_DATETIME_INTERVAL_CODE fields have the same manifest constants both for SQL data types (in implementation descriptors) and for C data types (in application descriptors).

Concise SQL type | Concise C type | Verbose type | DATETIME_INTERVAL_CODE
SQL_TYPE_DATE | SQL_C_TYPE_DATE | SQL_DATETIME | SQL_CODE_DATE
SQL_TYPE_TIME | SQL_C_TYPE_TIME | SQL_DATETIME | SQL_CODE_TIME
SQL_TYPE_TIMESTAMP | SQL_C_TYPE_TIMESTAMP | SQL_DATETIME | SQL_CODE_TIMESTAMP
SQL_INTERVAL_MONTH | SQL_C_INTERVAL_MONTH | SQL_INTERVAL | SQL_CODE_MONTH
SQL_INTERVAL_YEAR | SQL_C_INTERVAL_YEAR | SQL_INTERVAL | SQL_CODE_YEAR
SQL_INTERVAL_YEAR_TO_MONTH | SQL_C_INTERVAL_YEAR_TO_MONTH | SQL_INTERVAL | SQL_CODE_YEAR_TO_MONTH
SQL_INTERVAL_DAY | SQL_C_INTERVAL_DAY | SQL_INTERVAL | SQL_CODE_DAY
SQL_INTERVAL_HOUR | SQL_C_INTERVAL_HOUR | SQL_INTERVAL | SQL_CODE_HOUR
SQL_INTERVAL_MINUTE | SQL_C_INTERVAL_MINUTE | SQL_INTERVAL | SQL_CODE_MINUTE
SQL_INTERVAL_SECOND | SQL_C_INTERVAL_SECOND | SQL_INTERVAL | SQL_CODE_SECOND
SQL_INTERVAL_DAY_TO_HOUR | SQL_C_INTERVAL_DAY_TO_HOUR | SQL_INTERVAL | SQL_CODE_DAY_TO_HOUR
SQL_INTERVAL_DAY_TO_MINUTE | SQL_C_INTERVAL_DAY_TO_MINUTE | SQL_INTERVAL | SQL_CODE_DAY_TO_MINUTE
SQL_INTERVAL_DAY_TO_SECOND | SQL_C_INTERVAL_DAY_TO_SECOND | SQL_INTERVAL | SQL_CODE_DAY_TO_SECOND
SQL_INTERVAL_HOUR_TO_MINUTE | SQL_C_INTERVAL_HOUR_TO_MINUTE | SQL_INTERVAL | SQL_CODE_HOUR_TO_MINUTE
SQL_INTERVAL_HOUR_TO_SECOND | SQL_C_INTERVAL_HOUR_TO_SECOND | SQL_INTERVAL | SQL_CODE_HOUR_TO_SECOND
SQL_INTERVAL_MINUTE_TO_SECOND | SQL_C_INTERVAL_MINUTE_TO_SECOND | SQL_INTERVAL | SQL_CODE_MINUTE_TO_SECOND


Pseudo-Type Identifiers

For application programming convenience, ODBC defines a number of pseudo-type identifiers. These identifiers do not actually correspond to actual data types, but instead, depending on the situation, resolve to existing data types.


Default C Data Types

If an application specifies SQL_C_DEFAULT in SQLBindCol, SQLGetData, or SQLBindParameter, the driver assumes that the C data type of the output or input buffer corresponds to the SQL data type of the column or parameter to which the buffer is bound.

Important Interoperable applications should not use SQL_C_DEFAULT. Instead, they should always specify the C type of the buffer they are using. This is because drivers cannot always correctly determine the default C type, for the following reasons:

  • If the DBMS promotes an SQL data type of a column or parameter, the driver cannot determine the original SQL data type of a column or parameter. Therefore, it cannot determine the corresponding default C data type.
  • If the driver cannot determine whether a particular column or parameter is signed, as is often the case when this is handled by the DBMS, the driver cannot determine whether the corresponding default C data type should be signed or unsigned.

    Because SQL_C_DEFAULT is provided only as a programming convenience, the application does not lose any functionality when it specifies the actual C data type.

A table showing the default C data type for each SQL data type is included in "Converting Data from SQL to C Data Types," later in this appendix.


Bookmark C Data Type

The bookmark C data type allows an application to retrieve a bookmark. The bookmark C types are used only to retrieve bookmark values that can be variable in length; they should not be converted to other data types. An application retrieves a bookmark either from column 0 of the result set with SQLBulkOperations (with an operation of SQL_ADD), SQLFetch, SQLFetchScroll, or SQLGetData. For more information, see "Bookmarks" in Chapter 11: Retrieving Results (Advanced).

The following table lists the value of CType for the bookmark C data type, the ODBC C data type that implements the bookmark C data type, and the definition of this data type from SQL.H.

Note The SQL_C_BOOKMARK data type has been deprecated. ODBC 3.x applications should not use SQL_C_BOOKMARK. ODBC 3.x drivers need to support SQL_C_BOOKMARK only if they want to work with ODBC 2.x applications that use it. The Driver Manager maps SQL_C_VARBOOKMARK to SQL_C_BOOKMARK when an application works with an ODBC 2.x driver.

C type identifier | ODBC C typedef | C type
SQL_C_BOOKMARK (Deprecated) | BOOKMARK | unsigned long int
SQL_C_VARBOOKMARK | SQLCHAR * | unsigned char *


SQL_ARD_TYPE

The SQL_ARD_TYPE type identifier is used to indicate that the data in a buffer will be of the type specified in the SQL_DESC_CONCISE_TYPE field of the ARD. SQL_ARD_TYPE is entered in the TargetType argument of a call to SQLGetData instead of a specific data type and enables an application to change the data type of the buffer by changing the descriptor field. This value ties the data type of the *TargetValuePtr buffer to the descriptor field. (SQL_ARD_TYPE is not entered in a call to SQLBindCol or SQLBindParameter because the type of the bound buffer is already tied to the SQL_DESC_TYPE and SQL_DESC_CONCISE_TYPE fields and can be changed at any time by changing either of those fields.)

The SQL_ARD_TYPE type identifier can be used to specify nondefault values for leading precision and seconds precision of interval data types, and precision and scale values for the SQL_C_NUMERIC data type. For more information, see "Overriding Default Leading and Seconds Precision for Interval Data Types" and "Overriding Default Precision and Scale for Numeric Data Types," later in this appendix.


SQL_C_TCHAR

The SQL_C_TCHAR type identifier does not actually identify a data type; it is a macro that exists within the header file for Unicode conversion. It is replaced by SQL_C_CHAR or SQL_C_WCHAR depending on the setting of the UNICODE #define. It is useful for an application transferring character data that is compiled as both an ANSI and a Unicode application.


Transferring Data in Its Binary Form

An application can safely transfer data (in the internal form used by a specified DBMS) between two data sources that use the same DBMS and hardware platform. For a given piece of data, the SQL data types must be the same in the source and target data sources. The C data type is SQL_C_BINARY.

When the application calls SQLFetch, SQLFetchScroll, or SQLGetData to retrieve the data from the source data source, the driver retrieves the data from the data source and transfers it, without conversion, to a storage location of type SQL_C_BINARY. When the application calls SQLBulkOperations, SQLExecute, SQLExecDirect, SQLPutData, or SQLSetPos to send the data to the target data source, the driver retrieves the data from the storage location and transfers it, without conversion, to the target data source.

Note Applications that transfer any data (except binary data) in this manner are not interoperable among DBMSs.

SQLCopyDesc can be used to copy row bindings from the source DBMS to parameter bindings in the target DBMS.


Guidelines for Interval and Numeric Data Types

The following sections address interval and numeric data types.

  • Interval Data Types
  • Numeric Literals
  • Overriding Default Precision and Scale for Numeric Data Types


Interval Data Types

An interval is defined as the difference between two dates and times. Intervals are expressed in one of two different ways. One is a year-month interval that expresses intervals in terms of years and an integral number of months. The other is a day-time interval that expresses intervals in terms of days, minutes, and seconds. These two types of intervals are distinct and cannot be mixed, because months can have varying numbers of days.

An interval consists of a set of fields. There is an implied ordering among the fields. For example, in a year-to-month interval, the year comes first, followed by the month. Similarly, in a day-to-minute interval, the fields are in the order day, hour, and minute. The first field in an interval type is called the leading field, or the high-order field. The last field is called the trailing field.

In all intervals, the leading field is not constrained by rules of the Gregorian calendar. For example, in an hour-to-minute interval, the hour field is not constrained to be between 0 and 23 (inclusive), as it normally is. The trailing fields subsequent to the leading field follow the usual constraints of the Gregorian calendar. For more information, see "Constraints of the Gregorian Calendar," later in this appendix.

There are 13 interval SQL data types and 13 interval C data types. Each of the interval C data types uses the same structure, SQL_INTERVAL_STRUCT, to contain the interval data. (For more information, see the next section, "C Interval Structure.") For more information on the SQL data types, see "SQL Data Types" earlier in this appendix; for more information on the C data types, see "C Data Types."

Type identifier | Class | Description
MONTH | Year-Month | Number of months between two dates.
YEAR | Year-Month | Number of years between two dates.
YEAR_TO_MONTH | Year-Month | Number of years and months between two dates.
DAY | Day-Time | Number of days between two dates.
HOUR | Day-Time | Number of hours between two date/times.
MINUTE | Day-Time | Number of minutes between two date/times.
SECOND | Day-Time | Number of seconds between two date/times.
DAY_TO_HOUR | Day-Time | Number of days/hours between two date/times.
DAY_TO_MINUTE | Day-Time | Number of days/hours/minutes between two date/times.
DAY_TO_SECOND | Day-Time | Number of days/hours/minutes/seconds between two date/times.
HOUR_TO_MINUTE | Day-Time | Number of hours/minutes between two date/times.
HOUR_TO_SECOND | Day-Time | Number of hours/minutes/seconds between two date/times.
MINUTE_TO_SECOND | Day-Time | Number of minutes/seconds between two date/times.


C Interval Structure

Each of the C interval data types listed in the "C Data Types" section uses the same structure to contain the interval data. When SQLFetch, SQLFetchScroll, or SQLGetData is called, the driver returns data into the SQL_INTERVAL_STRUCT structure, uses the value that was specified by the application for the C data types (in the call to SQLBindCol, SQLGetData, or SQLBindParameter) to interpret the contents of SQL_INTERVAL_STRUCT, and populates the interval_type field of the structure with the enum value corresponding to the C type. Note that drivers do not read the interval_type field to determine the type of the interval; they retrieve the value of the SQL_DESC_CONCISE_TYPE descriptor field. When the structure is used for parameter data, the driver uses the value specified by the application in the SQL_DESC_CONCISE_TYPE field of the APD to interpret the contents of SQL_INTERVAL_STRUCT, even if the application sets the value of the interval_type field to a different value.

This structure is defined as follows:

typedef struct tagSQL_INTERVAL_STRUCT
{
   SQLINTERVAL interval_type;
   SQLSMALLINT interval_sign;
   union {
         SQL_YEAR_MONTH_STRUCT   year_month;
         SQL_DAY_SECOND_STRUCT   day_second;
         } intval;
} SQL_INTERVAL_STRUCT;
typedef enum
{
   SQL_IS_YEAR = 1,
   SQL_IS_MONTH = 2,
   SQL_IS_DAY = 3,
   SQL_IS_HOUR = 4,
   SQL_IS_MINUTE = 5,
   SQL_IS_SECOND = 6,
   SQL_IS_YEAR_TO_MONTH = 7,
   SQL_IS_DAY_TO_HOUR = 8,
   SQL_IS_DAY_TO_MINUTE = 9,
   SQL_IS_DAY_TO_SECOND = 10,
   SQL_IS_HOUR_TO_MINUTE = 11,
   SQL_IS_HOUR_TO_SECOND = 12,
   SQL_IS_MINUTE_TO_SECOND = 13
} SQLINTERVAL;

typedef struct tagSQL_YEAR_MONTH
{
   SQLUINTEGER year;
   SQLUINTEGER month;
} SQL_YEAR_MONTH_STRUCT;

typedef struct tagSQL_DAY_SECOND
{
   SQLUINTEGER day;
   SQLUINTEGER hour;
   SQLUINTEGER minute;
   SQLUINTEGER second;
   SQLUINTEGER fraction;
} SQL_DAY_SECOND_STRUCT;

The interval_type field of the SQL_INTERVAL_STRUCT indicates to the application what structure is held in the union and also what members of the structure are relevant. The interval_sign field has the value SQL_FALSE if the interval leading field is unsigned; if it is SQL_TRUE, the leading field is negative. The value in the leading field itself is always unsigned, regardless of the value of interval_sign. The interval_sign field acts as a sign bit.


Interval Data Type Precision

Precision for an interval data type includes interval leading precision, interval precision, and seconds precision.

The leading field of an interval is a signed numeric. The maximum number of digits for the leading field is determined by a quantity called interval leading precision, which is a part of the data type declaration. For example, the declaration: INTERVAL HOUR(5) TO MINUTE has an interval leading precision of 5; the HOUR field can take values from –99999 through 99999. The interval leading precision is contained in the SQL_DESC_DATETIME_INTERVAL_PRECISION field of the descriptor record.

The list of fields that an interval data type is made up of is called interval precision. It is not a numeric value, as the term "precision" might imply. For example, the interval precision of the type INTERVAL DAY TO SECOND is the list DAY, HOUR, MINUTE, SECOND. There is no descriptor field that holds this value; the interval precision can always be determined by the interval data type.

Any interval data type that has a SECOND field has a seconds precision. This is the number of decimal digits allowed in the fractional part of the seconds value. This is different than for other data types, where precision indicates the number of digits before the decimal point. The seconds precision of an interval data type is the number of digits after the decimal point. For example, if the seconds precision is set to 6, the number 123456 in the fraction field would be interpreted as .123456 and the number 1230 would be interpreted as .001230. For other data types, this is referred to as scale. Interval seconds precision is contained in the SQL_DESC_PRECISION field of the descriptor. If the precision of the fractional seconds component of the SQL interval value is greater than what can be held in the C interval structure, it is driver-defined whether the fractional seconds value in the SQL interval is rounded or truncated when converted to the C interval structure.

When the SQL_DESC_CONCISE_TYPE field is set to an interval data type, the SQL_DESC_TYPE field is set to SQL_INTERVAL and the SQL_DESC_DATETIME_INTERVAL_CODE is set to the code for the interval data type. The SQL_DESC_DATETIME_INTERVAL_PRECISION field is automatically set to the default interval leading precision of 2, and the SQL_DESC_PRECISION field is automatically set to the default interval seconds precision of 6. If either of these values is not appropriate, the application should explicitly set the descriptor field through a call to SQLSetDescField.


Interval Data Type Length

The following rules are used to determine the length of an interval data type in characters. Length is expressed in number of characters. The number of bytes depends upon the character set. The length includes the following values added together:

  • Two characters for every field in the interval that is not the leading field.
  • For the leading field, the number of characters that is the express or implicit leading precision. If the leading precision is not specified, the default value is 2.
  • One character for the separator between the fields.
  • One plus the express or implied seconds precision. If the seconds precision is not specified, the default value is 6.

Specific column length values for each interval data type are contained in "Column Size," later in this appendix.


Interval Literals

ODBC requires that all drivers support conversion of the SQL_CHAR or SQL_VARCHAR data type to all C interval data types. If the underlying data source does not support interval data types, however, the driver needs to know the correct format of the value in the SQL_CHAR field in order to support these conversions. Similarly, ODBC requires that any ODBC C type be convertible to SQL_CHAR or SQL_VARCHAR, so a driver needs to know what format an interval stored in the character field should have. This section describes the syntax of interval literals, which the driver writer needs to use to validate the SQL_CHAR fields during conversion either to or from C interval data types.

Note The complete BNF syntax for interval literals is shown in the section "Interval Literal Syntax" in Appendix C: SQL Grammar.

To pass interval literals as part of an SQL statement, an escape clause syntax is defined for interval literals. For more information, see "Date, Time, and Timestamp Literals" in Chapter 8: SQL Statements.

An interval literal is of the form:

INTERVAL[<sign>] 'value' <interval qualifier>

where "INTERVAL" indicates that the character literal is an interval. The sign can be either plus or minus; it is outside the interval string and is optional.

The interval qualifier can either be a single datetime field or be composed of two datetime fields, in the form: <leading field> TO <trailing field>.

  • When the interval is composed of a single field, the single field can be a non-second field that may be accompanied by an optional leading precision in parentheses. The single datetime field can also be a second field that may be accompanied by the optional leading precision, the optional fractional-seconds precision in parentheses, or both. If both a leading precision and a fractional-seconds precision are present for a seconds field, they are separated by commas. If the seconds field has a fractional-seconds precision, it must also have a leading precision.
  • When the interval is composed of leading and trailing fields, the leading field is a non-second field that may be accompanied by the interval leading field precision in parentheses. The trailing field can be either a non-second field or a second field that may be accompanied by an interval fractional-seconds precision in parentheses.

The interval string in value is enclosed in single quotation marks. It can be either a year-month literal or a day-time literal. The format of the string in value is determined by the following rules:

  • The string contains a decimal value for every field that is implied by the <interval qualifier>.
  • If the interval precision includes the fields YEAR and MONTH, the values of these fields are separated by a minus sign.
  • If the interval precision includes the fields DAY and HOUR, the values of these fields are separated by a space.
  • If the interval precision includes the field HOUR and the lower order fields (MINUTE and SECOND), the values of these fields are separated by a colon.
  • If the interval precision includes a SECOND field and the expressed or implied seconds precision is nonzero, the character immediately before the first digit of the fractional part of the second is a period.
  • No field can be more than two digits long, except:
    • The value of the leading field can be as long as the expressed or implied interval leading precision.
    • The fractional part of the SECOND field can be as long as the expressed or implied seconds precision.
    • The trailing fields follow the usual constraints of the Gregorian calendar. (See "Constraints of the Gregorian Calendar," later in this appendix).

The following table lists examples of valid interval literals as included in the ODBC escape clause for intervals. The syntax of the escape clause is as follows:

{INTERVAL sign interval-string interval-qualifier}
Literal escape clause | Interval specified
{INTERVAL '326' YEAR(4)} | Specifies an interval of 326 years. The interval leading precision is 4.
{INTERVAL '326' MONTH(3)} | Specifies an interval of 326 months. The interval leading precision is 3.
{INTERVAL '3261' DAY(4)} | Specifies an interval of 3261 days. The interval leading precision is 4.
{INTERVAL '163' HOUR(3)} | Specifies an interval of 163 days. The interval leading precision is 3.
{INTERVAL '163' MINUTE(3)} | Specifies an interval of 163 minutes. The interval leading precision is 3.
{INTERVAL '223.16' SECOND(3,2)} | Specifies an interval of 223.16 seconds. The interval leading precision is 3, and the seconds precision is 2.
{INTERVAL '163-11' YEAR(3) TO MONTH} | Specifies an interval of 163 years and 11 months. The interval leading precision is 3.
{INTERVAL '163 12' DAY(3) TO HOUR} | Specifies an interval of 163 days and 12 hours. The interval leading precision is 3.
{INTERVAL '163 12:39' DAY(3) TO MINUTE} | Specifies an interval of 163 days, 12 hours, and 39 minutes. The interval leading precision is 3.
{INTERVAL '163 12:39:59.163' DAY(3) TO SECOND(3)} | Specifies an interval of 163 days, 12 hours, 39 minutes, and 59.163 seconds. The interval leading precision is 3, and the seconds precision is 3.
{INTERVAL '163:39' HOUR(3) TO MINUTE} | Specifies an interval of 163 hours and 39 minutes. The interval leading precision is 3.
{INTERVAL '163:39:59.163' HOUR(3) TO SECOND(4)} | Specifies an interval of 163 hours, 39 minutes, and 59.163 seconds. The interval leading precision is 3, and the seconds precision is 4.
{INTERVAL '163:59.163' MINUTE(3) TO SECOND(5)} | Specifies an interval of 163 minutes and 59.163 seconds. The interval leading precision is 3, and the seconds precision is 5.
{INTERVAL -'16 23:39:56.23' DAY TO SECOND} | Specifies an interval of minus 16 days, 23 hours, 39 minutes, and 56.23 seconds. The implied leading precision is 2, and the implied seconds precision is 6.

The following table lists examples of invalid interval literals:

Literal escape clause | Reason why invalid
{INTERVAL '163' HOUR(2)} | The interval leading precision is 2, but the value of the leading field is 163.
{INTERVAL '223.16' SECOND(2,2)} {INTERVAL '223.16' SECOND(3,1)} | In the first example, the leading precision is too small, and in the second example, the seconds precision is too small.
{INTERVAL '223.16' SECOND} {INTERVAL '223' YEAR} | Because the leading precision is unspecified, it defaults to 2, which is too small to hold the specified literal.
{INTERVAL '22.1234567' SECOND} | The seconds precision is unspecified, so it defaults to 6. The literal has seven digits after the decimal point.
{INTERVAL '163-13' YEAR(3) TO MONTH} {INTERVAL '163 65' DAY(3) TO HOUR} {INTERVAL '163 62:39' DAY(3) TO MINUTE} {INTERVAL '163 12:125:59.163' DAY(3) TO SECOND(3)} {INTERVAL '163:144' HOUR(3) TO MINUTE} {INTERVAL '163:567:234.163' HOUR(3) TO SECOND(4)} {INTERVAL '163:591.163' MINUTE(3) TO SECOND(5)} | The trailing field does not follow the rules of the Gregorian calendar.


Overriding Default Leading and Seconds Precision for Interval Data Types

When the SQL_DESC_TYPE field of an ARD is set to a datetime or interval C type, by calling either SQLBindCol or SQLSetDescField, the SQL_DESC_PRECISION field (which contains the interval seconds precision) is set to the following defaults:

  • 6 for timestamp and all interval data types with a second component.
  • 0 for all other data types.

For all interval data types, the SQL_DESC_DATETIME_INTERVAL_PRECISION descriptor field, which contains the interval leading field precision, is set to a default value of 2.

When the SQL_DESC_TYPE field in an APD is set to a datetime or interval C type, by calling either SQLBindParameter or SQLSetDescField, the SQL_DESC_PRECISION and SQL_DESC_DATETIME_INTERVAL_PRECISION fields in the APD are set to the default given previously. This is true for input parameters but not for input/output or output parameters.

A call to SQLSetDescRec sets the interval leading precision to the default but sets the interval seconds precision (in the SQL_DESC_PRECISION field) to the value of its Precision argument.

If either of the defaults given previously is not acceptable to an application, the application should set the SQL_DESC_PRECISION or SQL_DESC_DATETIME_INTERVAL_PRECISION field by calling SQLSetDescField.

If the application calls SQLGetData to return data into a datetime or interval C type, the default interval leading precision and interval seconds precision are used. If either default is not acceptable, the application must call SQLSetDescField to set either descriptor field, or SQLSetDescRec to set SQL_DESC_PRECISION. The call to SQLGetData should have a TargetType of SQL_ARD_TYPE to use the values in the descriptor fields.

When SQLPutData is called, the interval leading precision and interval seconds precision are read from the fields of the descriptor record that correspond to the data-at-execution parameter or column, which are APD fields for calls to SQLExecute or SQLExecDirect, or ARD fields for calls to SQLBulkOperations or SQLSetPos.


Numeric Literals

Numeric literals are used when numeric data values are stored in character strings. For conversion of numeric SQL data to an SQL_C_CHAR string, or conversion of numeric C data to an SQL_CHAR or SQL_VARCHAR string, numeric literal syntax is used to specify what is stored in the target. For conversion of a numeric stored as a SQL_C_CHAR string to numeric SQL data, or a numeric stored as a SQL_CHAR string to numeric C data, this syntax is used to validate what is stored in the source.

Numeric literals should conform to the syntax defined in the section "Numeric Literal Syntax" in Appendix C: SQL Grammar.


Rules for Conversions

The rules in this section apply for conversions involving numeric literals. For the purposes of these rules, the following terms are defined:

  • Store assignment: When sending data into a table column in a database. This occurs during calls to SQLExecute, SQLExecDirect, and SQLSetPos. During store assignment, "target" refers to a database column and "source" refers to data in application buffers.
  • Retrieval assignment: When retrieving data from the database into application buffers. This occurs during calls to SQLFetch, SQLGetData, SQLFetchScroll, and SQLSetPos. During retrieval assignment, "target" refers to the application buffers and "source" refers to the database column.
  • CS: The value in the character source.
  • NT: The value in the numeric target.
  • NS: The value in the numeric source.
  • CT: The value in the character target.
  • Precision of an exact numeric literal: the number of digits it contains.
  • The scale of an exact numeric literal: the number of digits to the right of the expressed or implied period.
  • The precision of an approximate numeric literal: the precision of its mantissa.

Character Source to Numeric Target

Following are the rules for converting from a character source (CS) to a numeric target (NT):

  1. Replace CS with the value obtained by removing any leading or trailing spaces in CS. If CS is not a valid numeric-literal, SQLSTATE 22018 (Invalid character value for cast specification) is returned.
  2. Replace CS with the value obtained by removing leading zeroes before the decimal point, trailing zeroes after the decimal point, or both.
  3. Convert CS to NT. If the conversion results in a loss of significant digits, SQLSTATE 22003 (Numeric value out of range) is returned. If the conversion results in the loss of nonsignificant digits, SQLSTATE 01S07 (Fractional truncation) is returned.

Numeric Source to Character Target

Following are the rules for converting from a numeric source (NS) to a character target (CT):

  1. Let LT be the length in characters of CT. For retrieval assignment, LT is equal to the length of the buffer in characters minus the number of bytes in the null-termination character for this character set.
  2. Cases:
    • If NS is an exact numeric type, then let YP equal the shortest character string that conforms to the definition of exact-numeric-literal such that the scale of YP is the same as the scale of NS, and the interpreted value of YP is the absolute value of NS.
    • If NS is an approximate numeric type, then let YP be a character string as follows:

      Case:

      If NS is equal to 0, then YP is 0.

      Let YSN be the shortest character string that conforms to the definition of exact-numeric-literal and whose interpreted value is the absolute value of NS. If the length of YSN is less than the (precision + 1) of the data type of NS, then let YP equal YSN.

      Otherwise, YP is the shortest character string that conforms to the definition of approximate-numeric-literal whose interpreted value is the absolute value of NS and whose mantissa consists of a single digit that is not '0', followed by a period and an unsigned-integer.

  3. Case:
    • If NS is less than 0, then let Y be the result of:

      '-' || YP

      where '||' is the string concatenation operator.

      Otherwise, let Y equal YP.

  4. Let LY be the length in characters of Y.
  5. Case:
    • If LY equals LT, then CT is set to Y.
    • If LY is less than LT, then CT is set to Y extended on the right by appropriate number of spaces.

      Otherwise (LY > LT), copy the first LT characters of Y into CT.

      Case:

      If this is a store assignment, return the error SQLSTATE 22001 (String data, right-truncated).

      If this is retrieval assignment, return the warning SQLSTATE 01004 (String data, right-truncated). When the copy results in the loss of fractional digits (other than trailing zeros), it is driver-defined whether one of the following occurs:

      (1) The driver truncates the string in Y to an appropriate scale
      (which can be zero also) and writes the result into CT.
      (2) The driver rounds the string in Y to an appropriate scale
      (which can be zero also) and writes the result into CT.
      (3) The driver neither truncates nor rounds, but just copies
      the first LT characters of Y into CT.


Overriding Default Precision and Scale for Numeric Data Types

When the SQL_DESC_TYPE field in an ARD is set to SQL_C_NUMERIC, by calling either SQLBindCol or SQLSetDescField, the SQL_DESC_SCALE field in the ARD is set to 0 and the SQL_DESC_PRECISION field is set to a driver-defined default precision. This is also true when the SQL_DESC_TYPE field in an APD is set to SQL_C_NUMERIC, by calling either SQLBindParameter or SQLSetDescField. This is true for input, input/output, or output parameters.

If either of the defaults described previously are not acceptable for an application, the application should set the SQL_DESC_SCALE or SQL_DESC_PRECISION field by calling SQLSetDescField or SQLSetDescRec.

If the application calls SQLGetData to return data into an SQL_C_NUMERIC structure, the default SQL_DESC_SCALE and SQL_DESC_PRECISION fields are used. If the defaults are not acceptable, the application must call SQLSetDescRec or SQLSetDescField to set the fields and then call SQLGetData with a TargetType of SQL_ARD_TYPE to use the values in the descriptor fields.

When SQLPutData is called, the call uses the SQL_DESC_SCALE and SQL_DESC_PRECISION fields of the descriptor record that corresponds to the data-at-execution parameter or column, which are APD fields for calls to SQLExecute or SQLExecDirect, or ARD fields for calls to SQLBulkOperations or SQLSetPos.


Constraints of the Gregorian Calendar

Date and datetime data types, and the trailing fields of interval data types, must conform to the constraints of the Gregorian calendar. These constraints are as follows:

  • The value of the month field must be between 1 and 12, inclusive.
  • The value of the day field must be in the range from 1 through the number of days in the month. The number of days in the month is determined from the values of the year and months fields and can be 28, 29, 30, or 31. (The number of days in the month can also depend on whether it is a leap year.)
  • The value of the hour field must be between 0 and 23, inclusive.
  • The value of the minute field must be between 0 and 59, inclusive.
  • For the trailing seconds field of interval data types, the value of the seconds field must be between 0 and 59.9(n), inclusive, where n is the number of digits in the fractional seconds precision.
  • For the trailing seconds field of datetime data types, the value of the seconds field must be between 0 and 61.9(n), inclusive, where n specifies the number of "9" digits and the value of n is the fractional seconds precision. (The range of seconds allows as many as two leap seconds to maintain synchronization of sidereal time.)


Column Size, Decimal Digits, Transfer Octet Length, and Display Size

Data types are characterized by their column (or parameter) size, decimal digits, length, and display size. The following ODBC functions return these attributes for a parameter in an SQL statement or for an SQL data type on a data source. Each ODBC function returns a different set of these attributes, as follows:

  • SQLDescribeCol returns the column size and decimal digits of the columns it describes.
  • SQLDescribeParam returns the parameter size and decimal digits of the parameters it describes. SQLBindParameter sets the parameter size and decimal digits for a parameter in an SQL statement.
  • The catalog functions SQLColumns, SQLProcedureColumns, and SQLGetTypeInfo return attributes for a column in a table, result set, or a procedure parameter and the catalog attributes of the data types in the data source. SQLColumns returns the column size, decimal digits, and length of a column in specified tables (such as the base table, view, or a system table). SQLProcedureColumns returns the column size, decimal digits, and length of a column in a procedure. SQLGetTypeInfo returns the maximum column size and the minimum and maximum decimal digits of an SQL data type on a data source.

The values returned by these functions for the column or parameter size correspond to "precision" as defined in ODBC 2.x. However, the values do not necessarily correspond to the values returned in SQL_DESC_PRECISION or any other one descriptor field. The same is true for decimal digits, which correspond to "scale" as defined in ODBC 2.x. It does not necessarily correspond to the values returned in SQL_DESC_SCALE or any other one descriptor field, but comes from different descriptor fields depending on the data type. For further information, see the sections "Column Size" and "Decimal Digits," later in this appendix.

Similarly, the values for transfer octet length do not come from SQL_DESC_LENGTH. They come from the SQL_DESC_OCTET_LENGTH of a field of a descriptor for all character and binary types. There is no descriptor field that holds this information for other types.

The display size value for all data types corresponds to the value in a single descriptor field, SQL_DESC_DISPLAY_SIZE.

Descriptor fields describe the characteristics of a result set. Descriptor fields do not contain valid values about data before statement execution. The values for column size, decimal digits, and display size returned by SQLColumns, SQLProcedureColumns, and SQLGetTypeInfo, on the other hand, return characteristics of database objects, such as table columns and data types, that exist in the data source's catalog. Likewise, in its result set, SQLColAttribute returns the column size, decimal digits, and transfer octet length of columns at the data source; these values are not necessarily the same as the values in the SQL_DESC_PRECISION, SQL_DESC_SCALE, and SQL_DESC_OCTET_LENGTH descriptor fields.

For more information about these descriptor fields, see SQLSetDescField.


Column Size

The column (or parameter) size of numeric data types is defined as the maximum number of digits used by the data type of the column or parameter, or the precision of the data. For character types, this is the length in characters of the data; for binary data types, column size is defined as the length in bytes of the data. For the time, timestamp, and all interval data types, this is the number of characters in the character representation of this data. The column size defined for each concise SQL data type is shown in the following table.

SQL type identifier | Column size
All character types[a],[b] | The defined or maximum column size in characters of the column or parameter (as contained in the SQL_DESC_LENGTH descriptor field). For example, the column size of a single-byte character column defined as CHAR(10) is 10.
SQL_DECIMAL SQL_NUMERIC | The defined number of digits. For example, the precision of a column defined as NUMERIC(10,3) is 10.
SQL_BIT[c] | 1
SQL_TINYINT[c] | 3
SQL_SMALLINT[c] | 5
SQL_INTEGER[c] | 10
SQL_BIGINT[c] | 19 (if signed) or 20 (if unsigned)
SQL_REAL[c] | 7
SQL_FLOAT[c] | 15
SQL_DOUBLE[c] | 15
All binary types[a],[b] | The defined or maximum length in bytes of the column or parameter. For example, the length of a column defined as BINARY(10) is 10.
SQL_TYPE_DATE[c] | 10 (the number of characters in the yyyy-mm-dd format).
SQL_TYPE_TIME[c] | 8 (the number of characters in the hh-mm-ss format), or 9 + s (the number of characters in the hh:mm:ss[.fff…] format, where s is the seconds precision).
SQL_TYPE_TIMESTAMP | 16 (the number of characters in the yyyy-mm-dd hh:mm format) 19 (the number of characters in the yyyy-mm-dd hh:mm:ss format) or 20 + s (the number of characters in the yyyy-mm-dd hh:mm:ss[.fff…] format, where s is the seconds precision).
SQL_INTERVAL_SECOND | Where p is the interval leading precision and s is the seconds precision, p (if s=0) or p+s+1 (if s>0).[d]
SQL_INTERVAL_DAY_TO_SECOND | Where p is the interval leading precision and s is the seconds precision, 9+p (if s=0) or 10+p+s (if s>0).[d]
SQL_INTERVAL_HOUR_TO_SECOND | Where p is the interval leading precision and s is the seconds precision, 6+p (if s=0) or 7+p+s (if s>0).[d]
SQL_INTERVAL_MINUTE_TO_SECOND | Where p is the interval leading precision and s is the seconds precision, 3+p (if s=0) or 4+p+s (if s>0).[d]
SQL_INTERVAL_YEAR SQL_INTERVAL_MONTH SQL_INTERVAL_DAY SQL_INTERVAL_HOUR SQL_INTERVAL_MINUTE | p, where p is the interval leading precision.[d]
SQL_INTERVAL_YEAR_TO_MONTH SQL_INTERVAL_DAY_TO_HOUR | 3+p, where p is the interval leading precision.[d]
SQL_INTERVAL_DAY_TO_MINUTE | 6+p, where p is the interval leading precision.[d]
SQL_INTERVAL_HOUR_TO_MINUTE | 3+p, where p is the interval leading precision.[d]
SQL_GUID | 36 (the number of characters in the aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee format)

[a] For an ODBC 1.0 application calling SQLSetParam in an ODBC 2.0 driver, and for an ODBC 2.0 application calling SQLBindParameter in an ODBC 1.0 driver, when *StrLen_or_IndPtr is SQL_DATA_AT_EXEC for a SQL_LONGVARCHAR or SQL_LONGVARBINARY type, ColumnSize must be set to the total length of the data to be sent, not the precision as defined in this table.

[b] If the driver cannot determine the column or parameter length for a variable type, it returns SQL_NO_TOTAL.

[c] The ColumnSize argument of SQLBindParameter is ignored for this data type.

[d] For general rules about column length in interval data types, see "Interval Data Type Length," earlier in this appendix.

The values returned for the column (or parameter) size do not correspond to the values in any one descriptor field. The values can come from either the SQL_DESC_PRECISION or the SQL_DESC_LENGTH field, depending on the type of data, as shown in the following table.

SQL type | Descriptor field corresponding to column or parameter size
All character and binary types | LENGTH
All numeric types | PRECISION
All datetime and interval types | LENGTH
SQL_BIT | LENGTH


Decimal Digits

The decimal digits of decimal and numeric data types is defined as the maximum number of digits to the right of the decimal point, or the scale of the data. For approximate floating-point number columns or parameters, the scale is undefined because the number of digits to the right of the decimal point is not fixed. For datetime or interval data that contains a seconds component, the decimal digits is defined as the number of digits to the right of the decimal point in the seconds component of the data.

For the SQL_DECIMAL and SQL_NUMERIC data types, the maximum scale is usually the same as the maximum precision. However, some data sources impose a separate limit on the maximum scale. To determine the minimum and maximum scales allowed for a data type, an application calls SQLGetTypeInfo.

The decimal digits defined for each concise SQL data type is shown in the following table.

SQL type | Decimal digits
All character and binary types[a] | n/a
SQL_DECIMAL SQL_NUMERIC | The defined number of digits to the right of the decimal point. For example, the scale of a column defined as NUMERIC(10,3) is 3. This can be a negative number to support storage of very large numbers without using exponential notation; for example, "12000" could be stored as "12" with a scale of –3.
All exact numeric types other than SQL_DECIMAL and SQL_NUMERIC[a] | 0
All approximate data types[a] | n/a
SQL_TYPE_DATE, and all interval types with no seconds component[a] | n/a
All datetime types except SQL_TYPE_DATE, and all interval types with a seconds component | The number of digits to the right of the decimal point in the seconds part of the value (fractional seconds). This number cannot be negative.
SQL_GUID | n/a

[a] The DecimalDigits argument of SQLBindParameter is ignored for this data type.

The values returned for the decimal digits do not correspond to the values in any one descriptor field. The values can come from either the SQL_DESC_SCALE or the SQL_DESC_PRECISION field, depending on the data type, as shown in the following table.

SQL type | Descriptor field corresponding to decimal digits
All character and binary types | n/a
All exact numeric types | SCALE
SQL_BIT | n/a
All approximate numeric types | n/a
All datetime types | PRECISION
All interval types with a seconds component | PRECISION
All interval types with no seconds component | n/a


Transfer Octet Length

The transfer octet length of a column is the maximum number of bytes returned to the application when data is transferred to its default C data type. For character data, the transfer octet length does not include space for the null-termination character. The transfer octet length of a column may be different than the number of bytes required to store the data on the data source.

The transfer octet length defined for each ODBC SQL data type is shown in the following table.

SQL type identifier | Length
All character types[a] | The defined or the maximum (for variable type) length of the column in bytes. This is the same value as the descriptor field SQL_DESC_OCTET_LENGTH.
SQL_DECIMAL SQL_NUMERIC | The number of bytes required to hold the character representation of this data if the character set is ANSI, and twice this number if the character set is UNICODE. This is the maximum number of digits plus two, because the data is returned as a character string and characters are needed for the digits, a sign, and a decimal point. For example, the transfer length of a column defined as NUMERIC(10,3) is 12.
SQL_TINYINT | 1
SQL_SMALLINT | 2
SQL_INTEGER | 4
SQL_BIGINT | The number of bytes required to hold the character representation of this data if the character set is ANSI, and twice this number if the character set is UNICODE, because this data type is returned as a character string by default. The character representation consists of 20 characters: 19 for digits and a sign, if signed, or 20 digits, if unsigned. Therefore, the length is 20.
SQL_REAL | 4
SQL_FLOAT | 8
SQL_DOUBLE | 8
SQL_BIT | 1
All binary types[a] | The number of bytes required to hold the defined (for fixed types) or maximum (for variable types) number of characters.
SQL_TYPE_DATE SQL_TYPE_TIME | 6 (the size of the SQL_DATE_STRUCT or SQL_TIME_STRUCT structure).
SQL_TYPE_TIMESTAMP | 16 (the size of the SQL_TIMESTAMP_STRUCT structure).
All interval data types | 34 (the size of the interval structure).
SQL_GUID | 16 (the size of the GUID structure).

[a] If the driver cannot determine the column or parameter length for variable types, it returns SQL_NO_TOTAL.


Display Size

The display size of a column is the maximum number of characters needed to display data in character form. The following table defines the display size for each ODBC SQL data type.

SQL type identifier | Display size
All character types[a] | The defined (for fixed types) or maximum (for variable types) number of characters needed to display the data in character form.
SQL_DECIMAL SQL_NUMERIC | The precision of the column plus 2 (a sign, precision digits, and a decimal point). For example, the display size of a column defined as NUMERIC(10,3) is 12.
SQL_BIT | 1 (1 digit).
SQL_TINYINT | 4 if signed (a sign and 3 digits) or 3 if unsigned (3 digits).
SQL_SMALLINT | 6 if signed (a sign and 5 digits) or 5 if unsigned (5 digits).
SQL_INTEGER | 11 if signed (a sign and 10 digits) or 10 if unsigned (10 digits).
SQL_BIGINT | 20 (a sign and 19 digits if signed or 20 digits if unsigned).
SQL_REAL | 14 (a sign, 7 digits, a decimal point, the letter E, a sign, and 2 digits).
SQL_FLOAT SQL_DOUBLE | 24 (a sign, 15 digits, a decimal point, the letter E, a sign, and 3 digits).
All binary types[a] | The defined or maximum (for variable types) length of the column times 2. (Each binary byte is represented by a 2-digit hexadecimal number.)
SQL_TYPE_DATE | 10 (a date in the format yyyy-mm-dd).
SQL_TYPE_TIME | 8 (a time in the format hh:mm:ss) - or - 9 + s (a time in the format hh:mm:ss[.fff…], where s is the fractional seconds precision).
SQL_TYPE_TIMESTAMP | 19 (for a timestamp in the yyyy-mm-dd hh:mm:ss format) - or - 20 + s (for a timestamp in the yyyy-mm-dd hh:mm:ss[.fff…] format, where s is the fractional seconds precision).
All interval data types | See the "Interval Data Type Length" section, earlier in this appendix.
SQL_GUID | 36 (the number of characters in the aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee format

[a] If the driver cannot determine the column or parameter length of variable types, it returns SQL_NO_TOTAL.


Converting Data from SQL to C Data Types

When an application calls SQLFetch, SQLFetchScroll, or SQLGetData, the driver retrieves the data from the data source. If necessary, it converts the data from the data type in which the driver retrieved it to the data type specified by the TargetType argument in SQLBindCol or SQLGetData. Finally, it stores the data in the location pointed to by the TargetValuePtr argument in SQLBindCol or SQLGetData (and the SQL_DESC_DATA_PTR field of the ARD).

The following table shows the supported conversions from ODBC SQL data types to ODBC C data types. A filled circle indicates the default conversion for an SQL data type (the C data type to which the data will be converted when the value of TargetType is SQL_C_DEFAULT). A hollow circle indicates a supported conversion.

For an ODBC 3.x application working with an ODBC 2.x driver, conversion from driver-specific data types might not be supported.

The format of the converted data is not affected by the Windows® country setting.

The tables in the following sections describe how the driver or data source converts data retrieved from the data source; drivers are required to support conversions to all ODBC C data types from the ODBC SQL data types that they support. For a given ODBC SQL data type, the first column of the table lists the legal input values of the TargetType argument in SQLBindCol and SQLGetData. The second column lists the outcomes of a test, often using the BufferLength argument specified in SQLBindCol or SQLGetData, which the driver performs to determine whether it can convert the data. For each outcome, the third and fourth columns list the values placed in the buffers specified by the TargetValuePtr and StrLen_or_IndPtr arguments specified in SQLBindCol or SQLGetData after the driver has attempted to convert the data. (The StrLen_or_IndPtr argument corresponds to the SQL_DESC_OCTET_LENGTH_PTR field of the ARD.) The last column lists the SQLSTATE returned for each outcome by SQLFetch, SQLFetchScroll, or SQLGetData.

If the TargetType argument in SQLBindCol or SQLGetData contains an identifier for an ODBC C data type not shown in the table for a given ODBC SQL data type, SQLFetch, SQLFetchScroll, or SQLGetData returns SQLSTATE 07006 (Restricted data type attribute violation). If the TargetType argument contains an identifier that specifies a conversion from a driver-specific SQL data type to an ODBC C data type and this conversion is not supported by the driver, SQLFetch, SQLFetchScroll, or SQLGetData returns SQLSTATE HYC00 (Optional feature not implemented).

Although it is not shown in the tables, the driver returns SQL_NULL_DATA in the buffer specified by the StrLen_or_IndPtr argument when the SQL data value is NULL. For an explanation of the use of StrLen_or_IndPtr when multiple calls are made to retrieve data, see the SQLGetData function description. When SQL data is converted to character C data, the character count returned in *StrLen_or_IndPtr does not include the null-termination byte. If TargetValuePtr is a null pointer, SQLGetData returns SQLSTATE HY009 (Invalid use of null pointer); in SQLBindCol, this unbinds the column.

The following terms and conventions are used in the tables:

  • Byte length of data is the number of bytes of C data available to return in *TargetValuePtr, whether or not the data will be truncated before it is returned to the application. For string data, this does not include the space for the null-termination character.
  • Character byte length is the total number of bytes needed to display the data in character format. This is as defined for each C data type in the section "Display Size," earlier in this appendix, except character byte length is in bytes while the display size is in characters.
  • Words in italics represent function arguments or elements of the SQL grammar. For the syntax of grammar elements, see Appendix C: SQL Grammar.


SQL to C: Character

The identifiers for the character ODBC SQL data types are:

SQL_CHAR
SQL_VARCHAR
SQL_LONGVARCHAR
SQL_WCHAR
SQL_WVARCHAR
SQL_WLONGVARCHAR

The following table shows the ODBC C data types to which character SQL data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from SQL to C Data Types," earlier in this appendix.

C type identifier | Test | *TargetValuePtr | *StrLen_or_IndPtr | SQLSTATE
SQL_C_CHAR | Byte length of data < BufferLength Byte length of data >= BufferLength | Data Truncated data | Length of data in bytes Length of data in bytes | n/a 01004
SQL_C_WCHAR | Character length of data < BufferLength Character length of data >= BufferLength | Data Truncated data | Length of data in characters Length of data in characters | n/a 01004
SQL_C_STINYINT SQL_C_UTINYINT SQL_C_TINYINT SQL_C_SBIGINT SQL_C_UBIGINT SQL_C_SSHORT SQL_C_USHORT SQL_C_SHORT SQL_C_SLONG SQL_C_ULONG SQL_C_LONG SQL_C_NUMERIC | Data converted without truncation[b] Data converted with truncation of fractional digits[a] Conversion of data would result in loss of whole (as opposed to fractional) digits[a] Data is not a numeric-literal[b] | Data Truncated data Undefined Undefined | Number of bytes of the C data type Number of bytes of the C data type Undefined Undefined | n/a 01S07 22003 22018
SQL_C_FLOAT SQL_C_DOUBLE | Data is within the range of the data type to which the number is being converted[a] Data is outside the range of the data type to which the number is being converted[a] Data is not a numeric-literal[b] | Data Undefined Undefined | Size of the C data type Undefined Undefined | n/a 22003 22018
SQL_C_BIT | Data is 0 or 1 Data is greater than 0, less than 2, and not equal to 1 Data is less than 0 or greater than or equal to 2 Data is not a numeric-literal | Data Truncated data Undefined Undefined | 1[b] 1[b] Undefined Undefined | n/a 01S07 22003 22018
SQL_C_BINARY | Byte length of data <= BufferLength Byte length of data > BufferLength | Data Truncated data | Length of data in bytes Length of data | n/a 01004
SQL_C_TYPE_DATE | Data value is a valid date-value[a] Data value is a valid timestamp-value; time portion is zero[a] Data value is a valid timestamp-value; time portion is nonzero[a],[c] Data value is not a valid date-value or timestamp-value[a] | Data Data Truncated data Undefined | 6[b] 6[b] 6[b] Undefined | n/a n/a 01S07 22018
SQL_C_TYPE_TIME | Data value is a valid time-value and the fractional seconds value is 0[a] Data value is a valid timestamp-value or a valid time-value; fractional seconds portion is zero[a],[d] Data value is a valid timestamp-value; fractional seconds portion is nonzero[a],[d],[e] Data value is not a valid time-value or timestamp-value[a] | Data Data Truncated data Undefined | 6[b] 6[b] 6[b] Undefined | n/a n/a 01S07 22018
SQL_C_TYPE_TIMESTAMP | Data value is a valid timestamp-value or a valid time-value; fractional seconds portion not truncated[a] Data value is a valid timestamp-value or a valid time-value; fractional seconds portion truncated[a] Data value is a valid date-value[a] Data value is a valid time-value[a] Data value is not a valid date-value, time-value, or timestamp-value[a] | Data Truncated data Data[f] Data[g] Undefined | 16[b] 16[b] 16[b] 16[b] Undefined | n/a 01S07 n/a n/a 22018
All C interval types | Data value is a valid interval value; no truncation Data value is a valid interval value; truncation of one or more trailing fields Data is valid interval; leading field significant precision is lost The data value is not a valid interval value | Data Truncated data Undefined Undefined | Length of data in bytes Length of data in bytes Undefined Undefined | n/a 01S07 22015 22018

[a] The value of BufferLength is ignored for this conversion. The driver assumes that the size of
*TargetValuePtr is the size of the C data type.

[b] This is the size of the corresponding C data type.

[c] The time portion of the timestamp-value is truncated.

[d] The date portion of the timestamp-value is ignored.

[e] The fractional seconds portion of the timestamp is truncated.

[f] The time fields of the timestamp structure are set to zero.

[g] The date fields of the timestamp structure are set to the current date.

When character SQL data is converted to numeric, date, time, timestamp, or interval C data, leading and trailing spaces are ignored.


SQL to C: Numeric

The identifiers for the numeric ODBC SQL data types are:

SQL_DECIMAL SQL_BIGINT
SQL_NUMERIC SQL_REAL
SQL_TINYINT SQL_FLOAT
SQL_SMALLINT SQL_DOUBLE
SQL_INTEGER

The following table shows the ODBC C data types to which numeric SQL data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from SQL to C Data Types."

C type identifier | Test | *TargetValuePtr | *StrLen_or_IndPtr | SQLSTATE
SQL_C_CHAR | Character byte length < BufferLength Number of whole (as opposed to fractional) digits < BufferLength Number of whole (as opposed to fractional) digits >= BufferLength | Data Truncated data Undefined | Length of data in bytes Length of data in bytes Undefined | n/a 01004 22003
SQL_C_WCHAR | Character length < BufferLength Number of whole (as opposed to fractional) digits < BufferLength Number of whole (as opposed to fractional) digits >= BufferLength | Data Truncated data Undefined | Length of data in characters Length of data in characters Undefined | n/a 01004 22003
SQL_C_STINYINT SQL_C_UTINYINT SQL_C_TINYINT SQL_C_SBIGINT SQL_C_UBIGINT SQL_C_SSHORT SQL_C_USHORT SQL_C_SHORT SQL_C_SLONG SQL_C_ULONG SQL_C_LONG SQL_C_NUMERIC | Data converted without truncation[a] Data converted with truncation of fractional digits[a] Conversion of data would result in loss of whole (as opposed to fractional) digits[a] | Data Truncated data Undefined | Size of the C data type Size of the C data type Undefined | n/a 01S07 22003
SQL_C_FLOAT SQL_C_DOUBLE | Data is within the range of the data type to which the number is being converted[a] Data is outside the range of the data type to which the number is being converted[a] | Data Undefined | Size of the C data type Undefined | n/a 22003
SQL_C_BIT | Data is 0 or 1[a] Data is greater than 0, less than 2, and not equal to 1[a] Data is less than 0 or greater than or equal to 2[a] | Data Truncated data Undefined | 1[b] 1[b] Undefined | n/a 01S07 22003
SQL_C_BINARY | Byte length of data <= BufferLength Byte length of data > BufferLength | Data Undefined | Length of data Undefined | n/a 22003
SQL_C_INTERVAL_MONTH[c] SQL_C_INTERVAL_YEAR[c] SQL_C_INTERVAL_DAY[c] SQL_C_INTERVAL_HOUR[c] SQL_C_INTERVAL_MINUTE[c] SQL_C_INTERVAL_SECOND[c] | Data not truncated Fractional seconds portion truncated Whole part of number truncated | Data Truncated data Undefined | Length of data in bytes Length of data in bytes Undefined | n/a 01S07 22015
SQL_C_INTERVAL_YEAR_TO_MONTH SQL_C_INTERVAL_DAY_TO_HOUR SQL_C_INTERVAL_DAY_TO_MINUTE SQL_C_INTERVAL_DAY_TO_SECOND SQL_C_INTERVAL_HOUR_TO_MINUTE SQL_C_INTERVAL_HOUR_TO_SECOND | Whole part of number truncated | Undefined | Undefined | 22015

[a] The value of BufferLength is ignored for this conversion. The driver assumes that the size of *TargetValuePtr is the size of the C data type.

[b] This is the size of the corresponding C data type.

[c] This conversion is supported only for the exact numeric data types (SQL_DECIMAL, SQL_NUMERIC, SQL_TINYINT, SQL_SMALLINT, SQL_INTEGER, and SQL_BIGINT). It is not supported for the approximate numeric data types (SQL_REAL, SQL_FLOAT, or SQL_DOUBLE).


SQL to C: Bit

The identifier for the bit ODBC SQL data type is:

SQL_BIT

The following table shows the ODBC C data types to which bit SQL data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from SQL to C Data Types."

C type identifier | Test | *TargetValuePtr | *StrLen_or_IndPtr | SQLSTATE
SQL_C_CHAR SQL_C_WCHAR | BufferLength > 1 BufferLength <= 1 | Data Undefined | 1 Undefined | n/a 22003
SQL_C_STINYINT SQL_C_UTINYINT SQL_C_TINYINT SQL_C_SBIGINT SQL_C_UBIGINT SQL_C_SSHORT SQL_C_USHORT SQL_C_SHORT SQL_C_SLONG SQL_C_ULONG SQL_C_LONG SQL_C_FLOAT SQL_C_DOUBLE SQL_C_NUMERIC | None[a] | Data | Size of the C data type | n/a
SQL_C_BIT | None[a] | Data | 1[b] | n/a
SQL_C_BINARY | BufferLength >= 1 BufferLength < 1 | Data Undefined | 1 Undefined | n/a 22003

[a] The value of BufferLength is ignored for this conversion. The driver assumes that the size of *TargetValuePtr is the size of the C data type.

[b] This is the size of the corresponding C data type.

When bit SQL data is converted to character C data, the possible values are "0" and "1".


SQL to C: Binary

The identifiers for the binary ODBC SQL data types are:

SQL_BINARY
SQL_VARBINARY
SQL_LONGVARBINARY

The following table shows the ODBC C data types to which binary SQL data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from SQL to C Data Types."

C type identifier | Test | *TargetValuePtr | *StrLen_or_IndPtr | SQLSTATE
SQL_C_CHAR | (Byte length of data) * 2 < BufferLength (Byte length of data) * 2 >= BufferLength | Data Truncated data | Length of data in bytes Length of data in bytes | n/a 01004
SQL_C_WCHAR | (Character length of data) * 2 < BufferLength (Character length of data) * 2 >= BufferLength | Data Truncated data | Length of data in characters Length of data in characters | n/a 01004
SQL_C_BINARY | Byte length of data <= BufferLength Byte length of data > BufferLength | Data Truncated data | Length of data in bytes Length of data in bytes | n/a 01004

When binary SQL data is converted to character C data, each byte (8 bits) of source 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".

The driver always converts individual bytes to pairs of hexadecimal digits and terminates the character string with a null byte. Because of this, if BufferLength is even and is less than the length of the converted data, the last byte of the *TargetValuePtr buffer is not used. (The converted data requires an even number of bytes, the next-to-last byte is a null byte, and the last byte cannot be used.)

Note Application developers are discouraged from binding binary SQL data to a character C data type. This conversion is usually inefficient and slow.


SQL to C: Date

The identifier for the date ODBC SQL data type is:

SQL_TYPE_DATE

The following table shows the ODBC C data types to which date SQL data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from SQL to C Data Types."

C type identifier | Test | *TargetValuePtr | *StrLen_or_IndPtr | SQLSTATE
SQL_C_CHAR | BufferLength > Character byte length 11 <= BufferLength <= Character byte length BufferLength < 11 | Data Truncated data Undefined | 10 Length of data in bytes Undefined | n/a 01004 22003
SQL_C_WCHAR | BufferLength > Character length 11 <= BufferLength <= Character length BufferLength < 11 | Data Truncated data Undefined | 10 Length of data in characters Undefined | n/a 01004 22003
SQL_C_BINARY | Byte length of data <= BufferLength Byte length of data > BufferLength | Data Undefined | Length of data in bytes Undefined | n/a 22003
SQL_C_TYPE_DATE | None[a] | Data | 6[c] | n/a
SQL_C_TYPE_TIMESTAMP | None[a] | Data[b] | 16[c] | n/a

[a] The value of BufferLength is ignored for this conversion. The driver assumes that the size of *TargetValuePtr is the size of the C data type.

[b] The time fields of the timestamp structure are set to zero.

[c] This is the size of the corresponding C data type.

When date SQL data is converted to character C data, the resulting string is in the "yyyy-mm-dd" format. This format is not affected by the Windows® country setting.


SQL to C: GUID

The identifier for the GUID ODBC SQL data type is:

SQL_GUID

The following table shows the ODBC C data types to which GUID SQL data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from SQL to C Data Types."

C type identifier | Test | *TargetValuePtr | *StrLen_or _IndPtr | SQLSTATE
SQL_C_CHAR | BufferLength > Character byte length | Data | 36 | n/a
 | BufferLength < 37 | Undefined | Undefined | 22003
SQL_C_WCHAR | BufferLength > Character length | Data | 36 | n/a
 | BufferLength < 37 | Undefined | Undefined | 22003
SQL_C_BINARY | Byte length of data <= BufferLength | Data | Length of data in bytes | n/a
 | Byte length of data > BufferLength | Undefined | Undefined | 22003
SQL_C_GUID | None[a] | Data | 16[b] | n/a

[a] The value of BufferLength is ignored for this conversion. The driver assumes that the size of *TargetValuePtr is the size of the C data type.

[b] This is the size of the corresponding C data type.


SQL to C: Time

The identifier for the time ODBC SQL data type is:

SQL_TYPE_TIME

The following table shows the ODBC C data types to which time SQL data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from SQL to C Data Types."

C type identifier | Test | *TargetValuePtr | *StrLen_or_IndPtr | SQLSTATE
SQL_C_CHAR | BufferLength > Character byte length 9 <= BufferLength <= Character byte length BufferLength < 9 | Data Truncated data[a] Undefined | Length of data in bytes Length of data in bytes Undefined | n/a 01004 22003
SQL_C_WCHAR | BufferLength > Character length 9 <= BufferLength <= Character length BufferLength < 9 | Data Truncated data[a] Undefined | Length of data in characters Length of data in characters Undefined | n/a 01004 22003
SQL_C_BINARY | Byte length of data <= BufferLength Byte length of data > BufferLength | Data Undefined | Length of data in bytes Undefined | n/a 22003
SQL_C_TYPE_TIME | None[b] | Data | 6[d] | n/a
SQL_C_TYPE_TIMESTAMP | None[b] | Data[c] | 16[d] | n/a

[a] The fractional seconds of the time are truncated.

[b] The value of BufferLength is ignored for this conversion. The driver assumes that the size of *TargetValuePtr is the size of the C data type.

[c] The date fields of the timestamp structure are set to the current date, and the fractional seconds field of the timestamp structure is set to zero.

[d] This is the size of the corresponding C data type.

When time SQL data is converted to character C data, the resulting string is in the "hh:mm:ss" format. This format is not affected by the Windows® country setting.


SQL to C: Timestamp

The identifier for the timestamp ODBC SQL data type is:

SQL_TYPE_TIMESTAMP

The following table shows the ODBC C data types to which timestamp SQL data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from SQL to C Data Types."

C type identifier | Test | *TargetValuePtr | *StrLen_or_IndPtr | SQLSTATE
SQL_C_CHAR | BufferLength > Character byte length 20 <= BufferLength <= Character byte length BufferLength < 20 | Data Truncated data[b] Undefined | Length of data in bytes Length of data in bytes Undefined | n/a 01004 22003
SQL_C_WCHAR | BufferLength > Character length 20 <= BufferLength <= Character length BufferLength < 20 | Data Truncated data[b] Undefined | Length of data in characters Length of data in characters Undefined | n/a 01004 22003
SQL_C_BINARY | Byte length of data <= BufferLength Byte length of data > BufferLength | Data Undefined | Length of data in bytes Undefined | n/a 22003
SQL_C_TYPE_DATE | Time portion of timestamp is zero[a] Time portion of timestamp is nonzero[a] | Data Truncated data[c] | 6[f] 6[f] | n/a 01S07
SQL_C_TYPE_TIME | Fractional seconds portion of timestamp is zero[a] Fractional seconds portion of timestamp is nonzero[a] | Data[d] Truncated data[d], [e] | 6[f] 6[f] | n/a 01S07
SQL_C_TYPE_TIMESTAMP | Fractional seconds portion of timestamp is not truncated[a] Fractional seconds portion of timestamp is truncated[a] | Data[e] Truncated data[e] | 16[f] 16[f] | n/a 01S07

[a] The value of BufferLength is ignored for this conversion. The driver assumes that the size of *TargetValuePtr is the size of the C data type.

[b] The fractional seconds of the timestamp are truncated.

[c] The time portion of the timestamp is truncated.

[d] The date portion of the timestamp is ignored.

[e] The fractional seconds portion of the timestamp is truncated.

[f] This is the size of the corresponding C data type.

When timestamp SQL data is converted to character C data, the resulting string is in the "yyyy-mm-dd hh:mm:ss[.f...]" format, where up to nine digits may be used for fractional seconds. This format is not affected by the Windows® country setting. (Except for the decimal point and fractional seconds, the entire format must be used, regardless of the precision of the timestamp SQL data type.)


SQL to C: Year-Month Intervals

The identifiers for the year-month interval ODBC SQL data types are:

SQL_INTERVAL_YEAR
SQL_INTERVAL_MONTH
SQL_INTERVAL_YEAR_TO_MONTH

The following table shows the ODBC C data types to which year-month interval SQL data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from SQL to C Data Types."

C type identifier | Test | *TargetValuePtr | *StrLen_or_IndPtr | SQLSTATE
SQL_C_INTERVAL_MONTH[a] SQL_C_INTERVAL_YEAR[a] SQL_C_INTERVAL_YEAR_TO_MONTH[a] | Trailing fields portion not truncated Trailing fields portion truncated Leading precision of target is not big enough to hold data from source | Data Truncated data Undefined | Length of data in bytes Length of data in bytes Undefined | n/a 01S07 22015
SQL_C_STINYINT[b] SQL_C_UTINYINT[b] SQL_C_USHORT[b] SQL_C_SHORT[b] SQL_C_SLONG[b] SQL_C_ULONG[b] SQL_C_NUMERIC[b] SQL_C_BIGINT[b] | Interval precision was a single field and the data was converted without truncation Interval precision was a single field and truncated whole Interval precision was not a single field | Data Truncated data Undefined | Size of the C data type Length of data in bytes Size of the C data type | n/a 22003 22015
SQL_C_BINARY | Byte length of data <= BufferLength Byte length of data > BufferLength | Data Undefined | Length of data in bytes Undefined | n/a 22003
SQL_C_CHAR | Character byte length < BufferLength Number of whole (as opposed to fractional) digits < BufferLength Number of whole (as opposed to fractional) digits >= BufferLength | Data Truncated data Undefined | Size of the C data type Size of the C data type Undefined | n/a 01004 22003
SQL_C_WCHAR | Character length < BufferLength Number of whole (as opposed to fractional) digits < BufferLength Number of whole (as opposed to fractional) digits >= BufferLength | Data Truncated data Undefined | Size of the C data type Size of the C data type Undefined | n/a 01004 22003

[a] A year-month interval SQL type can be converted to any year-month interval C type.

[b] If the interval precision is a single field (one of YEAR or MONTH), the interval SQL type can be converted to any exact numeric (SQL_C_STINYINT, SQL_C_UTINYINT, SQL_C_USHORT, SQL_C_SHORT, SQL_C_SLONG, SQL_C_ULONG, or SQL_C_NUMERIC).

The default conversion of an interval SQL type is to the corresponding C interval data type. The application then binds the column or parameter (or sets the SQL_DESC_DATA_PTR field in the appropriate record of the ARD) to point to the initialized SQL_INTERVAL_STRUCT structure (or passes a pointer to the SQL_ INTERVAL_STRUCT structure as the TargetValuePtr argument in a call to SQLGetData).


SQL to C: Day-Time Intervals

The identifiers for the day-time interval ODBC SQL data types are:

SQL_INTERVAL_DAY | SQL_INTERVAL_DAY_TO_MINUTE
SQL_INTERVAL_HOUR | SQL_INTERVAL_DAY_TO_SECOND
SQL_INTERVAL_MINUTE | SQL_INTERVAL_HOUR_TO_MINUTE
SQL_INTERVAL_SECOND | SQL_INTERVAL_HOUR_TO_SECOND
SQL_INTERVAL_DAY_TO_HOUR | SQL_INTERVAL_MINUTE_TO_SECOND

The following table shows the ODBC C data types to which day-time interval SQL data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from SQL to C Data Types."

C type identifier | Test | *TargetValuePtr | *StrLen_or_IndPtr | SQLSTATE
All day-time C interval types | Trailing fields portion not truncated Trailing fields portion truncated Leading precision of target is not big enough to hold data from source | Data Truncated data Undefined | Length of data Length of data Undefined | n/a 01S07 22015
SQL_C_STINYINT[b] SQL_C_UTINYINT[b] SQL_C_USHORT[b] SQL_C_SHORT[b] SQL_C_SLONG[b] SQL_C_ULONG[b] SQL_C_NUMERIC[b] SQL_C_BIGINT[b] | Interval precision was a single field and the data was converted without truncation Interval precision was a single field and truncated fractional Interval precision was a single field and truncated whole Interval precision was not a single field | Data Truncated data Truncated data Undefined | Size of the C data type Length of data Length of data Size of the C data type | n/a 01S07 22003 07006
SQL_C_BINARY | Byte length of data <= BufferLength Byte length of data > BufferLength | Data Undefined | Length of data Undefined | n/a 22003
SQL_C_CHAR | Character byte length < BufferLength Number of whole (as opposed to fractional) digits < BufferLength Number of whole (as opposed to fractional) digits >= BufferLength | Data Truncated data Undefined | Size of the C data type Size of the C data type Undefined | n/a 01004 22003
SQL_C_WCHAR | Character length < BufferLength Number of whole (as opposed to fractional) digits < BufferLength Number of whole (as opposed to fractional) digits >= BufferLength | Data Truncated data Undefined | Size of the C data type Size of the C data type Undefined | n/a 01004 22003

[a] A day-time interval SQL type can be converted to any day-time interval C type.

[b] If the interval precision is a single field (one of DAY, HOUR, MINUTE, or SECOND), the interval SQL type can be converted to any exact numeric (SQL_C_STINYINT, SQL_C_UTINYINT, SQL_C_USHORT, SQL_C_SHORT, SQL_C_SLONG, SQL_C_ULONG, or SQL_C_NUMERIC).

The default conversion of an interval SQL type is to the corresponding C interval data type. The application then binds the column or parameter (or sets the SQL_DESC_DATA_PTR field in the appropriate record of the ARD) to point to the initialized SQL_INTERVAL_STRUCT structure (or passes a pointer to the SQL_ INTERVAL_STRUCT structure as the TargetValuePtr argument in a call to SQLGetData).

The following example demonstrates how to transfer data from a column of type SQL_INTERVAL_DAY_TO_MINUTE into the SQL_INTERVAL_STRUCT structure such that it comes back as a DAY_TO_HOUR interval.

SQL_INTERVAL_STRUCT is;
SQLINTEGER    cbValue;
SQLUINTEGER   days, hours;

// Execute a select statement; "interval_column" is a column
// whose data type is SQL_INTERVAL_DAY_TO_MINUTE.
SQLExecDirect(hstmt, "SELECT interval_column FROM table", SQL_NTS);

// Bind
SQLBindCol(hstmt, 1, SQL_C_INTERVAL_DAY_TO_MINUTE, &is, sizeof(SQL_INTERVAL_STRUCT), &cbValue);

// Fetch
SQLFetch(hstmt);

// Process data
days = is.intval.day_second.day;
hours = is.intval.day_second.hour;


SQL to C Data Conversion Examples

The examples shown in the following table illustrate how the driver converts SQL data to C data:

SQL type identifier | SQL data value | C type identifier | Buffer length | *TargetValuePtr | SQLSTATE
SQL_CHAR | abcdef | SQL_C_CHAR | 7 | abcdef\0[a] | n/a
SQL_CHAR | abcdef | SQL_C_CHAR | 6 | abcde\0[a] | 01004
SQL_DECIMAL | 1234.56 | SQL_C_CHAR | 8 | 1234.56\0[a] | n/a
SQL_DECIMAL | 1234.56 | SQL_C_CHAR | 5 | 1234\0[a] | 01004
SQL_DECIMAL | 1234.56 | SQL_C_CHAR | 4 | ---- | 22003
SQL_DECIMAL | 1234.56 | SQL_C_FLOAT | ignored | 1234.56 | n/a
SQL_DECIMAL | 1234.56 | SQL_C_SSHORT | ignored | 1234 | 01S07
SQL_DECIMAL | 1234.56 | SQL_C_STINYINT | ignored | ---- | 22003
SQL_DOUBLE | 1.2345678 | SQL_C_DOUBLE | ignored | 1.2345678 | n/a
SQL_DOUBLE | 1.2345678 | SQL_C_FLOAT | ignored | 1.234567 | n/a
SQL_DOUBLE | 1.2345678 | SQL_C_STINYINT | ignored | 1 | n/a
SQL_TYPE_DATE | 1992-12-31 | SQL_C_CHAR | 11 | 1992-12-31\0[a] | n/a
SQL_TYPE_DATE | 1992-12-31 | SQL_C_CHAR | 10 | ----- | 22003
SQL_TYPE_DATE | 1992-12-31 | SQL_C_TIMESTAMP | ignored | 1992,12,31, 0,0,0,0[b] | n/a
SQL_TYPE_TIMESTAMP | 1992-12-31 23:45:55.12 | SQL_C_CHAR | 23 | 1992-12-31 23:45:55.12\0[a] | n/a
SQL_TYPE_TIMESTAMP | 1992-12-31 23:45:55.12 | SQL_C_CHAR | 22 | 1992-12-31 23:45:55.1\0[a] | 01004
SQL_TYPE_TIMESTAMP | 1992-12-31 23:45:55.12 | SQL_C_CHAR | 18 | ---- | 22003

[a] "\0" represents a null-termination byte. The driver always null-terminates SQL_C_CHAR data.

[b] The numbers in this list are the numbers stored in the fields of the TIMESTAMP_STRUCT structure.


Converting Data from C to SQL Data Types

When an application calls SQLExecute or SQLExecDirect, the driver retrieves the data for any parameters bound with SQLBindParameter from storage locations in the application. When an application calls SQLSetPos, the driver retrieves the data for an update or add operation from columns bound with SQLBindCol. For data-at-execution parameters, the application sends the parameter data with SQLPutData. If necessary, the driver converts the data from the data type specified by the ValueType argument in SQLBindParameter to the data type specified by the ParameterType argument in SQLBindParameter, and then sends the data to the data source.

The following table shows the supported conversions from ODBC C data types to ODBC SQL data types. A filled circle indicates the default conversion for an SQL data type (the C data type from which the data will be converted when the value of ValueType or the SQL_DESC_CONCISE_TYPE descriptor field is SQL_C_DEFAULT). A hollow circle indicates a supported conversion.

The format of the converted data is not affected by the Windows® country setting.

The tables in the following sections describe how the driver or data source converts data sent to the data source; drivers are required to support conversions from all ODBC C data types to the ODBC SQL data types that they support. For a given ODBC C data type, the first column of the table lists the legal input values of the ParameterType argument in SQLBindParameter. The second column lists the outcomes of a test that the driver performs to determine if it can convert the data. The third column lists the SQLSTATE returned for each outcome by SQLExecDirect, SQLExecute, SQLBulkOperations, SQLSetPos, or SQLPutData. Data is sent to the data source only if SQL_SUCCESS is returned.

If the ParameterType argument in SQLBindParameter contains the identifier of an ODBC SQL data type that is not shown in the table for a given C data type, SQLBindParameter returns SQLSTATE 07006 (Restricted data type attribute violation). If the ParameterType argument contains a driver-specific identifier and the driver does not support the conversion from the specific ODBC C data type to that driver-specific SQL data type, SQLBindParameter returns SQLSTATE HYC00 (Optional feature not implemented).

If the ParameterValuePtr and StrLen_or_IndPtr arguments specified in SQLBindParameter are both null pointers, that function returns SQLSTATE HY009 (Invalid use of null pointer). Although it is not shown in the tables, an application sets the value of the length/indicator buffer pointed to by the StrLen_or_IndPtr argument of SQLBindParameter or the value of the StrLen_or_IndPtr argument of SQLPutData to SQL_NULL_DATA to specify a NULL SQL data value. (The StrLen_or_IndPtr argument corresponds to the SQL_DESC_OCTET_LENGTH_PTR field of the APD.) The application sets these values to SQL_NTS to specify that the value in *ParameterValuePtr in SQLBindParameter or *DataPtr in SQLPutData (pointed to by the SQL_DESC_DATA_PTR field of the APD) is a null-terminated string.

The following terms are used in the tables:

  • Byte length of data—The number of bytes of SQL data available to send to the data source, whether or not the data will be truncated before it is sent to the data source. For string data, this does not include space for the null-termination character.
  • Column byte length—The number of bytes required to store the data at the data source.
  • Character byte length—The maximum number of bytes needed to display data in character form. This is as defined for each SQL data type in the section "Display Size" earlier in this appendix, except character byte length is in bytes, while the display size is in characters.
  • Number of digits—The number of characters used to represent a number, including the minus sign, decimal point, and exponent (if needed).
  • Words in italics—Elements of the SQL grammar. For the syntax of grammar elements, see Appendix C: SQL Grammar.


C to SQL: Character

The identifiers for the character ODBC C data type are:

SQL_C_CHAR
SQL_C_WCHAR

The following table shows the ODBC SQL data types to which C character data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from C to SQL Data Types."

Note When character C data is converted to Unicode SQL data, the length of the Unicode data must be an even number.

SQL type identifier | Test | SQLSTATE
SQL_CHAR SQL_VARCHAR SQL_LONGVARCHAR | Byte length of data <= Column length. Byte length of data > Column length. | n/a 22001
SQL_WCHAR SQL_WVARCHAR SQL_WLONGVARCHAR | Character length of data <= Column length. Character length of data > Column length. | n/a 22001
SQL_DECIMAL SQL_NUMERIC SQL_TINYINT SQL_SMALLINT SQL_INTEGER SQL_BIGINT | Data converted without truncation Data converted with truncation of fractional digits[e] Conversion of data would result in loss of whole (as opposed to fractional) digits[e] Data value is not a numeric-literal | n/a 22001 22001 22018
SQL_REAL SQL_FLOAT SQL_DOUBLE | Data is within the range of the data type to which the number is being converted Data is outside the range of the data type to which the number is being converted Data value is not a numeric-literal | n/a 22003 22018
SQL_BIT | Data is 0 or 1 Data is greater than 0, less than 2, and not equal to 1 Data is less than 0 or greater than or equal to 2 Data is not a numeric-literal | n/a 22001 22003 22018
SQL_BINARY SQL_VARBINARY SQL_LONGVARBINARY | (Byte length of data) / 2 <= column byte length (Byte length of data) / 2 > column byte length Data value is not a hexadecimal value | n/a 22001 22018
SQL_TYPE_DATE | Data value is a valid ODBC-date-literal Data value is a valid ODBC-timestamp-literal; time portion is zero Data value is a valid ODBC-timestamp-literal; time portion is nonzero[a] Data value is not a valid ODBC-date-literal or ODBC-timestamp-literal | n/a n/a 22008 22018
SQL_TYPE_TIME | Data value is a valid ODBC-time-literal Data value is a valid ODBC-timestamp-literal; fractional seconds portion is zero[b] Data value is a valid ODBC-timestamp-literal; fractional seconds portion is nonzero[b] Data value is not a valid ODBC-time-literal or ODBC-timestamp-literal | n/a n/a 22008 22018
SQL_TYPE_TIMESTAMP | Data value is a valid ODBC- timestamp-literal; fractional seconds portion not truncated Data value is a valid ODBC-timestamp-literal; fractional seconds portion truncated Data value is a valid ODBC-date-literal[c] Data value is a valid ODBC-time-literal[d] Data value is not a valid ODBC-date-literal, ODBC-time-literal, or ODBC-timestamp-literal | n/a 22008 n/a n/a 22018
All SQL interval types | Data value is a valid interval value; no truncation occurs Data value is a valid interval value; the value in one of the fields is truncated The data value is not a valid interval literal | n/a 22015 22018

[a] The time portion of the timestamp is truncated.

[b] The date portion of the timestamp is ignored.

[c] The time portion of the timestamp is set to zero.

[d] The date portion of the timestamp is set to the current date.

[e] The driver/data source effectively waits until the entire string has been received (even if the character data is sent in pieces by calls to SQLPutData) before attempting to perform the conversion.

When character C data is converted to numeric, date, time, or timestamp SQL data, leading and trailing blanks are ignored.

When character C data is converted to binary SQL data, each two bytes of character data are converted to a single byte (8 bits) of binary data. Each two bytes of character data represent a number in hexadecimal form. For example, "01" is converted to a binary 00000001 and "FF" is converted to a binary 11111111.

The driver always converts pairs of hexadecimal digits to individual bytes and ignores the null-termination byte. Because of this, if the length of the character string is odd, the last byte of the string (excluding the null-termination byte, if any) is not converted.

Note Application developers are discouraged from binding character C data to a binary SQL data type. This conversion is usually inefficient and slow.


C to SQL: Numeric

The identifiers for the numeric ODBC C data types are:

SQL_C_STINYINT | SQL_C_SLONG
SQL_C_UTINYINT | SQL_C_ULONG
SQL_C_TINYINT | SQL_C_LONG
SQL_C_SSHORT | SQL_C_FLOAT
SQL_C_USHORT | SQL_C_DOUBLE
SQL_C_SHORT | SQL_C_NUMERIC
SQL_C_SBIGINT | SQL_C_UBIGINT

The following table shows the ODBC SQL data types to which numeric C data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from C to SQL Data Types."

SQL type identifier | Test | SQLSTATE
SQL_CHAR SQL_VARCHAR SQL_LONGVARCHAR | Number of digits <= Column byte length Number of digits > Column byte length | n/a 22001
SQL_WCHAR SQL_WVARCHAR SQL_WLONGVARCHAR | Number of characters <= Column character length Number of characters > Column character length | n/a 22001
SQL_DECIMAL[b] SQL_NUMERIC[b] SQL_TINYINT[b] SQL_SMALLINT[b] SQL_INTEGER[b] SQL_BIGINT[b] | Data converted without truncation or with truncated of fractional digits Data converted with truncation of whole digits | n/a 22003
SQL_REAL SQL_FLOAT SQL_DOUBLE | Data is within the range of the data type to which the number is being converted Data is outside the range of the data type to which the number is being converted | n/a 22003
SQL_BIT | Data is 0 or 1 Data is greater than 0, less than 2, and not equal to 1 Data is less than 0 or greater than or equal to 2 | n/a 22001 22003
SQL_INTERVAL_YEAR[a] SQL_INTERVAL_MONTH[a] SQL_INTERVAL_DAY[a] SQL_INTERVAL_HOUR[a] SQL_INTERVAL_MINUTE[a] SQL_INTERVAL_SECOND[a] | Data not truncated. Data truncated. | n/a 22015

[a] These conversions are supported only for the exact numeric data types (SQL_C_STINYINT, SQL_C_UTINYINT, SQL_C_SSHORT, SQL_C_USHORT, SQL_C_SLONG, SQL_C_ULONG, or SQL_C_NUMERIC). They are not supported for the approximate numeric data types (SQL_C_FLOAT or SQL_C_DOUBLE). Exact numeric C data types cannot be converted to an interval SQL type whose interval precision is not a single field.

[b] For the "n/a" case, a driver may optionally return SQL_SUCCESS_WITH_INFO and 01S07 when there is a fractional truncation.

The driver ignores the length/indicator value when converting data from the numeric C data types and assumes that the size of the data buffer is the size of the numeric C data type. The length/indicator value is passed in the StrLen_or_Ind argument in SQLPutData and in the buffer specified with the StrLen_or_IndPtr argument in SQLBindParameter. The data buffer is specified with the DataPtr argument in SQLPutData and the ParameterValuePtr argument in SQLBindParameter.


C to SQL: Bit

The identifier for the bit ODBC C data type is:

SQL_C_BIT

The following table shows the ODBC SQL data types to which bit C data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from C to SQL Data Types."

SQL type identifier | Test | SQLSTATE
SQL_CHAR SQL_VARCHAR SQL_LONGVARCHAR SQL_WCHAR SQL_WVARCHAR SQL_WLONGVARCHAR | None | n/a
SQL_DECIMAL SQL_NUMERIC SQL_TINYINT SQL_SMALLINT SQL_INTEGER SQL_BIGINT SQL_REAL SQL_FLOAT SQL_DOUBLE | None | n/a
SQL_BIT | None | n/a

The driver ignores the length/indicator value when converting data from the bit C data type and assumes that the size of the data buffer is the size of the bit C data type. The length/indicator value is passed in the StrLen_or_Ind argument in SQLPutData and in the buffer specified with the StrLen_or_IndPtr argument in SQLBindParameter. The data buffer is specified with the DataPtr argument in SQLPutData and the ParameterValuePtr argument in SQLBindParameter.


C to SQL: Binary

The identifier for the binary ODBC C data type is:

SQL_C_BINARY

The following table shows the ODBC SQL data types to which binary C data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from C to SQL Data Types."

SQL type identifier | Test | SQLSTATE
SQL_CHAR SQL_VARCHAR SQL_LONGVARCHAR | Byte length of data <= Column byte length Byte length of data > Column byte length | n/a 22001
SQL_WCHAR SQL_WVARCHAR SQL_WLONGVARCHAR | Character length of data <= Column character length Character length of data > Column character length | n/a 22001
SQL_DECIMAL SQL_NUMERIC SQL_TINYINT SQL_SMALLINT SQL_INTEGER SQL_BIGINT SQL_REAL SQL_FLOAT SQL_DOUBLE SQL_BIT SQL_TYPE_DATE SQL_TYPE_TIME SQL_TYPE_TIMESTAMP | Byte length of data = SQL data length Byte length of data <> SQL data length | n/a 22003
SQL_BINARY SQL_VARBINARY SQL_LONGVARBINARY | Length of data <= column length Length of data > column length | n/a 22001


C to SQL: Date

The identifier for the date ODBC C data type is:

SQL_C_TYPE_DATE

The following table shows the ODBC SQL data types to which date C data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from C to SQL Data Types."

SQL type identifier | Test | SQLSTATE
SQL_CHAR SQL_VARCHAR SQL_LONGVARCHAR | Column byte length >= 10 Column byte length < 10 Data value is not a valid date | n/a 22001 22008
SQL_WCHAR SQL_WVARCHAR SQL_WLONGVARCHAR | Column character length >= 10 Column character length < 10 Data value is not a valid date | n/a 22001 22008
SQL_TYPE_DATE | Data value is a valid date Data value is not a valid date | n/a 22007
SQL_TYPE_TIMESTAMP | Data value is a valid date[a] Data value is not a valid date | n/a 22007

[a] The time portion of the timestamp is set to zero.

For information about what values are valid in a SQL_C_TYPE_DATE structure, see the section "C Data Types," earlier in this appendix.

When date C data is converted to character SQL data, the resulting character data is in the "yyyy-mm-dd" format.

The driver ignores the length/indicator value when converting data from the date C data type and assumes that the size of the data buffer is the size of the date C data type. The length/indicator value is passed in the StrLen_or_Ind argument in SQLPutData and in the buffer specified with the StrLen_or_IndPtr argument in SQLBindParameter. The data buffer is specified with the DataPtr argument in SQLPutData and the ParameterValuePtr argument in SQLBindParameter.


C to SQL: GUID

The identifier for the GUID ODBC C data type is:

SQL_C_GUID

The following table shows the ODBC SQL data types to which GUID C data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from C to SQL Data Types."

SQL type identifier | Test | SQLSTATE
SQL_CHAR | Column byte length >= 36 | n/a
SQL_VARCHAR | Column byte length < 36 | 22001
SQL_LONGVARCHAR | Data value is not a valid GUID | 22018
SQL_WCHAR | Column character length >= 36 | n/a
SQL_WVARCHAR | Column character length < 36 | 22001
SQL_WLONGVARCHAR | Data value is not a valid GUID | 22018
SQL_GUID | None[a] | n/a

[a] All hexidecimal values are valid as a GUID.

The driver ignores the length/indicator value when converting data from the GUID C data type and assumes that the size of the data buffer is the size of the GUID C data type. The length/indicator value is passed in the StrLen_or_Ind argument in SQLPutData and in the buffer specified with the StrLen_or_IndPtr argument in SQLBindParameter. The data buffer is specified with the DataPtr argument in SQLPutData and the ParameterValuePtr argument in SQLBindParameter.


C to SQL: Time

The identifier for the time ODBC C data type is:

SQL_C_TYPE_TIME

The following table shows the ODBC SQL data types to which time C data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from C to SQL Data Types."

SQL type identifier | Test | SQLSTATE
SQL_CHAR SQL_VARCHAR SQL_LONGVARCHAR | Column byte length >= 8 Column byte length < 8 Data value is not a valid time | n/a 22001 22008
SQL_WCHAR SQL_WVARCHAR SQL_WLONGVARCHAR | Column character length >= 8 Column character length < 8 Data value is not a valid time | n/a 22001 22008
SQL_TYPE_TIME | Data value is a valid time Data value is not a valid time | n/a 22007
SQL_TYPE_TIMESTAMP | Data value is a valid time[a] Data value is not a valid time | n/a 22007

[a] The date portion of the timestamp is set to the current date, and the fractional seconds portion of the timestamp is set to zero.

For information about what values are valid in a SQL_C_TYPE_TIME structure, see the section "C Data Types," earlier in this appendix.

When time C data is converted to character SQL data, the resulting character data is in the "hh:mm:ss" format.

The driver ignores the length/indicator value when converting data from the time C data type and assumes that the size of the data buffer is the size of the time C data type. The length/indicator value is passed in the StrLen_or_Ind argument in SQLPutData and in the buffer specified with the StrLen_or_IndPtr argument in SQLBindParameter. The data buffer is specified with the DataPtr argument in SQLPutData and the ParameterValuePtr argument in SQLBindParameter.


C to SQL: Timestamp

The identifier for the timestamp ODBC C data type is:

SQL_C_TYPE_TIMESTAMP

The following table shows the ODBC SQL data types to which timestamp C data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from C to SQL Data Types."

SQL type identifier | Test | SQLSTATE
SQL_CHAR SQL_VARCHAR SQL_LONGVARCHAR | Column byte length >= Character byte length 19 <= Column byte length < Character byte length Column byte length < 19 Data value is not a valid timestamp | n/a 22001 22001 22008
SQL_WCHAR SQL_WVARCHAR SQL_WLONGVARCHAR | Column character length >= Character length of data 19 <= Column character length < Character length of data Column character length < 19 Data value is not a valid timestamp | n/a 22001 22001 22008
SQL_TYPE_DATE | Time fields are zero Time fields are nonzero Data value does not contain a valid date | n/a 22008 22007
SQL_TYPE_TIME | Fractional seconds fields are zero[a] Fractional seconds fields are nonzero[a] Data value does not contain a valid time | n/a 22008 22007
SQL_TYPE_TIMESTAMP | Fractional seconds fields are not truncated Fractional seconds fields are truncated Data value is not a valid timestamp | n/a 22008 22007

[a] The date fields of the timestamp structure are ignored.

For information about what values are valid in a SQL_C_TIMESTAMP structure, see the section "C Data Types," earlier in this appendix.

When timestamp C data is converted to character SQL data, the resulting character data is in the "yyyy-mm-dd hh:mm:ss[.f...]" format.

The driver ignores the length/indicator value when converting data from the timestamp C data type and assumes that the size of the data buffer is the size of the timestamp C data type. The length/indicator value is passed in the StrLen_or_Ind argument in SQLPutData and in the buffer specified with the StrLen_or_IndPtr argument in SQLBindParameter. The data buffer is specified with the DataPtr argument in SQLPutData and the ParameterValuePtr argument in SQLBindParameter.


C to SQL: Year-Month Intervals

The identifiers for the year-month interval ODBC C data types are:

SQL_C_INTERVAL_MONTH
SQL_C_INTERVAL_YEAR
SQL_C_INTERVAL_YEAR_TO_MONTH

The following table shows the ODBC SQL data types to which year-month interval C data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from C to SQL Data Types."

SQL type identifier | Test | SQLSTATE
SQL_CHAR[a] SQL_VARCHAR[a] SQL_LONGVARCHAR[a] | Column byte length >= Character byte length Column byte length < Character byte length[a] Data value is not a valid interval literal | n/a 22001 22015
SQL_WCHAR[a] SQL_WVARCHAR[a] SQL_WLONGVARCHAR[a] | Column character length >= Character length of data Column character length < Character length of data[a] Data value is not a valid interval literal | n/a 22001 22015
SQL_TINYINT[b] SQL_SMALLINT[b] SQL_INTEGER[b] SQL_BIGINT[b] SQL_NUMERIC[b] SQL_DECIMAL[b] | Conversion of a single-field interval did not result in truncation of whole digits Conversion resulted in truncation of whole digits | n/a 22003
SQL_INTERVAL_MONTH SQL_INTERVAL_YEAR SQL_INTERVAL_YEAR_TO_MONTH | Data value was converted without truncation of any fields One or more fields of data value were truncated during conversion | n/a 22015

[a] All C interval data types can be converted to a character data type.

[b] If the type field in the interval structure is such that the interval is a single field (SQL_YEAR or SQL_MONTH), the interval C type can be converted to any exact numeric (SQL_TINYINT, SQL_SMALLINT, SQL_INTEGER, SQL_BIGINT, SQL_DECIMAL, or SQL_NUMERIC).

The default conversion of an interval C type is to the corresponding year-month interval SQL type.

The driver ignores the length/indicator value when converting data from the interval C data type and assumes that the size of the data buffer is the size of the interval C data type. The length/indicator value is passed in the StrLen_or_Ind argument in SQLPutData and in the buffer specified with the StrLen_or_IndPtr argument in SQLBindParameter. The data buffer is specified with the DataPtr argument in SQLPutData and the ParameterValuePtr argument in SQLBindParameter.


C to SQL: Day-Time Intervals

The identifiers for the day-time interval ODBC C data types are:

SQL_C_INTERVAL_DAY
SQL_C_INTERVAL_HOUR
SQL_C_INTERVAL_MINUTE
SQL_C_INTERVAL_SECOND
SQL_C_INTERVAL_DAY_TO_HOUR
SQL_C_INTERVAL_DAY_TO_MINUTE
SQL_C_INTERVAL_DAY_TO_SECOND
SQL_C_INTERVAL_HOUR_TO_MINUTE
SQL_C_INTERVAL_HOUR_TO_SECOND
SQL_C_INTERVAL_MINUTE_TO_SECOND

The following table shows the ODBC SQL data types to which interval C data may be converted. For an explanation of the columns and terms in the table, see "Converting Data from C to SQL Data Types."

SQL type identifier | Test | SQLSTATE
SQL_CHAR[a] SQL_VARCHAR[a] SQL_LONGVARCHAR[a] | Column byte length >= Character byte length Column byte length < Character byte length[a] Data value is not a valid interval literal | n/a 22001 22015
SQL_WCHAR[a] SQL_WVARCHAR[a] SQL_WLONGVARCHAR[a] | Column character length >= Character length of data Column character length < Character length of data[a] Data value is not a valid interval literal | n/a 22001 22015
SQL_TINYINT[b] SQL_SMALLINT[b] SQL_INTEGER[b] SQL_BIGINT[b] SQL_NUMERIC[b] SQL_DECIMAL[b] | Conversion of a single-field interval did not result in truncation of whole digits Conversion resulted in truncation of whole digits | n/a 22003
SQL_INTERVAL_DAY SQL_INTERVAL_HOUR SQL_INTERVAL_MINUTE SQL_INTERVAL_SECOND SQL_INTERVAL_DAY_TO_HOUR SQL_INTERVAL_DAY_TO_MINUTE SQL_INTERVAL_DAY_TO_SECOND SQL_INTERVAL_HOUR_TO_MINUTE SQL_INTERVAL_HOUR_TO_SECOND SQL_INTERVAL_MINUTE_TO_SECOND | Data value was converted without truncation of any fields One or more fields of data value were truncated during conversion | n/a 22015

[a] All C interval data types can be converted to a character data type.

[b] If the type field in the interval structure is such that the interval is a single field (SQL_DAY, SQL_HOUR, SQL_MINUTE, or SQL_SECOND), the interval C type can be converted to any exact numeric (SQL_TINYINT, SQL_SMALLINT, SQL_INTEGER, SQL_BIGINT, SQL_DECIMAL, or SQL_NUMERIC).

The default conversion of an interval C type is to the corresponding day-time interval SQL type.

The driver ignores the length/indicator value when converting data from the interval C data type and assumes that the size of the data buffer is the size of the interval C data type. The length/indicator value is passed in the StrLen_or_Ind argument in SQLPutData and in the buffer specified with the StrLen_or_IndPtr argument in SQLBindParameter. The data buffer is specified with the DataPtr argument in SQLPutData and the ParameterValuePtr argument in SQLBindParameter.

The following example demonstrates how to send interval C data stored in the SQL_INTERVAL_STRUCT structure into a database column. The interval structure contains a DAY_TO_SECOND interval; it will be stored in a database column of type SQL_INTERVAL_DAY_TO_MINUTE.

SQL_INTERVAL_STRUCT is;
SQLINTEGER cbValue;

// Initialize the interval struct to contain the DAY_TO_SECOND
// interval "154 days, 22 hours, 44 minutes, and 10 seconds"
is.intval.day_second.day      = 154;
is.intval.day_second.hour     = 22;
is.intval.day_second.minute   = 44;
is.intval.day_second.second   = 10;
is.interval_sign              = SQL_FALSE;

// Bind the dynamic parameter
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_INTERVAL_DAY_TO_SECOND,
                  SQL_INTERVAL_DAY_TO_MINUTE, 0, 0, &is,
                  sizeof(SQL_INTERVAL_STRUCT), &cbValue);

// Execute an insert statement; "interval_column" is a column
// whose data type is SQL_INTERVAL_DAY_TO_MINUTE.
SQLExecDirect(hstmt,"INSERT INTO table(interval_column) VALUES (?)",SQL_NTS);


C to SQL Data Conversion Examples

The following examples illustrate how the driver converts C data to SQL data:

C type identifier | C data value | SQL type identifier | Column length | SQL data value | SQLSTATE
SQL_C_CHAR | abcdef\0[a] | SQL_CHAR | 6 | abcdef | n/a
SQL_C_CHAR | abcdef\0[a] | SQL_CHAR | 5 | abcde | 22001
SQL_C_CHAR | 1234.56\0[a] | SQL_DECIMAL | 8[b] | 1234.56 | n/a
SQL_C_CHAR | 1234.56\0[a] | SQL_DECIMAL | 7[b] | 1234.5 | 22001
SQL_C_CHAR | 1234.56\0[a] | SQL_DECIMAL | 4 | ---- | 22003
SQL_C_FLOAT | 1234.56 | SQL_FLOAT | n/a | 1234.56 | n/a
SQL_C_FLOAT | 1234.56 | SQL_INTEGER | n/a | 1234 | 22001
SQL_C_FLOAT | 1234.56 | SQL_TINYINT | n/a | ---- | 22003
SQL_C_TYPE_DATE | 1992,12,31[c] | SQL_CHAR | 10 | 1992-12-31 | n/a
SQL_C_TYPE_DATE | 1992,12,31[c] | SQL_CHAR | 9 | ---- | 22003
SQL_C_TYPE_DATE | 1992,12,31[c] | SQL_TIMESTAMP | n/a | 1992-12-31 00:00:00.0 | n/a
SQL_C_TYPE_TIMESTAMP | 1992,12,31, 23,45,55, 120000000[d] | SQL_CHAR | 22 | 1992-12-31 23:45:55.12 | n/a
SQL_C_TYPE_TIMESTAMP | 1992,12,31, 23,45,55, 120000000[d] | SQL_CHAR | 21 | 1992-12-31 23:45:55.1 | 22001
SQL_C_TYPE_TIMESTAMP | 1992,12,31, 23,45,55, 120000000[d] | SQL_CHAR | 18 | ---- | 22003

[a] "\0" represents a null-termination byte. The null-termination byte is required only if the length of the data is SQL_NTS.

[b] In addition to bytes for numbers, one byte is required for a sign and another byte is required for the decimal point.

[c] The numbers in this list are the numbers stored in the fields of the SQL_DATE_STRUCT structure.

[d] The numbers in this list are the numbers stored in the fields of the SQL_TIMESTAMP_STRUCT structure.


Appendix E: Scalar Functions

ODBC specifies the following types of scalar functions, with detailed information about each of these function types provided in the corresponding sections of this appendix. The function descriptions include associated syntax.

  • String Functions
  • Numeric Functions
  • Time, Date, and Interval Functions
  • System Functions
  • Explicit Data Type Conversion Function
  • SQL-92 CAST Function

ODBC does not mandate a data type for return values from scalar functions because the functions are often data source–specific. Applications should use the CONVERT scalar function whenever possible to force data type conversion.

ODBC and SQL-92 Scalar Functions

The tables in this appendix include functions that have been added in ODBC 3.0 to align with SQL-92. Those functions added for a particular type of scalar function, as defined in ODBC, are indicated in each section.

ODBC and SQL-92 classify their scalar functions differently. ODBC classifies scalar functions by argument type; SQL-92 classifies them by return value. For example, the EXTRACT function is classified as a timedate function by ODBC, because the extract-field argument is a datetime keyword and the extract-source argument is a datetime or interval expression. SQL-92, on the other hand, classifies EXTRACT as a numeric scalar function, because the return value is a numeric.

An application can determine which scalar functions a driver supports by calling SQLGetInfo. Information types are included both for ODBC and for SQL-92 classifications of scalar functions. Because these classifications are different, the support for some scalar functions may be indicated in information types that do not correspond to ODBC and SQL-92. For example, support for EXTRACT in ODBC is indicated by the SQL_TIMEDATE_FUNCTIONS information type; support for EXTRACT in SQL-92, on the other hand, is indicated by the SQL_SQL92_NUMERIC_VALUE_FUNCTIONS information type.


String Functions

The following table lists string manipulation functions. An application can determine which string functions are supported by a driver by calling SQLGetInfo with an information type of SQL_STRING_FUNCTIONS.

Arguments denoted as string_exp can be the name of a column, a character-string-literal, or the result of another scalar function, where the underlying data type can be represented as SQL_CHAR, SQL_VARCHAR, or SQL_LONGVARCHAR.

Arguments denoted as character_exp are a variable-length character string.

Arguments denoted as start, length, or count can be a numeric-literal or the result of another scalar function, where the underlying data type can be represented as SQL_TINYINT, SQL_SMALLINT, or SQL_INTEGER.

The string functions listed here are 1-based; that is, the first character in the string is character 1.

The BIT_LENGTH, CHAR_LENGTH, CHARACTER_LENGTH, OCTET_LENGTH, and POSITION string scalar functions have been added in ODBC 3.0 to align with SQL-92.

Function | Description
ASCII(string_exp)
(ODBC 1.0) 		Returns the ASCII code value of the leftmost character of string_exp as an integer.
BIT_LENGTH(string_exp)
(ODBC 3.0) 		Returns the length in bits of the string expression.
CHAR(code)
(ODBC 1.0) 		Returns the character that has the ASCII code value specified by code. The value of code should be between 0 and 255; otherwise, the return value is data source–dependent.
CHAR_LENGTH(string_exp)
(ODBC 3.0) 		Returns the length in characters of the string expression, if the string expression is of a character data type; otherwise, returns the length in bytes of the string expression (the smallest integer not less than the number of bits divided by 8). (This function is the same as the CHARACTER_LENGTH function.)
CHARACTER_LENGTH(string_exp)
(ODBC 3.0) 		Returns the length in characters of the string expression, if the string expression is of a character data type; otherwise, returns the length in bytes of the string expression (the smallest integer not less than the number of bits divided by 8). (This function is the same as the CHAR_LENGTH function.)
CONCAT(string_exp1, string_exp2)
(ODBC 1.0) 		Returns a character string that is the result of concatenating string_exp2 to string_exp1. The resulting string is DBMS-dependent. For example, if the column represented by string_exp1 contained a NULL value, DB2 would return NULL but SQL Server would return the non-NULL string.
DIFFERENCE(string_exp1, string_exp2)
(ODBC 2.0) 		Returns an integer value that indicates the difference between the values returned by the SOUNDEX function for string_exp1 and string_exp2.
INSERT(string_exp1, start, length, string_exp2)
(ODBC 1.0) 		Returns a character string where length characters have been deleted from string_exp1, beginning at start, and where string_exp2 has been inserted into string_exp, beginning at start.
LCASE(string_exp)
(ODBC 1.0) 		Returns a string equal to that in string_exp, with all uppercase characters converted to lowercase.
LEFT(string_exp, count)
(ODBC 1.0) 		Returns the leftmost count characters of string_exp.
LENGTH(string_exp)
(ODBC 1.0) 		Returns the number of characters in string_exp, excluding trailing blanks.
LOCATE(string_exp1, string_exp2[, start])
(ODBC 1.0) 		Returns the starting position of the first occurrence of string_exp1 within string_exp2. The search for the first occurrence of string_exp1 begins with the first character position in string_exp2 unless the optional argument, start, is specified. If start is specified, the search begins with the character position indicated by the value of start. The first character position in string_exp2 is indicated by the value 1. If string_exp1 is not found within string_exp2, the value 0 is returned.

If an application can call the LOCATE scalar function with the string_exp1, string_exp2, and start arguments, the driver returns SQL_FN_STR_LOCATE when SQLGetInfo is called with an Option of SQL_STRING_FUNCTIONS. If the application can call the LOCATE scalar function with only the string_exp1 and string_exp2 arguments, the driver returns SQL_FN_STR_LOCATE_2 when SQLGetInfo is called with an Option of SQL_STRING_FUNCTIONS. Drivers that support calling the LOCATE function with either two or three arguments return both SQL_FN_STR_LOCATE and SQL_FN_STR_LOCATE_2.
LTRIM(string_exp)
(ODBC 1.0) 		Returns the characters of string_exp, with leading blanks removed.
OCTET_LENGTH(string_exp)
(ODBC 3.0) 		Returns the length in bytes of the string expression. The result is the smallest integer not less than the number of bits divided by 8.
POSITION(character_exp IN character_exp)
(ODBC 3.0) 		Returns the position of the first character expression in the second character expression. The result is an exact numeric with an implementation-defined precision and a scale of 0.
REPEAT(string_exp, count)
(ODBC 1.0) 		Returns a character string composed of string_exp repeated count times.
REPLACE(string_exp1, string_exp2, string_exp3)
(ODBC 1.0) 		Search string_exp1 for occurrences of string_exp2, and replace with string_exp3.
RIGHT(string_exp, count)
(ODBC 1.0) 		Returns the rightmost count characters of string_exp.
RTRIM(string_exp)
(ODBC 1.0) 		Returns the characters of string_exp with trailing blanks removed.
SOUNDEX(string_exp)
(ODBC 2.0) 		Returns a data source–dependent character string representing the sound of the words in string_exp. For example, SQL Server returns a 4-digit SOUNDEX code; Oracle returns a phonetic representation of each word.
SPACE(count) (ODBC 2.0) | Returns a character string consisting of count spaces.
SUBSTRING(string_exp, start, length)
(ODBC 1.0) 		Returns a character string that is derived from string_exp, beginning at the character position specified by start for length characters.
UCASE(string_exp)
(ODBC 1.0) 		Returns a string equal to that in string_exp, with all lowercase characters converted to uppercase.


Numeric Functions

The following table describes numeric functions that are included in the ODBC scalar function set. By calling SQLGetInfo with an information type of SQL_NUMERIC_FUNCTIONS, an application can determine which numeric functions are supported by a driver.

All numeric functions return values of data type SQL_FLOAT except for ABS, ROUND, TRUNCATE, SIGN, FLOOR, and CEILING, which return values of the same data type as the input parameters.

Arguments denoted as numeric_exp can be the name of a column, the result of another scalar function, or a numeric-literal, where the underlying data type could be represented as SQL_NUMERIC, SQL_DECIMAL, SQL_TINYINT, SQL_SMALLINT, SQL_INTEGER, SQL_BIGINT, SQL_FLOAT, SQL_REAL, or SQL_DOUBLE.

Arguments denoted as float_exp can be the name of a column, the result of another scalar function, or a numeric-literal, where the underlying data type can be represented as SQL_FLOAT.

Arguments denoted as integer_exp can be the name of a column, the result of another scalar function, or a numeric-literal, where the underlying data type can be represented as SQL_TINYINT, SQL_SMALLINT, SQL_INTEGER, or SQL_BIGINT.

The CURRENT_DATE, CURRENT_TIME, and CURRENT_TIMESTAMP scalar functions have been added in ODBC 3.0 to align with SQL-92.

Function | Description
ABS(numeric_exp) (ODBC 1.0) | Returns the absolute value of numeric_exp.
ACOS(float_exp) (ODBC 1.0) | Returns the arccosine of float_exp as an angle, expressed in radians.
ASIN(float_exp) (ODBC 1.0) | Returns the arcsine of float_exp as an angle, expressed in radians.
ATAN(float_exp) (ODBC 1.0) | Returns the arctangent of float_exp as an angle, expressed in radians.
ATAN2(float_exp1, float_exp2) (ODBC 2.0) | Returns the arctangent of the x and y coordinates, specified by float_exp1 and float_exp2, respectively, as an angle, expressed in radians.
CEILING(numeric_exp) (ODBC 1.0) | Returns the smallest integer greater than or equal to numeric_exp. The return value is of the same data type as the input parameter.
COS(float_exp) (ODBC 1.0) | Returns the cosine of float_exp, where float_exp is an angle expressed in radians.
COT(float_exp) (ODBC 1.0) | Returns the cotangent of float_exp, where float_exp is an angle expressed in radians.
DEGREES(numeric_exp) (ODBC 2.0) | Returns the number of degrees converted from numeric_exp radians.
EXP(float_exp) (ODBC 1.0) | Returns the exponential value of float_exp.
FLOOR(numeric_exp) (ODBC 1.0) | Returns the largest integer less than or equal to numeric_exp. The return value is of the same data type as the input parameter.
LOG(float_exp) (ODBC 1.0) | Returns the natural logarithm of float_exp.
LOG10(float_exp) (ODBC 2.0) | Returns the base 10 logarithm of float_exp.
MOD(integer_exp1, integer_exp2) (ODBC 1.0) | Returns the remainder (modulus) of integer_exp1 divided by integer_exp2.
PI( ) (ODBC 1.0) | Returns the constant value of pi as a floating-point value.
POWER(numeric_exp, integer_exp) (ODBC 2.0) | Returns the value of numeric_exp to the power of integer_exp.
RADIANS(numeric_exp) (ODBC 2.0) | Returns the number of radians converted from numeric_exp degrees.
RAND([integer_exp]) (ODBC 1.0) | Returns a random floating-point value using integer_exp as the optional seed value.
ROUND(numeric_exp, integer_exp) (ODBC 2.0) | Returns numeric_exp rounded to integer_exp places right of the decimal point. If integer_exp is negative, numeric_exp is rounded to |integer_exp| places to the left of the decimal point.
SIGN(numeric_exp) (ODBC 1.0) | Returns an indicator of the sign of numeric_exp. If numeric_exp is less than zero, –1 is returned. If numeric_exp equals zero, 0 is returned. If numeric_exp is greater than zero, 1 is returned.
SIN(float_exp) (ODBC 1.0) | Returns the sine of float_exp, where float_exp is an angle expressed in radians.
SQRT(float_exp) (ODBC 1.0) | Returns the square root of float_exp.
TAN(float_exp) (ODBC 1.0) | Returns the tangent of float_exp, where float_exp is an angle expressed in radians.
TRUNCATE(numeric_exp, integer_exp) (ODBC 2.0) | Returns numeric_exp truncated to integer_exp places right of the decimal point. If integer_exp is negative, numeric_exp is truncated to |integer_exp| places to the left of the decimal point.


Time, Date, and Interval Functions

The following table lists time and date functions that are included in the ODBC scalar function set. An application can determine which time and date functions are supported by a driver by calling SQLGetInfo with an information type of SQL_TIMEDATE_FUNCTIONS.

Arguments denoted as timestamp_exp can be the name of a column, the result of another scalar function, or an ODBC-time-escape, ODBC-date- escape, or ODBC-timestamp-escape, where the underlying data type could be represented as SQL_CHAR, SQL_VARCHAR, SQL_TYPE_TIME, SQL_TYPE_DATE, or SQL_TYPE_TIMESTAMP.

Arguments denoted as date_exp can be the name of a column, the result of another scalar function, or an ODBC-date- escape or ODBC-timestamp-escape, where the underlying data type could be represented as SQL_CHAR, SQL_VARCHAR, SQL_TYPE_DATE, or SQL_TYPE_TIMESTAMP.

Arguments denoted as time_exp can be the name of a column, the result of another scalar function, or an ODBC-time-escape or ODBC-timestamp-escape, where the underlying data type could be represented as SQL_CHAR, SQL_VARCHAR, SQL_TYPE_TIME, or SQL_TYPE_TIMESTAMP.

The CURRENT_DATE, CURRENT_TIME, and CURRENT_TIMESTAMP timedate scalar functions have been added in ODBC 3.0 to align with SQL-92.

Function Description
CURRENT_DATE( )
(ODBC 3.0) 		Returns the current date.
CURRENT_TIME[(time-precision)]
(ODBC 3.0) 		Returns the current local time. The time-precision argument determines the seconds precision of the returned value.
CURRENT_TIMESTAMP
[(timestamp-precision)]
(ODBC 3.0) 		Returns the current local date and local time as a timestamp value. The timestamp-precision argument determines the seconds precision of the returned timestamp.
CURDATE( )
(ODBC 1.0) 		Returns the current date.
CURTIME( )
(ODBC 1.0) 		Returns the current local time.
DAYNAME(date_exp)
(ODBC 2.0) 		Returns a character string containing the data source–specific name of the day (for example, Sunday through Saturday or Sun. through Sat. for a data source that uses English, or Sonntag through Samstag for a data source that uses German) for the day portion of date_exp.
DAYOFMONTH(date_exp)
(ODBC 1.0) 		Returns the day of the month based on the month field in date_exp as an integer value in the range of 1–31.
DAYOFWEEK(date_exp)
(ODBC 1.0) 		Returns the day of the week based on the week field in date_exp as an integer value in the range of 1–7, where 1 represents Sunday.
DAYOFYEAR(date_exp)
(ODBC 1.0) 		Returns the day of the year based on the year field in date_exp as an integer value in the range of 1–366.
EXTRACT(extract-field FROM extract-source)
(ODBC 3.0) 		Returns the extract-field portion of the extract-source. The extract-source argument is a datetime or interval expression. The extract-field argument can be one of the following keywords:

YEAR
MONTH
DAY
HOUR
MINUTE
SECOND

The precision of the returned value is implementation-defined. The scale is 0 unless SECOND is specified, in which case the scale is not less than the fractional seconds precision of the extract-source field.
HOUR(time_exp)
(ODBC 1.0) 		Returns the hour based on the hour field in time_exp as an integer value in the range of 0–23.
MINUTE(time_exp)
(ODBC 1.0) 		Returns the minute based on the minute field in time_exp as an integer value in the range of 0–59.
MONTH(date_exp)
(ODBC 1.0) 		Returns the month based on the month field in date_exp as an integer value in the range of 1–12.
MONTHNAME(date_exp)
(ODBC 2.0) 		Returns a character string containing the data source–specific name of the month (for example, January through December or Jan. through Dec. for a data source that uses English, or Januar through Dezember for a data source that uses German) for the month portion of date_exp.
NOW( )
(ODBC 1.0) 		Returns current date and time as a timestamp value.
QUARTER(date_exp)
(ODBC 1.0) 		Returns the quarter in date_exp as an integer value in the range of 1–4, where 1 represents January 1 through March 31.
SECOND(time_exp)
(ODBC 1.0) 		Returns the second based on the second field in time_exp as an integer value in the range of 0–59.

Returns the timestamp calculated by adding integer_exp intervals of type interval to timestamp_exp. Valid values of interval are the following keywords:

SQL_TSI_FRAC_SECOND
SQL_TSI_SECOND
SQL_TSI_MINUTE
SQL_TSI_HOUR
SQL_TSI_DAY
SQL_TSI_WEEK
SQL_TSI_MONTH
SQL_TSI_QUARTER
SQL_TSI_YEAR

where fractional seconds are expressed in billionths of a second. For example, the following SQL statement returns the name of each employee and his or her one-year anniversary date:

SELECT NAME, {fn
TIMESTAMPADD(SQL_TSI_YEAR,
1, HIRE_DATE)} FROM
EMPLOYEES

If timestamp_exp is a time value and interval specifies days, weeks, months, quarters, or years, the date portion of timestamp_exp is set to the current date before calculating the resulting timestamp.

If timestamp_exp is a date value and interval specifies fractional seconds, seconds, minutes, or hours, the time portion of timestamp_exp is set to 0 before calculating the resulting timestamp.

An application determines which intervals a data source supports by calling SQLGetInfo with the SQL_TIMEDATE_ADD_INTERVALS option.
TIMESTAMPDIFF(interval, timestamp_exp1, timestamp_exp2)
(ODBC 2.0) 		Returns the integer number of intervals of type interval by which timestamp_exp2 is greater than timestamp_exp1. Valid values of interval are the following keywords:

SQL_TSI_FRAC_SECOND
SQL_TSI_SECOND
SQL_TSI_MINUTE
SQL_TSI_HOUR
SQL_TSI_DAY
SQL_TSI_WEEK
SQL_TSI_MONTH
SQL_TSI_QUARTER
SQL_TSI_YEAR

where fractional seconds are expressed in billionths of a second. For example, the following SQL statement returns the name of each employee and the number of years he or she has been employed:

SELECT NAME, {fn
TIMESTAMPDIFF(SQL_TSI_YEAR,
{fn CURDATE()}, HIRE_DATE)}
FROM EMPLOYEES

If either timestamp expression is a time value and interval specifies days, weeks, months, quarters, or years, the date portion of that timestamp is set to the current date before calculating the difference between the timestamps.

If either timestamp expression is a date value and interval specifies fractional seconds, seconds, minutes, or hours, the time portion of that timestamp is set to 0 before calculating the difference between the timestamps.

An application determines which intervals a data source supports by calling SQLGetInfo with the SQL_TIMEDATE_DIFF_INTERVALS option.
WEEK(date_exp)
(ODBC 1.0) 		Returns the week of the year based on the week field in date_exp as an integer value in the range of 1–53.
YEAR(date_exp)
(ODBC 1.0) 		Returns the year based on the year field in date_exp as an integer value. The range is data source–dependent.


System Functions

The following table lists system functions that are included in the ODBC scalar function set. By calling SQLGetInfo with an information type of SQL_SYSTEM_FUNCTIONS, an application can determine which system functions are supported by a driver.

Arguments denoted as exp can be the name of a column, the result of another scalar function, or a literal, where the underlying data type could be represented as SQL_NUMERIC, SQL_DECIMAL, SQL_TINYINT, SQL_SMALLINT, SQL_INTEGER, SQL_BIGINT, SQL_FLOAT, SQL_REAL, SQL_DOUBLE, SQL_TYPE_DATE, SQL_TYPE_TIME, or SQL_TYPE_TIMESTAMP.

Arguments denoted as value can be a literal constant, where the underlying data type can be represented as SQL_NUMERIC, SQL_DECIMAL, SQL_TINYINT, SQL_SMALLINT, SQL_INTEGER, SQL_BIGINT, SQL_FLOAT, SQL_REAL, SQL_DOUBLE, SQL_TYPE_DATE, SQL_TYPE_TIME, or SQL_TYPE_TIMESTAMP.

Values returned are represented as ODBC data types.

Function | Description
DATABASE( ) (ODBC 1.0) | Returns the name of the database corresponding to the connection handle. (The name of the database is also available by calling SQLGetConnectOption with the SQL_CURRENT_QUALIFIER connection option.)
IFNULL(exp, value) (ODBC 1.0) | If exp is null, value is returned. If exp is not null, exp is returned. The possible data type or types of value must be compatible with the data type of exp.
USER( ) (ODBC 1.0) | Returns the user name in the DBMS. (The user name is also available by way of SQLGetInfo by specifying the information type: SQL_USER_NAME.) This can be different than the login name.


Explicit Data Type Conversion Function

Explicit data type conversion is specified in terms of SQL data type definitions.

The ODBC syntax for the explicit data type conversion function does not restrict conversions. The validity of specific conversions of one data type to another data type will be determined by each driver-specific implementation. The driver will, as it translates the ODBC syntax into the native syntax, reject those conversions that, although legal in the ODBC syntax, are not supported by the data source. The ODBC function SQLGetInfo, with the conversion options (such as SQL_CONVERT_BIGINT, SQL_CONVERT_BINARY, SQL_CONVERT_INTERVAL_YEAR_MONTH, and so on), provides a way to inquire about conversions supported by the data source.

The format of the CONVERT function is:

CONVERT(value_exp, data_type)

The function returns the value specified by value_exp converted to the specified data_type, where data_type is one of the following keywords:

SQL_BIGINT | SQL_INTERVAL_HOUR_TO_MINUTE
SQL_BINARY | SQL_INTERVAL_HOUR_TO_SECOND
SQL_BIT | SQL_INTERVAL_MINUTE_TO_SECOND
SQL_CHAR | SQL_LONGVARBINARY
SQL_DECIMAL | SQL_LONGVARCHAR
SQL_DOUBLE | SQL_NUMERIC
SQL_FLOAT | SQL_REAL
SQL_GUID | SQL_SMALLINT
SQL_INTEGER | SQL_DATE
SQL_INTERVAL_MONTH | SQL_TIME
SQL_INTERVAL_YEAR | SQL_TIMESTAMP
SQL_INTERVAL_YEAR_TO_MONTH | SQL_TINYINT
SQL_INTERVAL_DAY | SQL_VARBINARY
SQL_INTERVAL_HOUR | SQL_VARCHAR
SQL_INTERVAL_MINUTE | SQL_WCHAR
SQL_INTERVAL_SECOND | SQL_WLONGVARCHAR
SQL_INTERVAL_DAY_TO_HOUR | SQL_WVARCHAR
SQL_INTERVAL_DAY_TO_MINUTE |
SQL_INTERVAL_DAY_TO_SECOND |

The ODBC syntax for the explicit data type conversion function does not support specification of conversion format. If specification of explicit formats is supported by the underlying data source, a driver must specify a default value or implement format specification.

The argument value_exp can be a column name, the result of another scalar function, or a numeric or string literal. For example:

{ fn CONVERT( { fn CURDATE() }, SQL_CHAR ) }

converts the output of the CURDATE scalar function to a character string.

Because ODBC does not mandate a data type for return values from scalar functions (because the functions are often data source–specific), applications should use the CONVERT scalar function whenever possible to force data type conversion.

The following two examples illustrate the use of the CONVERT function. These examples assume the existence of a table called EMPLOYEES, with an EMPNO column of type SQL_SMALLINT and an EMPNAME column of type SQL_CHAR.

If an application specifies the following SQL statement:

SELECT EMPNO FROM EMPLOYEES WHERE {fn CONVERT(EMPNO,SQL_CHAR)} LIKE '1%'
  • A driver for ORACLE translates the SQL statement to:
    SELECT EMPNO FROM EMPLOYEES WHERE to_char(EMPNO) LIKE '1%'
  • A driver for SQL Server translates the SQL statement to:
    SELECT EMPNO FROM EMPLOYEES WHERE convert(char,EMPNO) LIKE '1%'

If an application specifies the following SQL statement:

SELECT {fn ABS(EMPNO)}, {fn CONVERT(EMPNAME,SQL_SMALLINT)}
   FROM EMPLOYEES WHERE EMPNO <> 0
  • A driver for ORACLE translates the SQL statement to:
    SELECT abs(EMPNO), to_number(EMPNAME) FROM EMPLOYEES WHERE EMPNO <> 0
  • A driver for SQL Server translates the SQL statement to:
    SELECT abs(EMPNO), convert(smallint, EMPNAME) FROM EMPLOYEES
   WHERE EMPNO <> 0
  • 8A driver for Ingres translates the SQL statement to:
    SELECT abs(EMPNO), int2(EMPNAME) FROM EMPLOYEES WHERE EMPNO <> 0


SQL-92 CAST Function

The CAST function defined in SQL-92 is equivalent to the CONVERT function defined in ODBC. The syntax of the equivalent functions is as follows:

{ fn CONVERT (value-exp, data-type) } /* ODBC
CAST (value-exp AS data-type) /* SQL92

The SQL-92 CAST function mandates which data types can be converted to which other data types. (For more information, see the SQL-92 specification.) The CAST function is supported at the FIPS Transitional level.

An application can determine support for the CAST function as follows:

  1. Call SQLGetInfo with the SQL_SQL_CONFORMANCE information type. If the return value for the information type is SQL_SC_FIPS127_2_TRANSITIONAL, SQL_SC_SQL92_INTERMEDIATE, or SQL_SC_SQL92_FULL, the CAST function is supported.
  2. If the return value of the SQL_SQL_CONFORMANCE information type is SQL_SC_ENTRY_LEVEL or 0, call SQLGetInfo with the SQL_SQL92_VALUE_EXPRESSIONS information type. If the SQL_SVE_CAST bit is set, the CAST function is supported.


Appendix F: ODBC Cursor Library

The ODBC cursor library (Odbccr32.dll) supports block scrollable cursors for any driver that complies with the Level 1 API conformance level and can be redistributed by developers with their applications or drivers. The cursor library also supports positioned update and delete statements for result sets generated by SELECT statements. Although it supports only static and forward-only cursors, the cursor library satisfies the needs of many applications. Furthermore, it can provide good performance, especially for small-sized to medium-sized result sets, and for applications that do not have good cursor support.

The cursor library is a dynamic-link library (DLL) that resides between the Driver Manager and the driver. When an application calls a function, the Driver Manager calls the function in the cursor library, which either executes the function or calls it in the specified driver. For a given connection, an application specifies whether the cursor library is always used, used if the driver does not support scrollable cursors, or never used.

The cursor library appears as a driver to the Driver Manager. If the cursor library resides between the Driver Manager and an ODBC 2.x driver, the cursor library appears as an ODBC 2.x driver. If the cursor library resides between the Driver Manager and an ODBC 3.x driver, the cursor library appears as an ODBC 3.x driver. The behavior exhibited by the cursor library depends upon the version of the driver it is working with, with the exception of binding offsets, which is supported for both ODBC 2.x and ODBC 3.x drivers.

To implement block cursors in SQLFetch and SQLFetchScroll, the cursor library repeatedly calls SQLFetch in the driver. To implement scrolling, it caches the data it has retrieved in memory and in disk files. When an application requests a new rowset, the cursor library retrieves it as necessary from the driver or the cache.

To implement positioned update and delete statements, the cursor library constructs an UPDATE or DELETE statement with a WHERE clause that specifies the cached value of each bound column in the row. When it executes a positioned update statement, the cursor library updates its cache from the values in the rowset buffers.

For more information about the ODBC cursor library, see the following sections of this appendix:

  • Using the ODBC Cursor Library
  • Executing Positioned Update and Delete Statements
  • Cursor Library Code Example
  • Implementation Notes
  • ODBC Cursor Library Error Codes


Using the ODBC Cursor Library

To use the ODBC cursor library, an application:

  1. Calls SQLSetConnectAttr with an Attribute of SQL_ATTR_ODBC_CURSORS to specify how the cursor library should be used with a particular connection. The cursor library can be always used (SQL_CUR_USE_ODBC), used only if driver does not support scrollable cursors (SQL_CUR_USE_IF_NEEDED), or never used (SQL_CUR_USE_DRIVER).
  2. Calls SQLConnect, SQLDriverConnect, or SQLBrowseConnect to connect to the data source.
  3. Calls SQLSetStmtAttr to specify the cursor type (SQL_ATTR_CURSOR_TYPE), concurrency (SQL_ATTR_CONCURRENCY), and rowset size (SQL_ATTR_ROW_ARRAY_SIZE). The cursor library supports forward-only and static cursors. Forward-only cursors must be read-only, while static cursors can be read-only or can use optimistic concurrency control comparing values.
  4. Allocates one or more rowset buffers and calls SQLBindCol one or more times to bind these buffers to result set columns.
  5. Generates a result set by executing a SELECT statement or a procedure, or by calling a catalog function. If the application will execute positioned update statements, it should execute a SELECT FOR UPDATE statement to generate the result set.
  6. Calls SQLFetch or SQLFetchScroll one or more times to scroll through the result set.

The application can change data values in the rowset buffers. To refresh the rowset buffers with data from the cursor library's cache, an application calls SQLFetchScroll with the FetchOrientation argument set to SQL_FETCH_RELATIVE and the FetchOffset argument set to 0.

To retrieve data from an unbound column, the application calls SQLSetPos to position the cursor on the desired row. It then calls SQLGetData to retrieve the data.

To determine the number of rows that have been retrieved from the data source, the application calls SQLRowCount.


Executing Positioned Update and Delete Statements

After an application has fetched a block of data with SQLFetchScroll, it can update or delete the data in the block. To execute a positioned update or delete, the application:

  1. Calls SQLSetPos to position the cursor on the row to be updated or deleted.
  2. Constructs a positioned update or delete statement with the following syntax:

    UPDATE table-name
    SET column-identifier = {expression | NULL}
    [, column-identifier = {expression | NULL}]
    WHERE CURRENT OF cursor-name

    DELETE FROM table-name WHERE CURRENT OF cursor-name

    The easiest way to construct the SET clause in a positioned update statement is to use parameter markers for each column to be updated and use SQLBindParameter to bind these to the rowset buffers for the row to be updated. In this case, the C data type of the parameter will be the same as the C data type of the rowset buffer.

  3. Updates the rowset buffers for the current row if it will execute a positioned update statement. After successfully executing a positioned update statement, the cursor library copies the values from each column in the current row to its cache.

    Caution If the application does not correctly update the rowset buffers before executing a positioned update statement, the data in the cache will be incorrect after the statement is executed.

  4. Executes the positioned update or delete statement using a different statement than the statement associated with the cursor.

    Caution The WHERE clause constructed by the cursor library to identify the current row can fail to identify any rows, identify a different row, or identify more than one row. For more information, see "Constructing Searched Statements," later in this appendix.

All positioned update and delete statements require a cursor name. To specify the cursor name, an application calls SQLSetCursorName before the cursor is opened. To use the cursor name generated by the driver, an application calls SQLGetCursorName after the cursor is opened.

After the cursor library executes a positioned update or delete statement, the status array, rowset buffers, and cache maintained by the cursor library contain the values shown in the following table.

Statement used | Value in row status array | Values in rowset buffers | Values in cache buffers
Positioned update | SQL_ROW_UPDATED | New values[1] | New values[1]
Positioned delete | SQL_ROW_DELETED | Old values | Old values

[1] The application must update the values in the rowset buffers before executing the positioned update statement; after executing the positioned update statement, the cursor library copies the values in the rowset buffers to its cache.


Cursor Library Code Example

The following example uses the cursor library to retrieve each order's ID, open date, and status from the ORDERS table. It then displays 20 rows of data. If the user updates this data, the code updates the rowset buffers and executes a positioned update statement. Finally, it prompts the user for the direction to scroll and repeats the process.

#define ROWS 20
#define STATUS_LEN 6
#define OPENDATE_LEN 11
#define DONE -1

SQLHENV        henv;
SQLHDBC        hdbc;
SQLHSTMT       hstmt1, hstmt2;
SQLRETURN      retcode;
SQLCHAR        szStatus[ROWS][STATUS_LEN],
               szOpenDate[ROWS][OPENDATE_LEN];
SQLCHAR        szNewStatus[STATUS_LEN], szNewOpenDate[OPENDATE_LEN];
SQLSMALLINT    sOrderID[ROWS], sNewOrderID[ROWS];
SQLINTEGER     cbStatus[ROWS], cbOrderID[ROWS], cbOpenDate[ROWS];
SQLUINTEGER    FetchOrientation, crow, FetchOffset, irowUpdt;
SQLUSMALLINT   RowStatusArray[ROWS];

SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3,0);
SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);

/* Specify that the ODBC Cursor Library is always used, then connect. */

SQLSetConnectAttr(hdbc, SQL_ATTR_ODBC_CURSORS, SQL_CUR_USE_ODBC);
SQLConnect(hdbc, "SalesOrder", SQL_NTS,
            "JohnS", SQL_NTS,
            "Sesame", SQL_NTS);

if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {

   /* Allocate a statement handle for the result set and a statement */
   /* handle for positioned update statements. */

   SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt1);
   SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt2);

   /* Specify an updatable static cursor with 20 rows of data. Set */
   /* the cursor name, execute the SELECT statement, and bind the */
   /* rowset buffers to result set columns in column-wise fashion. */
   SQLSetStmtAttr(hstmt1, SQL_ATTR_CONCURRENCY, SQL_CONCUR_VALUES, 0);
   SQLSetStmtAttr(hstmt1, SQL_ATTR_CURSOR_TYPE, SQL_CURSOR_STATIC, 0);
   SQLSetStmtAttr(hstmt1, SQL_ATTR_ROW_ARRAY_SIZE, ROWS, 0);
   SQLSetStmtAttr(hstmt1, SQL_ATTR_ROW_STATUS_PTR, RowStatusArray, 0);
   SQLSetStmtAttr(hstmt1, SQL_ATTR_ROWS_FETCHED_PTR, &crow, 0);
   SQLSetCursorName(hstmt1, "ORDERCURSOR", SQL_NTS);
   SQLExecDirect(hstmt1,
                  "SELECT ORDERID, OPENDATE, STATUS FROM ORDERS ",
                  SQL_NTS);
   SQLBindCol(hstmt1, 1, SQL_C_SSHORT, sOrderID, 0, cbName);
   SQLBindCol(hstmt1, 2, SQL_C_CHAR, szOpenDate, OPENDATE_LEN, cbOpenDate);
   SQLBindCol(hstmt1, 3, SQL_C_CHAR, szStatus, STATUS_LEN, cbStatus);

   /* Fetch the first block of data and display it. Prompt the user */
   /* for new data values. If the user supplies new values, update */
   /* the rowset buffers, bind them to the parameters in the update */
   /* statement, and execute a positioned update on another hstmt. */
   /* Prompt the user for how to scroll. Fetch and redisplay data as */
   /* needed. */

   FetchOrientation = SQL_FETCH_FIRST;
   FetchOffset = 0;
   do {

      SQLFetchScroll(hstmt1, FetchOrientation, FetchOffset);
      DisplayRows(sOrderID, szOpenDate, szStatus, RowStatusArray);

      if (PromptUpdate(&irowUpdt,&sNewOrderID,szNewOpenDate,szNewStatus)==TRUE){
         sOrderID[irowUpdt] = sNewOrderID;
         cbOrderID[irowUpdt] = 0;
         strcpy(szOpenDate[irowUpdt], szNewOpenData);
         cbOpenDate[irowUpdt] = SQL_NTS;
         strcpy(szStatus[irowUpdt], szNewStatus);
         cbStatus[irowUpdt] = SQL_NTS;
         SQLBindParameter(hstmt2, 1, SQL_PARAM_INPUT,
                           SQL_C_SSHORT, SQL_INTEGER, 0, 0,
                           &sOrderID[irowUpdt], 0, &cbOrderID[irowUpdt]);
         SQLBindParameter(hstmt2, 2, SQL_PARAM_INPUT,
                           SQL_C_CHAR, SQL_TYPE_DATE, OPENDATE_LEN, 0,
                           szOpenDate[irowUpdt], OPENDATE_LEN, &cbOpenDate[irowUpdt]);
         SQLBindParameter(hstmt2, 3, SQL_PARAM_INPUT,
                           SQL_C_CHAR, SQL_CHAR, STATUS_LEN, 0,
                           szStatus[irowUpdt], STATUS_LEN, &cbStatus[irowUpdt]);
         SQLExecDirect(hstmt2,
                        "UPDATE EMPLOYEE SET ORDERID = ?, OPENDATE = ?, STATUS = ?"
                        "WHERE CURRENT OF EMPCURSOR",
                        SQL_NTS);
      }

   while (PromptScroll(&FetchOrientation, &FetchOffset) != DONE)
}


Implementation Notes

This section describes how the ODBC cursor library is implemented. It describes how the cursor library maintains its cache, executes SQL statements, and implements ODBC functions.


Cursor Library Cache

For each row of data in the result set, the cursor library caches the data for each bound column, the length of the data in each bound column, and the status of the row. The cursor library uses the values in the cache both to return through SQLFetch and SQLFetchScroll and to construct searched statements for positioned operations. For more information, see "Constructing Searched Statements," later in this appendix.


Column Data

The cursor library creates a buffer in the cache for each data buffer bound to the result set with SQLBindCol. It uses the values in these buffers to construct a WHERE clause when it emulates a positioned update or delete statement. It updates these buffers from the rowset buffers when it fetches data from the data source and when it executes positioned update statements.

When the cursor library updates its cache from the rowset buffers, it transfers the data according to the C data type specified in SQLBindCol. For example, if the C data type of a rowset buffer is SQL_C_SLONG, the cursor library transfers four bytes of data; if it is SQL_C_CHAR and BufferLength is 10, the cursor library transfers 10 bytes of data. The cursor library does not perform any type checking or conversions on the data it transfers.

Note The cursor library does not update its cache for a column if *StrLen_or_IndPtr in the corresponding rowset buffer is SQL_DATA_AT_EXEC or the result of the SQL_LEN_DATA_AT_EXEC macro.

When it updates a column, a data source blank-pads fixed-length character data and zero-pads fixed-length binary data as necessary. For example, a data source stores "Smith" in a CHAR(10) column as "Smith ". The cursor library does not blank-pad or zero-pad data in the rowset buffers when it copies this data to its cache after executing a positioned update statement. Therefore, if an application requires that the values in the cursor library's cache are blank-padded or zero-padded, it must blank-pad or zero-pad the values in the rowset buffers before executing a positioned update statement.


Length of Column Data

The cursor library creates a buffer in the cache for each length/indicator buffer bound to the result set with SQLBindCol. It uses the values in these buffers to construct a WHERE clause when it emulates positioned update or delete statements. It updates these buffers from the rowset buffers when it fetches data from the data source and when it executes positioned update statements.

If the C type of a data buffer is SQL_C_CHAR or SQL_C_BINARY, and the length/indicator value is SQL_NTS, the string length of the data is put into the length/indicator buffer.

Note The cursor library does not update its cache for a column if *StrLen_or_IndPtr in the corresponding rowset buffer is SQL_DATA_AT_EXEC or the result of the SQL_LEN_DATA_AT_EXEC macro.


Row Status

The cursor library creates a buffer in the cache for the row status. The cursor library retrieves values for the row status array (specified with the SQL_ATTR_ROW_STATUS_PTR statement attribute) from this buffer. For each row, the cursor library sets this buffer to:

  • SQL_ROW_DELETED when it executes a positioned delete statement on the row.
  • SQL_ROW_ERROR when it encounters an error retrieving the row from the data source with SQLFetch.
  • SQL_ROW_SUCCESS when it successfully fetches the row from the data source with SQLFetch.
  • SQL_ROW_UPDATED when it executes a positioned update statement on the row.


Location of Cache

The cursor library caches data in memory and in Windows® temporary files. This limits the size of the result set that the cursor library can handle only by available disk space. A temporary file is used when the data to be cached would cross the segment boundary if inserted at the end of the cursor library cache. Instead, the data to be cached is added in place of the last-saved block of data in the cache. The last-saved block of data is saved in a temporary file. If the cursor library terminates abnormally, such as when the power fails, it can leave Windows temporary files on the disk. These are named ~CTTnnnn.tmp and are created in the current directory.

Note If the cursor library in Microsoft® Windows NT®/Windows 2000 attempts to cache data in a temporary file on the current directory while the application is running from a read-only share or a compact disk (such as a Microsoft Foundation Class Library sample), SQLSTATE HY000 (General Error-Unable to create a file buffer) will be returned.


Processing SQL Statements

The ODBC cursor library passes all SQL statements directly to the driver except the following:

  • Positioned update and delete statements
  • SELECT FOR UPDATE statements
  • Batched SQL statements

To execute positioned update and delete statements and to position the cursor on a row to call SQLGetData for that row, the cursor library constructs a searched statement that identifies the row.


Processing Positioned Update and Delete Statements

The cursor library supports positioned update and delete statements by replacing the WHERE CURRENT OF clause in such statements with a WHERE clause that enumerates the values stored in its cache for each bound column. The cursor library passes the newly constructed UPDATE and DELETE statements to the driver for execution. For positioned update statements, the cursor library then updates its cache from the values in the rowset buffers and sets the corresponding value in the row status array to SQL_ROW_UPDATED. For positioned delete statements, it sets the corresponding value in the row status array to SQL_ROW_DELETED.

Caution The WHERE clause constructed by the cursor library to identify the current row can fail to identify any rows, identify a different row, or identify more than one row. For more information, see "Constructing Searched Statements," later in this appendix.

Positioned update and delete statements are subject to the following restrictions:

  • Positioned update and delete statements can be used only in the following cases: when a SELECT statement generated the result set; when the SELECT statement did not contain a join, a UNION clause, or a GROUP BY clause; and when any columns that used an alias or expression in the select list were not bound with SQLBindCol.
  • If an application prepares a positioned update or delete statement, it must do so after it has called SQLFetch or SQLFetchScroll. Although the cursor library submits the statement to the driver for preparation, it closes the statement and executes it directly when the application calls SQLExecute.
  • If the driver supports only one active statement, the cursor library fetches the rest of the result set and then refetches the current rowset from its cache before it executes a positioned update or delete statement. If the application then calls a function that returns metadata in a result set (for example, SQLNumResultCols or SQLDescribeCol), the cursor library returns an error.
  • If a positioned update or delete statement is performed on a column of a table that includes a timestamp column that is automatically updated every time an update is performed, all subsequent positioned update or delete statements will fail if the timestamp column is bound. This occurs because the searched update or delete statement that the cursor library creates will not accurately identify the row to update. The value in the searched statement for the timestamp column will not match the automatically updated value of the timestamp column.


Processing SELECT FOR UPDATE Statements

For maximum interoperability, applications should generate result sets that will be updated with a positioned update statement by executing a SELECT FOR UPDATE statement. Although the cursor library does not require this, it is required by most data sources that support positioned update statements.

The cursor library ignores the columns in the FOR UPDATE clause of a SELECT FOR UPDATE statement; it removes this clause before passing the statement to the driver. In the cursor library, the SQL_ATTR_CONCURRENCY statement attribute, along with the restrictions mentioned in the previous section, controls whether the columns in a result set can be updated.


Processing Batches of SQL Statements

The cursor library does not support batches of SQL statements, including SQL statements for which the SQL_ATTR_PARAMSET_SIZE statement attribute is greater than 1. If an application submits a batch of SQL statements to the cursor library, the results are undefined.


Constructing Searched Statements

To support positioned update and delete statements, the cursor library constructs a searched UPDATE or DELETE statement from the positioned statement. To support calls to SQLGetData in a block of data, the cursor library constructs a searched SELECT statement to create a result set containing the current row of data. In each of these statements, the WHERE clause enumerates the values stored in the cache for each bound column that returns SQL_PRED_SEARCHABLE or SQL_PRED_BASIC for the SQL_DESC_SEARCHABLE field identifier in SQLColAttribute.

Caution The WHERE clause constructed by the cursor library to identify the current row can fail to identify any rows, identify a different row, or identify more than one row.

If a positioned update or delete statement affects more than one row, the cursor library updates the row status array only for the row on which the cursor is positioned and returns SQL_SUCCESS_WITH_INFO and SQLSTATE 01001 (Cursor operation conflict). If the statement does not identify any rows, the cursor library does not update the row status array and returns SQL_SUCCESS_WITH_INFO and SQLSTATE 01001 (Cursor operation conflict). An application can call SQLRowCount to determine the number of rows that were updated or deleted.

If the SELECT clause used to position the cursor for a call to SQLGetData identifies more than one row, SQLGetData is not guaranteed to return the correct data. If it does not identify any rows, SQLGetData returns SQL_NO_DATA.

If an application conforms to the following guidelines, the WHERE clause constructed by the cursor library should uniquely identify the current row, except when this is impossible, such as when the data source contains duplicate rows.

  • Bind columns that uniquely identify the row. If the bound columns do not uniquely identify the row, the WHERE clause constructed by the cursor library might identify more than one row. In a positioned update or delete statement, such a clause might cause more than one row to be updated or deleted. In a call to SQLGetData, such a clause might cause the driver to return data for the wrong row. Binding all the columns in a unique key guarantees that each row is uniquely identified.
  • Allocate data buffers large enough that no truncation occurs. The cursor library's cache is a copy of the values in the rowset buffers bound to the result set with SQLBindCol. If data is truncated when it is placed in these buffers, it will also be truncated in the cache. A WHERE clause constructed from truncated values might not correctly identify the underlying row in the data source.
  • Specify non-null length buffers for binary C data. The cursor library allocates length buffers in its cache only if the StrLen_or_IndPtr argument in SQLBindCol is non-null. When the TargetType argument is SQL_C_BINARY, the cursor library requires the length of the binary data to construct a WHERE clause from the data. If there is no length buffer for a SQL_C_BINARY column and the application calls SQLGetData or attempts to execute a positioned update or delete statement, the cursor library returns SQL_ERROR and SQLSTATE SL014 (A positioned request was issued and not all column count fields were buffered).
  • Specify non-null length buffers for nullable columns. The cursor library allocates length buffers in its cache only if the StrLen_or_IndPtr argument in SQLBindCol is non-null. Because SQL_NULL_DATA is stored in the length buffer, the cursor library assumes that any column for which no length buffer is specified is non-nullable. If no length column is specified for a nullable column, the cursor library constructs a WHERE clause that uses the data value for the column. This clause will not correctly identify the row.


ODBC Functions

When the ODBC cursor library is enabled for a connection, the Driver Manager calls functions in the cursor library instead of in the driver. The cursor library either executes the function or calls it in the specified driver.


ODBC Functions Executed by the Cursor Library

The cursor library executes the following functions. When an application calls a function in this list, the Driver Manager invokes the cursor library, not the driver. Note that the cursor library may call the driver when executing the function.

SQLBindCol | SQLGetStmtOption
SQLBindParam | SQLNativeSql
SQLBindParameter | SQLNumParams
SQLCloseCursor | SQLParamOptions
SQLEndTran | SQLRowCount
SQLExtendedFetch | SQLSetConnectAttr
SQLFetchScroll | SQLSetConnectOption
SQLFreeHandle | SQLSetDescField
SQLFreeStmt | SQLSetDescRec
SQLGetData | SQLSetPos
SQLGetDescField | SQLSetScrollOptions
SQLGetDescRec | SQLSetStmtAttr
SQLGetFunctions | SQLSetStmtOption
SQLGetInfo | SQLTransact
SQLGetStmtAttr |


ODBC Functions Not Executed by the Cursor Library

The cursor library does not execute the following functions. When an application calls one of these functions, the Driver Manager invokes the driver, not the cursor library.

SQLFetch | SQLGetEnvAttr
SQLGetConnectAttr | SQLSetDescRec
SQLGetDiagField | SQLSetEnvAttr
SQLGetDiagRec |


SQLBindCol in the Cursor Library

An application allocates one or more buffers for the cursor library to return the current rowset in. It calls SQLBindCol one or more times to bind these buffers to the result set.

An application can call SQLBindCol to rebind result set columns after it has called SQLExtendedFetch, SQLFetch, or SQLFetchScroll, as long as the C data type, column size, and decimal digits of the bound column remain the same. The application need not close the cursor to rebind columns to different addresses.

The cursor library supports setting the SQL_ATTR_ROW_BIND_OFFSET_PTR statement attribute to use bind offsets. (SQLBindCol does not have to be called for this rebinding to occur.) If the cursor library is used with an ODBC 3.x driver, the bind offset is not used when SQLFetch is called. The bind offset is used if SQLFetch is called when the cursor library is used with an ODBC 2.x driver because SQLFetch is then mapped to SQLExtendedFetch.

The cursor library supports calling SQLBindCol to bind the bookmark column.

When working with an ODBC 2.x driver, the cursor library returns SQLSTATE HY090 (Invalid string or buffer length) when SQLBindCol is called to set the buffer length for a bookmark column to a value not equal to 4. When working with an ODBC 3.x driver, the cursor library allows the buffer to be any size.


SQLBindParameter in the Cursor Library

An application can call SQLBindParameter to rebind parameters, as long as the C data type, column size, and decimal digits of the bound column remain the same.

The cursor library supports setting the SQL_ATTR_ROW_BIND_OFFSET_PTR statement attribute to use bind offsets. (SQLBindParameter does not have to be called for this rebinding to occur.)

The cursor library supports binding data-at-execution parameters.


SQLBulkOperations and the Cursor Library

The cursor library does not support calling SQLBulkOperations.


SQLCloseCursor in the Cursor Library

The cursor library does not support calling SQLCloseCursor without an open cursor. Attempting this will return SQLSTATE 24000 (Invalid cursor state). Calling SQLFreeStmt with an Option of SQL_CLOSE when no cursor is open is supported by the cursor library.


SQLEndTran in the Cursor Library

The cursor library does not support transactions and passes calls to SQLEndTran directly to the driver. However, the cursor library does support the cursor commit and rollback behaviors as returned by the data source with the SQL_CURSOR_ROLLBACK_BEHAVIOR and SQL_CURSOR_COMMIT_BEHAVIOR information types:

  • For data sources that preserve cursors across transactions, changes that are rolled back in the data source are not rolled back in the cursor library's cache. To make the cache match the data in the data source, the application must close and reopen the cursor.
  • For data sources that close cursors at transaction boundaries, the cursor library closes the cursors and deletes the caches for all statements on the connection.
  • For data sources that delete prepared statements at transaction boundaries, the application must reprepare all prepared statements on the connection before reexecuting them.


SQLExtendedFetch in the Cursor Library

The cursor library implements SQLExtendedFetch by repeatedly calling SQLFetch in the driver.

The cursor library supports calling SQLExtendedFetch with a FetchOrientation of SQL_FETCH_BOOKMARK.

When the cursor library is used, calls to SQLExtendedFetch cannot be mixed with calls to either SQLFetchScroll or SQLFetch.


SQLFetch in the Cursor Library

When the cursor library is used, calls to SQLFetch cannot be mixed with calls to either SQLFetchScroll or SQLExtendedFetch.

If SQLFetch is called with SQL_ATTR_ROW_ARRAY_SIZE set to a value greater than 1, the cursor library will pass the call to the driver. If the driver is an ODBC 2.x driver, the rowset size will be ignored and the call to SQLFetch will return a single row of data.

If the cursor library is used with an ODBC 2.x driver, a bind offset (as defined by the SQL_ATTR_ROW_BIND_OFFSET_PTR statement attribute) is not used when SQLFetch is called.

When the cursor library is loaded, an application cannot call SQLFetch to fetch bookmark columns. The cursor library passes the call to SQLFetch through to the driver, but the function calls to enable bookmarks and bind the bookmark column are intercepted by the cursor library.


SQLFetchScroll in the Cursor Library

The cursor library implements SQLFetchScroll by repeatedly calling SQLFetch in the driver. It transfers the data it retrieves from the driver to the rowset buffers provided by the application. It also caches the data in memory and disk files. When an application requests a new rowset, the cursor library retrieves it as necessary from the driver (if it has not been previously fetched) or the cache (if it has been previously fetched). Finally, the cursor library maintains the status of the cached data and returns this information to the application in the row status array.

When the cursor library is used, calls to SQLFetchScroll cannot be mixed with calls to either SQLFetch or SQLExtendedFetch.

When the cursor library is used, calls to SQLFetchScroll are supported both for ODBC 2.x and for ODBC 3.x drivers.

Rowset Buffers

The cursor library optimizes the transfer of data from the driver to the rowset buffer provided by the application if:

  • The application uses row-wise binding.
  • There are no unused bytes between fields in the structure the application declares to hold a row of data.
  • The fields in which SQLFetch or SQLFetchScroll returns the length/indicator for a column follows the buffer for that column and precedes the buffer for the next column. These fields are optional.

When the application requests a new rowset, the cursor library retrieves data from its cache and from the driver as necessary. If the new and old rowsets overlap, the cursor library may optimize its performance by reusing the data from the overlapping sections of the rowset buffers. Thus, unsaved changes to the rowset buffers are lost unless the new and old rowsets overlap and the changes are in the overlapping sections of the rowset buffers. To save the changes, an application submits a positioned update statement.

Note that the cursor library always refreshes the rowset buffers with data from the cache when an application calls SQLFetchScroll with the FetchOrientation argument set to SQL_FETCH_RELATIVE and the FetchOffset argument set to 0.

The cursor library supports calling SQLSetStmtAttr with an Attribute of SQL_ATTR_ROW_ARRAY_SIZE to change the rowset size while a cursor is open. The new rowset size will take effect the next time SQLFetchScroll is called.

Result Set Membership

The cursor library retrieves data from the driver only as the application requests it. Depending on the data source and the setting of the SQL_CONCURRENCY statement attribute, this has the following consequences:

  • The data retrieved by the cursor library might differ from the data that was available at the time the statement was executed. For example, after the cursor was opened, rows inserted at a point beyond the current cursor position can be retrieved by some drivers.
  • The data in the result set might be locked by the data source for the cursor library and therefore be unavailable to other users.

After the cursor library has cached a row of data, it cannot detect changes to that row in the underlying data source (except for positioned updates and deletes operating on the same cursor's cache). This occurs because, for calls to SQLFetchScroll, the cursor library never refetches data from the data source. Instead, it refetches data from its cache.

Scrolling

The cursor library supports the following fetch types in SQLFetchScroll.

Cursor type | Fetch types
Forward-only | SQL_FETCH_NEXT
Static | SQL_FETCH_NEXT SQL_FETCH_PRIOR SQL_FETCH_FIRST SQL_FETCH_LAST SQL_FETCH_RELATIVE SQL_FETCH_ABSOLUTE SQL_FETCH_BOOKMARK

Errors

When SQLFetchScroll is called and one of the calls to SQLFetch returns SQL_ERROR, the cursor library proceeds as follows. After it completes these steps, the cursor library continues processing.

  1. Calls SQLGetDiagRec to obtain error information from the driver and posts this as a diagnostic record in the Driver Manager.
  2. Sets the SQL_DIAG_ROW_NUMBER field in the diagnostic record to the appropriate value.
  3. Sets the SQL_DIAG_COLUMN_NUMBER field in the diagnostic record to the appropriate value, if applicable; otherwise, it sets it to 0.
  4. Sets the value for the row in error in the row status array to SQL_ROW_ERROR.

After the cursor library has called SQLFetch multiple times in its implementation of SQLFetchScroll, any error or warning returned by one of the calls to SQLFetch will be in a diagnostic record and can be retrieved by a call to SQLGetDiagRec. If the data was truncated when it was fetched, the truncated data will now reside in the cursor library's cache. Subsequent calls to SQLFetchScroll to scroll to a row with truncated data will return the truncated data, and no warning will be raised because the data is fetched from the cursor library's cache. To keep track of the length of data returned so that it can determine whether the data returned in a buffer has been truncated, an application should bind the length/indicator buffer.

Bookmark Operations

The cursor library supports calling SQLFetchScroll with a FetchOrientation of SQL_FETCH_BOOKMARK. It also supports specifying an offset in the FetchOffset argument to be used in the bookmark operation. This is the only bookmark operation the cursor library supports. The cursor library does not support calling SQLBulkOperations.

If the application has set the SQL_ATTR_USE_BOOKMARKS statement attribute and has bound to the bookmark column, the cursor library generates a fixed-length bookmark and returns it to the application. The cursor library creates and maintains the bookmarks that it uses; it does not use bookmarks maintained at the data source. When SQLFetchScroll is called to retrieve a block of data that has already been fetched from the data source, it retrieves the data from the cursor library cache. As a result, the bookmark used in a call to SQLFetchScroll with a FetchOrientation of SQL_FETCH_BOOKMARK must be created and maintained by the cursor library.

Interaction with Other Functions

An application must call SQLFetch or SQLFetchScroll before it prepares or executes any positioned update or delete statements.


SQLFreeStmt in the Cursor Library

If an application calls SQLFreeStmt with the SQL_UNBIND option after it calls SQLExtendedFetch, SQLFetch, or SQLFetchScroll, the cursor library returns an error. Before it can unbind result set columns, an application must call SQLCloseCursor or SQLFreeStmt with the SQL_CLOSE option.


SQLGetData in the Cursor Library

The cursor library implements SQLGetData by first constructing a SELECT statement with a WHERE clause that enumerates the values stored in its cache for each bound column in the current row. It then executes the SELECT statement to reselect the row and calls SQLGetData in the driver to retrieve the data from the data source (as opposed to the cache).

Caution The WHERE clause constructed by the cursor library to identify the current row can fail to identify any rows, identify a different row, or identify more than one row. For more information, see "Constructing Searched Statements," earlier in this appendix.

If the SQL_ATTR_USE_BOOKMARKS statement attribute is set to SQL_UB_VARIABLE, SQLGetData can be called on column 0 to return bookmark data.

Calls to SQLGetData are subject to the following restrictions:

  • SQLGetData cannot be called for forward-only cursors.
  • SQLGetData can be called only when the following conditions are met: a SELECT statement generated the result set; the SELECT statement did not contain a join, a UNION clause, or a GROUP BY clause; and any columns that used an alias or expression in the select list were not bound with SQLBindCol.
  • If the driver supports only one active statement, the cursor library fetches the rest of the result set before executing the SELECT statement and calling SQLGetData.


SQLGetDescField and SQLGetDescRec in the Cursor Library

The cursor library executes SQLGetDescRec to return metadata for bookmark columns. The cursor library executes SQLGetDescField to return the same fields returned by SQLGetDescRec, which are SQL_DESC_NAME, SQL_DESC_TYPE, SQL_DESC_DATETIME_INTERVAL_CODE, SQL_DESC_OCTET_LENGTH, SQL_DESC_PRECISION, SQL_DESC_SCALE, and SQL_DESC_NULLABLE. For consistency, SQLGetDescField also returns SQL_DESC_UNNAMED.

The cursor library executes SQLGetDescField when it is called to return the value of the following fields that are set for binding bookmark columns: SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, SQL_DESC_OCTET_LENGTH_PTR, and SQL_DESC_LENGTH.

The cursor library executes SQLGetDescField when it is called to return the value of the SQL_DESC_BIND_OFFSET_PTR, SQL_DESC_BIND_TYPE, SQL_DESC_ROW_ARRAY_SIZE, or SQL_DESC_ROW_STATUS_PTR field. These fields can be returned for any row, not just the bookmark row.

If an application calls SQLGetDescField to return the value of any field other than those mentioned previously, the cursor library passes the call to the driver.


SQLGetFunctions in the Cursor Library

The cursor library returns that it supports SQLExtendedFetch, SQLFetchScroll, SQLSetPos, and SQLSetScrollOptions in addition to the functions supported by the driver.


SQLGetInfo in the Cursor Library

The cursor library returns values for the following values of InfoType (| represents a bitwise OR); for all other values of InfoType, it calls SQLGetInfo in the driver.

InfoType | Returned value
SQL_BOOKMARK_PERSISTENCE | SQL_BP_SCROLL
SQL_DYNAMIC_CURSOR_ATTRIBUTES1 | 0
SQL_DYNAMIC_CURSOR_ATTRIBUTES2 | 0
SQL_FETCH_DIRECTION[1] | SQL_FD_FETCH_ABSOLUTE | SQL_FD_FETCH_FIRST | SQL_FD_FETCH_LAST | SQL_FD_FETCH_NEXT | SQL_FD_FETCH_PRIOR | SQL_FD_FETCH_RELATIVE | SQL_FD_FETCH_BOOKMARK
SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 | SQL_CA1_NEXT | SQL_CA1_ABSOLUTE | SQL_CA1_RELATIVE | SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION | SQL_CA1_POSITIONED_DELETE | SQL_CA1_POSITIONED_UPDATE | SQL_CA1_SELECT_FOR_UPDATE
SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 | SQL_CA2_READ_ONLY_CONCUR | SQL_CA2_OPT_VALUES_CONCURRENCY | SQL_CA2_SENSITIVITY_UPDATES
SQL_GETDATA_EXTENSIONS | SQL_GD_BLOCK | any values returned by the driver Note When data is retrieved with SQLFetchScroll, SQLGetData supports the functionality specified with the SQL_GD_ANY_COLUMN and SQL_GD_BOUND bitmasks.
SQL_KEYSET_DRIVEN_CURSOR_ATTRIBUTES1 | 0
SQL_KEYSET_DRIVEN_CURSOR_ATTRIBUTES2 | 0
SQL_LOCK_TYPES[1] | SQL_LCK_NO_CHANGE
SQL_STATIC_CURSOR_ATTRIBUTES1 | SQL_CA1_NEXT | SQL_CA1_ABSOLUTE | SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK | SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION | SQL_CA1_POSITIONED_DELETE | SQL_CA1_POSITIONED_UPDATE | SQL_CA1_SELECT_FOR_UPDATE
SQL_STATIC_CURSOR_ATTRIBUTES2 | SQL_CA2_READ_ONLY_CONCUR | SQL_CA2_OPT_VALUES_ CONCURRENCY | SQL_CA2_SENSITIVITY_UPDATES
SQL_POS_OPERATIONS[1] | SQL_POS_POSITION
SQL_POSITIONED_STATEMENTS[1] | SQL_PS_POSITIONED_DELETE | SQL_PS_POSITIONED_UPDATE | SQL_PS_SELECT_FOR_UPDATE
SQL_ROW_UPDATES | "Y"
SQL_SCROLL_CONCURRENCY[1] | SQL_SCCO_READ_ONLY | SQL_SCCO_OPT_VALUES
SQL_SCROLL_OPTIONS | SQL_SO_FORWARD_ONLY | SQL_SO_STATIC
SQL_STATIC_SENSITIVITY[1] | SQL_SS_UPDATES

[1] Used only when the cursor library is used with an ODBC 2.x driver.

Important The cursor library implements the same cursor behavior when transactions are committed or rolled back as the data source. That is, committing or rolling back a transaction, either by calling SQLEndTran or by using the SQL_ATTR_AUTOCOMMIT connection attribute, can cause the data source to delete the access plans and close the cursors for all statements on a connection. For more information, see the SQL_CURSOR_COMMIT_BEHAVIOR and SQL_CURSOR_ROLLBACK_BEHAVIOR information types in SQLGetInfo.


SQLGetStmtAttr in the Cursor Library

The cursor library supports the following statement attributes with SQLGetStmtAttr:

SQL_ATTR_CONCURRENCY | SQL_ATTR_ROW_BIND_OFFSET_PTR
SQL_ATTR_CURSOR_TYPE | SQL_ATTR_ROW_BIND_TYPE
SQL_ATTR_FETCH_BOOKMARK_PTR | SQL_ATTR_ROW_NUMBER
SQL_ATTR_PARAM_BIND_OFFSET_PTR | SQL_ATTR_ROW_ARRAY_SIZE
SQL_ATTR_PARAM_BIND_TYPE | SQL_ATTR_SIMULATE_CURSOR


SQLGetStmtOption in the Cursor Library

The cursor library supports the following statement options with SQLGetStmtOption:

SQL_BIND_TYPE | SQL_ROW_NUMBER
SQL_CONCURRENCY | SQL_ROWSET_SIZE
SQL_CURSOR_TYPE | SQL_SIMULATE_CURSOR
SQL_GET_BOOKMARK |


SQLNativeSql in the Cursor Library

If the driver supports this function, the cursor library calls SQLNativeSql in the driver and passes it the SQL statement. For positioned update, positioned delete, and SELECT FOR UPDATE statements, the cursor library modifies the statement before passing it to the driver.

Note The cursor library incorrectly returns SQLSTATE 34000 (Invalid cursor name) if the cursor name is invalid in a positioned update or delete statement that is passed in the InStatementText argument of SQLNativeSql. SQLNativeSql is not intended to return syntax errors, which are returned only upon statement preparation or execution.


SQLRowCount in the Cursor Library

When an application calls SQLRowCount with the statement associated with the cursor, the cursor library returns the number of rows of data it has retrieved from the driver.

When an application calls SQLRowCount with the statement associated with a positioned update or delete statement, the cursor library returns the number of rows affected by the statement.

When an application calls SQLRowCount after a SELECT statement, the cursor library returns –1.


SQLSetConnectAttr in the Cursor Library

An application calls SQLSetConnectAttr with the SQL_ATTR_ODBC_CURSORS attribute to specify whether the cursor library is always used, used if the driver does not support scrollable cursors, or never used. The cursor library assumes that a driver supports scrollable cursors if it returns SQL_CA1_RELATIVE for the SQL_STATIC_CURSOR_ATTRIBUTES1 information type in SQLGetInfo.

The application must call SQLSetConnectAttr to specify the cursor library usage after it calls SQLAllocHandle with a HandleType of SQL_HANDLE_DBC to allocate the connection and before it connects to the data source. If an application calls SQLSetConnectAttr with the SQL_ATTR_ODBC_CURSORS attribute while the connection is still active, the cursor library returns an error.

To set a statement attribute supported by the cursor library for all statements associated with a connection, an application must call SQLSetConnectAttr for that statement attribute after it connects to the data source and before it opens the cursor. If an application calls SQLSetConnectAttr with a statement attribute and a cursor is open on a statement associated with the connection, the statement attribute will not be applied to that statement until the cursor is closed and reopened.


SQLSetDescField and SQLSetDescRec in the Cursor Library

The cursor library executes SQLSetDescField when it is called to return the value of the fields set for bookmark columns: SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, SQL_DESC_OCTET_LENGTH_PTR, SQL_DESC_LENGTH, SQL_DESC_OCTET_LENGTH, SQL_DESC_DATETIME_INTERVAL_CODE, SQL_DESC_SCALE, SQL_DESC_PRECISION, SQL_DESC_TYPE, SQL_DESC_NAME, SQL_DESC_UNNAMED, and SQL_DESC_NULLABLE. The cursor library executes calls to SQLSetDescRec for a bookmark column.

When working with an ODBC 2.x driver, the cursor library returns SQLSTATE HY090 (Invalid string or buffer length) when SQLSetDescField or SQLSetDescRec is called to set the SQL_DESC_OCTET_LENGTH field for the bookmark record of an ARD to a value not equal to 4. When working with an ODBC 3.x driver, the cursor library allows the buffer to be any size.

The cursor library executes SQLSetDescField when it is called to return the value of the SQL_DESC_BIND_OFFSET_PTR, SQL_DESC_BIND_TYPE, SQL_DESC_ROW_ARRAY_SIZE, or SQL_DESC_ROW_STATUS_PTR field. These fields can be returned for any row, not just the bookmark row.

The cursor library does not execute SQLSetDescField to change any descriptor field other than the fields mentioned previously. If an application calls SQLSetDescField to set any other field while the cursor library is loaded, the call is passed through to the driver.

The cursor library supports changing the SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR fields of any row of an application row descriptor dynamically (after a call to SQLExtendedFetch, SQLFetch, or SQLFetchScroll). The SQL_DESC_OCTET_LENGTH_PTR field can be changed to a null pointer only to unbind the length buffer for a column.

The cursor library does not support changing the SQL_DESC_BIND_TYPE field in an APD or ARD when a cursor is open. The SQL_DESC_BIND_TYPE field can be changed only after the cursor is closed and before a new cursor is opened. The only descriptor fields that the cursor library supports changing when a cursor is open are SQL_DESC_ARRAY_STATUS_PTR, SQL_DESC_BIND_OFFSET_PTR, SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, SQL_DESC_OCTET_LENGTH_PTR, and SQL_DESC_ROWS_PROCESSED_PTR.

The cursor library does not support modifying the SQL_DESC_COUNT field of the ARD after SQLExtendedFetch or SQLFetchScroll has been called and before the cursor has been closed.


SQLSetEnvAttr and the Cursor Library

The cursor library is unaffected by the setting of the SQL_ATTR_ODBC_VERSION environment attribute, regardless of the application version or driver version.


SQLSetPos in the Cursor Library

The cursor library supports the SQL_POSITION operation only for the Operation argument in SQLSetPos. It supports the SQL_LOCK_NO_CHANGE value only for the LockType argument.

If the driver does not support bulk operations, the cursor library returns SQLSTATE HYC00 (Driver not capable) when SQLSetPos is called with RowNumber equal to 0. This driver behavior is not recommended.

The cursor library does not support the SQL_UPDATE and SQL_DELETE operations in a call to SQLSetPos. The cursor library implements a positioned update or delete SQL statement by creating a searched update or delete statement with a WHERE clause that enumerates the values stored in its cache for each bound column. For more information, see "Processing Positioned Update and Delete Statements," earlier in this appendix.

If the driver does not support static cursors, an application working with the cursor library should call SQLSetPos only on a rowset fetched by SQLExtendedFetch or SQLFetchScroll, not by SQLFetch. The cursor library implements SQLExtendedFetch and SQLFetchScroll by making repeated calls of SQLFetch (with a rowset size of 1) in the driver. The cursor library passes calls to SQLFetch, on the other hand, through to the driver. If SQLSetPos is called on a multirow rowset fetched by SQLFetch when the driver does not support static cursors, the call will fail because SQLSetPos does not work with forward-only cursors. This will occur even if an application has successfully called SQLSetStmtAttr to set SQL_ATTR_CURSOR_TYPE to SQL_CURSOR_STATIC, which the cursor library supports even if the driver does not support static cursors.


SQLSetScrollOptions in the Cursor Library

The cursor library supports SQLSetScrollOptions only for backward compatibility; applications should use the SQL_ATTR_CONCURRENCY, SQL_ATTR_CURSOR_TYPE, and SQL_ATTR_ROW_ARRAY_SIZE statement attributes instead.


SQLSetStmtAttr in the Cursor Library

The cursor library supports the following statement attributes with SQLSetStmtAttr:

SQL_ATTR_CONCURRENCY | SQL_ATTR_ROW_BIND_OFFSET_PTR
SQL_ATTR_CURSOR_TYPE | SQL_ATTR_ROW_BIND_TYPE
SQL_ATTR_FETCH_BOOKMARK_PTR | SQL_ATTR_ROWSET_ARRAY_SIZE
SQL_ATTR_PARAM_BIND_OFFSET_PTR | SQL_ATTR_SIMULATE_CURSOR
SQL_ATTR_PARAM_BIND_TYPE | SQL_ATTR_USE_BOOKMARKS

The cursor library supports only the SQL_CURSOR_FORWARD_ONLY and SQL_CURSOR_STATIC values of the SQL_ATTR_CURSOR_TYPE statement attribute.

For forward-only cursors, the cursor library supports the SQL_CONCUR_READ_ONLY value of the SQL_ATTR_CONCURRENCY statement attribute. For static cursors, the cursor library supports the SQL_CONCUR_READ_ONLY and SQL_CONCUR_VALUES values of the SQL_ATTR_CONCURRENCY statement attribute.

The cursor library supports only the SQL_SC_NON_UNIQUE value of the SQL_ATTR_SIMULATE_CURSOR statement attribute.

Although the ODBC specification supports calls to SQLSetStmtAttr with the SQL_ATTR_PARAM_BIND_TYPE or SQL_ATTR_ROW_BIND_TYPE attributes after SQLFetch or SQLFetchScroll has been called, the cursor library does not. Before it can change the binding type in the cursor library, the application must close the cursor. The cursor library supports changing the SQL_ATTR_ROW_BIND_OFFSET_PTR, SQL_ATTR_PARAM_BIND_OFFSET_PTR, SQL_ATTR_ROWS_FETCHED_PTR, and SQL_ATTR_PARAMS_PROCESSED_PTR statement attributes when a cursor is open.

An application can call SQLSetStmtAttr with an Attribute of SQL_ATTR_ROW_ARRAY_SIZE to change the rowset size while a cursor is open. The new rowset size will take effect the next time SQLFetchScroll or SQLFetch is called.

The cursor library supports setting the SQL_ATTR_PARAM_BIND_OFFSET_PTR or SQL_ATTR_ROW_BIND_OFFSET_PTR statement attribute to enable binding offsets. The binding offset will not be used for calls to SQLFetch when the cursor library is used with an ODBC 2.x driver.

The cursor library supports setting the SQL_ATTR_USE_BOOKMARKS statement attribute to SQL_UB_VARIABLE.


ODBC Cursor Library Error Codes

The ODBC cursor library returns the following SQLSTATEs in addition to those listed in Chapter 21: ODBC API Reference.

Note The cursor library does not order status records; the Driver Manager and ODBC 3.x drivers are responsible for ordering status records.

SQLSTATE | Description | Can be returned from
01000 | Cursor is not updatable. | SQLFetch SQLFetchScroll
01000 | Cursor library not used. Load failed. | SQLBrowseConnect SQLConnect SQLDriverConnect
01000 | Cursor library not used. Insufficient driver support. | SQLBrowseConnect SQLConnect SQLDriverConnect
01000 | Cursor library not used. Version mismatch with Driver Manager. | SQLBrowseConnect SQLConnect SQLDriverConnect
01000 | Driver returned SQL_SUCCESS_WITH_INFO. The warning message has been lost. | SQLFetch SQLFetchScroll
S1000 | General error: Unable to create file buffer. | SQLFetch SQLFetchScroll SQLGetData
S1000 | General error: Unable to read from file buffer. | SQLFetch SQLFetchScroll SQLGetData
S1000 | General error: Unable to write to file buffer. | SQLFetch SQLFetchScroll SQLGetData
S1000 | General error: Unable to close or remove file buffer. | SQLFreeHandle SQLFreeStmt
SL001 | Positioned request cannot be performed because no searchable columns were bound. | SQLExecDirect SQLGetData SQLPrepare
SL002 | Positioned request could not be performed because result set was created by a join condition. | SQLExecute SQLExecDirect SQLGetData
SL003 | Bound buffer exceeds maximum segment size. | SQLFetch SQLFetchScroll
SL004 | Result set was not generated by a SELECT statement. | SQLGetData
SL005 | SELECT statement contains a GROUP BY clause. | SQLGetData
SL006 | Parameter arrays are not supported with positioned requests. | SQLPrepare SQLExecDirect
SL008 | SQLGetData is not allowed on a forward-only (nonbuffered) cursor. | SQLGetData
SL009 | No columns were bound prior to calling SQLFetch or SQLFetchScroll. | SQLFetch SQLFetchScroll
SL010 | SQLBindCol returned SQL_ERROR during an attempt to bind to an internal buffer. | SQLFetch SQLFetchScroll SQLGetData
SL011 | Statement option is valid only after calling SQLFetch or SQLFetchScroll. | SQLGetStmtAttr
SL012 | Statement bindings may not be changed while a cursor is open. | SQLBindCol SQLFreeHandle SQLFreeStmt SQLSetStmtAttr
SL014 | A positioned request was issued and not all column count fields were buffered. | SQLExecDirect SQLExecute SQLPrepare
SL015 | SQLFetch and SQLFetchScroll cannot be mixed. | SQLExtendedFetch SQLFetch SQLFetchScroll


Appendix G: Driver Guidelines for Backward Compatibility

This appendix provides information for driver writers working on ODBC 3.x drivers that need to support ODBC 2.x applications. Changes between ODBC 2.x and ODBC 3.x fall into three categories; for more information about these categories, see "Backward Compatibility and Standards Compliance" in Chapter 17: Programming Considerations.

  • New Features. New features are features that exist in ODBC 3.x and not in ODBC 2.x. ODBC 3.x drivers generally do not have to worry about backward compatibility with new features because ODBC 2.x applications never use them. The sole exceptions to this are features related to SQLFetch, SQLFetchScroll, SQLSetPos, and SQLExtendedFetch; for more information, see "Block Cursors, Scrollable Cursors, and Backward Compatibility," later in this appendix.
  • Duplicated Features. Duplicated features are features that are implemented differently in ODBC 3.x and ODBC 2.x. ODBC 3.x drivers do not have to worry about backward compatibility with duplicated features because the Driver Manager always maps ODBC 2.x features to ODBC 3.x features when calling an ODBC 3.x driver. Thus, an ODBC 3.x driver sees only ODBC 3.x features. For more information about these mappings, see "Mapping Deprecated Functions," later in this appendix.
  • Behavioral Changes. Behavior changes are features that are handled differently in ODBC 3.x and ODBC 2.x. ODBC 3.x drivers have to worry about behavior changes and act in response to the SQL_ATTR_ODBC_VERSION environment attribute set by the application. For more information, see "Behavioral Changes and ODBC 3.x Drivers," later in this appendix.

For information about other topics where backward compatibility of ODBC 3.x drivers with ODBC 2.x drivers may be of concern to users and developers, see the following sections of this appendix:

  • Datetime Data Types
  • Backward Compatibility of C Data Types
  • Fixed-Length Bookmarks
  • SQLGetInfo Support
  • Returning SQL_NO_DATA
  • Calling SQLSetPos to Insert Data
  • Loading by Ordinal


Block Cursors, Scrollable Cursors, and Backward Compatibility

The existence of both SQLFetchScroll and SQLExtendedFetch represents the first clear split in ODBC between the Application Programming Interface (API), which is the set of functions the application calls, and the Service Provider Interface (SPI), which is the set of functions the driver implements. This split is necessary so that ODBC 3.x, which uses SQLFetchScroll, be aligned with the standards and also be compatible with ODBC 2.x, which uses SQLExtendedFetch.

The ODBC 3.x API, which is the set of functions the application calls, includes SQLFetchScroll and related statement attributes. The ODBC 3.x SPI, which is the set of functions the driver implements, includes SQLFetchScroll, SQLExtendedFetch, and related statement attributes. Because ODBC does not formally enforce this split between the API and the SPI, it is possible for ODBC 3.x applications to call SQLExtendedFetch and related statement attributes. However, there is no reason for ODBC 3.x application to do this. For more information about APIs and SPIs, see the introduction to Chapter 3: ODBC Architecture.

For information about what functions and statement attributes an ODBC 3.x application should use with block and scrollable cursors, see "Block Cursors, Scrollable Cursors, and Backward Compatibility for ODBC 3.x Applications" in Chapter 17: Programming Considerations.


What the Driver Manager Does

The following table summarizes how the ODBC 3.x Driver Manager maps calls to ODBC 2.x and ODBC 3.x drivers.

Function or
statement attribute
Comments
SQL_ATTR_FETCH_BOOKMARK_PTR Points to the bookmark to use with SQLFetchScroll. The following are implementation details:
  • When an application sets this in an ODBC 2.x driver, the ODBC 3.x Driver Manager caches it. It dereferences the pointer and passes the value to the ODBC 2.x driver in the FetchOffset argument of SQLExtendedFetch when SQLFetchScroll is later called by the application.
  • When an application sets this in an ODBC 3.x driver, the ODBC 3.x Driver Manager passes the call to the driver.
SQL_ATTR_ROW_STATUS_PTR Points to the row status array filled by SQLFetch, SQLFetchScroll, SQLBulkOperations, and SQLSetPos. The following are implementation details:
  • When an application sets this in an ODBC 2.x driver, the ODBC 3.x Driver Manager caches its value. It passes this value to the ODBC 2.x driver in the RowStatusArray argument of SQLExtendedFetch when SQLFetchScroll or SQLFetch is called.
  • When an application sets this in an ODBC 3.x driver, the ODBC 3.x Driver Manager passes the call to the driver.
  • In state S6, if an application sets SQL_ATTR_ROW_STATUS_PTR and then calls SQLBulkOperations (with an Operation of SQL_ADD) or SQLSetPos without first calling SQLFetch or SQLFetchScroll, SQLSTATE HY011 (Attribute cannot be set now) is returned.
SQL_ATTR_ROWS_FETCHED_PTR Points to the buffer in which SQLFetch and SQLFetchScroll return the number of rows fetched. The following are implementation details:
  • When an application sets this in an ODBC 2.x driver, the ODBC 3.x Driver Manager caches its value. It passes this value to the ODBC 2.x driver in the RowCountPtr argument of SQLExtendedFetch when SQLFetch or SQLFetchScroll is called by the application.
  • When an application sets this in an ODBC 3.x driver, the ODBC 3.x Driver Manager passes the call to the driver.
SQL_ATTR_ROW_ARRAY_SIZE Sets the rowset size. The following are implementation details:
  • When an application sets this in an ODBC 2.x driver, the ODBC 3.x Driver Manager maps it to the SQL_ROWSET_SIZE statement attribute.
  • When an application sets this in an ODBC 3.x driver, the ODBC 3.x Driver Manager passes the call to the driver.
  • When an application working with an ODBC 3.x driver calls SQLSetScrollOptions, SQL_ROWSET_SIZE is set to the value in the RowsetSize argument if the underlying driver does not support SQLSetScrollOptions.
SQL_ROWSET_SIZE Sets the rowset size used by SQLExtendedFetch when SQLExtendedFetch is called by an ODBC 2.x application. The following are implementation details:
  • When an application sets this, the ODBC 3.x Driver Manager passes the call to the driver, regardless of driver version.
  • When an application working with an ODBC 2.x driver calls SQLSetScrollOptions, SQL_ROWSET_SIZE is set to the value in the RowsetSize argument.
SQLBulkOperations Performs an insert operation, or update, delete, or fetch by bookmark operations. The following are implementation details:
  • When an application calls SQLBulkOperations with an Operation of SQL_ADD in an ODBC 2.x driver, the ODBC 3.x Driver Manager maps it to SQLSetPos with an Operation of SQL_ADD.
  • When working with an ODBC 2.x driver that does not support SQLSetPos with an Operation of SQL_ADD, the ODBC 3.x Driver Manager does not map SQLSetPos with an Operation of SQL_ADD to SQLBulkOperations with an Operation of SQL_ADD. This is because SQLBulkOperations cannot be called in state S7, which in ODBC 2.x was the only state in which SQLSetPos could be called.
  • If the application calls SQLBulkOperations with an Operation of SQL_ADD in an ODBC 2.x driver before calling SQLFetchScroll, the ODBC 3.x Driver Manager returns an error.
SQLExtendedFetch
  • Returns the specified rowset. Except for the restriction just noted, the ODBC 3.x Driver Manager passes calls to SQLExtendedFetch to the driver, regardless of the driver version.
SQLFetch Returns the next rowset. The following are implementation details:
  • When an application calls SQLFetch in an ODBC 2.x driver, the ODBC 3.x Driver Manager maps it to SQLExtendedFetch. The FetchOrientation argument of SQLExtendedFetch is set to SQL_FETCH_NEXT. The Driver Manager uses the cached value of the SQL_ATTR_ROW_STATUS_PTR statement attribute for the RowStatusArray argument and the cached value of the SQL_ATTR_ROWS_FETCHED_PTR statement attribute for the RowCountPtr argument.
  • An ODBC 3.x application can mix calls to SQLFetch and SQLFetchScroll in an ODBC 2.x driver because the ODBC 3.x Driver Manager maps SQLFetch to SQLExtendedFetch when an application calls it in an ODBC 2.x driver.
  • If an ODBC 2.x driver does not support SQLExtendedFetch, the ODBC 3.x Driver Manager does not map SQLFetch or SQLFetchScroll to SQLExtendedFetch when an application calls it in that driver. If the application attempts to set SQL_ATTR_ROW_ARRAY_SIZE to a value greater than 1, SQLSTATE HYC00 (Optional feature not implemented) is returned.
  • Except for the restrictions just noted, the ODBC 3.x Driver Manager passes calls to SQLFetch to the driver, regardless of the driver version.
SQLFetchScroll Returns the specified rowset. The following are implementation details:
  • When an application calls SQLFetchScroll in an ODBC 2.x driver, the ODBC 3.x Driver Manager maps it to SQLExtendedFetch. It uses the cached value of the SQL_ATTR_ROW_STATUS_PTR statement attribute for the RowStatusArray argument and the cached value of the SQL_ATTR_ROWS_FETCHED_PTR statement attribute for the RowCountPtr argument. If the FetchOrientation argument in SQLFetchScroll is SQL_FETCH_BOOKMARK, it uses the cached value of the SQL_ATTR_FETCH_BOOKMARK_PTR statement attribute for the FetchOffset argument and returns an error if the FetchOffset argument of SQLFetchScroll is not 0.
  • When an application calls this in an ODBC 3.x driver, the ODBC 3.x Driver Manager passes the call to the driver.
SQLSetPos | Performs various positioned operations. The ODBC 3.x Driver Manager passes calls to SQLSetPos to the driver, regardless of the driver version.
SQLSetScrollOptions | When the Driver Manager maps SQLSetScrollOptions for an application working with an ODBC 3.x driver that does not support SQLSetScrollOptions, the Driver Manager sets the SQL_ROWSET_SIZE statement option, not the SQL_ATTR_ROW_ARRAY_SIZE statement attribute, to the RowsetSize argument in SQLSetScrollOption. As a result, SQLSetScrollOptions cannot be used by an application when fetching multiple rows by a call to SQLFetch or SQLFetchScroll. It can be used only when fetching multiple rows by a call to SQLExtendedFetch.


What the Driver Does

The following table summarizes what functions and statement attributes an ODBC 3.x driver should implement for block and scrollable cursors.

Function or
statement attribute
Comments
SQL_ATTR_ROW_STATUS_PTR | Sets the address of the row status array filled by SQLFetch and SQLFetchScroll. This array is also filled by SQLSetPos if SQLSetPos is called in statement state S6. If SQLSetPos is called in state S7, this array is not filled but the array pointed to by the RowStatusArray argument of SQLExtendedFetch is filled. For more information, see "Statement Transitions" in Appendix B: ODBC State Transition Tables.
SQL_ATTR_ROWS_FETCHED_PTR | Sets the address of the buffer in which SQLFetch and SQLFetchScroll return the number of rows fetched. If SQLExtendedFetch is called, this buffer is not filled but the RowCountPtr argument points to the number of rows fetched.
SQL_ATTR_ROW_ARRAY_SIZE | Sets the rowset size used by SQLFetch and SQLFetchScroll.
SQL_ROWSET_SIZE | Sets the rowset size used by SQLExtendedFetch. ODBC 3.x drivers implement this if they want to work with ODBC 2.x applications that call SQLExtendedFetch or SQLSetPos.
SQLBulkOperations | If an ODBC 3.x driver should work with ODBC 2.x applications that use SQLSetPos with an Operation of SQL_ADD, the driver must support SQLSetPos with an Operation of SQL_ADD in addition to SQLBulkOperations with an Operation of SQL_ADD.
SQLExtendedFetch Returns the specified rowset. ODBC 3.x drivers implement this if they want to work with ODBC 2.x applications that call SQLExtendedFetch or SQLSetPos. The following are implementation details:
  • The driver retrieves the rowset size from the value of the SQL_ROWSET_SIZE statement attribute.
  • The driver retrieves the address of the row status array from the RowStatusArray argument, not the SQL_ATTR_ROW_STATUS_PTR statement attribute. The RowStatusArray argument in a call to SQLExtendedFetch must not be a null pointer. (Note that in ODBC 3.x, the SQL_ATTR_ROW_STATUS_PTR statement attribute can be a null pointer.)
  • The driver retrieves the address of the rows fetched buffer from the RowCountPtr argument, not the SQL_ATTR_ROWS_FETCHED_PTR statement attribute.
  • The driver returns SQLSTATE 01S01 (Error in row) to indicate that an error has occurred while rows were fetched by a call to SQLExtendedFetch. An ODBC 3.x driver should return SQLSTATE 01S01 (Error in row) only when SQLExtendedFetch is called, not when SQLFetch or SQLFetchScroll is called. To preserve backward compatibility, when SQLSTATE 01S01 (Error in row) is returned by SQLExtendedFetch, the Driver Manager does not order status records in the error queue according to the rules stated in the "Sequence of Status Records" section of SQLGetDiagField.
SQLFetch Returns the next rowset. The following are implementation details:
  • The driver retrieves the rowset size from the value of the SQL_ATTR_ROW_ARRAY_SIZE statement attribute.
  • The driver retrieves the address of the row status array from the SQL_ATTR_ROW_STATUS_PTR statement attribute.
  • The driver retrieves the address of the rows fetched buffer from the SQL_ATTR_ROWS_FETCHED_PTR statement attribute.
  • The application can mix calls between SQLFetchScroll and SQLFetch.
  • SQLFetch returns bookmarks if column 0 is bound.
  • SQLFetch can be called to return more than one row.
  • The driver does not return SQLSTATE 01S01 (Error in row) to indicate that an error has occurred while rows were fetched by a call to SQLFetch.
SQLFetchScroll Returns the specified rowset. The following are implementation details:
  • The driver retrieves the rowset size from the SQL_ATTR_ROW_ARRAY_SIZE statement attribute.
  • The driver retrieves the address of the row status array from the SQL_ATTR_ROW_STATUS_PTR statement attribute.
  • The driver retrieves the address of the rows fetched buffer from the SQL_ATTR_ROWS_FETCHED_PTR statement attribute.
  • The application can mix calls between SQLFetchScroll and SQLFetch.
  • The driver does not return SQLSTATE 01S01 (Error in row) to indicate that an error has occurred while rows were fetched by a call to SQLFetchScroll.
SQLSetPos Performs various positioned operations. The following are implementation details:
  • This can be called in statement states S6 or S7. For more details, see "Statement Transitions" in Appendix B: ODBC State Transition Tables.
  • If this is called in statement state S5 or S6, the driver retrieves the rowset size from the SQL_ATTR_ROWS_FETCHED_PTR statement attribute and the address of the row status array from the SQL_ATTR_ROW_STATUS_PTR statement attribute.
  • If this is called in statement state S7, the driver retrieves the rowset size from the SQL_ROWSET_SIZE statement attribute and the address of the row status array from the RowStatusArray argument of SQLExtendedFetch.
  • The driver returns SQLSTATE 01S01 (Error in row) only to indicate that an error has occurred while rows were fetched by a call to SQLSetPos to perform a bulk operation when the function is called in state S7. To preserve backward compatibility, if SQLSTATE 01S01 (Error in row) is returned by SQLSetPos, the Driver Manager does not order status records in the error queue according to the rules stated in the "Sequence of Status Records" section of SQLGetDiagField.
  • If the driver should work with ODBC 2.x applications that call SQLSetPos with an Operation argument of SQL_ADD, the driver must support SQLSetPos with an Operation argument of SQL_ADD.


Mapping Deprecated Functions

This section describes how deprecated functions are mapped by the ODBC 3.x Driver Manager to guarantee backward compatibility of ODBC 3.x drivers that are used with ODBC 2.x applications. The Driver Manager performs this mapping regardless of the version of the application. Because each of the ODBC 2.x functions in the following list is mapped to the corresponding ODBC 3.x function when called in an ODBC 3.x driver, the ODBC 3.x driver does not have to implement the ODBC 2.x functions.

The mapping in the list is triggered when the driver is an ODBC 3.x driver and the driver does not support the function that is being mapped.

The following table lists all duplicated functionality that was introduced in ODBC 3.x.

ODBC 2.x function | ODBC 3.x function
SQLAllocConnect | SQLAllocHandle
SQLAllocEnv | SQLAllocHandle
SQLAllocStmt | SQLAllocHandle
SQLBindParam[1] | SQLBindParameter
SQLColAttributes | SQLColAttribute
SQLError | SQLGetDiagRec
SQLFreeConnect | SQLFreeHandle
SQLFreeEnv | SQLFreeHandle
SQLFreeStmt with an Option of SQL_DROP | SQLFreeHandle
SQLGetConnectOption | SQLGetConnectAttr
SQLGetStmtOption | SQLGetStmtAttr
SQLParamOptions | SQLSetStmtAttr
SQLSetConnectOption | SQLSetConnectAttr
SQLSetParam[2] | SQLBindParameter
SQLSetScrollOption | SQLSetStmtAttr
SQLSetStmtOption | SQLSetStmtAttr
SQLTransact | SQLEndTran

[1] Even though this function did not exist in ODBC 2.x, it is in the X/Open and ISO standards.

[2] This is an ODBC 1.0 function.


SQLAllocConnect Mapping

When an application calls SQLAllocConnect through an ODBC 3.x driver, the call to SQLAllocConnect(henv, phdbc) is mapped to SQLAllocHandle as follows:

  1. The Driver Manager allocates a connection and returns it to the application.
  2. When the application establishes a connection, the Driver Manager calls
    SQLAllocHandle(SQL_HANDLE_DBC, InputHandle, OutputHandlePtr)

    in the driver with InputHandle set to henv, and OutputHandlePtr set to phdbc.


SQLAllocEnv Mapping

When an application calls SQLAllocEnv through an ODBC 3.x driver, the call to SQLAllocEnv(phenv) is mapped to SQLAllocHandle as follows:

  1. The Driver Manager allocates an environment handle and returns it to the application. The Driver Manager calls SQLSetEnvAttr to set the SQL_ATTR_ODBC_VERSION environment attribute to SQL_OV_ODBC2.
  2. When the application establishes the first connection to a driver, the Driver Manager calls
    SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, OutputHandlePtr)

    in the driver with OutputHandlePtr set to phenv.


SQLAllocStmt Mapping

When an application calls SQLAllocStmt through an ODBC 3.x driver, the call to:

SQLAllocStmt(hdbc, phstmt)

is mapped to SQLAllocHandle by the Driver Manager in the driver as follows:

SQLAllocHandle(SQL_HANDLE_STMT, InputHandle, OutputHandlePtr)

with InputHandle set to hdbc and OutputHandlePtr set to phstmt.


SQLBindParam Mapping

SQLBindParam cannot truly be called deprecated because it was never there in ODBC; however, it still represents duplicated functionality—the Driver Manager needs to export it because ISO and X/Open–compliant applications will be using it. Because SQLBindParameter contains all the functionality of SQLBindParam, SQLBindParam will be mapped on top of SQLBindParameter (when the underlying driver is an ODBC 3.x driver). An ODBC 3.x driver does not need to implement SQLBindParam.

When the following call to SQLBindParam is made:

SQLBindParam(StatementHandle, ParameterNumber, ValueType, ParameterType, ColumnSize, DecimalDigits, ParameterValuePtr, StrLen_or_IndPtr)

the Driver Manager calls SQLBindParameter in the driver as follows:

SQLBindParameter(StatementHandle, ParameterNumber, SQL_PARAM_INPUT, ValueType, ParameterType, ColumnSize, DecimalDigits, ParameterValuePtr, BufferLength, StrLen_or_IndPtr)


SQLColAttributes Mapping

When an application calls SQLColAttributes through an ODBC 3.x driver, the call to SQLColAttributes is mapped to SQLColAttribute as follows:

Note The prefix used in FieldIdentifier values in ODBC 3.x has been changed from that used in ODBC 2.x. The new prefix is "SQL_DESC"; the old prefix was "SQL_COLUMN".

  1. If the application is an ODBC 2.x application, fDescType is SQL_COLUMN_TYPE, and the returned type is a concise DATETIME type, the Driver Manager maps the return values for date, time, and timestamp codes.
  2. If fDescType is SQL_COLUMN_NAME, SQL_COLUMN_NULLABLE, or SQL_COLUMN_COUNT, the Driver Manager calls SQLColAttribute in the driver with the FieldIdentifier argument mapped to SQL_DESC_NAME, SQL_DESC_NULLABLE, or SQL_DESC_COUNT, as appropriate. All other values of fDescType are passed through to the driver.

An ODBC 3.x driver must support all the ODBC 3.x FieldIdentifiers listed for SQLColAttribute.

An ODBC 3.x driver must support SQL_COLUMN_PRECISION and SQL_DESC_PRECISION, SQL_COLUMN_SCALE and SQL_DESC_SCALE, and SQL_COLUMN_LENGTH and SQL_DESC_LENGTH. These values are different because precision, scale, and length are defined differently in ODBC 3.x than they were in ODBC 2.x. For more information, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" in Appendix D: Data Types.


SQLError Mapping

When an application calls SQLError through an ODBC 3.x driver, the call to

SQLError(henv, hdbc, hstmt, szSqlState, pfNativeError, szErrorMsg, cbErrorMsgMax, pcbErrorMsg)

is mapped to

SQLGetDiagRec(HandleType, Handle, RecNumber, szSqlstate, pfNativeErrorPtr, szErrorMsg, cbErrorMsgMax, pcbErrorMsg)

with the HandleType argument set to the value SQL_HANDLE_ENV, SQL_HANDLE_DBC, or SQL_HANDLE_STMT, as appropriate, and the Handle argument set to the value in henv, hdbc, or hstmt, as appropriate. The RecNumber argument is determined by the Driver Manager.


SQLFreeConnect Mapping

When an application calls SQLFreeConnect through an ODBC 3.x driver, the call to

SQLFreeConnect(hdbc)

is mapped to

SQLFreeHandle(SQL_HANDLE_DBC,Handle)

with the Handle argument set to the value in hdbc.


SQLFreeEnv Mapping

When an application calls SQLFreeEnv through an ODBC 3.x driver, the call to

SQLFreeEnv(henv)

is mapped to

SQLFreeHandle(SQL_HANDLE_ENV,Handle)

with the Handle argument set to the value in henv.


SQLFreeStmt Mapping

When an application calls SQLFreeStmt with an Option argument of SQL_DROP through an ODBC 3.x driver, the call to

SQLFreeStmt(hstmt, SQL_DROP)

is mapped to

SQLFreeHandle(SQL_HANDLE_STMT,Handle)

with the Handle argument set to the value in hstmt.


SQLGetConnectOption Mapping

When an application calls SQLGetConnectOption through an ODBC 3.x driver, the call to

SQLGetConnectOption(hdbc, fOption, pvParam)

is mapped as follows:

  • If fOption indicates an ODBC-defined connection option that returns a string, the Driver Manager calls
    SQLGetConnectAttr(ConnectionHandle, Attribute, ValuePtr, BufferLength, NULL)
  • If fOption indicates an ODBC-defined connection option that returns a 32-bit integer value, the Driver Manager calls
    SQLGetConnectAttr(ConnectionHandle, Attribute, ValuePtr, 0, NULL)
  • If fOption indicates a driver-defined statement option, the Driver Manager calls
    SQLGetConnectAttr(ConnectionHandle, Attribute, ValuePtr, BufferLength, NULL)

In the preceding three cases, the ConnectionHandle argument is set to the value in hdbc, the Attribute argument is set to the value in fOption, and the ValuePtr argument is set to the same value as pvParam.

For ODBC-defined string connection options, the Driver Manager sets the BufferLength argument in the call to SQLGetConnectAttr to the predefined maximum length (SQL_MAX_OPTION_STRING_LENGTH); for a nonstring connection option, BufferLength is set to 0.

For an ODBC 3.x driver, the Driver Manager no longer checks to see if Option is in between SQL_CONN_OPT_MIN and SQL_CONN_OPT_MAX, or is greater than SQL_CONNECT_OPT_DRVR_START. The driver must check the validity of the option values.


SQLGetStmtOption Mapping

When an application calls SQLGetStmtOption to an ODBC 3.x driver that does not support it, the call to

SQLGetStmtOption(hstmt, fOption, pvParam)

will result as follows:

  • If fOption indicates an ODBC-defined statement option that returns a string, the Driver Manager calls
    SQLGetStmtAttr(StatementHandle, Attribute, ValuePtr, BufferLength, NULL)
  • If fOption indicates an ODBC-defined statement option that returns a 32-bit integer value, the Driver Manager calls
    SQLGetStmtAttr(StatementHandle, Attribute, ValuePtr, 0, NULL)
  • If fOption indicates a driver-defined statement option, the Driver Manager calls
    SQLGetStmtAttr(StatementHandle, Attribute, ValuePtr, BufferLength, NULL)

In the preceding three cases, the StatementHandle argument is set to the value in hstmt, the Attribute argument is set to the value in fOption, and the ValuePtr argument is set to the same value as pvParam.

For ODBC-defined string connection options, the Driver Manager sets the BufferLength argument in the call to SQLGetConnectAttr to the predefined maximum length (SQL_MAX_OPTION_STRING_LENGTH); for a nonstring connection option, BufferLength is set to 0.

The SQL_GET_BOOKMARK statement option has been deprecated in ODBC 3.x. For an ODBC 3.x driver to work with ODBC 2.x applications that use SQL_GET_BOOKMARK, it must support SQL_GET_BOOKMARK. For an ODBC 3.x driver to work with ODBC 2.x applications, it must support setting SQL_USE_BOOKMARKS to SQL_UB_ON and should expose fixed-length bookmarks. If an ODBC 3.x driver supports only variable-length bookmarks, not fixed-length bookmarks, it must return SQLSTATE HYC00 (Optional feature not implemented) if an ODBC 2.x application attempts to set SQL_USE_BOOKMARKS to SQL_UB_ON.

For an ODBC 3.x driver, the Driver Manager no longer checks to see whether Option is in between SQL_STMT_OPT_MIN and SQL_STMT_OPT_MAX, or is greater than SQL_CONNECT_OPT_DRVR_START. The driver must check this.


SQLInstallTranslator Mapping

When an ODBC 2.x application calls SQLInstallTranslator through an ODBC 3.x driver, the Driver Manager maps the call to SQLInstallTranslatorEx. An application should not call SQLInstallTranslator in the ODBC 3.x Driver Manager with the lpszInfFile argument set to a value other than NULL. The ODBC.INF file used in ODBC 2.x is no longer supported in ODBC 3.x, even for backward compatibility.


SQLParamOptions Mapping

When an application calls SQLParamOptions through an ODBC 3.x driver, the call

SQLParamOptions(hstmt, crow, piRow);

will be mapped to two calls of SQLSetStmtAttr as follows:

SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, crow, 0);
SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, piRow, 0);


SQLSetConnectOption Mapping

When an ODBC 2.x application calls SQLSetConnectOption through an ODBC 3.x driver, the call to

SQLSetConnectOption(hdbc, fOption, vParam)

will result as follows:

  • If fOption indicates an ODBC-defined connection attribute that requires a string, the Driver Manager calls
    SQLSetConnectAttr(ConnectionHandle, Attribute, ValuePtr, SQL_NTS)
  • If fOption indicates an ODBC-defined connection attribute that returns a 32-bit integer value, the Driver Manager calls
    SQLSetConnectAttr(ConnectionHandle, Attribute, ValuePtr, 0)
  • If fOption indicates a driver-defined connection attribute, the Driver Manager calls
    SQLSetConnectAttr(ConnectionHandle, Attribute, ValuePtr, BufferLength)

In the preceding three cases, the ConnectionHandle argument is set to the value in hdbc, the Attribute argument is set to the value in fOption, and the ValuePtr argument is set to the same value as vParam.

Because the Driver Manager does not know whether the driver-defined connection attribute needs a string or 32-bit integer value, it has to pass in a valid value for the BufferLength argument of SQLSetConnectAttr. If the driver has defined special semantics for driver-defined connect attributes and needs to be called using SQLSetConnectOption, it must support SQLSetConnectOption.

If an ODBC 2.x application calls SQLSetConnectOption to set a driver-specific statement option in an ODBC 3.x driver, and the option was defined in an ODBC 2.x version of the driver, a new manifest constant should be defined for the option in the ODBC 3.x driver. If the old manifest constant is used in the call to SQLSetConnectOption, the Driver Manager will call SQLSetConnectAttr with the StringLength argument set to 0.

For an ODBC 3.x driver, the Driver Manager no longer checks to see if fOption is in between SQL_CONN_OPT_MIN and SQL_CONN_OPT_MAX, or is greater than SQL_CONNECT_OPT_DRVR_START.

Setting Statement Options on the Connection Level

In ODBC 2.x, an application could call SQLSetConnectOption to set a statement option. When that is done, the driver establishes the statement option as a default for any statements later allocated for that connection. It is driver-defined whether the driver sets the statement option for any existing statements associated with the specified connection.

This ability has been deprecated in ODBC 3.x. ODBC 3.x drivers need only support setting ODBC 2.x statement attributes at the connection level if they want to work with ODBC 2.x applications that do this. ODBC 3.x applications should never set statement attributes at the connection level. ODBC 3.x statement attributes cannot be set at the connection level, with the exception of the SQL_ATTR_METADATA_ID and SQL_ATTR_ASYNC_ENABLE attributes, which are both connection attributes and statement attributes, and can be set at either the connection level or the statement level.


SQLSetParam Mapping

SQLSetParam continues to be mapped on top of SQLBindParameter as in ODBC 2.x. Even though it is conceptually similar to SQLBindParam, the Driver Manager does not map SQLSetParam to SQLBindParam. This is because certain existing ODBC 2.x drivers use the special value of BufferLength (SQL_SETPARAM_VALUE_MAX) that the Driver Manager generates when it maps SQLSetParam on top of SQLBindParameter to determine when it is called by a 1.x ODBC application.

A call to

SQLSetParam(hstmt, ipar, fCType, fSqlType, cbColDef, ibScale, rgbValue, pcbValue)

will result in the following:

SQLBindParameter(StatementHandle, ParameterNumber, SQL_PARAM_INPUT_OUTPUT, ValueType, ParameterType, ColumnSize, DecimalDigits, ParameterValuePtr, SQL_SETPARAM_VALUE_MAX, StrLen_or_IndPtr)


SQLSetScrollOptions Mapping

When an application calls SQLSetScrollOptions through an ODBC 3.x driver and the driver does not support SQLSetScrollOptions, the call to

SQLSetScrollOptions(StatementHandle, Concurrency, KeysetSize, RowsetSize)

will result as follows:

  • A call to
    SQLGetInfo(ConnectionHandle, InfoType, InfoValuePtr, BufferLength, StringLengthPtr)

    with the InfoType argument set to one of the values in the following table, depending on the value of the KeysetSize argument in SQLSetScrollOptions.

    KeysetSize argument | InfoType argument
    SQL_SCROLL_FORWARD_ONLY | SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2
    SQL_SCROLL_STATIC | SQL_STATIC_CURSOR_ATTRIBUTES2
    SQL_SCROLL_KEYSET_DRIVEN | SQL_KEYSET_CURSOR_ATTRIBUTES2
    SQL_SCROLL_DYNAMIC | SQL_DYNAMIC_CURSOR_ATTRIBUTES2
    A value greater than the RowsetSize argument | SQL_KEYSET_CURSOR_ATTRIBUTES2

    If the value of the KeysetSize argument is not listed in the preceding table, the call to SQLSetScrollOptions returns SQLSTATE S1107 (Row value out of range) and none of the following steps are performed.

    The Driver Manager then verifies whether the appropriate bit is set in the *InfoValuePtr value returned by the call to SQLGetInfo, according to the value of the Concurrency argument in SQLSetScrollOptions.

    Concurrency argument | InfoType setting
    SQL_CONCUR_READ_ONLY | SQL_CA2_READ_ONLY_CONCURRENCY
    SQL_CONCUR_LOCK | SQL_CA2_LOCK_CONCURRENCY
    SQL_CONCUR_ROWVER | SQL_CA2_ROWVER_CONCURRENCY
    SQL_CONCUR_VALUES | SQL_CA2_VALUES_CONCURRENCY

    If the Concurrency argument is not one of the values in the preceding table, the call to SQLSetScrollOptions returns SQLSTATE S1108 (Concurrency option out of range) and none of the following steps are performed. If the appropriate bit (as indicated in the preceding table) is not set in *InfoValuePtr to one of the values corresponding to the Concurrency argument, the call to SQLSetScrollOptions returns SQLSTATE S1C00 (Driver not capable) and none of the following steps are performed.

  • A call to
    SQLSetStmtAttr(StatementHandle, SQL_ATTR_CURSOR_TYPE, ValuePtr, 0)

    with *ValuePtr set to one of the values in the following table, according to the value of the KeysetSize argument in SQLSetScrollOptions.

    KeysetSize argument | *ValuePtr
    SQL_SCROLL_FORWARD_ONLY | SQL_CURSOR_FORWARD_ONLY
    SQL_SCROLL_STATIC | SQL_CURSOR_STATIC
    SQL_SCROLL_KEYSET_DRIVEN | SQL_CURSOR_KEYSET_DRIVEN
    SQL_SCROLL_DYNAMIC | SQL_CURSOR_DYNAMIC
    A value greater than the RowsetSize argument | SQL_CURSOR_KEYSET_DRIVEN
  • A call to
    SQLSetStmtAttr(StatementHandle, SQL_ATTR_CONCURRENCY, ValuePtr, 0)

    with *ValuePtr set to the Concurrency argument in SQLSetScrollOptions.

  • If the KeysetSize argument in the call to SQLSetScrollOptions is positive, a call to
    SQLSetStmtAttr(StatementHandle, SQL_ATTR_KEYSET_SIZE, ValuePtr, 0)

    with *ValuePtr set to the KeysetSize argument in SQLSetScrollOptions.

  • A call to
    SQLSetStmtAttr(StatementHandle, SQL_ROWSET_SIZE, ValuePtr, 0)

    with *ValuePtr set to the RowsetSize argument in SQLSetScrollOptions.

    Note When the Driver Manager maps SQLSetScrollOptions for an application working with an ODBC 3.x driver that does not support SQLSetScrollOptions, the Driver Manager sets the SQL_ROWSET_SIZE statement option, not the SQL_ATTR_ROW_ARRAY_SIZE statement attribute, to the RowsetSize argument in SQLSetScrollOption. As a result, SQLSetScrollOptions cannot be used by an application when fetching multiple rows by a call to SQLFetch or SQLFetchScroll. It can be used only when fetching multiple rows by a call to SQLExtendedFetch.


SQLSetStmtOption Mapping

When an application calls SQLSetStmtOption through an ODBC 3.x driver, the call to

SQLSetStmtOption(StatementHandle, fOption, vParam)

will result as follows:

  • If fOption indicates an ODBC-defined statement attribute that is a string, the Driver Manager calls
    SQLSetStmtAttr(StatementHandle, fOption, ValuePtr, SQL_NTS)
  • If fOption indicates an ODBC-defined statement attribute that returns a 32-bit integer value, the Driver Manager calls
    SQLSetStmtAttr(StatementHandle, fOption, ValuePtr, 0)
  • If fOption indicates a driver-defined statement attribute, the Driver Manager calls
    SQLSetStmtAttr(StatementHandle, fOption, ValuePtr, BufferLength)

In the preceding three cases, the StatementHandle argument is set to the value in hstmt, the Attribute argument is set to the value in fOption, and the ValuePtr argument is set to the value as vParam.

Because the Driver Manager does not know whether the driver-defined statement attribute needs a string or 32-bit integer value, it has to pass in a valid value for the StringLength argument of SQLSetStmtAttr. If the driver has defined special semantics for driver-defined statement attributes and needs to be called using SQLSetStmtOption, it must support SQLSetStmtOption.

If an application calls SQLSetStmtOption to set a driver-specific statement option in an ODBC 3.x driver and the option was defined in an ODBC 2.x version of the driver, a new manifest constant should be defined for the option in the ODBC 3.x driver. If the old manifest constant is used in the call to SQLSetStmtOption, the Driver Manager will call SQLSetStmtAttr with the StringLength argument set to 0.

When an application calls SQLSetStmtAttr to set SQL_ATTR_USE_BOOKMARKS to SQL_UB_ON in an ODBC 3.x driver, the SQL_ATTR_USE_BOOKMARKS statement attribute is set to SQL_UB_FIXED. SQL_UB_ON is the same constant as SQL_UB_FIXED. The Driver Manager passes SQL_UB_FIXED through to the driver. SQL_UB_FIXED has been deprecated in ODBC 3.x, but an ODBC 3.x driver must implement it to work with ODBC 2.x applications that use fixed-length bookmarks.

For an ODBC 3.x driver, the Driver Manager no longer checks to see if Option is in between SQL_STMT_OPT_MIN and SQL_STMT_OPT_MAX, or is greater than SQL_CONNECT_OPT_DRVR_START.


SQLTransact Mapping

SQLTransact is now replaced by SQLEndTran. The major difference between the two functions is that SQLEndTran contains an argument HandleType, which specifies the scope of the work to be done. The HandleType argument can specify the environment or the connection handle. The following call to SQLTransact:

SQLTransact(henv, hdbc, fType)

is mapped to

SQLEndTran(SQL_HANDLE_DBC, ConnectionHandle, CompletionType);

if ConnectionHandle is not equal to SQL_NULL_HDBC. The ConnectionHandle argument is set to the value of hdbc.

SQL_Transact is mapped to

SQLEndTran (SQL_HANDLE_ENV, EnvironmentHandle, CompletionType);

if ConnectionHandle is equal to SQL_NULL_HDBC. The EnvironmentHandle argument is set to the value of henv.

In both of the preceding cases, the CompletionType argument is set to the same value as fType.


Behavioral Changes and ODBC 3.x Drivers

The environment attribute SQL_ATTR_ODBC_VERSION indicates to the driver whether it needs to exhibit ODBC 2.x behavior or ODBC 3.x behavior. How the SQL_ATTR_ODBC_VERSION environment attribute is set depends on the application. ODBC 3.x applications must call SQLSetEnvAttr to set this attribute after they call SQLAllocHandle to allocate an environment handle and before they call SQLAllocHandle to allocate a connection handle. If they fail to do this, the Driver Manager returns SQLSTATE HY010 (Function sequence error) on the latter call to SQLAllocHandle.

Note For more information on behavioral changes and how an application acts, see "Behavioral Changes" in Chapter 17: Programming Considerations.

ODBC 2.x applications and ODBC 2.x applications recompiled with the ODBC 3.x header files do not call SQLSetEnvAttr. However, they call SQLAllocEnv instead of SQLAllocHandle to allocate an environment handle. Therefore, when the application calls SQLAllocEnv in the Driver Manager, the Driver Manager calls SQLAllocHandle and SQLSetEnvAttr in the driver. Thus, ODBC 3.x drivers can always count on this attribute being set.

If a standards-compliant application compiled with the ODBC_STD compile flag calls SQLAllocEnv (which may occur because SQLAllocEnv is not deprecated in ISO), the call is mapped to SQLAllocHandleStd at compile time. At run time, the application calls SQLAllocHandleStd. The Driver Manager sets the SQL_ATTR_ODBC_VERSION environment attribute to SQL_OV_ODBC3. A call to SQLAllocHandleStd is equivalent to a call to SQLAllocHandle with a HandleType of SQL_HANDLE_ENV and a call to SQLSetEnvAttr to set SQL_ATTR_ODBC_VERSION to SQL_OV_ODBC3.

In certain driver architectures, there is a need for the driver to appear as either an ODBC 2.x driver or an ODBC 3.x driver, depending on the connection. The driver in this case might not actually be a driver but a layer that resides between the Driver Manager and another driver. For example, it might mimic a driver, like ODBC Spy. In another example, it might act as a gateway, like EDA/SQL. To appear as an ODBC 3.x driver, such a driver must be able to export SQLAllocHandle, and to appear as an ODBC 2.x driver, must be able to export SQLAllocConnect, SQLAllocEnv, and SQLAllocStmt. When an environment, connection, or statement is to be allocated, the Driver Manager checks to see if this driver exports SQLAllocHandle. Since the driver does, the Driver Manager calls SQLAllocHandle in the driver. If the driver is working with an ODBC 2.x driver, the driver must map the call to SQLAllocHandle to SQLAllocConnect, SQLAllocEnv, or SQLAllocStmt, as appropriate. It must also do nothing with the SQLSetEnvAttr call when behaving as an ODBC 2.x driver.


Datetime Data Types

In ODBC 3.x, the identifiers for date, time, and timestamp SQL data types have changed from SQL_DATE, SQL_TIME, and SQL_TIMESTAMP (with instances of #define in the header file of 9, 10, and 11) to SQL_TYPE_DATE, SQL_TYPE_TIME, and SQL_TYPE_TIMESTAMP (with instances of #define in the header file of 91, 92, and 93), respectively. The corresponding C type identifiers have changed from SQL_C_DATE, SQL_C_TIME, and SQL_C_TIMESTAMP to SQL_C_TYPE_DATE, SQL_C_TYPE_TIME, and SQL_C_TYPE_TIMESTAMP, respectively, and the instances of #define have changed accordingly.

The column size and decimal digits returned for the SQL datetime data types in ODBC 3.x are the same as the precision and scale returned for them in ODBC 2.x. These values are different than the values in the SQL_DESC_PRECISION and SQL_DESC_SCALE descriptor fields. (For more information, see "Column Size, Decimal Digits, Transfer Octet Length, and Display Size" in Appendix D: Data Types.)

These changes affect SQLDescribeCol, SQLDescribeParam, and SQLColAttributes; SQLBindCol, SQLBindParameter, and SQLGetData; and SQLColumns, SQLGetTypeInfo, SQLProcedureColumns, SQLStatistics, and SQLSpecialColumns.

An ODBC 3.x driver processes the function calls listed in the previous paragraph according to the setting of the SQL_ATTR_ODBC_VERSION environment attribute. For SQLColumns, SQLGetTypeInfo, SQLProcedureColumns, SQLSpecialColumns, and SQLStatistics, if SQL_ATTR_ODBC_VERSION is set to SQL_OV_ODBC3, the functions return SQL_TYPE_DATE, SQL_TYPE_TIME, and SQL_TYPE_TIMESTAMP in the DATA_TYPE field. The COLUMN_SIZE column (in the result set returned by SQLColumns, SQLGetTypeInfo, SQLProcedureColumns, and SQLSpecialColumns) contains the binary precision for the approximate numeric type. The NUM_PREC_RADIX column (in the result set returned by SQLColumns, SQLGetTypeInfo, and SQLProcedureColumns) contains a value of 2. If SQL_ATTR_ODBC_VERSION is set to SQL_OV_ODBC2, then the functions return SQL_DATE, SQL_TIME, and SQL_TIMESTAMP in the DATA_TYPE field, the COLUMN_SIZE column contains the decimal precision for the approximate numeric type, and the NUM_PREC_RADIX column contains a value of 10.

When all data types are requested in a call to SQLGetTypeInfo, the result set returned by the function will contain both SQL_TYPE_DATE, SQL_TYPE_TIME, and SQL_TYPE_TIMESTAMP as defined in ODBC 3.x, and SQL_DATE, SQL_TIME, and SQL_TIMESTAMP as defined in ODBC 2.x.

Because of how the ODBC 3.x Driver Manager performs mapping of the date, time, and timestamp data types, ODBC 3.x drivers need only recognize #defines of 91, 92, and 93 for the date, time, and timestamp C data types entered in the TargetType arguments of SQLBindCol and SQLGetData or the ValueType argument of SQLBindParameter, and need only recognize #defines of 91, 92, and 93 for the date, time, and timestamp SQL data types entered in the ParameterType argument of SQLBindParameter or the DataType argument of SQLGetTypeInfo. For more information, see "Datetime Data Type Changes" in Chapter 17: Programming Considerations.


Backward Compatibility of C Data Types

SQL_C_SHORT, SQL_C_LONG, and SQL_C_TINYINT have been replaced in ODBC by signed and unsigned types: SQL_C_SSHORT and SQL_C_USHORT, SQL_C_SLONG and SQL_C_ULONG, and SQL_C_STINYINT and SQL_C_UTINYINT. An ODBC 3.x driver that should work with ODBC 2.x applications should support SQL_C_SHORT, SQL_C_LONG, and SQL_C_TINYINT, because when they are called, the Driver Manager passes them through to the driver.


Fixed-Length Bookmarks

If an ODBC 3.x driver should work with an ODBC 2.x application that uses fixed-length bookmarks, the driver must support the following:

  • SQL_UB_ON as a value for the SQL_USE_BOOKMARKS statement option. (SQL_UB_ON is deprecated in ODBC 3.x.)
  • The SQL_GET_BOOKMARK statement option.


SQLGetInfo Support

When an ODBC 2.x application calls SQLGetInfo to an ODBC 3.x driver, the InfoType arguments in the following table must be supported.

InfoType Returns
SQL_ALTER_TABLE
(ODBC 2.0)

Note This information type is not deprecated; the bitmasks in the column to the right are deprecated.

 An SQLINTEGER bitmask enumerating the clauses in the ALTER TABLE statement supported by the data source.

The following bitmasks are used to determine which clauses are supported:

SQL_AT_DROP_COLUMN = The ability to drop columns is supported. Whether this results in cascade or restrict behavior is driver-defined. (ODBC 2.0)

SQL_AT_ADD_COLUMN = The ability to add multiple columns in a single ALTER TABLE statement is supported. This bit does not combine with other SQL_AT_ADD_COLUMN_XXX bits or SQL_AT_CONSTRAINT_XXX bits. (ODBC 2.0)

SQL_FETCH_DIRECTION
(ODBC 1.0)

The information type was introduced in ODBC 1.0; each bitmask is labeled with the version in which it was introduced.

 An SQLINTEGER bitmask enumerating the supported fetch direction options.

The following bitmasks are used in conjunction with the flag to determine which options are supported:

SQL_FD_FETCH_NEXT (ODBC 1.0)
SQL_FD_FETCH_FIRST (ODBC 1.0)
SQL_FD_FETCH_LAST (ODBC 1.0)
SQL_FD_FETCH_PRIOR (ODBC 1.0)
SQL_FD_FETCH_ABSOLUTE (ODBC 1.0)
SQL_FD_FETCH_RELATIVE (ODBC 1.0)
SQL_FD_FETCH_BOOKMARK (ODBC 2.0)
SQL_LOCK_TYPES
(ODBC 2.0) 		An SQLINTEGER bitmask enumerating the supported lock types for the fLock argument in SQLSetPos.

The following bitmasks are used in conjunction with the flag to determine which lock types are supported:

SQL_LCK_NO_CHANGE
SQL_LCK_EXCLUSIVE
SQL_LCK_UNLOCK
SQL_ODBC_API_CONFORMANCE
(ODBC 1.0) 		An SQLSMALLINT value indicating the level of ODBC conformance.

SQL_OAC_NONE = None

SQL_OAC_LEVEL1 = Level 1 supported

SQL_OAC_LEVEL2 = Level 2 supported
SQL_ODBC_SQL_CONFORMANCE
(ODBC 1.0) 		An SQLSMALLINT value indicating SQL grammar supported by the driver. See Appendix C: SQL Grammar, for definition of SQL conformance levels.

SQL_OSC_MINIMUM = Minimum grammar supported

SQL_OSC_CORE = Core grammar supported

SQL_OSC_EXTENDED = Extended grammar supported
SQL_POS_OPERATIONS
(ODBC 2.0) 		An SQLINTEGER bitmask enumerating the supported operations in SQLSetPos.

The following bitmasks are used to in conjunction with the flag to determine which options are supported:

SQL_POS_POSITION (ODBC 2.0)
SQL_POS_REFRESH (ODBC 2.0)
SQL_POS_UPDATE (ODBC 2.0)
SQL_POS_DELETE (ODBC 2.0)
SQL_POS_ADD (ODBC 2.0)
SQL_POSITIONED_STATEMENTS
(ODBC 2.0) 		An SQLINTEGER bitmask enumerating the supported positioned SQL statements.

The following bitmasks are used to determine which statements are supported:

SQL_PS_POSITIONED_DELETE
SQL_PS_POSITIONED_UPDATE
SQL_PS_SELECT_FOR_UPDATE
SQL_SCROLL_CONCURRENCY
(ODBC 1.0) 		An SQLINTEGER bitmask enumerating the concurrency control options supported for the cursor.

The following bitmasks are used to determine which options are supported:

SQL_SCCO_READ_ONLY = Cursor is read-only. No updates are allowed.

SQL_SCCO_LOCK = Cursor uses the lowest level of locking sufficient to ensure that the row can be updated.

SQL_SCCO_OPT_ROWVER = Cursor uses optimistic concurrency control, comparing row versions, such as SQLBase ROWID or Sybase TIMESTAMP.

SQL_SCCO_OPT_VALUES = Cursor uses optimistic concurrency control, comparing values.
SQL_STATIC_SENSITIVITY
(ODBC 2.0) 		An SQLINTEGER bitmask enumerating whether changes made by an application to a static or keyset-driven cursor through SQLSetPos or positioned update or delete statements can be detected by that application.

SQL_SS_ADDITIONS = Added rows are visible to the cursor; the cursor can scroll to these rows. Where these rows are added to the cursor is driver-dependent.

SQL_SS_DELETIONS = Deleted rows are no longer available to the cursor and do not leave a "hole" in the result set; after the cursor scrolls from a deleted row, it cannot return to that row.

SQL_SS_UPDATES = Updates to rows are visible to the cursor; if the cursor scrolls from and returns to an updated row, the data returned by the cursor is the updated data, not the original data. This option applies only to static cursors or updates on keyset-driven cursors that do not update the key. This option does not apply for a dynamic cursor or in the case in which a key is changed in a mixed cursor.

Whether an application can detect changes made to the result set by other users, including other cursors in the same application, depends on the cursor type.

An ODBC 3.x application working with an ODBC 3.x driver should not call SQLGetInfo with the InfoType arguments described in the preceding table but should use the ODBC 3.x InfoType arguments listed in the following paragraph. There is not a one-to-one correspondence between InfoType arguments used in ODBC 2.x and those used in ODBC 3.x. An ODBC 3.x application working with an ODBC 2.x driver, on the other hand, should use the InfoType arguments described previously.

Some of the information types in the previous table are deprecated in favor of the cursor attributes information types. These deprecated information types are SQL_FETCH_DIRECTION, SQL_LOCK_TYPES, SQL_POS_OPERATIONS, SQL_POSITIONED_STATEMENTS, SQL_SCROLL_CONCURRENCY, and SQL_STATIC_SENSITIVITY. The new cursor attributes types are SQL_XXX_CURSOR_ATTRIBUTES1and SQL_XXX_CURSOR_ATTRIBUTES2, where XXX equals DYNAMIC, FORWARD_ONLY, KEYSET_DRIVEN, or STATIC. Each of the new types indicates the driver capabilities for a single cursor type. For more information about these options, see the SQLGetInfo function description.


Returning SQL_NO_DATA

When an ODBC 2.x application working with an ODBC 3.x driver calls SQLExecDirect, SQLExecute, or SQLParamData, and a searched update or delete statement was executed but did not affect any rows at the data source, the ODBC 3.x driver should return SQL_SUCCESS. When an ODBC 3.x application working with an ODBC 3.x driver calls SQLExecDirect, SQLExecute, or SQLParamData with the same result, the ODBC 3.x driver should return SQL_NO_DATA.

If a searched update or delete statement in a batch of statements does not affect any rows at the data source, SQLMoreResults returns SQL_SUCCESS. It cannot return SQL_NO_DATA, because that would mean that there are no more results, not that there is a result from a searched update/delete that affected no rows.


Calling SQLSetPos to Insert Data

When an ODBC 2.x application working with an ODBC 3.x driver calls SQLSetPos with an Operation argument of SQL_ADD, the Driver Manager does not map this call to SQLBulkOperations. If an ODBC 3.x driver should work with an application that calls SQLSetPos with SQL_ADD, the driver should support that operation.

One major difference in behavior when SQLSetPos is called with SQL_ADD occurs when it is called in state S6. In ODBC 2.x, the driver returned S1010 when SQLSetPos was called with SQL_ADD in state S6 (after the cursor has been positioned with SQLFetch). In ODBC 3.x, SQLBulkOperations with an Operation of SQL_ADD can be called in state S6. A second major difference in behavior is that SQLBulkOperations with an Operation of SQL_ADD can be called in state S5, while SQLSetPos with an Operation of SQL_ADD cannot. For the statement transitions that can occur for the same call in ODBC 3.x, see Appendix B: ODBC State Transition Tables.


Loading by Ordinal

In ODBC 2.x, loading by ordinal could be performed to improve the performance of the connection process. An ODBC 2.x driver exports a dummy function with the ordinal 199; when the Driver Manager detects it, it resolves the addresses of the ODBC functions by ordinal, not by name. This functionality is still supported for ODBC 2.x drivers but is not supported for ODBC 3.x drivers.


ODBC Glossary

A

access plan
A plan generated by the database engine to execute an SQL statement. Equivalent to executable code compiled from a third-generation language such as C.
aggregate function
A function that generates a single value from a group of values, often used with GROUP BY and HAVING clauses. Aggregate functions include AVG, COUNT, MAX, MIN, and SUM. Also known as set functions. See also scalar function.
ANSI
American National Standards Institute. The ODBC API is based on the ANSI Call-Level Interface.
APD
See application parameter descriptor (APD).
API
Application Programming Interface. A set of routines that an application uses to request and carry out lower-level services. The ODBC API is composed of the ODBC functions.
application
An executable program that calls functions in the ODBC API.
application parameter descriptor (APD)
A descriptor that describes the dynamic parameters used in an SQL statement before any conversion specified by the application.
application row descriptor (ARD)
A descriptor that represents the column metadata and data in the application's buffers, describing a row of data following any data conversion specified by the application.
ARD
See application row descriptor (ARD).
auto-commit mode
A transaction commit mode in which transactions are committed immediately after they are executed.

B

behavioral change
A change in certain functionality from ODBC 3.x behavior to ODBC 2.x behavior, or vice versa. Caused by changing the SQL_ATTR_ODBC_VERSION environment attribute.
Binary large object (BLOB)
Any binary 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 result set or a parameter in an SQL statement with an application variable. As a noun, the association.
binding offset
A value added to the data buffer addresses and length/indicator buffer addresses for all bound column or parameter data, producing new addresses.
block cursor
A cursor capable of fetching more than one row of data at a time.
buffer
A piece of application memory used to pass data between the application and driver. Buffers often come in pairs: a data buffer and a data length buffer.
byte
Eight bits or one octet. See also octet.

C

C data type
The data type of a variable in a C program, in this case the application.
catalog
The set of system tables in a database that describe the shape of the database. Also known as a schema or data dictionary.
catalog function
An ODBC function used to retrieve information from the database's catalog.
CLI
See API.
client/server
A database access strategy in which one or more clients access data through a server. The clients usually 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.
commit
To make the changes in a transaction permanent.
concurrency
The ability of more than one transaction to access the same data at the same time.
conformance level
A discrete set of functionality supported by a driver or data source. ODBC defines API conformance levels and SQL conformance levels.
connection
A particular instance of a driver and data source.
connection browsing
Searching the network for data sources to connect to. Connection browsing might involve several steps. For example, the user might first browse the network for servers and then browse a particular server for a database.
connection handle
A handle to a data structure that contains information about a connection.
current row
The row currently pointed to by the cursor. Positioned operations act on the current row.
cursor
A piece of software that returns rows of data to the application. Probably named after the blinking cursor on a computer terminal; just as that cursor indicates the current position on the screen, a cursor on a result set indicates the current position in the result set.

D

data buffer
A buffer used to pass data. Often associated with a data buffer is a data length buffer.
data dictionary
See catalog.
data length buffer
A buffer used to pass the length of the value in a corresponding data buffer. The data length buffer is also used to store indicators, such as whether the data value is null-terminated.
data source
The data that the user wants to access and its associated operating system, DBMS, and network platform (if any).
data type
The type of a piece of data. ODBC defines C and SQL data types. See also type indicator.
data-at-execution column
A column for which data is sent after SQLSetPos is called. So named because the data is sent at execution time rather than being placed in a rowset buffer. Long data is generally sent in parts at execution time.
data-at-execution parameter
A parameter for which data is sent after SQLExecute or SQLExecDirect is called. So named because the data is sent when the SQL statement is executed rather than being placed in a parameter buffer. Long data is generally sent in parts at execution time.
database
A discrete collection of data in a DBMS. Also a DBMS.
database engine
The software in a DBMS that parses and executes SQL statements and accesses the physical data.
DBMS
Database Management System. A layer of software between the physical database and the user. The DBMS manages all access to the database.
DBMS-based driver
A driver that accesses physical data through a stand-alone database engine.
DDL
Data Definition Language. Those statements in SQL that define, as opposed to manipulate, data. For example, CREATE TABLE, CREATE INDEX, GRANT, and REVOKE.
delimited identifier
An identifier that is enclosed in identifier quote characters so that it can contain special characters or match keywords (also known as a quoted identifier).
descriptor
A data structure that holds information about either column data or dynamic parameters. The physical representation of the descriptor is not defined; applications gain direct access to a descriptor only by manipulating its fields by calling ODBC functions with the descriptor handle.
desktop database
A DBMS designed to run on a personal computer. Generally, these DBMSs do not provide a stand-alone database engine and must be accessed through a file-based driver. The engines in these drivers generally have reduced support for SQL and transactions. For example, dBASE, Paradox, Btrieve, or Microsoft® FoxPro®.
diagnostic
A record containing diagnostic information about the last function called that used a particular handle. Diagnostic records are associated with environment, connection, statement, and descriptor handles.
DML
Data Manipulation Language. Those statements in SQL that manipulate, as opposed to define, data. For example, INSERT, UPDATE, DELETE, and SELECT.
driver
A routine library that exposes the functions in the ODBC API. Drivers are specific to a single DBMS.
Driver Manager
A routine library that manages access to drivers for the application. The Driver Manager loads and unloads (or connects to and disconnects from) drivers and passes calls to ODBC functions to the correct driver.
driver setup DLL
A DLL that contains driver-specific installation and configuration functions.
dynamic cursor
A scrollable cursor capable of detecting rows updated, deleted, or inserted in the result set.
dynamic SQL
A type of embedded SQL in which SQL statements are created and compiled at run time. See also static SQL.

E

embedded SQL
SQL statements that are included directly in a program written in another language, such as COBOL or C. ODBC does not use embedded SQL. See also static SQL and dynamic SQL.
environment
A global context in which to access data; associated with the environment is any information that is global in nature, such as a list of all connections in that environment.
environment handle
A handle to a data structure that contains information about the environment.
escape clause
A clause in an SQL statement.
execute
To run an SQL statement.

F

fat cursor
See block cursor.
fetch
To retrieve one or more rows from a result set.
field
See column.
file-based driver
A driver that accesses physical data directly. In this case, the driver contains a database engine and acts as both driver and data source.
file data source
A data source for which connection information is stored in a .dsn file.
foreign key
A column or columns in a table that match the primary key in another table.
forward-only cursor
A cursor that can only move forward through the result set and generally fetches only one row at a time. Most relational databases support only forward-only cursors.

H

handle
A value that uniquely identifies something such as a file or data structure. Handles are meaningful only to the software that creates and uses them but are passed by other software to identify things. ODBC defines handles for environments, connections, statements, and descriptors.

I

implementation parameter descriptor (IPD)
A descriptor that describes the dynamic parameters used in an SQL statement after any conversion specified by the application.
implementation row descriptor (IRD)
A descriptor that describes a row of data before any conversion specified by the application.
installer DLL
A DLL that installs ODBC components and configures data sources.
Integrity Enhancement Facility
A subset of SQL designed to maintain the integrity of a database.
interface conformance level
The level of the ODBC 3.7 interface supported by a driver; can be Core, Level 1, or Level 2.
interoperability
The ability of one application to use the same code when accessing data in different DBMSs.
IPD
See Implementation Parameter Descriptor (IPD).
IRD
See Implementation Row Descriptor (IRD).
ISO/IEC
International Standards Organization/International Electrotechnical Commission. The ODBC API is based on the ISO/IEC Call-Level Interface.

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 foreign key and primary key.
keyset
A set of keys used by a mixed or keyset-driven cursor to refetch rows.
keyset-driven cursor
A scrollable cursor that detects updated and deleted rows by using a keyset.

L

literal
A character representation of an actual data value in an SQL statement.
locking
The process by which a DBMS restricts access to a row in a multiuser environment. The DBMS usually sets a bit on a row or the physical page containing a row that indicates the row or page is locked.
long data
Any binary or character data over a certain length, such as 255 bytes or characters. Typically much longer. Such data is generally sent to and retrieved from the data source in parts. Also known as BLOBs or CLOBs.

M

machine data source
A data source for which connection information is stored on the system (for example, the registry).
manual-commit mode
A transaction commit mode in which transactions must be explicitly committed by calling SQLTransact.
metadata
Data that describes a parameter in an SQL statement or a column in a result set. For example, the data type, byte length, and precision of a parameter.
multiple-tier driver
See DBMS-based driver.

N

NULL value
Having no explicitly assigned value. In particular, a NULL value is different from a zero or a blank.

O

octet
Eight bits or one byte. See also byte.
octet length
The length in octets of a buffer or the data it contains.
ODBC
Open Database Connectivity. A specification for an API that defines a standard set of routines with which an application can access data in a data source.
ODBC Administrator
An executable program that calls the installer DLL to configure data sources.
optimistic concurrency
A strategy to increase concurrency in which rows are not locked. Instead, before they are updated or deleted, a cursor checks to see if they have been changed since they were last read. If so, the update or delete fails. See also pessimistic concurrency.
outer join
A join in which both matching and nonmatching rows are returned. The values of all columns from the unmatched table in nonmatching rows are set to NULL.
owner
The owner of a table.

P

parameter
A variable in an SQL statement, marked with a parameter marker or question mark (?). Parameters are bound to application variables and their values retrieved when the statement is executed.
parameter descriptor
A descriptor that describes the run-time parameters used in an SQL statement, either before any conversion specified by the application (an application parameter descriptor, or APD) or after any conversion specified by the application (an implementation parameter descriptor, or IPD).
parameter operation array
An array containing values that an application can set to indicate that the corresponding parameter should be ignored in an SQLExecDirect or SQLExecute operation.
parameter status array
An array containing the status of a parameter after a call to SQLExecDirect or SQLExecute.
pessimistic concurrency
A strategy for implementing serializability, in which rows are locked so that other transactions cannot change them. See also optimistic concurrency and serializability.
positioned operation
Any operation that acts on the current row. For example, positioned update and delete statements, SQLGetData, and SQLSetPos.
positioned update statement
An SQL statement used to update the values in the current row.
positioned delete statement
An SQL statement used to delete the current row.
prepare
To compile an SQL statement. An access plan is created by preparing an SQL statement.
primary key
A column or columns that uniquely identifies a row in a table.
procedure
A group of one or more precompiled SQL statements that are stored as a named object in a database.
procedure column
An argument in a procedure call, the value returned by a procedure, or a column in a result set created by a procedure.

Q

qualifier
A database that contains one or more tables.
query
An SQL statement. Sometimes used to mean a SELECT statement.
quoted identifier
An identifier that is enclosed in identifier quote characters so that it can contain special characters or match keywords (also known in SQL-92 as a delimited identifier).

R

radix
The base of a number system. Usually 2 or 10.
record
See row.
result set
The set of rows created by executing a SELECT statement.
return code
The value returned by an ODBC function.
roll back
To return the values changed by a transaction to their original state.
row
A set of related columns that describe a specific entity. Also known as a record.
row descriptor
A descriptor that describes the columns of a result set, either before any conversion specified by the application (an implementation row descriptor, or IRD) or after any conversion specified by the application (an application row descriptor, or ARD).
row operation array
An array containing values that an application can set to indicate that the corresponding row should be ignored in a SQLSetPos operation.
row status array
An array containing the status of a row after a call to SQLFetch, SQLFetchScroll, or SQLSetPos.
rowset
The set of rows returned in a single fetch by a block cursor.
rowset buffers
The buffers bound to the columns of a result set and in which the data for an entire rowset is returned.

S

SAG
See SQL Access Group (SAG).
scalar function
A function that generates a single value from a single value. For example, a function that changes the case of character data.
schema
See catalog.
scrollable cursor
A cursor that can move forward or backward through the result set.
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.
server database
A DBMS designed to be run in a client/server environment. These DBMSs provide a stand-alone database engine that provides rich support for SQL and transactions. They are accessed through DBMS-based drivers. For example, Oracle, Informix, DB/2, or Microsoft® SQL Server.
set function
See aggregate function.
setup DLL
See driver setup DLL and translator setup DLL.
single-tier driver
See file-based driver.
SQL
Structured Query Language. A language used by relational databases to query, update, and manage data.
SQL Access Group (SAG)
An industry consortium of companies concerned with SQL DBMSs. The X/Open Call-Level Interface is based on work originally done by the SQL Access Group.
SQL conformance level
The level of SQL-92 grammar supported by a driver; can be Entry, FIPS Transitional, Intermediate, or Full.
SQL data type
The data type of a column or parameter as it is stored in the data source.
SQLSTATE
A five-character value that indicates a particular error.
SQL statement
A complete phrase in SQL that begins with a keyword and completely describes an action to be taken. For example, SELECT * FROM Orders. SQL statements should not be confused with statements.
state
A well-defined condition of an item. For example, a connection has seven states, including unallocated, allocated, connected, and needing data. Certain operations can be done only when an item is in a particular state. For example, a connection can be freed only when it is in an allocated state and not, for example, when it is in a connected state.
state transition
The movement of an item from one state to another. ODBC defines rigorous state transitions for environments, connections, and statements.
statement
A container for all the information related to an SQL statement. Statements should not be confused with SQL statements.
statement handle
A handle to a data structure that contains information about a statement.
static cursor
A scrollable cursor that cannot detect updates, deletes, or inserts in the result set. Usually implemented by making a copy of the result set.
static SQL
A type of embedded SQL in which SQL statements are hard-coded and compiled when the rest of the program is compiled. See also dynamic SQL.
stored procedure
See procedure.

T

table
A collection of rows.
thunking
The conversion of 16-bit addresses to 32-bit addresses, or vice versa, when 16-bit applications are used with 32-bit ODBC drivers.
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. There are five transaction isolation levels: Read Uncommitted, Read Committed, Repeatable Read, Serializable, and Versioning.
translator DLL
A DLL used to translate data from one character set to another.
translator setup DLL
A DLL that contains translator-specific installation and configuration functions.
two-phase commit
The process of committing a distributed transaction in two phases. In the first phase, the transaction processor checks that all parts of the transaction can be committed. In the second phase, all parts of the transaction are committed. If any part of the transaction indicates in the first phase that it cannot be committed, the second phase does not occur. ODBC does not support two-phase commits.
type indicator
An integer value passed to or returned from an ODBC function to indicate the data type of an application variable, a parameter, or a column. ODBC defines type indicators for both C and SQL data types.

V

view
An alternative way of looking at the data in one or more tables. A view is usually created as a subset of the columns from one or more tables. In ODBC, views are generally equivalent to tables.

X

X/Open
A company that publishes standards. In particular, it publishes SQL Access Group (SAG) standards.


ODBC Data Source Administrator

Microsoft ODBC Data Source Administrator

The Microsoft® ODBC Data Source Administrator manages database drivers and data sources. On computers running the Microsoft Windows® 2000 operating system, this application is located in the Windows Control Panel under Administrative Tools. Its icon is named Data Sources (ODBC).

Note On computers running previous versions of Microsoft Windows, the ODBC Administrator icon is named 32-bit ODBC or simply ODBC.

For information about detailed ODBC Administrator procedures, open the ODBC Data Source Administrator dialog box and click Help.


About Drivers and Data Sources

Drivers are the components that process ODBC requests and return data to the application. If necessary, drivers modify an application's request into a form that is understood by the data source. You must use the driver's setup program to add or delete a driver from your computer.

Data sources are the databases or files accessed by a driver and are identified by a data source name (DSN). Use the ODBC Data Source Administrator to add, configure, and delete data sources from your system. The types of data sources that can be used are described in the following table.

Data source | Description
User | User DSNs are local to a computer and can be used only by the current user. They are registered in the HKEY_CURRENT_USER registry subtree.
System | System DSNs are local to a computer rather than dedicated to a user. The system or any user with privileges can use a data source set up with a system DSN. System DSNs are registered in the HKEY_LOCAL_MACHINE registry subtree.
File | File DSNs are file-based sources that can be shared among all users who have the same drivers installed and therefore have access to the database. These data sources need not be dedicated to a user nor be local to a computer. File data source names are not identified by dedicated registry entries; instead, they are identified by a file name with a .dsn extension.

User and system data sources are collectively known as machine data sources because they are local to a computer.

Each of these data sources has a tab in the ODBC Data Source Administrator dialog box. For more information about data sources, see Data Sources in Chapter 3 of the ODBC Programmer's Reference, "ODBC Architecture."


Running the ODBC Administrator

To open the ODBC Administrator from the Control Panel

  1. Click Start, point to Settings, and then click Control Panel.
  2. On computers running Microsoft Windows 2000, double-click Administrative Tools, and then double-click Data Sources (ODBC). The ODBC Data Source Administrator dialog box appears.

    On computers running previous versions of Microsoft Windows, double-click 32-bit ODBC or ODBC.

Click Help for detailed information about each tab of the ODBC Data Source Administrator dialog box.


Managing Data Sources

After you have installed an ODBC driver from the driver's setup program, you can define one or more data sources for it. The data source name (DSN) should provide a unique description of the data; for example, Payroll or Accounts Payable. The user and system data sources that are defined for all currently installed drivers are listed in the User DSN or System DSN tabs of the ODBC Data Source Administrator dialog box. The file data sources in a given directory are listed in the File DSN tab; the directory to be shown is entered in the Look in box in the File DSN tab.

A data source associates a particular ODBC driver with the data you want to access through that driver. For example, you might create a data source to use the ODBC dBASE driver to access one or more dBASE files found in a specific directory on your hard disk or a network drive. Using the ODBC Data Source Administrator, you can add, modify, and delete data sources, as described in the following table.

Action | Description
Adding data sources | It is possible to add multiple data sources, each one associating a driver with some data you want to access by using that driver. Give each data source a name that uniquely identifies that data source. For example, if you create a data source for a set of dBASE files that contain customer information, you might name the data source "Customers." Applications typically display data source names for users to choose from. Adding a file data source is slightly different from adding user or system data sources. For more information, see the ODBC Data Source Administrator help file.
Modifying data sources | Depending on your requirements, you might find it necessary to reconfigure data sources. You can reset options by clicking Configure in any driver setup dialog box.
Deleting data sources | Click Remove after selecting a data source.

Note Data sources for 32-bit drivers can be used by both 16-bit and 32-bit applications. The Driver Manager provides thunking layers to allow 16-bit applications to use 32-bit drivers.

For more information about file data sources, see Connecting Using File Data Sources in Chapter 6: Connecting to a Data Source or Driver, or the SQLDriverConnect function reference in the ODBC Programmer's Reference.


Setting Tracing Options

The Tracing tab of the ODBC Data Source Administrator dialog box enables you to configure the way ODBC function calls are traced.

How Tracing Works

When you activate tracing from the Tracing tab, the Driver Manager will log all ODBC function calls for all subsequently run applications. ODBC function calls from applications running before tracing is activated are not logged. ODBC function calls are recorded in a log file you specify.

Tracing ceases only after you click Stop Tracing Now. Remember that while tracing is on, the log file continues to increase in size and that this affects the performance of all your ODBC applications.

For more information about tracing, see Tracing in Chapter 17 of the ODBC Programmer's Reference, "Programming Considerations."

Visual Studio Analyzer Tracing

Visual Studio® Analyzer Tracing provides performance and debugging information about the ODBC layer. All outbound events will be fired at the top-level interface to present as accurate a picture as possible regarding time spent in ODBC components. Visual Studio Analyzer Tracing requires any event source to register when the source is set up. For more information about this type of tracing, see the Visual Studio documentation.


Setting ODBC Connection Pooling Options

Connection pooling enables an application to use a connection from a pool of connections that do not need to be reestablished for each use. You can use the Connection Pooling tab of the ODBC Data Source Administrator dialog box to alter the connection Retry Wait time for any selected driver. You can also use it to enable and disable performance monitoring. Double-click a driver name to set the connection time-out period.

At the driver level, connection pooling is enabled by the CPTimeout registry value. This selective per-driver enabling allows a system administrator to enable connection pooling for just the drivers that can support it. It is accomplished by setting the default value of CPTimeout during the driver's setup program.

When the ODBC Driver Manager detects that a database server is unavailable, it waits for a certain amount of time before attempting to reconnect. You can adjust the waiting time by setting the Retry Wait time value (in seconds) in the Connection Pooling tab of the ODBC Data Source Administrator dialog box. Double-click a driver name to set the connection time-out period.

For more information about connection pooling, see ODBC Connection Pooling in Chapter 6 of the ODBC Programmer's Reference, "Connecting to a Data Source or Driver."

Performance Monitoring

Performance monitoring tracks connection performance by recording a variety of statistics. These statistics can be customized by the developer to include items such as the following:

  • The number of connections made to data sources per second.
  • The number of disconnections made from data sources per second.
  • The number of connections to the pool per second.
  • The number of disconnections from the pool per second.
  • Connections currently active.
  • The current number of connections being used by applications.
  • Connections currently free.
  • The current number of connections available for connection requests.

You must specify your own monitoring parameters. Samples for performance monitoring have been included with this version of ODBC.


Viewing Drivers

To configure data sources, you must have installed at least one driver on your system. The Drivers tab in the ODBC Data Source Administrator dialog box lists all drivers installed on your computer, including the name, version, company, file name, and file creation date of each driver. You must use the driver's setup program to add or delete a driver from your system. For more information about modifying drivers, see Managing Data Sources.


ODBC Drivers

Microsoft ODBC Drivers

Drivers are libraries that implement the functions in the ODBC API. Each driver is specific to a particular database management system (DBMS). In most cases, drivers expose the capabilities of the underlying DBMS; they are not required to implement capabilities not supported by the DBMS. Drivers perform specific tasks and can be classified into two categories: file-based and DBMS-based. For more information about drivers and driver architecture, see Drivers in Chapter 3 of the ODBC Programmer's Reference, "ODBC Architecture."

For information about support for Microsoft Data Access Components, see "Getting Help from Microsoft Product Support Services."


ODBC Driver Architecture

Driver writers must be aware that the driver architecture can affect whether an application can use DBMS-specific SQL.

File-based Drivers

When the driver accesses the physical data directly, the driver acts as both driver and data source. The driver must process both ODBC calls and SQL statements. Developers of file-based drivers must write their own database engines.

DBMS-Based Drivers

When a separate database engine is used to access physical data, the driver processes only ODBC calls. It passes SQL statements to the database engine for processing.

Network Architecture

File and DBMS ODBC configurations can exist on a single network.

Other Driver Architectures

When a driver is required to work with a variety of data sources, it can be used as middleware. Heterogeneous join engine architecture can make the driver appear as a driver manager. Drivers can also be installed on servers, where they can be shared by a series of clients.

Redistributable Files

It is preferable to install the redistributable files for ODBC components using the self-contained setup included with the Microsoft® Data Access Components (MDAC) SDK. Updates to this setup are available on the Microsoft Universal Data Access Web site. After downloading any updates to the executable file, extract the files from Mdac_typ.exe in the \Redist directory.

To run the command, click Start, click Run, and then type mdac_typ.exe /C /T:<path_to_dir>, where <path_to_dir> is the target directory for the extracted files.

For more information about driver architecture, see Driver Architecture in Chapter 3 of the ODBC Programmer's Reference, "ODBC Architecture."

More information about driver issues can be found in the locations described in the following table.

Issue | Topic | Location
Compatibility issues with applications and drivers | Application/Driver Compatibility | Chapter 17: Programming Considerations, in the ODBC Programmer's Reference
Writing ODBC drivers | Writing ODBC 3.x Drivers | Chapter 17: Programming Considerations, in the ODBC Programmer's Reference
Driver guidelines for backward compatibility | Driver Guidelines for Backward Compatibility | Appendix G: Driver Guidelines for Backward Compatibility, in the ODBC Programmer's Reference
Connecting to a driver | Choosing a Data Source or Driver | Chapter 6; Connecting to a Data Source or Driver, in the ODBC Programmer's Reference
Identifying drivers | Viewing Drivers | "Viewing Drivers," accessed from the Microsoft ODBC Data Source Administrator online help file
Enabling connection pooling | ODBC Connection Pooling | Chapter 6; Connecting to a Data Source or Driver, in the ODBC Programmer's Reference
Unicode/ANSI driver and connection issues | Unicode Drivers | Chapter 17: Programming Considerations, in the ODBC Programmer's Reference


Driver Manager

The Driver Manager is a library that manages communication between applications and drivers. It exists mainly as a convenience to application writers and solves a number of problems common to all applications, including determining which driver to load based on a data source name, loading and unloading drivers, and calling functions in drivers. For more information about the Driver Manager, see The Driver Manager in Chapter 3 of the ODBC Programmer's Reference, "ODBC Architecture."


Using 16-Bit and 32-Bit Applications with 32-Bit Drivers

With the ODBC component of the MDAC SDK, you can use 16-bit and 32-bit applications with 32-bit drivers. The Microsoft® Windows® 95/98 and Microsoft Windows NT®/Windows 2000 operating systems support the following combinations of applications and drivers:

  • 16-bit applications with 32-bit drivers
  • 32-bit applications with 32-bit drivers

Using a 32-bit application with a 16-bit driver is not supported.

Note Beginning with the release of ODBC version 3.0, Windows NT 4.0 for Intel®, Alpha, and MIPS platforms were supported. (However, neither MDAC 2.5 nor Windows 2000 is supported on Alpha platforms.)

ODBC in the MDAC SDK includes the ODBC components necessary to support the above configurations by "thunking" dynamic-link libraries (DLLs) to convert 16-bit addresses to 32-bit addresses and vice versa. The Setup program determines which operating system you are using and installs ODBC components required by that system. You can also choose to install the ODBC components used by all systems.

In most cases, porting an application or driver from 16-bit to 32-bit involves five types of changes:

  • Changes to message-handling code
  • Changes because integers and handles are 32 bits
  • Changes in calls to Windows application programming interfaces (APIs)
  • Changes to make the driver thread-safe
  • Changes to ODBC components

For information about the first four items, see the Microsoft Platform SDK documentation.

From an application or driver programming standpoint, the major difference between 16-bit and 32-bit ODBC components is that they have different file names. From a system standpoint, the architecture of each application or driver connection is different and the tools used to manage data sources are different.


Using 16-Bit Applications with 32-Bit Drivers

You can run 16-bit applications with 32-bit drivers on your Windows-based system as long as the 32-bit driver does not explicitly call Win32® API functions that create threads. The Windows on Windows (WOW) subsystem runs the applications in 16-bit mode and resolves 16-bit calls to the operating system. ODBC thunking DLLs resolve 16-bit calls from the application to 32-bit drivers. The 16-bit applications use the Windows API, and 32-bit drivers use the Win32 API.

Architecture

The following illustration shows how 16-bit applications communicate with 32-bit drivers. Between the 16-bit Driver Manager and the 32-bit drivers are generic thunking DLLs that convert 16-bit ODBC calls to 32-bit ODBC calls.

Note Anytime a 16-bit application interacts with a 32-bit driver, the 32-bit Driver Manager always returns "2.0" as the version of ODBC supported by the driver.

Administration

You can manage data sources for 32-bit drivers by using the ODBC Data Source Administrator. To open the ODBC Administrator on computers running Microsoft® Windows® 2000, open the Windows Control Panel, double-click Administrative Tools, and then double-click Data Sources (ODBC). On computers running previous versions of Microsoft Windows, the icon is named 32-bit ODBC or simply ODBC.

The following illustration shows how a 16-bit application calls a 32-bit driver setup DLL. Between the 16-bit installer DLL and the 32-bit driver setup DLL is a generic thunking DLL that converts 16-bit installer DLL calls to 32-bit installer DLL calls.

In Windows on Windows (16-bit to 32-bit thunking), an additional thunking DLL named Ds32gt.dll converts 16-bit argument values passed through a 32-bit setup DLL back to 16-bit.

Components

The ODBC component of the MDAC SDK includes the following files for running 16-bit applications with 32-bit drivers. These components are in the \Redist directory.

File name | Description
Odbc16gt.dll | 16-bit ODBC generic thunking DLL
Odbc32gt.dll | 32-bit ODBC generic thunking DLL
Odbccp32.dll | 32-bit installer DLL
Odbcad32.exe | 32-bit Administrator program
Odbcinst.hlp | Installer Help file
Ds16gt.dll | 16-bit driver setup generic thunking DLL
Ctl3d32.dll | 32-bit three-dimensional window style library

In addition, the following files along with the 16-bit ODBC 2.10 Driver Manager, which are not part of ODBC 3.51, are required by and should be installed with the 16-bit application.

File name | Description
Odbc.dll | 16-bit Driver Manager
Odbcinst.dll | 16-bit Installer DLL
Odbcadm.exe | 16-bit ODBC Administrator program

Note For a complete list of redistributable files, see Redistributable Files in Chapter 18 of the ODBC Programmer's Reference, "Installing ODBC Components."


Using 32-Bit Applications with 32-Bit Drivers

You can run 32-bit applications with 32-bit drivers on Microsoft® Windows® 95/98 and Microsoft Windows NT®/Windows 2000. The 32-bit applications and the 32-bit drivers use the Win32® API.

Architecture

The following illustration shows how 32-bit applications communicate with 32-bit drivers. The application calls the 32-bit Driver Manager, which in turn calls 32-bit drivers.

Important Do not use the 32-bit thunking installer DLL on Windows NT/Windows 2000. Although it has the same file name as the 32-bit installer DLL, it is a different DLL.

Administration

You can manage data sources for 32-bit drivers by using the ODBC Data Source Administrator. To open the ODBC Administrator on computers running Windows 2000, open the Windows Control Panel, double-click Administrative Tools, and then double-click Data Sources (ODBC). On computers running previous versions of Microsoft Windows, the icon is named 32-bit ODBC or simply ODBC.

Components

The ODBC component of the MDAC SDK includes the following files for running 32-bit applications with 32-bit drivers. These components are in the \Redist directory.

File name | Description
Odbc32.dll | 32-bit Driver Manager
Odbccp32.dll | 32-bit Installer DLL
Odbcad32.exe | 32-bit ODBC Administrator program
Odbcinst.hlp | Installer Help file
Msvcrt40.dll | C run-time library

Note For a complete list of redistributable files, see Redistributable Files in Chapter 18 of the ODBC Programmer's Reference, "Installing ODBC Components."


Microsoft ODBC Desktop Database Drivers

ODBC is an API that uses Structured Query Language (SQL) as the database access language. You can access a wide variety of database management systems (DBMSs) with the same ODBC source code that is directly incorporated into an application's source code. With the Microsoft ODBC Desktop Database Drivers, a user of an ODBC-enabled application can open, query, and update a desktop database through the ODBC interface.

The Microsoft ODBC Desktop Database Drivers are a Microsoft Jet-based set of ODBC drivers. Whereas Microsoft ODBC Desktop Database Drivers 2.0 include both 16-bit and 32-bit drivers, versions 3.0 and later include only 32-bit drivers that work on Windows 95 or later, Windows NT Workstation or Server version 4.0, Windows 2000 Professional, or Windows 2000 Server. These drivers provide access to the following types of data sources:

  • Microsoft Access
  • dBASE
  • Microsoft Excel
  • Paradox
  • Text

See Visual FoxPro ODBC Driver for detailed documentation about the Microsoft Visual FoxPro® ODBC Driver.

Note Access to other data sources, such as Lotus 1-2-3, Microsoft Exchange, and HTML, is enabled by installable ISAM (IISAM) drivers. For more information about these drivers, see "Accessing External Data" in the Microsoft Jet Database Engine Programmer's Reference. ODBC Desktop Database Drivers 4.0 do not support Btrieve and EMS data formats.


Desktop Database Drivers Architecture

These drivers are designed for use on Microsoft Windows 95 or later, or Windows NT 4.0 and Windows 2000. Only 32-bit applications are supported on Windows 95 or later; 16-bit and 32-bit applications are supported on Windows NT 4.0 and Windows 2000.

Note For information about the version of ODBC to be used with these drivers, refer to the ODBC Programmer's Reference, and past and current release notes. Except for noted areas, these drivers conform to the ODBC Programmer's Reference.

The ODBC Desktop Database Drivers include 32-bit drivers for Microsoft Access, dBASE, Microsoft Excel, Paradox, and Text. No 16-bit drivers are included. (A driver for Microsoft FoxPro is available separately.)

The application/driver architecture on Windows 95 or later is:

The use of these drivers by 16-bit applications on Windows 95 is not supported.

The application/driver architecture on Windows NT 4.0 and Windows 2000 is:

The Desktop Database Drivers are two-tier drivers. In a two-tier configuration, the driver does not perform the process of parsing, validating, optimizing, and executing the query. Instead, Microsoft Jet performs these tasks. It processes ODBC API calls and acts as an SQL engine. Microsoft Jet has become an integral, inseparable part of the drivers: It is shipped with the drivers and resides with the drivers, even if no other application on the computer uses it.

The Desktop Database Drivers consist of six different drivers—or, more precisely, one driver file (Odbcjt32.dll) that the ODBC Driver Manager uses in six different ways. The DRIVERID flag in the registry entry for a data source determines which driver in Odbcjt32.dll the Driver Manager uses. An application passes this flag in the connection string included in a call to SQLDriverConnect. By default, the flag is the ID of the Microsoft Access driver.

The driver setup file changes the DRIVERID flag at setup time. All drivers except the Microsoft Access driver have an associated setup DLL. When you click Setup in the Microsoft ODBC Data Source Administrator for a data source, the ODBC installer DLL (Odbcinst.dll) loads the setup DLL. The setup DLL exports the ODBC installer function SQLConfigDataSource. If a window handle is passed to SQLConfigDataSource, this function displays a setup window and changes the DRIVERID flag according to the driver selected from the user interface.

When a file is created programmatically, a NULL window handle is passed to SQLConfigDataSource, and the function creates a data source dynamically, changing the DRIVERID flag according to the lpszDriver argument in the function call.

Odbcjt32.dll implements ODBC functions on top of the Microsoft Jet API. There is no direct mapping between ODBC and Microsoft Jet functions, however. Many factors, such as the cursor models and SQL mapping, prevent a direct correlation of the functions.

The ODBC driver resides between the Microsoft Jet engine and the ODBC Driver Manager. Some ODBC functions called by an application are handled by the Driver Manager and not passed to the driver. For these functions, Microsoft Jet never sees the function call because it does not have a direct connection to the Driver Manager.


History of the Desktop Database Drivers

Version | Release Date | Description
1.0 | August 1993 | Used the SIMBA query processor produced by PageAhead Software. SIMBA received ODBC calls and SQL statements, processed them into Microsoft Jet installable ISAM calls, and then called the Microsoft Jet ISAM dispatch layer to load and call the appropriate installable ISAM driver.
2.0 December 1994 Used with ODBC 2.0, which significantly expanded ODBC functionality. The major change in version 2.0 was that the Microsoft Jet database engine replaced the SIMBA query processor. With the Microsoft Jet database engine, the Desktop Database Drivers integrated much more tightly with the Microsoft Jet installable ISAM drivers and Microsoft Access technology. Significant enhancements were:
  • Native support for scrollable cursors.
  • Native support for outer joins, updatable and heterogeneous joins, and transactions.
  • 32-bit versions of the drivers for Microsoft Windows NT.
3.0 | October 1995 | Provided support for Windows 95 and Windows NT Workstation or NT Server 3.51. Only 32-bit drivers were included in this release; the 16-bit drivers for Windows version 3.1 were removed.
3.5 | October 1996 | These drivers were double-byte character set (DBCS)-enabled, were better suited for use with Internet applications than previous versions, and accommodated the use of File data source names (DSNs). The Microsoft Access driver was released in an RISC version for use on Alpha platforms for Windows 95/98 and Windows NT 3.51 and later operating systems. (However, neither Windows 2000 nor MDAC 2.5 is supported on Alpha platforms.)
4.0 | Late 1998 | Provides support for Microsoft Jet Engine Unicode format along with compatibility for ANSI format of earlier versions.

Note The version 3.5 drivers were designed to work with ODBC 2.x. Although they also work with ODBC 3.0, they do not support all ODBC 3.0 features. For more information about how these drivers work with ODBC 3.0, see "Backward Compatibility and Standards Compliance" in Chapter 17 of the ODBC Programmer's Reference, "Programming Considerations."


What's New

The Desktop Database Drivers documentation was updated with current features, including ODBC support of Unicode format, for MDAC 2.1. For MDAC 2.5, which is released with Windows 2000, the driver setup online help has been enhanced with more accurate documentation but reduced in scope. The MDAC SDK (this guide) now contains the reference material for the Desktop Database Drivers. It has been expanded and reorganized along driver-specific issues.

The ExtendedAnsiSQL connection attribute, which was supported in MDAC 2.1, is now documented in "Using the ExtendedAnsiSQL Connection String Attribute."

The ODBC DAYNAME and MONTHNAME functions, which were introduced in ODBC 2.0, now are supported by the ODBC Desktop Database Drivers. For more information about these functions, see "Time, Date, and Interval Functions" in Appendix E of the ODBC Programmer's Reference, "Scalar Functions."


Product Support

Product support for ODBC is provided by Microsoft Product Support Services. Because many Microsoft products utilize ODBC as a core component and also redistribute ODBC drivers, Microsoft Product Support Services considers ODBC to be a part of each product that incorporates ODBC as a component. Support for ODBC is provided in accordance with the support agreement of that product. See the individual product documentation for more information.


Implementing Desktop Database Drivers

Instructions and technical notes about how to implement and set up the Microsoft ODBC Desktop Database Drivers can be found in the ODBC Data Source Administrator online help. This file can be accessed during driver setup routines from the Microsoft ODBC Data Source Administrator.


Files to Use with the Desktop Database Drivers

The ODBC Desktop Database Drivers are used with the Microsoft Jet Database Engine, the installable ISAM files, and Microsoft ODBC files. Not all are required for the redistribution of the product, and not all are core components.

For information about | See
Files used by each Desktop Database Driver | ODBC Driver Files
Microsoft Jet files used by each Desktop Database Driver | Microsoft Jet Files
IISAM driver files that provide data access between Microsoft Jet and the other DBMS | Installable ISAM Driver Files
ODBC core components and redistributable files | Redistributable Files


ODBC Component Files

The ODBC Driver files are required by each component of ODBC Desktop Database Drivers 4.0.

Some files are required by more than one component. These files must be redistributed if you intend to redistribute any of the ODBC Desktop Database Drivers with your commercial applications.

These files are installed in the \Windows\System directory for Windows 95 or later, or in the \Windows\System32 directory for Windows NT 3.51 or later and Windows 2000. If ODBC files were previously installed in a different directory, make sure that you use the newer files in the \Windows\System (or System32) directory.


Hardware and Software Requirements

Hardware Requirements

To use the ODBC Desktop Database Drivers, you must have:

  • An IBM-compatible personal computer.
  • A hard disk with 6 MB of free disk space.
  • At least 16 MB of random-access memory (RAM).

Software Requirements

To access data with an ODBC driver, you must have:

  • The ODBC driver.
  • The 32-bit ODBC Driver Manager, version 3.51 or later (Odbc32.dll).
  • Microsoft Windows 95 or later, or Windows NT 4.0 or Windows 2000.
  • A stack size of at least 20 KB for an application using a Microsoft ODBC driver.

When using Microsoft Windows NT 4.0 or Windows 2000, the 32-bit driver is thread-safe, but only through the use of a global semaphore that controls access to the driver. Concurrent use of the driver is very limited under Windows NT. All access to the Jet ISAM layer will be single-threaded for all applications using the Microsoft Jet engine.

When running multiple 16-bit applications on Windows on Windows (WOW) on Microsoft Windows NT 4.0, the applications must be run in separate memory spaces. (The same memory space cannot be used because ODBC does not support multiple environments in the same process.) To run an application in a separate memory space, select the application's icon in the Program Manager, open the File menu and click Properties, and then click Run In Separate Memory Space.

The use of these drivers by 16-bit applications on Windows 95 is not supported.

Driver-Specific Hardware and Software Requirements

  • The Microsoft Access and dBASE drivers may require changes in the Autoexec.bat or Config.sys files.


Descriptors

A descriptor is a data structure that holds information about either column data or dynamic parameters. SQLGetDescField can be used to retrieve the supported descriptors listed below. Implementation Parameter Descriptors (IPD) are not automatically populated because SQLDescribeParam is not supported. Descriptor fields that are not available through Jet (such as SQL_DESC_BASE_TABLE_NAME) are also not supported.

For more information about Jet-supported descriptor fields, see the Microsoft Jet Database Engine Programmer's Guide.

For more information about descriptors, see the topics under "Descriptors" in the ODBC Programmer's Reference.

Descriptor fields | Support level
SQL_DESC_ALLOC_TYPE | Supported
SQL_DESC_ARRAY_SIZE | Supported only for ARD
SQL_DESC_ARRAY_STATUS_PTR | Supported
SQL_DESC_BIND_OFFSET_PTR | Supported
SQL_DESC_BIND_TYPE | Supported
SQL_DESC_COUNT | Supported
SQL_DESC_ROWS_PROCESSED_PTR | Supported only for ARD
SQL_DESC_AUTO_UNIQUE_VALUE | Supported
SQL_DESC_BASE_COLUMN_NAME | Supported (NEW)
SQL_DESC_BASE_TABLE_NAME | Supported (NEW)
SQL_DESC_CASE_SENSITIVE | Always FALSE
SQL_DESC_CATALOG_NAME | Not supported
SQL_DESC_CONCISE_TYPE | Supported
SQL_DESC_DATA_PTR | Supported
SQL_DESC_DATETIME_INTERVAL_CODE | Supported
SQL_DESC_DATETIME_INTERVAL_PRECISION | Supported for INTERVAL C types
SQL_DESC_DISPLAY_SIZE | Supported
SQL_DESC_FIXED_PREC_SCALE | Supported (TRUE for money)
SQL_DESC_INDICATOR_PTR | Supported
SQL_DESC_LABEL | Supported
SQL_DESC_LENGTH | Supported
SQL_DESC_LITERAL_PREFIX | Supported
SQL_DESC_LITERAL_SUFFIX | Supported
SQL_DESC_LOCAL_TYPE_NAME | Not supported (returns EMPTY string)
SQL_DESC_NAME | Supported
SQL_DESC_NULLABLE | Supported Note Unsupported in versions preceding Jet 4.0
SQL_DESC_NUM_PREC_RADIX | Supported
SQL_DESC_OCTET_LENGTH | Supported
SQL_DESC_OCTET_LENGTH_PTR | Supported
SQL_DESC_PARAMETER_TYPE | Only input parameters
SQL_DESC_PRECISION | Supported
SQL_DESC_SCALE | Supported
SQL_DESC_SCHEMA_NAME | Not supported
SQL_DESC_SEARCHABLE | Supported
SQL_DESC_TABLE_NAME | Not supported
SQL_DESC_TYPE | Supported
SQL_DESC_TYPE_NAME | Supported
SQL_DESC_UNNAMED | Supported
SQL_DESC_UNSIGNED | Supported
SQL_DESC_UPDATABLE | Supported


Diagnostics

All errors and warnings not checked or partially checked by the Driver Manager are handled by the driver. The driver also maps native errors, or errors returned by the data source, to SQLSTATEs. Each function listed in the ODBC Programmer's Reference contains a "Diagnostics" section that specifies conditions and messages.

Applications call SQLGetDiagRec to retrieve SQLSTATE, native error code, and diagnostic messages. Calling SQLGetDiagField and specifying the field retrieves individual diagnostic fields. The support level of the diagnostic identifiers is listed in the following table.

DiagIdentifiers | Support level
SQL_DIA_DYNAMIC_FUNCTION | Not supported
SQL_DIAG_CLASS_ORIGIN | Supported. Always "ODBC 3.0" for versions 3.0 and later of this driver.
SQL_DIAG_COLUMN_NUMBER | Supported
SQL_DIAG_CURSOR_ROW_COUNT | Not supported
SQL_DIAG_DYNAMIC_FUNCTION_CODE | Not supported
SQL_DIAG_MESSAGE_TEXT | Supported
SQL_DIAG_NATIVE | Supported
SQL_DIAG_NUMBER | Supported
SQL_DIAG_RETURNCODE | Supported but implemented by the Driver Manager
SQL_DIAG_ROW_COUNT | Supported
SQL_DIAG_ROW_NUMBER | Supported
SQL_DIAG_SERVER_NAME | Not supported
SQL_DIAG_SQLSTATE | Supported
SQL_DIAG_SUBCLASS_ORIGIN | Supported


Functions Accepting String Parameters

All functions that take string parameters will be converted to Unicode. (The "W" form of the function will be exported.) Count of bytes is converted to count of characters for those applicable ODBC APIs. This applies to the following functions:

  • SQLConnect
  • SQLDriverConnect
  • SQLColAttributes
  • SQLDescribeCol
  • SQLError (replaced by SQLGetDiagField)
  • SQLExecDirect
  • SQLGetCursorName
  • SQLSetCursorName
  • SQLGetStmtAttr
  • SQLGetInfo
  • SQLGetStmtOption (becomes SQLGetStmtAttr)
  • SQLSetStmtOption (becomes SQLSetStmtAttr)
  • SQLGetConnectOption
  • SQLSetConnectOption
  • SQLGetTypeInfo
  • SQLStatistics
  • SQLTables
  • SQLNativeSQL
  • SQLSpecialColumns
  • ConfigDSNEx
  • ConfigDSN


SQL-92 Compliance

The ODBC Desktop Database Drivers and the underlying Microsoft Jet engine are not SQL-92 compliant. They support many features that have been defined in SQL-92. Some features supported in the driver are not supported in SQL-92. For more information, see the Microsoft Jet Database Engine Programmer's Guide. The following are the major differences between the two:

  • The SQL used by the Desktop Database Drivers supports more powerful expressions than those specified by SQL-92.
  • Different rules apply to the BETWEEN predicate.
  • The SQL used by the Desktop Database Drivers and ANSI SQL supports different keywords.

The following SQL-92 features are not supported by Microsoft Jet SQL:

  • Security statements, such as GRANT and LOCK.
  • DISTINCT with aggregate function references.

The following features are enhancements in the SQL used by the Desktop Database Drivers that are not specified by SQL-92:

  • The TRANSFORM statement providing support for crosstab queries.
  • Additional aggregate functions (StDev and VarP).

Note The Desktop Database Drivers support the standard ANSI syntax for % (percent) and _ (underscore), not * (asterisk) and ? (question mark).


Base Address of Drivers

Each of the ODBC Desktop Database Drivers has been linked with a base address of 0x04c0000.


Setup DLL

The ODBC Administrator calls the function ConfigDSN when users configure data sources.


Translation DLLs

Not supported.


Compatibility

Unicode is a method of software character encoding that treats all characters as having a fixed width of two bytes. This method is used as an alternative to Windows ANSI character encoding, which, because it represents characters in one byte, is limited to 256 characters. Because Unicode can represent over 65,000 characters, it accommodates many languages whose characters are not represented in ANSI encoding.

The ODBC 3.5 (or later) Driver Manager is Unicode-enabled. This affects two major areas: function calls and string data types. The Driver Manager maps function string arguments and string data as required by the application and driver, both of which can be either Unicode-enabled or ANSI-enabled.

The ODBC 3.5 (or later) Driver Manager supports the use of a Unicode driver with both a Unicode application and an ANSI application. It also supports the use of an ANSI driver with an ANSI application. The Driver Manager provides limited Unicode-to-ANSI mapping for a Unicode application working with an ANSI driver. This allows access to the Jet 3.5 databases and support of all existing ISAM file types.

When an ANSI application uses the ODBC Desktop Database Driver 4.0 and accesses Microsoft Access 4.0 or later, the driver exposes the data type as SQL_CHAR, SQL_VARCHAR, or SQL_LONGVARCHAR even though Jet 4.0 supports the wide version. Older versions of Jet do not support SQL_WCHAR, SQL_WVARCHAR, and SQL_WLONGVARCHAR. This restriction also applies in cases where the old formats are used with the Jet 4.0 Database Engine.

For more information concerning Unicode issues with ODBC, see the Unicode topics in "Programming Considerations" of the ODBC Programmer's Reference.


Performance Issues

To ensure compatibility with existing ANSI applications, the SQL_WCHAR, SQL_WVARCHAR, and SQL_WLONGVARCHAR data types are exposed as SQL_CHAR, SQL_VARCHAR, and SQL_LONGVARCHAR for Microsoft Access 4.0 or higher data sources. The data sources do not return WIDE CHAR data types but the data still must be sent to Jet in Wide Char form. It is important to understand that conversion will take place if a SQL_C_CHAR parameter or result column is bound to a SQL_CHAR data type in an ANSI application.

This conversion can be especially inefficient in terms of memory when a SQL_C_CHAR type is bound to a parameter of type LONGVARCHAR. Since the Jet 4.0 engine is unable to stream LONGTEXT parameter data, a UNICODE conversion buffer must be allocated that is twice the size of the SQL_C_CHAR ANSI buffer. The most efficient mechanism is for the application to perform the UNICODE conversion and bind the parameter as type SQL_C_WCHAR. When a parameter is marked as data-at-execution and the data is supplied in multiple calls to SQLPutData, a longtext data buffer is grown. One way to avoid the expense of growing this "Put Data" buffer is to supply an optional length via SQL_DATA_AT_EXEC_LEN(x), where x is the expected length of bytes. This will initialize the size of an internal PutData buffer to x bytes.

Note An efficient way to insert or update long data can be accomplished using SQLBulkOperations() or SQLSetPos() and setting the long data to SQL_DATA_AT_EXEC. (EXEC_LEN is ignored in this case.) Data can be streamed in chunks by calling SQLPutData multiple times, which will effectively append the data to the table.

When an application using a Jet 3.5 database through the Microsoft ODBC Desktop Database Drivers is upgraded to version 4.0, some performance degradation and an increased working set size may occur. This is because when a version 3.x database is opened using the new version 4.0 driver, it loads Jet 4.0. When Jet 4.0 opens the database and sees that the database is a 3.x version, it loads an Installable ISAM driver that is equivalent to loading the Jet 3.5 engine as well. To remove the performance and size penalty, the Jet 3.x database should be compacted into a Jet 4.0 format database. This will eliminate loading two Jet engines and minimize the code path to the data.

Also, the Jet 4.0 engine is a Unicode engine. All strings are stored and manipulated in Unicode. When an ANSI application accesses a Jet 3.x database through the Jet 4.0 engine, the data is converted from ANSI to Unicode and back to ANSI. If the database is updated to the version 4.0 format, the strings are converted to Unicode, removing one level of string conversion as well as minimizing the code path to the data by going through only one Jet engine.


Microsoft Access Driver Programming Considerations

This section contains the following topics:

  • Setting Options Programmatically for the Access Driver
  • Microsoft Access Data Types
  • SQLGetInfo Returned Values for Access
  • Other Access Driver Programming Details


Setting Options Programmatically for the Access Driver

Option | Description | Method
Buffer Size | The size of the internal buffer, in kilobytes, that is used by Microsoft Access to transfer data to and from the disk. The default buffer size is 2048 KB (displayed as 2048). Any integer value divisible by 256 can be entered. | To set this option dynamically, use the MAXBUFFERSIZE keyword in a call to SQLConfigDataSource.
Data Source Name | A name that identifies the data source, such as Payroll or Personnel. | To set this option dynamically, use the DSN keyword in a call to SQLConfigDataSource.
Database | A Microsoft Access data source can be set up without selecting or creating a database. If no database is provided upon setup, the user will be prompted to choose a database file when connecting to the data source. | To set this option dynamically, use the DBQ keyword in a call to SQLConfigDataSource.
Description | An optional description of the data in the data source; for example, "Hire date, salary history, and current review of all employees." | To set this option dynamically, use the DESCRIPTION keyword in a call to SQLConfigDataSource.
Exclusive | If the Exclusive box is selected, the database will be opened in Exclusive mode and can be accessed by only one user at a time. Performance is enhanced when running in Exclusive mode. | To set this option dynamically, use the EXCLUSIVE keyword in a call to SQLConfigDataSource.
ImplicitCommitSync | Determines how changes made outside of a transaction are written to the database. This value is initially set to "Yes", which means that the Microsoft Access driver will wait for commits in an internal/implicit transaction to be completed. | This option is included in the Set Advanced Options dialog box for the Microsoft Access driver.
Page Timeout Specifies the period of time, in tenths of a second, that a page (if not used) remains in the buffer before being removed. For the Microsoft Access driver, the default is five-tenths of a second (0.5 seconds). This option applies to all data sources that use the ODBC driver.

The page timeout cannot be 0 because of an inherent delay. The page timeout cannot be less than the inherent delay, even if the page timeout option is set below that value.

 To set this option dynamically, use the PAGETIMEOUT keyword in a call to SQLConfigDataSource.
Read Only | Designates the database as read-only. | To set this option dynamically, use the READONLY keyword in a call to SQLConfigDataSource.
System Database The full path of the Microsoft Access system database to be used with the Microsoft Access database you want to access.

Click the System Database button to select the system database to be used. The ODBC Microsoft Access driver prompts the user for a name and password. The default name is Admin and the default password in Microsoft Access for the Admin user is an empty string.

To increase the security of your Microsoft Access database, create a new user to replace the Admin user and delete the Admin user, or change the objects to which the Admin user has access.

 To set this option dynamically, use the SYSTEMDB keyword in a call to SQLConfigDataSource.
Threads The number of background threads for the engine to use. For the Microsoft Access driver, this value defaults to 3, but can be changed. The user may want to increase the number of threads if there is a large amount of activity in the database.

This option is included in the Set Advanced Options dialog box for the Microsoft Access driver.

 To set this option dynamically, use the THREADS keyword in a call to SQLConfigDataSource.
UserCommitSync Determines whether the Microsoft Access driver will perform an explicit user-defined transactions asynchronously. This value is initially set to "Yes", which means that the Microsoft Access driver will wait for commits in a user-defined transaction to be completed.

Setting this option to False can have unpredictable consequences in a multi-user environment.

 To set this option dynamically, use the USERCOMMITSYNC keyword in a call to SQLConfigDataSource.


Microsoft Access Data Types

The following table shows the Microsoft Access data types, data types used to create tables, and ODBC SQL data types.

Microsoft Access data type | Data type (CREATETABLE) | ODBC SQL data type
BIGBINARY[1] | LONGBINARY | SQL_LONGVARBINARY
BINARY | BINARY | SQL_BINARY
BIT | BIT | SQL_BIT
COUNTER | COUNTER | SQL_INTEGER
CURRENCY | CURRENCY | SQL_NUMERIC
DATE/TIME | DATETIME | SQL_TIMESTAMP
GUID | GUID | SQL_GUID
LONG BINARY | LONGBINARY | SQL_LONGVARBINARY
LONG TEXT | LONGTEXT | SQL_LONGVARCHAR[2] SQL_WLONGVARCHAR[3]
MEMO | LONGTEXT | SQL_LONGVARCHAR[2] SQL_WLONGVARCHAR[3]
NUMBER (FieldSize= SINGLE) | SINGLE | SQL_REAL
NUMBER (FieldSize= DOUBLE) | DOUBLE | SQL_DOUBLE
NUMBER (FieldSize= BYTE) | UNSIGNED BYTE | SQL_TINYINT
NUMBER (FieldSize= INTEGER) | SHORT | SQL_SMALLINT
NUMBER (FieldSize= LONG INTEGER) | LONG | SQL_INTEGER
NUMERIC | NUMERIC | SQL_NUMERIC
OLE | LONGBINARY | SQL_LONGVARBINARY
TEXT | VARCHAR | SQL_VARCHAR[1] SQL_WVARCHAR[2]
VARBINARY | VARBINARY | SQL_VARBINARY

[1] Access 4.0 applications only. Maximum length of 4000 bytes. Behavior similar to LONGBINARY.

[2] ANSI applications only.

[3] Unicode and Access 4.0 applications only.

Note SQLGetTypeInfo returns ODBC data types. It will not return all Microsoft Access data types if more than one Microsoft Access type is mapped to the same ODBC SQL data type. All conversions in Appendix D of the ODBC Programmer's Reference are supported for the SQL data types listed in the previous table.

The following table shows limitations on Microsoft Access data types.

Data type | Description
BINARY, VARBINARY, and VARCHAR | Creating a BINARY, VARBINARY, or VARCHAR column of zero or unspecified length actually returns a 510-byte column.
BYTE | Even though a Microsoft Access NUMBER field with a FieldSize equal to BYTE is unsigned, a negative number can be inserted into the field when using the Microsoft Access driver.
CHAR, LONGVARCHAR, and VARCHAR | A character string literal can contain any ANSI character (1-255 decimal). Use two consecutive single quotation marks ('') to represent one single quotation mark ('). Procedures should be used to pass character data when using any special character in a character data type column.
DATE | Date values must be either delimited according to the ODBC canonical date format or delimited by the datetime delimiter ("#"). Otherwise, Microsoft Access will treat the value as an arithmetic expression and will not raise a warning or error. For example, the date "March 5, 1996" must be represented as {d '1996-03-05'} or #03/05/1996#; otherwise, if only 03/05/1993 is submitted, Microsoft Access will evaluate this as 3 divided by 5 divided by 1996. This value rounds up to the integer 0, and since the zero day maps to 1899-12-31, this is the date used. A pipe character (|) cannot be used in a date value, even if enclosed in back quotes.
GUID | Data type limited to Microsoft Access 4.0.
NUMERIC | Data type limited to Microsoft Access 4.0.

More limitations on data types can be found in "Data Type Limitations."


SQLGetInfo Returned Values for Access

The following table lists the C-language #defines for the fInfoType argument and the corresponding values returned by SQLGetInfo. This information can be retrieved by passing the listed C-language #defines to SQLGetInfo in the fInfoType argument. For more information about the values returned by SQLGetInfo, see the ODBC Programmer's Reference.

Note Where SQLGetInfo returns a 32-bit bitmask, a vertical bar (|) represents a bitwise OR.

InfoType | Returned Value
SQL_ACCESSIBLE_PROCEDURES | "Y"
SQL_ACCESSIBLE_TABLES | "Y"
SQL_ACTIVE_ENVIRONMENTS | 0
SQL_AGGREGATE_FUNCTIONS | All set
SQL_ALTER_DOMAIN | 0
SQL_ALTER_TABLE | 0
SQL_ASYNC_MODE | 0
SQL_BATCH_ROW_COUNT | 0
SQL_BATCH_SUPPORT | 0
SQL_BOOKMARK_PERSISTENCE | Multiple values
SQL_CATALOG_LOCATION | SQL_QL_START
SQL_CATALOG_NAME | "Y"
SQL_CATALOG_NAME_SEPARATOR | "."
SQL_CATALOG_TERM | "Database"
SQL_CATALOG_USAGE | Multiple values
SQL_COLLATION_SEQ | ""
SQL_COLUMN_ALIAS | "Y"
SQL_CONCAT_NULL_BEHAVIOR | SQL_CB_NON_NULL
SQL_CONVERT_BIGINT | 0
SQL_CONVERT_BINARY | Multiple values
SQL_CONVERT_BIT | 0
SQL_CONVERT_CHAR | Multiple values
SQL_CONVERT_DATE | Multiple values
SQL_CONVERT_DECIMAL | 0
SQL_CONVERT_DOUBLE | Multiple values
SQL_CONVERT_FLOAT | Multiple values
SQL_CONVERT_FUNCTIONS | SQL_FN_CVT_CONVERT
SQL_CONVERT_INTEGER | Multiple values
SQL_CONVERT_LONGVARBINARY | Multiple values
SQL_CONVERT_LONGVARCHAR | Multiple values
SQL_CONVERT_NUMERIC | Multiple values
SQL_CONVERT_REAL | Multiple values
SQL_CONVERT_SMALLINT | Multiple values
SQL_CONVERT_TIME | Multiple values
SQL_CONVERT_TIMESTAMP | Multiple values
SQL_CONVERT_TINYINT | Multiple values
SQL_CONVERT_VARBINARY | Multiple values
SQL_CONVERT_VARCHAR | Multiple values
SQL_CORRELATION_NAME | SQL_CN_ANY
SQL_CREATE_ASSERTION | 0
SQL_CREATE_CHARACTER_SET | 0
SQL_CREATE_COLLATION | 0
SQL_CREATE_DOMAIN | 0
SQL_CREATE_SCHEMA | 0
SQL_CREATE_TABLE | SQL_CT_CREATE_TABLE
SQL_CREATE_TRANSLATION | 0
SQL_CREATE_VIEW | SQL_CV_CREATE_VIEW
SQL_CURSOR_COMMIT_BEHAVIOR | SQL_CB_CLOSE
SQL_CURSOR_ROLLBACK_BEHAVIOR | SQL_CB_CLOSE
SQL_CURSOR_SENSITIVITY | SQL_UNSPECIFIED
SQL_DATA_SOURCE_NAME | The DSN from Odbc.ini, or "" if DRIVER keyword is used in Odbc.ini
SQL_DATA_SOURCE_READ_ONLY | "N"
SQL_DATABASE_NAME | File name
SQL_DATETIME_LITERALS | 0
SQL_DBMS_NAME | "ACCESS"
SQL_DBMS_VER | Multiple values
SQL_DDL_INDEX | Multiple values
SQL_DEFAULT_TXN_ISOLATION | SQL_TXN_READ_COMMITTED
SQL_DESCRIBE_PARAMETER | 0
SQL_DRIVER_HDBC | Handled by the Driver Manager.
SQL_DRIVER_HENV | Handled by the Driver Manager.
SQL_DRIVER_HLIB | Handled by the Driver Manager.
SQL_DRIVER_HSTMT | Handled by the Driver Manager.
SQL_DRIVER_NAME | "OdbcJt32.dll"
SQL_DRIVER_ODBC_VER | "3.51.0000"
SQL_DRIVER_VER | "4.00.nnnn" (nnnn specifies the build date)
SQL_DROP_ASSERTION | 0
SQL_DROP_CHARACTER_SET | 0
SQL_DROP_COLLATION | 0
SQL_DROP_DOMAIN | 0
SQL_DROP_SCHEMA | 0
SQL_DROP_TABLE | SQL_DT_DROP_TABLE
SQL_DROP_TRANSLATION | 0
SQL_DROP_VIEW | SQL_DV_DROP_VIEW
SQL_EXPRESSIONS_IN_ORDERBY | "Y"
SQL_FILE_USAGE | SQL_FILE_CATALOG
SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 | SQL_CA1_NEXT
SQL_GETDATA_EXTENSIONS | Multiple values
SQL_GROUP_BY | SQL_GB_GROUP_BY_CONTAINS_SELECT
SQL_IDENTIFIER_CASE | SQL_IC_MIXED
SQL_IDENTIFIER_QUOTE_CHAR | "`" (back quote)
SQL_KEYWORDS | Multiple values
SQL_LIKE_ESCAPE_CLAUSE | "N"
SQL_MAX_BINARY_LITERAL_LEN | 255
SQL_MAX_CATALOG_NAME_LEN | 66
SQL_MAX_CHAR_LITERAL_LEN | 255
SQL_MAX_COLUMN_NAME_LEN | 64
SQL_MAX_COLUMNS_IN_GROUP_BY | 10
SQL_MAX_COLUMNS_IN_INDEX | 32
SQL_MAX_COLUMNS_IN_ORDER_BY | 10
SQL_MAX_COLUMNS_IN_SELECT | 255
SQL_MAX_COLUMNS_IN_TABLE | 255
SQL_MAX_CONCURRENT_ACTIVITIES | 0
SQL_MAX_CURSOR_NAME_LEN | 64
SQL_MAX_DRIVER_CONNECTIONS | 64
SQL_MAX_INDEX_SIZE | 255
SQL_MAX_PROCEDURE_NAME_LEN | 64
SQL_MAX_ROW_SIZE | 2096
SQL_MAX_ROW_SIZE_INCLUDES_LONG | "N"
SQL_MAX_SCHEMA_NAME_LEN | 0
SQL_MAX_STATEMENT_LEN | 65000
SQL_MAX_TABLE_NAME_LEN | 64
SQL_MAX_TABLES_IN_SELECT | 16
SQL_MAX_USER_NAME_LEN | 0
SQL_MULT_RESULT_SETS | "N"
SQL_MULTIPLE_ACTIVE_TXN | "Y"
SQL_NEED_LONG_DATA_LEN | "N"
SQL_NON_NULLABLE_COLUMNS | SQL_NNC_NON_NULL
SQL_NULL_COLLATION | SQL_NC_LOW
SQL_NUMERIC_FUNCTIONS | Multiple values
SQL_ODBC_SAG_CLI_ CONFORMANCE | SQL_OSCC_COMPLIANT
SQL_ODBC_SQL_INTEGRITY | "N"
SQL_ODBC_VER | From Driver Manager
SQL_OJ_CAPABILITIES | Multiple values
SQL_ORDER_BY_COLUMNS_IN_SELECT | "N"
SQL_OUTER_JOINS | "Y"
SQL_PROCEDURE_TERM | "QUERY"
SQL_PROCEDURES | "Y"
SQL_QUOTED_IDENTIFIER_CASE | SQL_IC_MIXED
SQL_ROW_UPDATES | "N"
SQL_SCHEMA_TERM | ""
SQL_SCHEMA_USAGE | 0
SQL_SCROLL_OPTIONS | Multiple values
SQL_SEARCH_PATTERN_ESCAPE | "\"
SQL_SERVER_NAME | "ACCESS"
SQL_SPECIAL_CHARACTERS | "~`@#$%^&*_-+=\}{"';:?/><,.!'[]|"
SQL_STRING_FUNCTIONS | Multiple values
SQL_SUBQUERIES | Multiple values
SQL_SYSTEM_FUNCTIONS | 0
SQL_TABLE_TERM | "TABLE"
SQL_TIMEDATE_ADD_INTERVALS | 0
SQL_TIMEDATE_DIFF_INTERVALS | 0
SQL_TIMEDATE_FUNCTIONS | Multiple values
SQL_TXN_CAPABLE | SQL_TC_ALL
SQL_TXN_ISOLATION_OPTION | SQL_TXN_READ_COMMITTED
SQL_UNION | Multiple values
SQL_USER_NAME | "ADMIN"


Other Access Driver Programming Details

This section contains the following topics:

  • SQLColAttributes
  • SQLColumns
  • SQLConfigDataSource
  • SQLDriverConnect
  • SQLGetInfo
  • SQLGetTypeInfo
  • SQLProcedureColumns
  • SQLSetConnectOption
  • SQLStatistics
  • SQLTables
  • SQLTransact
  • Encryption
  • Using the ExtendedAnsiSQL Connection String Attribute


SQLColAttributes

Attribute | Comments
SQL_COLUMN_DISPLAY_SIZE | For LONGVARBINARY data, SQL_COLUMN_DISPLAY_SIZE is the maximum length of the column, not the maximum length of the column times 2.
SQL_OWNER_NAME | An empty string ("") is returned in this column because owner name is not supported.
SQL_QUALIFIER_NAME | The path to a database file is returned.
SQL_COLUMN_SEARCHABLE | LONGVARBINARY and LONGVARCHAR columns are reported as SQL_UNSEARCHABLE. Fixed-length and variable-length binary and character data types are searchable, even though LONGVARBINARY and LONGVARCHAR are not.

Note The above is not a complete list of the attributes returned by SQLColAttributes.


SQLColumns

Column | Comments
TABLE_QUALIFIER | The path to a database file is returned.
TABLE_OWNER | NULL is returned in this column because owner name is not supported.
NULLABLE | SQL_NO_NULLS is returned for columns that participate in a primary key or unique index.


SQLConfigDataSource

The SQLConfigDataSource function that is used to add, modify, or delete a data source dynamically uses the following keywords.

Keyword | Description
COLLATINGSEQUENCE The sequence in which the fields are sorted.

This sets the same option as Collating Sequence in the setup dialog box.
COMPACT_DB Performs data compaction on a database file. Has the following format: COMPACT_DB=<path_name><optionaL_sort_order><optional ENCRYPT keyword>.

When using the COMPACT_DB keyword in the same statement with a DSN keyword, this driver ignores the DSN keyword. Therefore, compacting a database and specifying a DSN is a two-step process.
CREATE_DB Creates a database file. Has the following format: CREATE_DB=<path_name><optional_sort-order><optional_ENCRYPT keyword>, where the path name is the full path to a Microsoft Access database. An error will be returned if the path name specifies an existing database. The sort order will be as set up in the New Database dialog box displayed when the Create button is pressed in the Microsoft Access Setup dialog box. If no sort order is specified, General is used.

When using the CREATE_DB keyword in the same statement with a DSN keyword, this driver ignores the DSN keyword. Therefore, creating a database and specifying a DSN is a two-step process.When using the CREATE_DB keyword, if the pathname of the Microsoft Access database to be created contains one or more spaces, then the entire pathname must be enclosed by double quotation marks, as shown in the following examples:

"C:\PROGRAM FILES\COMMON FILES\
MyAccess.mdb"

"C:\PROGRAM FILES\Access2.mdb"

CREATE_DB=C:\TEMP\test.mdb (no quotation marks needed)
CREATE_SYSDB | Creates a system database file. Has the following format: CREATE_SYSDB=<path-name><optional-sort-order>, where the path name is the full path to a Microsoft Access database. An error will be returned if the path name specifies an existing database. The sort order will be as set up in the New Database dialog box displayed when the Create button is clicked in the ODBC Microsoft Access Setup dialog box. If no sort order is specified, General is used.
CREATE_V2DB Creates a database file that is compatible with Microsoft Access 2.0. Has the following format: CREATE_V2DB=<path-name><optional-sort-order>, where the path name is the full path to a Microsoft Access database. An error will be returned if the path name specifies an existing database. The sort order will be as set up in the New Database dialog box displayed when the Create button is pressed in the Microsoft Access Setup dialog box. If no sort order is specified, General is used.

When using the CREATE_V2DB keyword in the same statement with a DSN keyword, this driver ignores the DSN keyword. Therefore, creating a database and specifying a DSN is a two-step process.

When using the CREATE_V2DB keyword, if the pathname of the Microsoft Access database to be created contains one or more spaces, then the entire pathname must be enclosed by double quotation marks, as shown in the following examples:

"C:\PROGRAM FILES\COMMON FILES\
MyAccess.mdb"

"C:\PROGRAM FILES\Access2.mdb"

CREATE_V2DB=C:\TEMP\test.mdb (no quotation marks needed)
DBQ The name of the database file.

This sets the same option as Database in the setup dialog box.
DEFAULTDIR | The path specification to the database file.
DESCRIPTION A description of the data in the data source.

This sets the same option as Description in the setup dialog box.
DRIVER | The path specification to the driver DLL.
DRIVERID An integer ID for the driver.
25 (Microsoft Access)
FIL File type
MS Access for Microsoft Access
IMPLICITCOMMITSYNC Determines whether the Microsoft Access driver will perform internal or implicit commits asynchronously. This value is initially set to "Yes", which means that the Microsoft Access driver will wait for commits in an internal/implicit transaction to be completed.

The value of this option should not be changed without careful consideration of the consequences. For more information about the option, see the Microsoft Jet Database Engine Programmer's Guide.

This sets the same option as ImplicitCommitSync in the setup dialog box.
MAXBUFFERSIZE | The size of the internal buffer, in kilobytes, that is used by Microsoft Access to transfer data to and from the disk. The default buffer size is 2048 KB (displayed as 2048). Any integer value divisible by 256 can be used. This sets the same option as Buffer Size in the setup dialog box.
MAXSCANROWS The number of rows to be scanned when setting a column's data type based upon existing data.

A number from 1 to 16 can be entered for the rows to scan. The value defaults to 8; if it is set to 0, all rows are scanned. (A number outside the limit will return an error.)

This sets the same option as Rows to Scan in the setup dialog box.
PAGETIMEOUT Specifies the period of time, in tenths of a second, that a page (if not used) remains in the buffer before being removed. The default is five-tenths of a second (0.5 seconds). Note that this option applies to all data sources that use the ODBC driver.

This sets the same option as Page Timeout in the setup dialog box.
PWD | The password.
READONLY TRUE to make file read-only; FALSE to make file not read-only.

This sets the same option as Read Only in the setup dialog box.
REPAIR_DB Repairs a database damaged by a failure that occurs during the commit process.

When using the REPAIR_DB keyword in the same statement with a DSN keyword, this driver ignores the DSN keyword. Therefore, repairing a database and specifying a DSN is a two-step process.
SYSTEMDB For the Microsoft Access driver, the path specification to the system database file.

This sets the same option as System Database in the setup dialog box.
THREADS The number of background threads for the engine to use. This value defaults to 3, but can be changed.

This sets the same option as Threads in the setup dialog box.
UID | For the Microsoft Access driver, the user ID name used for login.
USERCOMMITSYNC Determines whether the Microsoft Access driver will perform user-defined transactions asynchronously. This value is initially set to "Yes", which means that the Microsoft Access driver will wait for commits in a user-defined transaction to be completed.

The value of this option should not be changed without careful consideration of the consequences. For more information about the option, see the Microsoft Jet Database Engine Programmer's Guide.

This sets the same option as UserCommitSync in the setup dialog box.


SQLDriverConnect

SQLDriverConnect enables you to connect to a driver without creating a data source (DSN).

The following keywords are supported in the connection string for all drivers: DSN, DBQ, and FIL.

The UID and PWD keywords are also supported.

The PWD keyword should not include any of the special characters (see SQL_SPECIAL_CHARACTERS in SQLGetInfo Returned Values).

The following table shows the minimum keywords required to connect to each driver, and provides an example of keyword/value pairs used with SQLDriverConnect. For a full list of DRIVERID values, see SQLConfigDataSource.

Driver | Keywords Required | Examples
Microsoft Access | Driver, DBQ | Driver={Microsoft Access Driver (*.mdb)}; DBQ=c:\temp\sample.mdb


SQLGetInfo

SQLGetInfo supports the SQL_FILE_USAGE information type. The returned value is a 16-bit integer that indicates how the driver directly treats files in a data source:

  • SQL_FILE_NOT_SUPPORTED—The driver is not a single-tier driver.
  • SQL_FILE_TABLE—A single-tier driver treats files in a data source as tables.
  • SQL_FILE_QUALIFIER—A single-tier driver treats files in a data source as a qualifier.

The ODBC driver returns SQL_FILE_QUALIFIER because each file is a complete database.


SQLGetTypeInfo

The name of the type (TYPE_NAME) returned in the table produced by SQLGetTypeInfo will be the name most commonly used by the data source.

SQL_ALL_EXCEPT_LIKE will be returned in the SEARCHABLE column for the Byte, Counter, Double, Single, Long, and Short data types. (The LIKE capability can be achieved by converting the value to a character using the ODBC canonical conversion functions, then performing the comparison.)


SQLProcedureColumns

Application developers should look for driver-defined columns starting at the end of the result set and proceeding backward.

Column | Comments
COLUMN_TYPE | SQL_PARAM_INPUT or SQL_RESULT_COL
ORDINAL | This is a driver-specific column that is returned at the end of the result set. The SQL type of the column is an integer.


SQLSetConnectOption

fOption | Comment
SQL_ACCESS_MODE | The SQL_ACCESS_MODE fOption can be set to either SQL_MODE_READ_ONLY or SQL_MODE_READ_WRITE. However, the driver does not prevent updates if SQL_ACCESS_MODE is set to SQL_MODE_READ_ONLY.
SQL_AUTOCOMMIT | When the Microsoft Access driver is used, the SQL_AUTOCOMMIT option may be set to either SQL_AUTOCOMMIT_ON or SQL_AUTOCOMMIT_OFF, because the Microsoft Access driver supports transactions[1].
SQL_CURRENT_QUALIFIER | Supported.
SQL_LOGIN_TIMEOUT | Not supported.
SQL_OPT_TRACE | Supported.
SQL_OPT_TRACEFILE | Supported.
SQL_PACKET_SIZE | Not supported.
SQL_QUIET_MODE | Not supported.
SQL_TRANSLATE_DLL | Not supported.
SQL_TRANSLATION_OPTION | Not supported.
SQL_TXN_ISOLATION | SQL_TXN_ISOLATION is always SQL_TXN_READ_COMMITTED.

[1] Atomic transactions are not supported by the Microsoft Access driver. When committing a transaction using the Microsoft Access driver, a finite delay exists between the time the transaction is committed and the time the values are written to disk. This delay is determined by a delay inherent in the Microsoft Jet engine. The page timeout will not be less than a minimum value, even if the PageTimeout option is set below that value. As a result, there is no guarantee that committed data is stable, since changes may be made during the delay.


SQLStatistics

Column | Comments
TABLE_QUALIFIER | The path to a database file is returned for Microsoft Access. Pattern matching is not supported in the szTableQualifier argument.
TABLE_OWNER | NULL is returned in this column, because owner name is not supported.
TABLE_NAME | Undelimited table name. Pattern matching is not supported in the szTableName argument.
INDEX_QUALIFIER | NULL is always returned.
INDEX_NAME | Index-dependent.
TYPE | Only SQL_TABLE_STAT or SQL_INDEX_OTHER will be returned for TYPE.
SEQ_IN_INDEX | Index-dependent.
COLUMN_NAME | Index-dependent.
COLLATION | Index-dependent.
CARDINALITY | Returned for Microsoft Access only.
PAGES | NULL is always returned.

Filtering is based on uniqueness (the fUnique argument). The fAccuracy parameter is ignored.


SQLTables

Argument | Comments
szTableOwner | The only valid argument for szTableOwner is NULL because none of the drivers support owner names. With szTableOwner set to NULL, all tables are returned. NULL is returned in the TABLE_OWNER column.
szTableQualifier | In the TABLE_QUALIFIER column, SQLTables will return the path to a database file.
SzTableType | When the Microsoft Access driver is used, "SYSTEM TABLE" is supported for szTableType for system tables, "SYNONYM" is supported for attached tables, and "VIEW" is supported for row-returning queries.


SQLTransact

When the Microsoft Access driver is used, SQL_COMMIT and SQL_ROLLBACK are supported for the fType argument in a call to SQLTransact.

If a failure occurs during the commit process, the affected database can be repaired using the Repair Database option in the Microsoft Access driver setup, or through the use of the REPAIR_DB keyword in the SQLConfigDataSource function.


Encryption

(This control is available only when Version 4.x format is selected.)

With the release of the 4.0 version of the Microsoft Jet Database Engine, database encryption is allowed when creating Access databases. See the Jet Database Engine Programmer's Reference for additional information.


Using the ExtendedAnsiSQL Connection String Attribute

Jet 4.0 has several new features that are not enabled by default in the ODBC Desktop Database Drivers version 4.0. To take advantage of these new features, an application will have to set a new connection attribute, add a new attribute to the connection string, or set the attribute in a DSN. The attribute is called ExtendedAnsiSQL and pertains only to the Microsoft ODBC Desktop Database Drivers 4.0 and later.

ExtendedAnsiSQL features:

  • Setting ExtendedAnsiSQL
  • Enabling New Data Types by Setting ExtendedAnsiSQL
  • Creating and Dropping Views and Procedures Using ExtendedAnsiSQL
  • Data Trunctation Detection Enabled Using ExtendedAnsiSQL
  • Jet 4.0 Uses SQL-92 Reserved Words List when ExtendedAnsiSQL Set


Setting ExtendedAnsiSQL

The attribute can be controlled in the connection string by adding the ExtendedAnsiSQL attribute:

Value | Description
ExtendedAnsiSQL=0 (default) | This setting does not enable the new features.
ExtendedAnsiSQL=1 | This setting enables the new features.

The attribute can also be set in a DSN through the Advanced Options dialog box when configuring a DSN through the Control Panel.

Setting the attribute to 0 disables the new features; setting it to 1 enables the new features.

The attribute can also be set using SQLSetConnectAttr(). The attribute value is 65501 and is set to a SQLINTEGER value of 1 or 0 as documented above. It can be called before or after connecting, but it is preferable to call it after connecting because of the order in which the driver processes cached connection attributes and connection strings.


Enabling New Data Types by Setting ExtendedAnsiSQL

Two new data types are available in Jet 4.0 databases when the ExtendedAnsiSQL flag is turned on: SQL_DECIMAL and SQL_NUMERIC. The default precision and scale are 18 and 0, respectively. Data accessed via ODBC that is typed as SQL_DECIMAL or SQL_NUMERIC will be mapped to Microsoft Jet Decimal instead of Currency.

When the ExtendedAnsiSQL flag is turned off, you cannot create tables with decimal or numeric types, and these types will not appear in SQLGetTypeInfo(). However, if the table contains the new data types, they can be used with the correct data types.


Creating and Dropping Views and Procedures Using ExtendedAnsiSQL

When the ExtendedAnsiSQL flag is turned on, Jet 4.0 supports the ability to create and drop views and procedures through SQL. These views and procedures will not be visible through Access.


Data Truncation Detection Enabled Using ExtendedAnsiSQL

When the ExtendedAnsiSQL flag is turned on and the application is inserting data into a char or binary column and data is truncated, the truncation will be detected. When the ExtendedAnsiSQL flag is turned off, the data is truncated without warning, as it was in previous versions of the ODBC Desktop Database Drivers.


Jet 4.0 Uses SQL-92 Reserved Words List when ExtendedAnsiSQL_Set

ODBC Drivers

Jet 4.0 Uses SQL-92 Reserved Words List when ExtendedAnsiSQL Set

When the ExtendedAnsiSQL flag is turned on, Jet 4.0 uses the SQL-92 reserved words list. Trying to use a SQL-92 reserved word as an unquoted object name will result in a syntax error. When the ExtendedAnsiSQL flag is turned off, the new reserved words can be used as object names as before.


Microsoft Excel Driver Programming Considerations

This section contains the following topics:

  • Setting Options Programmatically for the Excel Driver
  • Microsoft Excel Data Types
  • SQLGetInfo Returned Values for Excel
  • Other Excel Driver Programming Details


Setting Options Programmatically for the Excel Driver

Option | Description | Method
Data Source Name | A name that identifies the data source, such as Payroll or Personnel. | To set this option dynamically, use the DSN keyword in a call to SQLConfigDataSource.
Database | A Microsoft Access data source can be set up without selecting or creating a database. If no database is provided upon setup, the user will be prompted to choose a database file when connecting to the data source. | To set this option dynamically, use the DBQ keyword in a call to SQLConfigDataSource.
Description | An optional description of the data in the data source; for example, "Hire date, salary history, and current review of all employees." | To set this option dynamically, use the DESCRIPTION keyword in a call to SQLConfigDataSource.
Directory Displays the currently selected directory.

For Microsoft Excel 3.0/4.0 files, the path display is labeled "Directory", while for Microsoft Excel 5.0, 7.0, or 97 files, the path display is labeled "Workbook".

 To set this option dynamically, use the DEFAULTDIR keyword in a call to SQLConfigDataSource.
Read Only | Designates the database as read-only. | To set this option dynamically, use the READONLY keyword in a call to SQLConfigDataSource.
Rows to Scan The number of rows to scan to determine the data type of each column. The data type is determined given the maximum number of kinds of data found. If data is encountered that does not match the data type guessed for the column, the data type will be returned as a NULL value.

For the Microsoft Excel driver, you can enter a number from 1 to 16 for the rows to scan. The value defaults to 8; if it is set to 0, all rows are scanned. (A number outside the limit will return an error.)

 To set this option dynamically, use the MAXSCANROWS keyword in a call to SQLConfigDataSource.
Select Directory Displays a dialog box where you can select a directory containing the files you want to access.

When defining a data source directory (for all drivers except Microsoft Access), specify the directory where your most commonly used files are located. The ODBC driver uses this directory as the default directory. Copy other files into this directory if they are used frequently. Alternatively, you can qualify file names in a SELECT statement with the directory name:

SELECT * FROM C:\MYDIR\EMP

Or, you can specify a new default directory by using the SQLSetConnectOption function with the SQL_CURRENT_QUALIFIER option.

For Microsoft Excel 3.0 or 4.0 files, the path display is labeled "Directory", and the path selection button is labeled "Select Directory". For Microsoft Excel 5.0, 7.0, or 97 files, the path display is labeled "Workbook", and the path selection button is labeled "Select Workbook". When defining a data source directory, specify the directory where your most commonly used Microsoft Excel files are located for Microsoft Excel 3.0/4.0, or the directory where the workbook file is located for Microsoft Excel 5.0, 7.0, or 97. Use Current Directory is disabled for Microsoft Excel 5.0, 7.0, and 97.

 To set this option dynamically, use the DEFAULTDIR keyword in a call to SQLConfigDataSource.


Microsoft Excel Data Types

The following table shows how Microsoft Excel driver data types are mapped to ODBC SQL data types. The Microsoft Excel driver assigns these data types to columns in Microsoft Excel tables based on the data in the column.

Microsoft Excel data type | ODBC data type
CURRENCY | SQL_NUMERIC
DATETIME | SQL_TIMESTAMP
LOGICAL | SQL_BIT
NUMBER | SQL_DOUBLE
TEXT | SQL_VARCHAR

Note SQLGetTypeInfo returns ODBC SQL data types. All conversions in Appendix D of the ODBC Programmer's Reference are supported for the ODBC SQL data types listed earlier in this topic.

The following table shows limitations on Microsoft Excel data types.

Data type | Description
Encrypted data | The Microsoft Excel driver cannot read encrypted data.
Error Strings | The Microsoft Excel driver cannot return a character string for the Microsoft Excel error values (#N/A!, #VALUE!, #REF!, #DIV/0!, #NUM!, #NAME?, and #NULL!), but returns a NULL instead.
LOGICAL | The value in a LOGICAL column is returned in a SQL_C_CHAR buffer as either 0 or 1.
NUMBER | If an integer column is created, numbers that are too big for the integer data type can be entered, and data containing non-integer values can be inserted, with the result that the column may be converted to SQL_DOUBLE.
TEXT When the rows of a column contain more than one Microsoft Excel data type, the ODBC Microsoft Excel driver assigns the SQL_VARCHAR data type to the column. There is one exception to this: if the column contains only two or three of the datetime data types (DATE, TIME, and DATETIME), the ODBC Microsoft Excel driver assigns the SQL_TIMESTAMP data type to the column.

Creating a TEXT column of zero or unspecified length actually returns a 255-byte column.

A character string literal can contain any ANSI character (1-255 decimal). Use two consecutive single quotation marks (") to represent one single quotation mark (').

Inserting a NULL into a column with a data type other than SQL_VARCHAR will cause the data type of the column to change to SQL_VARCHAR.

More limitations on data types can be found in Data Type Limitations.


SQLGetInfo Returned Values for Excel

The following table lists the C-language #defines for the fInfoType argument and the corresponding values returned by SQLGetInfo. This information can be retrieved by passing the listed C-language #defines to SQLGetInfo in the fInfoType argument. For more information about the values returned by SQLGetInfo, see the ODBC Programmer's Reference.

Note Where SQLGetInfo returns a 32-bit bitmask, a vertical bar (|) represents a bitwise OR.

InfoType | Returned value
SQL_ACCESSIBLE_PROCEDURES | "N"
SQL_ACCESSIBLE_TABLES | "Y"
SQL_ACTIVE_ENVIRONMENTS | 0
SQL_AGGREGATE_FUNCTIONS | All set
SQL_ALTER_DOMAIN | 0
SQL_ALTER_TABLE | 0
SQL_ASYNC_MODE | 0
SQL_BATCH_ROW_COUNT | 0
SQL_BATCH_SUPPORT | 0
SQL_BOOKMARK_PERSISTENCE | Multiple values
SQL_CATALOG_LOCATION | SQL_QL_START
SQL_CATALOG_NAME | "Y"
SQL_CATALOG_NAME_SEPARATOR | Multiple values
SQL_CATALOG_TERM | Multiple values
SQL_CATALOG_USAGE | Multiple values
SQL_COLLATION_SEQ | ""
SQL_COLUMN_ALIAS | "Y"
SQL_CONCAT_NULL_BEHAVIOR | SQL_CB_NON_NULL
SQL_CONVERT_BIGINT | 0
SQL_CONVERT_BINARY | Multiple values
SQL_CONVERT_BIT | 0
SQL_CONVERT_CHAR | Multiple values
SQL_CONVERT_DATE | Multiple values
SQL_CONVERT_DECIMAL | 0
SQL_CONVERT_DOUBLE | Multiple values
SQL_CONVERT_FLOAT | Multiple values
SQL_CONVERT_FUNCTIONS | SQL_FN_CVT_CONVERT
SQL_CONVERT_INTEGER | Multiple values
SQL_CONVERT_LONGVARBINARY | Multiple values
SQL_CONVERT_LONGVARCHAR | Multiple values
SQL_CONVERT_NUMERIC | Multiple values
SQL_CONVERT_REAL | Multiple values
SQL_CONVERT_SMALLINT | Multiple values
SQL_CONVERT_TIME | Multiple values
SQL_CONVERT_TIMESTAMP | Multiple values
SQL_CONVERT_TINYINT | Multiple values
SQL_CONVERT_VARBINARY | Multiple values
SQL_CONVERT_VARCHAR | Multiple values
SQL_CORRELATION_NAME | SQL_CN_ANY
SQL_CREATE_ASSERTION | 0
SQL_CREATE_CHARACTER_SET | 0
SQL_CREATE_COLLATION | 0
SQL_CREATE_DOMAIN | 0
SQL_CREATE_SCHEMA | 0
SQL_CREATE_TABLE | SQL_CT_CREATE_TABLE
SQL_CREATE_TRANSLATION | 0
SQL_CREATE_VIEW | 0
SQL_CURSOR_COMMIT_BEHAVIOR | SQL_CB_CLOSE
SQL_CURSOR_ROLLBACK_BEHAVIOR | SQL_CB_CLOSE
SQL_CURSOR_SENSITIVITY | SQL_UNSPECIFIED
SQL_DATA_SOURCE_NAME | The DSN from Odbc.ini, or "" if DRIVER keyword is used in Odbc.ini
SQL_DATA_SOURCE_READ_ONLY | "Y"
SQL_DATABASE_NAME | Current database directory
SQL_DATETIME_LITERALS | 0
SQL_DBMS_NAME | "EXCEL"
SQL_DBMS_VER | Multiple values
SQL_DDL_INDEX | 0
SQL_DEFAULT_TXN_ISOLATION | 0
SQL_DESCRIBE_PARAMETER | 0
SQL_DRIVER_HDBC | Handled by the Driver Manager.
SQL_DRIVER_HENV | Handled by the Driver Manager.
SQL_DRIVER_HLIB | Handled by the Driver Manager.
SQL_DRIVER_HSTMT | Handled by the Driver Manager.
SQL_DRIVER_NAME | "OdbcJt32.dll"
SQL_DRIVER_ODBC_VER | "3.51.0000"
SQL_DRIVER_VER | "4.00.nnnn" (nnnn specifies the build date)
SQL_DROP_ASSERTION | 0
SQL_DROP_CHARACTER_SET | 0
SQL_DROP_COLLATION | 0
SQL_DROP_DOMAIN | 0
SQL_DROP_SCHEMA | 0
SQL_DROP_TABLE | SQL_DT_DROP_TABLE
SQL_DROP_TRANSLATION | 0
SQL_DROP_VIEW | SQL_DV_DROP_VIEW
SQL_EXPRESSIONS_IN_ORDERBY | "Y"
SQL_FILE_USAGE | Multiple values
SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 | SQL_CA1_NEXT
SQL_GETDATA_EXTENSIONS | Multiple values
SQL_GROUP_BY | SQL_GB_GROUP_BY_CONTAINS_SELECT
SQL_IDENTIFIER_CASE | SQL_IC_MIXED
SQL_IDENTIFIER_QUOTE_CHAR | "`" (back quote)
SQL_KEYWORDS | Multiple values
SQL_LIKE_ESCAPE_CLAUSE | "N"
SQL_MAX_BINARY_LITERAL_LEN | 255
SQL_MAX_CATALOG_NAME_LEN | 66
SQL_MAX_CHAR_LITERAL_LEN | Multiple values
SQL_MAX_COLUMN_NAME_LEN | Multiple values
SQL_MAX_COLUMNS_IN_GROUP_BY | 10
SQL_MAX_COLUMNS_IN_INDEX | 0
SQL_MAX_COLUMNS_IN_ORDER_BY | 10
SQL_MAX_COLUMNS_IN_SELECT | 255
SQL_MAX_COLUMNS_IN_TABLE | 255 When using the Microsoft Excel Driver, a CREATE TABLE statement might allow 256 columns, but the 255-column limit is still valid and an insert into column 256 will fail.
SQL_MAX_CONCURRENT_ACTIVITIES | 0
SQL_MAX_CURSOR_NAME_LEN | 64
SQL_MAX_DRIVER_CONNECTIONS | 64
SQL_MAX_INDEX_SIZE | 0
SQL_MAX_PROCEDURE_NAME_LEN | 0
SQL_MAX_ROW_SIZE | 65535
SQL_MAX_ROW_SIZE_INCLUDES_LONG | "Y"
SQL_MAX_SCHEMA_NAME_LEN | 0
SQL_MAX_STATEMENT_LEN | 65000
SQL_MAX_TABLE_NAME_LEN | Multiple values
SQL_MAX_TABLES_IN_SELECT | 16
SQL_MAX_USER_NAME_LEN | 0
SQL_MULT_RESULT_SETS | "N"
SQL_MULTIPLE_ACTIVE_TXN | "Y"
SQL_NEED_LONG_DATA_LEN | "N"
SQL_NON_NULLABLE_COLUMNS | SQL_NNC_NON_NULL
SQL_NULL_COLLATION | SQL_NC_LOW
SQL_NUMERIC_FUNCTIONS | Multiple values
SQL_ODBC_SAG_CLI_ CONFORMANCE | SQL_OSCC_COMPLIANT
SQL_ODBC_SQL_INTEGRITY | "N"
SQL_ODBC_VER | From Driver Manager
SQL_OJ_CAPABILITIES | Multiple values
SQL_ORDER_BY_COLUMNS_IN_SELECT | "N"
SQL_OUTER_JOINS | "Y"
SQL_PROCEDURE_TERM | ""
SQL_PROCEDURES | "N"
SQL_QUOTED_IDENTIFIER_CASE | SQL_IC_MIXED
SQL_ROW_UPDATES | "N"
SQL_SCHEMA_TERM | ""
SQL_SCHEMA_USAGE | 0
SQL_SCROLL_OPTIONS | Multiple values
SQL_SEARCH_PATTERN_ESCAPE | "\"
SQL_SERVER_NAME | "EXCEL"
SQL_SPECIAL_CHARACTERS | "~`@#$%^&*_-+=\}{"';:?/><,.!'[]|"
SQL_STRING_FUNCTIONS | Multiple values
SQL_SUBQUERIES | Multiple values
SQL_SYSTEM_FUNCTIONS | 0
SQL_TABLE_TERM | "TABLE"
SQL_TIMEDATE_ADD_INTERVALS | 0
SQL_TIMEDATE_DIFF_INTERVALS | 0
SQL_TIMEDATE_FUNCTIONS | Multiple values
SQL_TXN_CAPABLE | SQL_TC_NONE
SQL_TXN_ISOLATION_OPTION | 0
SQL_UNION | Multiple values
SQL_USER_NAME | ""


Other Excel Driver Programming Details

This section contains the following topics:

  • SQLBindParameter
  • SQLColAttributes
  • SQLColumns
  • SQLConfigDataSource
  • SQLDriverConnect
  • SQLGetInfo
  • SQLGetTypeInfo
  • SQLSetConnectOption
  • SQLStatistics
  • SQLTables
  • SQLTransact
  • Read-Only Status


SQLBindParameter

When the Microsoft Excel driver is used, executing an INSERT statement that uses a parameter to insert a NULL into a SQL_CHAR column will return SQL_SUCCESS_WITH_INFO with SQLSTATE 01004, "Data Truncated."


SQLColAttributes

Attribute | Comments
SQL_COLUMN_DISPLAY_SIZE | For LONGVARBINARY data, SQL_COLUMN_DISPLAY_SIZE is the maximum length of the column, not the maximum length of the column times 2.
SQL_OWNER_NAME | An empty string ("") is returned in this column because owner name is not supported.
SQL_QUALIFIER_NAME | The path to a directory is returned.
SQL_COLUMN_SEARCHABLE | LONGVARBINARY and LONGVARCHAR columns are reported as SQL_UNSEARCHABLE. Fixed-length and variable-length binary and character data types are searchable, even though LONGVARBINARY and LONGVARCHAR are not.

Note The above is not a complete list of the attributes returned by SQLColAttributes.


SQLColumns

Column | Comments
TABLE_QUALIFIER | The path to a directory is returned.
TABLE_OWNER | NULL is returned in this column because owner name is not supported.
NULLABLE | SQL_NO_NULLS is returned for columns that participate in a primary key or unique index.


SQLConfigDataSource

The SQLConfigDataSource function that is used to add, modify, or delete a data source dynamically uses the following keywords.

Keyword | Description
DBQ | For the Microsoft Excel driver when accessing Microsoft Excel 5.0, 7.0, or 97 files, the name of the workbook file. This sets the same option as Database in the setup dialog box.
DEFAULTDIR | The path specification to the directory. This sets the same option as Select Directory or Select Workbook in the setup dialog box.
DESCRIPTION | A description of the data in the data source. This sets the same option as Description in the setup dialog box.
DRIVER | The path specification to the driver DLL.
DRIVERID | An integer ID for the driver. 534 (Microsoft Excel 3.0) 278 (Microsoft Excel 4.0) 22 (Microsoft Excel 5.0/7.0) 790 (Microsoft Excel 97)
FIL | File type Excel 3.0, Excel 4.0, Excel 5.0, Excel 7.0, or Excel 97
FIRSTROWHASNAMES | Indicates whether the cells of the first row of the range contain the column names for the table (1) or not (0).
MAXSCANROWS | The number of rows to be scanned when setting a column's data type based upon existing data. A number from 1 to 16 can be entered for the rows to scan. The value defaults to 8; if it is set to 0, all rows are scanned. (A number outside the limit will return an error.) This sets the same option as Rows to Scan in the setup dialog box.
READONLY | TRUE to make file read-only; FALSE to make file not read-only. This sets the same option as Read Only in the setup dialog box.
THREADS | The number of background threads for the engine to use. For the Microsoft Access driver, this value defaults to 3, but can be changed. For the dBASE, Microsoft Excel driver this value is 3, and cannot be changed. This sets the same option as Threads in the setup dialog box.


SQLDriverConnect

SQLDriverConnect enables you to connect to a driver without creating a data source (DSN).

The following keywords are supported in the connection string for all drivers: DSN, DBQ, and FIL.

The following table shows the minimum keywords required to connect to each driver, and provides an example of keyword/value pairs used with SQLDriverConnect. For a full list of DRIVERID values, see SQLConfigDataSource.

Note If DBQ or DefaultDir is not specified for the Microsoft Excel 3.0 or 4.0 driver, the driver will connect to the current directory.

Driver Keywords Required Examples
Microsoft Excel 3.0 or 4.0 Driver, DriverID Driver={Microsoft Excel Driver (*.xls)};
DBQ=c:\temp; DriverID=278
Microsoft Excel 5.0/7.0 Driver, DriverID,
DBQ 		Driver={Microsoft Excel Driver (*.xls)};
DBQ=c:\temp\sample.xls;
DriverID=22
Microsoft Excel 97 Driver, DriverID,
DBQ 		Driver={Microsoft Excel Driver (*.xls)};
DBQ=c:\temp\sample.xls;
DriverID=790


SQLGetInfo

SQLGetInfo supports the SQL_FILE_USAGE information type. The returned value is a 16-bit integer that indicates how the driver directly treats files in a data source:

  • SQL_FILE_NOT_SUPPORTED—The driver is not a single-tier driver.
  • SQL_FILE_TABLE—A single-tier driver treats files in a data source as tables.
  • SQL_FILE_QUALIFIER—A single-tier driver treats files in a data source as a qualifier.

The ODBC driver returns SQL_FILE_TABLE for the Microsoft Excel driver because each file is a table.


SQLGetTypeInfo

The name of the type (TYPE_NAME) returned in the table produced by SQLGetTypeInfo will be the name most commonly used by the data source.

SQL_ALL_EXCEPT_LIKE will be returned in the SEARCHABLE column for the Byte, Counter, Double, Single, Long, and Short data types. (The LIKE capability can be achieved by converting the value to a character using the ODBC canonical conversion functions, then performing the comparison.)

When the Microsoft Excel driver is used, the ODBC type names are returned in the TYPE_NAME column that is returned by SQLGetTypeInfo.


SQLSetConnectOption

fOption | Comment
SQL_ACCESS_MODE | The SQL_ACCESS_MODE fOption can be set to either SQL_MODE_READ_ONLY or SQL_MODE_READ_WRITE. However, the driver does not prevent updates if SQL_ACCESS_MODE is set to SQL_MODE_READ_ONLY.
SQL_AUTOCOMMIT | The Microsoft Excel driver only supports SQL_AUTOCOMMIT being set to ON (the default state), because they do not support transactions.
SQL_CURRENT_QUALIFIER | Supported.
SQL_LOGIN_TIMEOUT | Not supported.
SQL_OPT_TRACE | Supported.
SQL_OPT_TRACEFILE | Supported.
SQL_PACKET_SIZE | Not supported.
SQL_QUIET_MODE | Not supported.
SQL_TRANSLATE_DLL | Not supported.
SQL_TRANSLATION_OPTION | Not supported.
SQL_TXN_ISOLATION | Not supported.


SQLStatistics

Column | Comments
TABLE_QUALIFIER | The path to a directory. Pattern matching is not supported in the szTableQualifier argument.
TABLE_OWNER | NULL is returned in this column because owner name is not supported.
TABLE_NAME | Undelimited table name. Pattern matching is not supported in the szTableName argument.
INDEX_QUALIFIER | NULL is always returned.
INDEX_NAME | Index-dependent.
TYPE | Only SQL_TABLE_STAT or SQL_INDEX_OTHER will be returned for TYPE.
SEQ_IN_INDEX | Index-dependent.
COLUMN_NAME | Index-dependent.
COLLATION | Index-dependent.
PAGES | NULL is always returned.

Filtering is based on uniqueness (the fUnique argument). The fAccuracy parameter is ignored.


SQLTables

Argument | Comments
szTableOwner | The only valid argument for szTableOwner is NULL because none of the drivers support owner names. With szTableOwner set to NULL, all tables are returned. NULL is returned in the TABLE_OWNER column.
szTableQualifier | When the Microsoft Excel 3.0 or 4.0 driver is used, if you call SQLTables with a value for szTableQualifier that is not the name of an existing table, the driver will create a table with that name. In the TABLE_QUALIFIER column, SQLTables will return the path to a directory.
SzTableType | For Microsoft Excel 3.0 or 4.0, "TABLE" is the only table type supported. For Microsoft Excel 5.0, 7.0, or 97 files, "SYSTEM TABLE" is returned for sheet names (tables with a "$" on the end), and "TABLE" is returned for tables within worksheets.


SQLTransact

This function supports SQL_COMMIT, but not SQL_ROLLBACK.


Read-Only Status

When the Microsoft Excel driver is used, data source tables are opened as read-only by default, and can be opened by only one user at a time. Even though tables have read-only status, however, applications can perform insertions and updates for Microsoft Excel tables.

When an application performs a Save As command on Microsoft Excel data through the Microsoft Excel driver, the application should create a new table and insert the data to be saved into the new table. Inserts result in an append to the table. No other operations can be performed on the table until it is closed and reopened. Once the table is closed, no subsequent insert can be performed because the table is then a read-only table.

It is possible to update values when using the Microsoft Excel driver, but a row cannot be deleted from a table based on a Microsoft Excel spreadsheet, so updates are not considered officially supported by the Microsoft Excel driver.


Paradox Driver Programming Considerations

This section contains the following topics:

  • Setting Options Programmatically for the Paradox Driver
  • Paradox Data Types
  • SQLGetInfo Returned Values for Paradox
  • Other Paradox Driver Programming Details

Note Accessing Paradox ISAM files through the ODBC Desktop Database Drivers requires installation of the Borland database engine.


Setting Options Programmatically for the Paradox Driver

Option | Description | Method
Directory | Sets the targeted directory. | To set this option dynamically, use the DEFAULTDIR keyword in a call to SQLConfigDataSource.
Collating Sequence The sequence in which the fields are sorted.

The sequence can be ASCII (the default), International, Swedish-Finnish, or Norwegian-Danish.

 To set this option dynamically, use the COLLATINGSEQUENCE keyword in a call to SQLConfigDataSource.
Description | An optional description of the data in the data source; for example, "Hire date, salary history, and current review of all employees." | To set this option dynamically, use the DESCRIPTION keyword in a call to SQLConfigDataSource.
Exclusive | If the Exclusive box is selected, the database will be opened in Exclusive mode and can be accessed by only one user at a time. Performance is enhanced when running in Exclusive mode. | To set this option dynamically, use the EXCLUSIVE keyword in a call to SQLConfigDataSource.
Net Style | The network access style to use when accessing Paradox data: either "3.x" for Paradox 3.x or "4.x" for Paradox 4.x or 5.x. Can be set to "3.x" or "4.x" if the version is Paradox 4.x or 5.x; if the version is Paradox 3.x, the style must be "3.x". | To set this option dynamically, use the PARADOXNETSTYLE keyword in a call to SQLConfigDataSource.
Page Timeout Specifies the period of time, in tenths of a second, that a page (if not used) remains in the buffer before being removed. The default is 600 tenths of a second (60 seconds). This option applies to all data sources that use the ODBC driver.

The page timeout cannot be 0 because of an inherent delay. The page timeout cannot be less than the inherent delay, even if the page timeout option is set below that value.

 To set this option dynamically, use the PAGETIMEOUT keyword in a call to SQLConfigDataSource.
Read Only | Designates the database as read-only. | To set this option dynamically, use the READONLY keyword in a call to SQLConfigDataSource.
Select Directory Displays a dialog box where you can select a directory containing the files you want to access.

When defining a data source directory specify the directory where your most commonly used files are located. The ODBC driver uses this directory as the default directory. Copy other files into this directory if they are used frequently. Alternatively, you can qualify file names in a SELECT statement with the directory name:

SELECT * FROM C:\MYDIR\EMP

Or, you can specify a new default directory by using the SQLSetConnectOption function with the SQL_CURRENT_QUALIFIER option.

 To set this option dynamically, use the DEFAULTDIR keyword in a call to SQLConfigDataSource.
Select Network Directory The full path of the directory containing a Paradox lock database, because it contains either the Pdoxusrs.net file (in Paradox 4.x) or the Paradox.net file (in Paradox 5.x). If the directory does not contain one of these files, the Paradox driver creates one. For information about these files, see the Paradox documentation.

Before you can select a network directory, you must enter your Paradox user name in the User Name text box. Click Select Network Directory to select a network directory.

 To set this option dynamically, use the PARADOXNETPATH keyword in a call to SQLConfigDataSource.
User Name | The Paradox user name. This is the name displayed to other users of Paradox files when a lock is encountered. | To set this option dynamically, use the PARADOXUSERNAME keyword in a call to SQLConfigDataSource.


Paradox Data Types

The ODBC Paradox driver maps Paradox data types to ODBC SQL data types. The following table lists all Paradox data types and shows the ODBC SQL data types they are mapped to.

Paradox data type | ODBC data type
ALPHANUMERIC | SQL_VARCHAR
AUTOINCREMENT[1] | SQL_INTEGER
BCD[1] | SQL_DOUBLE
BYTES[1] | SQL_BINARY
DATE | SQL_DATE
IMAGE[2] | SQL_LONGVARBINARY
LOGICAL[1] | SQL_BIT
LONG[1] | SQL_INTEGER
MEMO[2] | SQL_LONGVARCHAR
MONEY[1] | SQL_DOUBLE
NUMBER | SQL_DOUBLE
SHORT | SQL_SMALLINT
TIME[1] | SQL_TIMESTAMP
TIMESTAMP[1] | SQL_TIMESTAMP

[1] Valid only for Paradox versions 5.x.

[2] Valid only for Paradox versions 4.x and 5.x.

Note SQLGetTypeInfo returns ODBC SQL data types. All conversions in Appendix D of the ODBC Programmer's Reference are supported for the ODBC SQL data types listed earlier in this topic.

The following table shows limitations on Paradox data types.

Data type | Description
ALPHANUMERIC | Creating an ALPHANUMERIC column of zero or unspecified length actually returns a 255-byte column.
BYTES | If you insert NULL into a binary column with the Paradox5 driver, it is changed to 0.
LONG | The maximum negative value supported by the Paradox driver for the Long data type in Paradox 5.x is not -2^31 (-2147483648), as it should be since Long maps to the ODBC data type SQL_INTEGER. The maximum negative value supported for Long is actually -2^31 + 1 (-2147483647).
TIMESTAMP | When a value is inserted into a TIMESTAMP column by the Paradox driver, then subsequently retrieved from the column, the retrieved value may differ from the inserted value by as much as 1 second because of rounding.

More limitations on data types can be found in Data Type Limitations.


SQLGetInfo Returned Values for Paradox

The following table lists the C-language #defines for the fInfoType argument and the corresponding values returned by SQLGetInfo. This information can be retrieved by passing the listed C-language #defines to SQLGetInfo in the fInfoType argument. For more information about the values returned by SQLGetInfo, see the ODBC Programmer's Reference.

Note Where SQLGetInfo returns a 32-bit bitmask, a vertical bar (|) represents a bitwise OR.

InfoType | Returned value
SQL_ACCESSIBLE_PROCEDURES | "N"
SQL_ACCESSIBLE_TABLES | "Y"
SQL_ACTIVE_ENVIRONMENTS | 0
SQL_AGGREGATE_FUNCTIONS | All set
SQL_ALTER_DOMAIN | 0
SQL_ALTER_TABLE | Multiple values
SQL_ASYNC_MODE | 0
SQL_BATCH_ROW_COUNT | 0
SQL_BATCH_SUPPORT | 0
SQL_BOOKMARK_PERSISTENCE | Multiple values
SQL_CATALOG_LOCATION | SQL_QL_START
SQL_CATALOG_NAME | "Y"
SQL_CATALOG_NAME_SEPARATOR | "\"
SQL_CATALOG_TERM | "Directory"
SQL_CATALOG_USAGE | Multiple values
SQL_COLLATION_SEQ | ""
SQL_COLUMN_ALIAS | "Y"
SQL_CONCAT_NULL_BEHAVIOR | SQL_CB_NON_NULL
SQL_CONVERT_BIGINT | 0
SQL_CONVERT_BINARY | Multiple values
SQL_CONVERT_BIT | 0
SQL_CONVERT_CHAR | Multiple values
SQL_CONVERT_DATE | Multiple values
SQL_CONVERT_DECIMAL | 0
SQL_CONVERT_DOUBLE | Multiple values
SQL_CONVERT_FLOAT | Multiple values
SQL_CONVERT_FUNCTIONS | SQL_FN_CVT_CONVERT
SQL_CONVERT_INTEGER | Multiple values
SQL_CONVERT_LONGVARBINARY | Multiple values
SQL_CONVERT_LONGVARCHAR | Multiple values
SQL_CONVERT_NUMERIC | Multiple values
SQL_CONVERT_REAL | Multiple values
SQL_CONVERT_SMALLINT | Multiple values
SQL_CONVERT_TIME | Multiple values
SQL_CONVERT_TIMESTAMP | Multiple values
SQL_CONVERT_TINYINT | Multiple values
SQL_CONVERT_VARBINARY | Multiple values
SQL_CONVERT_VARCHAR | Multiple values
SQL_CORRELATION_NAME | SQL_CN_ANY
SQL_CREATE_ASSERTION | 0
SQL_CREATE_CHARACTER_SET | 0
SQL_CREATE_COLLATION | 0
SQL_CREATE_DOMAIN | 0
SQL_CREATE_SCHEMA | 0
SQL_CREATE_TABLE | SQL_CT_CREATE_TABLE
SQL_CREATE_TRANSLATION | 0
SQL_CREATE_VIEW | 0
SQL_CURSOR_COMMIT_BEHAVIOR | SQL_CB_CLOSE
SQL_CURSOR_ROLLBACK_BEHAVIOR | SQL_CB_CLOSE
SQL_CURSOR_SENSITIVITY | SQL_UNSPECIFIED
SQL_DATA_SOURCE_NAME | The DSN from Odbc.ini, or "" if DRIVER keyword is used in Odbc.ini
SQL_DATA_SOURCE_READ_ONLY | "N" (This depends on the data source.)
SQL_DATABASE_NAME | Current database directory
SQL_DATETIME_LITERALS | 0
SQL_DBMS_NAME | "PARADOX"
SQL_DBMS_VER | Multiple values
SQL_DDL_INDEX | Multiple values
SQL_DEFAULT_TXN_ISOLATION | 0
SQL_DESCRIBE_PARAMETER | 0
SQL_DRIVER_HDBC | Handled by the Driver Manager.
SQL_DRIVER_HENV | Handled by the Driver Manager.
SQL_DRIVER_HLIB | Handled by the Driver Manager.
SQL_DRIVER_HSTMT | Handled by the Driver Manager.
SQL_DRIVER_NAME | "OdbcJt32.dll"
SQL_DRIVER_ODBC_VER | "3.51.0000"
SQL_DRIVER_VER | "4.00.nnnn" (nnnn specifies the build date)
SQL_DROP_ASSERTION | 0
SQL_DROP_CHARACTER_SET | 0
SQL_DROP_COLLATION | 0
SQL_DROP_DOMAIN | 0
SQL_DROP_SCHEMA | 0
SQL_DROP_TABLE | SQL_DT_DROP_TABLE
SQL_DROP_TRANSLATION | 0
SQL_DROP_VIEW | SQL_DV_DROP_VIEW
SQL_EXPRESSIONS_IN_ORDERBY | "Y"
SQL_FILE_USAGE | SQL_FILE_TABLE
SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 | SQL_CA1_NEXT
SQL_GETDATA_EXTENSIONS | Multiple values
SQL_GROUP_BY | SQL_GB_GROUP_BY_CONTAINS_SELECT
SQL_IDENTIFIER_CASE | SQL_IC_MIXED
SQL_IDENTIFIER_QUOTE_CHAR | "`" (back quote)
SQL_KEYWORDS | Multiple values
SQL_LIKE_ESCAPE_CLAUSE | "N"
SQL_MAX_BINARY_LITERAL_LEN | 255
SQL_MAX_CATALOG_NAME_LEN | 66
SQL_MAX_CHAR_LITERAL_LEN | 255
SQL_MAX_COLUMN_NAME_LEN | 25
SQL_MAX_COLUMNS_IN_GROUP_BY | 10
SQL_MAX_COLUMNS_IN_INDEX | 0 (Limit Unknown or Not Applicable)
SQL_MAX_COLUMNS_IN_ORDER_BY | 10
SQL_MAX_COLUMNS_IN_SELECT | 255
SQL_MAX_COLUMNS_IN_TABLE | 255
SQL_MAX_CONCURRENT_ACTIVITIES | 0
SQL_MAX_CURSOR_NAME_LEN | 64
SQL_MAX_DRIVER_CONNECTIONS | 64
SQL_MAX_INDEX_SIZE | 1350
SQL_MAX_PROCEDURE_NAME_LEN | 0
SQL_MAX_ROW_SIZE | 1350
SQL_MAX_ROW_SIZE_INCLUDES_LONG | "N"
SQL_MAX_SCHEMA_NAME_LEN | 0
SQL_MAX_STATEMENT_LEN | 65000
SQL_MAX_TABLE_NAME_LEN | 12
SQL_MAX_TABLES_IN_SELECT | 16
SQL_MAX_USER_NAME_LEN | 0
SQL_MULT_RESULT_SETS | "N"
SQL_MULTIPLE_ACTIVE_TXN | "Y"
SQL_NEED_LONG_DATA_LEN | "N"
SQL_NON_NULLABLE_COLUMNS | SQL_NNC_NON_NULL
SQL_NULL_COLLATION | SQL_NC_LOW
SQL_NUMERIC_FUNCTIONS | Multiple values
SQL_ODBC_SAG_CLI_ CONFORMANCE | SQL_OSCC_COMPLIANT
SQL_ODBC_SQL_INTEGRITY | "N"
SQL_ODBC_VER | From Driver Manager
SQL_OJ_CAPABILITIES | Multiple values
SQL_ORDER_BY_COLUMNS_IN_SELECT | "N"
SQL_OUTER_JOINS | "Y"
SQL_PROCEDURE_TERM | ""
SQL_PROCEDURES | "N"
SQL_QUOTED_IDENTIFIER_CASE | SQL_IC_MIXED
SQL_ROW_UPDATES | "N"
SQL_SCHEMA_TERM | ""
SQL_SCHEMA_USAGE | 0
SQL_SCROLL_OPTIONS | Multiple values
SQL_SEARCH_PATTERN_ESCAPE | "\"
SQL_SERVER_NAME | "PARADOX"
SQL_SPECIAL_CHARACTERS | "~`@#$%^&*_-+=\}{"';:?/><,.!'[]|"
SQL_STRING_FUNCTIONS | Multiple values
SQL_SUBQUERIES | Multiple values
SQL_SYSTEM_FUNCTIONS | 0
SQL_TABLE_TERM | "TABLE"
SQL_TIMEDATE_ADD_INTERVALS | 0
SQL_TIMEDATE_DIFF_INTERVALS | 0
SQL_TIMEDATE_FUNCTIONS | Multiple values
SQL_TXN_CAPABLE | SQL_TC_NONE
SQL_TXN_ISOLATION_OPTION | 0
SQL_UNION | Multiple values
SQL_USER_NAME | ""


Other Paradox Driver Programming Details

This section contains the following topics:

  • SQLColAttributes
  • SQLColumns
  • SQLConfigDataSource
  • SQLDriverConnect
  • SQLGetInfo
  • SQLGetTypeInfo
  • SQLSetConnectOption
  • SQLStatistics
  • SQLTables
  • SQLTransact
  • CREATE INDEX for Paradox
  • Multiple hstmts


SQLConfigDataSource

The SQLConfigDataSource function that is used to add, modify, or delete a data source dynamically uses the following keywords.

Keyword | Description
COLLATINGSEQUENCE | The sequence in which the fields are sorted. When the Paradox driver is used, the sequence can be ASCII (default), International, Swedish-Finnish, or Norwegian-Danish. This sets the same option as Collating Sequence in the setup dialog box.
DBQ | The name of the database file. This sets the same option as Database in the setup dialog box.
DEFAULTDIR | The path specification to the directory.
DESCRIPTION | A description of the data in the data source. This sets the same option as Description in the setup dialog box.
DRIVER | The path specification to the driver DLL.
DRIVERID | An integer ID for the driver. 26 (Paradox 3.x) 282 (Paradox 4.x) 538 (Paradox 5.x)
EXCLUSIVE | Determines whether the database will be opened in exclusive mode (accessed by only one user at a time) or shared mode (accessed by more than one user at a time). Can be true (exclusive mode) or false (shared mode). This sets the same option as Exclusive in the setup dialog box.
FIL | File type Paradox 3.x, Paradox 4.x, or Paradox 5.x
FILETYPE | File type for the Text driver (Text).
PAGETIMEOUT | Specifies the period of time, in tenths of a second, that a page (if not used) remains in the buffer before being removed. The default is 600 tenths of a second (60 seconds). Note that this option applies to all data sources that use the ODBC driver. This sets the same option as Page Timeout in the setup dialog box.
PARADOXNETPATH | The full path of the directory containing a Paradox lock database, because it contains either the PDOXUSRS.net file (in Paradox 4.x) or the PARADOX.net file (in Paradox 5.x). If the directory does not contain one of these files, the Paradox driver creates one. For information about these files, see the Paradox documentation. Before a network directory can be selected, a Paradox user name must be entered. This sets the same option as Select Network Directory in the setup dialog box.
PARADOXNETSTYLE | For the Paradox driver, the network access style to use when accessing Paradox data: either "3.x" for Paradox 3.x or "4.x" for Paradox 4.x or 5.x. Can be set to "3.x" or "4.x" if the version is Paradox 4.x or 5.x; if the version is Paradox 3.x, the style must be "3.x". This sets the same option as Net Style in the setup dialog box.
PARADOXUSERNAME | For the Paradox driver, the Paradox user name. This sets the same option as User Name in the setup dialog box.
PWD | The password. This is an optional keyword and will never be written to the file by the driver. It is used in a call to SQLDriverConnect against password-secured Paradox files. The password used will be valid whenever a table is opened. If no password is passed in the connection string, no password is established for that table. If tables have different passwords, more than one cannot be opened in the same session, nor can the tables be joined.
READONLY | TRUE to make file read-only; FALSE to make file not read-only. This sets the same option as Read Only in the setup dialog box.
THREADS | The number of background threads for the engine to use. This value is 3, and cannot be changed. This sets the same option as Threads in the setup dialog box.


SQLDriverConnect

SQLDriverConnect enables you to connect to a driver without creating a data source (DSN).

The following keywords are supported in the connection string for all drivers: DSN, DBQ, and FIL.

The PWD keyword is also supported. The PWD keyword should not include any of the special characters (see SQL_SPECIAL_CHARACTERS in SQLGetInfo Returned Values).

After a password-protected file has been opened by a user, other users are not allowed to open the same file.

The following table shows the minimum keywords required to connect to each driver, and provides an example of keyword/value pairs used with SQLDriverConnect. For a full list of DRIVERID values, see SQLConfigDataSource.

Note If DBQ or DefaultDir is not specified for the Paradox driver, the driver will connect to the current directory.

Driver | Keywords required | Example
Paradox | Driver, DriverID | Driver={Microsoft Paradox Driver (*.db )}; DBQ=c:\temp;DriverID=26


SQLGetInfo

SQLGetInfo supports the SQL_FILE_USAGE information type. The returned value is a 16-bit integer that indicates how the driver directly treats files in a data source:

  • SQL_FILE_NOT_SUPPORTED—The driver is not a single-tier driver.
  • SQL_FILE_TABLE—A single-tier driver treats files in a data source as tables.
  • SQL_FILE_QUALIFIER—A single-tier driver treats files in a data source as a qualifier.

The ODBC driver returns SQL_FILE_TABLE because each file is a table.


SQLGetTypeInfo

The name of the type (TYPE_NAME) returned in the table produced by SQLGetTypeInfo will be the name most commonly used by the data source.

SQL_ALL_EXCEPT_LIKE will be returned in the SEARCHABLE column for the Byte, Counter, Double, Single, Long, and Short data types. (The LIKE capability can be achieved by converting the value to a character using the ODBC canonical conversion functions, and then performing the comparison.)


SQLSetConnectOption

fOption | Comment
SQL_ACCESS_MODE | The SQL_ACCESS_MODE fOption can be set to either SQL_MODE_READ_ONLY or SQL_MODE_READ_WRITE. However, the driver does not prevent updates if SQL_ACCESS_MODE is set to SQL_MODE_READ_ONLY.
SQL_AUTOCOMMIT | The Paradox driver only supports SQL_AUTOCOMMIT being set to ON (the default state), because they do not support transactions.
SQL_CURRENT_QUALIFIER | Supported.
SQL_LOGIN_TIMEOUT | Not supported.
SQL_OPT_TRACE | Supported.
SQL_OPT_TRACEFILE | Supported.
SQL_PACKET_SIZE | Not supported.
SQL_QUIET_MODE | Not supported.
SQL_TRANSLATE_DLL | Not supported.
SQL_TRANSLATION_OPTION | Not supported.
SQL_TXN_ISOLATION | Not supported.


SQLTables

Argument | Comments
szTableOwner | The only valid argument for szTableOwner is NULL because none of the drivers support owner names. With szTableOwner set to NULL, all tables are returned. NULL is returned in the TABLE_OWNER column.
szTableQualifier | In the TABLE_QUALIFIER column, SQLTables will return the path to a directory.
SzTableType | For Paradox files, "TABLE" is the only table type supported.


CREATE INDEX for Paradox

The syntax of the CREATE INDEX statement for the ODBC Paradox driver is:

CREATE [UNIQUE] INDEX index-name
ON table-name
(column-identifier [ASC]
[, column-identifier [ASC]...])

The ODBC Paradox driver does not support the DESC keyword in the ODBC SQL grammar for the CREATE INDEX statement. The table-name argument can specify the full path of the table.

If the keyword UNIQUE is specified, the ODBC Paradox driver will create a unique index. The first unique index is created as a primary index. This is a Paradox primary key file named table-name.PX. Primary indexes are subject to the following restrictions:

  • The primary index must be created before any rows are added to the table.
  • A primary index must be defined upon the first "n" columns in a table.
  • Only one primary index is allowed per table.
  • A table cannot be updated by the Paradox driver if a primary index is not defined on the table. (Note that this is not true for an empty table, which can be updated even if a unique index is not defined on the table.)
  • The index-name argument for a primary index must be the same as the base name of the table, as required by Paradox.

If the keyword UNIQUE is omitted, the ODBC Paradox driver will create a non-unique index. This consists of two Paradox secondary index files named table-name.Xnn and table-name.Ynn, where nn is the number of the column in the table. Non-unique indexes are subject to the following restrictions:

  • Before a non-unique index can be created for a table, a primary index must exist for that table.
  • For Paradox 3.x, the index-name argument for any index other than a primary index (unique or non-unique) must be the same as the column name. For Paradox 4.x and 5.x, the name of such an index can be, but doesn't have to be, the same as the column name.
  • Only one column can be specified for a non-unique index.

Columns cannot be added once an index has been defined on a table. If the first column of the argument list of a CREATE TABLE statement creates an index, a second column cannot be included in the argument list.

For example, to use the sales order number and line number columns as the unique index on the SO_LINES table, use the statement:

CREATE UNIQUE INDEX SO_LINES
 ON SO_LINES (SONum, LineNum)

To use the part number column as a non-unique index on the SO_LINES table, use the statement:

CREATE INDEX PartNum
 ON SO_LINES (PartNum)

Note that when two CREATE INDEX statements are performed, the first statement will always create a primary index with the same name as the table and the second statement will always create a non-unique index with the same name as the column. These indexes will be named this way even if different names are entered in the CREATE INDEX statements and even if the index is labeled UNIQUE in the second CREATE INDEX statement.

Note When you use the Paradox driver without implementing the Borland Database Engine, only read and append statements are allowed.


Multiple hstmts

When the ODBC Paradox driver is used, if you want to use more than one hstmt to execute queries on a table, the table must have a unique index (Paradox primary key).


dBASE Driver Programming Considerations

This section contains the following topics:

  • Setting Options Programmatically for the dBASE Driver
  • dBASE Data Types
  • SQLGetInfo Returned Values for dBASE
  • Other dBASE Driver Programming Details

Note Accessing dBASE ISAM files through the ODBC Desktop Database Drivers requires installation of the Borland database engine.


Setting Options Programmatically for the dBASE Driver

Option | Description | Method
Approximate Row Count | Determines whether table size statistics are approximated. This option applies to all data sources that use the ODBC driver. | To set this option dynamically, use the STATISTICS keyword in a call to SQLConfigDataSource.
Collating Sequence The sequence in which the fields are sorted.

The sequence can be: ASCII (the default) or International.

 To set this option dynamically, use the COLLATINGSEQUENCE keyword in a call to SQLConfigDataSource.
Data Source Name | A name that identifies the data source, such as Payroll or Personnel. | To set this option dynamically, use the DSN keyword in a call to SQLConfigDataSource.
Database | A Microsoft Access data source can be set up without selecting or creating a database. If no database is provided upon setup, the user will be prompted to choose a database file when connecting to the data source. | To set this option dynamically, use the DBQ keyword in a call to SQLConfigDataSource.
Description | An optional description of the data in the data source; for example, "Hire date, salary history, and current review of all employees." | To set this option dynamically, use the DESCRIPTION keyword in a call to SQLConfigDataSource.
Exclusive | If the Exclusive box is selected, the database will be opened in Exclusive mode and can be accessed by only one user at a time. Performance is enhanced when running in Exclusive mode. | To set this option dynamically, use the EXCLUSIVE keyword in a call to SQLConfigDataSource.
Page Timeout Specifies the period of time, in tenths of a second, that a page (if not used) remains in the buffer before being removed. The default is 600 tenths of a second (60 seconds). This option applies to all data sources that use the ODBC driver.

The page timeout cannot be 0 because of an inherent delay. The page timeout cannot be less than the inherent delay, even if the page timeout option is set below that value.

 To set this option dynamically, use the PAGETIMEOUT keyword in a call to SQLConfigDataSource.
Read Only | Designates the database as read-only. | To set this option dynamically, use the READONLY keyword in a call to SQLConfigDataSource.
Select Directory Displays a dialog box where you can select a directory containing the files you want to access.

When defining a data source directory specify the directory where your most commonly used files are located. The ODBC driver uses this directory as the default directory. Copy other files into this directory if they are used frequently. Alternatively, you can qualify file names in a SELECT statement with the directory name:

SELECT * FROM C:\MYDIR\EMP

Or, you can specify a new default directory by using the SQLSetConnectOption function with the SQL_CURRENT_QUALIFIER option.

 To set this option dynamically, use the DEFAULTDIR keyword in a call to SQLConfigDataSource.
Show Deleted Rows | Specifies whether or not rows that have been marked as deleted can be retrieved or positioned on. If cleared, deleted rows are not displayed; if selected, deleted rows are treated the same as non-deleted rows. The default is cleared. | To set this option dynamically, use the DELETED keyword in a call to SQLConfigDataSource.


dBASE Data Types

The following table shows how dBASE data types are mapped to ODBC SQL data types. Note that not all ODBC SQL data types are supported.

dBASE data type | ODBC data type
CHAR | SQL_VARCHAR
DATE | SQL_DATE
FLOAT[1] | SQL_DOUBLE
LOGICAL | SQL_BIT
MEMO | SQL_LONGVARCHAR
NUMERIC (BCD) | SQL_DOUBLE
OLEOBJECT[1] | SQL_LONGBINARY

[1] Valid only for dBASE version 5.x

Precision in dBASE III allows numbers with up to two-digit exponents and in dBASE IV numbers with up to three-digit exponents. Because numbers are stored as text, they are converted to numbers. If the number to convert does not fit in a field, unexplained results may occur.

While dBASE allows a precision and a scale to be specified with a NUMERIC data type, it is not supported by the ODBC dBASE driver. The ODBC dBASE driver always returns a precision of 15 and a scale of 0 for a NUMERIC data type.

A column created with the Numeric data type using the ODBC dBASE driver maps to the SQL_DOUBLE ODBC data type. Thus the data in this column is subject to rounding. This behavior is not the same as that of the NUMERIC data type in dBASE (type N), which is Binary Coded Decimal (BCD).

Note SQLGetTypeInfo returns ODBC SQL data types. All conversions in Appendix D of the ODBC Programmer's Reference are supported for the ODBC SQL data types listed earlier in this topic.

The following table shows limitations on dBASE data types.

Data type | Description
CHAR | Creating a CHAR column of zero or unspecified length actually returns a 254-byte column.
Encrypted data | The dBASE driver does not support encrypted dBASE tables.
LOGICAL | The dBASE driver cannot create an index on a LOGICAL column.
MEMO | The maximum length of a MEMO column is 65,500 bytes.

More limitations on data types can be found in Data Type Limitations.


SQLGetInfo Returned Values for dBASE

The following table lists the C-language #defines for the fInfoType argument and the corresponding values returned by SQLGetInfo. This information can be retrieved by passing the listed C-language #defines to SQLGetInfo in the fInfoType argument. For more information about the values returned by SQLGetInfo, see the ODBC Programmer's Reference.

Note Where SQLGetInfo returns a 32-bit bitmask, a vertical bar (|) represents a bitwise OR.

InfoType | Returned value
SQL_ACCESSIBLE_PROCEDURES | "N"
SQL_ACCESSIBLE_TABLES | "Y"
SQL_ACTIVE_ENVIRONMENTS | 0
SQL_AGGREGATE_FUNCTIONS | All set
SQL_ALTER_DOMAIN | 0
SQL_ALTER_TABLE | Multiple values
SQL_ASYNC_MODE | 0
SQL_BATCH_ROW_COUNT | 0
SQL_BATCH_SUPPORT | 0
SQL_BOOKMARK_PERSISTENCE | Multiple values
SQL_CATALOG_LOCATION | SQL_QL_START
SQL_CATALOG_NAME | "Y"
SQL_CATALOG_NAME_SEPARATOR | "\"
SQL_CATALOG_TERM | "Directory"
SQL_CATALOG_USAGE | Multiple values
SQL_COLLATION_SEQ | ""
SQL_COLUMN_ALIAS | "Y"
SQL_CONCAT_NULL_BEHAVIOR | SQL_CB_NON_NULL
SQL_CONVERT_BIGINT | 0
SQL_CONVERT_BINARY | Multiple values
SQL_CONVERT_BIT | 0
SQL_CONVERT_CHAR | Multiple values
SQL_CONVERT_DATE | Multiple values
SQL_CONVERT_DECIMAL | 0
SQL_CONVERT_DOUBLE | Multiple values
SQL_CONVERT_FLOAT | Multiple values
SQL_CONVERT_FUNCTIONS | SQL_FN_CVT_CONVERT
SQL_CONVERT_INTEGER | Multiple values
SQL_CONVERT_LONGVARBINARY | Multiple values
SQL_CONVERT_LONGVARCHAR | Multiple values
SQL_CONVERT_NUMERIC | Multiple values
SQL_CONVERT_REAL | Multiple values
SQL_CONVERT_SMALLINT | Multiple values
SQL_CONVERT_TIME | Multiple values
SQL_CONVERT_TIMESTAMP | Multiple values
SQL_CONVERT_TINYINT | Multiple values
SQL_CONVERT_VARBINARY | Multiple values
SQL_CONVERT_VARCHAR | Multiple values
SQL_CORRELATION_NAME | SQL_CN_ANY
SQL_CREATE_ASSERTION | 0
SQL_CREATE_CHARACTER_SET | 0
SQL_CREATE_COLLATION | 0
SQL_CREATE_DOMAIN | 0
SQL_CREATE_SCHEMA | 0
SQL_CREATE_TABLE | SQL_CT_CREATE_TABLE
SQL_CREATE_TRANSLATION | 0
SQL_CREATE_VIEW | 0
SQL_CURSOR_COMMIT_BEHAVIOR | SQL_CB_CLOSE
SQL_CURSOR_ROLLBACK_BEHAVIOR | SQL_CB_CLOSE
SQL_CURSOR_SENSITIVITY | SQL_UNSPECIFIED
SQL_DATA_SOURCE_NAME | The DSN from Odbc.ini, or "" if DRIVER keyword is used in Odbc.ini
SQL_DATA_SOURCE_READ_ONLY | "N" (This depends on the data source.)
SQL_DATABASE_NAME | Current database directory
SQL_DATETIME_LITERALS | 0
SQL_DBMS_NAME | "DBASE"
SQL_DBMS_VER | Multiple values
SQL_DDL_INDEX | Multiple values
SQL_DEFAULT_TXN_ISOLATION | 0
SQL_DESCRIBE_PARAMETER | 0
SQL_DRIVER_HDBC | Handled by the Driver Manager.
SQL_DRIVER_HENV | Handled by the Driver Manager.
SQL_DRIVER_HLIB | Handled by the Driver Manager.
SQL_DRIVER_HSTMT | Handled by the Driver Manager.
SQL_DRIVER_NAME | "OdbcJt32.dll"
SQL_DRIVER_ODBC_VER | "3.51.0000"
SQL_DRIVER_VER | "4.00.nnnn" (nnnn specifies the build date)
SQL_DROP_ASSERTION | 0
SQL_DROP_CHARACTER_SET | 0
SQL_DROP_COLLATION | 0
SQL_DROP_DOMAIN | 0
SQL_DROP_SCHEMA | 0
SQL_DROP_TABLE | SQL_DT_DROP_TABLE
SQL_DROP_TRANSLATION | 0
SQL_DROP_VIEW | SQL_DV_DROP_VIEW
SQL_EXPRESSIONS_IN_ORDERBY | "Y"
SQL_FILE_USAGE | SQL_FILE_TABLE
SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 | SQL_CA1_NEXT
SQL_GETDATA_EXTENSIONS | Multiple values
SQL_GROUP_BY | SQL_GB_GROUP_BY_CONTAINS_SELECT
SQL_IDENTIFIER_CASE | SQL_IC_UPPER (The qualifier is returned in mixed case so that Windows NT can locate the directory.)
SQL_IDENTIFIER_QUOTE_CHAR | "`" (back quote)
SQL_KEYWORDS | Multiple values
SQL_LIKE_ESCAPE_CLAUSE | "N"
SQL_MAX_BINARY_LITERAL_LEN | 255
SQL_MAX_CATALOG_NAME_LEN | 66
SQL_MAX_CHAR_LITERAL_LEN | 254
SQL_MAX_COLUMN_NAME_LEN | 10
SQL_MAX_COLUMNS_IN_GROUP_BY | 10
SQL_MAX_COLUMNS_IN_INDEX | 0 (Limit Unknown or Not Applicable)
SQL_MAX_COLUMNS_IN_ORDER_BY | 10
SQL_MAX_COLUMNS_IN_SELECT | 255
SQL_MAX_COLUMNS_IN_TABLE | 255
SQL_MAX_CONCURRENT_ACTIVITIES | 0
SQL_MAX_CURSOR_NAME_LEN | 64
SQL_MAX_DRIVER_CONNECTIONS | 64
SQL_MAX_INDEX_SIZE | 220
SQL_MAX_PROCEDURE_NAME_LEN | 0
SQL_MAX_ROW_SIZE | 4000
SQL_MAX_ROW_SIZE_INCLUDES_LONG | "N"
SQL_MAX_SCHEMA_NAME_LEN | 0
SQL_MAX_STATEMENT_LEN | 65000
SQL_MAX_TABLE_NAME_LEN | 12
SQL_MAX_TABLES_IN_SELECT | 16
SQL_MAX_USER_NAME_LEN | 0
SQL_MULT_RESULT_SETS | "N"
SQL_MULTIPLE_ACTIVE_TXN | "Y"
SQL_NEED_LONG_DATA_LEN | "N"
SQL_NON_NULLABLE_COLUMNS | SQL_NNC_NON_NULL
SQL_NULL_COLLATION | SQL_NC_LOW
SQL_NUMERIC_FUNCTIONS | Multiple values
SQL_ODBC_SAG_CLI_ CONFORMANCE | SQL_OSCC_COMPLIANT
SQL_ODBC_SQL_INTEGRITY | "N"
SQL_ODBC_VER | From Driver Manager
SQL_OJ_CAPABILITIES | Multiple values
SQL_ORDER_BY_COLUMNS_IN_SELECT | "N"
SQL_OUTER_JOINS | "Y"
SQL_PROCEDURE_TERM | ""
SQL_PROCEDURES | "N"
SQL_QUOTED_IDENTIFIER_CASE | SQL_IC_MIXED
SQL_ROW_UPDATES | "N"
SQL_SCHEMA_TERM | ""
SQL_SCHEMA_USAGE | 0
SQL_SCROLL_OPTIONS | Multiple values
SQL_SEARCH_PATTERN_ESCAPE | "\"
SQL_SERVER_NAME | "DBASE"
SQL_SPECIAL_CHARACTERS | "~`@#$%^&*_-+=\}{"';:?/><,.!'[]|"
SQL_STRING_FUNCTIONS | Multiple values
SQL_SUBQUERIES | Multiple values
SQL_SYSTEM_FUNCTIONS | 0
SQL_TABLE_TERM | "TABLE"
SQL_TIMEDATE_ADD_INTERVALS | 0
SQL_TIMEDATE_DIFF_INTERVALS | 0
SQL_TIMEDATE_FUNCTIONS | Multiple values
SQL_TXN_CAPABLE | SQL_TC_NONE
SQL_TXN_ISOLATION_OPTION | 0
SQL_UNION | Multiple values
SQL_USER_NAME | ""


Other dBASE Driver Programming Details

This section contains the following topics:

  • SQLColAttributes
  • SQLColumns
  • SQLConfigDataSource
  • SQLDriverConnect
  • SQLGetInfo
  • SQLGetTypeInfo
  • SQLSetConnectOption
  • SQLStatistics
  • SQLTables
  • SQLTransact
  • dBASE Indexes


SQLConfigDataSource

The SQLConfigDataSource function that is used to add, modify, or delete a data source dynamically uses the following keywords.

Keyword | Description
COLLATINGSEQUENCE | The sequence in which the fields are sorted. The sequence can be: ASCII (the default) or International. This sets the same option as Collating Sequence in the setup dialog box.
DEFAULTDIR | The path specification to the directory.
DELETED | For the dBASE driver, specifies whether or not rows that have been marked as deleted can be retrieved or positioned on. If set to 1, deleted rows are not displayed; if set to 0, deleted rows are treated the same as non-deleted rows. This sets the same option as Show Deleted Rows in the setup dialog box.
DESCRIPTION | A description of the data in the data source. This sets the same option as Description in the setup dialog box.
DRIVER | The path specification to the driver DLL.
DRIVERID | An integer ID for the driver. 21 (dBASE III) 277 (dBASE IV) 533 (dBASE 5.0)
FIL | File type dBase III, dBase IV, or dBase 5
PAGETIMEOUT | Specifies the period of time, in tenths of a second, that a page (if not used) remains in the buffer before being removed. The default is 600 tenths of a second (60 seconds). Note that this option applies to all data sources that use the ODBC driver. This sets the same option as Page Timeout in the setup dialog box.
READONLY | TRUE to make file read-only; FALSE to make file not read-only. This sets the same option as Read Only in the setup dialog box.
STATISTICS | For the dBASE driver, determines whether table size statistics are approximated. Note that this option applies to all data sources that use the ODBC driver. This sets the same option as Approximate Row Count in the setup dialog box.
THREADS | The number of background threads for the engine to use. This value is 3 and cannot be changed. This sets the same option as Threads in the setup dialog box.


SQLDriverConnect

SQLDriverConnect enables you to connect to a driver without creating a data source (DSN).

The following keywords are supported in the connection string for all drivers: DSN, DBQ, and FIL.

When the Paradox driver is used, after a password-protected file has been opened by a user, other users are not allowed to open the same file.

The following table shows the minimum keywords required to connect to each driver, and provides an example of keyword/value pairs used with SQLDriverConnect. For a full list of DRIVERID values, see SQLConfigDataSource.

Note If DBQ or DefaultDir is not specified for the dBASE driver, the driver will connect to the current directory.

Driver | Keywords required | Examples
dBASE | Driver, DriverID | Driver={Microsoft dBASE Driver (*.dbf)}; DBQ=c:\temp; DriverID=277


SQLSetConnectOption

fOption | Comment
SQL_ACCESS_MODE | The SQL_ACCESS_MODE fOption can be set to either SQL_MODE_READ_ONLY or SQL_MODE_READ_WRITE. However, the driver does not prevent updates if SQL_ACCESS_MODE is set to SQL_MODE_READ_ONLY.
SQL_AUTOCOMMIT | The dBASE driver only supports SQL_AUTOCOMMIT being set to ON (the default state), because it does not support transactions.
SQL_CURRENT_QUALIFIER | Supported.
SQL_LOGIN_TIMEOUT | Not supported.
SQL_OPT_TRACE | Supported.
SQL_OPT_TRACEFILE | Supported.
SQL_PACKET_SIZE | Not supported.
SQL_QUIET_MODE | Not supported.
SQL_TRANSLATE_DLL | Not supported.
SQL_TRANSLATION_OPTION | Not supported.
SQL_TXN_ISOLATION | Not supported.


SQLTables

Argument | Comments
szTableOwner | The only valid argument for szTableOwner is NULL because none of the drivers supports owner names. With szTableOwner set to NULL, all tables are returned. NULL is returned in the TABLE_OWNER column.
szTableQualifier | In the TABLE_QUALIFIER column, SQLTables will return the path to a directory.
SzTableType | For dBASE files, "TABLE" is the only table type supported.


dBASE Indexes

The ODBC dBASE driver automatically opens and updates dBASE IV index files. You must use the Select Indexes dialog box displayed through the ODBC Data Source Administrator to associate dBASE III .ndx files with dBASE files.

The following limitations apply to the creation of dBASE indexes:

  • All column names must be valid.
  • All columns must be in the same ascending or descending order.
  • The length of any single text column must be less than 100 bytes.
  • If more than one column exists, all of the columns must be text columns and the sum of the column sizes must be less than 100 bytes.
  • Memo fields cannot be indexed.
  • An index must not be specified for the current set of fields (that is, duplicate indexes are not allowed).
  • The index name must match the dBASE index naming convention. dBASE III requires that each index be in a separate file, each having an .ndx extension. In dBASE IV, indexes are created as tag names that are stored in a single .mdx file. The .mdx file has the same base name as the database file (for example, Emp.mdx is the index file for the Emp.dbf database).
  • dBASE defines a unique index as one where only one record from a set with identical key values is added to the index.


Text File Driver Programming Considerations

This section contains the following topics:

  • Setting Options Programmatically for the Text File Driver
  • Text File Data Types
  • SQLGetInfo Returned Values for Text Files
  • Other Text File Driver Programming Details


Setting Options Programmatically for the Text File Driver

Option | Description | Method
Data Source Name | A name that identifies the data source, such as Payroll or Personnel. | To set this option dynamically, use the DSN keyword in a call to SQLConfigDataSource.
Define Format | Displays the Define Text Format dialog box and enables you to specify the schema for individual tables in the data source directory. | This option cannot be set dynamically by a call to SQLConfigDataSource.
Description | An optional description of the data in the data source; for example, "Hire date, salary history, and current review of all employees." | To set this option dynamically, use the DESCRIPTION keyword in a call to SQLConfigDataSource.
Directory | Selects the targeted directory. | To set this option dynamically, use the DEFAULTDIR keyword in a call to SQLConfigDataSource.
Extensions List | Lists the file name extensions of the text files on the data source. When the Text driver is used, a file with no extension is created when the CREATE TABLE statement is executed with a name that has no extension. Other drivers create a file with a default extension when no extension is provided. To create a file with a .txt extension, the extension must be included in the name. To display files without extensions in the Define Text Format dialog box, "*." must be added to the Extensions List. | To set this option dynamically, use the EXTENSIONS keyword in a call to SQLConfigDataSource.
Read Only | Designates the database as read-only. | To set this option dynamically, use the READONLY keyword in a call to SQLConfigDataSource.
Rows to Scan The number of rows to scan to determine the data type of each column. The data type is determined given the maximum number of kinds of data found. If data is encountered that does not match the data type guessed for the column, the data type will be returned as a NULL value.

For the Text driver, you may enter a number from 1 to 32767 for the number of rows to scan; however, the value will always default to 25. (A number outside the limit will return an error.)

 To set this option dynamically, use the MAXSCANROWS keyword in a call to SQLConfigDataSource.
Select Directory Displays a dialog box where you can select a directory containing the files you want to access.

When defining a data source directory specify the directory where your most commonly used files are located. The ODBC driver uses this directory as the default directory. Copy other files into this directory if they are used frequently. Alternatively, you can qualify file names in a SELECT statement with the directory name:

SELECT * FROM C:\MYDIR\EMP

Or, you can specify a new default directory by using the SQLSetConnectOption function with the SQL_CURRENT_QUALIFIER option.

 To set this option dynamically, use the DEFAULTDIR keyword in a call to SQLConfigDataSource.


Text File Data Types

The following table shows how text data types are mapped to ODBC SQL data types. Note that not all ODBC SQL data types are supported by the ODBC Text driver.

Text data type | ODBC data type
CHAR | SQL_VARCHAR
DATETIME | SQL_TIMESTAMP
FLOAT | SQL_DOUBLE
INTEGER | SQL_INTEGER
LONGCHAR | SQL_LONGVARCHAR

Note SQLGetTypeInfo returns ODBC data types. All conversions in Appendix D of the ODBC Programmer's Reference are supported for the SQL data types listed in the previous table.

The following table shows limitations on Text data types.

Data type | Description
CHAR | Creating a CHAR column of zero or unspecified length actually returns a 255-bit column. In delimited files, a CHAR column may or may not have double quotation mark delimiters at the beginning and the end; in fixed-length files, double quotation marks are not used as delimiters.
DATETIME | MM-DD-YY (for example, 01-17-92) MMM-DD-YY (for example, Jan-17-92) DD-MMM-YY (for example, 17-Jan-92) YYYY-MM-DD (for example, 1992-01-17) YYYY-MMM-DD (for example, 1992-Jan-17) Mixed date separators are not allowed within a table. The Text ISAM formats a DATETIME field in the American or European format, depending upon the International setting in the Windows Control Panel.
FLOAT | The maximum width includes the sign and decimal point. In Schema.ini, the width is denoted as follows: 14.083 is FLOAT Width 6 -14.083 is FLOAT Width 7 +14.083 is FLOAT Width 7 14083. is FLOAT Width 6 ODBC always returns 8 for FLOAT columns. FLOAT columns can also be in scientific notation, for example: -3.04E+2 is Float Width 8 25E4 is Float Width 4 Note Decimal and scientific notation cannot be mixed in a column. NULL values are represented by a blank padded string in fixed-length files, and are omitted in delimited files. Float data can be padded with leading blanks.
INTEGER | Valid values for INTEGER columns are 32767 to -32766. In Schema.ini, the width is denoted as follows: 14083 is INTEGER Width 5 0 is INTEGER Width 1 ODBC always returns 4 for INTEGER columns. The maximum width includes a sign. The maximum width of an INTEGER column is 11, although the width can be greater due to blanks that are allowed in fixed-format tables.
LONGCHAR | The theoretical limit on the width of a LONGCHAR column in either a fixed-length or delimited table is 65500K. The Text ISAM is more likely to provide reliable support up to about 32K.

More limitations on data types can be found in Data Type Limitations.


SQLGetInfo Returned Values for Text Files

The following table lists the C-language #defines for the fInfoType argument and the corresponding values returned by SQLGetInfo. This information can be retrieved by passing the listed C-language #defines to SQLGetInfo in the fInfoType argument. For more information about the values returned by SQLGetInfo, see the ODBC Programmer's Reference.

Note Where SQLGetInfo returns a 32-bit bitmask, a vertical bar (|) represents a bitwise OR.

InfoType | Returned value
SQL_ACCESSIBLE_PROCEDURES | "N"
SQL_ACCESSIBLE_TABLES | "Y"
SQL_ACTIVE_ENVIRONMENTS | 0
SQL_AGGREGATE_FUNCTIONS | All set
SQL_ALTER_DOMAIN | 0
SQL_ALTER_TABLE | 0
SQL_ASYNC_MODE | 0
SQL_BATCH_ROW_COUNT | 0
SQL_BATCH_SUPPORT | 0
SQL_BOOKMARK_PERSISTENCE | Multiple values
SQL_CATALOG_LOCATION | SQL_QL_START
SQL_CATALOG_NAME | "Y"
SQL_CATALOG_NAME_SEPARATOR | "\"
SQL_CATALOG_TERM | "Directory"
SQL_CATALOG_USAGE | Multiple values
SQL_COLLATION_SEQ | ""
SQL_COLUMN_ALIAS | "Y"
SQL_CONCAT_NULL_BEHAVIOR | SQL_CB_NON_NULL
SQL_CONVERT_BIGINT | 0
SQL_CONVERT_BINARY | Multiple values
SQL_CONVERT_BIT | 0
SQL_CONVERT_CHAR | Multiple values
SQL_CONVERT_DATE | Multiple values
SQL_CONVERT_DECIMAL | 0
SQL_CONVERT_DOUBLE | Multiple values
SQL_CONVERT_FLOAT | Multiple values
SQL_CONVERT_FUNCTIONS | SQL_FN_CVT_CONVERT
SQL_CONVERT_INTEGER | Multiple values
SQL_CONVERT_LONGVARBINARY | Multiple values
SQL_CONVERT_LONGVARCHAR | Multiple values
SQL_CONVERT_NUMERIC | Multiple values
SQL_CONVERT_REAL | Multiple values
SQL_CONVERT_SMALLINT | Multiple values
SQL_CONVERT_TIME | Multiple values
SQL_CONVERT_TIMESTAMP | Multiple values
SQL_CONVERT_TINYINT | Multiple values
SQL_CONVERT_VARBINARY | Multiple values
SQL_CONVERT_VARCHAR | Multiple values
SQL_CORRELATION_NAME | SQL_CN_ANY
SQL_CREATE_ASSERTION | 0
SQL_CREATE_CHARACTER_SET | 0
SQL_CREATE_COLLATION | 0
SQL_CREATE_DOMAIN | 0
SQL_CREATE_SCHEMA | 0
SQL_CREATE_TABLE | SQL_CT_CREATE_TABLE
SQL_CREATE_TRANSLATION | 0
SQL_CREATE_VIEW | 0
SQL_CURSOR_COMMIT_BEHAVIOR | SQL_CB_CLOSE
SQL_CURSOR_ROLLBACK_BEHAVIOR | SQL_CB_CLOSE
SQL_CURSOR_SENSITIVITY | SQL_UNSPECIFIED
SQL_DATA_SOURCE_NAME | The DSN from Odbc.ini, or "" if DRIVER keyword is used in Odbc.ini
SQL_DATA_SOURCE_READ_ONLY | "Y"
SQL_DATABASE_NAME | Current database directory
SQL_DATETIME_LITERALS | 0
SQL_DBMS_NAME | "TEXT"
SQL_DBMS_VER | ISAM: Text Version: 1.0 Format of version number: 01.00.0000
SQL_DDL_INDEX | 0
SQL_DEFAULT_TXN_ISOLATION | 0
SQL_DESCRIBE_PARAMETER | 0
SQL_DRIVER_HDBC | Handled by the Driver Manager.
SQL_DRIVER_HENV | Handled by the Driver Manager.
SQL_DRIVER_HLIB | Handled by the Driver Manager.
SQL_DRIVER_HSTMT | Handled by the Driver Manager.
SQL_DRIVER_NAME | "OdbcJt32.dll"
SQL_DRIVER_ODBC_VER | "3.51.0000"
SQL_DRIVER_VER | "4.00.nnnn" (nnnn specifies the build date)
SQL_DROP_ASSERTION | 0
SQL_DROP_CHARACTER_SET | 0
SQL_DROP_COLLATION | 0
SQL_DROP_DOMAIN | 0
SQL_DROP_SCHEMA | 0
SQL_DROP_TABLE | SQL_DT_DROP_TABLE
SQL_DROP_TRANSLATION | 0
SQL_DROP_VIEW | SQL_DV_DROP_VIEW
SQL_EXPRESSIONS_IN_ORDERBY | "Y"
SQL_FILE_USAGE | SQL_FILE_TABLE
SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 | SQL_CA1_NEXT
SQL_GETDATA_EXTENSIONS | Multiple values
SQL_GROUP_BY | SQL_GB_GROUP_BY_CONTAINS_SELECT
SQL_IDENTIFIER_CASE | SQL_IC_MIXED
SQL_IDENTIFIER_QUOTE_CHAR | "`" (back quote)
SQL_KEYWORDS | Multiple values
SQL_LIKE_ESCAPE_CLAUSE | "N"
SQL_MAX_BINARY_LITERAL_LEN | 255
SQL_MAX_CATALOG_NAME_LEN | 66
SQL_MAX_CHAR_LITERAL_LEN | 255
SQL_MAX_COLUMN_NAME_LEN | 64
SQL_MAX_COLUMNS_IN_GROUP_BY | 10
SQL_MAX_COLUMNS_IN_INDEX | 0
SQL_MAX_COLUMNS_IN_ORDER_BY | 10
SQL_MAX_COLUMNS_IN_SELECT | 255
SQL_MAX_COLUMNS_IN_TABLE | 255
SQL_MAX_CONCURRENT_ACTIVITIES | 0
SQL_MAX_CURSOR_NAME_LEN | 64
SQL_MAX_DRIVER_CONNECTIONS | 64
SQL_MAX_INDEX_SIZE | 0
SQL_MAX_PROCEDURE_NAME_LEN | 0
SQL_MAX_ROW_SIZE | 65535
SQL_MAX_ROW_SIZE_INCLUDES_LONG | "Y"
SQL_MAX_SCHEMA_NAME_LEN | 0
SQL_MAX_STATEMENT_LEN | 65000
SQL_MAX_TABLE_NAME_LEN | 12
SQL_MAX_TABLES_IN_SELECT | 16
SQL_MAX_USER_NAME_LEN | 0
SQL_MULT_RESULT_SETS | "N"
SQL_MULTIPLE_ACTIVE_TXN | "Y"
SQL_NEED_LONG_DATA_LEN | "N"
SQL_NON_NULLABLE_COLUMNS | SQL_NNC_NON_NULL
SQL_NULL_COLLATION | SQL_NC_LOW
SQL_NUMERIC_FUNCTIONS | Multiple values
SQL_ODBC_SAG_CLI_ CONFORMANCE | SQL_OSCC_COMPLIANT
SQL_ODBC_SQL_INTEGRITY | "N"
SQL_ODBC_VER | From Driver Manager
SQL_OJ_CAPABILITIES | Multiple values
SQL_ORDER_BY_COLUMNS_IN_SELECT | "N"
SQL_OUTER_JOINS | "Y"
SQL_PROCEDURE_TERM | ""
SQL_PROCEDURES | "N"
SQL_QUOTED_IDENTIFIER_CASE | SQL_IC_MIXED
SQL_ROW_UPDATES | "N"
SQL_SCHEMA_TERM | ""
SQL_SCHEMA_USAGE | 0
SQL_SCROLL_OPTIONS | Multiple values
SQL_SEARCH_PATTERN_ESCAPE | "\"
SQL_SERVER_NAME | "TEXT"
SQL_SPECIAL_CHARACTERS | "~`@#$%^&*_-+=\}{"';:?/><,.!'[]|"
SQL_STRING_FUNCTIONS | Multiple values
SQL_SUBQUERIES | Multiple values
SQL_SYSTEM_FUNCTIONS | 0
SQL_TABLE_TERM | "TABLE"
SQL_TIMEDATE_ADD_INTERVALS | 0
SQL_TIMEDATE_DIFF_INTERVALS | 0
SQL_TIMEDATE_FUNCTIONS | Multiple values
SQL_TXN_CAPABLE | SQL_TC_NONE
SQL_TXN_ISOLATION_OPTION | 0
SQL_UNION | Multiple values
SQL_USER_NAME | ""


Other Text File Driver Programming Details

This section contains the following topics:

  • Defining Text Format
  • Schema
  • Schema.ini File
  • Text File Format
  • SQLColAttributes
  • SQLColumns
  • SQLConfigDataSource
  • SQLDriverConnect
  • SQLGetInfo
  • SQLGetTypeInfo
  • SQLSetConnectOption
  • SQLStatistics
  • SQLTables
  • SQLTransact
  • Creating and Opening Tables
  • Read-Only Status


Defining Text Format

When the Text driver is used, you can use the Define Text Format dialog box to define the format for columns in a selected file. This dialog box enables you to specify the schema for each data table. This information is written to a Schema.ini file in the data source directory. A separate Schema.ini file is created for each text data source directory.

Note The same default file format applies to all new text data tables. All files created by the CREATE TABLE statement inherit those same default format values, which are set by selecting file format values in the Define Text Format dialog box with <default> chosen in the Tables list. The Text driver does not change the format of an existing text file to match the format defined in this dialog box, but returns an error when it uses the format, such as when it attempts to retrieve data from the text file.

The following options are available in the Define Text Format dialog box:

Option | Information
Add | Adds a column using the values in Data Type, Name, and Width from the dialog box, and if applicable, the Date Separator value from Schema.ini.
Characters | ANSI or OEM. OEM specifies a non-ANSI character set. This defaults to OEM if the format of the item selected in the Tables list has not been previously defined by this dialog box.
Column Name Header | Indicates whether the columns of the first row of the selected table are to be used as column names. Either TRUE or FALSE. Defaults to FALSE if the format of the item selected in the Tables list has not been previously defined by this dialog box.
Columns | Lists the column names for each column in the selected table. The order of the columns reflects the order of the columns in the table. This list is enabled if a file has been selected in the Tables list.
Data Type | Can be BIT, BYTE, CHAR, CURRENCY, DATE, FLOAT, INTEGER, LONGCHAR, SHORT, or SINGLE. Date data types can be in the following formats: "dd-mmm-yy", "mm-dd-yy", "mmm-dd-yy", "yyyy-mm-dd", or "yyyy-mmm-dd". "mm" denotes numbers for months; "mmm" denotes letters for months.
Delimiter | Specifies the custom delimiter character to be used to separate columns. Enabled when the Custom Delimited format is selected. The delimiter can be only one character in length, and double quotation marks (") cannot be used as the delimiter character. (The delimiter cannot be specified in hexadecimal or decimal format.)
Format Either delimited or fixed length. If delimited, indicates the type of delimiter used: comma (CSV), tab, or special character (custom). This defaults to CSV Delimited if the format of the item selected in the Tables list has not been previously defined by this dialog box.

If Format is fixed-length and Column Name Header is TRUE, the first line must be comma-delimited.
Guess Automatically generates the column's data type, name, and width values for the columns in the selected table by scanning the table's contents according to the Format box selection. Enabled when the table format is delimited. Any previously defined columns in the Columns list are cleared and replaced with new entries. If Column Name Header is not selected, column names are generated automatically as "F1", "F2", and so on. No default value is shown in the Data Type box.

This functionality works only on columns that are less than 64,513 bytes.
Modify | Modifies the selected column using the values in Data Type, Name, and Width.
Name Displays the name of the selected column. Can be used to specify a new column name for either an existing column or a new column.

If Column Name Header is TRUE, the column name displayed is ignored.
Remove | Deletes the selected column.
Rows to Scan The number of rows that Setup or the driver will scan when setting the columns and column data types based upon existing data.

You can enter a number from 1 to 32767 for the number of rows to scan. This defaults to 25 if the format of the item selected in the Tables list has not been previously defined by this dialog box. (A number outside the limit will return an error.)
Tables Contains a list of all files in the directory selected in the Text Setup dialog box that match the list of extensions specified.

When <default> is selected, and one of the following is true, the values of the table attributes in the Tables group are written to Schema.ini (no other entries in Schema.ini are touched):

  • There is no Schema.ini in the specified directory.
  • The Schema.ini file exists, but there is no section in Schema.ini for one of the Text files (with the specified extension) in the directory.
  • The section for a Text file exists in Schema.ini, but the body is empty.

When <default> is selected, the Columns group is disabled.
Width The width of the column can be changed for CHAR or LONGCHAR columns. The width defaults to 1 if the format of the item selected in the Tables list has not been previously defined by this dialog box.

For other data types, the width control is disabled and no value is displayed.


Schema

The schema includes information about each table (text file) in a data source, including the table's format, the number of rows to scan to determine column types, whether the first row of the table contains column names, whether the source file is written using an OEM or ANSI codepage, and each column's name, data type, and width.


Schema.ini File

When the Text driver is used, the format of the text file is determined by using a schema information file. The schema information file, which is always named Schema.ini and always kept in the same directory as the text data source, provides the IISAM with information about the general format of the file, the column name and data type information, and a number of other data characteristics. A Schema.ini file is always required for accessing fixed-length data; you should use a Schema.ini file when your text table contains DateTime, Currency, or Decimal data or any time you want more control over the handling of the data in the table.

Note The Text ISAM will obtain initial values from the registry, not from Schema.ini. The same default file format applies to all new text data tables. All files created by the CREATE TABLE statement inherit those same default format values, which are set by selecting file format values in the Define Text Format dialog box with <default> chosen in the Tables list. If the values in the registry are different from the values in Schema.ini, the values in the registry will be overwritten by the values from Schema.ini.

Understanding Schema.ini Files

Schema.ini files provide schema information about the records in a text file. Each Schema.ini entry specifies one of five characteristics of the table:

  • The text file name
  • The file format
  • The field names, widths, and types
  • The character set
  • Special data type conversions

The following sections discuss these characteristics.

Specifying the File Name

The first entry in Schema.ini is always the name of the text source file enclosed in square brackets. The following example illustrates the entry for the file Sample.txt:

[Sample.txt]

Specifying the File Format

The Format option in Schema.ini specifies the format of the text file. The Text IISAM can read the format automatically from most character-delimited files. You can use any single character as a delimiter in the file except the double quotation mark ("). The Format setting in Schema.ini overrides the setting in the Windows Registry on a file-by-file basis. The following table lists the valid values for the Format option.

Format specifier | Table format | Schema.ini Format statement
Tab Delimited | Fields in the file are delimited by tabs. | Format=TabDelimited
CSV Delimited | Fields in the file are delimited by commas (comma-separated values). | Format=CSVDelimited
Custom Delimited | Fields in the file are delimited by any character you choose to input into the dialog box. All except the double quote (") are allowed, including blank. | Format=Delimited(custom character) -or- With no delimiter specified: Format=Delimited( )
Fixed Length | Fields in the file are of a fixed length. | Format=FixedLength

Specifying the Fields

You can specify field names in a character-delimited text file in two ways:

  • Include the field names in the first row of the table and set ColNameHeader to True.
  • Specify each column by number and designate the column name and data type.

You must specify each column by number and designate the column name, data type, and width for fixed-length files.

Note The ColNameHeader setting in Schema.ini overrides the FirstRowHasNames setting in the Windows Registry on a file-by-file basis.

The data types of the fields can also be determined. Use the MaxScanRows option to indicate how many rows should be scanned when determining the column types. If you set MaxScanRows to 0, the entire file is scanned. The MaxScanRows setting in Schema.ini overrides the setting in the Windows Registry on a file-by-file basis.

The following entry indicates that Microsoft Jet should use the data in the first row of the table to determine field names and should examine the entire file to determine the data types used:

ColNameHeader=True
MaxScanRows=0

The next entry designates fields in a table by using the column number (Col n) option, which is optional for character-delimited files and required for fixed-length files. The example shows the Schema.ini entries for two fields, a 10-character CustomerNumber text field and a 30-character CustomerName text field:

Col1=CustomerNumber Text Width 10
Col2=CustomerName Text Width 30

The syntax of Col n is:

Coln=ColumnName type [Width #]

The following table describes each part of the Col n entry.

Parameter | Description
ColumnName | The text name of the column. If the column name contains embedded spaces, you must enclose it in double quotation marks.
type | Data types are: Microsoft Jet data types Bit Byte Short Long Currency Single Double DateTime Text Memo ODBC data types Char (same as Text) Float (same as Double) Integer (same as Short) LongChar (same as Memo) Date date format
Width | The literal string value Width. Indicates that the following number designates the width of the column (optional for character-delimited files; required for fixed-length files).
# | The integer value that designates the width of the column (required if Width is specified).

Selecting a Character Set

You can select from two character sets: ANSI and OEM. The CharacterSet setting in Schema.ini overrides the setting in the Windows Registry on a file-by-file basis. The following example shows the Schema.ini entry that sets the character set to ANSI:

CharacterSet=ANSI

Specifying Data Type Formats and Conversions

The Schema.ini file contains a number of options that you can use to specify how data is converted or displayed. The following table lists each of these options.

Option | Description
DateTimeFormat | Can be set to a format string indicating dates and times. You should specify this entry if all date/time fields in the import/export are handled with the same format. All Microsoft Jet formats except A.M. and P.M. are supported. In the absence of a format string, the Windows Control Panel short date picture and time options are used.
DecimalSymbol | Can be set to any single character that is used to separate the integer from the fractional part of a number.
NumberDigits | Indicates the number of decimal digits in the fractional portion of a number.
NumberLeadingZeros | Specifies whether a decimal value less than 1 and greater than –1 should contain leading zeros; this value can either be False (no leading zeros) or True.
CurrencySymbol | Indicates the currency symbol to be used for currency values in the text file. Examples include the dollar sign ($) and Dm.
CurrencyPosFormat Can be set to any of the following values:
  • Currency symbol prefix with no separation ($1)
  • Currency symbol suffix with no separation (1$)
  • Currency symbol prefix with one character separation ($ 1)
  • Currency symbol suffix with one character separation (1 $)
CurrencyDigits | Specifies the number of digits used for the fractional part of a currency amount.
CurrencyNegFormat Can be one of the following values:
  • ($1)
  • –$1
  • $–1
  • $1–
  • (1$)
  • –1$
  • 1–$
  • 1$–
  • –1 $
  • –$ 1
  • 1 $–
  • $ 1–
  • $ –1
  • 1– $
  • ($ 1)
  • (1 $)

This example shows the dollar sign, but you should replace it with the appropriate CurrencySymbol value in the actual program.

CurrencyThousandSymbol | Indicates the single-character symbol to be used for separating currency values in the text file by thousands.
CurrencyDecimalSymbol | Can be set to any single character that is used to separate the whole from the fractional part of a currency amount.

Note If you omit an entry, the default value in the Windows Control Panel is used.


Text File Format

The ODBC Text driver supports both delimited and fixed-width text files. A text file consists of an optional header line and zero or more text lines.

Although the header line uses the same format as the other lines in the text file, the ODBC Text driver interprets the header line entries as column names, not data.

A delimited text line contains one or more data values separated by delimiters: commas, tabs, or a custom delimiter. The same delimiter must be used throughout the file. Null data values are denoted by two delimiters in a row with no data between them. Character strings in a delimited text line can be enclosed in double quotation marks (""). No blanks can occur before or after delimited values.

The width of each data entry in a fixed-width text line is specified in a schema. Null data values are denoted by blanks.

Tables are limited to a maximum of 255 fields. Field names are limited to 64 characters, and field widths are limited to 32,766 characters. Records are limited to 65,000 bytes.

A text file can be opened only for a single user. Multiple users are not supported.

The following grammar, written for programmers, defines the format of a text file that can be read by the ODBC text driver:

Format | Representation
Non-italics | Characters that must be entered as shown
italics | Arguments that are defined elsewhere in the grammar
brackets ([]) | Optional items
braces ({}) | A list of mutually exclusive choices
vertical bars (|) | Separate mutually exclusive choices
ellipses (...) | Items that can be repeated one or more times

The format of a text file is:

text-file ::=
   [delimited-header-line] [delimited-text-line]... end-of-file |
   [fixed-width-header-line] [fixed-width-text-line]... end-of-file
delimited-header-line ::= delimited-text-line
delimited-text-line ::=
   blank-line |
   delimited-data [delimiter delimited-data]... end-of-line
fixed-width-header-line ::= fixed-width-text-line
fixed-width-text-line ::=
   blank-line |
   fixed-width-data [fixed-width-data]... end-of-line
end-of-file ::= <EOF>
blank-line ::= end-of-line
delimited-data ::= delimited-string | number | date | delimited-null
fixed-width-data ::= fixed-width-string | number | date | fixed-width-null

Note The width of each column in a fixed-width text file is specified in the Schema.ini file.

end-of-line ::= <CR> | <LF> | <CR><LF>
delimited-string ::= unquoted-string | quoted-string
unquoted-string ::= [character | digit] [character | digit | quote-character]...
quoted-string ::=
   quote-character
   [character | digit | delimiter | end-of-line | embedded-quoted-string]...
   quote-character
embedded-quoted-string ::=
   quote-character quote-character
   [character | digit | delimiter | end-of-line]
   quote-character quote-character
fixed-width-string ::= [character | digit | delimiter | quote-character] ...
character ::= any character except:
   delimiter
   digit
   end-of-file
   end-of-line
   quote-character
digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
delimiter ::= , | <TAB> | custom-delimiter
custom-delimiter ::= any character except:
   end-of-file
   end-of-line
   quote-character

Note The delimiter in a custom-delimited text file is specified in the Schema.ini file.

quote-character ::= "
number ::= exact-number | approximate-number
exact-number ::= [+ | -] {unsigned-integer[.unsigned-integer] |
   unsigned-integer. |
   .unsigned-integer}
approximate-number ::= exact-number{e | E}[+ | -]unsigned-integer
unsigned-integer ::= {digit}...
date ::=
   mm date-separator dd date-separator yy |
   mmm date-separator dd date-separator yy |
   dd date-separator mmm date-separator yy |
   yyyy date-separator mm date-separator dd |
   yyyy date-separator mmm date-separator dd
mm ::= digit [digit]
dd ::= digit [digit]
yy ::= digit digit
yyyy ::= digit digit digit digit
mmm ::= Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec
date-separator ::= - | / | .
delimited-null ::=

Note For delimited files, a NULL is represented by no data between two delimiters.

fixed-width-null ::= <SPACE>...

Note For fixed-width files, a NULL is represented by spaces.


SQLColAttributes

Attribute | Comments
SQL_COLUMN_DISPLAY_SIZE | For LONGVARBINARY data, SQL_COLUMN_DISPLAY_SIZE is the maximum length of the column, not the maximum length of the column times 2.
SQL_OWNER_NAME | An empty string ("") is returned in this column because owner name is not supported.
SQL_QUALIFIER_NAME | The path to a directory is returned.
SQL_COLUMN_SEARCHABLE | LONGVARBINARY and LONGVARCHAR columns are reported as SQL_UNSEARCHABLE. Fixed-length and variable-length binary and character data types are searchable, even though LONGVARBINARY and LONGVARCHAR are not.

Note For more information about the attributes returned by SQLColAttributes, see SQLColAttributes in the ODBC Programmer's Reference.


SQLConfigDataSource

The SQLConfigDataSource function that is used to add, modify, or delete a data source dynamically uses the following keywords.

Keyword | Description
CHARACTERSET | For the Text driver, OEM or ANSI.
COLNAMEHEADER | For the Text driver, indicates whether the first record of data will specify the column names. Either TRUE or FALSE.
DEFAULTDIR | The path specification to the directory.
DESCRIPTION | A description of the data in the data source. This sets the same option as Description in the setup dialog box.
DRIVER | The path specification to the driver DLL.
DRIVERID | An integer ID for the driver. 27 (Text)
EXTENSIONS | Lists the file name extensions of the Text files on the data source. This sets the same option as Extensions List in the setup dialog box.
FIL | File type Text
FILETYPE | File type for the Text driver (Text).
FORMAT | For the Text driver, can be FIXEDLENGTH, TABDELIMITED, CSVDELIMITED (by a comma), or DELIMITED() (by the special character specified in the parentheses). The special character is one character in length and can be in character, decimal, or hexadecimal format.
MAXSCANROWS | The number of rows to be scanned when setting a column's data type based upon existing data. For the Text driver, you can enter a number from 1 to 32767 for the number of rows to scan; however, the value will always default to 25. (A number outside the limit will return an error.) This sets the same option as Rows to Scan in the setup dialog box.
READONLY | TRUE to make file read-only; FALSE to make file not read-only. This sets the same option as Read Only in the setup dialog box.


SQLDriverConnect

SQLDriverConnect enables you to connect to a driver without creating a data source (DSN).

The following keywords are supported in the connection string for all drivers: DSN, DBQ, and FIL.

The following table shows the minimum keywords required to connect to each driver, and provides an example of keyword/value pairs used with SQLDriverConnect. For a full list of DRIVERID values, see SQLConfigDataSource.

Note If DBQ or DefaultDir is not specified for the Text driver, the driver will connect to the current directory.

Driver | Keywords required | Examples
Text | Driver | Driver={Microsoft Text Driver (*.txt;*.csv)}; DefaultDir=c:\temp


SQLGetInfo

SQLGetInfo supports the SQL_FILE_USAGE information type. The returned value is a 16-bit integer that indicates how the driver directly treats files in a data source:

  • SQL_FILE_NOT_SUPPORTED—The driver is not a single-tier driver.
  • SQL_FILE_TABLE—A single-tier driver treats files in a data source as tables.
  • SQL_FILE_QUALIFIER—A single-tier driver treats files in a data source as a qualifier.

The ODBC driver returns SQL_FILE_TABLE for the Text driver, because each file is a table.


SQLGetTypeInfo

The name of the type (TYPE_NAME) returned in the table produced by SQLGetTypeInfo will be the name most commonly used by the data source.

SQL_ALL_EXCEPT_LIKE will be returned in the SEARCHABLE column for the Byte, Counter, Double, Single, Long, and Short data types. (The LIKE capability can be achieved by converting the value to a character using the ODBC canonical conversion functions, then performing the comparison.)

When the Text driver is used, SQLGetTypeInfo returns a CASE_SENSITIVE value of FALSE for the text data types (CHAR and LONGCHAR), when the data types actually are case-sensitive.


SQLSetConnectOption

fOption | Comment
SQL_ACCESS_MODE | The SQL_ACCESS_MODE fOption can be set to either SQL_MODE_READ_ONLY or SQL_MODE_READ_WRITE. However, the driver does not prevent updates if SQL_ACCESS_MODE is set to SQL_MODE_READ_ONLY.
SQL_AUTOCOMMIT | The Text driver only supports SQL_AUTOCOMMIT being set to ON (the default state), because they do not support transactions.
SQL_CURRENT_QUALIFIER | Supported.
SQL_LOGIN_TIMEOUT | Not supported.
SQL_OPT_TRACE | Supported.
SQL_OPT_TRACEFILE | Supported.
SQL_PACKET_SIZE | Not supported.
SQL_QUIET_MODE | Not supported.
SQL_TRANSLATE_DLL | Not supported.
SQL_TRANSLATION_OPTION | Not supported.
SQL_TXN_ISOLATION | Not supported.


SQLTables

Argument | Comments
szTableOwner | The only valid argument for szTableOwner is NULL because none of the drivers support owner names. With szTableOwner set to NULL, all tables are returned. NULL is returned in the TABLE_OWNER column.
szTableQualifier | In the TABLE_QUALIFIER column, SQLTables will return the path to a directory.
SzTableType | "TABLE" is the only table type supported. When the Text driver is used, the list of files returned by SQLTables is determined by the file extensions in the Extensions List box in the ODBC Text Setup dialog box.


Creating and Opening Tables

When the Text driver is used, a new table is created using the format specified in Odbcinst.ini. If not specified, tables are created in CSVDELIMITED format. By default, INTEGER columns default to 11 characters and FLOAT columns default to 22 characters. DATE columns use the YYYY-MM-DD format. CHAR and LONGCHAR columns are the width specified in the CREATE statement.


Read-Only Status

When the Text driver is used, data source tables are read-only. However, applications can perform insertions for Text tables.


Additional Supported ODBC SQL Grammar

The ODBC drivers completely support the Minimum ODBC 2.5 grammar and the following clauses in the Core and Extended ODBC 2.5 grammar. Additional information may be displayed by jumping to the ALTER TABLE Statement, Correlation Names, and Procedure Invocation topics.

  • Approximate numeric literal (Core)
  • ALTER TABLE Statement (Core)
  • Binary literals (Extended)
  • BETWEEN Predicate (Core)
  • Correlation Names (Core)
  • EXISTS predicate (Core)
  • IN (valuelist) (Core)
  • Outer Joins (Extended)
  • Procedure Invocation (Extended)
  • CREATE INDEX Statement
  • SELECT statement (Extended) (including GROUP BY, HAVING, and UNION clauses)
  • Subqueries (SUBSELECTs) (Core)
  • Date Arithmetic
  • Date, Time, and Timestamp Literals
  • DROP INDEX Statement
  • Fixed-width Text File
  • GROUP BY expression-list
  • ORDER BY expression-list
  • ORDER BY with GROUP BY
  • Scalar Functions
  • Table Names


ALTER TABLE Statement

When the Microsoft Access, dBASE, or Paradox driver is used, the ALTER TABLE statement is supported with add and drop table constraint definition clauses. (ALTER TABLE statements are not supported for the Microsoft Excel or Text drivers.)

Note When you use the Paradox driver without implementing the Borland Database Engine, ALTER TABLE statements are not supported; only read and append statements are allowed.


Correlation Names

Correlation names are fully supported, including within the table list. For example, in the following string, E1 is the correlation name for the table named Emp:

SELECT * FROM Emp E1
WHERE E1.LastName = 'Smith'


Procedure Invocation

When the Microsoft Access driver is used, procedures can be invoked from the driver by using the SQLExecDirect or SQLPrepare function with the following syntax: {CALL procedure-name [(parameter[,parameter]...)]}. Note that expressions are not supported as parameters to a called procedure.

If a procedure name includes a dash, the name must be delimited with back quotes (`).

A parameterized query can be called using the previous statement.


BETWEEN Predicate

The syntax:

expression1 BETWEEN expression2 AND expression3

returns true only if expression1 is greater than or equal to expression2 and expression1 is less than or equal to expression3.

The semantics of this syntax are different for the Desktop Database Drivers and the Microsoft Jet engine. In Microsoft Jet SQL, expression2 can be greater than expression3 so that the statement will return TRUE only if expression1 is greater than or equal to expression3, and expression1 is less than or equal to expression2.


CREATE INDEX Statement

The syntax of the CREATE INDEX statement is:

CREATE [UNIQUE] INDEX index-name ON table-name (column-identifier [ASC][DESC][, column-identifier [ASC][DESC]...]) WITH <index option list>

where <index option list> can be: PRIMARY | DISALLOW NULL | IGNORE NULL

Only the Microsoft Access driver uses the DISALLOW NULL and IGNORE NULL index options. The dBASE and Paradox drivers accept the syntax, but ignore the presence of either option.

When the Paradox driver is used, the CREATE INDEX statement creates Paradox primary key files and secondary files.

This statement is not supported by the Microsoft Excel or Text drivers.


Date Arithmetic

The driver supports adding and subtracting an integer from a DATE, TIME, or TIMESTAMP column. For a DATE column, the integer specifies the number of days to add or subtract. For a TIME or TIMESTAMP column, the integer specifies the number of seconds to add or subtract.


Date, Time, and Timestamp Literals

For maximum interoperability, applications should pass date literals in the ODBC canonical format using escape-clause syntax:

  • For date literals, {d 'value'}, where value is in the form "yyyy-mm-dd"
  • For time literals, {t 'value'}, where value is in the form "hh:mm:ss"

For timestamp literals {ts 'value'}, where value is in the form "yyyy-mm-dd hh:mm:ss[.f…]".


DROP INDEX Statement

When the Microsoft Access, dBASE, or Paradox driver is used, the syntax of the DROP INDEX statement is "DROP INDEX a on b" where "a" is the name of the index and "b" is the name of the table (not DROP INDEX index-name).

When the Paradox driver is used, the DROP INDEX statement deletes Paradox secondary index files.

The DROP INDEX statement is not supported for the Microsoft Excel or Text drivers.


Fixed-Width Text File

When the Text driver is used, the last column of a fixed-width text file can be variable length.


GROUP BY expression-list

GROUP BY supports an expression list as well as a column name. The select list of a SELECT statement that has a GROUP BY clause can only include expressions from the GROUP BY clause or set functions.


ORDER BY expression-list

Expressions can be used in the ORDER BY clause. For example, in the following clauses the table is ordered by three key expressions: a+b, c+d, and e.

SELECT * FROM emp
ORDER BY a+b,c+d,e

No ordering is allowed on set functions or an expression that contains a set function.


ORDER BY with GROUP BY

ORDER BY can be performed on any expression in the GROUP BY expression-list or any column in the result set.


Outer Joins

A SELECT statement can contain a list of OUTER JOIN clauses. Nested OUTER JOINS are supported. For more information about OUTER JOINS, see the Microsoft Jet Database Engine Programmer's Guide.


Scalar Functions

The Microsoft ODBC Desktop Database Drivers support the following scalar functions:

CONCAT | LCASE | RIGHT
CONVERT | LEFT | RTRIM
CURDATE | LENGTH | SUBSTRING
CURTIME | LOCATE | UCASE
DATABASE | LTRIM | USER
DAYOFMONTH | MOD | YEAR
DAYOFWEEK | MONTH |

For information about the arguments and return values of scalar functions, see Appendix F of the ODBC Programmer's Reference.


Table Names

When the dBASE, Microsoft Excel, Paradox, or Text driver is used, table names that occur in the FROM clause of SELECT or DELETE, after the INTO clause in INSERT, and after UPDATE, CREATE TABLE, and DROP TABLE can contain a valid path, primary name, and file name extension.

Use of a table name elsewhere in an SQL statement does not support the use of paths or extensions but will accept only the primary name (for example, EMP FROM C:\ABC\EMP).

Correlation names (aliases) can be used. For example:

SELECT *
FROM C:\ABC\EMP T1
WHERE T1.COL1 = 'aaa'


Limitations

This section describes limitations of the ODBC Desktop Database Drivers with regard to the following topics:

Functions | Statements | Clauses, types, and so on
Aggregate Function | ALTER TABLE Statement | Column Name
Scalar Function | CALL Statement | Data Type
Set Functions | CREATE INDEX Statement | Date Arithmetic
Sorting | CREATE TABLE Statement | Identifiers
CONVERT Function | DELETE Statement | Index Name
 | DROP INDEX Statement | Parameterized Query
 | DROP TABLE Statement | Reserved Words
 | INSERT Statement | AND Predicate
 | SELECT DISTINCT Statement | DISTINCT Keyword
 | SELECT Statement | FROM Clause
 | UPDATE Statement | HAVING Clause
 |  | LIKE Predicate
 |  | NOT NULL
 |  | ORDER BY Clause
 |  | Table Name
 |  | Table References
 |  | Views
 |  | WHERE Clause
 |  | WHERE CURRENT OF Clause
 |  | String


Aggregate Function Limitations

ODBC Drivers

Aggregate Function Limitation

An aggregate function and a non-aggregate column reference cannot both be used as arguments to a single SQL statement.


Column Name Limitations

Column names can contain any valid characters (for example, spaces). If column names contain any characters except letters, numbers, and underscores, the name must be delimited by enclosing it in back quotes (`).

When the Microsoft Access or Microsoft Excel driver is used, column names are limited to 64 characters, and longer names generate an error. When the Paradox driver is used, the maximum column name is 25 characters. When the Text driver is used, the maximum column name is 64 characters, and longer names are truncated.

When the dBASE driver is used, characters with an ASCII value greater than 127 are converted to underscores.

When the Microsoft Excel driver is used, if column names are present, they must be in the first row. A name that in Microsoft Excel would use the "!" character must be enclosed in back quotes (`). The "!" character is converted to the "$" character, because the "!" character is not legal in an ODBC name, even when the name is enclosed in back quotes. All other valid Microsoft Excel characters (except the pipe character (|)) can be used in a column name, including spaces. A delimited identifier must be used for a Microsoft Excel column name to include a space. Unspecified column names will be replaced with driver-generated names, for example, "Col1" for the first column.

The pipe character (|) cannot be used in a column name, whether the name is enclosed in back quotes or not.

When the Text driver is used, the driver provides a default name if a column name is not specified. For example, the driver calls the first column F1, the second column F2, and so on.


Data Type Limitations

The Microsoft ODBC Desktop Database Drivers impose the following limitations on data types:

Data type | Description
All data types | Type conversion failures might result in the affected column being set to NULL.
BINARY | Creating a zero-length BINARY column actually returns a 255-byte BINARY column.
DATE | The DATE data type cannot be converted to another data type (or itself) by the CONVERT function.
DECIMAL (Exact Numeric) | Not supported.
Floating-Point Data Types | The number of decimal places in a floating-point number may be limited by the number format set in the International section of the Windows Control Panel.
NUMERIC | Supports maximum precision and a scale of 28.
TIMESTAMP | The TIMESTAMP data type cannot be converted to itself by the CONVERT function.
TINYINT | TINYINT values are always unsigned.
Zero-Length Strings | When a dBASE, Microsoft Excel, Paradox, or Text driver is used, inserting a zero-length string into a column actually inserts a NULL instead.


Date Arithmetic Limitations

Date arithmetic is not supported for subtracting a DATE data type from a DATE data type.


Identifiers Limitations

If an identifier contains a space or a special symbol, the identifier must be enclosed in back quotes. A valid name is a string of no more than 64 characters, of which the first character must not be a space. Valid names cannot include control characters or the following special characters: ` | # * ? [ ] . ! $ .

Do not use the reserved words listed in the SQL grammar in Appendix C of the ODBC Programmer's Reference (or the shorthand form of these reserved words) as identifiers (that is, table or column names), unless you surround the word in back quotes (`).


Index Name Limitations

When the Paradox driver is used, a primary index must have the same name as the table upon which it is defined. Other unique or non-unique indexes must have the same name as the table upon which they are defined.


Parameterized Query Limitations

When the Microsoft Access driver is used, a parameterized query can be called using the following syntax: CALL query-name [(parameter[,parameter]...)].


Reserved Word Limitations

Do not use the reserved words listed in the SQL grammar in Appendix C of the ODBC Programmer's Reference (or the shorthand form of these reserved words) as identifiers (that is, table or column names), unless you surround the word in back quotes (`).


Scalar Function Limitations

Scalar functions are supported only by using the ODBC canonical format.


Set Functions Limitations

The set functions (AVG, MAX, MIN, and SUM) do not support the DISTINCT keyword.


Sorting Limitations

The maximum length of a sort key in a GROUP BY clause, ORDER BY clause, SELECT DISTINCT statement, or outer join is 255 bytes; the maximum length of all sort keys in a sort row is 65,500 bytes.


String Limitations

The maximum length of an SQL statement string is 65,000 characters.

When the Microsoft Access driver is used, only SQL-92 string constants (with single quotation marks, not double quotation marks) are supported.

The pipe character (|) cannot be used in a string, whether the character is enclosed in back quotes or not.

For maximum interoperability, applications should pass strings in parameters, rather than passing quoted strings.


Table Name Limitations

Table names can contain any valid characters (for example, spaces). If table names contain any characters except letters, numbers, and underscores, the name must be delimited by enclosing it in back quotes (`).

When the Microsoft Excel driver is used, and a table name is not qualified by a database reference, the default database is implied. If a name in Microsoft Excel includes the "!" character, it will automatically be translated to the "$" character instead.

The Microsoft Excel table name that references <filename> is supported for Microsoft Excel 3.0 and 4.0 files. The Microsoft Excel table name that references <workbook-name> is supported for Microsoft Excel 5.0, 7.0, or 97 files.

When the dBASE driver is used, characters with an ASCII value greater than 127 are converted to underscores.

When the Microsoft Access driver is used, the table name is limited to 64 characters.

When the dBASE, Microsoft Excel 3.0 or 4.0, Paradox, or Text driver is used, special MS-DOS keywords CON, AUX, LPT1, and LPT2 should not be used as table names.


Table References Limitations

A maximum of 16 table references can be included in any query statement.


Views Limitations

Not supported by the dBASE, Microsoft Excel, Paradox, or Text drivers.


ALTER TABLE Statement Limitations

When the dBASE or Paradox driver is used, once an index has been created and a new record added, the structure of the table cannot be changed by the ALTER TABLE statement unless the index is dropped and the contents of the table are deleted.

ALTER TABLE statements are not supported for the Microsoft Excel or Text drivers.

Note When you use the Paradox driver without implementing the Borland Database Engine, ALTER TABLE statements are not supported; only read and append statements are allowed.


AND Predicate Limitations

A maximum of 40 is supported.


CALL Statement Limitations

(This limitation applies to the Microsoft Access driver only.)

Expressions are not supported as parameters to a called procedure.


CONVERT Function Limitations

Type conversion failures result in the affected column being set to NULL.

Neither the DATE nor TIMESTAMP data type can be converted to another data type (or itself) by the CONVERT function.


CREATE INDEX Statement Limitations

The CREATE INDEX statement is not supported for the Microsoft Excel or Text drivers.

An index can be defined on a maximum of 10 columns. If more than 10 columns are included in a CREATE INDEX statement, the index will not be recognized and the table will be treated as though no index were created.

The dBASE driver cannot create an index on a LOGICAL column.

When the dBASE driver is used, response time on large files can be improved by building an .mdx (or .ndx) index on the column (field) specified in the WHERE clauses of a SELECT statement. Existing .mdx indexes will automatically be applied for =, >, <, >=, =<, and BETWEEN operators in a WHERE clause, and LIKE predicates, as well as in join predicates.

When the dBASE driver is used, the index created by a CREATE UNIQUE INDEX statement is actually non-unique, and duplicate values can be inserted into the indexed column. Only one record from a set with identical key values can be added to the index.

When the Paradox driver is used, a unique index must be defined upon a contiguous subset of the columns in a table, including the first column. A table cannot be updated by the Paradox driver if a unique index is not defined on the table or when the Paradox driver is used without the implementation of the Borland Database Engine.


CREATE TABLE Statement Limitations

When the Microsoft Access, Microsoft Excel, or Paradox driver is used, and the length of a text or binary column is not specified (or is specified as 0), the column length will be set to 255.

When the dBASE driver is used, and the length of a text or binary column is not specified (or is specified as 0), the column length will be set to 254.

A maximum of 255 columns is supported.

When the Microsoft Excel driver is used on a Microsoft Excel 5.0, 7.0, or 97 data source, a worksheet cannot be created with the same name as a worksheet that was previously dropped. When the Microsoft Excel driver is used to access a version 5.0, 7.0, or 97 worksheet, a DROP TABLE statement clears the worksheet, but does not delete the worksheet name.

When the Paradox driver is used, columns cannot be added once an index has been defined on a table. If the first column of the argument list of a CREATE TABLE statement creates an index, a second column cannot be included in the argument list.


DELETE Statement Limitations

The DELETE statement is not supported for the Microsoft Excel or Text driver. Note that the INSERT statement is supported for the Text driver.

The dBASE driver does not support packing a table to remove "deleted" values.

For the Paradox driver to delete a row from a table, the table must have a unique index (Paradox primary key).


DISTINCT Keyword Limitations

Not supported for Long Text fields (Microsoft Access) or Memo fields (dBASE).


DROP INDEX Statement Limitations

The DROP INDEX statement is not supported for the Microsoft Excel or Text driver.


DROP TABLE Statement Limitations

When the Microsoft Excel 5.0, 7.0, or 97 driver is used, the DROP TABLE statement clears the worksheet but does not delete the worksheet name. Because the worksheet name still exists in the workbook, another worksheet cannot be created with the same name.


FROM Clause Limitations

The maximum number of tables in a FROM clause is 16.


HAVING Clause Limitations

The maximum number of search conditions in a HAVING clause is 40.


INSERT Statement Limitations

Inserted data is truncated on the right without warning if it is too long to fit into the column.

Attempting to insert a value that is out of the range of a column's data type causes a NULL to be inserted into the column.

When a dBASE, Microsoft Excel, Paradox, or Text driver is used, inserting a zero-length string into a column actually inserts a NULL instead.

When the Microsoft Excel driver is used, if an empty string is inserted into a column, the empty string is converted to a NULL; a searched SELECT statement that is executed with an empty string in the WHERE clause will not succeed on that column.

A table is not updatable by the Paradox driver under two conditions:

  • When a unique index is not defined on the table. This is not true for an empty table, which can be updated with a single row even if a unique index is not defined on the table. If a single row is inserted in an empty table that does not have a unique index, an application cannot create a unique index or insert additional data after the single row has been inserted.
  • If the Borland Database Engine is not implemented, only read and append statements are allowed on the Paradox table.

When the Text driver is used, NULL values are represented by a blank-padded string in fixed-length files, but are represented by no spaces in delimited files. For example, in the following row containing three fields, the second field is a NULL value:

 "Smith:,, 123

When the Text driver is used, all column values can be padded with leading spaces. The length of any row must be less than or equal to 65,543 bytes.


LIKE Predicate Limitations

If data in a column is longer than 255 characters, the LIKE comparison will be based only on the first 255 characters.

A LIKE used in a procedure is supported only with constant patterns. The Desktop Database Drivers support SQL-92 LIKE pattern matching.

Use of an escape clause in a LIKE predicate is not supported.

A LIKE comparison should not be performed on a column containing data of a numeric or float data type. The results may be unpredictable. For more information, see the Microsoft Jet Database Engine Programmer's Guide.


NOT NULL Limitations

The NOT NULL constraint in the CREATE TABLE statement is not supported.


ORDER BY Clause Limitations

If a SELECT statement contains a GROUP BY clause and an ORDER BY clause, the ORDER BY clause can contain only a column in the result set or an expression in the GROUP BY clause.


SELECT DISTINCT Limitations

The DISTINCT keyword does not apply to binary data.


SELECT Statement Limitations

An aggregate-function column cannot be mixed with a non-aggregate column in a SELECT statement.

The select list of a SELECT statement that has a GROUP BY clause can only have expressions from the GROUP BY clause or set functions.

The use of an asterisk (to select all columns) in a SELECT statement containing a GROUP BY clause is not supported. The names of the columns to be selected must be specified.

The use of a vertical bar in a SELECT statement is not supported. Use a parameter in the SELECT statement if you need to refer to a data value that contains a vertical bar.

When using a column alias in a SELECT statement, the word "as" must precede the alias. For example, "SELECT col1 as a from b." Without the "as", the statement will return an error.

If an incorrect column name is entered into a SELECT statement, a SQLSTATE 07001 error, "Wrong Number of Parameters," is returned instead of a SQLSTATE S0022 error, "Column Not Found."

When the Microsoft Excel driver is used, if an empty string is inserted into a column, the empty string is converted to a NULL; a searched SELECT statement that is executed with an empty string in the WHERE clause will not succeed on that column.


UPDATE Statement Limitations

For the Paradox driver to update a table, the table must have a unique index (Paradox primary key). When you use the Paradox driver without implementing the Borland Database Engine, it is not possible to update a Paradox table.

Not supported by the Text driver.

When the Microsoft Excel driver is used, it is possible to update values, but a row cannot be deleted from a table based on a Microsoft Excel spreadsheet. As a result, the UPDATE statement is not considered officially supported by the Microsoft Excel driver. Only the INSERT statement is considered supported.


WHERE Clause Limitations

The maximum number of clauses in a WHERE clause is 40.

LONGVARBINARY and LONGVARCHAR columns can be compared to literals of up to 255 characters in length, but cannot be compared using parameters.


WHERE CURRENT OF Clause Limitations

Not supported.


Errors

When an error occurs, the Microsoft ODBC Desktop Database Drivers return the native error number, the SQLSTATE (an ODBC error code), and an error message.


Native Error

Positive native error codes are generated by the Desktop Database Drivers. Negative error codes are generated by Microsoft Jet. For more information, see the Microsoft Jet Database Engine Programmer's Guide.


SQLSTATE

For errors that occur in the data source, the ODBC driver maps the returned native error to the appropriate SQLSTATE. For errors that are detected by the driver or the Driver Manager, the ODBC driver or Driver Manager generates the appropriate SQLSTATE.


Error Messages

For errors that occur in the data source, the ODBC driver returns an error message returned to it by the ODBC File Library. For errors that occur in the ODBC driver or the Driver Manager, the driver returns an error message based on the text associated with the SQLSTATE.

Error messages have the following format:

[vendor][ODBC-component][data-source]message-text

The prefixes in brackets ([ ]) identify the location of the error. When the error occurs in the Driver Manager, data-source is not given. When the error occurs in the data source, the [vendor] and [ODBC-component] prefixes identify the vendor and name of the ODBC component that received the error from the data source.

The following table shows the error messages returned by the Driver Manager and driver ISAM:

Error message | Error location
[Microsoft][ODBC Driver Manager] message-text | Driver Manager (Odbc32.dll)
[Microsoft][ODBC driver-name] message-text | Driver ISAM (see Driver ISAMs)


Arithmetic Errors

The ODBC driver evaluates the WHERE clause in a SELECT statement as it fetches each row. If a row contains a value that causes an arithmetic error, such as divide-by-zero or numeric overflow, the driver returns all rows, but returns errors for columns with arithmetic errors. When inserting or updating, however, the ODBC driver stops inserting or updating data when the first arithmetic error is encountered.


Supported ODBC API Functions

The purpose of leveling is to inform the application what features are available to it from the driver. The Microsoft ODBC Desktop Database Drivers support all Core and Level 1 functions.

For more information about conformance levels for functions and grammar, see Conformance Levels in the ODBC Programmer's Reference.

Support of ODBC API functions can be dependent on the driver used. The following table maps the function to either the specific ODBC Desktop Database Driver or a description applicable to all drivers.

SQLBindParameter | Excel |  |  |  |
SQLColAttributes | Access | dBASE | Paradox | Text File | Excel
SQLColumns | Access | dBASE | Paradox | Text File | Excel
SQLConfigDataSource | Access | dBASE | Paradox | Text File | Excel
SQLDriverConnect | Access | dBASE | Paradox | Text File | Excel
SQLGetCursorName | All drivers |  |  |  |
SQLGetData | All drivers |  |  |  |
SQLGetInfo | Access | dBASE | Paradox | Text File | Excel
SQLGetStmtOption | All drivers |  |  |  |
SQLGetTypeInfo | Access | dBASE | Paradox | Text File | Excel
SQLMoreResults | All drivers |  |  |  |
SQLPrepare | All drivers |  |  |  |
SQLProcedureColumns | Access |  |  |  |
SQLProcedures | All drivers |  |  |  |
SQLSetConnectOption | Access | dBASE | Paradox | Text File | Excel
SQLSetCursorName | All drivers |  |  |  |
SQLSetPos | All drivers |  |  |  |
SQLSetScrollOptions | All drivers |  |  |  |
SQLSetStmtOption | All drivers |  |  |  |
SQLSpecialColumns | All drivers |  |  |  |
SQLStatistics | Access | dBASE | Paradox | Text File | Excel
SQLTables | Access | dBASE | Paradox | Text File | Excel
SQLTransact | Access | dBASE | Paradox | Text File | Excel


SQLGetCursorName

SQLGetCursorName is supported, but can only be used when the Cursor Library is used, because positioned operations are not supported in the driver.


SQLGetData

This function can retrieve data from any column, whether or not there are bound columns after it and regardless of the order in which the columns are retrieved.

Note *pcbValue in SQLGetData may return twice as many characters as actually available when binding to ANSI data longer than 510 characters on a Jet 4.0 database. Character values of 510 or less will return the actual cbValue.


SQLGetStmtOption

The bookmarks returned by an fOption of SQL_GETBOOKMARK are only valid while the query is open and are invalidated when the query is reissued. Persistent bookmarks are not supported.


SQLMoreResults

This function always returns SQL_NO_DATA_FOUND.


SQLPrepare

A statement prepared with an incorrect number of columns returns an error at execution time, not upon statement preparation.


SQLProcedures

SQLProcedures will only return rows for those procedures that have at least one argument. Procedures that have no arguments are treated as views.

Column | Comments
PROCEDURE_QUALIFIER | The path to the database file.
PROCEDURE_OWNER | NULL
PROCEDURE_NAME | Undelimited procedure name
PROCEDURE_TYPE | SQL_PT_PROCEDURE


SQLSetCursorName

Because the driver does not support a positioned update or delete by the WHERE CURRENT OF cursorname syntax, SQLSetCursorName is supported, but cannot be used for positioned updates. It can only be used when the Cursor Library is enabled and the application is using SQLExtendedFetch.


SQLSetPos

The bulk-model semantics for SQLSetPos calls with the irow argument equal to 0 are supported.

SQL_LOCK_NO_CHANGE is supported for fLock. SQL_LOCK_EXCLUSIVE and SQL_LOCK_UNLOCK are not supported.

SQLSetPos supports updatable joins. (For more information, see the Microsoft Jet Database Engine Programmer's Guide.)


SQLSetScrollOptions

Forward and static cursors are supported for SQL_CONCUR_READ_ONLY.

Only keyset-driven cursors are supported for an fConcurrency argument of SQL_CONCUR_LOCK.

An fConcurrency argument of SQL_CONCUR_ROWVER is not supported.

Dynamic cursors and mixed cursors are not supported.


SQLSetStmtOption

fOption | Comments
SQL_ASYNC_ENABLE | Asynchronous processing is not supported. The SQL_ASYNC_ENABLE fOption will return SQLSTATE S1C00 (Driver not capable).
SQL_KEYSET_SIZE | The only valid keyset size is 0, because mixed and dynamic cursors are not supported. If this value is set to any other number, it will be changed to 0 and the call will return SQL_SUCCESS_WITH_INFO and SQLSTATE 01S02 (Option value changed).
SQL_MAX_ROWS | The only valid rowset size is 0, because the Desktop Database Drivers do not support limiting the number of rows that are returned. If this value is set to any other number, it will be changed to 0 and the call will return SQL_SUCCESS_WITH_INFO and SQLSTATE 01S02 (Option value changed).
SQL_QUERY_TIMEOUT | Not supported.
SQL_ROW_NUMBER | Not supported.
SQL_SIMULATE_CURSOR | Not supported.


SQLSpecialColumns

A unique index will be returned (if one exists) for the SQL_BEST_ROWID flag in fColType. No result set will be returned for the SQL_ROWVER flag.

All row IDs have a scope of SQL_SCOPE_CURROW.

Pattern matching is not supported for either the szTableQualifier or szTableName argument.


ODBC Driver for Oracle

The Microsoft® ODBC Driver for Oracle allows you to connect your ODBC-compliant application to an Oracle database. The ODBC Driver for Oracle conforms to the Open Database Connectivity (ODBC) specification described in the ODBC Programmer's Reference. It allows access to PL/SQL packages, XA/DTC integration, and Oracle access from within Internet Information Services (IIS).

Oracle RDBMS is a multiuser relational database management system that runs with various workstation and minicomputer operating systems. IBM-compatible computers running Microsoft Windows can communicate with Oracle database servers over a network. Supported networks include Microsoft LAN Manager, NetWare, VINES, DECnet, and any network that supports TCP/IP.

The ODBC Driver for Oracle enables an application to access data in an Oracle database through the ODBC interface. The driver can access local Oracle databases or it can communicate with the network through SQL*Net. The following diagram details this application and driver architecture:

The ODBC Driver for Oracle complies with API Conformance Level 1 and SQL Conformance Level Core. It also supports some functions in API Conformance Level 2 and most of the grammar in the Core and Extended SQL conformance levels. The driver is ODBC 2.5 compliant and supports 32-bit systems. Oracle 7.3x is supported fully; Oracle8 has limited support. The ODBC Driver for Oracle does not support any of the new Oracle8 data types—Unicode data types, BLOBs, CLOBs, and so on—nor does it support Oracle's new Relational Object Model. For more information about supported data types, see Supported Data Types in this guide.

To access Oracle data, the following components are required:

  • The ODBC Driver for Oracle
  • An Oracle RDBMS database
  • Oracle Client Software

Additionally, for remote connections:

  • A network that connects the computers that run the driver and the database. The network must support SQL*Net connections.

Component Documentation

This guide contains detailed information about setting up and configuring the Microsoft ODBC Driver for Oracle and adding programmatic functionality. It also contains technical reference material.

For information regarding specific Oracle product behavior, consult the documentation that accompanies the Oracle product.

For information about setting up or configuring the Microsoft ODBC Driver for Oracle using the ODBC Data Source Administrator, see the ODBC Data Source Administrator documentation.


System Requirements

To use the Microsoft ODBC Driver for Oracle, you must have Microsoft Windows 95/98 or Microsoft Windows NT/Windows 2000 and Oracle Client Software version 7.3 or later installed on your Windows system. Oracle8 has limited support: not all data types in Oracle8 are supported, and the Object extensions to Oracle8 are not supported. For more information about supported data types, see "Supported Data Types" in this documentation.

The ODBC Driver for Oracle supports only SQL*Net 2.3 or later. Supported network software includes Microsoft LAN Manager, NetWare, VINES, DECnet, and any network that supports TCP/IP.

To enable this driver to work with your Oracle client software, the client's registry must be modified by running a registry file from a command line. Multiple instances of the client software should not run concurrently. These files are listed in the following table and are located within the same directory structure that contains your MDAC installation.

Oracle client Windows NT or Windows 9x Windows 2000
7.x
mtxoci7x_winnt.reg
mtxoci7x_win2k.reg
8.0
mtxoci80x_winnt.reg
mtxoci80x_win2k.reg
8.1
mtxoci81x_winnt.reg
mtxoci81x_win2k.reg

For more information about Oracle products, refer to your Oracle documentation.


User's Guide

The ODBC Driver for Oracle User's Guide is intended to complement the setup help that can be accessed through the ODBC Data Source Administrator. The User's Guide contains the following topics:

  • Driver Version Scheme
  • Installing the Software
  • Oracle Software Patches
  • Adding and Modifying Data Sources Using Setup
  • Testing the ODBC Connection
  • Configuring the ODBC Driver for Oracle
  • Connecting to a Data Source
  • Determining Installed Oracle Components
  • Setting the Date Format on Connection


Driver Version Scheme

The following table lists all released versions of the Microsoft ODBC Driver for Oracle.

Driver version | Build number | Availability history
1.0 | 2.00.6235 | Visual C++ 4.2 and Visual Basic 5.0, Enterprise Edition
2.0 | 2.73.7269 | Visual Studio 97 and MDAC 1.5a
2.0 updated | 2.73.7283.01 | IIS 4.0
2.0 updated | 2.73.7283.03 | MDAC 1.5b and 1.5c
2.0 updated | 2.73.7356 | ODBC 3.5 SDK
2.5 | 2.573.2927 | Visual Studio 6.0 and MDAC 2.0
2.5 updated | 2.573.3513 | SQL Server 7.0 SQL Server 6.5 SP5

Build 2.00.6235 (version 1) was the first release of the Microsoft ODBC Driver for Oracle. After the release of the first version, a new naming convention was adopted.

For example, 2.73.7283.03 can be divided into the following distinct components:

  • 2 = The version number.
  • 73 = The version of Oracle Server for which the driver was designed.
  • 7283.03 = The build number of the driver.

Note With release 2.573.2973, the naming convention has led to some confusion that 2.573 is an earlier release than 2.73, but each section of the build number should be considered individually. The number 573 is larger than 73, so it is a newer version. Also, "2.5" indicates the driver's version number.


Installing the Software

The ODBC Driver for Oracle is a component of the Microsoft Data Access Components. It accompanies other ODBC components, such as the ODBC Data Source Administrator, and should already be installed. The driver also can be found under "Drivers and Other Downloads" on the Microsoft Product Support Services Online Web site at www.microsoft.com.

Network software must be installed according to its own documentation. The ODBC Driver for Oracle requires no special installation considerations as long as the network software is supported. For more information about network software requirements, see System Requirements and the introduction of this guide.

Oracle software must be installed according to its own documentation. The ODBC Driver for Oracle generally requires no special installation considerations as long as the driver supports the version. However, to keep products compatible, install the ODBC Driver for Oracle last to ensure you have the latest version of the driver. Oracle maintains a public FTP site where it posts, among other things, patches to the Oracle server products and the client component that ships with the server products. These patches are required for the proper functioning of several Microsoft products and technologies. For more information about this site, see Oracle Software Patches.

Caution Installing Oracle software over the Microsoft Data Access Components (MDAC) may overwrite current versions of MDAC. If problems arise using ODBC components, reinstall MDAC.


Oracle Software Patches

Patches for the Oracle server products and its client component are required for the proper functioning of several Microsoft products and technologies, including the Microsoft ODBC Driver for Oracle, the Microsoft OLE DB Provider for Oracle, Internet Information Services (IIS), Component Services (or Microsoft Transaction Server, if you are using Windows NT), and so forth.

Note The following instructions may not be completely accurate because the Oracle FTP site is subject to change.

To download the Oracle software patches

  1. Connect to the public FTP site at oracle-ftp.oracle.com. The user ID is "anonymous" and the password is your e-mail address.
  2. Navigate to the following directory: /server/wgt_tech/server/windowsNT.
  3. To download patches most relevant for Windows 95, Windows 98 and Windows NT/Windows 2000, navigate to the subdirectory for your version of Oracle—7.3 or 8.0. The two subdirectories are /73patchsets and /80patchsets.
  4. To download patches for Oracle's network technology, either SQL*Net or Net8, navigate to the following directory: /network.

Accessing this FTP site from your Web browser might not work. If you experience problems, try using a "traditional" FTP client or use the DOS command prompt.

Note Because Oracle fixes bugs in current versions and then retrofits them to earlier versions using software patches, it is recommended that you download the latest patch available. This is especially true for the Oracle Server Client components. If you have questions about installing these patches, contact Oracle Support.


Adding and Modifying Data Sources Using Setup

A data source identifies a path to data that can include a network library, server, database, and other attributes—in this case, the data source is the path to an Oracle database. To connect to a data source, the Driver Manager checks the Windows registry for specific connection information.

The registry entry created by the ODBC Data Source Administrator is used by the ODBC Driver Manager and ODBC drivers. This entry contains information about each data source and its associated driver. Before you can connect to a data source, its connection information must be added to the registry.

To add and configure data sources, use the ODBC Data Source Administrator. The ODBC Administrator updates your data source connection information. As you add data sources, the ODBC Administrator updates the registry information for you.

To add a data source for Windows

  1. Open the ODBC Data Source Administrator.
  2. In the ODBC Data Source Administrator dialog box, click Add. The Create New Data Source dialog box appears.
  3. Select Microsoft ODBC for Oracle, and then click Finish. The Microsoft ODBC for Oracle Setup dialog box appears.
  4. In the Data Source Name box, type the name of the data source you want to access. It can be any name that you choose.
  5. In the Description box, type the description for the driver. This optional field describes the database driver that the data source connects to. It can be any name that you choose.
  6. In the User Name box, type your database user name (your database user ID).
  7. In the Server box, type the Database Alias or connect string for the Oracle Server engine that you want to access.
  8. Click OK to add this data source.

    Note The Data Sources dialog box appears, and the ODBC Administrator updates the registry information. The user name and connect string that you typed become the default connection values for this data source when you connect to it.

  9. Click Options to make more specifications about the ODBC Driver for Oracle setup:
    • Translation—Click Select to choose a loaded data translator. The default is <No Translator>.
    • Performance—The Include REMARKS in Catalog Functions check box specifies whether the driver returns Remarks columns for the SQLColumns result set. The ODBC Driver for Oracle provides faster access when this value is not set.

      The Include SYNONYMS in SQL Columns check box specifies whether the driver returns column information. Buffer Size specifies the size, in bytes, allocated to receive fetched data. The driver optimizes fetching so that one fetch from the Oracle Server returns enough rows to fill a buffer of the specified size. Larger values tend to increase performance when fetching a lot of data.
    • Customization—The Enforce ODBC DayOfWeek Standard check box specifies whether the result set will conform to the ODBC specified day-of-week format (Sunday = 1; Saturday = 7). If this check box is cleared, the locale-specific Oracle value is returned.

      The SQLDescribeCol always returns a value for precision check box specifies whether or not the driver should return a non-zero value for the cbColDef argument of SQLDescribeCol. This connection string attribute applies only to columns where there is no Oracle-defined scale, such as computed numeric columns and columns defined as NUMBER without a precision or scale. A SQLDescribeCol call returns 130 for the precision when Oracle does not provide that information. If this check box is cleared, the driver will return 0 for these types of columns instead.
  10. Click Add to add another data source, or click Close to exit.

To modify a data source for Windows

  1. Open the ODBC Data Source Administrator. Click the appropriate DSN tab.
  2. Select the Oracle data source you want to modify and then click Configure. The Microsoft ODBC for Oracle Setup dialog box appears.
  3. Modify the applicable data source fields, and then click OK.

When you have finished modifying the information in this dialog box, the ODBC Administrator updates the registry information.


Testing the ODBC Connection

When troubleshooting ODBC access to Oracle 7.x and Oracle8 RDBMS servers, it might be necessary to verify that the underlying SQL*Net and Oracle Protocol Adapters are correctly installed. To do this, use the Oracle-supplied utility Nettest.exe in the Orawin\Bin directory.

Nettest is a simple utility that attempts to log on to the selected server using only the installed SQL*Net software that is part of the Oracle client. The utility will ask for a login name, password, and TNS connect string. If the Oracle client is correctly installed, the utility will simply display "Ping Successful." If the login was not successful, you will need to consult with a database administrator.


Configuring the ODBC Driver for Oracle

You can control performance of the ODBC Driver for Oracle by knowing the data environment and correctly setting the parameters of the data source connection through the ODBC Data Source Administrator dialog box or through connect string parameters. The dialog box provides the following controls for connecting to a data source using the dialog box or using connect strings:

  • User DSN tab Lists the Data Source Names that are local to the computer.
  • System DSN tab Enables you to add or remove a system data source. System data sources are accessible to all users on the local machine.
  • File DSN tab Enables you to add or remove a file data source from the local machine. File data sources can be shared by all users who have the same driver installed.
  • Drivers tab Lists the installed ODBC drivers.
  • Tracing tab Enables you to specify how the ODBC Driver Manager traces calls to ODBC functions. You can configure tracing separately for each installed ODBC application.
  • Connection Pooling tab Enables you to select connection options for each installed driver.
  • About tab Lists the installed ODBC component files.

After you add a data source, you can use the ODBC Data Source Administrator dialog box to configure the access to your data source. Select a data source, and then click one of the tabs to edit or review the information.


Connecting to a Data Source

An ODBC application can pass connection information in a number of ways. For example, the application might have the driver always prompt the user for connection information. Or the application might expect a connection string that specifies the data source connection. How you connect to a data source depends on the connection method used by your ODBC application.

One common way to connect to a data source is through the Data Source dialog box. If your ODBC application is set up to use a dialog box, that dialog box is displayed and prompts you for the appropriate data source connection information.

You can also connect to a data source using the connection string.

To connect to a data source using a dialog box

  1. When the Data Source dialog box appears, select an Oracle data source and then click OK. The Connect dialog box appears.
  2. Fill in the appropriate information for the Connect dialog box, and then click OK.

Once the connection information is verified, your application can use the ODBC Driver for Oracle to access the information that the data source contains.


Determining Installed Oracle Components

To determine the Oracle components installed on your system (and their versions), navigate to the \Orainst directory under the Oracle home directory. Open one of the following text files: Nt.rgs, Win95.rgs, or Win98.rgs.

The file format is similar to the following:

0 ntinstall     all    "orainst"  "3.3.1.0.0C"  "Oracle Installer"
20 w32tcp80     adp80  "tcp80"    "8.0.5.0.0"   "Oracle TCP/IP Pro"
23 w32nmp80     adp80  "nmp80"    "8.0.5.0.0"   "Oracle Named Pipe"
26 w32util80    all    "util80"   "8.0.5.0.0"   "Oracle8 Utilities"
34 w32rsf80     all    "rsf80"    "8.0.5.0.0"   "Required Support"
47 w32netclt80  net80  "netc80"   "8.0.5.0.0"   "Oracle Net8 Client"
69 w32plus80    all    "plus80"   "8.0.5.0.0"   "SQL*Plus"

The .rgs files also include installation information and descriptions of each component.


Setting the Date Format on Connection

The new version of the Microsoft ODBC Driver for Oracle does not automatically set the date format for Oracle date fields. Previously when the driver connected, it used ALTER SESSION SET NLS_DATE_FORMAT ='YYYY-MM-DD HH:MI:SS'.

To set the date format, call ALTER SESSION SET and then perform the insert. For example:

conn.Execute "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH:MI:SS' "
sSql = "INSERT INTO DATETEST VALUES (24,'1988-12-01 10:23:03')"
conn.Execute sSql


ODBC Driver for Oracle Programmer's Reference

This section includes the following topics:

  • Connection String Format and Attributes
  • Connect Options
  • Statement Options
  • Cursor Type and Concurrency Combinations
  • Using Microsoft Component Services
  • Using Microsoft Internet Information Services
  • Using Operating System Authentication
  • Using SQLConfigDatasource with the ODBC Driver for Oracle
  • Limitations of Using Keyset-Driven Cursors


Connection String Format and Attributes

Instead of using a dialog box, some applications might require a connection string that specifies data source connection information. The connection string is made up of a number of attributes that specify how a driver connects to a data source. An attribute identifies a specific piece of information that the driver needs to know before it can make the appropriate data source connection. Each driver might have a different set of attributes, but the connection string format is always the same. A connection string has the following format:

"DSN=data-source-name[;SERVER=value] [;PWD=value] [;UID=value] [;<Attribute>=<value>]"

Note The Microsoft ODBC Driver for Oracle supports the connection string format of the first version of the driver, which used CONNECTSTRING= instead of SERVER=.

You must specify the data source name if you do not specify the UID, PWD, SERVER (or CONNECTSTRING), and DRIVER attributes. However, all other attributes are optional. If you do not specify an attribute, that attribute defaults to the one specified in the relevant DSN tab of the ODBC Data Source Administrator dialog box. The attribute value might be case-sensitive.

The attributes for the connection string are as follows:

Attribute | Description | Default value
DSN | The data source name listed in the Drivers tab of the ODBC Data Source Administrator dialog box. | ""
PWD | The password for the Oracle Server that you want to access. This driver supports limitations that Oracle places on passwords. | ""
SERVER | The connect string for the Oracle Server that you want to access. | ""
UID | The Oracle Server user name. Depending on your system, this attribute might not be optional—that is, certain databases and tables might require this attribute for security purposes. Use "/" to use Oracle’s operating system authentication. | ""
BUFFERSIZE | The optimal buffer size used when fetching columns. The driver optimizes fetching so that one fetch from the Oracle Server returns enough rows to fill a buffer of this size. Larger values tend to increase performance if you fetch a lot of data. | 65535
SYNONYMCOLUMNS | When this value is true (1), an SQLColumn( ) API call returns column information. Otherwise, SQLColumn( ) returns only columns for tables and views. The ODBC Driver for Oracle provides faster access when this value is not set. | 1
REMARKS | When this value is true (1), the driver returns Remarks columns for the SQLColumns result set. The ODBC Driver for Oracle provides faster access when this value is not set. | 0
StdDayOfWeek | Enforces the ODBC standard for the DAYOFWEEK scalar. By default this is turned on, but users who need the localized version can change the behavior to use whatever Oracle returns. | 1
GuessTheColDef | Specifies whether or not the driver should return a non-zero value for the cbColDef argument of SQLDescribeCol. Applies only to columns where there is no Oracle-defined scale, such as computed numeric columns and columns defined as NUMBER without a precision or scale. A SQLDescribeCol call returns 130 for the precision when Oracle does not provide that information. | 0

For example, a connection string that connects to the Employees data source using the mickey.world Oracle Server and the Oracle User cindy would be:

"DSN=Employees;UID=cindy;PWD=secret;SERVER=mickey.world"

A connection string that connects to the Payroll data source using operating system authentication and the moola Oracle Server would be:

"DSN=Payroll;UID=/;PWD=;SERVER=moola"


Connect Options

These options allow customization of the database connection within an application.

Connect option | Notes
SQL_AUTOCOMMIT | If you choose SQL_AUTOCOMMIT_OFF, your application must explicitly commit or roll back transactions with SQLTransact.
SQL_ODBC_CURSORS | This connection attribute is implemented in the Driver Manager.
SQL_OPT_TRACE | This connection attribute is implemented in the Driver Manager.
SQL_OPT_TRACEFILE | This connection attribute is implemented in the Driver Manager.
SQL_TRANSLATE_DLL | Returns error: "Driver not capable."
SQL_TRANSLATE_OPTION | A 32-bit value passed to the translation .dll.
SQL_TXN_ISOLATION | The driver allows only SQL_TXN_READ_COMMITTED. The following vParams are not supported: SQL_TXN_READ_UNCOMMITTED SQL_TXN_REAPEATABLE_READ SQL_TXN_SERIALIZABLE
SQL_ATTR_ENLIST_IN_DTC | This ODBC 3.0 connection attribute allows you to use the ODBC Driver for Oracle in distributed transactions coordinated by Microsoft Component Services (or MTS, if you are using Windows NT). It provides the interface pointer pITransaction to the transaction as the vParam argument.
SQL_ATTR_CONNECTION_DEAD | This read-only ODBC 3.5 connection attribute allows you to determine whether the connection to the Oracle server has failed. Get only; cannot Set.


Statement Options

These options allow customization of a specific execution statement within an application.

Statement option | Notes
SQL_BIND_TYPE | Cannot exceed 2,147,483,647 bytes or available memory.
SQL_CONCURRENCY | For allowed values, see the Cursor Type and Concurrency Combinations.
SQL_CURSOR_TYPE | The driver does not allow SQL_CURSOR_DYNAMIC. See SQLSetScrollOptions for more information. For allowed values, see the Cursor Type and Concurrency Combinations.
SQL_GET_BOOKMARK | Returns a 32-bit integer value that is the bookmark for the current record number. Get only; cannot Set.
SQL_KEYSET_SIZE | Can be set only to 0.
SQL_MAX_ROWS | The maximum number of rows to return from a result set.
SQL_ROW_NUMBER | Returns a 32-bit integer specifying the position of the current row within the result set. Get only; cannot Set.
SQL_ROWSET_SIZE | Cannot exceed 4,294,967,296 rows; however, you must have enough virtual memory in your computer to handle your request.
SQL_USE_BOOKMARKS | Supports setting SQL_USE_BOOKMARKS to SQL_UB_ON and exposes fixed-length bookmarks.


Cursor Type and Concurrency Combinations

Cursor types control the functionality of the cursor provided to the user. Concurrency options control the updatability and locking behavior of a result set.

Cursor type | Concurrency (allowed values)
SQL_CURSOR_FORWARD_ONLY | SQL_CONCUR_READ_ONLY
SQL_CURSOR_STATIC | SQL_CONCUR_READ_ONLY
SQL_CURSOR_KEYSET_DRIVEN[1] | SQL_CONCUR_READ_ONLY SQL_CONCUR_LOCK[2] SQL_CONCUR_VALUES

[1] See Limitations of Using Keyset-Driven Cursors.

[2] SQL_CONCUR_LOCK is supported only when the SQL_AUTOCOMMIT connection option is set to SQL_AUTOCOMMIT_OFF.


Using Microsoft Component Services

You can enable an Oracle database to work with transactional Component Services (or MTS, if you are using Windows NT) on Microsoft Windows NT/Windows 2000 and Microsoft Windows 95/98. To enable an Oracle database to work with Component Services that support transactions, system administrators should create a view named V$XATRANS$. To create this script, you must run an Oracle-supplied script. For more information, see the Component Services Help or your Oracle documentation.


Using Microsoft Internet Information Services

If you have difficulty connecting from within an IIS script (particularly if you receive an ORA-12641 error), add the following line to the Sqlnet.ora file:

SQLNET.AUTHENTICATION_SERVICES = (none)

This will disable the Secure Network Services so you can connect using anonymous authentication.


Using Operating System Authentication

Oracle operating system authentication relies on the underlying operating system to control access to database accounts. Users need not enter a password when using this type of login.

To take advantage of this feature, specify "/" as the user ID and do not specify a password when connecting using any of the following connection APIs: SQLBrowseConnect, SQLConnect, or SQLDriverConnect.

Oracle databases use SQL*Net Authentication Services to authenticate users that are logged on. This service works well if users are logged into Oracle through SQLPlus; however, when the logged-in user is a service such as Internet Information Services, the authentication fails. This is a known limitation of SQL*Net Authentication and produces the following error: "[Microsoft][ODBC driver for Oracle][Oracle]ORA-12641: TNS:authentication service failed to initialize."

You can correct this problem by editing the Sqlnet.ora file. This configuration file is usually stored in the Network\Admin subdirectory of the Oracle home directory. Add the following line to Sqlnet.ora:

SQLNET.AUTHENTICATION_SERVICES = (none)


Using SQLConfigDatasource with the ODBC Driver for Oracle

The following table lists valid SQLConfigDatasource settings for the Microsoft ODBC Driver for Oracle, version 1.0 (Msorcl10.dll) and the Microsoft ODBC Driver for Oracle, version 2.0 (Msorcl32.dll).

Note The Msorcl10.dll driver (version 1.0) supports all settings except Server. The Msorcl32.dll driver (version 2.0 and higher) supports all settings.

Some settings are ignored by the driver but are accepted by SQLConfigDatasource. Including these settings in the ODBC connection string is the only way they will be accepted at run time. An ignored setting will not be stored in the registry when SQLConfigDatasource creates the data source.

In the following table, A/N means any valid alphanumeric string up to the maximum allowable length. Max Len (maximum length) is the maximum allowable string length accepted by the setting, including the string-terminator character.

Setting | Max Len | Default value | Valid values | Description
BufferSize | 7 | 65535 | 1000 | Minimum fetch buffer size up to 65535 bytes
CatalogCap | 2 | 1 | 0 or 1 | If 1, nonquoted identifiers will be converted to uppercase in the catalog functions.
ConnectString | 128 | "" | A/N | Connection string. Required method of specifying the server name with the Msorcl10.dll driver.
Description | 256 | "" | A/N | Description.
DSN | 33 | "" | A/N | Data source name.
GuessTheColDef | 4 | 0 | A/N | Returns a non-zero value for columns without Oracle-defined scale.
NumberFloat | 2 | "" | 0 or 1 | If 0, FLOAT columns are treated as SQL_FLOAT. If 1, FLOAT columns are treated as SQL_DOUBLE.
PWD | 30 | "" | A/N | Password.
RDOSupport | 2 | "" | 0 or 1 | Allows RDO to call Oracle procedures.
Remarks | 2 | 0 | 0 or 1 | Include REMARKS in catalog functions.
RowLimit | 4 | "" | 0 to 99 | Maximum number of rows returned by a SELECT statement. A zero-length string indicates that no limit is applied.
Server | 128 | "" | A/N | Oracle server name.
SynonymColumns | 2 | 1 | 0 or 1 | Include SYNONYMs in SQLColumns.
SystemTable | 2 | "" | 0 or 1 | If 0, system tables will not be displayed. If 1, system tables will be displayed.
TranslationDLL | 33 | "" | A/N | Translation .dll name.
TranslationName | 33 | "" | A/N | Translation name.
TranslationOption | 33 | "" | A/N | Translation option.
TxnCap | 2 | "" | A/N | Transaction capable. If 0, the driver reports that it does not support transactions. If 1, the driver reports that it is capable of performing transactions.
UID | 30 | "" | A/N | User name.


Limitations of Using Keyset-Driven Cursors

You must be able to retrieve a single ROWID column for the table queried. A keyset-driven cursor cannot be used on joins, queries, or statements containing DISTINCT, GROUP BY, UNION, INTERSECT, or MINUS clauses.

Also, if your application uses table aliases, keyset-driven cursors will not work; forward-only or static cursor types are required. Using the keyset cursor type with table aliases will result in the following error: "[Microsoft][ODBC driver for Oracle]Cannot use Keyset-driven cursor on join, with union, intersect or minus or on read only result set."

Note Due to the way the driver handles the SQL statement that is sent to the Oracle server, Oracle internally returns the following error message: "ORA-00964: table name not in FROM list."


Stored Procedures

This section includes the following topics:

  • Stored Procedure Parameter Limitations
  • Using Synonyms with Stored Procedures
  • Revoking and Granting Rights When Using Stored Procedures
  • Returning Array Parameters from Stored Procedures


Stored Procedure Parameter Limitations

When running Oracle stored procedures that utilize 10 or more output parameters, the stored procedure call will fail, resulting in an Access Violation or ActiveX Data Objects (ADO) error. This can occur when using the Microsoft ODBC Driver for Oracle with versions 8.0.4.0.0 and 8.0.4.0.4 of the Oracle client software.

To correct the problem, the Oracle client software must be upgraded to version 8.0.4.2.0 or higher. Contact Oracle Corporation for more information about the patches.

Note This problem does not occur with the early release of Oracle client software version 8.0.3.0.0.


Using Synonyms with Stored Procedures

The Microsoft ODBC Driver for Oracle versions 2.0 and 2.5 do not support synonyms when calling Oracle stored procedures. Synonyms work as expected when used with other Oracle database objects such as tables.


Revoking and Granting Rights When Using Stored Procedures

The Microsoft ODBC Driver for Oracle returns the following error message when user rights are granted and then revoked on a table accessed by a stored procedure:

SQL_ERROR=-1

szErrorMsg="[Microsoft][ODBC driver for Oracle]Wrong number of parameters"

szErrorMsg="[Microsoft][ODBC driver for Oracle]Syntax error or access violation"

The call to the Oracle OCI function Odessp() fails in this scenario but is necessary in order to implement default parameters. After the underlying table permissions are modified, the stored procedure must be recompiled before running it again.


Returning Array Parameters from Stored Procedures

In Oracle 7.3, there is no way to access a PL/SQL Record Type except from a PL/SQL program. If a packaged procedure or function has a formal argument defined as a PL/SQL Record Type, it is not possible to bind that formal argument as a parameter. Use the PL/SQL TABLE type in the Microsoft ODBC Driver for Oracle to invoke array parameters from procedures containing the correct escape sequences.

To invoke the procedure, use the following syntax:

{call  <package-name>.<proc-or-func>;
(..., {resultset <max-records-requested> ,<formal-array-param_1>,;
 <formal-array-param_2>,...,<formal-array-param_n> }, ... ) }

Note The <max-records-requested> parameter must be greater than or equal to the number of rows present in the result set. Otherwise, Oracle returns an error that is passed to the user by the driver.

PL/SQL records cannot be used as array parameters. Each array parameter can represent only one column of a database table.

The following example defines a package containing two procedures that return different result sets, and then provides two ways to return result sets from the package.

Package definition:

CREATE OR REPLACE PACKAGE SimplePackage AS

TYPE t_id is TABLE of  NUMBER(5)
    INDEX BY BINARY_INTEGER;

TYPE t_Course is TABLE of VARCHAR2(10)
    INDEX BY BINARY_INTEGER;

TYPE t_Dept is TABLE of VARCHAR2(5)
    INDEX BY BINARY_INTEGER;

PROCEDURE proc1
   (
   o_id             OUT    t_id,
   ao_course       OUT    t_Course,
   ao_dept         OUT    t_Dept
   );

TYPE t_pk1Type1 IS TABLE OF VARCHAR2(100) INDEX BY BINARY_INTEGER;
TYPE t_pk1Type2 IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
PROCEDURE proc2
   (
   i_Arg1         IN    NUMBER,
   ao_Arg2         OUT   t_pk1Type1,
   ao_Arg3         OUT   t_pk1Type2
   );

END SimplePackage;

CREATE OR REPLACE PACKAGE BODY SimplePackage AS
    PROCEDURE  proc1 ( o_id OUT t_id,
    ao_course OUT t_Course, ao_dept OUT t_Dept   ) AS
    BEGIN
          o_id(1):= 200;
          ao_course(1) :=  'M101';
          ao_dept(1) :=  'EEE' ;

          o_id(2) := 201;
          ao_course(2) :=  'PHY320';
          ao_dept(2) :=  'ECE' ;

     END proc1;
PROCEDURE proc2
   (
   i_Arg1         IN    NUMBER,
   ao_Arg2         OUT   t_pk1Type1,
   ao_Arg3         OUT   t_pk1Type2
   )
AS
   i   NUMBER;
BEGIN
   FOR i IN 1 .. i_Arg1 LOOP
      ao_Arg2(i) := 'Row Number ' || to_char(i);
   END LOOP;
   FOR i IN 1 .. i_Arg1 LOOP
      ao_Arg3(i) := i;
   END LOOP;
END proc2;
END SimplePackage;

To invoke procedure PROC1

  1. Return all the columns in a single result set:
    {call SimplePackage.Proc1( {resultset  3, o_id , ao_course, ao_dept  } ) }
  2. Return each column as a single result set:
    {call SimplePackage.Proc1( {resultset 3, o_id},  {resultset 3, ao_course}, {resultset 3, ao_dept} ) }

    This returns three result sets, one for each column.

To invoke procedure PROC2

  1. Return all the columns in a single result set:
    {call SimplePackage.Proc2( 5 , {resultset  5, ao_Arg2, ao_Arg3} ) }
  2. Return each column as a single result set:
    {call SimplePackage.Proc2( 5 , {resultset 5, ao_Arg2}, {resultset 5, ao_Arg3} ) }

Ensure that your applications fetch all the result sets using the SQLMoreResults API. For more information, refer to the ODBC Programmer's Reference.

Note In the ODBC Driver for Oracle version 2.0, Oracle functions that return PL/SQL arrays cannot be used to return result sets.


Error Messages

When an error occurs, the Microsoft ODBC Driver for Oracle returns the SQLSTATE (an ODBC error code) and an error message. The driver derives this information both from errors detected by the driver and from errors returned by the Oracle Server.

This section includes the following topic:

  • Messages Returned by the ODBC Driver for Oracle


Messages Returned by the ODBC Driver for Oracle

If an Oracle error message is available, it will be returned preceded by the [Microsoft], [ODBC Driver for Oracle], and [Oracle] tags; otherwise, the message is returned without the [Oracle] tag as in the following examples:

Oracle error message:

[Microsoft][ODBC driver for Oracle][Oracle]ORA-nnnnn message-text

ODBC Driver for Oracle error message:

[Microsoft][ODBC driver for Oracle]


Technical Reference

This section includes the following topics:

  • ODBC Conformance Levels
  • API Functions
  • Supported Data Types
  • Mapping Data Types


ODBC Conformance Levels

ODBC defines two types of conformance standards for drivers: the API conformance standard and the SQL grammar conformance standard. API conformance refers to the functions that a driver supports. SQL conformance refers to the SQL grammar that the driver supports. Each conformance standard is made up of levels.


API Conformance Level

The ODBC Driver for Oracle supports the Core Level API functions and Level 1 API functions. The driver also supports the following Level 2 API functions:

  • SQLBrowseConnect( )
  • SQLDataSources( )
  • SQLDescribeParam( )
  • SQLExtendedFetch( )
  • SQLForeignKeys( )
  • SQLMoreResults( )
  • SQLNativeSql( )
  • SQLNumParams( )
  • SQLPrimaryKeys( )
  • SQLProcedureColumns( )
  • SQLProcedures( )
  • SQLSetPos( )
  • SQLSetScrollOptions( )


Supported Options

The ODBC Driver for Oracle supports the following options for the SQLGetConnectOption( ) and SQLSetConnectOption( ) Level 1 functions:

  • SQL_ACCESS_MODE (SQLGetConnectOption( ) only)
  • SQL_AUTOCOMMIT
  • SQL_ODBC_CURSORS
  • SQL_OPT_TRACEFILE
  • SQL_OPT_TRACE
  • SQL_TRANSLATE_DLL
  • SQL_TRANSLATE_OPTION
  • SQL_TXN_ISOLATION

The ODBC Driver for Oracle supports the following options for the SQLGetStmtOption( ) and SQLSetStmtOption( ) Level 1 functions:

  • SQL_BIND_TYPE
  • SQL_CONCURRENCY
  • SQL_CURSOR_TYPE
  • SQL_KEYSET_SIZE
  • SQL_MAX_ROWS
  • SQL_ROWSET_SIZE


SQL Conformance Levels

The ODBC Driver for Oracle supports the Minimum SQL grammar and Core SQL grammar and also supports the following ODBC extensions to SQL:

  • Date, time, and timestamp data
  • Left and right outer joins
  • Numeric functions:
    Abs | Log | round | tan
    Ceiling | Log10 | second | truncate
    Cos | Mod | sign
    Exp | Pi | sin
    Floor | Power | sqrt
  • Date functions:
    Curdate | Dayofweek | monthname | second
    Curtime | Dayofyear | minute | week
    Dayname | Hour | now | year
    Dayofmonth | Month | quarter
  • String functions:
    Ascii | Left | right | ucase
    Char | Length | rtrim
    Concat | Ltrim | soundex
    Lcase | Replace | substring
  • Type-conversion function:
    Convert
  • System functions:
    Ifnull
    User


API Functions

The Microsoft ODBC Driver for Oracle supports the Core Level, Level 1, and Level 2 API functions. These functions are listed in ODBC Conformance Levels.

  • Core Level Interface (CLI) conformance provides features defined in the ISO CLI specification and the mandatory features defined in the X/Open CLI specification.
  • Level 1 conformance provides Core Level Interface functionality as well as additional features such as transactions.
  • Level 2 conformance provides Level 1 functionality as well as additional features such as bookmarks, dynamic parameters, and asynchronous execution of ODBC functions.


Thread-Safety Notes on API Functions

The Microsoft ODBC Driver for Oracle is thread-safe; however, Oracle does not allow multiple concurrent statements on a single connection. The driver enforces this restriction. In other words, in multithreaded applications, although any thread can call into the ODBC Driver for Oracle at any time, the driver blocks any other thread from the driver on the same connection until the original thread leaves the driver.

The driver does not block if there are two statements on two different connections. However, if there is a single connection with two statements, there is potential for blocking.


Core Level API Functions

Functions at this level comprise the minimum level of interface conformance for ODBC drivers.

API function | Notes
SQLAllocConnect | Allocates memory for a connection handle, hdbc, within the environment identified by henv. The Driver Manager processes this call and calls the driver's SQLAllocConnect function whenever SQLConnect, SQLBrowseConnect, or SQLDriverConnect is called.
SQLAllocEnv | Displays a dialog box specifying the requirement for Oracle Client software and then returns SQL_NULL_HANDLE. If the Oracle Client software is not installed, this function allocates memory for an environment handle, henv, and initializes the ODBC call-level interface for use by an application.
SQLAllocStmt | Allocates memory for a statement handle and associates the statement handle with the connection specified by hdbc. The Driver Manager passes this call to the driver, which allocates the memory for the hstmt structure.
SQLBindCol | Assigns storage space for a result column and specifies the type of the result.
SQLCancel | Cancels the processing on a statement handle, hstmt. In some cases, Oracle does not allow cancellation of a running statement. This means that a running statement will continue until Oracle completes the process, at which time the results from the statements are canceled by the ODBC Driver for Oracle.
SQLColAttributes | Returns descriptor information for a column in a result set. Descriptor information is returned as a character string, a 32-bit descriptor-dependent value, or an integer value.
SQLConnect | Connects to a data source. To use Oracle Operating System Authentication, specify "/" as the szUID parameter and "" as the szAuthStr parameter.
SQLDescribeCol Returns the name, type, precision, scale, and nullability of the given result column.

Note SQLDescribeCol reports calculated columns as SQL_VARCHAR.

SQLDisconnect | Closes a connection. If connection pooling is enabled for a shared environment and an application calls SQLDisconnect on a connection in that environment, the connection is returned to the connection pool and is still available to other components using the same shared environment.
SQLError | Returns error or status information about the last error. The driver maintains a stack or list of errors that can be returned for the hstmt, hdbc, and henv arguments, depending on how the call to SQLError is made. The error queue is flushed after each statement. Usually retrieves an Oracle error message and is otherwise empty.
SQLExecDirect Executes a new, unprepared SQL statement. The driver uses the current values of the parameter marker variables if any parameters exist in the statement. If your table, view, or field names contain spaces, enclose the names in back quote marks. For example, if your database contains a table named My Table and the field My Field, enclose each element of the identifier like so:

SELECT `My Table`. `My Field1`,;
`My Table`.`My Field2` FROM `My Table`
SQLExecute | Executes a prepared SQL statement (a statement already prepared by SQLPrepare). The driver uses the current values of the parameter marker variables if any parameters exist in the statement.
SQLFetch | Retrieves one row from a result set into the locations specified by the previous calls to SQLBindCol. Prepares the driver for a call to SQLGetData for the unbound columns.
SQLFreeConnect | Releases a connection handle and frees all memory allocated for the handle.
SQLFreeEnv | Closes the ODBC Driver for Oracle and releases all memory associated with the driver.
SQLFreeStmt | Stops processing associated with a specific hstmt, closes any open cursors associated with the hstmt, discards pending results, and optionally frees all resources associated with the statement handle.
SQLGetCursorName | Returns the name of the cursor associated with the given hstmt.
SQLNumResultCols | Returns the number of columns in a result set cursor.
SQLPrepare Prepares an SQL statement by planning how to optimize and execute the statement. The SQL statement is compiled for execution by SQLExecDirect.

If your table, view, or field names contain spaces, enclose the names in back quote marks. For example, if your database contains a table named My Table and the field My Field, enclose each element of the identifier as follows:

SELECT `My Table`.`My Field` FROM `My Table`

For information about using result sets containing arrays as formal parameters, see Returning Array Parameters from Stored Procedures.
SQLRowCount | Oracle does not provide a way to determine the number of rows in a result set until after you fetch the last row, so it returns –1.
SQLSetCursorName | Associates a cursor name with an active statement handle, hstmt.
SQLSetParam | Replaced by SQLBindParameter in ODBC 2.x.
SQLTransact | Requests a commit or rollback operation for all active operations on all statement handles (hstmts) associated with a connection, or for all connections associated with the environment handle, henv. If a commit fails when in manual mode, the transaction remains active; you can choose to roll back the transaction or retry the commit operation. If a commit operation fails when in automatic transaction mode, the transaction is rolled back automatically; the transaction cannot be inactive.


Level 1 API Functions

Functions at this level provide Core interface conformance plus additional functionality such as transaction support.

API function | Notes
SQLColumns | Creates a result set for a table, which is the column list for the specified table or tables. When you request columns for a PUBLIC synonym, you must have set the SYNONYMCOLUMNS connection attribute and specified an empty string as the szTableOwner argument. When returning columns for PUBLIC synonyms, the driver sets the TABLE NAME column to an empty string. The result set contains an additional column, ORDINAL POSITION, at the end of each row. This value is the ordinal position of the column in the table.
SQLDriverConnect | Connects to an existing data source. For details, see Connection String Format and Attributes.
SQLGetConnectOption | Returns the current setting of a connection option. This function is partially supported. The driver supports all values for the fOption argument but does not support some vParam values for the fOption argument SQL_TXN_ISOLATION. For more information, see Connect Options.
SQLGetData | Retrieves the value of a single field in the current record of the given result set.
SQLGetFunctions | Returns TRUE for all supported functions. Implemented by the Driver Manager.
SQLGetInfo | Returns information, including SQLHDBC, SQLUSMALLINT, SQLPOINTER, SQLSMALLINT, and SQLSMALLINT *, about the ODBC Driver for Oracle and data source associated with a connection handle, hdbc.
SQLGetStmtOption | Returns the current setting of a statement option. For more information, see Statement Options.
SQLGetTypeInfo | Returns information about the data types supported by a data source. The driver returns the information in an SQL result set.
SQLParamData | Used in conjunction with SQLPutData to specify parameter data at statement execution time.
SQLPutData | Allows an application to send data for a parameter or column to the driver at statement execution time.
SQLSetConnectOption | Provides access to options that govern aspects of the connection. This function is partially supported: The driver supports all values for the fOption argument but does not support some vParam values for the fOption argument SQL_TXN_ISOLATION. For more information, see Connect Options.
SQLSetStmtOption | Sets options related to a statement handle, hstmt. For more information, see Statement Options.
SQLSpecialColumns | Retrieves the optimal set of columns that uniquely identifies a row in the table.
SQLStatistics | Retrieves a list of statistics about a single table and the indexes, or tag names, associated with the table. The driver returns the information as a result set.
SQLTables | Returns the list of table names specified by the parameter in the SQLTables statement. If no parameter is specified, returns the table names stored in the current data source. The driver returns the information as a result set. Enumeration type calls will not receive a result set entry for remote views or local parameterized views. However, a call to SQLTables with a unique table name specifier will find a match for such a view, if present, with that name; this allows the API to check for name conflicts prior to creation of a new table. PUBLIC synonyms are returned with a TABLE_OWNER value of "". VIEWS owned by SYS or SYSTEM are identified as SYSTEM VIEW.


Level 2 API Functions

Functions at this level provide Level 1 interface conformance plus additional functionality such as support for bookmarks, dynamic parameters, and asynchronous execution of ODBC functions.

API function | Notes
SQLBindParameter | Associates a buffer with a parameter marker in an SQL statement.
SQLBrowseConnect | Returns successive levels of attributes and attribute values.
SQLDataSources | Lists data source names. Implemented by the Driver Manager.
SQLDescribeParam | Returns the description of a parameter marker associated with a prepared SQL statement. Returns a best guess of what the parameter is, based on parsing the statement. If the parameter type cannot be determined, SQL_VARCHAR returns with length 2000.
SQLDrivers | Implemented by the Driver Manager.
SQLExtendedFetch | Similar to SQLFetch but returns multiple rows using an array for each column. The result set is forward-scrollable and can be made backward-scrollable if the cursor is defined to be static, not forward-only. For forward-only cursors with default column binding, column data from data sets larger than the BUFFERSIZE connection attribute is fetched directly into data buffers. Does not support variable-length bookmarks and does not support fetching a rowset at an offset (other than 0) from a bookmark.
SQLForeignKeys | Returns a list of foreign keys in a single table, or a list of foreign keys in other tables that refer to a single table.
SQLMoreResults | Determines whether more results are pending on a statement handle, hstmt, containing SELECT, UPDATE, INSERT, or DELETE statements and if so, initializes processing for those results. Oracle supports multiple result sets only from stored procedures, when using {resultset… } escape sequences.
SQLNativeSql | For information about usage, see Returning Array Parameters from Stored Procedures.
SQLNumParams | Returns the number of parameters in an SQL statement. The number of parameters should equal the number of question marks in the SQL statement passed to SQLPrepare.
SQLPrimaryKeys | Returns the column names that comprise the primary key for a table.
SQLProcedureColumns | Returns a list of input and output parameters, the return value, the columns in the result set of a single procedure, and two additional columns, OVERLOAD and ORDINAL_POSITION. OVERLOAD is the OVERLOAD column from the ALL_ARGUMENTS table of the Oracle Data Dictionary View. ORDINAL_POSITION is the SEQUENCE column from the ALL_ARGUMENTS table of the Oracle Data Dictionary View. For packaged procedures, the PROCEDURE NAME column is in packagename.procedurename format. Does not return the procedure columns of a created synonym that refers to a procedure or function.
SQLProcedures | Returns a list of procedures in the data source. For packaged procedures, the PROCEDURE NAME column is in packagename.procedurename format. Because Oracle does not provide a way to distinguish packaged procedures from packaged functions, the driver returns SQL_PT_UNKNOWN for the PROCEDURE_TYPE column.
SQLSetPos | Sets the cursor position in a rowset. You can use SQLSetPos with SQLGetData to retrieve rows from unbound columns after positioning the cursor to a specific row in the rowset. Rows added to the result set using fOption SQL_ADD are added after the last row in the result set.
SQLSetScrollOptions | Sets options that control the behavior of cursors associated with a statement handle, hstmt. For details, see Cursor Type and Concurrency Combinations.


Supported Data Types

The ODBC Driver for Oracle supports all Oracle 7.3 data types; however, it does not support any of the new Oracle8 data types listed here.

Data type | Oracle 7.3 | Oracle8
BFILE | n/a | Not supported
BLOB | n/a | Not supported
CHAR | Supported | Supported
CLOB | n/a | Not supported
DATE | Supported | Supported
FLOAT | Supported | Supported
INTEGER | Supported | Supported
LONG | Supported | Supported
LONG RAW | Supported | Supported
NCHAR | n/a | Not supported
NCLOB | n/a | Not supported
NUMBER | Supported | Supported
NVARCHAR2 | n/a | Not supported
RAW | Supported | Supported
VARCHAR2 | Supported | Supported
MLSLABEL | Not supported. | Not supported.

Note For more information about the allowable size of the VARCHAR column, see VARCHAR Column Size in this guide.


Mapping Data Types

The Oracle Server supports a set of data types. The ODBC Driver for Oracle maps these data types to their appropriate ODBC SQL data types. The following table lists the Oracle 7.3 Server data types and their corresponding ODBC SQL data types.

The ODBC Driver for Oracle supports Oracle 7.3 and some Oracle8 data types. For more information about supported Oracle8 data types, see Supported Data Types.

Oracle Server data type | ODBC SQL data type
CHAR | SQL_CHAR
DATE | SQL_TIMESTAMP
FLOAT | SQL_DOUBLE
INTEGER | SQL_DECIMAL
LONG | SQL_LONGVARCHAR
LONG RAW | SQL_LONGVARBINARY
NUMBER | SQL_DECIMAL
RAW | SQL_VARBINARY
VARCHAR2 | SQL_VARCHAR

Note For more information about the allowable size of the VARCHAR column, see VARCHAR Column Size in this guide.


VARCHAR Column Size

In Oracle8, the maximum size of a VARCHAR column has increased from 2000 to 4000 bytes. The Oracle 7.3.x client software has no way to bind a parameter value larger than 2000 bytes. Therefore, if you create a table with a VARCHAR column of larger than 2000 bytes, you will be unable to perform parameterized inserts, updates, deletes, and queries against it with data that exceeds the 2000-byte limit of the client software. Because both the ODBC Driver for Oracle and the OLE DB Provider for Oracle use parameterized inserts, updates, deletes, and queries, they will report ORA-01026 errors in this case. Data that is within the limits enforced by the Oracle client software will work. To avoid this 2000-byte limit, you must upgrade your client software to Oracle8 (8.0.4.1.1c or higher).


Visual FoxPro ODBC Driver

Microsoft® Visual FoxPro® is a powerful object-oriented environment for database construction and application development. The Microsoft Visual FoxPro ODBC Driver enables applications to open, query, and update data in Visual FoxPro and earlier versions of FoxPro through the Open Database Connectivity (ODBC) interface.

For example, with the Microsoft Visual FoxPro ODBC Driver you can do the following:

  • Use Microsoft Query to query and update Visual FoxPro data from Microsoft Excel worksheets.
  • Create mail-merge letters using Visual FoxPro data with Microsoft Word.
  • Query and update Visual FoxPro views and tables from Microsoft Access.
  • Use Visual FoxPro as the data store for Microsoft Visual Basic®, Microsoft Visual C++®, and C applications.

You can use the driver to accomplish many other tasks. The following table lists a few topics to help you get started.

To | See
Find out more about using Visual FoxPro data with Microsoft Office | Accessing Visual FoxPro Data from Microsoft Office
Learn about using Visual FoxPro data in Visual Basic applications | Using the Visual FoxPro ODBC Driver with Your Visual Basic Application
View a simple example using Visual C++ to access Visual FoxPro data | Using the Visual FoxPro ODBC Driver with Your C or C++ Application
See a list of supported hardware and software | System Requirements

Driver Architecture

The Microsoft Visual FoxPro ODBC Driver is a 32-bit driver that enables you to open and query a Microsoft Visual FoxPro database or FoxPro tables through the Open Database Connectivity (ODBC) interface. You can access FoxPro data using the following types of applications:

  • A Microsoft Office application, such as Microsoft Excel or Microsoft Word, that uses Microsoft Query to communicate with ODBC.
  • An application written in Microsoft Visual C++ or C that uses the ODBC SDK API.
  • An application written in Microsoft Visual Basic or Microsoft Visual Basic for Applications.

In each case, the request for information uses the ODBC API. The ODBC Driver Manager works with the Visual FoxPro ODBC Driver to open and retrieve data from FoxPro tables and databases.

The architecture is represented in the following diagram:


Installing and Configuring

This section contains the following topics to help you install and configure the Microsoft Visual FoxPro ODBC Driver:

  • System Requirements
  • Supported Versions of FoxPro
  • Setting Up the Visual FoxPro ODBC Driver
  • ODBC Visual FoxPro Setup Dialog Box


System Requirements

The system requirements for installation provide the minimum operating system and disk space needed to successfully install the driver. After you've installed the driver, you can select the specific application software you want to use to access Visual FoxPro data.

Installation Requirements

To install the Microsoft Visual FoxPro ODBC Driver, you need the following:

  • Microsoft Windows® 95, Windows 98, Windows NT® 4.0, or Windows 2000
  • 2 MB disk space

For information about installing the driver, see Setting Up the Visual FoxPro ODBC Driver.

Accessing Visual FoxPro Data

To access Microsoft Visual FoxPro or FoxPro 2.x data, you must have the following:

  • ODBC Client Software (automatically installed with the driver)
  • Microsoft Visual FoxPro ODBC Driver
  • Any of the following types of application software:
    • Microsoft Office application such as Microsoft Excel or Microsoft Word
    • C or C++ language ODBC application
    • Microsoft Visual Basic ODBC application
  • Any of the following types of data:
    • Visual FoxPro database or a directory of free tables
    • FoxPro 2.0, 2.5, 2.6 table

The Visual FoxPro ODBC Driver supports double-byte character sets (DBCS). For more information, see International Support.

The driver does not support 16-bit Microsoft Windows 3.1 applications.


Supported Versions of FoxPro

You can use the Microsoft® Visual FoxPro® ODBC Driver to access data stored in FoxPro tables. The following versions of FoxPro data are supported:

  • 2.0
  • 2.5
  • 2.6
  • Visual FoxPro (all versions)

When you access data stored in Visual FoxPro, you can choose to connect to a database containing zero or more tables or to a directory of free tables.

For more information on connecting to a data source, see Adding a Visual FoxPro Data Source.


Setting Up the Visual FoxPro ODBC Driver

You use the Microsoft® Visual FoxPro® ODBC Driver Setup program to do the following:

  • Add new components.
  • Remove installed components.
  • Reinstall to restore missing files and settings.
  • Remove all previously installed components.

To set up the Visual FoxPro ODBC Driver

  1. Run Setup.exe from Disk 1 of the Visual FoxPro ODBC Driver.
  2. Follow the instructions on the screen.

Once you install the driver on your system, the Setup program recognizes the installed driver components and presents additional dialog boxes that enable you to change your driver's configuration.


ODBC Visual FoxPro Setup Dialog Box

The ODBC Visual FoxPro Setup dialog box enables you to add or change a Visual FoxPro data source.

Dialog Box Options

Data Source Name
Type the name you want to use for the data source.
Description
Type a description for the data source.
Database type
Lets you choose the type of database you want your data source to connect to.
Visual FoxPro database (.DBC)
Specifies that the data source connects to a Visual FoxPro database (.dbc file) and to all the tables and local views in the database.
Free Table directory
Specifies that the data source connects to a directory of free tables. Any database tables in the same directory are ignored by ODBC catalog functions such as SQLColumns or SQLTables. Database tables can be accessed by using SQL SELECT statements sent through SQLExecute and SQLExecDirect.
Path
Displays the path and name for the database or the directory of free tables to which the data source connects.
Browse
Enables you to search your system and network for the database or directory to which you want to connect the data source.
Options
Expands the dialog box so that you can set Visual FoxPro ODBC Driver options.

Driver

Collating sequence
The sequence in which fields are sorted. The default sequences reflect the sequences supported by your language version of the operating system. For a list of supported collating sequences, see SET COLLATE.
Exclusive
When this check box is selected, the driver opens the Visual FoxPro database exclusively when you access data using the data source. Other users cannot access the database or the tables in the database while the database is opened exclusively. Tables within the exclusively opened database are opened as SHARED. To open a table exclusively, use the SET EXCLUSIVE command. This check box is disabled when Database type is set to Free Table directory.
Null
Determines whether columns created with ALTER TABLE and CREATE TABLE allow null values. If you set Null ON, INSERT – SQL inserts a null value into any column not included in an INSERT – SQL… VALUE clause. A blank is inserted if Null is OFF. You can also control this option through a passed connection string as in the following code:
strCon = "DRIVER=MICROSOFT VISUAL FOXPRO DRIVER;
SOURCETYPE=DBC;SOURCEDB=D:\Testdata.dbc;BACKGROUNDFETCH=NO;NULL=NO"
Deleted
Determines whether rows marked as deleted are returned. You can also control this option through a passed connection string as in the following code:
strCon = "DRIVER=MICROSOFT VISUAL FOXPRO DRIVER;
SOURCETYPE=DBC;SOURCEDB=D:\Testdata.dbc;BACKGROUNDFETCH=NO;
DELETED=YES"
Fetch data in background
Determines whether records will be fetched in the background (progressive fetching) or your application will wait until all records in the result set are fetched.


Using the Visual FoxPro ODBC Driver

In this section, the following topics discuss how to use the Microsoft® Visual FoxPro® ODBC Driver:

  • Adding a Visual FoxPro Data Source
  • Modifying a Visual FoxPro Data Source
  • Deleting a Visual FoxPro Data Source
  • Connecting to a Visual FoxPro Data Source
  • Using Connection Strings
  • Using the Visual FoxPro ODBC Driver with Your Visual Basic Application
  • Using the Visual FoxPro ODBC Driver with Your C or C++ Application
  • Accessing Visual FoxPro Data from Microsoft Office


Adding a Visual FoxPro Data Source

To access Visual FoxPro data from your application, you must have a data source. You can create a data source as follows:

  • Within an application, such as Microsoft® Word, Microsoft Excel, or Microsoft Access, that uses ODBC drivers.
  • 67Outside of your application, using the Microsoft Windows® 95, Microsoft Windows 98, or Microsoft Windows NT®/Windows 2000 Control Panel.

Once a data source exists on your system, you can reuse the same data source each time you want to access Visual FoxPro data. If you have several different databases or tables you want to access, you can create a separate data source for each database or directory.

The following procedure creates a data source using the Control Panel. For more information on creating a data source from an application, see Accessing Visual FoxPro Data from Microsoft Office.

To add a Visual FoxPro data source

  1. On computers running Windows 2000, open the Windows Control Panel and double-click Administrative Tools.
  2. Double-click Data Sources (ODBC) to open the ODBC Data Source Administrator dialog box. This icon is available after you've installed the Visual FoxPro ODBC Driver or any ODBC driver software.

    Note If you are running a previous version of Windows, open the Windows Control Panel and double-click 32-bit ODBC or ODBC to open the ODBC Data Source Administrator dialog box.

  3. Click Add.
  4. In the Create New Data Source dialog box, select Microsoft Visual FoxPro Driver and then click Finish.
  5. In the ODBC Visual FoxPro Setup dialog box, type the data source name and description, select the database type, select the database or directory, and click OK.

    The new data source name is displayed in the User Data Sources list in the User DSN tab of the ODBC Data Source Administrator dialog box.

  6. Click OK to save the new data source and close the ODBC Data Source Administrator dialog box.


Modifying a Visual FoxPro Data Source

To modify a Visual FoxPro data source

  1. On computers running Windows 2000, open the Windows Control Panel and double-click Administrative Tools.
  2. Double-click Data Sources (ODBC) to open the ODBC Data Source Administrator dialog box. This icon is available after you've installed the Visual FoxPro ODBC Driver or any ODBC driver software.

    Note If you are running a previous version of Windows, open the Windows Control Panel and double-click 32-bit ODBC or ODBC to open the ODBC Data Source Administrator dialog box.

  3. In the User DSN or System DSN tab, select the name of the data source you want to modify and click Configure.
  4. In the ODBC Visual FoxPro Setup dialog box, select and change the items you want to modify and then click OK.
  5. Click OK to save changes and close the ODBC Data Source Administrator dialog box.

Your changes will take effect the next time you access the data source from your application.


Deleting a Visual FoxPro Data Source

To delete a Visual FoxPro data source

  1. On computers running Windows 2000, open the Windows Control Panel and double-click Administrative Tools.
  2. Double-click Data Sources (ODBC) to open the ODBC Data Source Administrator dialog box. This icon is available after you've installed the Visual FoxPro ODBC Driver or any ODBC driver software.

    Note If you are running a previous version of Windows, open the Windows Control Panel and double-click 32-bit ODBC or ODBC to open the ODBC Data Source Administrator dialog box.

  3. In the User DSN or System DSN tab, select the name of the data source you want to delete.
  4. Click Remove.
  5. Click OK to save changes and close the ODBC Data Source Administrator dialog box.


Connecting to a Visual FoxPro Data Source

You can connect to a Visual FoxPro data source using your Microsoft Office application or using the SQL API.

To connect from | See
Microsoft Access, Microsoft Excel, or Microsoft Word | Accessing Visual FoxPro Data from Microsoft Office
Your C or C++ application | SQLConnect SQLDriverConnect
Your Visual Basic application | Using the Visual FoxPro ODBC Driver with Your Visual Basic Application


Using Connection Strings

You can use a connection string to connect to a Visual FoxPro data source.

For example, to connect to the TasTrade data source and override the current setting of Exclusive associated with the data source, you would use the string:

   DSN=TasTrade;Exclusive=Yes

For a list of the attribute keywords and values you can include in the connection string, see SQLDriverConnect.

For a complete explanation of connection string syntax, see SQLBrowseConnect in the ODBC Programmer's Reference.


Using the VFP FoxPro ODBC Driver with Your Visual Basic Application

ODBC Drivers

Using the Visual FoxPro ODBC Driver with Your Visual Basic Application

Your Microsoft® Visual Basic® application can communicate with Visual FoxPro data by creating a data control that connects to a Visual FoxPro data source.

To connect to Visual FoxPro data using the Data Control in Visual Basic

  1. Create a data source named "test" that connects to the TasTrade sample database included in Visual FoxPro. The default Visual FoxPro installation places the TasTrade sample database in the location:
    c:\vfp\samples\mainsamp\data\tastrade.dbc
  2. In Visual Basic, create a new form and place a text box and a Data control on it.
  3. Change the Data control's Connect property as follows:
    ODBC;DATABASE=tastrade;DSN=test
  4. Change the RecordsetType property to the following:
    2 - Snapshot
  5. Change the RecordSource property to the following:
    customer
  6. Change the DataSource property for the text box to the default name for the Data control to the following:
    data1
  7. Change the text box's DataField property to the following:
    customer_id
  8. Run the form, and use the Data control to skip through the customer id fields from the Visual FoxPro TasTrade sample database.


Using the Visual FoxPro ODBC Driver with Your C or Visual C++ Application

ODBC Drivers

Using the Visual FoxPro ODBC Driver with Your C or C++ Application

Your C or C++ application communicates with Visual FoxPro data by sending a SQLExecute or SQLExecDirect statement to Visual FoxPro. This statement can contain the following:

  • SQL statements native to the Visual FoxPro language, such as the DROP TABLE command.
  • Supported ODBC SQL grammar.
  • Non-SQL Visual FoxPro language such as supported SET commands.

For more information about SQL native to Visual FoxPro, see the Visual FoxPro documentation.

Example: Using the Visual FoxPro ODBC Driver with Your C or C++ Application

The following example uses the ODBC C API to retrieve data stored in the last_name field in the employee table in the Microsoft® Visual FoxPro sample database named TasTrade. This database is provided with Visual FoxPro and is installed by default in the following location:

c:\vfp\samples\mainsamp\data\tastrade.dbc

The example displays one last name at a time, allowing you to click OK on the message box to see the next last name. It is assumed that a data source named Tastrade has been set up to use the Tastrade.dbc database.

Note Error checking should be performed on all ODBC API calls; this example excludes error checking for the sake of brevity.

#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#include <stdlib.h>
#include <mbstring.h>

#define MAX_DATA 100
#define MYSQLSUCCESS(rc) ((rc==SQL_SUCCESS)||(rc==SQL_SUCCESS_WITH_INFO))

class direxec
{
   RETCODE rc;        // ODBC return code
   HENV henv;         // Environment
   HDBC hdbc;         // Connection handle
   HSTMT hstmt;       // Statement handle
   unsigned char szData[MAX_DATA];   // Returned data storage
   SDWORD cbData;     // Output length of data
   unsigned char chr_ds_name[SQL_MAX_DSN_LENGTH];   // Data source name

public:
   direxec();           // Constructor
   void sqlconn();      // Allocate env, stat, and conn
   void sqlexec(unsigned char *);   // Execute SQL statement
   void sqldisconn();   // Free pointers to env, stat, conn,
                        // and disconnect
   void error_out();    // Displays errors
};

// Constructor initializes the string chr_ds_name with the
// data source name.
direxec::direxec()
{
   _mbscpy(chr_ds_name,(const unsigned char *)"tastrade");
}

// Allocate environment handle, allocate connection handle,
// connect to data source, and allocate statement handle.
void direxec::sqlconn(void)
{
   SQLAllocEnv(&henv);
   SQLAllocConnect(henv,&hdbc);
   rc=SQLConnect(hdbc,chr_ds_name,SQL_NTS,NULL,0,NULL,0);

   // Deallocate handles, display error message, and exit.
   if (!MYSQLSUCCESS(rc))
   {
      SQLFreeEnv(henv);
      SQLFreeConnect(hdbc);
      error_out();
      exit(-1);
   }

   rc=SQLAllocStmt(hdbc,&hstmt);

}

// Execute SQL command with SQLExecDirect() ODBC API.
void direxec::sqlexec(unsigned char * cmdstr)
{
   rc=SQLExecDirect(hstmt,cmdstr,SQL_NTS);
   if (!MYSQLSUCCESS(rc))  //Error
   {
      error_out();
      // Deallocate handles and disconnect.
      SQLFreeStmt(hstmt,SQL_DROP);
      SQLDisconnect(hdbc);
      SQLFreeConnect(hdbc);
      SQLFreeEnv(henv);
      exit(-1);
   }
   else
   {
      for (rc=SQLFetch(hstmt); rc == SQL_SUCCESS; rc=SQLFetch(hstmt))
      {
         SQLGetData(hstmt,1,SQL_C_CHAR,szData,sizeof(szData),&cbData);
         // In this example, the data is returned in a messagebox
         // for simplicity. However, normally the SQLBindCol() ODBC API
         // could be called to bind individual rows of data and assign
         // for a rowset.
         MessageBox(NULL,(const char *)szData,"ODBC",MB_OK);
      }
   }
}

// Free the statement handle, disconnect, free the connection handle, and
// free the environment handle.
void direxec::sqldisconn(void)
{
   SQLFreeStmt(hstmt,SQL_DROP);
   SQLDisconnect(hdbc);
   SQLFreeConnect(hdbc);
   SQLFreeEnv(henv);
}

// Display error message in a message box that has an OK button.
void direxec::error_out(void)
{
   unsigned char szSQLSTATE[10];
   SDWORD nErr;
   unsigned char msg[SQL_MAX_MESSAGE_LENGTH+1];
   SWORD cbmsg;

   while(SQLError(0,0,hstmt,szSQLSTATE,&nErr,msg,sizeof(msg),&cbmsg)==
   SQL_SUCCESS)
   {
      wsprintf((char *)szData,"Error:\nSQLSTATE=%s,Native
      error=%ld,msg='%s'",
         szSQLSTATE,nErr,msg);
      MessageBox(NULL,(const char *)szData,"ODBC Error",MB_OK);

   }

}

int WINAPI WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
{
   // Declare an instance of the direxec object.
   direxec x;

   // Allocate handles, and connect.
   x.sqlconn();

   // Execute SQL command "SELECT last_name FROM employee".
   x.sqlexec((UCHAR FAR *)"SELECT last_name FROM employee");

   // Free handles, and disconnect.
   x.sqldisconn();

   // Return success code; example executed successfully.
   return (TRUE);
}


Accessing Visual FoxPro Data from Microsoft Office

You can use the Microsoft Visual FoxPro ODBC Driver to access Visual FoxPro data from your Microsoft Office for Windows 95 or Windows 98 applications.

To | See
Use Microsoft Access | Querying and Updating Visual FoxPro Data from Access Importing Visual FoxPro Data into Microsoft Access
Use Microsoft Excel | Accessing a Visual FoxPro Data Source from Microsoft Excel Importing Data into Microsoft Excel from a Visual FoxPro Database
Use Microsoft Word | Creating Mailing Labels in Microsoft Word Using Visual FoxPro Data


Querying and Updating Visual FoxPro Data from Microsoft Access

You can query and update data stored in a Visual FoxPro database from a Microsoft Access database by using the Link Table option.

To link a Visual FoxPro database to a Microsoft Access database

  1. Open a Microsoft Access database.
  2. From the Tables tab, click New.
  3. In the New Table dialog box, select Link Table and click OK.
  4. In the Link dialog box, select ODBC Databases( ) in the Files of type list.
  5. In the SQL Data Sources dialog box, select the data source that connects to the Visual FoxPro data you want to query and click OK.
  6. In the Link Tables dialog box, select the tables you want to query and update and click OK. The linked Visual FoxPro tables are displayed in the Tables tab of the Microsoft Access database.

You can now use Microsoft Access to query and update data in the linked Visual FoxPro tables. Changes you make to linked data are sent back to the Visual FoxPro data source.

If you don't want changes you make in Microsoft Access to affect the data on the Visual FoxPro data source, see Importing Visual FoxPro Data into Microsoft Access.


Importing Visual FoxPro Data into Microsoft Access

You can import data stored in a Visual FoxPro database into a Microsoft Access database using the Import option.

To import Visual FoxPro data into a Microsoft Access database

  1. Open a Microsoft Access database.
  2. From the File menu, choose Get External Data and then Import.
  3. In the Import dialog box, select ODBC Databases( ) in the Files of type list.
  4. In the SQL Data Sources dialog box, select the Visual FoxPro data source that connects to the FoxPro data you want to query and click OK.
  5. In the Import Objects dialog box, select one or more tables you want to import and click OK. The names of the Visual FoxPro tables you imported are displayed in the Tables tab of the Microsoft Access database.

You can now use Microsoft Access to manipulate the data in the imported Visual FoxPro tables. The data you import is a snapshot of the data stored in Visual FoxPro; changes you make to imported data are not sent back to the Visual FoxPro data source.

If you want changes you make in Microsoft Access to change the data on the Visual FoxPro data source, see Querying and Updating Visual FoxPro Data from Microsoft Access.


Accessing a Visual FoxPro Data Source from Microsoft Excel

If you have Microsoft Query installed, you can create a data source in Microsoft Excel that connects to Visual FoxPro data.

To access Visual FoxPro data from Microsoft Excel

  1. Open a Microsoft Excel spreadsheet.
  2. From the Data menu, choose Get External Data. Microsoft Query opens.
  3. In the Select Data Source dialog box, click Other.
  4. In the ODBC Data Sources dialog box, click New.
  5. In the Add Data Source dialog box, select Microsoft Visual FoxPro Driver from the Installed ODBC Drivers list box and click OK.
  6. In the ODBC Visual FoxPro Setup dialog box, enter the data source name, select the Database type, enter the path to the database or directory, and click OK.

    The new data source name is displayed in the Enter Data Source text box of the ODBC Data Sources dialog box.

  7. Click OK.

    The new data source name is selected in the Available Data Sources text box of the Select Data Source dialog box.

  8. Click Use.

You can now add tables to the open query. For more information about building a query, see Importing Data into Microsoft Excel from a Visual FoxPro Database.


Importing Data into Microsoft Excel from a Visual FoxPro Database

You can import Visual FoxPro data into your Microsoft Excel worksheet if you have defined a data source for it. For information about creating a Visual FoxPro data source, see Accessing a Visual FoxPro Data Source from Microsoft Excel.

To import Visual FoxPro data into an Microsoft Excel worksheet

  1. Open a Microsoft Excel spreadsheet.
  2. From the Data menu, choose Get External Data. Microsoft Query opens.
  3. In the Select Data Source dialog box, select a Visual FoxPro data source and then click Use.
  4. If the database accessed by your data source includes tables, select a table from the Add Tables dialog box. Microsoft Query displays the added table in the top half of the query designer.

    Note The Owner list is not available in this dialog box because the driver does not support owners. The Database list is not available because the driver does not support multiple databases in a data source.

  5. Select fields for your query by dragging them from the table onto the lower half of the designer.
  6. Close Microsoft Query. The data you selected is imported into your Microsoft Excel spreadsheet.


Creating Mailing Labels in Microsoft Word Using Visual FoxPro Data

You can use Visual FoxPro data in a Microsoft Word for Windows 95 or Windows 98 document. For example, you might want to create mailing labels from the customer information stored in a Visual FoxPro table.

To create mailing labels

  1. In Microsoft Word, create a new blank document.
  2. From the Tools menu, choose Mail Merge.
  3. In the Mail Merge Helper, choose Create and then select Mailing Labels.
  4. Under Main Document, choose Active Window.
  5. Under Data Source, choose Get Data and then select Open Data Source.
  6. In the Open Data Source dialog box, choose MS Query.
  7. In the Select Data Source dialog box, select a Visual FoxPro data source and then click Use.
  8. If the database accessed by your data source includes tables, select a table from the Add Tables dialog box. Microsoft Query displays the added table in the top half of the query designer.
  9. Select fields for your query by dragging them from the table onto the lower half of the designer.
  10. From the File menu, choose Return Data to Microsoft Word. Microsoft Query closes, and the data you selected is available for use in your mail merge document.
  11. Under Main Document, choose Setup.
  12. In the Label Options dialog box, select the printer and label information you want and then click OK.
  13. In the Create Labels dialog box, select the fields you want to print on the mailing labels and then click OK.
  14. In the Mail Merge Helper, under Merge the Data with the Document, click Merge.
  15. In the Merge dialog box, select the options you want and then click Merge.


Technical Reference

This section of the Visual FoxPro ODBC Driver documentation contains the following topics:

  • Bookmark Support
  • Supported Concurrency Model
  • Supported Cursor Model
  • Supported Data Types
  • International Support
  • Support for Rules, Triggers, Default Values, and Stored Procedures
  • Supported ODBC SQL Grammar
  • Registry Entries
  • Supported Scalar Functions
  • Supported SET Commands
  • Thread Support
  • Unsupported FoxPro Commands and Functions
  • Error Messages
  • Troubleshooting


Bookmark Support

The Visual FoxPro ODBC Driver supports simple bookmarks. When you call SQLGetInfo with the SQL_BOOKMARK_PERSISTENCE InfoType, the return value is SQL_BP_SCROLL.

For information about bookmarks, see in Chapter 11 of the ODBC Programmer's Reference.


Supported Concurrency Model

The Visual FoxPro ODBC Driver supports read-only concurrency. Your application can call SQLSetStmtOption with a SQL_CONCURRENCY option of SQL_CONCUR_READ_ONLY.

For more information, see the ODBC Programmer's Reference.


Supported Cursor Model

The Visual FoxPro ODBC Driver supports both block (rowset) and static cursors. Static cursors are supported for any driver that conforms to Level 1 ODBC compliance. The driver does not support dynamic, keyset-driven, or mixed (keyset and dynamic) cursors.

Your application can call SQLSetStmtOption with a SQL_CURSOR_TYPE option of SQL_CURSOR_FORWARD_ONLY (block cursor) or SQL_CURSOR_STATIC (static cursor).

Note If you call SQLSetStmtOption with a SQL_CURSOR_TYPE option other than SQL_CURSOR_FORWARD_ONLY or SQL_CURSOR_STATIC, the function returns SQL_SUCCESS_WITH_INFO with a SQLSTATE of 01S02 (Option value changed). The driver sets all unsupported cursor modes to SQL_CURSOR_STATIC.

For more information about cursor types and about SQLSetStmtOption, see the ODBC Programmer's Reference.


Supported Data Types

The list of data types supported by the driver are presented through the ODBC API and in Microsoft Query.

Data Types in C Applications

You can obtain a list of data types supported by the Visual FoxPro ODBC Driver by using the SQLGetTypeInfo function in C or C++ applications.

Data Types in Applications Using Microsoft Query

If your application uses Microsoft Query to create a new table on a Visual FoxPro data source, Microsoft Query displays the New Table Definition dialog box. Under Field Description, the Type box lists Visual FoxPro field data types, represented by single characters.


International Support

The Microsoft Visual FoxPro ODBC Driver supports:

  • Double-byte character sets (DBCS)
  • Multiple collating sequences

A collating sequence defines the sort order for data stored in a Visual FoxPro table or database. By default, the driver is configured to use the collating sequences that support the language version of your operating system.

For a list of supported collating sequences, see SET COLLATE.


Support for Rules, Triggers, Default Values, and Stored Procedures

You cannot create Visual FoxPro rules, triggers, default values, or stored procedures using the Visual FoxPro ODBC Driver. However, your application might interact with existing rules, triggers, default values, or stored procedures as it inserts, updates, or deletes Visual FoxPro data stored in a database.

The following table lists the Visual FoxPro commands and functions supported by the Visual FoxPro ODBC Driver when the commands or functions exist in rules, triggers, default values, or stored procedures.

If your application interacts with data whose rules, triggers, default values, or stored procedures call any other Visual FoxPro commands or functions, the driver generates an error. See Unsupported Visual FoxPro Commands and Functions for a list of commands and functions not supported by the driver.

Tip If you want to insert conditional code into your rules, triggers, or stored procedures that determines the commands to execute when called by the driver, you can use the VERSION( ) function. The VERSION( ) function returns "Visual FoxPro ODBC Driver <version>" when called by the driver.

Visual FoxPro Commands and Functions Supported in Rules, Triggers, Default Values, and Stored Procedures

$ Operator | % Operator | & Command
&& Command | * Command | = Command

A

ABS( ) Function | ACOPY( ) Function | ADD TABLE Command
ADATABASES( ) Function | ADBOBJECTS( ) Function | AERROR( ) Function
ADEL( ) Function | AELEMENT( ) Function | ALEN( ) Function
AFIELDS( ) Function | AINS( ) Function | ALTER TABLE - SQL Command
ALIAS( ) Function | ALLTRIM( ) Function | APPEND FROM ARRAY Command
AND Operator | APPEND Command | APPEND MEMO Command
APPEND FROM Command | APPEND GENERAL Command | ASCAN( ) Function
APPEND PROCEDURES Command | ASC( ) Function | ASUBSCRIPT( ) Function
ASIN( ) Function | ASORT( ) Function | ATAN( ) Function
AT( ) Function | AT_C( ) Function | ATCLINE( ) Function
ATC( ) Function | ATCC( ) Function | AUSED( ) Function
ATLINE( ) Function | ATN2( ) Function |
AVERAGE Command | ACOS( ) Function |

B

BEGIN TRANSACTION Command | BETWEEN( ) Function | BITNOT( ) Function
BITCLEAR( ) Function | BITLSHIFT( ) Function | BITSET( ) Function
BITOR( ) Function | BITRSHIFT( ) Function | BLANK Command
BITTEST( ) Function | BITXOR( ) Function |
BOF( ) Function | BITAND( ) Function |

C

CALCULATE Command | CANDIDATE( ) Function | CHR( ) Function
CDX( ) Function | CEILING( ) Function | CLOSE Commands
CHRTRAN( ) Function | CHRTRANC( ) Function | COPY INDEXES Command
CMONTH( ) Function | CONTINUE Command | COPY STRUCTURE EXTENDED Command
COPY PROCEDURES Command | COPY STRUCTURE Command | COPY TO Command
COPY TAG Command | COPY TO ARRAY Command | CPCONVERT( ) Function
COS( ) Function | COUNT Command | CTOD( ) Function
CPCURRENT( ) Function | CPDBF( ) Function | CURSORSETPROP( ) Function
CTOT( ) Function | CURSORGETPROP( ) Function |
CURVAL( ) Function | CDOW( ) Function |

D

DATE( ) Function | DATETIME( ) Function | DAY( ) Function
DBC( ) Function | DBF( ) Function | DBGETPROP( ) Function
DBUSED( ) Function | DELETE - SQL Command | DELETE Command
DELETE TAG Command | DELETED( ) Function | DESCENDING( ) Function
DIFFERENCE( ) Function | DIMENSION Command | DISKSPACE( ) Function
DMY( ) Function | DO CASE ... ENDCASE Command | DO Command
DO WHILE ... ENDDO Command | DOW( ) Function | DTOC( ) Function
DTOR( ) Function | DTOS( ) Function | DTOT( ) Function

E

EMPTY( ) Function | EVALUATE( ) Function | EXIT Command
ERROR( ) Function | EXP( ) Function |
END TRANSACTION Command | EOF( ) Function |

F

FCOUNT( ) Function | FDATE( ) Function | FIELD( ) Function
FILE( ) Function | FILTER( ) Function | FLDLIST( ) Function
FLOCK( ) Function | FLOOR( ) Function | FLUSH Command
FOR ... ENDFOR Command | FOR( ) Function | FOUND( ) Function
FREE TABLE Command | FSIZE( ) Function | FTIME( ) Function
FULLPATH( ) Function | FUNCTION Command | FV( ) Function

G

GATHER Command | GETNEXTMODIFIED( ) Function | GO/GOTO Command
GETFLDSTATE( ) Function | GOMONTH( ) Function |
GETCP( ) Function | GETENV( ) Function |

H

HEADER( ) Function HOUR( ) Function

I

IDXCOLLATE( ) Function | IF ... ENDIF Command | IIF( ) Function
INDBC( ) Function | INDEX Command | INLIST( ) Function
INSERT-SQL Command | INT( ) Function | ISALPHA( ) Function
ISBLANK( ) Function | ISDIGIT( ) Function | ISEXCLUSIVE( ) Function
ISLEADBYTE( ) Function | ISLOWER( ) Function | ISNULL( ) Function
ISREADONLY( ) Function | ISUPPER( ) Function |

K

KEY( ) Function KEYMATCH( ) Function

L

LEFT( ) Function | LEFTC( ) Function | LIKEC( ) Function
LENC( ) Function | LIKE( ) Function | LOCK( ) Function
LOCAL Command | LOCATE Command | LOOKUP( ) Function
LOG( ) Function | LOG10( ) Function | LTRIM( ) Function
LOWER( ) Function | LPARAMETERS Command |
LUPDATE( ) Function | LEN( ) Function |

M

_MLINE System Memory Variable | MAX( ) Function | MDX( ) Function
MDY( ) Function | MEMLINES( ) Function | MESSAGE( ) Function
MIN( ) Function | MINUTE( ) Function | MLINE( ) Function
MOD( ) Function | MONTH( ) Function | MTON( ) Function

N

NDX( ) Function | NORMALIZE( ) Function | NOT Operator
NOTE Command | NTOM( ) Function | NVL( ) Function

O

OCCURS( ) Function | OLDVAL( ) Function | ON ERROR Command
ON KEY Command | ON( ) Function | OPEN DATABASE Command
OR Operator | ORDER( ) Function | OS( ) Function

P

PACK Command | PARAMETERS( ) Function | PAYMENT( ) Function
PARAMETERS Command | PRIMARY( ) Function | PRIVATE Command
PI( ) Function | PROGRAM( ) Function | PROPER( ) Function
PROCEDURE Command | PV( ) Function |
PUBLIC Command | PADL( ) | PADR( ) | PADC( ) Functions |

R

RAND( ) Function | RAT( ) Function | RATC( ) Function
RATLINE( ) Function | RECALL Command | RECCOUNT( ) Function
RECNO( ) Function | RECSIZE( ) Function | REGIONAL Command
RELATION( ) Function | REMOVE TABLE Command | REPLACE Command
REPLACE FROM ARRAY Command | REPLICATE( ) Function | RETRY Command
RETURN Command | RIGHT( ) Function | RIGHTC( ) Function
RLOCK( ) Function | ROLLBACK Command | ROUND( ) Function
RTOD( ) Function | RTRIM( ) Function |

S

SCAN ... ENDSCAN Command | SCATTER Command | SEC( ) Function
SECONDS( ) Function | SEEK Command | SEEK( ) Function
SELECT Command | SELECT( ) Function | SELECT-SQL Command
SET BLOCKSIZE Command | SET CARRY Command | SET CENTURY Command
SET COLLATE Command | SET DATABASE Command | SET DATE Command
SET DEFAULT Command | SET DELETED Command | SET EXACT Command
SET EXCLUSIVE Command | SET FDOW Command | SET FIELDS Command
SET FILTER Command | SET FIXED Command | SET FULLPATH Command
SET FWEEK Command | SET HOURS Command | SET INDEX Command
SET LOCK Command | SET MULTILOCKS Command | SET NEAR Command
SET NOCPTRANS Command | SET NOTIFY Command | SET NULL Command
SET OPTIMIZE Command | SET ORDER Command | SET PATH Command
SET PROCEDURE Command | SET RELATION Command | SET RELATION OFF Command
SET REPROCESS Command | SET SKIP Command | SET UDFPARMS Command
SET UNIQUE Command | SET VOLUME Command | SET( ) Function
SETFLDSTATE( ) Function | SIGN ( ) Function | SIN( ) Function
SKIP Command | SORT Command | SPACE( ) Function
SQRT( ) Function | STORE Command | STR( ) Function
STRCONV( ) Function | STRTRAN( ) Function | STUFF( ) Function
STUFFC( ) Function | SUBSTR( ) Function | SUBSTRC( ) Function
SUM Command | SYS(2011) Function |

T

_TALLY System Memory Variable | _TRIGGERLEVEL System Memory Variable | TAGCOUNT( ) Function
TABLEUPDATE( ) Function | TAG( ) Function | TARGET( ) Function
TAGNO( ) Function | TAN( ) Function | TRIM( ) Function
TIME( ) Function | TOTAL Command | TXNLEVEL( ) Function
TTOC( ) Function | TTOD( ) Function |
TYPE( ) Function | TABLEREVERT( ) Function |

U

UNIQUE( ) Function | UNLOCK Command | USE Command
UPDATE Command | UPPER( ) Function |
USED( ) Function | UPDATE - SQL Command |

V

VAL( ) Function VERSION( ) Function

W

WEEK( ) Function

Y

YEAR( ) Function

Z

ZAP Command


Supported ODBC SQL Grammar

The Microsoft Visual FoxPro ODBC Driver supports the following:

  • All SQL statements and clauses in the ODBC minimum SQL grammar
  • An additional SQL statement from the ODBC core SQL grammar

The following table lists the items supported by the driver, by ODBC SQL Grammar level.

Level | Elements | Item
Minimum | Data Definition Language (DDL) | CREATE TABLE and DROP TABLE
 | Data Manipulation Language (DML) | SELECT, INSERT, UPDATE, and DELETE
 | Expressions | Simple (such as A>B+C)
 | Data types | CHAR, VARCHAR, or LONG VARCHAR

In addition to the supported ODBC SQL grammar, the Visual FoxPro ODBC Driver supports the complete native Visual FoxPro language syntax for the following Visual FoxPro commands:

ALTER TABLE

CREATE TABLE

DELETE

DELETE TAG

DROP TABLE

INDEX

SELECT

UPDATE


Registry Entries

When you install the Visual FoxPro ODBC Driver, the installation program updates your system's registry, in the registry key HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCInst.ini, to add a new key called Microsoft Visual FoxPro Driver. Under that key, the values described in the following table are added.

Value name | Value type | Value
APILevel | REG_SZ | "1"
ConnectFunctions | REG_SZ | "YYN"
Driver | REG_SZ | System path to the vfpodbc.dll file
DriverODBCVer | REG_SZ | "02.50"
FileExtns | REG_SZ | "*.dbf,*.cdx,*.fpt"
FileUsage | REG_SZ | "1"
Setup | REG_SZ | System path to the vfpodbc.dll file
SQLLevel | REG_SZ | "0"

The installation program also adds the key "Visual FoxPro Files", representing the default Visual FoxPro driver, to your system's HKEY_CURRENT_USER\SOFTWARE\ODBC\Odbc.ini key. Under this key, the installation program adds the values described in the following table.

Value name | Value type | Value
Driver | REG_SZ | System path to the vfpodbc.dll file

Each time you add a Visual FoxPro ODBC data source to your ODBC configuration, a new key is added for that data source name. The values for the data source correspond to values you set in the ODBC Visual FoxPro Setup dialog box, as listed in the following table.

Value name (keyword) | Value type | Value
Collate | REG_SQ | Any supported collating sequence
Description | REG_SZ | User description of data source
Driver |  | System path to the vfpodbc.dll file
Exclusive |  | Yes or No
BackgroundFetch |  | Yes or No
SourceDB | REG_SZ | Path to .DBC file
SourceType | REG_SZ | "DBC" or "DBF"

You should not access this information directly; any administration of the registry is handled by the ODBC Administrator when you add, modify, or delete a data source.

You can use some of these keywords and values as parameters in the SQLDriverConnect ODBC API function.


Supported Scalar Functions

The Visual FoxPro ODBC Driver supports the following types of scalar functions as defined in the ODBC SQL grammar:

  • String functions
  • Numeric functions
  • Time and date functions


String Functions

The following table lists ODBC string manipulation functions supported by the Visual FoxPro ODBC Driver; when the Visual FoxPro grammar for the same function differs from the ODBC syntax, the Visual FoxPro equivalent is listed.

ODBC grammar Visual FoxPro grammar
ASCII (string_exp)

ASC (string_exp)

CHAR (code)

CHR (string_exp)

CONCAT (string_exp1, string_exp2)

string_exp1 + string_exp2

DIFFERENCE (string_exp1, string_exp2)

INSERT (string_exp1, start, length, string_exp2)

STUFF (string_exp1, start, length, string_exp2)

LCASE (string_exp)

LOWER (string_exp)

LEFT (string_exp, count)

LENGTH (string_exp)

LEN (string_exp)

LTRIM (string_exp)

REPEAT (string_exp, count)

REPLICATE (string_exp, count)

REPLACE (string_exp1, string_exp2, string_exp3)

STRTRAN (string_exp1, string_exp2, string_exp3)

RIGHT (string_exp, count)

RTRIM (string_exp)

SOUNDEX (string_exp)

SPACE (count)

SUBSTRING (string_exp, start, length)

SUBSTR (string_exp, start, length)

UCASE (string_exp)

UPPER (string_exp)


Numeric Functions

The following table describes ODBC numeric functions supported by the Visual FoxPro ODBC Driver; when the Visual FoxPro grammar for the same function differs from the ODBC syntax, the Visual FoxPro equivalent is listed.

ODBC grammar Visual FoxPro grammar
ABS (numeric_exp)

ACOS (float_exp)

ASIN (float_exp)

ATAN (float_exp)

ATAN2 (float_exp1, float_exp2)

ATN2 (float_exp1, float_exp2)

CEILING (numeric_exp)

COS (float_exp)

COT (float_exp)

DEGREES (numeric_exp)

RTOD (numeric_exp)

EXP (float_exp)

FLOOR (numeric_exp)

LOG (float_exp)

LOG10 (float_exp)

MOD (integer_exp1, integer_exp2)

PI ( )

RADIANS (numeric_exp)

DTOR (numeric_exp)

RAND ([integer_exp])

ROUND (numeric_exp, integer_exp)

SIGN (numeric_exp)

SIN (float_exp)

SQRT (float_exp)

TAN (float_exp)

The following numeric functions are not supported:

POWER (numeric_exp, integer_exp)
TRUNCATE (numeric_exp, integer_exp)


Time and Date Functions

The following table lists ODBC time and date functions supported by the Visual FoxPro ODBC Driver; when the Visual FoxPro grammar for the same function differs from the ODBC syntax, the Visual FoxPro equivalent is listed.

ODBC grammar Visual FoxPro grammar
CURDATE( )

DATE( )

CURTIME( )

TIME( )

DAYNAME(date_exp)

CDOW(date_exp)

DAYOFMONTH(date_exp)

DAY( )

HOUR(time_exp)

MINUTE(time_exp)

MONTH(time_exp)

MONTHNAME(date_exp)

CMONTH(date_exp)

NOW( )

DATETIME( )

SECOND(time_exp)

SEC(time_exp)

WEEK(date_exp)

YEAR(date_exp)

The following time and date functions are not supported:

DAYOFYEAR (date_exp)
QUARTER (date_exp)
TIMESTAMPADD (interval, integer_exp, timestamp_exp)
TIMESTAMPDIFF (interval, timestamp_exp1, timestamp_exp2)

ODBC Escape Sequences

The driver also supports the ODBC escape sequence for date and timestamp data. The escape clause syntax is as follows:

--(*vendor(Microsoft),product(ODBC) d 'value' *)—
--(*vendor(Microsoft),product(ODBC) ts ''value' *)—

In this syntax, d indicates that value is a date in the yyyy-mm-dd format and ts indicates that value is a timestamp in the yyyy-mm-dd hh:mm:ss[.f…] format. The shorthand syntax for date and timestamp data is as follows:

{d 'value'}
{ts 'value'}

For example, each of the following statements updates the ALLTYPES table by using the date and timestamp shorthand syntax in a supported SQL UPDATE command:

UPDATE alltypes
   SET DAT_COL={d'1968-04-28'}
   WHERE KEY=111

UPDATE alltypes
   SET DTI_COL={ts'1968-04-28 12:00:00'}
   WHERE KEY=111

For more information about escape sequences, see Escape Sequences in ODBC in the ODBC Programmer's Reference.


Supported SET Commands

Your application can send the following Visual FoxPro SET commands to a data source:

SET ANSI

SET BLOCKSIZE

SET COLLATE

SET DELETED

SET EXACT

SET EXCLUSIVE

SET NULL

SET PATH

SET REPROCESS

SET UNIQUE


Thread Support

The Visual FoxPro ODBC Driver is thread-safe. Access to environment handles (henv), connection handles (hdbc), and statement handles (hstmt) is wrapped in appropriate semaphores to prevent other processes from accessing and potentially altering the driver's internal data structures.

In a multithreaded application, you can cancel a function that is running synchronously on an hstmt by calling SQLCancel on a separate thread.

The driver uses a separate thread to fetch data when you use progressive fetching. To employ progressive fetching for a data source, check the Fetch data in background checkbox on the ODBC Visual FoxPro Setup dialog box or use the BackgroundFetch attribute keyword in your connection string. Avoid using background fetch when calling the driver from multithreaded applications. For information on connection string attribute keywords, see Using Connection Strings.

For more information about threads and SQLCancel, see SQLCancel in the ODBC Programmer's Reference.


Unsupported Visual FoxPro Commands and Functions

The following table lists FoxPro commands and functions that are not supported by the Visual FoxPro ODBC Driver but are supported by Microsoft® Visual FoxPro®.

If your application interacts with data whose rules, triggers, default values, or stored procedures call these Visual FoxPro commands or functions, the driver can generate an error.

Unsupported Visual FoxPro Commands and Functions

#DEFINE ... #UNDEF | #IF ... #ENDIF Preprocessor Directive | #IFDEF | #IFNDEF
#INCLUDE Preprocessor Directive | :: Scope Resolution Operator | ! Command (see RUN | ! Command)
? | ?? Command | ??? Command | \ | \\ Command
@ ... BOX Command | @ ... CLASS Command | @ ... CLEAR Command
@ ... EDIT - Edit Boxes Command | @ ... FILL Command | @ ... GET
@ ... MENU Command | @ ... PROMPT Command | @ ... SAY Command
@ ... SCROLL Command | @ ... TO Command |

A

ACCEPT Command | ACLASS( ) Function | ACTIVATE MENU Command
ACTIVATE POPUP Command | ACTIVATE SCREEN Command | ACTIVATE WINDOW Command
ActivateCell Method | ADD CLASS Command | ADIR( ) Function
AFONT( ) Function | AINSTANCE( ) Function | _ALIGNMENT System Memory Variable
AMEMBERS( ) Function | ANSITOOEM( ) Function | APRINTERS( ) Function
ASELOBJ( ) Function | ASSIST Command |

B

BAR( ) Function | BARCOUNT( ) Function | BARPROMPT( ) Function
_BEAUTIFY System Memory Variable | _BOX System Memory Variable | BROWSE Command
_BROWSER System Memory Variable | BUILD APP Command | BUILD EXE Command
BUILD PROJECT Command | _BUILDER System Memory Variable |

C

_CALCVALUE System Memory Variable | _CLIPTEXT System Memory Variable | _CONVERTER System Memory Variable
_CUROBJ System Memory Variable | CALL Command | CANCEL Command
CAPSLOCK( ) Function | CD Command | CHANGE Command
CHDIR Command | CHRSAW( ) Function | CLOSE MEMO Command
CNTBAR( ) Function | CNTPAD( ) Function | COL( ) Function
COMPILE Command | COMPILE DATABASE Command | COMPILE FORM Command
COMPOBJ( ) Function | Container Object | Control Object
COPY FILE Command | COPY MEMO Command | CREATE CLASS Command
CREATE CLASSLIB Command | CREATE COLOR SET Command | CREATE Command
CREATE CONNECTION Command | CREATE DATABASE Command | CREATE FORM Command
CREATE FROM Command | CREATE LABEL Command | CREATE MENU Command
CREATE PROJECT Command | CREATE QUERY Command | CREATE REPORT Command
CREATE SCREEN Command | CREATE SQL VIEW Command | CREATE TRIGGER Command
CREATE VIEW Command | CREATEOBJECT( ) Function | CURDIR( ) Function

D

_DBLCLICK System Memory Variable | _DIARYDATE System Memory Variable | DBSETPROP( ) Function
DDE Functions | DEACTIVATE MENU Command | DEACTIVATE POPUP Command
DEACTIVATE WINDOW Command | DECLARE - DLL Command | DECLARE Command
DEFINE BAR Command | DEFINE BOX Command | DEFINE CLASS Command
DEFINE MENU Command | DEFINE PAD Command | DEFINE POPUP Command
DEFINE WINDOW Command | DELETE CONNECTION Command | DELETE DATABASE Command
DELETE FILE Command | DELETE TRIGGER Command | DELETE VIEW Command
DIR Command | DIRECTORY Command | DISPLAY Command
DISPLAY CONNECTIONS Command | DISPLAY DATABASE Command | DISPLAY DLLS Command
DISPLAY FILES Command | DISPLAY MEMORY Command | DISPLAY OBJECTS Command
DISPLAY PROCEDURES Command | DISPLAY STATUS Command | DISPLAY STRUCTURE Command
DISPLAY TABLES Command | DISPLAY VIEWS Command | DO FORM Command

E

EDIT Command | ERROR Command |
ERASE Command | EXTERNAL Command | EXPORT Command
EJECT Command | EJECT PAGE Command |

F

_FOXDOC System Memory Variable | _FOXGRAPH System Memory Variable | FEOF( ) Function
FCLOSE( ) Function | FCREATE( ) Function | FGETS( ) Function
FERROR( ) Function | FFLUSH( ) Function | FKLABEL( ) Function
FILER Command | FIND Command | FOPEN( ) Function
FKMAX( ) Function | FONTMETRIC( ) Function | FSEEK( ) Function
FPUTS( ) Function | FREAD( ) Function |
FWRITE( ) Function | FCHSIZE( ) Function |

G

_GENGRAPH System Memory Variable | _GENMENU System Memory Variable | _GENPD System Memory Variable
_GENSCRN System Memory Variable | _GENXTAB System Memory Variable | GETBAR( ) Function
GETCOLOR( ) Function | GETDIR( ) Function | GETEXPR Command
GETFILE( ) Function | GETFONT( ) Function | GETOBJECT( ) Function
GETPAD( ) Function | GETPICT( ) Function | GETPRINTER( ) Function

H

HELP Command | HIDE MENU Command | HIDE POPUP Command
HIDE WINDOW Command | HOME( ) Function |

I

IMESTATUS( ) Function | IMPORT Command | INPUT Command
INDEX ON Command | INKEY( ) Function | ISCOLOR( ) Function
INSERT Command | INSMODE( ) Function |
ISMOUSE( ) Function | _INDENT System Memory Variable |

J

JOIN Command

K

KEYBOARD Command

L

_LMARGIN System Memory Variable | LABEL Command | LASTKEY( ) Function
LINENO( ) Function | LIST Commands | LIST CONNECTIONS Command
LOAD Command | LOCFILE( ) Function |

M

MCOL( ) Function | MD Command | MENU TO Command
MEMORY( ) Function | MENU Command | MKDIR Command
MENU( ) Function | MESSAGEBOX( ) Function | MODIFY CONNECTION Command
MODIFY CLASS Command | MODIFY COMMAND Command | MODIFY FORM Command
MODIFY DATABASE Command | MODIFY FILE Command | MODIFY MEMO Command
MODIFY GENERAL Command | MODIFY LABEL Command | MODIFY PROJECT Command
MODIFY MENU Command | MODIFY PROCEDURE Command | MODIFY SCREEN Command
MODIFY QUERY Command | MODIFY REPORT Command | MODIFY WINDOW Command
MODIFY STRUCTURE Command | MODIFY VIEW Command | MOVE WINDOW Command
MOUSE Command | MOVE POPUP Command | MROW( ) Function
MRKBAR( ) Function | MRKPAD( ) Function |
MWINDOW( ) Function | MDOWN( ) Function |

N

NUMLOCK( ) Function

O

OBJNUM( ) Function | OBJTOCLIENT( ) Function | ON BAR Command
OEMTOANSI( ) Function | ON APLABOUT Command | ON EXIT MENU Command
ON ESCAPE Command | ON EXIT BAR Command | ON KEY = Command
ON EXIT PAD Command | ON EXIT POPUP Command | ON PAD Command
ON KEY LABEL Command | ON MACHELP Command | ON SELECTION BAR Command
ON PAGE Command | ON READERROR Command | ON SELECTION POPUP Command
ON SELECTION MENU Command | ON SELECTION PAD Command |
ON SHUTDOWN Command | OBJVAR( ) Function |

P

_PADVANCE System Memory Variable | _PAGENO System Memory Variable | _PBPAGE System Memory Variable
_PCOLNO System Memory Variable | _PCOPIES System Memory Variable | _PDRIVER System Memory Variable
_PDSETUP System Memory Variable | _PECODE System Memory Variable | _PEJECT System Memory Variable
_PEPAGE System Memory Variable | _PLENGTH System Memory Variable | _PLINENO System Memory Variable
_PLOFFSET System Memory Variable | _PPITCH System Memory Variable | _PQUALITY System Memory Variable
_PRETEXT System Memory Variable | _PSCODE System Memory Variable | _PSPACING System Memory Variable
_PWAIT System Memory Variable | PACK DATABASE Command | PAD( ) Function
PCOL( ) Function | PEMSTATUS( ) Function | PLAY MACRO Command
POP KEY Command | POP MENU Command | POP POPUP Command
POPUP( ) Function | PRINTJOB ... ENDPRINTJOB Command | PRINTSTATUS( ) Function
PRMBAR( ) Function | PRMPAD( ) Function | PROMPT( ) Function
PROW( ) Function | PRTINFO( ) Function | PUSH KEY Command
PUSH MENU Command | PUSH POPUP Command | PUTFILE( ) Function

Q

QUIT Command

R

_RMARGIN System Memory Variable | RD Command | READKEY( ) Function
READ Command | READ MENU Command | RELEASE BAR Command
REFRESH() Function | REINDEX Command | RELEASE LIBRARY Command
RELEASE CLASSLIB Command | RELEASE Command | RELEASE PAD Command
RELEASE MENUS Command | RELEASE MODULE Command | RELEASE WINDOWS Command
RELEASE POPUPS Command | RELEASE PROCEDURE Command | RENAME Command
REMOVE CLASS Command | RENAME CLASS Command | RENAME VIEW Command
RENAME CONNECTION Command | RENAME TABLE Command | RESTORE FROM Command
REPORT Command | REQUERY( ) Function | RESTORE WINDOW Command
RESTORE MACROS Command | RESTORE SCREEN Command | RGBSCHEME( ) Function
RESUME Command | RGB( ) Function | RUN | ! Command
RMDIR Command | ROW( ) Function |
RUNSCRIPT Command | RDLEVEL( ) Function |

S

SAVE MACROS Command | SAVE SCREEN Command | SAVE TO Command
SAVE WINDOWS Command | SCHEME( ) Function | SCOLS( ) Function
SCROLL Command | _SCREEN System Memory Variable | SET Command
SET ALTERNATE Command | SET ANSI Command | SET APLABOUT Command
SET AUTOSAVE Command | SET BELL Command | SET BLINK Command
SET BORDER Command | SET BRSTATUS Command | SET CLASSLIB Command
SET CLEAR Command | SET CLOCK Command | SET COLOR OF Command
SET COLOR OF SCHEME Command | SET COLOR SET Command | SET COLOR TO Command
SET COMPATIBLE Command | SET CONFIRM Command | SET CONSOLE Command
SET CPCOMPILE | SET CPDIALOG | SET CURRENCY Command
SET CURSOR Command | SET DATASESSION Command | SET DEBUG Command
SET DECIMALS Command | SET DELIMITERS Command | SET DEVELOPMENT Command
SET DEVICE Command | SET DISPLAY Command | SET DOHISTORY Command
SET ECHO Command | SET ESCAPE Command | SET FORMAT Command
SET FUNCTION Command | SET HEADINGS Command | SET HELP Command
SET HELPFILTER Command | SET INTENSITY Command | SET KEY Command
SET KEYCOMP Command | SET LOGERRORS Command | SET MACDESKTOP Command
SET MACHELP Command | SET MACKEY Command | SET MARGIN Command
SET MARK OF Command | SET MARK TO Command | SET MEMOWIDTH Command
SET MESSAGE Command | SET MOUSE Command | SET ODOMETER Command
SET OLEOBJECT Command | SET PALETTE Command | SET PDSETUP Command
SET POINT Command | SET PRINTER Command | SET READBORDER Command
SET REFRESH Command | SET RESOURCE Command | SET SAFETY Command
SET SCOREBOARD Command | SET SECONDS Command | SET SEPARATOR Command
SET SHADOWS Command | SET SKIP OF Command | SET SPACE Command
SET STATUS Command | SET STATUS BAR Command | SET STEP Command
SET STICKY Command | SET SYSFORMATS Command | SET SYSMENU Command
SET TALK Command | SET TEXTMERGE Command | SET TEXTMERGE DELIMITERS Command
SET TOPIC Command | SET TOPIC ID Command | SET TRBETWEEN Command
SET TYPEAHEAD Command | SET VIEW Command | SET WINDOW OF MEMO Command
SET XCMDFILE Command | _SHELL System Memory Variable | SHOW GET Command
SHOW GETS Command | SHOW MENU Command | SHOW OBJECT Command
SHOW POPUP Command | SHOW WINDOW Command | SIZE POPUP Command
SIZE WINDOW Command | SKPBAR( ) Function | SKPPAD( ) Function
SOUNDEX( ) Function | _SPELLCHK System Memory Variable | SQL functions
SROWS( ) Function | _STARTUP System Memory Variable | SUSPEND Command
SYS() Functions except SYS(2011) | SYSMETRIC( ) Function |

T

_TABS System Memory Variable | TEXT ... ENDTEXT Command | TXTWIDTH( ) Function
TRANSFORM( ) Function | _TRANSPORT System Memory Variable |
TYPE Command | _THROTTLE System Memory Variable |

U

UPDATED( ) Function USE Command

V

VALIDATE DATABASE Command VARREAD( ) Function VERSION( ) Function

W

_WINDOWS System Memory Variable | _WIZARD System Memory Variable | WCHILD( ) Function
WAIT Command | WBORDER( ) Function | WFONT( ) Function
WCOLS( ) Function | WEXIST( ) Function | WLROW( ) Function
WITH ... ENDWITH Command | WLAST( ) Function | WONTOP( ) Function
WMAXIMUM( ) Function | WLCOL( ) Function | WREAD( ) Function
WOUTPUT( ) Function | WMINIMUM( ) Function | WVISIBLE( ) Function
WPARENT( ) Function | WTITLE( ) Function |
WROWS( ) Function | _WRAP System Memory Variable |

Z

ZOOM WINDOW Command


Error Messages

When an error occurs, the Visual FoxPro driver returns the following information:

  • The native error number and error message text
  • The SQLSTATE (an ODBC error code) and error message text

You access this error information by calling SQLError.

Native Errors

For errors that occur in the data source, the Visual FoxPro driver returns the native error number and error message text. For a list of native error numbers, see Visual FoxPro ODBC Driver Native Error Messages.

SQLSTATE (ODBC Error Codes)

For errors that are detected and returned by the Visual FoxPro driver, the driver maps the returned native error number to the appropriate SQLSTATE. If a native error number does not have an ODBC error code to map to, the Visual FoxPro driver returns SQLSTATE S1000 (General error).

For a list of SQLSTATE values generated by the Visual FoxPro ODBC Driver for corresponding Visual FoxPro errors, see ODBC Error Codes.

Syntax

Error messages have the following format:

[vendor][ODBC_component]error_message

The prefixes in brackets ([ ]) identify the source of the error as defined in the following table.

Data source Prefix Value
Driver Manager [vendor]
[ODBC_component]
[data_source] 		[Microsoft]
[ODBC Driver Manager]
N/A
Visual FoxPro driver vendor]
[ODBC_component]
[data_source] 		[Microsoft]
[ODBC Visual FoxPro driver]
N/A

For example, if the Visual FoxPro ODBC Driver could not find the file employee.dbf, it might return the following error message:

"[Microsoft][ODBC Visual FoxPro Driver]File 'employee.dbf' does not exist"


Visual FoxPro ODBC Driver Native Error Messages

The following tables list error messages native to the Visual FoxPro ODBC Driver.

001

1 | Feature is not available.
2 | Input/output operation failure.
3 | Free handle is not found.
5 | Use of unallocated handle.
99 | Procedure canceled.

100

100 | Too many files open.
101 | Cannot open file.
102 | Cannot create file.
105 | Error writing to file.
107 | Invalid key length.
109 | Record is out of range.
110 | Record is not in index.
111 | Invalid file descriptor.
113 | File is not open.
114 | Not enough disk space for value.
115 | Invalid operation for the cursor.
118 | Index file does not match table.
119 | No table is open.
120 | File does not exist.
121 | File already exists.
122 | Table has no index order set.
123 | Not a table.
125 | Index expression exceeds maximum length.
127 | You must use a logical expression with a FOR or WHILE clause.
128 | Not a numeric expression.
129 | Variable is not found.
132 | File is in use.
133 | Index does not match the table. Delete the index file and re-create the index.
135 | End of file encountered.
136 | Beginning of file encountered.
137 | Alias is not found.
139 | You must use a logical expression with FILTER.
142 | Cyclic relation.
143 | No fields were found to copy.
144 | The LOCATE command must be issued before the CONTINUE command.
145 | Must be a character or numeric key field.
146 | Cannot write to a read-only file.
147 | Target table is already engaged in a relation.
148 | Expression has been re-entered while the filter is executing.
149 | Not enough memory for buffer.
150 | Not enough memory for file map.
155 | Invalid buffdirty call.
156 | Duplicate field names.
158 | No fields found to process.
159 | Numeric overflow. Data was lost.
162 | Procedure 'value' is not found.
165 | value is not related to the current work area.
170 | Variable 'value' is not found.
171 | Cannot open file value.
173 | File 'value' does not exist.
174 | 'value' is not a memory variable.
175 | 'value' is not a file variable.
176 | 'value' is not an array.
177 | Alias 'value' is not found.
180 | File was not placed in memory using the LOAD command.
182 | There is not enough memory to complete this operation.

200

200 | Syntax error.
201 | Too many names used.
202 | Program is too large.
203 | Too many memory variables.
205 | Nesting error.
206 | Recursive macro definition.
209 | Line is too long.
210 | Allowed DO nesting level exceeded.
211 | An IF | ELSE | ENDIF statement is missing.
212 | Structure nesting is too deep.
213 | There is a missing keyword in the FOR...ENDFOR or DO CASE...ENDCASE command structure.
219 | Command contains unrecognized phrase/keyword.
221 | Command is missing required clause.
222 | Unrecognized command verb.
224 | Invalid subscript reference.
227 | Missing expression.
228 | Table number is invalid.
229 | Too few arguments.
230 | Too many arguments.
233 | Statement is not allowed in interactive mode.
234 | Subscript is outside defined range.
236 | Suspend program before using RESUME.
238 | No PARAMETER statement is found.
239 | Must specify additional parameters.
240 | Not a character expression.
250 | Too many PROCEDURE commands are in effect.
252 | Compiled code for this line is too long.
257 | Key string is too long.
291 | Expression used with ASIN() is out of range.
292 | Cannot use 0 or negative as the argument for LOG10().
293 | Expression used with ACOS() is out of range.
294 | FOXUSER.DBF file is invalid.
295 | Invalid path or file name.
296 | Error reading the resource.
297 | Command is allowed only in interactive mode.

300

301 | Operator/operand type mismatch.
302 | Data type mismatch.
305 | Expression evaluated to an illegal value.
307 | Cannot divide by 0.
308 | Insufficient stack space.
337 | Cannot nest the PRINTJOB command.

400

406 | Printer is not ready.
407 | Invalid argument used with the SET function.
410 | Unable to create temporary work files.
423 | Error creating the OLE object.
424 | Error copying the OLE object to the Clipboard.
462 | value internal consistency error.
465 | SQL pass-through internal consistency error.
466 | Connection handle is invalid.
467 | Property is invalid for local cursors.
468 | Property is invalid for table cursors.
469 | Property value is out of bounds.
470 | Incorrect property name.
471 | Incorrect column format.
473 | Environment-level property is invalid.
474 | Invalid call issued while executing a SQLEXEC() sequence.
479 | Invalid update column name \value\.
489 | General fields cannot be used in the WHERE condition of an update statement. Change the WhereType property of the view.
491 | No update tables are specified. Use the Tables property of the cursor.
492 | No key columns are specified for the update table \value\. Use the KeyFieldList property of the cursor.
493 | SQL parameter is missing.
494 | View definition has been changed.
495 | Warning: The key defined by the KeyField property for table value is not unique.
498 | SQL SELECT statement is invalid.
499 | SQL parameter value is invalid.

500

502 | Cannot write to the record because it is in use.
503 | File cannot be locked.
508 | Error initializing OLE.
520 | No database is open or set as the current database.
522 | Connectivity internal consistency error.
523 | Execution was canceled by the user.
525 | Function is not supported on remote tables.
526 | Connectivity error: value.
527 | Cannot load ODBC library, ODBC32.DLL.
528 | ODBC entry point missing, value.
530 | Fetching canceled; remote table is closed.
532 | Type conversion is not supported.
533 | This property is read-only.
536 | Function is not supported on native tables.
538 | A stored procedure is executing.
540 | Session number is invalid.
541 | Connection value is busy.
542 | Base table fields have been changed and no longer match view fields. View field properties cannot be set.
543 | Type conversion required by the DataType property for field 'value' is invalid.
544 | DataType property for field 'value' is invalid.
545 | Table buffer for alias \value\ contains uncommitted changes.
546 | Cannot close table during execution of table-bound expression.
547 | Cannot insert an empty row from a view into its base table(s).
548 | Table value has one or more non-structural indexes open. Please close them and retry the Begin Transaction.
549 | Data session #value cannot be released with open transaction(s).
550 | .DBC internal consistency error.
557 | The database must be opened exclusively.
559 | Property is not found.
560 | Property value is invalid.
561 | Database is invalid. Please validate.
562 | Cannot find object value in the database.
563 | Cannot find view value in the current database.
566 | Cannot issue the PACK command on a database while its tables are in use.
567 | Primary key property is invalid; please validate database.
570 | Database is read-only.
571 | The name value is already used for another
575 | Object name is invalid.
577 | Table value is referenced in a relation.
578 | Invalid database table name.
579 | Command cannot be issued on a table with cursors in table buffering mode.
580 | Feature is not supported for non-.DBC tables.
581 | Field value does not accept null value.
583 | Record validation rule is violated.
585 | Update conflict. Use TABLEUPDATE() with the lForce parameter to commit the update or TABLEREVERT() to roll back the update.
586 | Function requires row or table buffering mode.
587 | Illegal nested OLDVAL() or CURVAL().
589 | Table or row buffering requires that SET MULTILOCKS is set to ON.
590 | BEGIN TRANSACTION command failed. Nesting level is too deep.
591 | END TRANSACTION command cannot be issued without a corresponding BEGIN TRANSACTION command.
592 | ROLLBACK command cannot be issued without a corresponding BEGIN TRANSACTION command.
593 | Command cannot be issued within a transaction.
594 | Illegal to attempt a file lock in a transaction after taking prior record locks.
596 | Table buffering is not enabled.
597 | Views require either DB_BUFOPTROW or DB_BUFOPTTABLE.
598 | Rule and trigger code must balance transaction usage.
599 | Data session #value was forced to ROLLBACK all transactions to avoid deadlock.

600

601 | Alias name is already in use.
602 | Operation is invalid for a Memo, General, or Picture field.
612 | No such menu or menu item is defined.
618 | Menu has not been defined with DEFINE MENU.
624 | Menu title has not been defined with DEFINE PAD.
625 | Menu has not been defined with DEFINE POPUP.
631 | Array dimensions are invalid.
637 | File must be opened exclusively to convert the Memo file.
638 | Field must be a Memo field.
649 | No previous PRINTJOB command to correspond to this command.
651 | CANCEL or SUSPEND is not allowed.
659 | The table has memo fields that cannot be converted while open read-only.
683 | Index tag is not found.

700

700 | Record is in use by another user.
701 | File must be opened exclusively.
702 | File is in use by another user.
703 | Record is not locked.
705 | File access is denied.
706 | Cannot sort .IDX files in descending order.
707 | Structural .CDX file is not found.
708 | File is open in another work area.
712 | Field name is a duplicate or invalid.
714 | Window 'value' has not been defined.
718 | File is read-only.
722 | Preprocessor expression is invalid.
734 | Property value is not found.
737 | value is a method, event, or object.
738 | Property value is not a method or event.
740 | value is a read-only property.
748 | This file is incompatible with the current version of Visual FoxPro.
750 | File was created in a later version of Visual FoxPro than the current version.
763 | Property value already exists.
773 | Database object type is invalid.
784 | This object is derived from a base class and does not have a parent class.

800

802 | SQL: Cannot locate table.
872 | Too many columns.
879 | No primary key.
884 | Uniqueness of index value is violated.
885 | Only structural tags can be defined as candidate.
886 | Index does not accept NULL.
887 | Illegal recursion in rule evaluation.
888 | Tag name is too long.

900

901 | Function argument value, type, or count is invalid.
902 | Expression evaluator failed.
903 | String is too long to fit.
904 | ** or ^ domain error.
905 | LOG(): Zero or negative used as argument.
906 | SQRT() argument cannot be negative.
912 | Operation is invalid for a General field.
914 | Code page number is invalid.
915 | Collating sequence 'value' is not found.
918 | File name is too long.
922 | Volume does not exist.
923 | Object value is not found.
924 | value is not an object.
925 | Unknown member value.
928 | Statement is only valid within a class definition.
929 | value can only be used within a method.
930 | Cannot redefine value.
931 | Statement is not in a procedure.
934 | Statement is only valid within a method.
935 | The current object does not inherit from class value.
937 | Procedure file 'value' is not found.
938 | Object is not contained in a value.
939 | WITH/ENDWITH mismatch.
940 | Expression is not valid outside of WITH/ENDWITH.
941 | Error code is invalid.
942 | Objects cannot be assigned to arrays.
943 | Member value does not evaluate to an object.
945 | The current object has been released.
947 | Expression is too complex.
951 | Cannot clear the object in use.
955 | WIN.INI/registry is corrupted.
957 | Error accessing printer spooler.
959 | Invalid coordinates.
960 | Illegal redefinition of variable value.
971 | Cannot compile until the current COMPILE command has completed.
972 | Array value is in use.
974 | Arrays cannot be assigned to array elements.
976 | Cannot resolve backlink.
988 | Currency value is out of range.
990 | Cancel.
999 | Function is not implemented.


ODBC Error Codes

The following table lists Visual FoxPro error codes mapped to ODBC Error Code SQLSTATE values. The mapped SQLSTATE values come from SQLExecDirect and SQLPrepare. No other SQLSTATE values from other ODBC API are mapped because SQLExecDirect and SQLPrepare are the only functions that access the Visual FoxPro engine.

For more information on ODBC error codes, see Appendix A: ODBC Error Codes, in the ODBC Programmer's Reference.

SQLSTATE | Visual FoxPro error code
S1001 | 149 150 182 202 308
1004 | 159
37000 | 132 200 219 221 222 227 229 230 498 499 713 901
22005 | 301 302
22012 | 307
23000 | 581 583 884 886 988
S0001 | 121 571
S0002 | 173 120 123 295 562 563 802
S0012 | 683
S0021 | 156 712
S0022 | 158 806
S1000 | 100 101 102 105 107 109 110 111 113 114 115 118 119 125 133 135 136 137 145 146 171 173 177 201 205 239 240 252 257 296 305 407 410 462 502 503 520 538 550 561 567 570 575 578 580 585 602 702 705 707 708 718 750 872 879 887 888 912 914 915 918 922 923 947 976 999


Troubleshooting

The following sections discuss how to improve performance and solve problems you might encounter while using the Visual FoxPro ODBC Driver.

Accessing Parameterized Views

You can't access parameterized views in a Visual FoxPro database using the driver. A parameterized view creates a WHERE clause in the view's SQL SELECT statement that limits the records downloaded to those records that meet the conditions of the WHERE clause built using the value supplied for the parameter. Because the driver doesn't support passing parameters to the view, attempts to access a parameterized view by using the driver will fail.

  • The parameter value can be supplied at run time or passed programmatically to the view.

Accessing Remote Views

You can't access remote views in a Visual FoxPro database using the driver. Remote views are views that access either non-FoxPro data or a combination of FoxPro and non-FoxPro data. To access remote views, use Visual FoxPro.

Deleting Records

You can mark records for deletion using the driver, but you can't permanently remove records from the database. To permanently remove records from a table, use Visual FoxPro.

Increasing Performance Using Background Fetching

You can improve performance on large fetches by using the background fetching feature of the driver. Background fetching uses a separate thread to fetch data requested from a specific data source.

You can employ background fetching for a data source in one of the following ways:

  • Check the Fetch data in background checkbox on the ODBC Visual FoxPro Setup dialog box.
  • Use the BackgroundFetch attribute keyword in your connection string.

For information on connection string attribute keywords, see Using Connection Strings.

Updating Multitiered Views

A multitiered view is a view based on one or more views rather than on a base table. When you update data in a multitiered view, the updates go down only one level, to the view on which the top-level view is based; base tables are not updated.

Using Data Definition Language (DDL) in Stored Procedures

You can't use DDL, such as CREATE TABLE or ALTER TABLE, in Visual FoxPro stored procedures.

For information on language you can use in stored procedures, see Support for Rules, Triggers, Default Values, and Stored Procedures.

Using Positioned Updates

The driver doesn't support positioned updates. Use the SQL WHERE clause to identify the rows you want to update.

Using the SET ANSI Command

If you're a Visual FoxPro developer, you should be aware that the default setting for SET ANSI is ON for the driver, in contrast to a default setting of OFF for Visual FoxPro. The default ON setting for SET ANSI allows Visual FoxPro data sources to behave consistently with other ODBC data sources that typically perform exact comparisons. You can change the default setting. For more information, see SET ANSI.


ODBC API

ODBC Drivers

ODBC API Functions

Each API function discussed in this section provides a brief summary and any Visual FoxPro–specific details. The ODBC API functions have been divided into three main categories: Core Level API functions, Level 1 API functions, and Level 2 API functions.

Note Several of the functions behave differently depending on whether the data source is defined as a connection to a directory of free tables (.dbf files) or to a Visual FoxPro database (.dbc file). Certain operations are supported only for database connections.

Core Level API Support

The ODBC Core Level API functions are listed below; all of these functions are supported by the Visual FoxPro ODBC Driver:

SQLAllocConnect | SQLExecute
SQLAllocEnv | SQLFetch
SQLAllocStmt | SQLFreeConnect
SQLBindCol | SQLFreeEnv
SQLCancel | SQLFreeStmt
SQLColAttributes | SQLGetCursorName
SQLConnect | SQLNumResultCols
SQLDescribeCol | SQLPrepare
SQLDisconnect | SQLRowCount
SQLError | SQLSetCursorName
SQLExecDirect | SQLTransact

Level 1 API Support

The ODBC Level 1 API functions are listed below; all of these functions are fully or partially supported by the Visual FoxPro ODBC Driver:

SQLBindParameter | SQLGetTypeInfo
SQLColumns | SQLParamData
SQLDriverConnect | SQLPutData
SQLGetConnectOption | SQLSetConnectOption
SQLGetData | SQLSetStmtOption
SQLGetFunctions | SQLSpecialColumns
SQLGetInfo | SQLStatistics
SQLGetStmtOption | SQLTables

Level 2 API Support

The following ODBC Level 2 API functions are fully or partially supported:

  • SQLDataSources
  • SQLDrivers
  • SQLExtendedFetch
  • SQLMoreResults
  • SQLNumParams
  • SQLParamOptions
  • SQLPrimaryKeys
  • SQLSetPos
  • SQLSetScrollOptions (partial support)

The following Level 2 API functions are not supported:

  • SQLBrowseConnect
  • SQLColumnPrivileges
  • SQLDescribeParam
  • SQLForeignKeys
  • SQLNativeSql
  • SQLProcedureColumns
  • SQLProcedures
  • SQLTablePrivileges

For more detail about any ODBC API function, refer to Chapter 21: ODBC API Reference, in the ODBC Programmer's Reference.


SQLAllocConnect

Support: Full
ODBC API Conformance: Core Level

Allocates memory for a connection handle, hdbc, within the environment identified by henv. The Driver Manager processes this call and calls the driver's SQLAllocConnect whenever SQLConnect, SQLBrowseConnect, or SQLDriverConnect is called.

For more information, see SQLAllocConnect in the ODBC Programmer's Reference.


SQLAllocEnv

Support: Full
ODBC API Conformance: Core Level

Allocates memory for an environment handle, henv, and initializes the ODBC call level interface for use by an application.

For more information, see SQLAllocEnv in the ODBC Programmer's Reference.


SQLAllocStmt

Support: Full
ODBC API Conformance: Core Level

Allocates memory for a statement handle and associates the statement handle with the connection specified by hdbc. The Driver Manager passes this call to the driver, which allocates the memory for the hstmt structure.

For more information, see SQLAllocStmt in the ODBC Programmer's Reference.


SQLBindCol

Support: Full
ODBC API Conformance: Core Level

Assigns storage space for a result column and specifies the type of the result. When SQLFetch or SQLExtendedFetch is called, the driver places the data for all bound columns in the assigned locations. See SQLGetTypeInfo for the mapping between ODBC and Visual FoxPro data types.

For more information, see SQLBindCol in the ODBC Programmer's Reference.


SQLBindParameter

Support: Full
ODBC API Conformance: Level 1

Associates a buffer with a parameter marker in an SQL statement. The Visual FoxPro ODBC Driver supports input parameters as specified by the fParamType argument.

For more information, see SQLBindParameter in the ODBC Programmer's Reference.


SQLCancel

Support: Full
ODBC API Conformance: Core Level

Cancels the processing on a statement handle, hstmt.

For more information, see SQLCancel in the ODBC Programmer's Reference.


SQLColAttributes

Support: Full
ODBC API Conformance: Core Level

Returns descriptor information for a column in a result set. Descriptor information is returned as a character string, a 32-bit descriptor-dependent value, or an integer value.

Note SQLColAttributes cannot be used to return information about the bookmark column (column 0).

The Visual FoxPro ODBC Driver supports all fDescType values. The following table includes comments on the driver's implementation of selected values.

fDescType | Comment
SQL_COLUMN_AUTO_INCREMENT | Returns FALSE: Visual FoxPro has no counter fields.
SQL_COLUMN_CASE_SENSITIVE | Always returns TRUE if the column type is Character.
SQL_COLUMN_LABEL | Returns the column name, which is also returned by SQL_COLUMN_NAME.
SQL_COLUMN_MONEY | Returns TRUE if the column type is Currency (represented by a "Y" in the Visual FoxPro language).
SQL_COLUMN_OWNER_NAME | Always returns an empty string.
SQL_COLUMN_QUALIFIER_NAME | Always returns an empty string.
SQL_COLUMN_SEARCHABLE Returns SQL_UNSEARCHABLE for columns of type General; these columns cannot be used in a WHERE clause.

Returns SQL_SEARCHABLE for columns of type Character or Memo with NOCPTRANS not set; these columns can be used in a WHERE clause with any comparison operator.

Returns SQL_ALL_EXCEPT_LIKE for all other column types; these columns can be used in a WHERE clause with all comparison operators except LIKE.
SQL_COLUMN_TABLE_NAME | Always returns an empty string.

For more information, see SQLColAttributes in the ODBC Programmer's Reference.


SQLColumns

Support: Full
ODBC API Conformance: Level 1

For a table, creates a result set that is the column list for the specified table or tables.

For more information, see SQLColumns in the ODBC Programmer's Reference.


SQLConnect

Support: Full
ODBC API Conformance: Core Level

Connects to a data source, which can be either a database or a directory of tables. The Visual FoxPro ODBC Driver ignores the szUID, cbUID, szAuthStr, and cbAuthStr arguments.

For more information, see SQLConnect in the ODBC Programmer's Reference.


SQLDataSources

Support: Full
ODBC API Conformance: Level 2

Lists data source names.

For more information, see SQLDataSources in the ODBC Programmer's Reference.


SQLDescribeCol

Support: Full
ODBC API Conformance: Core Level

Returns the name, type, precision, scale, and nullability of the given result column.

For more information, see SQLDescribeCol in the ODBC Programmer's Reference.


SQLDisconnect

Support: Full
ODBC API Conformance: Core Level

Closes a connection.

For more information, see SQLDisconnect in the ODBC Programmer's Reference.


SQLDriverConnect

Support: Full
ODBC API Conformance: Level 1

Connects to an existing data source, which can be either a database or a directory of free tables. The ODBC attribute keywords UID and PWD are ignored. The following table lists the additional supported attribute keywords.

ODBC attribute keyword | Attribute value
DSN |
UID | Ignored by the Visual FoxPro ODBC Driver but does not generate an error.
PWD | Ignored by the Visual FoxPro ODBC Driver but does not generate an error.
Driver | The name and location of the Visual FoxPro ODBC Driver; implemented by the Driver Manager.

Visual FoxPro ODBC Driver attribute keyword | Attribute value
BackgroundFetch | "Yes" or "No"
Collate | "Machine" or other collating sequence. For a list of supported collating sequences, see SET COLLATE.
Description |
Exclusive | "Yes" or "No"
SourceDB | A fully qualified path to a directory containing zero or more free tables, or the absolute path and file name for a database.
SourceType | "DBC" or "DBF"
Version |

If the data source name is not specified, the Driver Manager prompts the user for the information (depending on the setting of the fDriverCompletion argument) and then continues. If more information is required, the Visual FoxPro ODBC Driver displays the prompt dialog.

For more information, see SQLDriverConnect in the ODBC Programmer's Reference.


SQLDrivers

Support: Full
ODBC API Conformance: Level 2

Lists driver descriptions and driver attribute keywords.

For more information, see SQLDrivers in the ODBC Programmer's Reference.


SQLError

Support: Full
ODBC API Conformance: Core Level

Returns error or status information about the last error. The driver maintains a stack or list of errors that can be returned for the hstmt, hdbc, and henv arguments, depending on how the call to SQLError is made. The error queue is flushed after each statement.

The following table describes the SQLError arguments and return values used by the driver.

SQLError argument | Return value description
szSQLState | The value for the SQLSTATE represented by the error.
pfNativeError | A nonzero value indicates a Visual FoxPro ODBC Driver Native Error Message. A value of zero indicates the error has been detected by the driver and mapped to the appropriate ODBC Error Code.
szErrorMsg | The text for the native error or ODBC error.
pcbErrorMsg | The length of the message text plus the length of the identifiers.

For more information on driver error messages, see Error Messages Overview. For more information about this function, see SQLError in the ODBC Programmer's Reference.


SQLExecDirect

Support: Full
ODBC API Conformance: Core Level

Executes a new, preparable SQL statement. The Visual FoxPro ODBC Driver uses the current values of the parameter marker variables if any parameters exist in the statement.

To create a batch command to submit more than one SQL statement at a time, use a semicolon (;) to separate each SQL statement in the batch.

If your table, view, or field names contain spaces, enclose the names in back quote marks. For example, if your database contains a table named My Table and the field My Field, enclose each element of the identifier as follows:

SELECT `My Table`.`Field1`, `My Table`.`Field2` FROM `My Table`

For more information, see SQLExecDirect in the ODBC Programmer's Reference.


SQLExecute

Support: Full
ODBC API Conformance: Core Level

Executes a prepared SQL statement (a statement already prepared by SQLPrepare). The driver uses the current values of the parameter marker variables if any parameters exist in the statement.

For more information, see SQLExecute in the ODBC Programmer's Reference.


SQLExtendedFetch

Support: Full
ODBC API Conformance: Level 2

Similar to SQLFetch but returns multiple rows using an array for each column. The result set is forward-scrollable and can be made backward-scrollable if the cursor is defined to be static, not forward-only.

By default, the Visual FoxPro ODBC Driver does not return rows marked as deleted in a FoxPro table. Rows marked for deletion but not yet removed from a table are not included in the result set cursor. You can change this behavior by using the SET DELETED command.

For more information, see SQLExtendedFetch in the ODBC Programmer's Reference.


SQLFetch

Support: Full
ODBC API Conformance: Core Level

Retrieves one row from a result set into the locations specified by the previous calls to SQLBindCol. Prepares the driver for a call to SQLGetData for the unbound columns.

For more information, see SQLFetch in the ODBC Programmer's Reference.


SQLFreeConnect

Support: Full
ODBC API Conformance: Core Level

Releases a connection handle and frees all memory allocated for the handle.

For more information, see SQLFreeConnect in the ODBC Programmer's Reference.


SQLFreeEnv

Support: Full
ODBC API Conformance: Core Level

Closes the Visual FoxPro ODBC Driver and releases all memory associated with the driver.

For more information, see SQLFreeEnv in the ODBC Programmer's Reference.


SQLFreeStmt

Support: Full
ODBC API Conformance: Core Level

Stops processing associated with a specific hstmt, closes any open cursors associated with the hstmt, discards pending results, and optionally frees all resources associated with the statement handle.

For more information, see SQLFreeStmt in the ODBC Programmer's Reference.


SQLGetConnectOption

Support: Partial
ODBC API Conformance: Level 1

Returns the current setting of a connection option. This function is partially supported: The driver supports all values for the fOption argument but does not support some of vParam values for the fOption argument SQL_TXN_ISOLATION.

The following table describes only those arguments with behavior specific to the Visual FoxPro ODBC Driver implementation of SQLGetConnectOption.

fOption | Remarks
SQL_AUTOCOMMIT | If you choose SQL_AUTOCOMMIT_OFF, your application must explicitly commit or roll back transactions with SQLTransact; the Visual FoxPro ODBC Driver does not automatically commit a transactable statement upon completion. The driver does begin a transaction if the statement is transactable.
SQL_CURRENT_QUALIFIER | Can be a fully qualified database (.dbc file) name or fully qualified path to a directory containing zero or more tables (.dbf files).
SQL_LOGINTIMEOUT | Returns "Driver Not Capable" error.
SQL_CURSORS | Returns "Driver Not Capable" error.
SQL_PACKET_SIZE | Returns "Driver Not Capable" error.
SQL_TXN_ISOLATION The driver allows only SQL_TXN_READ_COMMITTED.

The following vParams are not supported:

SQL_TXN_READ_UNCOMMITTED
SQL_TXN_REAPEATABLE_READ
SQL_TXN_SERIALIZABLE

For more information, see SQLGetConnectOption in the ODBC Programmer's Reference.


SQLGetCursorName

Support: Full
ODBC API Conformance: Core Level

Returns the name of the cursor associated with the given hstmt. SQLGetCursorName is included in the Visual FoxPro ODBC Driver API because it is a part of Core Level API functionality; it cannot be used with other API functions because the driver does not support positioned updates.

For more information, see SQLGetCursorName in the ODBC Programmer's Reference.


SQLGetData

Support: Full
ODBC API Conformance: Level 1

Retrieves the value of a single field in the current record of the given result set.

For more information, see SQLGetData in the ODBC Programmer's Reference.


SQLGetFunctions

Support: Full
ODBC API Conformance: Level 1

Returns TRUE for all supported functions.

The Visual FoxPro ODBC Driver supports all ODBC API Core and Level 1 functions. The following table indicates whether the driver supports a specific Level 2 function.

Ffunction | Supported
SQL_API_SQLBROWSECONNECT | No
SQL_API_SQLCOLUMNPRIVELEGES | No
SQL_API_SQLDATASOURCES | Yes
SQL_API_SQLDESCRIBEPARAM | No
SQL_API_SQLDRIVERS | Yes
SQL_API_SQLEXTENDEDFETCH | Yes
SQL_API_SQLFOREIGNKEYS | No
SQL_API_SQLMORERESULTS | Yes
SQL_API_SQLNATIVESQL | No
SQL_API_SQLNUMPARAMS | Yes
SQL_API_SQLPARAMOPTIONS | Yes
SQL_API_SQLPRIMARYKEYS | Yes
SQL_API_SQLPROCEDURECOLUMNS | No
SQL_API_SQLPROCEDURES | No
SQL_API_SQLSETPOS | Yes
SQL_API_SQLSETSCROLLOPTIONS | Yes
SQL_API_SQLTABLEPRIVILEGES | No

For more information, see SQLGetFunctions in the ODBC Programmer's Reference.


SQLGetInfo

Support: Full
ODBC API Conformance: Level 1

Returns general information about the Visual FoxPro ODBC Driver and data source associated with a connection handle, hdbc. The following list shows the value returned by the Visual FoxPro ODBC Driver for each fInfoType argument and comments regarding the returned values.

For more information, see SQLGetInfo in the ODBC Programmer's Reference.

A

SQL_ACCESSIBLE_PROCEDURES returns 'N'.
SQL_ACCESSIBLE_TABLES returns 'Y'.
SQL_ACTIVE_CONNECTIONS returns 0.
SQL_ACTIVE_STATEMENTS returns 0.
SQL_ALTER_TABLE returns either SQL_AT_ADD_COLUMN or SQL_AT_DROP_COLUMN.

B

SQL_BOOKMARK_PERSISTENCE returns SQL_BP_SCROLL.

C

SQL_COLUMN_ALIAS returns 'Y'.
SQL_CONCAT_NULL_BEHAVIOR returns SQL_CB_NULL.
SQL_CONVERT_BIGINT returns 0. The Visual FoxPro ODBC Driver does not support BigInt.
SQL_CONVERT_BINARY returns 0.
SQL_CONVERT_BIT returns 0.
SQL_CONVERT_CHAR returns 0.
SQL_CONVERT_DATE returns 0.
SQL_CONVERT_DECIMAL returns 0.
SQL_CONVERT_DOUBLE returns 0.
SQL_CONVERT_FLOAT returns 0.
SQL_CONVERT_INTEGER returns 0.
SQL_CONVERT_LONGVARBINARY returns 0.
SQL_CONVERT_LONGVARCHAR returns 0.
SQL_CONVERT_NUMERIC returns 0.
SQL_CONVERT_REAL returns 0.
SQL_CONVERT_SMALLINT returns 0.
SQL_CONVERT_TIME returns 0.
SQL_CONVERT_TIMESTAMP returns 0.
SQL_CONVERT_TINYINT returns 0.
SQL_CONVERT_VARBINARY returns 0.
SQL_CONVERT_VARCHAR returns 0.
SQL_CONVERT_FUNCTIONS returns 0.
SQL_CORRELATION_NAME returns SQL_CN_ANY.
SQL_CURSOR_COMMIT_BEHAVIOR returns SQL_CB_PRESERVE.
SQL_CURSOR_ROLLBACK_BEHAVIOR returns SQL_CB_PRESERVE.

D

SQL_DATA_SOURCE_NAME returns the value passed as DSN to SQLConnect, or SQLDriverConnect; returns an empty string if no DSN is specified.
SQL_DATA_SOURCE_READ_ONLY returns 'N'.
SQL_DATABASE_NAME returns a full UNC path to the current database if the data source is a database. If the data source connects to a directory of tables, the function returns the path to the directory.
SQL_DBMS_NAME returns "Visual FoxPro".
SQL_DBMS_VER returns "03.00.0000".
SQL_DEFAULT_TXN_ISOLATION returns SQL_TXN_READ_COMMITTED. Dirty reads are not possible, but nonrepeatable reads and phantoms are possible.
SQL_DRIVER_HDBC is implemented by the Driver Manager.
SQL_DRIVER_HENV is implemented by the Driver Manager.
SQL_DRIVER_HLIB is implemented by the Driver Manager.
SQL_DRIVER_HSTMT is implemented by the Driver Manager.
SQL_DRIVER_NAME returns "vfpodbc.dll".
SQL_DRIVER_ODBC_VER returns "02.50" (SQL_SPEC_MAJOR, SQL_SPEC_MINOR).
SQL_DRIVER_VER returns "01.00.0000".

E

SQL_EXPRESSIONS_IN_ORDERBY returns 'N'.

F

SQL_FETCH_DIRECTION returns:
SQL_FD_FETCH_NEXT
SQL_FD_FETCH_FIRST
SQL_FD_FETCH_LAST
SQL_FD_FETCH_PRIOR
SQL_FD_FETCH_ABSOLUTE
SQL_FD_FETCH_RELATIVE
SQL_FD_FETCH_BOOKMARK.
SQL_FILE_USAGE returns SQL_FILE_QUALIFIER both for database (.dbc file) and for free table (.dbf file) data sources.

G-H

SQL_GETDATA_EXENSIONS returns:
SQL_GD_ANY_COLUMN
SQL_GD_ANY_BLOCK
SQL_GD_ANY_BOUND
SQL_GD_ANY_ORDER
SQL_GROUP_BY returns SQL_GB_NO_RELATION.

I-J

SQL_IDENTIFIER_CASE returns SQL_IC_MIXED.
SQL_IDENTIFIER_QUOTE_CHAR returns `.

K

SQL_KEYWORDS returns "".

L

SQL_LIKE_ESCAPE_CLAUSE returns 'N'.
SQL_LOCK_TYPES returns SQL_LCK_NO_CHANGE.

M

SQL_MAX_BINARY_LITERAL_LEN returns 0.
SQL_MAX_CHAR_LITERAL_LEN returns 254.
SQL_MAX_COLUMN_NAME_LEN returns 128.
SQL_MAX_COLUMNS_IN_GROUP_BY returns 16.
SQL_MAX_COLUMNS_IN_ORDER_BY returns 16.
SQL_MAX_COLUMNS_IN_INDEX returns 0.
SQL_MAX_COLUMNS_IN_SELECT returns 254.
SQL_MAX_COLUMNS_IN_TABLE returns 254.
SQL_MAX_CURSOR_NAME_LEN returns 254.
SQL_MAX_INDEX_SIZE returns 0.
SQL_MAX_OWNER_NAME_LEN returns 0.
SQL_MAX_PROCEDURE_NAME_LEN returns 0. The Visual FoxPro ODBC Driver does not allow direct access to Visual FoxPro stored procedures.
SQL_MAX_QUALIFIER_NAME_LEN returns the maximum operating system path length.
SQL_MAX_ROW_SIZE returns 254^2.
SQL_MAX_ROW_SIZE_INCLUDES_LONG returns 'N'.
SQL_MAX_STATEMENT_LEN returns 8192.
SQL_MAX_TABLE_NAME_LEN returns 128.
SQL_MAX_TABLES_IN_SELECT returns 16.
SQL_MAX_USER_NAME_LEN returns 0.
SQL_MULT_RESULT_SETS returns 'Y'.
SQL_MULTIPLE_ACTIVE_TXN returns 'Y'. Multiple connections can have several transactions open at once.

N

SQL_NEED_LONG_DATA_LEN returns 'N'.
SQL_NON_NULLABLE_COLUMNS returns SQL_NNC_NON_NULL.
SQL_NULL_COLLATION returns SQL_NC_LOW.
SQL_NUMERIC_FUNCTIONS returns all functions except SQL_FN_NUM_POWER, which is not supported by the Visual FoxPro ODBC Driver. The following functions are supported:
SQL_FN_NUM_ABS
SQL_FN_NUM_ACOS
SQL_FN_NUM_ASIN
SQL_FN_NUM_ATAN
SQL_FN_NUM_ATAN2
SQL_FN_NUM_CELING
SQL_FN_NUM_COS
SQL_FN_NUM_COT
SQL_FN_NUM_DEGREES
SQL_FN_NUM_EXP
SQL_FN_NUM_FLOOR
SQL_FN_NUM_LOG
SQL_FN_NUM_LOG10
SQL_FN_NUM_MOD
SQL_FN_NUM_PI
SQL_FN_NUM_RADIANS
SQL_FN_NUM_RAND
SQL_FN_NUM_ROUND
SQL_FN_NUM_SIGN
SQL_FN_NUM_SIN
SQL_FN_NUM_SQRT
SQL_FN_NUM_TAN

O

SQL_ODBC_API_CONFORMANCE returns SQL_OAC_LEVEL1.
SQL_ODBC_SAG_CLI_CONFORMANCE returns SQL_OSCC_COMPLIANT.
SQL_ODBC_SQL_CONFORMANCE returns SQL_OSC_MINIMUM. Minimum SQL syntax is supported.
SQL_ODBC_SQL_OPT_IEF returns "N".
SQL_ODBC_VER is implemented by the Driver Manager.
SQL_ORDER_BY_COLUMNS_IN_SELECT returns "N".
SQL_OUTER_JOINS returns "N".
SQL_OWNER_TERM returns "". The Visual FoxPro ODBC Driver does not support owners for its objects.
SQL_OWNER_USAGE returns 0. The Visual FoxPro ODBC Driver does not support owners for its objects.

P

SQL_POS_OPERATIONS returns SQL_POS_POSITION.
SQL_POSITIONED_STATEMENTS returns 0.
SQL_PROCEDURE_TERM returns "".
SQL_PROCEDURES returns 'N'.

Q

SQL_QUALIFIER_LOCATION returns SQL_QL_START.
SQL_QUALIFIER_NAME_SEPARATOR returns '!' or '\'. The separator between database and table is '!' for data sources connected to databases, and '\' for data sources that are directories of free tables.
SQL_QUALIFIER_TERM returns "database" or "directory". The qualifier is "database" for data sources connected to databases, and "directory" for data sources that are directories of free tables.
SQL_QUALIFIER_USAGE does not support SQL_QU_PRIVILEGE_DEFINITION; it returns either SQL_QU_DML_STATEMENT or SQL_QU_TABLE_DEFINITION.
SQL_QUOTED_IDENTIFIER_CASE returns SQL_IC_MIXED.

R

SQL_ROW_UPDATES returns "N". The Visual FoxPro ODBC Driver supports only static and forward cursors.

S

SQL_SCROLL_CONCURRENCY returns SQL_SCCO_READ_ONLY.
SQL_SCROLL_OPTIONS returns either SQL_SO_STATIC or SQL_SO_READONLY.
SQL_SEARCH_PATTERN_ESCAPE returns "\".
SQL_SERVER_NAME returns "".
SQL_SPECIAL_CHARACTERS returns "~@#$%^".
SQL_STATIC_SENSITIVITY returns 0. The Visual FoxPro ODBC Driver does not support positional updates.
SQL_STRING_FUNCTIONS does not support SQL_FN_STR_INSERT, SQL_FN_STR_LOCATE, SQL_FN_STR_LOCATE_2, or SQL_FN_STR_SOUNDEX.
It returns:
SQL_FN_STR_ASCII
SQL_FN_STR_CHAR
SQL_FN_STR_CONCAT
SQL_FN_STR_DIFFERENCE
SQL_FN_STR_LCASE
SQL_FN_STR_LEFT
SQL_FN_STR_LENGTH
SQL_FN_STR_LTRIM
SQL_FN_STR_REPEAT
SQL_FN_STR_REPLACE
SQL_FN_STR_RIGHT
SQL_FN_STR_RTRIM
SQL_FN_STR_SUBSTRING
SQL_FN_STR_UCASE
SQL_FN_STR_SPACE.
SQL_SUBQUERIES returns:
SQL_SQ_CORRELATED_SUBQUERIES
SQL_SQ_COMPARISON
SQL_SQ_EXISTS
SQL_SQ_IN
SQL_SQ_QUANTIFIED.
SQL_SYSTEM_FUNCTIONS returns:
SQL_FN_SYS_DBNAME
SQL_FN_SYS_IFNULL

but not:
SQL_FN_SYS_USERNAME

T

SQL_TABLE_TERM returns "table".
SQL_TIMEDATE_ADD_INTERVALS returns:
SQL_FN_TSI_ SECOND
SQL_FN_TSI_MINUTE
SQL_FN_TSI_HOUR
SQL_FN_TSI_DAY
SQL_FN_TSI_MONTH
SQL_FN_TSI_YEAR

but not:
SQL_FN_TSI_FRAC_SECOND
SQL_FN_TSI_WEEK
SQL_FN_TSI_QUARTER
SQL_TIMEDATE_DIFF_INTERVALS returns:
SQL_FN_TSI_ SECOND
SQL_FN_TSI_MINUTE
SQL_FN_TSI_HOUR
SQL_FN_TSI_DAY
SQL_FN_TSI_MONTH
SQL_FN_TSI_YEAR
SQL_TIMEDATE_FUNCTIONS does not support SQL_FN_TD_QUARTER, SQL_FN_TD_TIMESTAMPADD, SQL_FN_TD_DAYOFYEAR, or SQL_FN_TD_WEEK.
It returns:
SQL_FN_TD_CURDATE
SQL_FN_TD_CURTIME
SQL_FN_TD_DAYNAME
SQL_FN_TD_DAYOFMONTH
SQL_FN_TD_DAYOFWEEK
SQL_FN_TD_HOUR
SQL_FN_TD_MINUTE
SQL_FN_TD_MONTH
SQL_FN_TD_MONTHNAME
SQL_FN_TD_NOW
SQL_FN_TD_SECOND
SQL_FN_TD_TIMESTAMPDIFF
SQL_FN_TD_YEAR .
SQL_TXN_CAPABLE returns SQL_TC_DML.
SQL_TXN_ISOLATION_OPTION returns SQL_TXN_READ_COMMITTED.

U-Z

SQL_UNION returns either SQL_U_UNION or SQL_U_UNION_ALL.
SQL_USER_NAME returns <blank>.


SQLGetStmtOption

Support: Full
ODBC API Conformance: Level One

Returns the current setting of a statement option.

FOption | Returns
SQL_GET_BOOKMARK | 32-bit integer value that is the bookmark for the current record number
SQL_ROW_NUMBER | 32-bit integer specifying the position of the current row within the result set
SQL_TRANSLATE_DLL | Error: "Driver not capable"

The Visual FoxPro ODBC Driver has no translation DLLs.

For more information, see SQLGetStmtOption in the ODBC Programmer's Reference.


SQLGetTypeInfo

Support: Full
ODBC API Conformance: Level 1

Returns information about the data types supported by a data source. The driver returns the information in an SQL result set. The following table lists ODBC data types and the corresponding Visual FoxPro data type.

ODBC type | Visual FoxPro type
SQL_BIGINT | Not supported. There is no 64-bit Visual FoxPro type.
SQL_BIT | Logical
SQL_CHAR | Character
SQL_DATE | Date
SQL_DECIMAL | Numeric
SQL_DOUBLE | Double
SQL_FLOAT | Double
SQL_INTEGER | Integer
SQL_LONGVARBINARY | Memo (Binary)
SQL_LONGVARCHAR | Memo
SQL_NUMERIC | Numeric*, Currency, Float
SQL_REAL | Double
SQL_SMALLINT | Integer
SQL_TIME | Not supported. There is no Visual FoxPro time type.
SQL_TIMESTAMP | DateTime
SQL_TINYINT | Integer
SQL_VARBINARY | Memo (Binary)*, General
SQL_VARCHAR | Character

*Default type

For more information on Visual FoxPro data types, see CREATE TABLE. For more information about this function, see SQLGetTypeInfo in the ODBC Programmer's Reference.


SQLMoreResults

Support: Full
ODBC API Conformance: Level 2

Determines whether more results are pending on a statement handle, hstmt, containing SELECT, UPDATE, INSERT, or DELETE statements and if so, initializes processing for those results.

For more information, see SQLMoreResults in the ODBC Programmer's Reference.


SQLNumParams

Support: Full
ODBC API Conformance: Level 2

Returns the number of parameters in an SQL statement. The number of parameters should equal the number of question marks in the SQL statement passed to SQLPrepare.

For more information on SQL grammar, see Supported ODBC SQL Grammar. For more information about this function, see SQLNumParams in the ODBC Programmer's Reference.


SQLNumResultCols

Support: Full
ODBC API Conformance: Core Level

Returns the number of columns in a result set cursor.

For more information, see SQLNumResultCols in the ODBC Programmer's Reference.


SQLParamData

Support: Full
ODBC API Conformance: Level 1

Used in conjunction with SQLPutData to specify parameter data at statement execution time.

For more information, see SQLParamData in the ODBC Programmer's Reference.


SQLParamOptions

Support: Full
ODBC API Conformance: Level 1

Allows an application to specify multiple values for the set of parameters assigned by SQLBindParameter. The ability to specify multiple values for a set of parameters is useful for bulk inserts and other work that requires the data source to process the same SQL statement multiple times with various parameter values. For example, an application can specify three sets of values for the set of parameters associated with an INSERT statement and then execute the INSERT statement once to perform the three insert operations.

For more information, see SQLParamOptions in the ODBC Programmer's Reference.


SQLPrepare

Support: Full
ODBC API Conformance: Core Level

Prepares an SQL statement by planning how to optimize and execute the statement. The SQL statement is compiled for execution by SQLExecDirect.

If your table, view, or field names contain spaces, enclose the names in back quote (`) marks. For example, if your database contains a table named My Table and the field My Field, enclose each element of the identifier as follows:

SELECT * FROM `My Table`.`My Field`

For more information, see SQLPrepare in the ODBC Programmer's Reference.


SQLPrimaryKeys

Support: Full
ODBC API Conformance: Level 2

Returns the column names that comprise the primary key for a table. The Visual FoxPro ODBC Driver implementation of SQLPrimaryKeys behaves as follows:

  • Ignores the szTableOwner and cbTableOwner arguments.
  • Works only for data sources that are databases. The driver returns the error "Driver does not support this function" if the data source is a directory of free tables.

For more information, see SQLPrimaryKeys in the ODBC Programmer's Reference.


SQLPutData

Support: Full
ODBC API Conformance: Level 1

Allows an application to send data for a parameter or column to the driver at statement execution time.

For more information, see SQLPutData in the ODBC Programmer's Reference.


SQL Row Count

ODBC Drivers

SQLRowCount

Support: Full
ODBC API Conformance: Core Level

Returns the number of rows affected by the last UPDATE, INSERT, or DELETE statement.

For more information, see SQLRowCount in the ODBC Programmer's Reference.


SQLSetConnectOption

Support: Partial
ODBC API Conformance: Level 1

Sets options that govern aspects of connections. This function is partially supported: The driver supports all values for the fOption argument but does not support some of vParam values for the fOption argument SQL_TXN_ISOLATION.

The following table describes only those arguments with behavior specific to the Visual FoxPro ODBC Driver implementation of SQLSetConnectOption.

fOption | Remarks
SQL_AUTOCOMMIT | If you choose SQL_AUTOCOMMIT_OFF, your application must explicitly commit or roll back transactions with SQLTransact; the Visual FoxPro ODBC Driver does not automatically commit a transactable statement upon completion. The driver does begin a transaction if the statement is transactable.
SQL_CURRENT_QUALIFIER | Can be a fully qualified database name or fully qualified path to a directory containing zero or more free tables.
SQL_LOGINTIMEOUT | Returns "Driver not capable" error.
SQL_CURSORS | Returns "Driver not capable" error.
SQL_PACKET_SIZE | Returns "Driver not capable" error.
SQL_TXN_ISOLATION The driver allows only SQL_TXN_READ_COMMITTED.

The following vParams are not supported:

SQL_TXN_READ_UNCOMMITTED SQL_TXN_REAPEATABLE_READ SQL_TXN_SERIALIZABLE

For more information, see SQLSetConnectOption in the ODBC Programmer's Reference.


SQLSetCursorName

Support: Full
ODBC API Conformance: Core Level

Associates a cursor name with an active statement handle, hstmt. SQLSetCursorName is included in the Visual FoxPro ODBC Driver API because it is a part of Core Level ODBC API functionality; it cannot be used with other API functions because the driver does not support positioned updates.

For more information, see SQLSetCursorName in the ODBC Programmer's Reference.


SQLSetPos

Support: Full
ODBC API Conformance: Level 2

Sets the cursor position in a rowset. You can use SQLSetPos with SQLGetData to retrieve rows from unbound columns after positioning the cursor to a specific row in the rowset.

For more information, see SQLSetPos in the ODBC Programmer's Reference.


SQLSetScrollOptions

Support: Partial
ODBC API Conformance: Level 2

Sets options that control the behavior of cursors associated with a statement handle, hstmt.

The Visual FoxPro ODBC Driver supports only SQL_CONCUR_READ_ONLY; it does not support the fConcurrency value SQL_CONCUR_ROWVER. The driver converts SQL_KEYSET_SIZE, SQL_CURSOR_DYNAMIC, and SQL_CURSOR_KEYSET_DRIVEN to SQL_SCROLL_STATIC with warning ODBC_01S02.

For more information, see SQLSetScrollOptions in the ODBC Programmer's Reference.


SQLSetStmtOption

Support: Full
ODBC API Conformance: Level 1

Sets options related to a statement handle, hstmt.

fOption | Allowed values | Comments
SQL_ASYNC_ENABLE | SQL_ASYNC_ENABLE_OFF | If you attempt to set this fOption, the driver returns the error: "Driver not capable". Visual FoxPro does not support asynchronous execution.
SQL_BIND_TYPE | SQL_BIND_BY_COLUMN or a 32-bit value denoting the length of the structure or an instance of a buffer into which result columns will be bound. |
SQL_CONCURRENCY | SQL_CONCUR_READ_ONLY SQL_CONCUR_LOCK SQL_CONCUR_VALUES | The driver doesn't allow SQL_CONCUR_ROWVER, because Visual FoxPro does not have row versioning based on timestamps.
SQL_CURSOR_TYPE | SQL_CURSOR_FORWARD_ONLY SQL_CURSOR_STATIC | The driver does not allow SQL_CURSOR_KEYSET_DRIVEN or SQL_CURSOR_DYNAMIC; see SQLSetScrollOptions for more information.
SQL_KEYSET_SIZE | Error: "Driver not capable" | Visual FoxPro does not support the keyset cursor model.
SQL_MAX_LENGTH | 0 | If you attempt to set this fOption value, the driver returns the error "Driver not capable".
SQL_MAX_ROWS | 0 | If you attempt to set this fOption value, the driver returns the error "Driver not capable".
SQL_NOSCAN | SQL_NOSCAN_OFF |
SQL_QUERY_TIMEOUT | 0 | If you attempt to set this fOption value, the driver returns the error "Driver not capable".
SQL_RETRIEVE_DATA | SQL_RD_ON, SQL_RD_OFF |
SQL_ROWSET_SIZE | 1 to 4,294,967,296 |
SQL_SIMULATE_CURSOR | Error: "Driver not capable" |
SQL_USE_BOOKMARKS | SQL_UB_OFF SQL_UB_ON |

For more information, see SQLSetStmtOption in the ODBC Programmer's Reference.


SQLSpecialColumns

Support: Full
ODBC API Conformance: Level 1

Retrieves the optimal set of columns that uniquely identifies a row in the table.

The Visual FoxPro ODBC Driver returns the columns that make up the primary key on the FoxPro table. (See SQLPrimaryKeys.) If called with fColType set to SQL_ROWVER, no columns are returned. SQLSpecialColumns works only for data sources that are databases.

For more information, see SQLSpecialColumns in the ODBC Programmer's Reference.


SQLStatistics

Support: Full
ODBC API Conformance: Level 1

Retrieves a list of statistics about a single table and the indexes, or tag names, associated with the table. The driver returns the information as a result set.

For more information, see SQLStatistics in the ODBC Programmer's Reference.


SQLTables

Support: Full
ODBC API Conformance: Level 1

Returns the list of table names specified by the parameter in the SQLTables statement. If no parameter is specified, returns the table names stored in the current data source. The driver returns the information as a result set.

Enumeration type calls will not receive a result set entry for remote views or local parameterized views. However, a call to SQLTables with a unique table name specifier will find a match for such a view if present with that name; this allows the API to be used to check for name conflicts prior to creation of a new table.

Note The Visual FoxPro ODBC driver differentiates between database tables and free tables, even when both types of tables are stored in the same directory on your system. If your data source is a directory of free tables, the Visual FoxPro ODBC Driver does not catalog or return the names of any tables that are associated with a database.

For more information, see SQLTables in the ODBC Programmer's Reference.


SQLTransact

Support: Full
ODBC API Conformance: Core Level

Requests a commit or rollback operation for all active operations on all statement handles (hstmts) associated with a connection or for all connections associated with the environment handle, henv. SQLTransact works only for data sources that are databases.

If a commit fails when in manual mode, the transaction remains active; you can choose to roll back the transaction or retry the commit operation. If a commit operation fails when in automatic transaction mode, the transaction is rolled back automatically; the transaction cannot be inactive.

For more information, see SQLTransact in the ODBC Programmer's Reference.


Visual FoxPro Language Reference

The Visual FoxPro Language Reference contains detailed information about the following commands:

ALTER TABLE - SQL Command

CREATE TABLE - SQL Command

DELETE - SQL Command

DELETE TAG Command

DROP TABLE Command

INDEX Command

INSERT - SQL Command

SELECT - SQL Command

SET ANSI Command

SET BLOCKSIZE Command

SET COLLATE Command

SET DELETED Command

SET EXACT Command

SET EXCLUSIVE Command

SET NULL Command

SET PATH Command

SET REPROCESS Command

SET UNIQUE Command

UPDATE - SQL Command


ALTER TABLE - SQL Command

Programmatically modifies the structure of a table.

Syntax

ALTER TABLE TableName1
   ADD | ALTER [COLUMN] FieldName1
      FieldType [(nFieldWidth [, nPrecision])]
      [NULL | NOT NULL]
      [CHECK lExpression1 [ERROR cMessageText1]]
      [DEFAULT eExpression1]
      [PRIMARY KEY | UNIQUE]
      [REFERENCES TableName2 [TAG TagName1]]
      [NOCPTRANS]
 – Or –
ALTER TABLE TableName1
   ALTER [COLUMN] FieldName2
      [NULL | NOT NULL]
      [SET DEFAULT eExpression2]
      [SET CHECK lExpression2 [ERROR cMessageText2]]
      [DROP DEFAULT]
      [DROP CHECK]
 – Or –
ALTER TABLE TableName1
   [DROP [COLUMN] FieldName3]
   [SET CHECK lExpression3 [ERROR cMessageText3]]
   [DROP CHECK]
   [ADD PRIMARY KEY eExpression3 TAG TagName2]
   [DROP PRIMARY KEY]
   [ADD UNIQUE eExpression4 [TAG TagName3]]
   [DROP UNIQUE TAG TagName4]
   [ADD FOREIGN KEY [eExpression5] TAG TagName4
      REFERENCES TableName2 [TAG TagName5]]
   [DROP FOREIGN KEY TAG TagName6 [SAVE]]
   [RENAME COLUMN FieldName4 TO FieldName5]
   [NOVALIDATE]

Arguments

TableName1
Specifies the name of the table whose structure is modified.
ADD [COLUMN] FieldName1
Specifies the name of the field to add.
ALTER [COLUMN] FieldName1
Specifies the name of an existing field to modify.
FieldType [(nFieldWidth [, nPrecision]])
Specifies the field type, field width, and field precision (number of decimal places) for a new or modified field.

FieldType is a single letter indicating the field's data type. Some field data types require that you specify nFieldWidth or nPrecision or both.

nFieldWidth and nPrecision are ignored for D, G, I, L, M, P, T, and Y types. nPrecision defaults to zero (no decimal places) if nPrecision isn't included for the B, F, or N types.

NULL | NOT NULL
Allows or prevents null values in the field.

If you omit NULL and NOT NULL, the current setting of SET NULL determines whether null values are allowed in the field. However, if you omit NULL and NOT NULL and include the PRIMARY KEY or UNIQUE clause, the current setting of SET NULL is ignored and the field defaults to NOT NULL.

CHECK lExpression1
Specifies a validation rule for the field. lExpression1 must evaluate to a logical expression and can be a user-defined function or a stored procedure. Whenever a blank record is appended, the validation rule is checked. An error is generated if the validation rule doesn't allow for a blank field value in an appended record.
ERROR cMessageText1
Specifies the error message displayed when the field validation rule generates an error.
DEFAULT eExpression1
Specifies a default value for the field. The data type of eExpression1 must be the same as the data type for the field.
PRIMARY KEY
Creates a primary index tag. The index tag has the same name as the field.
UNIQUE
Creates a candidate index tag with the same name as the field.

Note Candidate indexes (created by including the UNIQUE option, provided for ANSI compatibility in ALTER TABLE or CREATE TABLE) are not the same as indexes created with the UNIQUE option in the INDEX command. An index created with UNIQUE in the INDEX command allows duplicate index keys; candidate indexes do not allow duplicate index keys.

Null values and duplicate records are not permitted in a field used for a primary or candidate index.

If you are creating a new field with ADD COLUMN, Visual FoxPro will not generate an error if you create a primary or candidate index for a field that supports null values. However, Visual FoxPro will generate an error if you attempt to enter a null or duplicate value into a field used for a primary or candidate index.

If you are modifying an existing field and the primary or candidate index expression consists of fields in the table, Visual FoxPro checks the fields to see whether they contain null values or duplicate records. If they do, Visual FoxPro generates an error and the table is not altered.

REFERENCES TableName2 TAG TagName1
Specifies the parent table to which a persistent relationship is established. TAG TagName1 specifies the parent table's index tag on which the relationship is based. Index tag names can contain up to 10 characters.
NOCPTRANS
Prevents translation to a different code page for character and memo fields. If the table is converted to another code page, the fields for which NOCPTRANS has been specified are not translated. NOCPTRANS can be specified only for character and memo fields.

The following example creates a table named mytable containing two character fields and two memo fields. The second character field, char2, and the second memo field, memo2, include NOCPTRANS to prevent translation.

CREATE TABLE mytable (char1 C(10), char2 C(10) NOCPTRANS,;
   memo1 M, memo2 M NOCPTRANS)
ALTER [COLUMN] FieldName2
Specifies the name of an existing field to modify.
SET DEFAULT eExpression2
Specifies a new default value for an existing field. The data type of eExpression2 must be the same as the data type for the field.
SET CHECK lExpression2
Specifies a new validation rule for an existing field. lExpression2 must evaluate to a logical expression and may be a user-defined function or a stored procedure.
ERROR cMessageText2
Specifies the error message displayed when the field validation rule generates an error. The message is displayed only when data is changed within a Browse or Edit window.
DROP DEFAULT
Removes the default value for an existing field.
DROP CHECK
Removes the validation rule for an existing field.
DROP [COLUMN] FieldName3
Specifies a field to remove from the table. Removing a field from the table also removes the field's default value setting and field validation rule.

If index key or trigger expressions reference the field, the expressions become invalid when the field is removed. In this case, an error isn't generated when the field is removed but the invalid index key or trigger expressions will generate errors at run time.

SET CHECK lExpression3
Specifies the table validation rule. lExpression3 must evaluate to a logical expression and may be a user-defined function or a stored procedure.
ERROR cMessageText3
Specifies the error message displayed when the table validation rule generates an error. The message is displayed only when data is changed within a Browse or Edit window.
DROP CHECK
Removes the table's validation rule.
ADD PRIMARY KEY eExpression3 TAG TagName2
Adds a primary index to the table. eExpression3 specifies the primary index key expression, and TagName2 specifies the name of the primary index tag. Index tag names can contain up to 10 characters. If TAG TagName2 is omitted and eExpression3 is a single field, the primary index tag has the same name as the field specified in eExpression3.
DROP PRIMARY KEY
Removes the primary index and its index tag. Because a table can have only one primary key, it isn't necessary to specify the name of the primary key. Removing the primary index also deletes any persistent relations based on the primary key.
ADD UNIQUE eExpression4 [TAG TagName3]
Adds a candidate index to the table. eExpression4 specifies the candidate index key expression, and TagName3 specifies the name of the candidate index tag. Index tag names can contain up to 10 characters. If you omit TAG TagName3 and if eExpression4 is a single field, the candidate index tag has the same name as the field specified in eExpression4.
DROP UNIQUE TAG TagName4
Removes the candidate index and its index tag. Because a table can have multiple candidate keys, you must specify the name of the candidate index tag.
ADD FOREIGN KEY [eExpression5] TAG TagName4
Adds a foreign (nonprimary) index to the table. eExpression5 specifies the foreign index key expression, and TagName4 specifies the name of the foreign index tag. Index tag names can contain up to 10 characters.
REFERENCES TableName2 [TAG TagName5]
Specifies the parent table to which a persistent relationship is established. Include TAG TagName5 to establish a relation based on an existing index tag for the parent table. Index tag names can contain up to 10 characters. If you omit TAG TagName5, the relationship is established using the parent table's primary index tag.
DROP FOREIGN KEY TAG TagName6 [SAVE]
Deletes a foreign key whose index tag is TagName6. If you omit SAVE, the index tag is deleted from the structural index. Include SAVE to prevent deletion of the index tag from the structural index.
RENAME COLUMN FieldName4 TO FieldName5
Allows you to change the name of a field in the table. FieldName4 specifies the name of the field that is renamed. FieldName5 specifies the new name of the field.

Caution Exercise care when renaming table fields—index expressions, field and table validation rules, commands, functions, and so on may reference the original field names.

NOVALIDATE
Specifies that Visual FoxPro allows changes to be made to the structure of the table; these changes might violate the integrity of the data in the table. By default, Visual FoxPro prevents ALTER TABLE from making changes that violate the integrity of the data in the table. Include NOVALIDATE to override this default behavior.

Remarks

ALTER TABLE can be used to modify the structure of a table that has not been added to a database. However, Visual FoxPro generates an error if you include the DEFAULT, FOREIGN KEY, PRIMARY KEY, REFERENCES, or SET clauses when modifying a free table.

ALTER TABLE may rebuild the table by creating a new table header and appending records to the table header. For example, changing a field's type or width might cause the table to be rebuilt.

After a table is rebuilt, field validation rules are executed for any fields whose type or width is changed. If you change the type or width of any field in the table, the table rule is executed.

If you modify field or table validation rules for a table that has records, Visual FoxPro tests the new field or table validation rules against the existing data and issues a warning on the first occurrence of a field or table validation rule or of a trigger violation.

If the table you modify is in a database, ALTER TABLE - SQL requires exclusive use of the database. To open a database for exclusive use, include EXCLUSIVE in OPEN DATABASE.

See Also

CREATE TABLE - SQL

INDEX


CREATE TABLE - SQL Command

Creates a table having the specified fields.

The Visual FoxPro ODBC Driver supports the native Visual FoxPro language syntax for this command. For driver-specific information, see Driver Remarks.

Syntax

CREATE TABLE | DBF TableName1 [NAME LongTableName] [FREE]
   (FieldName1 FieldType [(nFieldWidth [, nPrecision])]
      [NULL | NOT NULL]
      [CHECK lExpression1 [ERROR cMessageText1]]
      [DEFAULT eExpression1]
      [PRIMARY KEY | UNIQUE]
      [REFERENCES TableName2 [TAG TagName1]]
      [NOCPTRANS]
   [, FieldName2 ...]
      [, PRIMARY KEY eExpression2 TAG TagName2
      |, UNIQUE eExpression3 TAG TagName3]
      [, FOREIGN KEY eExpression4 TAG TagName4 [NODUP]
            REFERENCES TableName3 [TAG TagName5]]
      [, CHECK lExpression2 [ERROR cMessageText2]])
| FROM ARRAY ArrayName

Arguments

CREATE TABLE | DBF TableName1
Specifies the name of the table to create. The TABLE and DBF options are identical.
NAME LongTableName
Specifies a long name for the table. A long table name can be specified only when a database is open, because long table names are stored in databases.

Long names can contain up to 128 characters and can be used in place of short file names in the database.

FREE
Specifies that the table will not be added to an open database. FREE isn't required if a database isn't open.
(FieldName1 FieldType [(nFieldWidth [, nPrecision])]
Specifies the field name, field type, field width, and field precision (number of decimal places), respectively.

FieldType is a single letter indicating the field's data type. Some field data types require that you specify nFieldWidth or nPrecision or both.

nFieldWidth and nPrecision are ignored for D, G, I, L, M, P, T, and Y types. nPrecision defaults to zero (no decimal places) if nPrecision isn't included for the B, F, or N types.

NULL
Allows null values in the field.
NOT NULL
Prevents null values in the field.

If you omit NULL and NOT NULL, the current setting of SET NULL determines whether null values are allowed in the field. However, if you omit NULL and NOT NULL and include the PRIMARY KEY or UNIQUE clause, the current setting of SET NULL is ignored and the field defaults to NOT NULL.

CHECK lExpression1
Specifies a validation rule for the field. lExpression1 can be a user-defined function. Whenever a blank record is appended, the validation rule is checked. An error is generated if the validation rule doesn't allow for a blank field value in an appended record.
ERROR cMessageText1
Specifies the error message Visual FoxPro displays when the field rule generates an error. The message is displayed only when data is changed within a Browse window or an Edit window.
DEFAULT eExpression1
Specifies a default value for the field. The data type of eExpression1 must be the same as the field's data type.
PRIMARY KEY
Creates a primary index for the field. The primary index tag has the same name as the field.
UNIQUE
Creates a candidate index for the field. The candidate index tag has the same name as the field.

Note Candidate indexes (created by including the UNIQUE option in CREATE TABLE or ALTER TABLE - SQL) are not the same as indexes created with the UNIQUE option in the INDEX command. An index created with the UNIQUE option in the INDEX command allows duplicate index keys; candidate indexes do not allow duplicate index keys. See INDEX for additional information on its UNIQUE option.

Null values and duplicate records are not permitted in a field used for a primary or candidate index. However, Visual FoxPro will not generate an error if you create a primary or candidate index for a field that supports null values. Visual FoxPro will generate an error if you attempt to enter a null or duplicate value into a field used for a primary or candidate index.

REFERENCES TableName2 [TAG TagName1]
Specifies the parent table to which a persistent relationship is established. If you omit TAG TagName1, the relationship is established using the primary index key of the parent table. If the parent table does not have a primary index, Visual FoxPro generates an error.

Include TAG TagName1 to establish a relation based on an existing index tag for the parent table. Index tag names can contain up to 10 characters.

The parent table cannot be a free table.

NOCPTRANS
Prevents translation to a different code page for character and memo fields. If the table is converted to another code page, the fields for which NOCPTRANS has been specified are not translated. NOCPTRANS can be specified only for character and memo fields.

The following example creates a table named mytable containing two character fields and two memo fields. The second character field, char2, and the second memo field, memo2, include NOCPTRANS to prevent translation.

   CREATE TABLE mytable (char1 C(10), char2 C(10) NOCPTRANS,;
      memo1 M, memo2 M NOCPTRANS)
PRIMARY KEY eExpression2 TAG TagName2
Specifies a primary index to create. eExpression2 specifies any field or combination of fields in the table. TAG TagName2 specifies the name for the primary index tag that is created. Index tag names can contain up to 10 characters.

Because a table can have only one primary index, you cannot include this clause if you have already created a primary index for a field. Visual FoxPro generates an error if you include more than one PRIMARY KEY clause in CREATE TABLE.

UNIQUE eExpression3 TAG TagName3
Creates a candidate index. eExpression3 specifies any field or combination of fields in the table. However, if you have created a primary index with one of the PRIMARY KEY options, you cannot include the field that was specified for the primary index. TAG TagName3 specifies a tag name for the candidate index tag that is created. Index tag names can contain up to 10 characters.

A table can have multiple candidate indexes.

FOREIGN KEY eExpression4 TAG TagName4 [NODUP]
Creates a foreign (nonprimary) index and establishes a relationship to a parent table. eExpression4 specifies the foreign index key expression, and TagName4 specifies the name of the foreign index key tag that is created. Index tag names can contain up to 10 characters. Include NODUP to create a candidate foreign index.

You can create multiple foreign indexes for the table, but the foreign index expressions must specify different fields in the table.

REFERENCES TableName3 [TAG TagName5]
Specifies the parent table to which a persistent relationship is established. Include TAG TagName5 to establish a relation based on an index tag for the parent table. Index tag names can contain up to 10 characters. By default, if you omit TAG TagName5, the relationship is established using the parent table's primary index key.
CHECK eExpression2 [ERROR cMessageText2]
Specifies the table validation rule. ERROR cMessageText2 specifies the error message Visual FoxPro displays when the table validation rule is executed. The message is displayed only when data is changed within a Browse window or Edit window.
FROM ARRAY ArrayName
Specifies the name of an existing array whose contents are the name, type, precision, and scale for each field in the table. The contents of the array can be defined with the AFIELDS( ) function.

Remarks

The new table is opened in the lowest available work area and can be accessed by its alias. The new table is opened exclusively, regardless of the current setting of SET EXCLUSIVE.

If a database is open and you don't include the FREE clause, the new table is added to the database. You cannot create a new table with the same name as a table in the database.

If a database is open, CREATE TABLE - SQL requires exclusive use of the database. To open a database for exclusive use, include EXCLUSIVE in OPEN DATABASE.

If a database isn't open when you create the new table, including the NAME, CHECK, DEFAULT, FOREIGN KEY, PRIMARY KEY, or REFERENCES clauses generates an error.

Note CREATE TABLE syntax uses commas to separate certain CREATE TABLE options. Also, the NULL, NOT NULL, CHECK, DEFAULT, PRIMARY KEY, and UNIQUE clause must be placed within the parentheses containing the column definitions.

Driver Remarks

When your application sends the ODBC SQL statement CREATE TABLE to the data source, the Visual FoxPro ODBC Driver translates the command into the Visual FoxPro CREATE TABLE command using the syntax shown in the following table.

ODBC syntax | Visual FoxPro syntax
CREATE TABLE base-table-name (column-identifier data type [NOT NULL] [,column-identifier data type [NOT NULL] ...) | CREATE TABLE TableName1 [NAME LongTableName] (FieldName1 FieldType [(nFieldWidth [, nPrecision])] [NOT NULL])

When you create a table using the driver, the driver closes the table immediately after creation to allow access to the table by other users. This differs from Visual FoxPro, which leaves the table open exclusively upon creation. However, if a stored procedure on your data source containing a CREATE TABLE statement executes, the table is left open.

If the data source is a database (.dbc file), the Visual FoxPro ODBC Driver creates a table named LongTableName with the same name as the base-table-name.

Using Data Definition Language (DDL)

You cannot include DDL in the following places:

  • In a batch SQL statement that requires a transaction
  • In manual-commit mode, after a statement that required a transaction, unless your application first calls SQLTransact.

For example, if you want to create a temporary table, you should create the table before you begin the statement requiring a transaction. If you include the CREATE TABLE statement in a batch SQL statement that requires a transaction, the driver returns an error message.

See Also

ALTER TABLE - SQL |

Supported Data Types |

INSERT - SQL |

SELECT - SQL |


DELETE - SQL Command

Marks records for deletion.

The Visual FoxPro ODBC Driver supports the native Visual FoxPro language syntax for this command. For driver-specific information, see Driver Remarks.

Syntax

DELETE FROM [DatabaseName!]TableName
   [WHERE FilterCondition1 [AND | OR FilterCondition2 ...]]

Arguments

FROM [DatabaseName!]TableName
Specifies the table in which records are marked for deletion.

DatabaseName! specifies the name of a database containing the table if the containing database is not the database specified with the data source. You must include the name of a database containing the table if the database is not the database specified with the data source. Include the exclamation point (!) delimiter after the database name and before the table name.

WHERE FilterCondition1 [AND | OR FilterCondition2 ...]
Specifies that Visual FoxPro mark only certain records for deletion.

FilterCondition specifies the criteria that records must meet to be marked for deletion. You can include as many filter conditions as you like, connecting them with the AND or OR operator. You can also use the NOT operator to reverse the value of a logical expression, or you can use EMPTY( ) to check for an empty field.

Remarks

If SET DELETED is set to ON, records marked for deletion are ignored by all commands that include a scope.

DELETE - SQL uses record locking when marking multiple records for deletion in tables opened for shared access. This reduces record contention in multiuser situations but can reduce performance. For maximum performance, open the table for exclusive use.

Driver Remarks

When your application sends the ODBC SQL statement DELETE to the data source, the Visual FoxPro ODBC Driver converts the command into the Visual FoxPro DELETE command without translation.

See Also

SET DELETED


DELETE TAG Command

Removes a tag or tags from a compound index (.cdx) file.

Syntax

DELETE TAG TagName1 [OF CDXFileName1]
   [, TagName2 [OF CDXFileName2]] ...
 – Or –
DELETE TAG ALL [OF CDXFileName]

Arguments

TagName1 OF CDXFileName1 [, TagName2 [OF CDXFileName2]] ...
Specifies a tag to remove from a compound index file. You can delete multiple tags with one DELETE TAG by including a list of tag names separated by commas. If two or more tags with the same name exist in the open index files, you can remove a tag from a specific index file by including OF CDXFileName.
ALL [OF CDXFileName]
Removes every tag from a compound index file. If the current table has a structural compound index file, all tags are removed from the index file, the index file is deleted from the disk, and the flag in the table's header indicating the presence of an associated structural compound index file is removed. Use ALL with OF CDXFileName to remove all tags from an open compound index file other than the structural compound index file.

Remarks

Compound index files, created with INDEX, contain tags corresponding to index entries. DELETE TAG is used to remove a tag or tags from open compound index files. You can delete only tags from compound index files open in the current work area. If you remove all the tags from a compound index file, the file is deleted from the disk.

Visual FoxPro looks first for a tag in the structural compound index file (if one is open). If the tag isn't in the structural compound index file, Visual FoxPro then looks for the tag in the other open compound index files.

See Also

INDEX


DROP TABLE Command

Removes a table from the database specified with the data source and deletes it from disk.

The Visual FoxPro ODBC Driver supports the native Visual FoxPro language syntax for this command. For driver-specific information, see Driver Remarks.

Syntax

DROP TABLE TableName | FileName | ?

Settings

TableName
Specifies the table to remove from the database specified with the data source and to delete from disk.
FileName
Specifies a free table to delete from disk.
?
Displays the Remove dialog from which you can choose a table to remove from the database specified with the data source and to delete from disk.

Remarks

When DROP TABLE is issued, all primary indexes, default values, and validation rules associated with the table are also removed. DROP TABLE also affects other tables in the database specified with the data source if those tables have rules or relations associated with the table being removed. The rules and relations are no longer valid when the table is removed from the database.

Driver Remarks

When your application sends the ODBC SQL statement DROP TABLE to the data source, the Visual FoxPro ODBC Driver converts the command into the Visual FoxPro DROP TABLE command using the syntax shown in the following table.

ODBC syntax | Data source | Visual FoxPro syntax
DROP TABLE base-table-name | Database (.dbc file) | REMOVE TABLE TableName DELETE
Directory of free tables (.dbf files)
ERASE dbfName

ERASE cdxName

ERASE fptName


INDEX Command

Creates an index file to display and access table records in a logical order.

Syntax

INDEX ON eExpression TO IDXFileName | TAG TagName [OF CDXFileName]
   [FOR lExpression]
   [COMPACT]
   [ASCENDING | DESCENDING]
   [UNIQUE | CANDIDATE]
   [ADDITIVE]

Arguments

eExpression
Specifies an index expression that can include the name of a field or fields from the current table. An index key based on the index expression is created in the index file for each record in the table. Visual FoxPro uses these keys to display and access records in the table.

Note Although not recommended, eExpression can also be a memory variable, an array element, or a field or field expression from a table in another work area. Memo fields cannot be used alone in index file expressions; they must be combined with other character expressions. If you access an index that contains a variable or field that no longer exists or cannot be located, Visual FoxPro generates an error message.

If you attempt to build an index with a key that varies in length, the key will be padded with spaces. Variable-length index keys aren't supported in Visual FoxPro.

It is possible to create an index key with zero length. For example, a zero length index key is created when the index expression is a substring of an empty memo field. A zero length index key generates an error message. When Visual FoxPro creates an index, it evaluates fields in the first record in the table. If a field is empty, it might be necessary to enter some temporary data in the field in the first record to prevent a 0-length index key.

TO IDXFileName
Creates an .idx index file. The index file is given the default extension .idx.
TAG TagName [OF CDXFileName]
Creates a compound index file. A compound index file is a single index file that consists of any number of separate tags (index entries). Each tag is identified by its unique tag name. Tag names must begin with a letter or an underscore and can consist of any combination of up to 10 letters, digits, or underscores. The number of tags in a compound index file is limited only by available memory and disk space.

Multiple-entry compound index files are always compact. It isn't necessary to include COMPACT when creating a compound index file. Names of compound index files are given a .cdx extension.

Two types of compound index files can be created: structural and nonstructural.

Structural Compound Index Files You can create a structural compound index file with TAG TagName by excluding the optional OF CDXFileName clause. A structural compound index file always has the same base name as the table and is automatically opened when the table is opened.

Nonstructural Compound Index Files You can create a nonstructural compound index file by including OF CDXFileName after TAG TagName. Unlike a structural compound index file, a nonstructural compound index file must be explicitly opened with the INDEX clause in USE.

If a compound index file has already been created and opened, issuing INDEX with TAG TagName adds a tag to the compound index file.

FOR lExpression
Specifies a condition whereby only records that satisfy the filter expression lExpression are available for display and access; index keys are created in the index file for just those records matching the filter expression.

Visual FoxPro Rushmore technology optimizes an INDEX ... FOR lExpression command if lExpression is an optimizable expression. For best performance, use an optimizable expression in the FOR clause.

COMPACT
Creates a compact .idx file.
ASCENDING
Specifies an ascending order for the .cdx file. By default, .cdx tags are created in ascending order. (You can include ASCENDING as a reminder of the index file's order.) A table can be indexed in reverse order by including DESCENDING.
DESCENDING
Specifies a descending order for the .cdx file. You can't include DESCENDING when creating .idx index files.
UNIQUE
Specifies that only the first record encountered with a particular index key value is included in an .idx file or a .cdx tag. UNIQUE can be used to prevent the display of or access to duplicate records. All records added with duplicate index keys are excluded from the index file. Using the UNIQUE option of INDEX is identical to executing SET UNIQUE ON before issuing INDEX or REINDEX.

When a UNIQUE index or index tag is active and a duplicate record is changed in a manner that changes its index key, the index or index tag is updated. However, the next duplicate record with the original index key cannot be accessed or displayed until you reindex the file using REINDEX.

CANDIDATE
Creates a candidate structural index tag. The CANDIDATE keyword can be included only when creating a structural index tag; otherwise, Visual FoxPro generates an error message.

A candidate index tag prevents duplicate values in the field or combination of fields specified in the index expression eExpression. The term candidate refers to the type of index; because candidate indexes prevent duplicate values, they qualify as a "candidate" to be a primary index.

Visual FoxPro generates an error if you create a candidate index tag for a field or combination of fields that already contains duplicate values.

ADDITIVE
Keeps open any previously opened index files. If you omit the ADDITIVE clause when you create an index file or files for a table with INDEX, any previously opened index files (except the structural compound index) are closed.

Remarks

Records in a table that has an index file are displayed and accessed in the order specified by the index expression. The physical order of the records in the table isn't changed by an index file.

Index Types

Visual FoxPro lets you create two types of index files:

  • Compound .cdx index files containing multiple index entries called tags
  • .idx index files containing one index entry

You can also create a structural compound index file, which is automatically opened with the table.

Note Because structural compound index files are automatically opened when the table is opened, they are the preferred index type.

Include COMPACT to create compact .idx index files. Compound index files are always compact.

Index Order and Updating

Only one index file (the master index file) or tag (the master tag) controls the order in which the table is displayed or accessed. Certain commands (SEEK, for example) use the master index file or tag to search for records. However, all open .idx and .cdx index files are updated as changes are made to the table.

User-Defined Functions

Although an index expression can contain a user-defined function, you should not use user-defined functions in an index expression. User-defined functions in an index expression increase the time it takes to create or update the index. Also, index updates might not occur when a user-defined function is used for an index expression.

If you use a user-defined function in an index expression, Visual FoxPro must be able to locate the user-defined function. When Visual FoxPro creates an index, the index expression is saved in the index file but only a reference to the user-defined function is included in the index expression.

See Also

ALTER TABLE |

DELETE TAG |

SET COLLATE |

SET UNIQUE |


INSERT - SQL Command

Appends a record to the end of a table that contains the specified field values.

The Visual FoxPro ODBC Driver supports the native Visual FoxPro language syntax for this command. For driver-specific information, see Driver Remarks.

Syntax

INSERT INTO dbf_name [(fname1 [, fname2, ...])]
   VALUES (eExpression1 [, eExpression2, ...])

Arguments

INSERT INTO dbf_name
Specifies the name of the table to which the new record is appended. dbf_name can include a path and can be a name expression.

If the table you specify isn't open, it is opened exclusively in a new work area and the new record is appended to the table. The new work area isn't selected; the current work area remains selected.

If the table you specify is open, INSERT appends the new record to the table. If the table is open in a work area other than the current work area, it isn't selected after the record is appended; the current work area remains selected.

[(fname1 [, fname2 [, ...]])]
Specifies in the new record the names of the fields into which the values are inserted.
VALUES (eExpression1 [, eExpression2 [, ...]])
Specifies the field values inserted into the new record. If you omit the field names, you must specify the field values in the order defined by the table structure.

Remarks

The new record contains the data listed in the VALUES clause.

Driver Remarks

When your application sends the ODBC SQL statement INSERT to the data source, the Visual FoxPro ODBC Driver converts the command into the Visual FoxPro INSERT command without translation.

See Also

CREATE TABLE - SQL | SELECT - SQL


SELECT - SQL Command

Retrieves data from one or more tables.

The Visual FoxPro ODBC Driver supports the native Visual FoxPro language syntax for this command. For driver-specific information, see Driver Remarks.

Syntax

SELECT [ALL | DISTINCT]
   [Alias.] Select_Item [AS Column_Name]
   [, [Alias.] Select_Item [AS Column_Name] ...]
FROM [DatabaseName!]Table [Local_Alias]
   [, [DatabaseName!]Table [Local_Alias] ...]
[WHERE JoinCondition [AND JoinCondition
…]
   [AND | OR FilterCondition [AND | OR FilterCondition ...]]]
[GROUP BY GroupColumn [, GroupColumn ...]]
[HAVING FilterCondition]
[UNION [ALL] SELECTCommand]
[ORDER BY Order_Item [ASC | DESC] [, Order_Item [ASC | DESC] ...]]

Note A subquery, referred to in the following arguments, is a SELECT within a SELECT and must be enclosed in parentheses. You can have up to two subqueries at the same level (not nested) in the WHERE clause. (See that section of the arguments.) Subqueries can contain multiple join conditions.

Arguments

[ALL | DISTINCT]
[Alias.] Select_Item [AS Column_Name]
[, [Alias.] Select_Item [AS Column_Name] ...]
The SELECT clause specifies the fields, constants, and expressions that are displayed in the query results.

ALL, by default, displays all the rows in the query results.

DISTINCT excludes duplicates of any rows from the query results.

Note You can use DISTINCT only once per SELECT clause.

Alias. qualifies matching item names. Each item you specify with Select_Item generates one column of the query results. If two or more items have the same name, include the table alias and a period before the item name to prevent columns from being duplicated.

Select_Item specifies an item to be included in the query results. An item can be one of the following:

  • The name of a field from a table in the FROM clause.
  • A constant specifying that the same constant value is to appear in every row of the query results.
  • An expression that can be the name of a user-defined function.

User-Defined Functions with SELECT

Although using user-defined functions in the SELECT clause has obvious benefits, you should also consider the following restrictions:

  • The speed of operations performed with SELECT might be limited by the speed at which such user-defined functions are executed. High-volume manipulations involving user-defined functions might be better accomplished by using API and user-defined functions written in C or assembly language.
  • The only reliable way to pass values to user-defined functions invoked from SELECT is by the argument list passed to the function when it is invoked.
  • Even if you experiment and discover a supposedly forbidden manipulation that works correctly in a certain version of FoxPro, there is no guarantee it will continue to work in later versions.

Apart from these restrictions, user-defined functions are acceptable in the SELECT clause. However, remember that using SELECT might slow performance.

The following field functions are available for use with a select item that is a field or an expression involving a field:

  • AVG(Select_Item)—Averages a column of numeric data.
  • COUNT(Select_Item)—Counts the number of select items in a column. COUNT(*) counts the number of rows in the query output.
  • MIN(Select_Item)—Determines the smallest value of Select_Item in a column.
  • MAX(Select_Item)—Determines the largest value of Select_Item in a column.
  • SUM(Select_Item)—Totals a column of numeric data.

You cannot nest field functions.

AS Column_Name
Specifies the heading for a column in the query output. This is useful when Select_Item is an expression or contains a field function and you want to give the column a meaningful name. Column_Name can be an expression but cannot contain characters (for example, spaces) that aren't permitted in table field names.
FROM [DatabaseName!]Table [Local_Alias]
[, [DatabaseName!]Table [Local_Alias] ...]
Lists the tables containing the data that the query retrieves. If no table is open, Visual FoxPro displays the Open dialog box so that you can specify the file location. Once open, the table remains open after the query is complete.

DatabaseName! specifies the name of a database other than the one specified with the data source. You must include the name of the database containing the table if the database is not specified with the data source. Include the exclamation point (!) delimiter after the database name and before the table name.

Local_Alias specifies a temporary name for the table named in Table. If you specify a local alias, you must use the local alias in place of the table name throughout the SELECT statement. The local alias doesn't affect the Visual FoxPro environment.

WHERE JoinCondition [AND JoinCondition ...]
[AND | OR FilterCondition [AND | OR FilterCondition ...]]
Tells Visual FoxPro to include only certain records in the query results. WHERE is required to retrieve data from multiple tables.

JoinCondition specifies fields that link the tables in the FROM clause. If you include more than one table in a query, you should specify a join condition for every table after the first.

Important Keep the following information in mind when creating join conditions:

  • If you include two tables in a query and don't specify a join condition, every record in the first table is joined with every record in the second table as long as the filter conditions are met. Such a query can produce lengthy results.
  • Use caution when joining tables with empty fields because Visual FoxPro matches empty fields. For example, if you join on CUSTOMER.ZIP and INVOICE.ZIP and if CUSTOMER contains 100 empty zip codes and INVOICE contains 400 empty zip codes, the query output contains 40,000 extra records resulting from the empty fields. Use the EMPTY( ) function to eliminate empty records from the query output.
  • You must use the AND operator to connect multiple join conditions. Each join condition has the following form:
FieldName1 Comparison FieldName2
FieldName1 is the name of a field from one table, FieldName2 is the name of a field from another table, and Comparison is one of the operators described in the following table.
Operator | Comparison
= Equal
== Exactly equal
LIKE | SQL LIKE
<>, !=, # Not equal
> More than
>= More than or equal to
< Less than
<= Less than or equal to

When you use the = operator with strings, it acts differently, depending on the setting of SET ANSI. When SET ANSI is set to OFF, Visual FoxPro treats string comparisons in a manner familiar to Xbase users. When SET ANSI is set to ON, Visual FoxPro follows ANSI standards for string comparisons. See SET ANSI and SET EXACT for additional information about how Visual FoxPro performs string comparisons.

FilterCondition specifies the criteria that records must meet to be included in the query results. You can include as many filter conditions in a query as you want, connecting them with the AND or OR operator. You can also use the NOT operator to reverse the value of a logical expression, or you can use EMPTY( ) to check for an empty field. FilterCondition can take any of the forms in the following examples:

Example 1 FieldName1 Comparison FieldName2

customer.cust_id = orders.cust_id

Example 2 FieldName Comparison Expression

payments.amount >= 1000

Example 3 FieldName Comparison ALL (Subquery)

company < ALL ;
(SELECT company FROM customer WHERE country = "UK")

When the filter condition includes ALL, the field must meet the comparison condition for all values generated by the subquery before its record is included in the query results.

Example 4 FieldName Comparison ANY | SOME (Subquery)

company < ANY ;
(SELECT company FROM customer WHERE country = "UK")

When the filter condition includes ANY or SOME, the field must meet the comparison condition for at least one of the values generated by the subquery.

The following example checks to see whether the values in the field are within a specified range of values:

Example 5 FieldName [NOT] BETWEEN Start_Range AND End_Range

customer.postalcode BETWEEN 90000 AND 99999

The following example checks to see whether at least one row meets the criteria in the subquery. When the filter condition includes EXISTS, the filter condition evaluates to True (.T.) unless the subquery evaluates to the empty set.

Example 6 [NOT] EXISTS (Subquery)

EXISTS ;
(SELECT * FROM orders WHERE customer.postalcode =
orders.postalcode)

Example 7 FieldName [NOT] IN Value_Set

customer.postalcode NOT IN ("98052","98072","98034")

When the filter condition includes IN, the field must contain one of the values before its record is included in the query results.

Example 8 FieldName [NOT] IN (Subquery)

customer.cust_id IN ;
(SELECT orders.cust_id FROM orders WHERE orders.city="Seattle")

Here the field must contain one of the values returned by the subquery before its record is included in the query results.

Example 9 FieldName [NOT] LIKE cExpression

customer.country NOT LIKE "UK"

This filter condition searches for each field that matches cExpression. You can use the percent sign (%) and underscore ( _ ) wildcards as part of cExpression. The underscore represents a single unknown character in the string.

GROUP BY GroupColumn [, GroupColumn ...]
Groups rows in the query based on values in one or more columns. GroupColumn can be one of the following:
  • The name of a regular table field.
  • A field that includes an SQL field function.
  • A numeric expression indicating the location of the column in the result table. (The leftmost column number is 1.)
HAVING FilterCondition
Specifies a filter condition that groups must meet to be included in the query results. HAVING should be used with GROUP BY and can include as many filter conditions as you like, connected with the AND or OR operator. You can also use NOT to reverse the value of a logical expression.

FilterCondition cannot contain a subquery.

A HAVING clause without a GROUP BY clause acts like a WHERE clause. You can use local aliases and field functions in the HAVING clause. Use a WHERE clause for faster performance if your HAVING clause contains no field functions.

[UNION [ALL] SELECTCommand]
Combines the final results of one SELECT with the final results of another SELECT. By default, UNION checks the combined results and eliminates duplicate rows. Use parentheses to combine multiple UNION clauses.

ALL prevents UNION from eliminating duplicate rows from the combined results.

UNION clauses follow these rules:

  • You cannot use UNION to combine subqueries.
  • Both SELECT commands must have the same number of columns in their query output.
  • Each column in the query results of one SELECT must have the same data type and width as the corresponding column in the other SELECT.
  • Only the final SELECT can have an ORDER BY clause, which must refer to output columns by number. If an ORDER BY clause is included, it affects the entire result.

You can also use the UNION clause to simulate an outer join.

When you join two tables in a query, only records with matching values in the joining fields are included in the output. If a record in the parent table doesn't have a corresponding record in the child table, the record in the parent table isn't included in the output. An outer join allows you to include all the records in the parent table in the output, along with the matching records in the child table. To create an outer join in Visual FoxPro, you need to use a nested SELECT command, as in the following example:

SELECT customer.company, orders.order_id, orders.emp_id ;
FROM customer, orders ;
WHERE customer.cust_id = orders.cust_id ;
UNION ;
SELECT customer.company, 0, 0 ;
FROM customer ;
WHERE customer.cust_id NOT IN ;
(SELECT orders.cust_id FROM orders)

Note Be sure to include the space immediately preceding each semicolon. Otherwise, you'll receive an error.

The section of the command before the UNION clause selects records from both tables that have matching values. The customer companies that do not have associated invoices are not included. The section of the command after the UNION clause selects records in the customer table that do not have matching records in the orders table.

Regarding the second section of the command, note the following:

  • The SELECT statement within the parentheses is processed first. This statement results in a selection of all customer numbers in the orders table.
  • The WHERE clause finds all customer numbers in the customer table that are not in the orders table. Since the first section of the command provided all companies that had a customer number in the orders table, all companies in the customer table are now included in the query results.
  • Because the structures of tables included in a UNION must be identical, there are two placeholders in the second SELECT statement to represent orders.order_id and orders.emp_id from the first SELECT statement.

    Note The placeholders must be the same type as the fields they represent. If the field is a date type, the placeholder should be { / / }. If the field is a character field, the placeholder should be the empty string ("").

ORDER BY Order_Item [ASC | DESC] [, Order_Item [ASC | DESC] ...]
Sorts the query results based on the data in one or more columns. Each Order_Item must correspond to a column in the query results and can be one of the following:
  • A field in a FROM table that is also a select item in the main SELECT clause (not in a subquery).
  • A numeric expression indicating the location of the column in the result table. (The leftmost column is number 1.)

ASC specifies an ascending order for query results, according to the order item or items, and is the default for ORDER BY.

DESC specifies a descending order for query results.

Query results appear unordered if you don't specify an order with ORDER BY.

Remarks

SELECT is an SQL command that is built into Visual FoxPro like any other Visual FoxPro command. When you use SELECT to pose a query, Visual FoxPro interprets the query and retrieves the specified data from the tables. You can create a SELECT query from within either the Command window or a Visual FoxPro program (as with any other Visual FoxPro command).

Note SELECT does not respect the current filter condition specified with SET FILTER.

Driver Remarks

When your application sends the ODBC SQL statement SELECT to the data source, the Visual FoxPro ODBC Driver converts the command into the Visual FoxPro SELECT command without translation unless the command contains an ODBC escape sequence. Items enclosed in an ODBC escape sequence are converted to Visual FoxPro syntax. For more information on using ODBC escape sequences, see Time and Date Functions and in the Microsoft ODBC Programmer's Reference, see Escape Sequences in ODBC.

See Also

CREATE TABLE - SQL | INSERT - SQL | SET ANSI | SET EXACT


SET ANSI Command

Determines how comparisons between strings of different lengths are made with the = operator in Visual FoxPro SQL commands.

Syntax

SET ANSI ON | OFF

Arguments

ON
(Default for the driver; the default for Visual FoxPro is OFF.) Pads the shorter string with the blanks needed to make it equal to the longer string's length. The two strings are then compared character for character for their entire lengths. Consider this comparison:
'Tommy' = 'Tom'

The result is False (.F.) if SET ANSI is on, because when padded, 'Tom' becomes 'Tom ' and the strings 'Tom ' and 'Tommy' don't match character for character.

The == operator uses this method for comparisons in Visual FoxPro SQL commands.

OFF
Specifies that the shorter string not be padded with blanks. The two strings are compared character for character until the end of the shorter string is reached. Consider this comparison:
'Tommy' = 'Tom'

The result is True (.T.) when SET ANSI is off, because the comparison stops after 'Tom'.

Remarks

SET ANSI determines whether the shorter of two strings is padded with blanks when an SQL string comparison is made. SET ANSI has no effect on the == operator; when you use the == operator, the shorter string is always padded with blanks for the comparison.

String Order

In SQL commands, the left-to-right order of the two strings in a comparison is irrelevant—switching a string from one side of the = or == operator to the other doesn't affect the result of the comparison.

See Also

SELECT - SQL | SET EXACT


SET BLOCKSIZE Command

Specifies how disk space is allocated for the storage of memo fields.

Syntax

SET BLOCKSIZE TO nBytes

Arguments

nBytes
Specifies the block size in which disk space for memo fields is allocated. If nBytes is 0, disk space is allocated in single bytes (blocks of 1 byte). If nBytes is an integer between 1 and 32, disk space is allocated in blocks of nBytes bytes multiplied by 512. If nBytes is greater than 32, disk space is allocated in blocks of nBytes bytes. If you specify a block size value greater than 32, you can save substantial disk space.

Remarks

The default value for SET BLOCKSIZE is 64. To reset the block size to a different value after the file has been created, set it to a new value and then use COPY to create a new table. The new table has the specified block size.


SET COLLATE Command

Specifies a collation sequence for character fields in subsequent indexing and sorting operations.

Syntax

SET COLLATE TO cSequenceName

Arguments

cSequenceName
Specifies a collation sequence. The available collation sequence options are described in the following table.
Options | Language
DUTCH | Dutch
GENERAL | English, French, German, Modern Spanish, Portuguese, and other Western European languages
GERMAN | German phone book order (DIN)
ICELAND | Icelandic
MACHINE | Machine (the default collation sequence for earlier FoxPro versions)
NORDAN | Norwegian, Danish
SPANISH | Traditional Spanish
SWEFIN | Swedish, Finnish
UNIQWT | Unique Weight

Note When you specify the SPANISH option, ch is a single letter that sorts between c and d, and ll sorts between l and m.

If you specify a collation sequence option as a literal character string, be sure to enclose the option in quotation marks:

SET COLLATE TO "SWEFIN"

MACHINE is the default collation sequence option and is the sequence Xbase users are familiar with. Characters are ordered as they appear in the current code page.

GENERAL may be preferable for U.S. and Western European users. Characters are ordered as they appear in the current code page. In FoxPro versions earlier than 2.5, indexes might have been created using the UPPER( ) or LOWER( ) functions to convert character fields to a consistent case. In FoxPro versions later than 2.5, you can instead specify the GENERAL collation sequence option and omit the UPPER( ) conversion.

If you specify a collation sequence option other than MACHINE and if you create an .idx file, a compact .idx is always created.

Use SET("COLLATE") to return the current collation sequence.

You can specify a collating sequence for a data source by using the ODBC Visual FoxPro Setup Dialog Box or by using the Collate keyword in your connection string with SQLDriverConnect. This is identical to issuing the following command:

SET COLLATE TO cSequenceName

Remarks

SET COLLATE enables you to order tables containing accented characters for any of the supported languages. Changing the setting of SET COLLATE doesn't affect the collating sequence of previously opened indexes. Visual FoxPro automatically maintains existing indexes, providing the flexibility to create many different types of indexes, even for the same field.

For example, if an index is created with SET COLLATE set to GENERAL and the SET COLLATE setting is later changed to SPANISH, the index retains the GENERAL collation sequence.

See Also

ODBC Visual FoxPro Setup Dialog Box


SET DELETED Command

Specifies whether records marked for deletion are processed and whether they are available for use in other commands.

Syntax

SET DELETED ON | OFF

Arguments

ON
(Default for the driver; the default for Visual FoxPro is OFF.) Specifies that commands that operate on records (including records in related tables) using a scope ignore records marked for deletion.
OFF
Specifies that records marked for deletion can be accessed by commands that operate on records (including records in related tables) using a scope.

Remarks

Queries that use DELETED( ) to test the status of records can be optimized using Visual FoxPro Rushmore technology if the table is indexed on DELETED( ).

Important SET DELETED is ignored if the default scope for the command is the current record or if you include a scope of a single record. INDEX always ignores SET DELETED and indexes all records in the table.

See Also

DELETE - SQL


SET EXACT Command

Specifies the rules for comparing two strings of different lengths.

Syntax

SET EXACT ON | OFF

Arguments

ON
Specifies that expressions must match character for character to be equivalent. Any trailing blanks in the expressions are ignored for the comparison. For the comparison, the shorter of the two expressions is padded on the right with blanks to match the length of the longer expression.
OFF
(Default.) Specifies that, to be equivalent, expressions must match character for character until the end of the expression on the right side is reached.

Remarks

The SET EXACT setting has no effect if both strings are the same length.

String Comparisons

Visual FoxPro has two relational operators that test for equality.

The = operator performs a comparison between two values of the same type. This operator is suited for comparing character, numeric, date, and logical data.

However, when you compare character expressions with the = operator, the results might not be exactly what you expect. Character expressions are compared character for character from left to right until one of the expressions isn't equal to the other, until the end of the expression on the right side of the = operator is reached (SET EXACT OFF), or until the ends of both expressions are reached (SET EXACT ON).

The == operator can be used when an exact comparison of character data is needed. If two character expressions are compared with the == operator, the expressions on both sides of the == operator must contain exactly the same characters, including blanks, to be considered equal. The SET EXACT setting is ignored when character strings are compared using ==.

The following table shows how the choice of operator and the SET EXACT setting affect comparisons. (An underscore represents a blank space.)

Comparison | = EXACT OFF | = EXACT ON | == EXACT ON or OFF
"abc" = "abc" | Match | Match | Match
"ab" = "abc" | No match | No match | No match
"abc" = "ab" | Match | No match | No match
"abc" = "ab_" | No match | No match | No match
"ab" = "ab_" | No match | Match | No match
"ab_" = "ab" | Match | Match | No match
"" = "ab" | No match | No match | No match
"ab" = "" | Match | No match | No match
"__" = "" | Match | Match | No match
"" = "___" | No match | Match | No match
TRIM("___") = "" | Match | Match | Match
"" = TRIM("___") | Match | Match | Match

See Also

SET ANSI


SET EXCLUSIVE Command

Specifies whether table files are opened for exclusive or shared use on a network.

Syntax

SET EXCLUSIVE ON | OFF

Arguments

ON
Limits accessibility of a table opened on a network to the user who opened it. The table isn't accessible to other users on the network. SET EXCLUSIVE ON also prevents all other users from having read-only access.
OFF
(Default for the driver; the defaults for Visual FoxPro are ON for the global data session and OFF for a private data session.) Allows a table opened on a network to be shared and modified by any user on the network.

Remarks

Changing the setting of SET EXCLUSIVE doesn't change the status of previously opened tables. For example, if a table is opened with SET EXCLUSIVE set to ON and SET EXCLUSIVE is later changed to OFF, the table retains its exclusive-use status.

See Also

ODBC Visual FoxPro Setup Dialog Box


SET NULL Command

Determines how null values are supported by the ALTER TABLE - SQL, CREATE TABLE - SQL, and INSERT - SQL commands.

Syntax

SET NULL ON | OFF

Arguments

ON
(Default for the driver; the default for Visual FoxPro is OFF.) Specifies that all columns in a table created with ALTER TABLE and CREATE TABLE will allow null values. You can override null value support for columns in the table by including the NOT NULL clause in the columns' definitions.

Also specifies that INSERT - SQL will insert null values into any columns not included in the INSERT - SQL VALUE clause. INSERT - SQL will insert null values only into columns that allow null values.

OFF
Specifies that all columns in a table created with ALTER TABLE and CREATE TABLE will not allow null values. You can designate null value support for columns in ALTER TABLE and CREATE TABLE by including the NULL clause in the columns' definitions.

Also specifies that INSERT - SQL will insert blank values into any columns not included in the INSERT - SQL VALUE clause.

Remarks

SET NULL affects only how null values are supported by ALTER TABLE, CREATE TABLE, and INSERT - SQL. Other commands are unaffected by SET NULL.

See Also

ALTER TABLE | CREATE TABLE - SQL | INSERT - SQL


SET PATH Command

Specifies a path for file searches. For driver-specific information, see Driver Remarks.

Syntax

SET PATH TO [Path]

Arguments

TO [Path]
Specifies the directories you want Visual FoxPro to search. Use commas or semicolons to separate the directories.

Remarks

SET PATH allows you to specify search paths for other Visual FoxPro programs that can be called within stored procedures. SET PATH will not change the path of the data source that you've specified for the connection.

Issue SET PATH TO without Path to restore the path to the default directory or folder.

Driver Remarks

If you issue SET PATH in a stored procedure, it will be ignored by the following functions and commands:

  • Catalog functions such as SQLTables and SQLColumns will ignore the new path and continue to reference the path specified by the data source in SQLPrepare or SQLExecDirect.
  • Commands such as SELECT, INSERT, UPDATE, DELETE, and CREATE TABLE will ignore the new path and continue to reference the path specified by the data source in SQLPrepare or SQLExecDirect.

If you issue SET PATH in a stored procedure and don't subsequently set the path back to its original state, other connections to the database will use the new path (because SET PATH is not scoped to data sessions).

If you want to create, select, or update tables in a directory other than that specified by the data source, specify the full path of the file with your command.

See Also

ODBC Visual FoxPro Setup Dialog Box | SQLColumns | SQLDriverConnect | SQLTables


SET REPROCESS Command

Specifies how many times or for how long to lock a file or record after an unsuccessful locking attempt.

Syntax

SET REPROCESS TO nAttempts [SECONDS] | TO AUTOMATIC

Arguments

TO nAttempts [SECONDS]
Specifies the number of times or number of seconds to try to lock a record or file after an initial unsuccessful attempt. The default value is 0; the maximum value is 32,000.

SECONDS specifies that Visual FoxPro attempts to lock a file or record for nAttempts seconds. It's available only when nAttempts is greater than zero.

For example, if nAttempts is 30, Visual FoxPro attempts to lock a record or file up to 30 times. If you also include SECONDS (SET REPROCESS TO 30 SECONDS), Visual FoxPro continuously attempts to lock a record or file for up to 30 seconds.

If an ON ERROR routine is in effect and if attempts by a command to lock the record or file are unsuccessful, the ON ERROR routine is executed. However, if a function attempts the lock, an ON ERROR routine isn't executed and the function returns False (.F.).

If an ON ERROR routine isn't in effect, a command attempts to lock the record or file, and the lock can't be placed, an error is generated. If a function attempts to place the lock, the alert isn't displayed and the function returns False (.F.).

If nAttempts is 0 (the default value) and you issue a command or function that attempts to lock a record or file, Visual FoxPro tries to lock the record or file indefinitely. If the record or file becomes available for locking while you wait, the lock is placed and the system message is cleared. If a function attempted to place the lock, the function returns True (.T.).

If an ON ERROR routine is in effect and a command is attempting to lock the record or file, the ON ERROR routine takes precedence over additional attempts to lock the record or file. The ON ERROR routine is immediately executed. Visual FoxPro does not attempt additional record or file locks and does not display the system message.

If nAttempts is –1, Visual FoxPro attempts to lock the record or file indefinitely and an ON ERROR routine isn't executed.

If a lock has been placed by another user on the record or file you are attempting to lock, you must wait until the user releases the lock.

TO AUTOMATIC
Specifies that Visual FoxPro attempts to lock the record or file indefinitely. (SET REPROCESS TO -2 is an equivalent command.)

Remarks

The first attempt to lock a record or file isn't always successful. Frequently, a record or file is locked by another user on the network. SET REPROCESS determines whether Visual FoxPro makes additional attempts to lock the record or file when the initial attempt is unsuccessful. You can specify either how many times additional attempts are made or for how long the attempts are made. An ON ERROR routine affects how unsuccessful lock attempts are handled.


SET UNIQUE Command

Specifies whether records with duplicate index key values are maintained in an index file.

Syntax

SET UNIQUE ON | OFF

Arguments

ON
Specifies that any record with a duplicate index key value not be included in the index file. Only the first record with the original index key value is included in the index file.
OFF
(Default.) Specifies that records with duplicate index key values be included in the index file.

Remarks

An index file retains its SET UNIQUE setting when you issue REINDEX. For more information, see INDEX.


UPDATE - SQL Command

Updates records in a table with new values.

The Visual FoxPro ODBC Driver supports the native Visual FoxPro language syntax for this command. For driver-specific information, see Driver Remarks.

Syntax

UPDATE [DatabaseName1!]TableName1
SET Column_Name1 = eExpression1
   [, Column_Name2 = eExpression2 ...]
   WHERE FilterCondition1 [AND | OR FilterCondition2 ...]

Arguments

UPDATE [DatabaseName1!]TableName1
Specifies the table in which records are updated with new values.

DatabaseName1! specifies the name of a database other than the database specified with the data source containing the table. You must include the name of the database containing the table if the database is not the current one. Include the exclamation point (!) delimiter after the database name and before the table name.

SET Column_Name1 = eExpression1
[, Column_Name2 = eExpression2
Specifies the columns that are updated and their new values. If you omit the WHERE clause, every row in the column is updated with the same value.
WHERE FilterCondition1 [AND | OR FilterCondition2 ...]
Specifies the records that are updated with new values.

FilterCondition specifies the criteria that records must meet to be updated with new values. You can include as many filter conditions as you like, connecting them with the AND or OR operator. You can also use the NOT operator to reverse the value of a logical expression, or you can use EMPTY( ) to check for an empty field.

Remarks

UPDATE - SQL can update only records in a single table.

Unlike REPLACE, UPDATE - SQL uses record locking when updating multiple records in tables opened for shared access. This reduces record contention in multiuser situations but can reduce performance. For maximum performance, open the table for exclusive use or use FLOCK( ) to lock the table.

Driver Remarks

When your application sends the ODBC SQL statement UPDATE to the data source, the Visual FoxPro ODBC Driver converts the command into the Visual FoxPro UPDATE command without translation.

See Also

DELETE - SQL | INSERT - SQL


Visual FoxPro Field Data Types

The following table lists the values for the FieldType argument in ALTER TABLE and CREATE TABLE and indicates whether nFieldWidth and nPrecision arguments are required.

FieldType | NFieldWidth | nPrecision | Description
B | - | d | Double
C | N | - | Character field of width n
D | - | - | Date
F | N | d | Floating numeric field of width n with d decimal places
G | - | - | General
I | - | - | Integer
L | - | - | Logical
M | - | - | Memo
N | N | d | Numeric field of width n with d decimal places
T | - | - | DateTime
Y | - | - | Currency


ODBC Test

ODBC Test

Microsoft® ODBC Test is an ODBC-enabled application that you can use to test ODBC drivers and the ODBC Driver Manager. ODBC 3.51 includes both ANSI and Unicode-enabled versions of ODBC Test. The corresponding files are as follows:

  • Odbcte32.exe and Gtrtst32.dll, for the ANSI version.
  • Odbct32w.exe and Gtrts32w.dll, for the Unicode version.

To use ODBC Test, you must understand the ODBC API, the C language, and SQL. For more information about the ODBC API, see the ODBC Programmer's Reference.

Help topics that were formerly included within this section of the MDAC SDK documentation are now contained within the ODBC Test program. Open Odbcte32.exe or Odbct32w.exe, open the Help menu, and then click Help Topics.


ODBC Samples

ODBC Samples Overview

The ODBC Samples consist of four code samples that enable you to connect, execute queries, and convert user and system data set names to file names. These samples to allow you to write a general-purpose ODBC-enabled application.

For sample information about See
  • Connecting to a data source
  • Executing SQL statements
  • Running scripts
  • Listing data source information
 Admin Demo Sample
Capabilities of the ODBC Cursor Library to:
  • Use a block cursor
  • Scroll
  • Position an update
  • Delete
 Cursor Demo Sample
  • Managing multiple simultaneous connections using ODBC
  • Executing any SQL query and browsing the results
 Query Demo Sample
  • Converting User and System DSNs to File DSNs
 Convert DSN Demo Sample


Admin Demo Sample

Admin Demo uses the ODBC API to enable you to connect to a data source, execute SQL statements, run scripts, and perform other special tasks. In addition, Admin Demo can list information (such as data types, functions, and general ODBC information) about the data source being used.

Note Admin Demo is provided as a sample. It is not designed to replace proprietary DBMS tools, because there are some tasks that can be done only with DBMS-specific tools. Instead, Admin Demo and its sample code are supplied as an example of how to write a general-purpose, ODBC-enabled application for performing system operations on different data sources.

Features

Connection Windows

Admin Demo enables you to connect to multiple data sources. The only limits on connections are those imposed by each driver and the amount of memory available on the system. Each new connection creates a multiple-document interface (MDI) child Connection window from which SQL statements can be executed.

Input and Output Windows

The Connection window contains two panes. The input (upper) pane enables you to edit a file, enter new statements, and execute those statements. The output (lower) pane is used to log error and diagnostic messages.

To toggle between the input and output panes, press F6. If a Results window is currently active, pressing F6 causes the cursor to activate the Connection window that owns that Results window.

Results Windows

The output from operations that produce result sets, such as executing SQL statements or invoking ODBC catalog functions, is displayed in a Results window. Each Results window is an MDI child window of the Connection window in which the operation was performed. This means that disconnecting from a Connection window closes all Results windows created from that Connection window.

Limitations

Admin Demo is a useful sample application, but not a polished product. It is up to you to improve its performance and functionality. You should be aware of the following limitations.

Maximum Rows Fetched in a Result Set: 1000

The maximum number of rows that can be fetched in a result set is 1000. Each fetched row is placed in a structure that is added to an owner-drawn list box. Because the operating system limits the number of elements in a list box to 1000, Admin Demo cannot handle more records than this limit.

Simplistic Memory Management

The memory management scheme used in Admin Demo is also very limited. When Admin Demo needs memory, it calls GlobalAlloc. This is not the most efficient method because it can cause memory fragmentation and poor performance.

No Asynchronous Operation

Admin Demo does not take advantage of the asynchronous capabilities of some ODBC drivers. It executes a query and fetches the result set synchronously, limiting performance for long queries.

Other Limitations

  • Maximum connections: limited by driver and memory
  • Maximum result sets: limited by user and memory
  • Maximum rowset buffer size: 64 KB


Admin Demo Sample Files

Files Location

The Admin Demo sample code resides in the \Samples\Odbc\Admin directory. The executable file Admndemo.exe resides in the \Bin\Odbc directory.

Sample Code File Contents

File name | Description
Admndemo.c | This is the main module. It creates the main window and handles rudimentary functionality (such as displaying the About dialog box).
Admndemo.def | The module definition file for Admin Demo.
Admndemo.dlg | Contains the dialog templates for Admin Demo.
Admndemo.h | Contains the prototypes and definitions for Admndemo.c.
Admndemo.ico | Admin Demo icon.
Ademo.mak | Builds the ODBC Admin Demo application.
Admndemo.rc | Resource file for Admin Demo.
Child.h | Contains the main structure for tracking Connection window information, including ODBC handles.
Connect.ico | Minimized Connection window icon.
Dialogs.c | Contains code for the catalog functions found under the Lists menu.
Dialogs.h | Contains the prototypes and definitions for Dialogs.c.
Errcheck.c | Contains generic error-checking utility functions.
Errcheck.h | Contains the prototypes and definitions for Errcheck.c.
Execute.c | Executes ad hoc SQL statements and file scripts, as well as the Windows® code for displaying result sets.
Execute.h | Contains the prototypes and definitions for Execute.c.
Info.c | Used to track informational items found under the Info menu.
Info.h | Contains the prototypes and definitions for Info.c.
Ini.c | Used to track .ini file information. These functions abstract access to the Admndemo.ini file, making it more portable.
Ini.h | Contains prototypes and definitions for Ini.c.
Menu.c | Contains state-tracking code as well as the menu-enabling routines.
Menu.h | Contains the prototypes and definitions for Menu.c.
Results.c | Contains code for describing result sets, including all column information, data, and screen output attributes.
Results.h | Contains the prototypes and definitions for Results.c.
Results.ico | This is the minimized result set icon.
Standard.c | Contains a set of common functions used by several different modules.
Standard.h | Contains the prototypes and definitions for Standard.c.
Strings.h | Contains string IDs for resource strings found in Admndemo.rc.


How to Build Admin Demo

The file \Samples\Odbc\Admin\Ademo.mak is set up for compilation in 32-bit environments using a Microsoft or Microsoft-compatible C compiler. Use the \Samples\Odbc\Admin\Ademo.bat file to set the appropriate environment and to invoke the appropriate compiler and linker.

Note The Ademo.mak file assumes that you have installed the Platform SDK. If you are using Microsoft® Visual C++® 1.0 or later on Microsoft Windows NT®/Windows 2000, Admin Demo will not compile with the provided Ademo.mak. Instead, you must use the makefile generated by Visual C++.

To build the sample, change to the directory where Ademo.bat resides, and then perform the following steps:

  • To build the 32-bit release version in the \NDEBUG32 directory, type:
    ADEMO NDEBUG32
  • To build the 32-bit debug version in the \DEBUG32 directory, type:
    ADEMO DEBUG32

Unrecognized arguments to Ademo.bat will halt compilation with a warning.

To rebuild the Admndemo.exe file, Ademo.bat calls NMAKE, the Microsoft Program Maintenance Utility provided with Microsoft C version 7.0 or later, on the file Ademo.mak. If Ademo.mak is unmodified, Ademo.bat will rebuild the original Admndemo.exe sample.

For information about using NMAKE, see your C compiler tools documentation.


How to Run Admin Demo

You can run the Admin Demo sample by double-clicking the Admndemo.exe file in Microsoft Windows Explorer or by clicking Start, and then Run, and typing \<path>\Admndemo.exe.

Admin Demo Command-Line Switches

Admin Demo supports command-line switches to automatically connect to data sources, execute SQL scripts, and trace ODBC function calls. The switches are:

/D[connection-string]

This switch specifies a connection string for SQLDriverConnect. See the driver documentation for the precise syntax of this string. For example:

 /D[DSN=myserver;UID=Smith;PWD=Sesame;]

Note Except for the /T option, the /D option must be specified for any other option to work.

/Ffilename

This switch specifies the full path of a file to execute in the same manner used for the File/Execute menu item.

/O:option

This switch specifies a set of options that determine how the file specified with /F is executed. Each option is separated by a colon.

  • T—The statement terminator, either CR (default) or '<value>'
  • M—Maximum statement size (default is 1000)

/Tfilename

This switch specifies the full path of a file to use for tracing of ODBC statements made by Admin Demo. This file name is used as a parameter to the SQLSetConnectAttr function with the SQL_ATTR_TRACEFILE option.

/Xoption

Set this switch to 'Y' to force Admin Demo to exit once all other switches have been processed; set it to 'N' (the default) to leave Admin Demo running.

Examples

In the following example, the statement terminator is the semicolon and the maximum statement size is 500:

/O:T';':M500

In the next example, the statement terminator is a carriage return-linefeed character. The maximum statement size is 2500:

/O:TCR:M2500

In the final example, all options are used:

ADMNDEMO.EXE /FC:\SQLADMIN\BACKUP.SQL
   /D[DSN=myserver;UID=Smith;PWD=;]
   /O:TCR:M2000 /TC:\TMP\ADMNDEMO.LOG /XN

When Admin Demo is run, ODBC calls are logged to the C:\TMP\Admndemo.log file, and Admin Demo connects to 'myserver' as user 'Smith' with no password. Once connected, the file C:\SQLADMIN\Backup.sql is executed using a carriage return-linefeed character as a statement terminator with a maximum statement size of 2000. After the file has been executed, the application releases control to the user instead of exiting.


Admin Demo Menus

This section briefly describes the menu commands in Admin Demo. For more information about the ODBC functions that are executed by many of the menu items, see the ODBC Programmer's Reference.

File Menu

Command | Description
Connect | Calls SQLDriverConnect to connect to a data source. If a new connection is made successfully, Admin Demo opens a new Connection window and adds the Connection window name to the Window menu.
Disconnect | Disconnects from a data source. The Connection window and all Results windows for the data source are closed. The Connection window name is removed from the Window menu list.
Execute | Executes the SQL statements found in the specified file. For more information, see "SQL Batch Files" later in this guide.
Open | Opens the specified file and places its contents in the input pane.
Close | Closes the current file.
Save | Saves the contents of the input pane to the current file.
Save As | Saves the contents of the input pane to the specified file.
Exit | Discards pending result sets, closes open connections, and exits Admin Demo.

Edit Menu

Command | Description
Undo | Reverses the previous editing operation in the input pane.
Cut | Cuts the selection from the active input or output pane to the Clipboard.
Copy | Copies the selection from the active input or output pane to the Clipboard.
Paste | Pastes the clipboard contents to the current cursor location (text only).
Select All | Selects the entire contents of the current window.

Command Menu

Command | Description
Execute | Executes the selected statement in the input pane. If no statement is selected, all statements in the input pane are executed. The statement or statements are executed with SQLExecDirect. Results (if present) are retrieved with SQLFetch and SQLGetData. After a result set is fetched, SQLMoreResults is called to handle any additional result sets.
Commit | If supported, this command calls SQLEndTran with the SQL_COMMIT option for the current Connection window.
Rollback | If supported, this command calls SQLEndTran with the SQL_ROLLBACK option for the current Connection window.
Pipe Menu | For more information, see "Pipes" later in this guide.
Edit | Creates a new pipe or modifies an existing pipe.
Do | Runs a pipe against a result set.

Info Menu

Command | Description
Manage Data Sources | Calls SQLManageDataSources in the ODBC installer DLL to enable you to add, modify, and delete data sources.
Data Sources | Calls SQLDataSources to list the data sources on the current system. Although SQLDataSources requires only an environment handle, Admin Demo needs a Connection window to display the results. Therefore, this item is valid only after connecting to a data source.
Data Types | Calls SQLGetTypeInfo to produce a result set of all data types on the system.
Functions | Calls SQLGetFunctions to list the functions supported by the current driver. The results are listed in alphabetical order by ISO 92, X/Open, and ODBC conformance.
ODBC Information | Calls SQLGetInfo to list the values returned for each information type defined in Sql.h and Sqlext.h. (No driver-defined values are retrieved.)
Lists | These menu items provide a dialog box interface to the ODBC catalog functions. See the ODBC Programmer's Reference for details about each of the following functions: Tables—Calls SQLTables to produce a result set of tables. Columns—Calls SQLColumns to produce a result set of columns. Statistics—Calls SQLStatistics to produce a result set of index information. Primary Keys—Calls SQLPrimaryKeys to produce a result set of primary key information. Foreign Keys—Calls SQLForeignKeys to produce a result set of foreign key information. Table Privileges—Calls SQLTablePrivileges to produce a result set of table privilege information. Column Privileges—Calls SQLColumnPrivileges to produce a result set of column privilege information. Special Columns—Calls SQLSpecialColumns to produce a result set of column information in a specified table. Procedures—Calls SQLProcedures to produce a result set of procedures. Procedure Columns—Calls SQLProcedureColumns to produce a result set of procedure columns.

Window Menu

Command | Description
Font | Selects the default font to use for Results windows. This value is stored in Admndemo.ini for subsequent use.
Cascade | Cascades all child windows, including Connection and Results windows.
Tile | Tiles all child windows, including Connection and Results windows.
Arrange Icons | Arranges all child window icons in the frame window area.
Close All | Closes all child windows, including Connection and Results windows.
List Of Open Windows | Lists each child window using the form N:<USER ID>@<DATA SOURCE> for Connection windows and N:<USER ID>@<DATA SOURCE>:<RESULTS> for Results windows, where N is the number in the list, <USER ID> is the user name, <DATA SOURCE> is the data source name, and <RESULTS> is the Results window name.

Help Menu

Command | Description
Help | Opens the ODBC Samples help file.
About | Displays version information for Admin Demo.


Pipes

Pipes enable the results fetched from a connection to be used as the parameters for another statement. All statements are executed against the Connection window that owns the Results window. Pipes cannot span multiple connections but could be expanded to do the following:

  • Copy table contents from one Connection window to another.
  • Perform administrative tasks. For example, you could select user names from one Connection window and apply them as a parameter to a CREATE USER statement in another Connection window.
  • Support any function that requires a result set as an input parameter.

Defining Pipes

A pipe consists of an SQL statement (which may or may not take parameters), a list of the parameters to use, and a parameter option. This information is stored in the Admndemo.ini file. To create a new pipe or modify an existing pipe, choose the Edit command from the Pipe menu.

Pipe Definition Example

The following example shows a pipe (as defined in the Admndemo.ini file) that can be used in conjunction with the Tables list to drop all selected tables:

[Drop Table/View]
sql=DROP ? ?.?.?
parms=4,1,2,3
parmopt=value
delete=1

The following table describes the items in the example pipe definition.

Item | Description
sql | The SQL statement to be executed. Use a parameter marker (?) for each parameter.
parms | The list of parameters to be placed into the SQL statement. These values are 1-based and correspond to the columns in the result set produced by the function. (For example, in the result set produced by the Tables item, column 1 is Qualifier, column 2 is Owner, column 3 is Name, and column 4 is Type.)
parmopt | The parameter passing method to be used, either VALUE or ADDRESS. For VALUE, a textual substitution is performed so that the SQL statement is executed as a complete statement with no unused parameter markers. For ADDRESS, the parameter markers are left in and the statement is prepared. The parameters are defined with SQLBindParameter, and SQLExecute substitutes the values of each parameter.
delete | If this is set to 1, the row is deleted from the result set after processing the pipe. If this is set to 0, the row is not deleted.

Piping By Value Example

Using the Drop Table/View pipe as an example, assume that you have created a result set with the following values:

(column) | Qualifier | Owner | Table | Type
(data) | accounting | joe | sales | table

When you choose Do from the Pipe menu and click the Pipe button, Admin Demo loads the pipe information and processes each selected row of the result set. When the row is processed, the parameter method of 'value' indicates that each parameter marker is to be replaced before execution. The 'parms' entry indicates that the order of parameters is 4, 1, 2, 3, which, when correlated to the result set, is Type, Qualifier, Owner, and Table. Therefore, the resulting SQL statement is:

DROP table accounting.joe.sales

Because the statement is complete, it is passed directly to SQLExecDirect, which executes the statement.

Passing data by value is often required due to the SQL involved. For example, most data sources do not allow parameter markers to mark keywords, as in this example.

Piping By Address Example

Parameters can be used to execute SQL statements many times. For example, parameters are useful for copying a table from one data source to another, as in the following code:

SQLPrepare(hstmt2, "INSERT INTO SALES VALUES (?,?,?)", SQL_NTS);
SQLExecDirect(hstmt1, "SELECT * FROM SALES", SQL_NTS);

/* Bind columns returned on hstmt1 to output buffers. */
/* Bind parameters markers to the same buffers. */

rc=SQLFetch(hstmt1);
while(rc != SQL_NO_DATA) {
   SQLExecute(hstmt2);
   rc=SQLFetch(hstmt1);
}

where:

  • hstmt1 is on a connection from server1
  • hstmt2 is on a connection from server2

This example does not show calls to SQLBindCol to bind buffers to the result set columns or calls to SQLBindParameter to bind the same buffers to the parameter markers. It also does not show error checking.

Although this is not a very sophisticated piece of code, it does show the power of the ODBC API: The results from a single SELECT statement were easily inserted into a table in another data source.

Admin Demo provides this type of pipe interface by allowing the 'address' parmopt keyword value. As an example, here is a pipe called Record Select that takes all selected rows and inserts their values into another table:

[Record Select]
sql=INSERT INTO CatchSelection VALUES (?,?,?,?)
parms=1,2,3,4
parmopt=address

In this example, the SQL statement is an INSERT statement that inserts four values into the CatchSelection table. Because the parmopt keyword is 'address', Admin Demo prepares the statement and calls SQLBindParameter for each parameter marker. The parms keyword specifies that columns 1, 2, 3, and 4 of the result set (Qualifier, Owner, Name, and Type) be linked with parameters 1, 2, 3, and 4, in that order. For each row of the result set, Admin Demo then copies the row values to the parameter buffers and calls SQLExecute.

Using the result set in the previous example, the parameter data would be as shown in the following table.

(column) | Qualifier | Owner | Table | Type
(data) | accounting | joe | sales | table

Selecting the data from CatchSelection would show this row in the table when the pipe was finished running.

Limitations of Pipes

Parameter Count

Only 18 parameters are allowed per statement.

Parsing

The parsing engine of the SQL statement for the VALUE parameter option is not robust. It simply identifies the parameter marker '?' and assumes it is the next parameter to replace. This means that the statement:

SELECT '?', ? FROM bigset

where only one parameter, Owner, was selected, will result in a final statement of:

SELECT 'joe', ? FROM bigset

This is not the intended result. If your needs exceed this limitation, you should either modify Admin Demo or create your own application.


SQL Batch Files

The Execute command in the File menu enables you to execute SQL statements stored in a text file. This is useful for running scripts, such as backups. When this option is selected, the Execute File dialog box appears.

Execute File Dialog Box Options

You use the Execute File dialog box to identify SQL batch files. The options for the dialog box are as follows:

Terminator

Use this box to specify the character or characters that terminate an SQL statement. If Carriage Return is selected, the \r\n string is used. If Character is selected, the value in the edit box is used.

The following example shows the Carrtn.sql batch file, which uses carriage returns to terminate statements:

CREATE TABLE SALES (SALESNUM INTEGER, CUSTOMER CHAR(10))
INSERT INTO SALES VALUES (10, 'Harry')
INSERT INTO SALES VALUES (20, 'June')
SELECT * FROM SALES

The next example file, Sqlsrvr.sql, shows how you can run a script for SQL Server that uses 'GO' to indicate the end of a block of statements:

CREATE TABLE Sales (SalesNum integer, Customer char(10))
GO
INSERT INTO Sales VALUES (10, 'Harry')
INSERT INTO Sales VALUES (20, 'June')
GO
SELECT * FROM Sales
GO

The third example file, Oracle.sql, shows the use of semicolons (;) to terminate SQL statements:

CREATE TABLE SALES
(SALESNUM NUMBER, CUSTOMER CHAR(10));
INSERT INTO SALES VALUES (10, 'Harry');
INSERT INTO SALES VALUES (20, 'June');
SELECT * FROM SALES;

Because the terminator is never actually sent to the server, you could take a script designed for SQL Server (with the 'GO' terminator) and run it against Oracle. Only the SQL statements would be sent. Provided the SQL grammar is compatible, it will work on either platform.

Maximum Statement Length

Use this box to specify the maximum statement length to be used. The statement length must be from 100 to 9999. This value is used to allocate global memory into which the file is read. If a statement is longer than this value, Admin Demo executes only the part of the statement that it has read. This is likely to cause a syntax error. The default of 1000 should be sufficient for nearly all batch files.

Batch File Limitations

The execution method is not particularly sophisticated. Because of this, the following limitations should be kept in mind:

Extended SQL

The only SQL statements that can be executed are those that the data source can process. That is, there is no special batch control mechanism in Admin Demo. Using SQL extensions such as WHILE and IF must be limited to data sources that can interpret them. For example, you might use various conditional statements when creating a stored procedure in SQL Server or Oracle. Admin Demo makes no attempt to distinguish between them.

Maximum File Size

Because only "Max Length" bytes are read from the file at a time, there should be no real limit on the overall size of the file being executed. However, the maximum statement size can be no longer than "Max Length," which is defined to be 100 to 9999.

Terminator Parsing

The search algorithm simply fills the buffer to the specified maximum by reading sequentially from the file. A search for the terminator string starts at the beginning of the buffer. When found, it is replaced with a null terminator and all carriage returns (assuming the terminator is not a carriage return) are removed before the statement is executed. This means that carriage returns embedded in data will be stripped.

An attempt is made to skip over data strings wherever possible (with the exception of carriage returns). For example, if your terminator is the semicolon (;) character, executing the following file:

SELECT ';', Customer FROM Sales;

is identical to executing the following:

SELECT ';', Customer FROM Sales

and

SELECT 'A line
   of data' FROM Sales

is identical to:

SELECT 'A line of data' FROM Sales


Cursor Demo Sample

Cursor Demo takes advantage of the block cursor, scrolling, positioned update, and delete capabilities of the ODBC Cursor Library. For more information about the ODBC Cursor Library, see the ODBC Programmer's Reference.

Features

Scrollable Result Set Windows

This feature provides a scrolling window over a result set. Individual rows in the result set can be selected and updated if the data source supports positioned updates. Result sets can be generated either by queries or by activating the ODBC functions that return a result set, such as SQLTables.

Each Results window is an MDI child window of the Connection window in which the operation was performed. This means that disconnecting from a Connection window closes all Results windows created from that Connection window.

Statement Options Dialog Box

Optional run modes are configurable with the Stmt menu's Options command. The Options dialog box is dynamic. Selecting an option changes the dialog box configuration to match the requirements of the option.

Multithreaded Support for Microsoft Windows NT/Windows 2000

Cursor Demo supports multithreading under Windows NT/Windows 2000 when compiled with the "thread" option. For more information about compiling with the "thread" option, see "How to Build Cursor Demo" later in this guide.

Limitations

One Active Connection Window

Cursor Demo can open only single connections. If the driver has the capability, Cursor Demo will support multiple statements over the connection.

Number of Rows Fetched and Rowset Size

The maximum number of rows Cursor Demo displays is set using the Stmt menu's Options command. The Rowset Size option defaults to 10; the maximum is 65500.

Simplistic Memory Management

Cursor Demo does not handle out-of-memory conditions very gracefully. Make sure you have sufficient memory to run the program and load your data.


Cursor Demo Sample Files

Files Location

The Cursor Demo sample code resides in the \Samples\Odbc\Cursor directory. The executable file Crsrdemo.exe is located in the \Bin\Odbc directory.

Sample Code File Contents

File name | Description
Child.C | Contains routines that maintain a child window. These include most interfaces with ODBC as well as all data retrieval and display.
Child.ico | The minimized child window icon.
Crsrdemo.bat | Batch file.
Crsrdemo.c | The main module. Contains the standard functions used in a Windows-based program (such as WinMain) plus two functions shared among all the files.
Crsrdemo.Def | The module definition file Cursor Demo.
Crsrdemo.h | Contains the prototypes and definitions for Crsrdemo.c.
Crsrdemo.ico | Cursor Demo icon.
Crsrdemo.mak | Builds the ODBC Cursor Demo application.
Crsrdemo.rc | Resource file for Cursor Demo.
Dialogs.c | Displays and manages Cursor Demo dialog boxes.
Frame.c | Manages the main frame window. The frame window owns the MDI client window, the menu bar, and other major parts of the interface.
Headers.c | Implements precompiled headers.
Headers.h | The precompiled-headers file.
Resource.h | Contains definitions used by Crsrdemo.rc.


How to Build Cursor Demo

The file \Samples\Odbc\Cursor\Crsrdemo.mak is set up for compilation in 32-bit environments using a Microsoft or Microsoft-compatible C compiler. Use the \Samples\Odbc\Cursor\Crsrdemo.bat file to set up the appropriate environment and invoke the appropriate compiler and linker.

Note The Crsrdemo.mak file assumes that you have installed the Platform SDK. If you are using Visual C++ 1.0 or later on Windows NT/Windows 2000, Cursor Demo will not compile with the provided Crsrdemo.mak. Instead, you must use the makefile generated by Visual C++.

To build the sample, change to the \Samples\Odbc\Cursor directory, and then perform the following steps:

  • To build the 32-bit release version in the \Samples\Odbc\Cursor\Ndebug32 directory, type:
    CRSRDEMO NDEBUG32
  • To build the 32-bit debug version in the \Samples\Odbc\Cursor\Debug32 directory, type:
    CRSRDEMO DEBUG32

Unrecognized arguments to Crsrdemo.bat will halt compilation with a warning.

Crsrdemo.bat calls NMAKE, the Microsoft Program Maintenance Utility provided with Microsoft C version 7.0 or later, on the file Crsrdemo.mak to rebuild the Crsrdemo.exe file. If Crsrdemo.mak is unmodified, Crsrdemo.bat will rebuild the original Crsrdemo.exe sample.

For information about using NMAKE, see your C compiler tools documentation.

Compiling a Multithreaded Cursor Demo

An optional build switch, "thread", compiles a version of Cursor Demo that is multithreaded under Windows NT/Windows 2000. For example, to compile a 32-bit debug version in the \Samples\Odbc\Cursor\Debug32 directory, type:

CRSRDEMO DEBUG32 THREAD

Only 32-bit builds (debug32 or ndebug32) can specify the "thread" switch. The default is the "nothread" option.

Note Perform a clean build before switching from a "thread" to a "nothread" build (or vice versa) in the same directory (DEBUG32 or NDEBUG32); that is, delete all .obj files before beginning the next build.


How to Run Cursor Demo

You can run the Cursor Demo sample by double-clicking the Crsrdemo.exe file in Windows Explorer, or by clicking Start, and then Run, and typing \<path>\Crsrdemo.exe.


Cursor Demo Menus

This section briefly describes the menu commands in Cursor Demo. For more information about the ODBC functions that are executed by many of the menu items, see the ODBC Programmer's Reference.

STMT Menu

Command | Description
New | Opens a new statement handle on the current connection.
Connect | Closes the current data source connection and connects to a new data source.
Disconnect | Disconnects from the current data source.
Sql | Opens the Statement dialog box, which enables you to execute an SQL statement.
Table Information Opens the Table Information dialog box, which enables you to query various types of information from the database. It contains the following items:

Tables—Calls SQLTables with the specified arguments.

Columns—Calls SQLColumns to return column information.

Procedures—Calls SQLProcedures for procedure information.

Statistics—Calls SQLStatistics to retrieve index information.

Privileges—Calls SQLTablePrivileges to retrieve privilege information.

Qualifier, Owner, Name, and Type—These edit boxes specify arguments for the ODBC functions. For information about ODBC functions, see the ODBC Programmer's Reference.

Type Information—Use this box to specify calls to SQLGetTypeInfo to return information about the data types supported by the data source.

Options—Opens the Options dialog box, which enables you to set Cursor Demo statement options. The Options dialog box contains the following:

  • Options List Box—Contains the statement attributes for Bind, Concurrency, General, and Scrolling. It enables you to specify values for the selected type.
  • Options Group—Available options for the selected type are displayed in this group. See the descriptions for the following statement options.
    • Bind options. This attribute uses SQLSetStmtAttr with an Attribute value of SQL_ATTR_BIND_TYPE. It enables you to set binding by row, single column, all columns, or a specified row or column number.
    • Concurrency options. This option uses SQLSetStmtAttr with an Attribute value of SQL_ATTR_CONCURRENCY to specify when fetched data can be updated, and a ValuePtr value as follows:

      Read-Only
      Fetched data cannot be updated (uses SQL_CONCUR_READ_ONLY).

      Optimistic by Timestamp
      Fetches will not be updated if the timestamp differs from the original (uses SQL_CONCUR_ROWVER).

      Optimistic by Values
      Fetches will not be updated if the values differ from the original (uses SQL_CONCUR_VALUES).
    • General options. These options appear when General is selected in the Options list box. They include:

      Asynchronous Processing
      This option uses SQLSetStmtAttr with an Attribute value of SQL_ATTR_ASYNC_ENABLE to enable Cursor Demo to perform asynchronous processing.

      Always Fetch First
      This option instructs Cursor Demo to always do SQLExtendedFetch with a FetchOrientation value of SQL_FETCH_FIRST after the successful execution of an SQL statement. If this option is not set, no rows will appear in the result set window until you select one of the commands in the Fetch menu.

      Max column width
      This option sets the maximum length of data that will be retrieved for any column. The maximum limit of this parameter is set in the MAX_MAXBIND definition of the Crsrdemo.h file. Because Cursor Demo allocates space for each column in the rowset, it can run out of memory if too many large columns are bound. Make this value smaller if you run out of memory, or increase it if you want to see more data in large columns. It uses SQLSetStmtAttr with an Attribute of SQL_ATTR_MAX_LENGTH.
    • Scrolling options. These options use SQLSetStmtAttr with an Attribute value of SQL_ATTR_CURSOR_TYPE to specify permissible movement of the cursor, and a ValuePtr value as follows:

      Forward only
      The cursor will scroll in the forward direction only (uses SQL_CURSOR_FORWARD_ONLY).

      Static (scrollable snapshot)
      The cursor will scroll in any direction, but the data will not be updated (uses SQL_CURSOR_STATIC).

      Keyset driven
      The cursor is fully keyset-driven (uses SQL_CURSOR_KEYSET_DRIVEN).

      Dynamic
      The driver saves and uses the keys for only the rows in the rowset (uses SQL_CURSOR_DYNAMIC).

      Rowset size
      Defines the number of rows in your rowset.
Close | Closes the current statement.
Close All | Closes all open statements.
Exit | Exits Cursor Demo.

Fetch Menu

All the Fetch menu items use SQLExtendedFetch with different values for the FetchOrientation argument. For information about SQLExtendedFetch, see the ODBC Programmer's Reference.

Command | Description
First | Uses SQLExtendedFetch with a FetchOrientation value of SQL_FETCH_FIRST. (Fetches the first rowset in the result set.)
Prev | Uses SQLExtendedFetch with a FetchOrientation value of SQL_FETCH_PRIOR. (Fetches the previous rowset in the result set.)
Next | Uses SQLExtendedFetch with a FetchOrientation value of SQL_FETCH_NEXT. (Fetches the next rowset in the result set.)
Last | Uses SQLExtendedFetch using a FetchOrientation value of SQL_FETCH_LAST. (Fetches the last rowset in the result set.)
Absolute | Uses SQLExtendedFetch with a FetchOrientation value of SQL_FETCH_ABSOLUTE. (Fetches the rowset starting at the given absolute row number.)
Relative | Uses SQLExtendedFetch with a FetchOrientation value of SQL_FETCH_RELATIVE. (Fetches the rowset starting at the given relative row number.)
Delete Row | Deletes the selected row.
Update Row | Opens the Update Row dialog box, which enables you to change data in the selected row.

Window Menu

Command | Description
Cascade | Cascades all open windows.
Tile Horizontal | Tiles all open windows horizontally.
Tile Vertical | Tiles all open windows vertically.
Arrange Icons | Lines up all minimized windows along the bottom of the Cursor Demo workspace.
List Of Open Windows | Lists all open windows along with the name of the cursor they represent.

Help Menu

Command | Description
Help | Opens the ODBC Samples help file.
About | Displays version information for Cursor Demo.


How to Use Cursor Demo

To connect to a data source, choose the Connect command from the Stmt menu. A list of data sources appears; select one, and then follow the connection procedures for that data source.

Once you have connected to a data source, an empty result set window appears. To retrieve a result set, either enter a query (using the Stmt menu's SQL command) or select one of the ODBC catalog functions (for example, the Stmt menu's Table Information command). If the query or catalog request executes successfully, a result set appears in the window.

Cursor Demo enables you to perform Cursor Library operations. You can select rows by clicking. To delete a selected row, use the Delete Row command in the Fetch menu. To update the values in a selected row, use the Update Row command in the Fetch menu. For information about the operation of the Cursor Library, see the Microsoft ODBC Programmer's Reference.


Query Demo Sample

Query Demo demonstrates multiple simultaneous connections using ODBC. It enables you to type any SQL query and browse the results.

Features

Connection Windows

Query Demo enables you to connect to multiple data sources. The only limits on connections are those imposed by each driver and the amount of memory available on the system. Each new connection creates a multiple-document interface (MDI) child Connection window from which SQL statements can be executed.

Statement Windows

Query Demo enables you to open multiple Statement windows on a connection. The only limits on statements are those imposed by each driver and the amount of memory available on the system.

Limitations

Maximum Number of Windows: 10

You can open a maximum of 10 Connection and Statement windows.

Non-SELECT Statements

For non-SELECT statements, application behavior is undefined.

Other Limitations

  • Maximum displayed columns: 15
  • Maximum displayed rows: 100


Query Demo Sample Files

Location

The Query Demo sample code resides in the \Samples\Odbc\Query directory. The executable file Qurydemo.exe resides in the \Bin\Odbc directory.

Sample Code File Contents

File name | Description
Main.c | Contains the main application window procedures. It demonstrates usage of the MDI windows, toolbar, and status bar.
Newquery.bmp | Contains the toolbar button bitmap used to open a new Statement window.
Query.c | Contains the code that uses the ODBC API. It is used for all data access operations (for example, connecting to a data source, executing and displaying query results, and so on).
Querywdw.ico | Contains the minimized Connection/Statement window icon.
Qurydemo.def | Contains the module definition file for Query Demo.
Qurydemo.h | Contains the prototypes and definitions for Qurydemo.c.
Qurydemo.ico | Contains the Query Demo icon.
Qurydemo.rc | Resource file for Query Demo.
Qurymak.mak | Builds the ODBC Query Demo application.
Runquery.bmp | Contains the toolbar button bitmap used to execute an SQL command.


How to Build Query Demo

The file \Samples\Odbc\Query\Qurydemo.mak is set up for compilation within 32-bit environments using a Microsoft or Microsoft-compatible C compiler. You use the \Samples\Odbc\Query\Qurydemo.bat file to set up the proper environment and invoke the appropriate compiler and linker.

Note The Querydemo.mak file assumes that you have installed the Platform SDK. If you are using Visual C++ 1.0 or later on Windows NT/Windows 2000, Query Demo will not compile with the provided Querydemo.mak. Instead, you must use the makefile generated by Visual C++.

To build the sample, change to the \Samples\Odbc\Query directory, and then perform the following steps:

  • To build the 32-bit release version in the \Samples\Odbc\Query\Ndebug32 directory, type:
    QURYDEMO NDEBUG32
  • To build the 32-bit debug version in the \Samples\Odbc\Query\Debug32 directory, type:
    QURYDEMO DEBUG32

Unrecognized arguments to Qurydemo.bat will halt compilation with a warning.

Qurydemo.bat calls NMAKE, the Microsoft Program Maintenance Utility provided with Microsoft C version 7.0 or later, on the file Qurydemo.mak to rebuild the Qurydemo.exe file. If Qurydemo.mak is unmodified, Build.bat will rebuild the original Qurydemo.exe sample.

For information about using NMAKE, see your C compiler tools documentation.


How to Run Query Demo

You can run the Query Demo sample by double-clicking the Qurydemo.exe file in Windows Explorer, or by clicking Start, and then Run, and typing \<path>\Qurydemo.exe.


Query Demo Menus and Toolbar

This section briefly describes the menu and toolbar commands in Query Demo. For more information about the ODBC functions that are executed by many of the menu items, see the Microsoft ODBC Programmer's Reference.

Login Menu

Command | Description
Connect | Calls SQLConnect to connect to a data source and provides a dialog box for entering connection information.
Driver Connect | Calls SQLDriverConnect to connect to a driver and a data source.
Disconnect | Disconnects from statement and connection handles.
Exit | Exits Query Demo.

Query Menu

Command | Description
Execute SQL | Executes the SQL query you typed in the Statement window.
New Query | Opens a new Statement window in which you can type an SQL query.

Window Menu

Command | Description
Tile | Tiles all child windows, including Connection and Statement windows.
Cascade | Cascades all windows in the Query Demo workspace.
Arrange Icons | Arranges all minimized components along the bottom of the Query Demo workspace.
Close All | Closes all open Connection and Statement windows.

Help Menu

Command | Description
Help | Opens the ODBC Samples help file.
About | Displays version information for Query Demo.

Toolbar

The toolbar contains:

  • A Connection window list box, listing connected data sources.
  • A Statement window list box, listing open statement handles.
  • A button to open a new Statement window (same as New Query in the Query menu).
  • A button to execute SQL statements (same as Execute SQL in the Query menu).


Making a Query with Query Demo

To make a query using Query Demo

  1. From the Login menu, choose either Connect or Driver Connect to connect to a data source. If connection is successful, a connection window opens. The title bar displays the values for DSN, HDBC, and HSTMT. The SQL> box enables you to type in SQL statements.
  2. Type a valid SQL statement in the SQL> box. From the Query menu, choose Execute SQL or use the corresponding toolbar button. The SQL statement is executed. If successful, the result set for the query will appear. You can scroll through the result set using the mouse or the keyboard. (You must click in the result set window before you can use the keyboard to scroll the result set.)
  3. If you want to open more than one Statement window, choose New Query from the Query menu to open a new query window or use the corresponding toolbar button.
  4. To free statement handles and disconnect from the connected data sources, choose Disconnect from the Login menu. The Disconnect From Data Source dialog box appears.
  5. Select all data sources you have connected to, and click the Disconnect button.
  6. To exit Query Demo, choose Exit from the Login menu. Query Demo closes all open connections before exiting.


Convert DSN Demo Sample

The Convert DSN Demo uses the ODBC API to enable you to convert User and System DSNs to File DSNs. Convert DSN creates File DSN pointers to machine DSNs by iterating through all User and System DSNs in the Windows registry and creating a pointer to each.

Note Convert DSN Demo is provided as a sample.

Features

The Convert DSN Demo iterates through all User and System DSNs and makes a version of the data source name acceptable for use as a file name. Then the Convert DSN Demo expands the name into a full, valid file name and writes the "DSN=" keyword into the File DSN.

Limitations

Convert DSN Demo does not log the progress of the conversion nor does it indicate when the conversion is complete.


Convert DSN Demo Sample Files

Location

The Convert DSN sample code resides in the \Samples\Odbc\Convdsn directory.

Sample Code File Contents

File name | Description
Convdsn.c | Contains the main application functions. It demonstrates the conversion process by iterating through all User and System DSNs and making a version of the data source name acceptable for use as a file name.
Cdsnbld.mak | Builds the ODBC Convert DSN Demo application.
Cdsnbld.bat | A batch file that sets up the appropriate environment and invokes the appropriate compiler and linker for building the Convert DSN Demo application.


How to Build Convert DSN Demo

The file \Samples\Odbc\Convdsn\Cdsnbld.mak is set up for compilation in the 32-bit environment using a Microsoft or Microsoft-compatible C compiler. You use the \Samples\Odbc\Convdsn\Cdsnbld.bat file to set up the appropriate environment and invoke the appropriate compiler and linker.

Note The Cdsnbld.mak file assumes that you have installed the Platform SDK. If you are using Visual C++ 1.0 or later on Windows NT/Windows 2000, Convert DSN Demo will not compile with the provided Cdsnbld.mak. Instead, you must use the makefile generated by Visual C++.

To build the sample, change to the directory where the batch file exists, and then perform the following steps:

  • To build the release version in the \Ndebug32 directory, type:
    CDSNBLD NDEBUG32
  • To build the debug version in the \Debug32 directory, type:
    CDSNBLD DEBUG32

Unrecognized arguments to Cdsnbld.bat will halt compilation with a warning.

Cdsnbld.bat calls NMAKE, the Microsoft Program Maintenance Utility provided with Microsoft C version 7.0 or later, on the file Cdsnbld.mak to rebuild the Convdsn.exe file. If Cdsnbld.mak is unmodified, Cdsnbld.bat will rebuild the original Convdsn.exe sample.

For information about using NMAKE, see your C compiler tools documentation.


How to Run Convert DSN Demo

You can run the Convert DSN Demo sample by building the debug or nondebug version and double-clicking the \Samples\Odbc\Convdsn\Ndebug32\Convdsn.exe or \Samples\Odbc\Convdsn\Debug32\Convdsn.exe file in Windows Explorer, or by clicking Start, and then Run, and typing \<path>\Convdsn.exe.

ODBC Sample Trace DLL

The ODBC 3.51 Driver Manager has an improved tracing mechanism that allows developers to implement their own trace DLLs. These can be used to provide more information than the standard driver manager trace DLL (Odbctrac.dll).

The standard trace DLL is a part of the ODBC 3.51 redistributable components, and ODBC vendors are required to ship this DLL unmodified from the original source whenever they ship ODBC core components. However, it is possible to also develop and ship a custom trace DLL to provide more functionality than the standard one.

How to Use the Alternative Trace DLL

To get the driver manager to use an alternative trace DLL, use the ODBC Administrator to set the trace DLL path and name.

This will cause the driver manager to use your custom trace DLL. If this key is not defined, the driver manager uses the standard trace DLL (Odbctrac.dll).

A trace DLL must export an entry point named TraceSQLxxxx for each ODBC 3.5 function, where xxxx is the name of the function (TraceSQLConnect, for example). This trace function will be called upon entry to the ODBC function. Trace functions take as arguments the standard arguments for the ODBC function, but they return a "magic cookie" that the driver manager will pass back to the trace DLL when the function exits.

In addition to the TraceSQLxxxx functions, a trace DLL must export the following:

  • TraceReturn, which logs the return code of a function.
  • TraceOpenLogFile, which opens a log file.
  • TraceCloseLogFile, which closes a log file.
  • TraceVersion, which passes back the TRACE_VERSION constant.

The prototypes and definitions of these functions are in DoTrace.c.

Note For security purposes, the trace DLL must be installed in the Windows system (or System32) directory. The driver manager will refuse to use a trace DLL from any other directory. This prevents users from installing the trace DLL on locked-down systems.

To use this sample trace DLL, copy STRACE.DLL to the System directory and use the ODBC Administrator (Odbcad32) to select Strace.dll as your custom DLL.

ODBC Connection Pooling Performance Monitoring

The batch file in this directory (Odbcpm.bat) will enable performance monitoring counters for ODBC connection pooling. Connection pooling allows one application to reuse open connection handles, which saves round-trips to the server. When the application calls SQLDisconnect with a connected handle, the driver manager adds the connection to a list of free connections for that driver or environment. When SQLDriverConnect or SQLConnect is called with a connection string and connection options that are compatible with the pooled connection, the driver manager will return this pooled connection to the application.

If no compatible requests are made within the timeout period specified for that driver, the pooled connection will actually be disconnected from the driver. The timeout period for the driver is specified in the registry under HKEY_LOCAL_MACHINE\SOFTWARE\Odbc\Odbcinst.ini\DriverName\CPTimeout.

Connection pooling is typically used for Web server applications, which tend to create many short-lived connections.

How to Enable/Disable Performance Monitoring

To enable performance monitoring, run odbcpm enable. To disable monitoring, run odbcpm disable.

To see the counters, run the perfmon program. Select the ODBC Connection Pooling object.

For more information, see SQL_ATTR_CONNECTION_POOLING under SQLSetEnvAttr in this guide.


Microsoft Data Access Technical Articles Overview

The Microsoft Data Access Components (MDAC) SDK provides technical articles to address cross-technology and comprehensive overview topics, and to bring you late-breaking information on MDAC SDK component technologies.

Many of the articles for the MDAC SDK have been updated, moved, or incorporated into the OLE DB or ADO documentation. If you can't find an article or information you need, see the Article Finder for its new whereabouts.

To see other articles not listed here, plus the latest information about the MDAC SDK, events, patches, and links to related material, see the Microsoft Data Access Web site.

For information on support for Microsoft Data Access Components, see Getting Help from Microsoft Product Support Services.

Introduction to MDAC

  • MDAC Technology Features
  • Choosing Your Data Access Strategy
  • Pooling in the Microsoft Data Access Components
  • Improving MDAC Application Performance

ADO

  • Migrating from DAO to ADO
  • ADO and RDS Security Issues in Microsoft Internet Explorer
  • Mapping ADO Methods to OLE DB Interfaces
  • Mapping ADOX Properties and Methods to OLE DB Interfaces
  • Using the Customization Handler Feature in RDS 2.1
  • Saving ADO Recordsets in XML Format

OLE DB

  • OLE DB Security
  • Optimistic Remote Updating in the Client Cursor Engine
  • OLE DB for the ODBC Programmer
  • OLE DB Minimum Levels of Consumer and Provider Functionality (formerly titled "OLE DB Leveling")
  • Mapping ADO Methods to OLE DB Interfaces
  • Microsoft OLE DB Provider for Oracle: Tips, Tricks, and Traps

ODBC

  • ODBC Security

Copyright

The information contained in these technical articles represents the current view of Microsoft Corporation on the issues discussed as of the date of publication. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information presented after the date of publication.

These documents are for information purposes. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, IN THESE DOCUMENTS.

The names of companies, products, people, characters, and/or data mentioned herein are fictitious and are in no way intended to represent any real individual, company, product, or event, unless otherwise noted.

© 1991-2000 Microsoft Corporation. All rights reserved.


MDAC Article Finder

MDAC Technical Articles

Article Finder

Technical articles in the MDAC SDK have been updated and edited to bring information up to date and present information more clearly. If you are looking for articles you have seen in the previous release, this document will help you locate the articles or find equivalent information.

Previously available articles | Current location of information
Accessing Data from Multiple Sources with UDA Technologies

OLE DB/ADO: Making Universal Data Access a Reality

Data Access Technologies

Microsoft Strategy for Universal Data Access

What's New (and Improved) in Microsoft Data Access 2.0

What's New in ADO 2.0

Data Access for the Masses Through OLE DB

 You can find information previously presented in these articles in more up-to-date and accessible form in the following four introductory articles:
  • What Are the MDAC Technologies?
  • MDAC Technology Features
  • Choosing Your Data Access Strategy
Redistributing Microsoft Data Access Components | If you are working with previous redistributable versions of MDAC, contact Microsoft Product Support Services for help. See the Fullredist.txt or Mdaceula.txt Help file for information on redistribution and on the End User License Agreement, which covers user obligations. For information about specific release contents, see the MDAC Release Manifests on the Microsoft Data Access Web site.
Implementing ADO with Various Development Languages: The ADO Rosetta Stone | Information previously presented in this document is now available in Programming with ADO in the ADO documentation in the MDAC SDK.
Windows Foundation Classes for Java and ADO/WFC | The information covered in this article is now incorporated in ADO/WFC Programming in the ADO documentation in the MDAC SDK.
What Are the Objects in the ADO Object Model? | The information covered in this article is now presented more completely and in context in ADO Object Model in the ADO documentation in the MDAC SDK.
How Do I Use the Connection Object? | The information covered in this article is now presented more completely and in context in Connection Object in the ADO documentation in the MDAC SDK.
Extracting Error Information from ADO in Visual Basic | Information in this article was obsolete or more completely covered in Error Object in the ADO documentation in the MDAC SDK.
Using Microsoft ActiveX Data Objects (ADO) via Visual Basic | Information in this article was obsolete or more completely covered in Using ADO with Microsoft Visual Basic in the ADO documentation in the MDAC SDK.
ADO and SQL Server Developer's Guide | Information in this article was obsolete. For a more complete and up-to-date presentation, see the SQL Server documentation. In the MSDN Library, click Data Access Services and open Microsoft SQL Server Programmer's Toolkit. Under Building SQL Server Applications, see the sections under ADO and SQL Server.
Understanding ODBC and OLE DB Information in this article was obsolete or more completely covered in the OLE DB documentation in the MDAC SDK or elsewhere in the introductory articles in the MDAC Technical Articles:
  • What Are the MDAC Technologies?
  • MDAC Technology Features
  • Choosing Your Data Access Strategy
Understanding ODBC Security Issues

Security Windows NT 4.0 to Prevent ODBC Tracing

 The information in these two articles has been combined into one article, ODBC Security.
Universal Data Access Q&A

OLE DB for OLAP Frequently Asked Questions

 These and other FAQs are being refreshed and updated. See the Microsoft Data Access Web site for the most current introductory information and workarounds.
Conformance Testing in OLE DB | The information in this article has been integrated into the OLE DB documentation. See Conformance Testing in OLE DB for more information.
OLE DB Leveling | This article has been retitled "OLE DB Minimum Levels of Consumer and Provider Functionality" and can be found in the MDAC Technical Articles section.
OLE DB Sample Provider | This OLE DB sample provider documentation is now located in the OLE DB Samples section.


MDAC General Technical Articles

  • MDAC Technology Features
  • Choosing Your Data Access Strategy
  • Pooling in the Microsoft Data Access Components
  • Improving MDAC Application Performance


MDAC Technology Features

This article describes many of the useful features available in the Microsoft® Data Access Components (MDAC) SDK, focusing on what you, the developer, can accomplish with them.

The following topics are included:

  • Managing Data describes how you can access data where it's stored and achieve data source neutrality.
  • Developer Advantages covers extensibility, scalability, optimizations, ease of use, and asynchronous support features. Many of these features are especially relevant to ADO users.
  • Service Providers describes features available in the Service Component Manager, Data Shaping Service, Cursor Service, Synchronization Service, Remoting Provider, and Persistence Provider.
  • XML briefly describes how XML can be used to stream and persist rowsets.
  • Other Features contains information about session pooling, automatic transaction enlistment, the connection information user interface, data conversion, and support for Microsoft Visual Studio® Analyzer.


Managing Data

The Universal Data Access strategy provides two very significant benefits:

  • You can access data exactly as it is stored, in various forms and locations throughout your enterprise, without conversion or replication.
  • The provider neutrality of the MDAC technologies enables you to choose from many possible data providers, rather than limiting you to just one.

Access Data Where It's Stored

Modern data-intensive applications require the integration of information stored not only in traditional database management systems but also in file systems, indexed-sequential files, desktop databases, spreadsheets, project management tools, electronic mail, directory services, multimedia data stores, spatial data stores, and more. Nevertheless, many database companies are still pursuing a traditional database-centric approach. In this outdated approach, the database vendor extends the database engine and programming interface to support new data types, including text, spatial, video, and audio. The vendor requires customers to move all the data needed by the application, which can be distributed in diverse sources across a corporation, into the vendor's database system. This process can be expensive and wasteful.

Universal Data Access (UDA) is Microsoft's effective alternative. The premise of Universal Data Access is to allow applications to efficiently access data where it resides, without replication, transformation, or conversion. Open interfaces allow connectivity among all data sources. Moreover, independent services provide for distributed queries, caching, batch updating, data marshaling, distributed transactions, and content indexing among sources. And if necessary, UDA can be implemented in a complementary fashion with the database-centric approach.

Provider Neutrality

The UDA goal of provider neutrality is realized through the core MDAC technologies: OLE DB, ADO, and ODBC. OLE DB, in particular, allows access to both relational and nonrelational data stores and does not implement its features specific to any one provider or data store. Also, OLE DB can access data in whatever format it presently exists, wherever it exists. In turn, ADO can access data from any OLE DB source, and like OLE DB, ADO is provider neutral. ODBC continues to be a staple to MDAC, providing continued access to scores of existing relational data stores. The OLE DB Provider for ODBC allows newer applications to use OLE DB to access data stores that have ODBC drivers but not OLE DB providers.


Developer Advantages

MDAC performance has steadily improved over successive releases, especially for multitier applications. For example, for providers that support it, the OLE DB service components now provide session pooling. MDAC continues with each release to scale better to multiple processors. Another example of the advantages gained using MDAC in your development work is free threading, which allows you to build very efficient and scalable Web server applications. This means, for example, that ADO can be used by multiple clients—each with its own thread—and the threads won't interfere with one other.

The following sections discuss how MDAC technologies can make your development work easier and more effective:

  • Extensibility with ADO
  • Optimizations
  • Simplified Programming with ADO
  • Asynchronous Support in ADO


Extensibility with ADO

ADO can dynamically expose properties specific to a data provider via collections of provider properties. In addition, ADO provides type extensibility by allowing access to COM objects exposed as column values. For example, a Recordset object can be exposed as a value of a field in a larger, parent Recordset, which simplifies using hierarchical data. ADO does this without breaking existing applications because it does not alter its Automation interface to provide these dynamic properties. This makes it very easy for ADO to adapt and grow with future data providers.


Optimizations

The following features are designed to improve upon the performance and efficiency of earlier versions of MDAC.

  • Executing commands in ADO without returned recordsets
  • Dual interface support in ADO
  • ADO native language bindings
  • ODBC performance monitors

Executing Commands in ADO Without Returned Recordsets

ADO includes options to optimize how commands are executed. To remove the overhead of asking for a rowset on non–row returning commands, you can specify the adExecuteNoRecords option when executing a command. The adCmdTableDirect option forces ADO to use IOpenRowset rather than attempting to open the rowset by building and executing a Command object. Depending on the environment, activating this feature can improve performance and reduce memory overhead for non–row returning commands.

To turn on this feature, add the value of adExecuteNoRecords to the value being passed to the Options parameter, as shown in the following example. (adExecuteNoRecords works only with adCmdStoredProc and adCmdText command types. The other types, by definition, should return results.)

Sub NoRecords()
   Dim conn As ADODB.Connection
   Dim cmd As ADODB.Command
   Set conn = New ADODB.Connection
   Set cmd = New ADODB.Command

   conn.ConnectionString = _
      "Provider=SQLOLEDB;Data Source=sql70server;" _
      & "User ID=sa;Password=''" ' ;Initial Catalog=pubs
   conn.Open

   'Use from the Connection object Execute method
   conn.Execute "drop table testtable", , adCmdText + adExecuteNoRecords
   conn.Execute "create table testtable (dbkey int primary key, " & _
      "field1 char(10))", , adCmdText + adExecuteNoRecords

   'Or from a Command object
   Set cmd.ActiveConnection = conn
   cmd.CommandText = "insert into testtable values (?, ?)"
   cmd.Parameters.Refresh
   cmd.Prepared = True

   For ii = 1 To 1000
      cmd.Execute , Array(ii, "string" & ii), _
         adCmdText + adExecuteNoRecords
   Next

   conn.Close
End Sub

Dual Interface Support in ADO

Dual interface support means that ADO supports an IDispatch interface as well as a VTBL interface, so applications written in Microsoft Visual Basic®, Microsoft Visual C++®, and Java can bind directly to the VTBL interface and get fast performance. Scripting languages can use the same objects through IDispatch.

ADO Native Language Bindings

Native language bindings, using extensions, are performance enhancements at the ADO level. The C++ bindings enable developers to bind directly to C data types without overhead for describing metadata or performing VARIANT conversions. Similarly, Java developers can use WFC to bind directly to native Java data types.

For C++, a set of interfaces that directly retrieve and set native C types from the underlying OLE DB provider both improves performance and decreases the lines of code required to interact with the ADO interface.

For Java, ADO extensions include a set of Java classes that extend the ADO interfaces and create notifications interesting to the Java programmer. These extensions expose functions that return Java types. In addition, the extensions are optimized to improve performance. The Java class directly accesses the native data types in the OLE DB rowset object and returns them as Java types without first converting them to or from a Variant.

ODBC Performance Monitors

The ODBC Driver Manager includes performance monitors for accessing ODBC data, and ODBC connection pooling and other options are configurable from the Microsoft® Windows® Control Panel. These monitors help you tune your application to provide the best performance.


Simplified Programming with ADO

Several features in ADO are designed to make your job easier. Just consider that the data access task analogous to "Hello World" now requires only three lines of code!

Commands as Methods

If a command is associated with a connection, the connection automatically acquires a method with the same name. This makes it easier to call stored procedures and queries and to pass parameters to them.

Programming Language Neutral

ADO is an Automation server, so it can be used from languages such as Microsoft® Visual Basic®, Microsoft Visual J++®, Microsoft Visual C++®, VBScript, and JScript®.

Automation defines Variants as its data types, and Variants work well in languages such as Visual Basic, which are not strongly typed. However, if you are writing in C++ or in Java, most operations in the code will require native data types to support the standard library features of the language. In these cases, ADO allows you to convert data between the Variant and native data types when passing arguments. Either way, because ADO is based on the COM Automation model, nearly every modern programming language can use ADO without any special conversion or translation libraries.

No Loss of OLE DB Functionality

ADO allows C++ programmers access to the underlying OLE DB interfaces. This means Visual C++ programmers get the best of both worlds. They can use ADO to easily perform the most common tasks and then access the underlying OLE DB interfaces when they need extra performance or more control than ADO provides.


Asynchronous Support in ADO

The following topics provide a detailed discussion of the asynchronous support features in ADO. These features allow you to write applications that are more responsive to your users' needs.

  • Event Handling for Asynchronous Operations
  • Asynchronously Connecting to a Data Source
  • Asynchronous Queries
  • Asynchronously Executing a Command
  • Asynchronously Fetching Results


Event Handling for Asynchronous Operations

Generally, in an asynchronous operation, a notification to the application signals that the operation is about to be asynchronously started. Later the application is notified that the operation is completed. In other words, a notification, is a signal to the application that some event has occurred, and therefore developers must write event handlers to process the event. In ADO, the names given to the events follow a pattern: Events that happen before an operation are named "Willxxx," and events that happen after are named "xxxComplete."

All notifications are exposed on the Connection and Recordset objects. The Connection object notifications deal with connecting to the data store, executing commands, and processing transactions. The Recordset notifications are related to fetching of result sets, scrolling, changing, and updating.

ADO exposes a list of events that can be used to control all aspects of the connection, query, and row-access process. This provides finer control over all aspects of the data access operation and eliminates the need to poll while waiting for completion of asynchronous operations. In addition, this feature enhances debugging with the Visual Studio Enterprise Debugger.


Asynchronously Connecting to a Data Source

You specify the start of an asynchronous connection by using the Options parameter to the Connection object's Open method. ADO implements the asynchronous connection operation internally. It does not rely on support from the OLE DB provider being used. Therefore, you should always be able to asynchronously connect.

Asynchronous connection can be done only from the Connection object. ADO allows the user to connect to a data source directly from the Command or Recordset object, but these objects cannot be used to asynchronously connect.

The State property, exposed on the ADO objects, supports asynchronous operations. This property can be used to determine the state of an object—that is, whether it is closed or open (adStateClosed, adStateOpen), or what asynchronous operation is occurring (adStateConnecting, adStateExecuting, adStateFetching).

The State property is most often useful in an environment in which events are not supported: It can be used in a loop to determine when an operation has completed. If an error occurs during the asynchronous operation, testing the State property may generate an error. The State property was implemented in this way so that, in environments without event support, the user can be notified in some way that an error occurred during the asynchronous operation.

In the following Visual Basic example, there is no code to determine when the connection operation has completed; there is only code to determine that the connection operation is in progress. Most applications require code to determine when the connection operation is complete because most operations on the Connection object fail until the operation successfully connects to the server.

Dim conn As ADODB.Connection
Set conn = New ADODB.Connection

Conn.ConnectionString = _
"Provider=SQLOLEDB;Data Source=sql70server;" _
   & "User ID=sa;Password='';Initial Catalog=pubs"
conn.Open , , , adAsyncConnect

If conn.State = adStateConnecting Then
   Debug.Print "Still Connecting"
   'If the next line attempted an operation on this connection object,
   'it will raise an error
End If

The following example shows how to set up an event that will be fired when the asynchronous connection operation has completed. The code assumes that a CommandButton and a ListBox control are on the form.

Private WithEvents cn As ADODB.Connection

Private Sub Command1_Click()
   List1.AddItem "Not connected"
   Set cn = New ADODB.Connection

   cn.Provider = "SQLOLEDB"
   cn.ConnectionString = "Data Source=sql70server;User Id=sa;" & _
      Password='';Initial Catalog=Northwind"
   cn.Open , , , adAsyncConnect
   List1.AddItem "Starting connection"

End Sub

Private Sub cn_ConnectComplete(ByVal pError As ADODB.Error, adStatus _
   As ADODB.EventStatusEnum, ByVal pConnection As ADODB.Connection)
   'It's safe to perform an operation on the connection object
   List1.AddItem "ConnectComplete event"
End Sub


Asynchronous Queries

Another operation that can take some time to process is a query that retrieves results to the client. Running a query asynchronously allows control to be returned to the application before the query is completed. Implementation of this feature enables the user to continue or cancel a query that takes a long time to run. With this ability, during a long operation, developers can also do other operations—for example, display a progress dialog box or continue painting the form, or even start other asynchronous operations.

Asynchronous operations can also be canceled. A query that takes too long can be stopped and restarted with a smaller result, just as a connection that takes too long can be canceled and another server tried.

The following code example shows how to start an asynchronous query. It does not show how to use the Cancel method to cancel a query that has not yet completed.

Private WithEvents rst As ADODB.Recordset

Private Sub Command1_Click()
   Dim strCnn As String
   strCnn = "Provider=SQLOLEDB;Data Source=sql70server;" & _
      "Initial Catalog=Northwind;User ID=sa;Password=''"

   Set rst = New ADODB.Recordset
   rst.CursorLocation = adUseClient

   rst.Open "SELECT * FROM Customers", strCnn, adOpenKeyset, _
      adLockOptimistic, adAsyncFetch
   rst.MoveLast

End Sub

Private Sub rst_FetchComplete(ByVal pError As ADODB.Error, adStatus _
   As ADODB.EventStatusEnum, ByVal pRecordset As ADODB.Recordset)
      MsgBox "FetchComplete executed"
End Sub


Asynchronously Executing a Command

Command execution is another operation that can take a significant amount of time. ADO allows asynchronous command execution in two ways: using the Connection.Execute method and using the Command.Execute method.

Asynchronous command execution covers a particular period of time. That is, it covers the time from when the data source object is requested to begin an operation to the time that the first row is available to be fetched (assuming a row-returning result).

The code for asynchronous command execution is similar to the code for asynchronous connection. The Options parameter to the ADO execute methods must include the value adAsyncExecute. Like many other option parameters for methods, this Options parameter can take more than one value; the most common technique to combine option values is to use the OR operator.

In the following example, an event handler has been implemented to print to the Debug window when the command has completed:

Dim WithEvents conn As ADODB.Connection

Sub Form_Load()
   Dim cmd As ADODB.Command
   Set cmd = New ADODB.Command
   Set conn = New ADODB.Connection

   conn.ConnectionString = _
   "Provider=SQLOLEDB;Data Source=sql70server;" _
      & "User ID=sa;Password='';Initial Catalog=pubs"
   conn.Open

   Set cmd.ActiveConnection = conn
   cmd.Execute "select * from authors", , adAsyncExecute
   Debug.Print "Command Execution Started."

End Sub

Private Sub conn_ExecuteComplete(ByVal RecordsAffected As Long, ByVal _
   pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal _
   pCommand As ADODB.Command, ByVal pRecordset As ADODB.Recordset, _
   ByVal pConnection As ADODB.Connection)

   Debug.Print "Completed Executing the Command."
End Sub


Asynchronously Fetching Results

With large result sets, fetching can take a long time. With asynchronous fetching, the user can be shown some results almost immediately and then the rest can be retrieved in the background.

Asynchronous fetching in ADO is implemented only for client cursors (CursorLocation = adUseClient). The actual background fetching is implemented by using the Remote Data Service (RDS) cursor engine.

There are two types of asynchronous fetches, blocking and nonblocking. The difference between blocking and nonblocking is in how operations that would attempt to refer to unfetched rows are handled. In the blocking case, the function being requested (for example, MoveLast) is executed and, before returning control to the application, ADO waits until the last row has been made available. In a nonblocking scenario, the MoveLast operation returns immediately but the current row is changed to the last one fetched; the user would be placed only on the last row currently fetched, not on the last row that would eventually be fetched into the result set. As a result, the user can see that row and all of the other rows already fetched.

Note The distinction between blocking and nonblocking asynchronous fetches is subtle, and there are uses for both cases.

By default, 50 rows are fetched before control is returned to the developer. The value 50 was chosen to represent approximately what might be displayed in a grid on a form. This number can be controlled by using the Initial Fetch Size property in the Recordset Properties collection.

Asynchronous fetching can be requested only by using the Recordset object, through the Open method's Options parameter. This is demonstrated in the following example, which uses a blocking (adAsyncFetch) asynchronous fetch.

Note This example shows the use of only one of the notification events available in ADO—FetchComplete—and there are several others. To learn more, see the ADO documentation.

Dim WithEvents rst As ADODB.Recordset

Sub Form_Load()

   Dim conn As ADODB.Connection
   Set conn = New ADODB.Connection
   Set Rst = New ADODB.Recordset

   conn.ConnectionString = _
   "Provider=SQLOLEDB;Data Source=sql70server;" _
      & "User ID=sa;Password='';Initial Catalog=pubs"
   conn.Open

   Set rst.ActiveConnection = conn
   rst.CursorLocation = adUseClient

   rst.Properties("Initial Fetch Size") = 1
   rst.Open "select * from authors", , , , adAsyncFetch
   Debug.Print rst.Fields(0) & "   : First field, Fetching Started"

End Sub

Private Sub rst_FetchComplete(ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pRecordset As ADODB.Recordset)

   Debug.Print "Completed Fetching Results."

End Sub


Service Providers

OLE DB service providers add features to existing data providers. Making a commonly used feature available externally to any data provider reduces the complexity of each provider and also eliminates redundancy of features among providers.

The functionality of service providers is further extended by the OLE DB Service Component Manager, a core component of the MDAC SDK, which enables or disables OLE DB services depending on the provider functionality required by the consumer. The Service Component Manager uses defaults set by the data provider in the registry. If the consumer uses the initialization component's IDataInitialize interface to invoke a data provider, all services are enabled by default. The consumer can override the defaults either in the connection string or by setting DBPROP_INIT_OLEDBSERVICES properties.

See the following topics for detailed information about the OLE DB service providers:

  • Data Shaping Service for OLE DB
  • Cursor Service for OLE DB
  • OLE DB Remoting Provider
  • OLE DB Persistence Provider


Data Shaping Service for OLE DB

Data shaping provides a way to model complex, hierarchical data relationships—such as one-to-many or many-to-many—to create sophisticated data analysis and reporting applications. Data shaping puts a lot of power in the developer's hands. However, the commands required to create data shapes can quickly become very complex. The Data Shaping Service simplifies this by providing a Shape language. The Shape language makes it possible to assemble this complex hierarchical recordsets with a syntax and grammar that resembles SQL.

In OLE DB, a single rowset can be used to retrieve data for each level in a hierarchical query. The parent rowset contains a "chapter-valued" column that identifies the set of rows within the single child rowset that pertain to that parent. Specifying this chapter value when calling row-retrieval methods on the child rowset effectively limits the rowset to those rows that pertain to the given chapter.

ADO supports binding to hierarchical data by exposing "tear-off" recordsets. Earlier, this article mentioned that ADO could expose COM objects in a column value (for details, see "Extensibility with ADO"). These COM objects can include Recordset objects. Binding to a column in a recordset that represents nested data returns a child recordset for that data. The way this recordset works is identical to how any other recordset in ADO works.

This service provider can be accessed through ADO or through OLE DB directly, and it works like any other provider. When connecting to the Data Shaping Service, the user specifies the underlying data provider from which the data to shape is obtained. The Data Shaping Service supports a special syntax for relating multiple result sets from the underlying provider to each other, similar to a relational join except that the results are returned as a hierarchical OLE DB rowset or ADO recordset instead of the more typical tabular rowset or recordset.

Alternatively, the Data Shaping Service can be used to aggregate values and return the results as hierarchical data.

The Data Shaping Service provides the following functions:

  • Creates relational, parameterized, or group hierarchies
  • Creates hierarchies by reshaping client rowsets
  • Extends the functionality provided for flat results to hierarchical data
  • Support for grandchild grouping
  • Allows creation of aggregate columns over child rowsets
  • Enables creation of parameterized computed child rowsets
  • Enables creation of client-calculated columns within a record
  • Enables the addition of empty user-defined columns to a result set

The hierarchy syntax aggregates a result, as defined by a command or a name, and constructs a new hierarchy. A command is a cursor definition native to the data provider. Commands can contain parameters, which customize the hierarchy when it is created. A name refers to existing local results already built by the Data Shaping Service; that is, the Data Shaping Service can add columns to a new result. The new columns can be an aggregation function over child rowsets, an expression, or just a new user-defined column.

By default, the value of the child reference column is different for each row because the Data Shaping Service creates a new chapter every time the current row is moved. The provider can optionally return a unique child reference for a given child, and update the membership of the respective child chapter on row changes to contain the child rows for the current parent row.

The Data Shaping Service applies the requery method called on the root rowset of a hierarchy to the entire hierarchy. All other functions such as navigation and synchronization are associated with each component rowset and should be called as needed on each component of a hierarchy.

In ADO, hierarchies are implemented by using a command syntax that defines how to shape the recordsets. Embedded within that syntax are the actual commands to be sent to the data source.

The Data Shaping Service implements three types of hierarchies. See the following topics for a complete description and an example of each hierarchy type:

  • Relation-Based Hierarchy
  • Parameterized Command Hierarchy
  • Grouping Hierarchy


Relation-Based Hierarchy

In a relation-based hierarchy, two recordsets are fetched to the client and then related on a common field or fields in the client application. For example, in a scenario relating customers and orders, the orders records for each customer are accessed through a field in the customer recordset that is a recordset in itself.

In the following example, the first command is the most basic of shape commands. It takes two recordsets (defined by the SQL statements in the curly braces) and then relates them on the customerid fields in both recordsets, as follows:

SHAPE  {select * from customers}
APPEND ({select * from orders} AS rsOrders
RELATE customerid TO customerid)

This yields the following result:

Customers.*
rsOrders
   |
   +----Orders.*

In the preceding diagram, the parent recordset contains all fields from the Customers table and a field called rsOrders, which provides a reference to the child recordset, which contains all the fields from the Orders table.

The following example executes the preceding SHAPE command and then prints out the results. For this example, the data store has changed to the NWIND Access database included with Microsoft® Visual Studio® and Microsoft Office.

Note The ADO documentation expands on the examples given here as well as describing the full grammar.

Sub shapetest()

   Dim rst As ADODB.Recordset
   Set rst = New ADODB.Recordset
   strConnect = "Provider=MSDataShape;data _
      provider=msdasql;Data Source=nwind;"

   rst.Source = "shape {select * from customers} APPEND " & _
      "({Select * from orders} As rsOrders " & _
         "RELATE customerid to customerid)"

   rst.ActiveConnection = strConnect
   rst.Open , , adOpenStatic, adLockBatchOptimistic
   printtbl rst, 0

   rst.Close

End Sub

Sub printtbl(rs, indent)
   Dim rsChild As adodb.Recordset

   While rs.EOF <> True
      For Each col In rs.Fields
         If col.Type <> adChapter Then
            Debug.Print Space(indent), col.Value,
         Else
            Debug.Print
            Set rsChild = col.Value
            printtbl rsChild, indent + 4
         End If
      Next
      Debug.Print
      rs.MoveNext
   Wend

End Sub


Parameterized Command Hierarchy

A parameterized hierarchy is similar to a relation-based hierarchy. The main difference is that instead of downloading all the records for both the parent and the child recordsets, the parents are downloaded and then the child records are downloaded as they are accessed by the consumer application. This has the benefit of a shorter initial query time, but the application must be connected to the server for new child records to be accessed.

The following example is a variation on the relation-based hierarchy example, and it generates the same results as that example's first SHAPE command. (There is a possible exception: Because the expression handling code in the Data Shaping Service for OLE DB is different than that in the server, there could be cases where the two commands might generate different results. This could be especially noticeable with relations involving Null values.) However, instead of getting all of the data for the orders recordset up front, it fetches data for the orders recordset only when the application attempts to look at any of the orders.

Note The ADO documentation expands on the examples given here as well as describing the full grammar.

SHAPE  {select * from customers}
APPEND ({select * from orders where customerid = ?} AS rsOrders
RELATE customerid TO PARAMETER 0)


Grouping Hierarchy

A grouping hierarchy involves only one recordset. The concept is similar to a GROUP BY clause in an SQL statement: Rows with a common column value are grouped together, and aggregate columns can be generated (for example, using SUM or AVG). However, the ADO hierarchy feature makes this more interesting than standard grouping. The aggregate values are stored in a parent recordset, and all of the children for that group are stored in the child recordset. This can be very useful for generating reports.

Although only one source recordset is used in the group hierarchy, two recordsets are returned from executing the command. The parent recordset contains group and aggregate information, and the child recordset contains the actual details of the individual rows. Consider the following example, which creates a two-recordset hierarchy.

Note In the following example, CustCount is the calculated count of the number of customers in each group. RS1 is a recordset-valued column. (The type for the Field.Type property is adChapter, to reflect the OLE DB column type DBTYPE_CHAPTER.)

SHAPE  {select customerid, region from customers} rs1
   COMPUTE COUNT(rs1.customerid) As CustCount, rs1 By region

The shape of the resulting hierarchy is as follows:

CustCount
Rs1
   |
   +----customerid
      region

Note The ADO documentation expands on the examples given here as well as describing the full grammar.


Cursor Service for OLE DB

Based on technology in Microsoft® Visual FoxPro®, query results are retrieved and stored in a cursor in memory on the client. Changes are made to a copy of the data stored in a second cursor. All changes are batched and sent to the server in one trip, reducing network traffic and improving performance.

The Cursor Service provides several functions, which are described in the following sections.

Data Fetching

When the consumer requests functionality that requires the Cursor Service, the Cursor Service component is invoked to fetch the data and cache it locally. The data is stored in memory buffers and, if needed, written to temporary files on disk. The data fetch can be performed synchronously or asynchronously. The fetch of large data fields, such as BLOBs, can be delayed until the data is actually required by the application.

Data Manipulation

You can access a rowset by using either ADO or OLE DB interfaces. Then, for example, you can use the ADO Recordset object to manage currency and manipulate the data. The Recordset exposes methods implemented by the Cursor Service to position on a row, get field values, and filter and sort the rows based on one or more fields. A Recordset can be cloned, which produces a second Recordset based on the same underlying rowset data. Clones have their own currency, filter, and sort order.

The Cursor Service also generates events exposed by the associated rowset. These events are used to bind rowset data to visual controls. Rowsets can be persisted in the Advanced Data Tablegram (ADTG) internal binary format or in XML using built-in support for the Persistence Provider. A client rowset can be remoted between two processes or machines over DCOM or HTTP, enabling distributed data access for multitier architectures over intranets and the Internet.

Local Updates

You can make any changes to the local data—such as modifying data fields, deleting existing rows, and inserting new rows into the rowset. In ADO, for example, the changes are immediately committed to the cache and are visible from all the cloned Recordset objects on that rowset. Local changes can be committed to the data provider row by row or once for the entire rowset via the synchronization service.

Local Rowsets

A developer often needs a place to temporarily store some data, or for data binding in a user interface, a developer might want certain local data to act as if it came from a server. By first defining the column information associated with the rowset and then opening it, the Cursor Service allows you to create a stand-alone rowset. Local rowsets can be used as data buffers or to hold and share local temporary data in an application. Indexes can be created on local rowsets to support sort, filter, and find operations. All data-manipulation operations are available on local rowsets.

The Cursor Service enables the developer to build an empty recordset by specifying column information and calling Open. The developer now has an empty recordset to work with, as demonstrated by the following ADO code example:

Dim Rst As ADODB.Recordset
Set Rst = New ADODB.Recordset
Rst.CursorLocation = adUseClient

'Add some fields
Rst.Fields.Append "dbkey", adInteger
Rst.Fields.Append "field1", adVarChar, 40, adFldIsNullable
Rst.Fields.Append "field2", adDate

'Create the recordset
Rst.Open , , adOpenStatic, adLockBatchOptimistic

'Add some rows
Rst.AddNew Array("dbkey", "field1", "field2"), _
   Array(1, "string1", Date)
Rst.AddNew Array("dbkey", "field1", "field2"), _
   Array(2, "string2", #1/6/1992#)

'Look at the values--a value of 1 for status column = newly added record
Rst.MoveFirst
Debug.Print "Status", "dbkey", "field1", "field2"
While Rst.EOF <> True
   Debug.Print Rst.Status, Rst!dbkey, Rst!field1, Rst!field2
   Rst.MoveNext
Wend

'Committing the rows without ActiveConnection set resets the status bits
Rst.UpdateBatch adAffectAll

'Change the first of the two rows
Rst.MoveFirst
Rst!field1 = "changed"

'Now look at the status: First row shows 2 (modified row),
'second shows 8 (no modifications).
'Also note that the OriginalValue property shows the value
'before the modification.
Rst.MoveFirst
While Rst.EOF <> True
   Debug.Print
   Debug.Print Rst.Status, Rst!dbkey, Rst!field1, Rst!field2
   Debug.Print , Rst!dbkey.OriginalValue, _
      Rst!field1.OriginalValue, Rst!field2.OriginalValue
   Rst.MoveNext
Wend
Rst.Close

One useful feature of a creatable recordset is that pending operations can be "committed" to the recordset. Whenever UpdateBatch is called on a client cursor that has no ActiveConnection set, the changes in the affected row (controlled by the AffectedRows parameter) will be committed to the buffer and the Status flags will be reset. The same applies to CancelBatch, except that the changes in the buffer will be reverted and the flag will be reset.

An additional scenario is to use the fabricated recordset as the basis for an OLE DB provider of sorts. For example, in ADO, an event called EndOfRecordset has been defined to notify the developer that the consumer has reached the end of the result set. The developer then has the option to add new rows within the event. To the consuming application, it appears that the rows have been there all along. The following example demonstrates this feature:

Dim WithEvents rst As ADODB.Recordset
Sub form_load()

   Set rst = New ADODB.Recordset
   rst.CursorLocation = adUseClient

   rst.Fields.Append "dbkey", adInteger
   rst.Fields.Append "field1", adVarChar, 40, adFldIsNullable
   rst.Fields.Append "field2", adDate

   rst.Open , , adOpenStatic, adLockBatchOptimistic

   'Add some rows
   rst.AddNew Array("dbkey", "field1", "field2"), _
      Array(1, "string1", Date)
   rst.AddNew Array("dbkey", "field1", "field2"), _
      Array(2, "string2", #1/6/1992#)

   Debug.Print "Status", "dbkey", "field1", "field2"
   While rst.EOF <> True
      Debug.Print rst.Status, rst!dbkey, rst!field1, rst!field2
      rst.MoveNext
   Wend

   rst.Close

End Sub

Private Sub rst_EndOfRecordset(fMoreData As Boolean, adStatus As ADODB.EventStatusEnum, ByVal pRecordset As ADODB.Recordset)

   If pRecordset.RecordCount < 50 Then
      StartIndex = pRecordset.RecordCount
      Debug.Print "Adding Some New Data"
      For ii = 1 To 10
         pRecordset.AddNew Array("dbkey", "field1", "field2"), _
            Array(StartIndex + ii, "string1", Date)
      Next
   fMoreData = True
   End If

End Sub

Independently Creatable Objects

All objects in ADO can be instantiated on their own, except the Error and Field objects. To allow greater flexibility in reusing objects in different contexts, the hierarchy of objects found in previous models like DAO and RDO is de-emphasized in the ADO model. For example, you can create a Command object, associate and execute it against one connection, and then associate it with a different connection and reexecute it. This approach also paves the way for you to create specialized objects (defined at design time) and temporary, unattached recordsets.

Extended Result Set Processing

OLE DB allows you to find a record within a rowset according to a column value. This provides the ability to support simple find operations and list box support. In addition, the specification defines a concept of views that, where appropriate, enable simple providers that do not support textual commands to expose simple filtering and sorting mechanisms. For command-based providers, applying a view to an existing rowset allows consumers to do postprocessing of results obtained from a query. The consumer does not have to reexecute the query and make an additional round-trip to the server to re-retrieve a subset of the data already obtained.

Finding, Sorting, and Filtering on Recordsets

ADO fully supports finding, sorting, and filtering on recordsets. Where the OLE DB provider supports these extensions, ADO uses the provider to achieve optimal performance for these operations. Where not supported by the provider, common services under ADO provide these extensions to ensure that the ADO functionality works when accessing data from any OLE DB provider.

Some operations that are easy for a desktop database application to perform are difficult (or, at the very least, exhibit poor performance characteristics) on a database server. ADO exposes a Find method and a Sort property. With these features, the user can do desktop-style data manipulation against any data source from within ADO.

Find and Sort are implemented to use support from the provider. If the provider does not support the proper OLE DB interface for Find, ADO attempts to carry out this operation at the client by brute force. (This brute-force operation for Find can be slow, but there is another alternative available, as discussed below.) If the provider does not support the proper OLE DB interfaces for sorting, setting the Sort property generates an error.

If client cursors are being used, the sort, find, and filter operations can be optimized in three ways:

  • The client cursor supports the OLE DB interfaces needed for both sorting and finding.
  • Because the data has already been downloaded to the client cursor, it requires few round-trips to the server (after the initial overhead of fetching the results).
  • The client cursor builds indexes on sorted columns that are used to optimize Find and Filter operations.

Sorting a Recordset

To sort a recordset, the developer sets the Recordset.Sort property to the name of the column that the recordset should be sorted by, as shown in the following code:

Sub main()

   Dim conn As ADODB.Connection
   Dim Rst As ADODB.Recordset
   Set conn = New ADODB.Connection
   Set Rst = New ADODB.Recordset

   conn.ConnectionString = _
   "Provider=SQLOLEDB;Data Source=sql70server;" _
      & "User ID=sa;Password='';Initial Catalog=pubs"
   conn.Open

   Set rst.ActiveConnection = conn
   rst.CursorLocation = adUseClient

   rst.Open "select * from authors", , adOpenStatic, _
      adLockBatchOptimistic

   'Print out the records in their original order
   While rst.EOF <> True
      Debug.Print rst!au_id, rst!au_lname, rst!au_fname
      Rst.MoveNext
   Wend

   'Print out the records ordered by author last name
   rst.Sort = "au_lname"

   'Note: Sort automatically moves you to the first row in the
   'result set so a MoveFirst is not needed.
   While rst.EOF <> True
      Debug.print rst!au_id, rst!au_lname, rst!au_fname
      Rst.MoveNext
   Wend

   Rst.Close
   conn.Close

End Sub

Sort can also be used to sort by multiple columns. To sort by last name and then by first name in the preceding example, the command is as follows:

   'Print out the records ordered by author last name
   'and then by first name
   rst.Sort = "au_lname, au_fname"

Finally, it can also be useful to sort one column in ascending direction and another in descending. To do this you specify DESC following the name to be sorted in descending direction, as follows:

   'Print out the records ordered by author last name and then by
   'first name, with the first name sorted in descending order
   rst.Sort = "au_lname, au_fname DESC"

Finding a Value in a Recordset

Even after narrowing the results returned from the server by using the SQL command, it is often interesting to search for a particular row in the recordset. ADO has implemented a Find method that will search a named row for a specified value.

In the Microsoft® Visual Basic® object browser, the Find method appears as follows:

Find(Criteria As String, [SkipRecords As Long], [SearchDirection As SearchDirectionEnum = adSearchForward], [Start])

This may seem complex for just finding a record in a recordset. However, here is a description of what each parameter to the method means:

  • Criteria This is where the actual Find expression is specified. The syntax is fieldname operator value. The fieldname value must be the name of a field, as found in the ADO Fields collection.
  • SkipRecords By using this optional parameter, you can specify whether to offset your search by a particular row. By default, Find searches always include the current row. A value of 1 for this parameter starts the search with the next row (in the direction you are searching). A value of –1 starts the search with the previous row.

    Note You can use values other than –1 and 1 to indicate other offsets.

  • SearchDirection By using this parameter, you specify whether to search forward or backward in the recordset.
  • Start In ADO, you can optionally specify a bookmark from which to start searching. If you specify a bookmark, the SearchDirection and SkipRecords parameters are applied from the bookmark.

If you understand how to use these parameters, you can get exactly the behavior you want from Find.

Note Find works only with a single column. It does not support searching multiple columns. If Find does not find a match, it positions you at either end-of-file (EOF), if you were searching forward, or beginning-of-file (BOF), if you were searching backward.

Sub main()

   Dim conn As ADODB.Connection
   Dim Rst As ADODB.Recordset
   Set conn = New ADODB.Connection
   Set Rst = New ADODB.Recordset

   conn.ConnectionString = _
   "Provider=SQLOLEDB;Data Source=sql70server;" _
      & "User ID=sa;Password='';Initial Catalog=pubs"
   conn.Open

   Set rst.ActiveConnection = conn
   rst.CursorLocation = adUseClient

   rst.Open "select * from authors", , adOpenStatic, _
      adLockBatchOptimistic

   'Search from the beginning and find an author named Ringer
   rst.Find "au_lname = 'Ringer'"
   'Search from the beginning and find an author named Green
   rst.Find "au_lname = 'Green'", , , adBookmarkFirst

   'Search from the end and find an author named Ringer
   rst.Find "au_lname = 'Ringer'", , adSearchBackward, adBookmarkLast

   rst.Close
   conn.Close

End Sub


Synchronization Service

The synchronization service works closely with the Cursor Service for OLE DB to perform the following functions: propagate updates to the data source, coordinate conflicting updates, refresh the data from the data source, and perform nondestructive resynchronization.

Updates

The Cursor Service has two modes of sending the updates back to the data provider: immediate and deferred. In immediate mode, changes are committed to the provider on a row-by-row basis. In deferred mode, the changes made to the rowset are buffered locally and the row's state is marked as changed. When the application decides to commit the changes, it calls the batch update method on the rowset and all buffered changes are sent to the data source.

The Synchronization Service is used to support the deferred mode. It uses information kept about the changes made to the local rowset, and updates or refreshes only the differences between the local rowset and the data source provider. When the batch update method is called, the Synchronization Service updates each of the local rows in the rowset with the status of the update attempt.

Using a local static rowset with deferred updates is a common application scenario, offering the following benefits:

  • It provides fast data navigation because after the data is fetched, most operations are local.
  • It eliminates heavy concurrency in accessing data on the server. Optimistic conflict resolution allows you to resolve any conflicts.
  • It reduces the overall network traffic associated with propagating changes to the data source by grouping multiple individual updates in a single request.

Optimistic Conflict Resolution

For each row in a rowset, the Cursor Service maintains a copy of the original row fetched from the provider, as well as the new, changed value. When the updates are sent to the data source, the synchronization service compares original values with the current values from the data source to verify whether the row has changed since the data was fetched. The synchronization service compares the values using a combination of key columns, row timestamps, changed fields, and sometimes the entire row.

If a conflict is detected, the status for that row changes to indicate an update conflict. You have the option to get the current values for the conflicting row and compare them with the original row values and the changed values in the rowset. After you resolve the conflict, the update can be submitted again, this time using the current values to detect new update conflicts. The update service has the option to get current data for all conflicting rows in a batch. It also allows you to control how the local data is updated when conflicts are detected, to match the transaction model implemented by the application.

Refresh

The ADO Refresh method updates the rowset with the most recent data available from the provider. The scope of the Refresh command can be a large data field, a row, a set of rows, or the entire rowset. To perform a refresh, the update service requires information about the base tables used to produce the rowset and their primary keys.

Nondestructive Resynchronization

The ADO Resync method gives the developer a way to find the current values on the server for the specified records in the recordset, without requiring a complete reexecution of the original query. The Resync method has existed in ADO since version 1.0, but it did not work for client cursors until after ADO 1.5. Additionally, the effects of Resync were "destructive"—that is, Resync discarded any pending changes to the resynchronized row. This was an intentional design. The original implementation did not consider disconnected operation.

Resync now supports client cursors. Additionally, it has a nondestructive option that updates just the UnderlyingValue property for each field without discarding pending changes. The nondestructive option is currently available for client cursors only. It relies on an OLE DB interface, IRowsetRefresh.

There can be cases in which the Resync operation generates an error. These cases arise when the client cursor cannot deduce enough information from the provider to uniquely determine how to get the new values from the server. This can also happen if the user requests a read-only client cursor. In that case, as a performance optimization, ADO does not ask for the full recordset metadata and is unable to perform the Resync operation.

To help ensure the operation's success, your result set should include either the primary key for the table or a column that has a unique key and is not opened as lock type adLockReadOnly.

Sub main()

   Dim conn As ADODB.Connection
   Dim Rst As ADODB.Recordset
   Set conn = New ADODB.Connection
   Set Rst = New ADODB.Recordset

   conn.ConnectionString = _
   "Provider=SQLOLEDB;Data Source=sql70server;" _
      & "User ID=sa;Password='';Initial Catalog=pubs"
   conn.Open

   conn.Execute "create table testtable (dbkey int primary key, _
      field1 char(10))"
   conn.Execute "insert into testtable values (1, 'string1')"

   Set Rst.ActiveConnection = conn
   Rst.CursorLocation = adUseClient

   Rst.Open "select * from testtable", conn, adOpenStatic, _
      adLockBatchOptimistic

   'Create an update to the server row that the
   'client cursor doesn't know about
   conn.Execute "Update testtable set field1 = 'CONFLICT' _
      where dbkey = 1"

   'Change the row on the client
   Rst!field1 = "NewValue"

   'Resync the row to find out what's on the server
   Rst.Resync adAffectCurrent, adResyncUnderlyingValues

   Debug.Print "Current Edited Value: " & Rst!field1.Value
   Debug.Print "Value Before Editing: " & Rst!field1.OriginalValue
   Debug.Print "Value On the Server: " & Rst!field1.UnderlyingValue

   Rst.Close
   conn.Close

End Sub


OLE DB Remoting Provider

The OLE DB Remoting Provider provides a transparent caching architecture. Internet data is frequently displayed in an HTML document: It's cached locally but it's static, and the data that you have isn't really live. You can work on it and then later send it back and say "Update it," but unlike traditional client/server applications, it is not interactive. By transparently caching data locally at the client side, the Remoting Provider minimizes round-trips to the server. The Remoting Provider brings the metadata and the data to the client so that a user can work on it at the client. When the client application is finished updating the data, the Remoting Provider will worry about the plumbing, getting the data to the server, and ensuring data integrity through optimistic concurrency.

The Remoting Provider transports the rich client experience, implemented by the Cursor Service component, to applications connecting over the Internet or other networks where it is difficult or expensive to maintain a consistent connection.

The Remoting Provider has some additional enhancements, such as a customization handler for the ADO RDS.DataFactory object. Server administrators can use the customization handler to control the operations that can be executed through the default RDS.DataFactory object. Also, the Remoting Provider has a cross-domain security feature that honors the security settings in the browser. Depending on the specified level of security, users are now restricted to connect to a database on the server from which the page was downloaded.

The following features are typical of distributed applications running over disconnected networks such as the Internet:

  • Server scalability and network bandwidth
  • Distributed applications and untrusted clients

Server Scalability and Network Bandwidth

Scalability of Web or application servers is tested every day by users accessing applications over the Internet. Even when the servers can scale to handle large numbers of Internet users, network bandwidths often cannot. Client computers must be able to accomplish more with less support from the server and, at times, without even a server connection. Server resources need to be handled judiciously by new brands of applications.

To solve the scalability problem, the Remoting Provider moves the application logic (that is, the user interface and client-side data manipulation) and data to the client. It implements a "stateless" model, in which client and server can be disconnected between data access operations. The Remoting Provider marshals data efficiently between the middle tier or server and the client; it provides support for batch updates by using an efficient client-side Cursor Service component that can process data locally without constant server requests. The work done at the client can be saved locally and later uploaded to the server when a connection is available.

Distributed Applications and Untrusted Clients

Some of the most important tasks that Web and application server administrators handle are data integration, security, and application management. Organizations deploy business components that run on the application servers behind the Web servers and control complete access to underlying enterprise data. These components implement business rules and validation logic, allowing client applications to read and update data. The client applications make requests to the components that are under central control of the system administrator. The Remoting Provider makes it easy to invoke these components over DCOM and HTTP and efficiently marshals input/output parameters, enabling effective distributed computing.

The Remoting Provider makes at least one interesting scenario possible: First, fabricate a recordset in a business object, and then, by remoting, return that data to the client to receive updates. The data is then remoted back to the Web server. The business object now has a recordset with all the information necessary to save the changes; that is, which rows changed, how they were changed, which rows were newly added. You can even set the Filter property of the Recordset object to adFilterPending to show deleted rows.


OLE DB Persistence Provider

Portable computing (for example, using laptops) has generated the need for applications that can run in both a connected and disconnected state. ADO has added support for doing this by giving the developer the ability to save a client cursor recordset to disk and reload it later.

There are several interesting scenarios in which to use this type of feature, including the following:

  • Traveling salesman: When taking the application on the road, it is vital to supply the ability to make changes and add new records that can then be reconnected to the database later and be committed.
  • Infrequently updated lookups: Often in an application, tables are used as lookups—for example, state tax tables. They are infrequently updated and are read-only. Instead of rereading this data from the server each time the application is started, the application can simply load the data from a locally persisted recordset.

In ADO, to save and load recordsets, use the Recordset.Save and Recordset.Open(,,,,adCmdFile) methods on the ADO Recordset object, as demonstrated in the following example:

Sub Form_Load()

   Dim conn As ADODB.Connection
   Dim rst As ADODB.Recordset
   Set conn = New ADODB.Connection
   Set rst = New ADODB.Recordset

   conn.ConnectionString = _
   "Provider=SQLOLEDB;Data Source=sql70server;" _
      & "User ID=sa;Password='';Initial Catalog=pubs"
   conn.Open

   conn.Execute "create table testtable (dbkey int _
      primary key, field1 char(10))"
   conn.Execute "insert into testtable values (1, 'string1')"

   Set rst.ActiveConnection = conn
   rst.CursorLocation = adUseClient

   rst.Open "select * from testtable", conn, adOpenStatic, _
      adLockBatchOptimistic

   'Change the row on the client
   rst!field1 = "NewValue"

   'Save to a file--the .dat extension is an example; choose
   'your own extension. The changes will be saved in the file
   'as well as the original data.
   Kill "c:\temp\temptbl.dat"
   rst.Save "c:\temp\temptbl.dat", adPersistADTG
   Set rst = Nothing

   'Now reload the data from the file
   Set rst = New ADODB.Recordset
   rst.Open "c:\temp\temptbl.dat", , adOpenStatic, _
      adLockBatchOptimistic, adCmdFile

   Debug.Print "After Loading the file from disk"
   Debug.Print "   Current Edited Value: " & rst!field1.Value
   Debug.Print "   Value Before Editing: " & rst!field1.OriginalValue

   'Note that you can reconnect to a connection and
   'submit the changes to the data source
   Set rst.ActiveConnection = conn
   rst.UpdateBatch

   rst.Close
   conn.Close

End Sub


XML

OLE DB rowsets are self-describing objects that contain data and metadata, created by executing commands against data stores. Because of its self-describing nature, rowset information can be easily represented in XML. Therefore, XML is supported as a streaming and persisting format for rowsets. This opens up the use of rowsets to non-COM platforms: If XML-represented structured data can be generated from there, it can be translated into rowsets on other machines and vice versa. This also allows ADO Recordset objects to be transmitted over HTTP as part of standard HTML text.

The OLE DB Persistence Provider can be used to save a recordset in XML format to any object that implements the IStream interface, such as the Active Server Pages Response object. This feature eliminates the inefficient step of saving the XML to a file on a disk and then sending that file to the Response object.

The following is a snippet of an XML stream representing a rowset. The rowset was generated from the sample Pubs database in Microsoft SQL Server™ 7.0, using the following command:

SELECT title, au_lname, price, ytd_sales FROM titleview

Following is the XML as generated by the Persistence Provider (in MDAC 2.5):

<xml xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882'
   xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882'
   xmlns:rs='urn:schemas-microsoft-com:rowset'
   xmlns:z='#RowsetSchema'>
<s:Schema id='RowsetSchema'>
   <s:ElementType name='row' content='eltOnly' rs:CommandTimeout='30'>
      <s:AttributeType name='title' rs:number='1' rs:writeunknown='true'>
         <s:datatype dt:type='string' rs:dbtype='str' dt:maxLength='80'
               rs:maybenull='false'/>
      </s:AttributeType>
      <s:AttributeType name='au_lname' rs:number='2'
            rs:writeunknown='true'>
         <s:datatype dt:type='string' rs:dbtype='str' dt:maxLength='40'
               rs:maybenull='false'/>
      </s:AttributeType>
      <s:AttributeType name='price' rs:number='3' rs:nullable='true'
            rs:writeunknown='true'>
         <s:datatype dt:type='i8' rs:dbtype='currency' dt:maxLength='8'
               rs:precision='19' rs:fixedlength='true'/>
      </s:AttributeType>
      <s:AttributeType name='ytd_sales' rs:number='4' rs:nullable='true'
            rs:writeunknown='true'>
         <s:datatype dt:type='int' dt:maxLength='4' rs:precision='10'
               rs:fixedlength='true'/>
      </s:AttributeType>
      <s:extends type='rs:rowbase'/>
   </s:ElementType>
</s:Schema>
<rs:data>
   <z:row title='You Can Combat Computer Stress!' au_lname='Green'
         price='2.99' ytd_sales='18722'/>
   <z:row title='The Gourmet Microwave' au_lname='DeFrance' price='2.99'
         ytd_sales='22246'/>
   <z:row title='The Gourmet Microwave' au_lname='Ringer' price='2.99'
         ytd_sales='22246'/>
   <z:row title='Fifty Years in Buckingham Palace Kitchens'
         au_lname='Blotchet-Halls' price='11.95' ytd_sales='15096'/>
</rs:data>
</xml>

For more information, see the technical article "Saving ADO Recordsets in XML Format."


Other Features

The following features are designed into the MDAC components:

  • Session pooling
  • Automatic transaction enlistment
  • Connection information user interface
  • Data conversion
  • Support for Microsoft® Visual Studio® Analyzer

Session Pooling

Session pooling (sometimes called resource pooling) is a new feature of the OLE DB service components that ADO developers can benefit from. (An OLE DB session corresponds to the ADO Connection object.) Session pooling is designed for the Web scenario. It is intended to improve the performance of connecting and disconnecting in large applications by keeping a pool of live connections open for a short period of time and then reusing them for new connection requests. This is an alternative to completely reconnecting to the data source. In session pooling, the security and other properties of the connection are respected. Pooled connections are reused only if the newly requested connection properties match the properties of the pooled connections.

The ADO developer does not have to do anything to use pooling with the OLE DB providers included in the MDAC SDK. For providers not included, the developer may need to create a registry setting to notify the service components that the provider can safely be pooled. For more information about session pooling and service components, the technical article "Pooling in the Microsoft Data Access Components" in the MDAC SDK.

In addition to tuning the individual components, MDAC includes session pooling at the OLE DB level. Connections and transaction enlistment are preserved between uses. Because of this, middle-tier applications that rely on quick, individual database actions can share connections rather than having to create and release connections for each action. This sophisticated session pooling maintains multiple, homogeneous pools of connections, reducing contention and improving throughput. Additional information, such as the provider's initialization properties, support for aggregation, and even the provider's class factory, are cached after the first connection to the provider to further improve performance and scalability.

Automatic Transaction Enlistment

The service components support automatic transaction enlistment in a Component Services (MTS, if you are using Microsoft Windows NT®) environment, if the provider was invoked using ADO, or, for OLE DB, using the IDataInitialize or IDBPromptInitialize interfaces.

Connection Information User Interface

MDAC has a universal Data Link component that provides a common user interface for specifying connection information to any provider. The connection information can be represented by using a common string format. Applications can use the Universal Data Link dialog box to collect information from the user, and users can use the Universal Data Link dialog box to create or edit connection information persisted in a universal Data Link (.udl) file. Because this component is used by ADO to parse the connection string, ADO users can pass a connection string directly or can specify a .udl file containing the connection information.

Data Conversion

The core services include a common data conversion component that can be used by any OLE DB provider to perform the conversions designated in the OLE DB specification. This component, which originally shipped as part of the OLE DB 1.0 SDK, was enhanced to support a number of additional conversions.

Support for Analyzer

Support for the Visual Studio Analyzer is included in both the ODBC Driver Manager and the ADO libraries.


Choosing Your Data Access Strategy

Summary: This article details the features and advantages of the Microsoft Universal Data Access strategy, compares alternative data access technologies, and suggests reasons to choose one technology over another.

The Universal Data Access Strategy

The information you use to make everyday business decisions is a lot more than just data stored in a relational database. It typically includes personal data in spreadsheets, project management applications, e-mail, mainframe data such as VSAM and AS/400, computed data calculated on the fly, and ISAM data. To that, add the information available in directory services, the documents available on your hard drive or on the network, and the wealth of data in myriad forms on the Internet. Each of these types of data needs to be taken into account when making decisions, yet each is in a different format, stored in a variety of containers, and accessed in a fundamentally different way.

If you've ever developed applications that access data from data stores, it's likely that you have been exposed to a bowl of alphabet soup: OLE DB, ADO, ODBC, RDO, DAO, and so on. And beyond those, many other APIs are used to access things like telephony, e-mail, and file systems. They're all related to getting some kind of information, or data.

This is where Microsoft's data access strategy, Universal Data Access (UDA), comes in. It is designed to provide access to all types of data through a single data access model. To understand the importance of this strategy, consider the following typical scenario.

Suppose that you are the project manager responsible for delivering a prototype of the new space shuttle. To prepare for a status meeting, you need to collect information from a variety of sources. And because Microsoft® Project does a great job of managing scheduling information, you already have a number of project files that describe who is allocated to what tasks, whether they are ahead or behind schedule, and so forth.

Reviewing the project, you see that the team responsible for the propulsion unit has continuously added resources to the project but is continuing to fall behind. After identifying the tasks that are causing the team to slip, you do a search on the technical documents on your computer and your local network describing those tasks. You then do a search of your e-mail folders to find all the related mail that discusses the project and the potential cause for it falling behind. You find a message that refers to a delay in receiving some critical parts for the thruster. The millions of parts that make up the shuttle are inventoried in a SQL Server™ database, so you search for the subset of parts used for the thruster and then compare that to your invoices, which are kept in a Microsoft Access database. After finding the invoice for the missing parts, you do a Web search for information about the supplying manufacturer and discover that they were involved in a takeover two months before the invoice due date. Further searching on the Web reveals two other companies that manufacture the same components.

Now that you've collected all the information from various resources, discovered the reason for the delay, and struck a quick deal with one of the other manufacturers, you want to make some changes.

First you want to update all of the parts from the old manufacturer so that they reflect your new supplier. To make this change, you need to update information in your inventory database, the project specification, and outstanding invoices. You have to make these changes to all sources of data that are related to the project in a safe way. You don't want the changes to be applied in two places and fail in the other two. You need the same type of transactional semantics you get in a single data store, even if the data is located in different containers.

So how might this work? You would probably have a project-tracking application to display and analyze project information. (See the following illustration.) The project-tracking application would use Automation to get to the high-level project information stored in the Microsoft Project files. Then you'd use file system interfaces to search for documents on your hard drive, MAPI to get information from the Microsoft Exchange data store, ODBC to get information from the SQL Server, and some HTTP search protocol to find information on the Internet.

In this case, the project-tracking application utilizes at least five separate APIs to access the different types of data. Some of the interfaces are COM interfaces, and some of them are not; some of them expose Automation interfaces, and some are procedural. In addition, transactional control does not exist, so you cannot safely make any changes to all of the information within one transaction.

What all of this means—that is, the unfortunate outcome of the preceding scenario—is that the developers who write the project-tracking application will have to become experts in a multitude of data access methods.

Is there a better way? Well, one solution is to put all of the different types of data into a single relational data store. That is, take the SQL data store and incorporate the project data, e-mail data, directory data, and information from the Internet and move it into a single vendor's relational database. This approach solves the problem of needing a single API that also gives you transactional control—but it has a number of compromises, as well as some development hurdles to clear.

First, it requires moving huge amounts of data. Second, you typically don't own much of the data you need, such as the wealth of information on the Internet. The next problem with moving the data into a single data store is that tools today don't look for or store data into a single relational data store. E-mail applications, project management tools, spreadsheets, and other tools look into their own data stores for their data. You'd have to rewrite your tools to access the data in the single data store or duplicate the data to the single data store from where it naturally lives—then you run into problems with synchronizing the data. Another problem is that one size does not fit all. The way that relational databases deal with data is not the best way to deal with all types of data, because data (structured, semi-structured, and unstructured) is stored in different containers based on how the data is used, viewed, managed, and accessed. Finally, the performance and query requirements are going to differ by data type, depending on what you are trying to do.

So if you can't have all of your data in a single data store, what's the alternative? The answer is Universal Data Access. With Universal Data Access, you get to data in different data stores through a common set of interfaces, regardless of where the data resides. Your application uses a common set of system-based interfaces that generalize the concept of data.

To achieve this breakthrough in its data access strategy, Microsoft examined what all types of data have in common, such as how you navigate, represent, bind, use, and share that data with other components. The result is a set of common interfaces that anyone can use to represent data kept in relational databases, spreadsheets, project and document containers, VSAM, e-mail, and file systems. The data stores simply expose common interfaces—a common data access model—to the data.

Microsoft has solicited extensive feedback from data access customers on the criteria they use in judging and selecting data access technologies and products and has learned that there are only a few main criteria used in the decision-making process:

  • High-performance access to data. Simply stated, new data access methods need to provide the same level of performance that is possible through the data provider's proprietary API. This means that customers will not compromise their number one criterion, performance, for any other benefit. Similarly, services that augment the native capabilities of data providers must be held to the same standards: They must not stand in the way of native performance. And because applications and components must frequently scale to support hundreds or thousands of concurrently connected users, performance must be maintained as usage grows.
  • Reliability. Customers want their database solutions to perform reliably. They use terms such as "rock-solid" and "fail-safe" to describe their requirements in this area. Underlying these statements is the need to minimize maintenance and support costs and to reduce the total cost of ownership.
  • Vendor commitment. Customers indicate that they are making strategic commitments to vendors of data access technologies and are looking for reciprocation. They indicate that database decisions are long term, that they are purchasing not a single release but a string of database and related product releases. On the flip side, customers are wary of becoming too dependent on a single vendor, a situation they term "vendor lock-in" or "vendor tie-in." Finally, to avoid costly replacement of existing capabilities, new technologies should evolve gracefully from current ones.
  • Broad industry support. This is defined by market share, as well as by the support of vendors of related products and technologies. For customers, broad industry support is a more important gauge than the blessing of a standards body when choosing data access products. Broad industry support carries many benefits—availability of skilled people to work with the products, greater industry momentum, wider and deeper variety of peer resources, and products that work together without expensive integration and customization.
  • Multiple platform support. Many organizations rely on a mixture of database and operating system platforms. Any strategy for providing data access must be able to access data on all major computing platforms.

In a nutshell, companies building client/server and Web-based database solutions seek maximum business advantage from the data and information distributed throughout their organizations. Microsoft's Universal Data Access Strategy meets all the criteria listed above and more:

  • Universal Data Access provides high-performance access to a variety of data and information sources on multiple platforms and an easy-to-use programming interface that works with practically any tool or language, leveraging the technical skills developers already have. The technologies that support Universal Data Access enable organizations to create easy-to-maintain solutions and use their choice of best-of-breed tools, applications, and data sources on the client, middle tier, or server.
  • Universal Data Access does not require expensive and time-consuming movement of all corporate data into a single data store, nor does it require commitment to a single vendor's products.
  • Universal Data Access is based on open industry specifications with broad industry support and works with all major established database products.
  • Universal Data Access is an evolutionary step from standard interfaces such as Open Database Connectivity (ODBC), Remote Data Objects (RDO), and Data Access Objects (DAO), and it significantly extends the functionality of these well-known and well-tested technologies.

As mentioned earlier, a major strength of the Microsoft Universal Data Access strategy is that it is delivered through a common set of modern, object-oriented interfaces. These interfaces are based on the Microsoft Component Object Model (COM), the most widely implemented object technology in the world. COM has become the choice of developers worldwide because it provides the following advantages:

  • The richest integrated value-adding services, including transactions, security, message queuing, and data access to support the broadest range of application scenarios.
  • The widest choice of tools from multiple vendors using multiple development languages and platforms.
  • The largest customer base for customizable applications and reusable components.
  • Proven interoperability with users' and developers' existing investments.

Because of the consistency and interoperability afforded through COM, the Microsoft Universal Data Access architecture is open and works with most tools and programming languages. It also provides a consistent data access model at all tiers of the modern application architecture.

The Microsoft Universal Data Access architecture exposes COM-based interfaces optimized for both low-level and high-level application development by using OLE DB and ADO, respectively.

Universal Data Access Is a High-Performance Architecture

Performance is of paramount concern to developers and users of data access technologies. Therefore, Universal Data Access has been designed with performance as its number one goal. This section will examine how the Universal Data Access technologies—ODBC, OLE DB, and ADO—all bundled within the Microsoft Data Access Components (MDAC) SDK, support this requirement and the positive implications for users.

Ultimately, the performance of Microsoft Data Access Components will be judged in comparison with that of native access methods. The goal is to establish OLE DB as the native interface to major Microsoft and non-Microsoft data stores. To accomplish this, performance tuning for all key scenarios (transactions, decision support, and so on) and with major data sources will be of paramount concern. The number one design goal for ADO and OLE DB is performance, and the architectural foundations to achieve this are in place.

Universal Data Access Builds on the ODBC Foundation

Universal Data Access is a strategy that includes and builds on the successful foundation of ODBC. ODBC successes include the following:

  • Establishing a market standard for database access. There are more than 170 ODBC drivers available today, providing access to a broad range of data.
  • Achieving portability so that applications can scale to new database platforms as an organization's requirements change.
  • Responding to customers' needs by steadily adding new features and performance improvements to enable better database applications.

ODBC continues to be a fundamental part of the MDAC architecture.

OLE DB: A Flexible, Component-Based Services Model

As one of the principal MDAC technologies, OLE DB represents an ongoing Microsoft effort to define a set of COM interfaces to provide applications with uniform access to data stored in diverse information sources. This approach allows a data source to share its data through the interfaces that support the amount of DBMS functionality appropriate to the data source. By design, the high-performance architecture of OLE DB is based on its use of a flexible, component-based services model. Rather than having a prescribed number of intermediary layers between the application and the data, OLE DB requires only as many components as are needed to accomplish a particular task.

For example, suppose a user wants to run a query. Consider the following scenarios:

  • The data resides in a relational database for which there currently exists an ODBC driver but no native OLE DB provider: The application uses ADO to talk to the OLE DB Provider for ODBC, which then loads the appropriate ODBC driver; the driver passes the SQL statement to the DBMS, which retrieves the data.
  • The data resides in Microsoft SQL Server or another data source for which there is a native OLE DB provider: The application uses ADO to talk directly to the OLE DB provider for Microsoft SQL Server; no intermediaries are required.
  • The data resides in Microsoft Exchange Server, for which there is an OLE DB provider but which does not expose an engine to process SQL queries: The application uses ADO to talk to the Microsoft Exchange data provider and calls upon an OLE DB query processor component. The query processor component interprets the SQL command and directly calls the OLE DB interface methods to work with the Exchange data provider.
  • The data resides in the Microsoft NTFS file system in the form of documents: Data is accessed by using a native OLE DB provider over Microsoft Index Server, which indexes the content and properties of documents in the file system to enable efficient content searches.

In all four of the preceding examples, the application can query the data. The user's needs are met with a minimum number of components. In each case, additional components are used only if needed, and only the required components are invoked. This demand-loading of reusable and shareable components greatly contributes to high performance when OLE DB is used.

ADO Performance Advantages

As with OLE DB, ADO is designed for rapid development as well as high performance. It reduces the amount of solution code developers must write by "flattening" the coding model. DAO and RDO, the object models that preceded ADO, are highly hierarchical models—that is, to return results from a data source, the programmer has to start at the top of the object model and traverse down to the layer that contains the recordset. However, the ADO object model is not hierarchical: The ADO programmer can create a recordset in code and be ready to retrieve results by setting two properties; the programmer can then execute a single method to run the query and populate the recordset with results.

The ADO approach dramatically decreases the amount and complexity of code that needs to be written by the programmer. This generally improves the performance of the code that is calling into ADO. Internally, ADO is extremely efficient. ADO offers better performance in getting data from recordsets. Scalability is improved by minimizing overhead in simple scenarios.

Minimal Network Traffic in Key Internet Scenarios

Microsoft has designed OLE DB and ADO for the Internet, supporting a "stateless" model in which client and server can be disconnected between data access operations. The MDAC SDK contains a Remote Data Service component that provides efficient marshaling of data between the middle tier or server and the client, including support for batch updates and an efficient client-side cursor engine that can process data locally without constant server requests. Thus MDAC provides greater local functionality and higher performance for Internet applications than other approaches.

Relationship of Universal Data Access and Windows DNA

Windows DNA is the Microsoft architectural framework for building modern, scalable, multitier distributed computing solutions that can be delivered over any network. Windows DNA provides a unified architecture that integrates the worlds of client/server and Web-based application development. Microsoft Universal Data Access, a central part of the Microsoft Windows DNA strategy, provides data access services for Windows DNA applications.

Windows DNA addresses requirements at all tiers of modern distributed applications: user interface and navigation, business process, and integrated storage. The core elements of the Windows DNA architecture are as follows:

  • Pluggable software components
  • Extensible Web browser and application server
  • Richly integrated platform services
  • Scalable distributed operating environment and servers
  • Open protocols and published interfaces
  • Choice of programming languages, tools, and hardware platforms

Because Microsoft Universal Data Access is based on COM, it provides a unified, consistent, and common data access model for all applications built to the Windows DNA model.

Solutions Built with Universal Data Access Components Are Reliable

Reliability is a primary requirement for organizations managing and supporting data access applications. Universal Data Access aims to address this need in three areas:

  • Increasing the manageability of client-side components
  • Enabling strong server-side coordination and control
  • Delivering well-tested components

The net result of the Universal Data Access architecture's three-pronged approach should be significant reductions in configuration and support expenses and a reduced total cost of ownership—in short, a reliable data access architecture.

Increasing the Manageability of Client-Side Components

One of the most important ways that organizations can increase reliability and decrease support costs is by reducing the number of components to support on client PCs. The Universal Data Access strategy and the Microsoft Data Access Components technologies support this approach by offering the following advantages:

  • Universal Data Access supports new multitier and Web deployment models, in which data access logic and business logic are centralized on middle-tier servers. Front ends provide presentation services by using browser-based interfaces or by using custom and packaged applications. In this model, application functionality is mainly centralized, not distributed to end-user PCs, thus reducing the number of components to manage on those machines.
  • Microsoft Data Access Components 2.5, which includes OLE DB and ADO, shipped as part of the Microsoft Windows 2000 operating system, and updates to MDAC will be treated like an update to the operating system. Because OLE DB and ADO ship like system components, organizations can rely on these components being available in a run-time environment and don't have to manage their distribution and maintenance.
  • ADO is tool and language independent and in many cases may be able to replace multiple data access libraries on client PCs. Organizations that may have previously supported DAO, RDO, and ODBC on each PC as part of a two-tier system can now get the same functionality by deploying Microsoft Internet Explorer to their clients and using a three-tier architecture. Data access functions previously handled by ODBC clients will move into their middle-tier objects or their servers. This means that Internet Explorer with ADO can replace DAO, RDO, and ODBC on client PCs. By consolidating your data access code to use only ADO, your code will be easier to maintain, which makes it more reliable.

Enabling Strong Server-Side Coordination and Control

Universal Data Access enables transactional control of diverse data sources and components by using Microsoft Component Services (or MTS, if you are using Microsoft Windows NT®). To achieve this, OLE DB data sources must implement the functionality of a resource manager, which handles transactions local to the data source and enables each data source to participate in distributed transactions. Microsoft Component Services provides a distributed transaction coordinator that guarantees atomic operations spanning multiple data sources, using a reliable, two-phase commit protocol, and enables applications to scale, with minimal additional effort, as the user load grows.

Delivering Well-Tested Components

MDAC components are rigorously tested for reliability. Because ADO and OLE DB have been shipping in high volume since the release of Microsoft Internet Information Server 3.0, they have enjoyed significant field usage. With the advent of MDAC, and the commitment to ship ADO and OLE DB in a synchronized fashion, these components are now developed and tested side by side.

After testing the components for interoperability, to guarantee reliable behavior in multithreaded, continuous-operation conditions in highly concurrent environments, Microsoft stress-tests the MDAC technologies with the products with which they ship (such as Internet Information Server and Internet Explorer). This stress-testing is designed to help ensure high-performance, highly reliable components that work well in a variety of real-world scenarios.

Microsoft Commitment to Universal Data Access

The choice of data access technologies is extremely strategic for organizations. Typically, internal factors, such as an organization's existing DBMS, largely drive this choice. However, customer surveys have indicated that data access decisions are made for the long haul and therefore need to be considered carefully.

Following are some of the questions that customers want answered when evaluating a data access vendor:

  • Can vendor support be depended on, not just for the current release but through a string of releases?
  • Does the vendor have a strong and compelling strategy for data access, and does that strategy mesh with the goals of my organization?
  • Has the vendor withstood the test of time by consistently delivering strong products in these markets?
  • Has the vendor succeeded in these markets, thereby proving its ability to deliver technological solutions and build market momentum, resulting in broad support for the technologies?

Based on market share, developer acceptance, and broad industry support for many technologies, Microsoft answers "Yes!" to all these questions. Microsoft has consistently met this set of criteria and has proved to be a market leader for data access technology.

A Short History of Microsoft as a Data Access Vendor

A look at how the Universal Data Access strategy has evolved at Microsoft will help illuminate the long-term commitment the company is making in this area.

Microsoft began investing in data access shortly after the initial release of Microsoft SQL Server 1.0 in 1989. Initial interest in Microsoft SQL Server was high, but the tools available to program it were limited. The SQL standard was in its infancy but was clearly bound to the coming client/server revolution. Microsoft knew that acceptance of client/server architecture would be highly beneficial and could see that the biggest problem the industry faced was the proliferation of data access interfaces—and the complexity of creating, maintaining, and programming against them.

Microsoft's solution to these problems was Open Database Connectivity (ODBC). The following important features made ODBC extremely attractive:

  • It was SQL-based, making it familiar to most database developers.
  • Its call-level interface supported a broad range of tools.
  • Its plug-in model for database drivers simplified implementation.

As ODBC gained broad support as a standard for data access, it became clear that a standards body should be defining its future. Microsoft turned over the specification for ODBC to the SQL Access Group, made up of a broad range of DBMS, middleware, and tools vendors.

Despite its advantages, ODBC had a number of shortcomings, which by 1993 were being addressed in the next phase of data access market development. ODBC was programmed as a Windows-based API, which made it difficult for the majority of customers to use it. A number of Microsoft tools and applications could use ODBC through the Microsoft Jet database engine, but ODBC functionality was not extensible except for a few API-level programmers. Therefore, high-level programming models were created—first Data Access Objects (DAO) and then Remote Data Objects (RDO)—that simplified the ODBC programming model and made it accessible to a wider range of programmers.

DAO provided Microsoft Access and Microsoft Office programmers with an interface to the Microsoft Jet database engine, and RDO provided higher-level interfaces to ODBC for programmers using the Microsoft Visual Basic® programming system. These interfaces were natural extensions to the Visual Basic language used in each of these products and have gained broad usage among database programmers.

By 1995, two major new trends began to shape the next phase of development. These two trends, which are still evolving, were the rise of the Internet as a database applications platform and the rise in importance of nonrelational data, which does not directly fit the database model encapsulated by ODBC.

The Internet presents new data access challenges on many levels, including as the following:

  • Scale. The Internet is synonymous with more people accessing more data. This amplifies the priorities for effective data management: Performance, reliability, and security take on new importance.
  • Higher volume of clients. Because of the high volume, the Internet needs a better paradigm to share server-side resources.
  • Distributed medium. The Internet is by definition a distributed medium, with many more potential points of failure and network performance issues that go beyond the boundaries typically relevant to information technologists. On the flip side, the medium presents many opportunities to distribute processing tasks for improved service.
  • Casually connected clients. Client and server, previously bound tightly in time and space, are now virtually independent. Client computers need to be able to accomplish more with less support from the server and, at times, without a server connection.
  • New business opportunities. The Internet opens up the possibility for employees, customers, suppliers, and business partners to connect with each other more simply, rapidly, and thoroughly than ever before.
  • New types of data. The Internet, while using existing data and databases in new ways, is also composed of massive amounts of text, images, and other media that continues to grow in importance. Successful organizations will need to manage new data types effectively and synthesize meaning from them.

As mentioned above, while the Internet catalyzes a major paradigm shift in database management and data access, a related shift is occurring: the emergence of nonrelational data sources. While the Internet highlights the need for management of textual and graphical data, organizations today also face a proliferation of data in a variety of DBMS and non-DBMS stores, including desktop applications, mail systems, workgroup and workflow systems, and others. Most established organizations face an even larger challenge: leveraging the data in mainframe and minicomputer flat files as they extend access to this information to intranet-based and Internet-based customers.

Data access today encompasses all of the issues traditionally addressed by DBMS systems, plus a range of new data types, new clients, and new access methods. Therefore, with its Universal Data Access strategy, Microsoft has taken another step forward to meet this new generation of challenges, leveraging the successful strategies of the past and embracing the architectures of the future.

Microsoft's Continued Commitment to the MDAC Technologies

ODBC continues to be a supported technology under the Universal Data Access umbrella. Due to the high number of drivers available, ODBC in the short and medium term is the best way to access a broad range of relational DBMS-based data. With ODBC remaining as a mature technology and OLE DB components becoming available, Microsoft does not want to force customers to choose between the two architectures and lose the thousands of work-years that were invested worldwide into ODBC-based software, or lose access to the newer features available only through OLE DB. The goal is to enable customers to take advantage of existing ODBC technologies while adopting the Universal Data Access architecture for new applications.

The most frequent customer issues surrounding ODBC are related to performance and configuration management, defined as matching database drivers on multiple machines with multiple back-end data sources. Microsoft is aware of these issues and will continue to address them through subsequent ODBC releases, including a new and significantly improved ODBC driver for Oracle.

It was by design, in response to consistent customer feedback, that the evolutionary strategy for migrating from ODBC to OLE DB was created. The very first OLE DB provider released by Microsoft was the OLE DB Provider for ODBC, allowing applications to be written to the ADO or OLE DB interface, with the OLE DB Provider for ODBC then connecting to the ODBC data source. So now if an organization wants to change data sources, add data sources, or change from the ODBC driver to a pure OLE DB provider for the existing data source, the existing database application can be adapted with minimal changes.

This strategy for migrating from ODBC to OLE DB carries some additional important benefits: OLE DB and ADO service component features may be invoked against ODBC data. For example, the ADO Find method provides for sorting and filtering within a result set. Therefore, the result set can be reused and further refined, without an additional round-trip to the server, a capability unavailable to an ODBC client. This means that both new and existing applications can gain additional data access features by using OLE DB to call broadly supported ODBC drivers.

Organizations should continue to plan on broad availability and support for ODBC drivers. And as they build new applications, they should look to the Universal Data Access architecture, using ADO and OLE DB interfaces. For relational data, organizations may choose between ODBC drivers and, as they become available, OLE DB providers and components. And because of the ability of OLE DB providers to expose nonrelational data, Microsoft believes customer demand will drive the market for OLE DB components and that they, too, will become broadly available. In the long term, organizations will benefit from the highest possible application performance and reliability while gaining new capabilities at a pace that suits their unique requirements.

Universal Data Access Is Strategic for Microsoft

The Universal Data Access strategy is intertwined with most of the major lines of business where Microsoft is serving organization customers, including operating systems, tools, applications, and Internet products. Universal Data Access is designed to work consistently across each of these major product lines, enabling organizations to leverage their data access expertise across teams and projects to build high-performance database solutions accessible to employees, customers, and business partners.

Also, making integrated access to all forms of relational and nonrelational data ubiquitous is strategic for Microsoft products because it enables those products to create valuable solutions using tools that use the Universal Data Access architecture. Customers are the ultimate beneficiaries as their tools and applications become more highly adept at processing the information they work with every day.

Broad Industry Support for Universal Data Access

To justify investment in the Universal Data Access architecture, organizations using data access components want to see the support of vendors of related products and technologies. For customers, broad industry support carries many benefits—safety in numbers, availability of skilled people to work with the products, and products that work together without expensive integration and customization.

The industry reception for Microsoft's Universal Data Access strategy has been very positive. This section details the activities in which Microsoft is engaged to solidify and publicize the broad range of companies supporting Universal Data Access.

The key industry segments supporting Universal Data Access are as follows:

  • DBMS vendors. These vendors benefit from Universal Data Access by gaining additional high-performance clients for their engines. Their customers benefit by gaining a broader choice of development tools and other supporting technologies and by connecting to and integrating with data from more sources. The ability to connect to data from more sources is very attractive to most customers; vendors that support Universal Data Access are more likely to attract and keep those customers.
  • Development tools vendors. Tools vendors benefit from Universal Data Access because it allows them to more efficiently provide access to a broader range of data sources, enabling their customers to build richer, more functional applications. Leading tools vendors are participating by incorporating MDAC, enabling their customers to create applications that access the vast majority of data sources available today.
  • Data access component builders. By building OLE DB components, data access component builders gain the benefits of a broadly accepted standard environment in which to deploy their products. Their customers can access more data, find more readily available support, and interoperate with a broad array of products across the platform. This increases the likelihood of widespread acceptance of the OLE DB components.

The list of vendors in each of the above categories is growing rapidly. Visit the Universal Data Access Web site for a list of some major vendors. Leading vendors in each industry segment are represented in the list of Universal Data Access supporters.

OLE DB Provider Strategy

To be successful, OLE DB must gain a broad array of native providers and components so that users can connect to practically any data source, reuse OLE DB service components, and realize performance and reliability benefits.

The tools that OLE DB provider and component vendors use to simplify their work are found in the MDAC SDK. In addition to the data access consumer components discussed in this paper (ADO, OLE DB, and ODBC), users of the SDK receive additional tools, documentation, and specifications to help them create high-performance OLE DB components. Provider writers will find the following:

  • OLE DB Minimum Levels of Consumer and Provider Functionality specification. Defines the minimum level of functionality that should be implemented by every provider. In addition, it defines the level of functionality that most consumers will expect and that should be implemented by a provider when supported natively by the data source. To tie these together, it also defines the services that are likely to be supported by various classes of service components.
  • Conformance tests. Includes interface tests to show that a provider correctly implements the interfaces that it supports. Also includes a minimal set of ADO tests that indicate whether a provider works well in an ADO application. The initial focus of these tests is on the minimum provider interfaces as defined in the leveling specification ("OLE DB Minimum Levels of Consumer and Provider Functionality" in the MDAC Technical Articles section).
  • OLE DB Simple Provider (OSP) Toolkit. Helps you quickly build providers for tabular and other simple data.
  • OLE DB providers. Custom providers for ODBC, Microsoft Jet, SQL Server, and Oracle data sources.

These tools simplify the process of writing OLE DB components, provide a framework for creating components that interoperate in well-defined ways, and provide criteria by which OLE DB consumers can easily compare component features. Anyone interested in creating OLE DB components should obtain the MDAC SDK.

Note Another useful tool for provider developers is the set of Microsoft Visual C++® development system template classes for OLE DB providers. These are a part of Visual C++ and are not actually included in the MDAC SDK.

How Universal Data Access Supports Data on Multiple Platforms

While the Microsoft Windows NT/Windows® 2000 operating system is emerging as an important platform for database management, many organizations rely on a mixture of operating systems and database platforms. To be successful, any strategy for providing data access must be able to efficiently access data on all major platforms. To that end, Universal Data Access provides the foundation for supporting efficient and reliable access to data on today's major computing platforms. Microsoft is actively engaged in supporting third-party development projects involving OLE DB providers for non-Windows-based data. Products using the Universal Data Access architecture to access leading DBMSs on non-Windows platforms are currently available from many companies, including International Software Group (ISG), MERANT, and Simba Technologies.

Because the OLE DB specification defines interfaces that components support, rather than providing a set of DLLs or actual system components, it is highly portable to other operating environments. It might be assumed that OLE DB components must run on a Windows-based PC—but this is not the case. OLE DB is based on the COM architecture, which is not proprietary to the Windows platform. OLE DB has two separate approaches that provide portability to non-Windows-based DBMS platforms:

  • A full port of COM, available today from several vendors
  • Implementations of COM interfaces on non-Windows-based platforms

The broad availability of MDAC components that integrate data on multiple platforms will benefit organizations that support multiple DBMS platforms. Users can continue to take advantage of new OLE DB capabilities, even when accessing non-Windows-based data. Powerful new service components, running on non-Windows platforms—on front ends or middle-tier servers—can be integrated with an OLE DB provider. For example, general-purpose query processors, cursor engines, or custom business objects can all add value to non-Windows-based data exposed by OLE DB. Mainframe and UNIX-based databases that previously did not support remoting of data—an essential feature for the Internet and loosely connected scenarios—may now implement it, gaining greater use from existing systems and applications.

How Universal Data Access Differs from Other Strategies

A number of leading DBMS vendors have begun shipping new databases and updated versions that follow data-centric database strategies—that is, consolidation of the dispersed data into a single database. Customers may be curious about how those strategies differ from Universal Data Access.

In the other approaches, data from across the organization is consolidated in the DBMS, and the DBMS is extended with additional processing capabilities to handle new data types. This strategy can be attractive for several reasons:

  • It centralizes data for more efficient management.
  • It places a DBMS "wrapper" around new data types, which can ensure the protection of DBMS security and transaction services while offering other potential benefits such as content indexing.
  • Many DBMSs are very efficient at serving up data, so organizations can generally expect good performance for applications based on traditional and nontraditional DBMS data.

Microsoft, while recognizing these benefits, believes they may be difficult for some organizations to attain. A data-centric database approach may require expensive and time-consuming movement to, and maintenance of, corporate data in the DBMS. It may require new, tightly coupled tools and applications to support it. And it may require compromises in the selection of supporting products. Customers' applications will need to either redesign their existing systems to implicitly support this architecture, which is unlikely, or be customized to integrate with it, which could be expensive.

Because the Universal Data Access strategy does not exclude any data stores, it can cooperate with the data-centric model if necessary. In fact, OLE DB providers for a number of new data-centric database products are currently under development. Customers can use the Universal Data Access strategy to access data in their existing databases, data-centric database servers, desktop applications, mainframes, and so forth. Organizations that combine Universal Data Access and data-centric database products where they are appropriate will ultimately benefit from a broad choice of best-of-breed tools, applications, and DBMS products available from leading data access vendors.

Choosing the MDAC Strategy That's Best for You

The following sections are intended to help you, the developer or information management professional, choose the data access strategy that is best for your organization and your customers.

OLE DB vs. ODBC

ODBC has been and remains a very important and successful data access standard. The ODBC technology and third-party market have matured to a point where ODBC is an ideal technology for accessing SQL databases. On the other hand, OLE DB has an improved architecture that provides a significant advantage over ODBC because providersno longer have to implement a SQL relational engine to expose data. An integral part of OLE DB is an OLE DB provider that enables OLE DB consumers to talk to ODBC drivers.

With ODBC, services such as cursoring and query processing need to be implemented by each ODBC driver writer. This represents overhead and possible inconsistency for both for the ODBC driver author and their end users. With OLE DB, reusable service components handle the processing chores for a variety of data providers.

OLE DB simplifies the process of writing data providers, which means they should be developed faster and be of a higher quality. It also reduces the number of components installed on data consumer machines.

Use the following tips to guide your choice of which technology to use:

  • If you are accessing standard relational databases from a non-OLE environment, ODBC is the best choice.
  • If you want to expose a data interface to nontabular data or use data from a source that does not support SQL, OLE DB is the best choice.
  • If you are programming in a COM environment, OLE DB is the best choice.
  • If you want to build interoperable database components, OLE DB is the only choice.

Technical differences between ODBC and OLE DB are summarized in the following table.

ODBC | OLE DB
Data access API | Database component APIs
C-level API | COM API
SQL-based tabular data | All tabular and multidimensional data
SQL-based standard | COM-based standard
Native providers provide all features | Component architecture allows reusable components to provide common features

Special Features and Benefits of OLE DB

As application developers move to solutions designed as reusable components, OLE DB enables business-centered components to behave and communicate like mini-databases, both as data consumers of other data sources and as providers. This capability is the basis for new, simpler ways to build applications based on components.

  • Access to all your corporate data regardless of storage format or location

    This capability extends across corporate data as well as Internet data, and across all types of data providers beyond SQL databases.

  • Simplified programming model for corporate developers

    When a developer uses a tool or language that supports OLE DB, different data sources can behave as a single, homogeneous source. From ADO, developers can create business applications that link many data sources.

  • Interoperable data-centered components

    Business components can excrete data change events, consume OLE DB data, and provide OLE DB data. In this way, business components can perform very complex processing and synchronize with other components while exposing simple, table-like interfaces.

  • Components that act as virtual tables for quicker application development

    Most graphical development tools automate loading OLE DB result sets from queries directly into user-interface controls. This technique is called data binding. Without OLE DB, more programming is required to access the result set and load the user-interface controls. Additionally, OLE DB supports events, or callback interfaces, which allow these controls to be more responsive and efficient.

  • Integration with the Microsoft suite of application development products and technologies

    OLE DB is a useful technology in its own right, but it becomes even more compelling as the broad suite of Microsoft enterprise tools and technologies integrate and extend one another's capabilities.

  • Full integration with ODBC

    OLE DB data consumer tools and languages have full access to all ODBC drivers and ODBC-based data.

ADO vs. DAO or RDO

ADO is now the standard data access language for Microsoft tools. The current versions of Internet Information Server, Internet Explorer, Visual Basic, Visual InterDev®, Visual C++, and Microsoft Visual J++®, have all been written to use ADO as their primary data access language.

Among the many benefits of ADO is a common language for accessing data. Whatever tool you are using, you can use the same code to query and manipulate data. This allows for much greater and easier code reuse across applications than was possible in the past.

Therefore, if you are designing an application now, you should use ADO, unless there are features you need that are not available in ADO but are available in one of the alternative technologies. Be sure to look at the features that are available through the ADO Extensions for Data Definition Language and Security (ADOX) library and the Jet and Replication Objects (JRO) library for features that were previously available only through DAO.

If you are using DAO or RDO, you should still think about how to move over to ADO when it supersedes these. That way, when the time comes, you will have an easier job migrating to ADO.

Following are scenarios in which you might still want to use DAO:

  • You have an existing application that uses DAO with Microsoft Jet, and you want to convert the application to use ODBC data to achieve better performance.
  • You need to create specific user and group PIDs when managing security access in a Microsoft Jet database.
  • You need security control over Microsoft Access objects stored in a Microsoft Jet database.
  • You need to lock a table exclusively in a Microsoft Jet database.
  • You need to retrieve implicit permissions on objects, in addition to explicit permissions.
  • You need to identify indexes that result from the creation of a foreign key in a relationship.

Following are scenarios in which you might still want to use RDO:

  • You are developing in Visual Basic 5.0, and you want to take advantage of binding controls with RDO.

ADO, DAO, RDO Feature Comparison

To help you decide which technology to use and also to determine if ADO meets your needs today, the following table presents a list of major features found in ADO, DAO, and RDO. ("X" indicates that the feature is present.)

Feature | ADO 2.6 | DAO 3.6 | RDO 2.0
Asynchronous connect | X |  | X
Queries run asynchronously | X |  | X
Batch updates and error handling | X | X | X
Disconnected recordsets | X |  | X
Events | X |  | X
Integration with data binding in Visual Basic 6.0 | X |  |
Integration with Visual Basic/Visual C++ Transact-SQL Debugger |  |  | X
Data shaping | X |  |
Persistent recordsets (as binary or XML) | X |  |
Distributed transactions | X |  | X
Thread-safe | X | X | X
Free-threaded | X |  |
In/out/return value parameters | X | X | X
Independently created objects | X | X1 | X
Queries as methods | X |  | X
Multiple recordsets returned | X | X | X
Efficient Microsoft Jet database access | X2 | X |

1 DDL objects

2 Using the OLE DB Provider for Microsoft Jet


Pooling in the Microsoft Data Access Components

May 1999

By Leland Ahlbeck and Don Willits

This article discusses the benefits, features, and behavior of pooling within the context of the Microsoft® Data Access Components (MDAC) architecture, including technology-specific tips for OLE DB, Microsoft ActiveX® Data Objects (ADO), and Open Database Connectivity (ODBC) developers. (Both ODBC and OLE DB support pooling of database connections, although the exact behavior and features differ slightly.)

The article begins with a general discussion of what pooling is and the benefits it provides. Next, the following topics are discussed in detail, with "how-to" code examples:

  • ODBC connection pooling
  • OLE DB resource pooling
  • Troubleshooting MDAC pooling
  • The MDAC pooling toolkit

Introduction

The Microsoft Data Access Components architecture provides a universal framework for exposing both traditional SQL-based database sources and non-SQL data stores such as documents or multidimensional sources. The architecture requires nothing of data except that it can be exposed in tabular form from an OLE DB data provider or ODBC data source. Microsoft ActiveX Data Objects (ADO), Remote Data Service (RDS), or even OLE DB itself can then expose that data to the consumer application.

Pooling enables an application to use a connection from a pool of connections that do not need to be reestablished for each use. Once a connection has been created and placed in a pool, an application can reuse that connection without performing the complete connection process.

Figure 1 shows the MDAC architecture and how pooling fits into it.

Figure 1. The MDAC Stack of Components and Pooling

For either ODBC or OLE DB, pooling creates and maintains a group of connections to a database or other data store and hands them out to consumers requesting a connection to that data store. As applications open connections to a given data store and subsequently release them, a pool of open connections is built with complete authentication information and connection properties. Then, if available, connection requests to the same data store (with the same user authentication and connection properties) are satisfied from the pool rather than by making the connection on demand. Connections are held open for a period of time after a consumer application has released them. They are released by the pooling mechanism when they time out.

The main benefit of pooling is performance. Making a connection to a database can be very time-consuming, depending on the speed of the network as well as on the proximity of the database server. When pooling is enabled, the request for a database connection can be satisfied from the pool rather than by (re)connecting to the server, (re)authenticating the connection information, and returning (again) a validated connection to the application.

Pooling is available in two forms for applications that use the Microsoft Data Access Components: ODBC offers connection pooling through the ODBC Data Source Administrator, and OLE DB core components provide resource pooling as well as additional services such as shaping and the client-side cursor.

You cannot use both ODBC connection pooling and OLE DB resource pooling for a given connection to the database. You should choose which form of pooling you want to use and stick with it exclusively within a given application. However, you can disable OLE DB resource pooling and use connection pooling if you are using the OLE DB Provider for ODBC.

ODBC connection pooling was first introduced with ODBC version 3.0. With the release of MDAC 2.0 (which included OLE DB 2.0 and ODBC 3.51), OLE DB resource pooling was introduced and was available to any provider that supported COM aggregation and the OLEDB_SERVICES registry key. However, by default, resource pooling for the OLE DB Provider for ODBC was disabled, so any ADO or OLE DB data consumer using this provider actually ended up using ODBC connection pooling. This changed with the release of MDAC 2.1, and the OLE DB Provider for ODBC now uses resource pooling by default. If you prefer to use connection pooling, you can still disable resource pooling for the OLE DB Provider for ODBC.

In this article, user refers to someone who makes a connection with a specific set of credentials used to access the database. In this case, user does not refer to the logon credentials—such as the Microsoft Windows® domain account and the password used to log on to Microsoft Windows 2000—used to access an operating system. The logon credentials for the MDAC user include both the connection string for ADO and any properties or attributes set prior to connection. However, pooling in MDAC also recognizes attributes you set in ODBC prior to the connection as well as properties set in OLE DB or ADO beyond the actual connection string used. Below is an example of two almost identical connection strings, with each resulting in different user authentication:

"DSN=LocalServer;UID=sa;PWD=;DATABASE=Pubs"
"DSN=LocalServer;UID=sa;PWD=;DATABASE=QFE"

In addition, setting the value of dynamic ADO properties can affect user authentication. Consider the PROMPT property, which corresponds to the OLE DB property DBPROP_INIT_PROMPT and from there maps to the DriverCompletion argument of the ODBC SQLDriverConnect function. Changing the value of this property between two different connection objects that otherwise share an identical connection string will create two different user authentications.

Benefits of Pooling

Any application that makes frequent calls to the data store for connections can benefit from pooling. Figure 2 shows how both a typical Web-based and a three-tier application use pooling. Notice that multiple connections to the database are open. Further, notice that a given connection is either in a pool for OLE DB providers or in a pool for ODBC drivers, but not both. When a user releases a connection, it is returned to the pool rather than being released. The connections are freed when the database activity and the load on the Web server drops.

Figure 2. Benefits of Pooling

In developing an application that will run in a Web-based or multi-tier environment, pooling becomes very important. Making connections to the database can be one of the application's most time-consuming activities. Maintaining connections to the database in the resource state of the Web server can create scalability problems because all users are forced through the same connection object (not to mention that Web servers are almost by definition "stateless"). Opening a new connection on every page of a Web server is bad because it's slow. MDAC pooling provides a way to get the best of both scenarios: a limited number of connections (just enough to match your system's current load) without introducing a scalability bottleneck.

ODBC Connection Pooling

When an application makes its database connections by using ODBC database drivers, the connections are cached through the ODBC connection pool. When the first request for a connection to a database is made, the connection passes through the pooling components and then user information and any properties configured prior to making the connection are cached. The connection request is next passed on to the back-end server, and a live connection is handed to the user to satisfy the connection request. The rest of the application will be unaffected by pooling.

When the application releases the connection, the pool returns S_OK to the user, simulating a successful disconnection from the database. However, the actual connection is not released by ODBC but instead waits in the pool. When the next request for a connection to the database that has the same data source and user information comes in, it is satisfied from the connection in the pool rather than by making a full connection to the database. However, if the connection has already timed out or if there are no connections in the pool matching the request, a new connection is opened. In this respect, connection pooling is transparent to the calling application; the exception to this is pool configuration, which will be covered in the section "Configuring ODBC Connection Pooling."

Figure 3. ODBC Connection Pooling and Web/Multi-Tier Applications

To take advantage of connection pooling, a driver must be thread-safe. (If you are unsure about whether a driver is thread-safe, check with the manufacturer.) For an ODBC driver to be thread-safe, the underlying components it uses must be thread-safe as well. For example, an Oracle ODBC driver relies on SQL*Net and the Oracle Call Level Interface, and the Microsoft Access ODBC driver relies heavily on both the Microsoft Jet database engine and the Microsoft Visual Basic® for Applications Expression Service. Earlier versions of these drivers were not thread-safe because either they or their underlying components were not thread-safe. Any previous version of either driver will not work correctly. However, the ODBC driver for Oracle shipping with ODBC 3.5 (MDAC version 1.5 and later) and the ODBC driver for Access shipping with ODBC 3.51 (MDAC version 2.0 and later) are both thread-safe.

Finally, the behavior of ODBC connection pooling can be programmatically controlled by an ODBC-based application. That is, the scope of the pool can be configured to be one pool per environment handle or to be driverwide. (If you develop using the OLE DB Provider for ODBC, this cannot be configured. Only if you use the native ODBC API can you modify this behavior.) If configured by environment handle, multiple connections to different drivers can be created off that handle and all of these connections will share the same pool.

Configuring ODBC Connection Pooling

Connection pooling can be enabled or disabled from the following locations:

  • The ODBC Data Source Administrator—At the Data Source Administrator level, pooling can be enabled or disabled for the entire driver. This includes features such as whether pooling is on, the time-out value, and the Wait Retry setting. Unless you are writing a native ODBC application, you need be concerned only with the Data Source Administrator.
  • The registry—Before the Data Source Administrator offered support for connection pooling in ODBC 3.5 (MDAC 1.5), you needed to use the registry to configure ODBC connection pooling.
  • The calling application—From an application level (native ODBC application only), your application can make calls to the Data Source Administrator to tell it whether you want pooling and the type of pooling you want.

Within ODBC connection pooling, the following two configurable settings can affect how pooling works. Both can be set by editing the registry directly, but with ODBC 3.5 (MDAC 1.5) and later, you can use the ODBC Data Source Administrator, which is the preferred method.

  • The CPTimeout value—This value indicates the amount of time that a connection remains unused in the pool before the connection is released. (We release the connections to avoid "connection creep," which will eventually lead to the server refusing any new connection requests when it hits its limit on available connections.)
  • The Wait Retry value—This value, expressed in seconds, is the length of time that the connection pool will be blocked when it is determined that the server is not responding.

In a multithreaded environment, blocking the pool avoids a situation such as the one described in the following example.

Suppose there are no connections in the pool when the first user tries to get a connection to the server that is not responding. Now 10 more users try to get their own connection to the same server, creating a race condition where the server and network are flooded with connection requests, none of which can succeed. In this situation, the Wait Retry setting helps to keep traffic on the network down and gives the server a chance to respond to the connection request. Therefore, if the server is not responding, the pooled connection for that specific user information is blocked and E_FAIL is returned to any request for a connection. Then, depending on the value of Wait Retry, a connection to the server is retried every n seconds.

The value of the Wait Retry setting will apply to all drivers on your system, not just the one you are configuring it for in the Data Source Administrator. The Wait Retry setting is stored in the registry under HKEY_LOCAL_MACHINE/SOFTWARE/ODBC/ODBCINST.INI/ODBC Connection Pooling. Changes to this setting will not take effect until the pool is released. It might be necessary to reboot your machine to put your changes into effect.

Connection pooling can be configured on a per-driver basis. For example, you can have pooling enabled for one driver and off for another driver. By setting the connection time-out value to 0 in the registry for a specific driver, pooling will be disabled for that driver. Conversely, setting CPTimeout to a nonzero value will turn on pooling for that driver.

Configuring Connection Pooling from the Data Source Administrator

For ODBC version 3.5 (MDAC 1.5) or later, in the ODBC core components connection pooling can be configured from the Data Source Administrator (or ODBC Data Source Administrator). The ODBC Data Source Administrator is found in the Control Panel of Microsoft Windows 95, Microsoft Windows 98, and Microsoft Windows NT® 4.0. With Windows 2000 (available in beta only at this writing), the location has moved to Administrative Tools in the Control Panel.

Alternatively, you can start the ODBC Data Source Administrator by entering Odbcad32 at the Run prompt: Click Start and then click Run.

The tab for managing connection pooling is found on the ODBC Data Source Administrator dialog box in version ODBC 3.5x (MDAC 1.5 and later).

Configuring Connection Pooling from the Registry

For versions prior to version 3.5 of the ODBC core components, you need to modify the registry directly to control the connection pooling CPTimeout value. To do so, start the registry editor and locate the CPTimeout value for your driver in the appropriate registry key. For example, the CPTimeout value for SQL Server is located in the registry under HKEY_LOCAL_MACHINE/SOFTWARE/ODBC/ODBCINST.INI/SQL Server. You can find the CPTimeout value for all drivers in the registry under the HKEY_LOCAL_MACHINE/SOFTWARE/ODBC/ODBCINST.INI/Driver_Name key.

The default time-out value for connection pooling is 60 seconds, and this value should be adequate for most installations.

The value setting for Wait Retry will apply to all drivers on your system, not just the one you are configuring it for in the Data Source Administrator. This Wait Retry value is stored in the registry under HKEY_LOCAL_MACHINE/SOFTWARE/ODBC/ODBCINST.INI/ODBC Connection Pooling. Changes to this setting will not take effect until the pool is released, and you might need to reboot your machine to put your changes into effect.

Warning Using the registry editor incorrectly can cause serious, systemwide problems that may require you to reinstall your Windows operating system to correct them. Microsoft cannot guarantee that any problems resulting from changes you make to the registry by using a registry editorcan be solved. Use this tool at your own risk.

Configuring Connection Pooling from Your Application

As discussed earlier, pooling can be limited to a scope of the environment handle or the driver. Your application controls this by calling SQLSetEnvAttr with either SQL_CP_ONE_PER_DRIVER or SQL_CP_ONE_PER_HENV.

In Code Example 1, the console application is being set up to pool along with the rest of the application on the machine by setting the pooling value to SQL_CP_ONE_PER_DRIVER. This code demonstrates how to enable ODBC connection pooling.

Note Other options for connection pooling are: SQL_CP_OFF, which disables connection pooling; SQL_CP_ONE_PER_HENV, indicating one pool per environment handle; and SQL_CP_DEFAULT, which is currently mapped to SQL_CP_OFF.

Code Example 1: Enabling ODBC Connection Pooling

HENV       henv       = SQL_NULL_HENV;
HDBC       hdbc       = SQL_NULL_HDBC;
SQLHSTMT   hstmt      = SQL_NULL_HSTMT;
RETCODE    rc         = SQL_SUCCESS;

rc = SQLSetEnvAttr( SQL_NULL_HENV,
                   SQL_ATTR_CONNECTION_POOLING,
                   SQL_CP_ONE_PER_DRIVER,
                   0 );
rc = SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HENV, &henv );
rc = SQLSetEnvAttr( henv,
                   SQL_ATTR_ODBC_VERSION,
                   (SQLPOINTER) SQL_OV_ODBC3,
                   SQL_IS_INTEGER );
rc = SQLAllocHandle ( SQL_HANDLE_DBC,
                     henv,
                     &hdbc );

for ( long i = 1; i<= nCount && ODBCSUCCESS( rc ); i++ )
{
     rc = SQLConnect( hdbc,
                    (SQLCHAR*) lpszDSN, SQL_NTS,
                    (SQLCHAR*) lpszUid, SQL_NTS,
                    (SQLCHAR*) lpszPwd, SQL_NTS );

     rc = SQLAllocHandle( SQL_HANDLE_STMT, hdbc, &hstmt );

     if ( hstmt ) SQLFreeHandle( SQL_HANDLE_STMT, hstmt );
     if ( hdbc )  SQLDisconnect( hdbc );
}

if ( hdbc ) SQLFreeHandle( SQL_HANDLE_DBC, hdbc );
if ( henv ) SQLFreeHandle( SQL_HANDLE_ENV, henv );

Enabling Connection Pooling

For Internet Information Server (IIS) version 3.0, pooling was set to off by default and you will need to manually turn on connection pooling. For IIS version 4.0 and later, pooling has been set to on by default. (IIS 3.0 used ODBC 3.0, and IIS 4.0 used ODBC 3.5.) If you are coding by using the ActiveX Data Objects (ADO) object model to the OLE DB Provider for ODBC (MSDASQL), connection pooling will be turned on for you. If you are not using ADO and are coding directly to the ODBC API, you will need to turn on pooling yourself. The section "Configuring ODBC Connection Pooling," earlier in this document, provides details on how to accomplish this.

As mentioned in the "Introduction" section, when OLE DB resource pooling first appeared in MDAC 2.0, it was by default disabled for the OLE DB Provider for ODBC. Therefore any ADO or OLE DB data consumer using this provider implicitly used ODBC connection pooling. With the release of MDAC 2.1 (and later versions), this changed and now the OLE DB Provider for ODBC uses OLE DB resource pooling by default. You can easily restore ODBC connection pooling by changing the registry setting of OLEDB_Services for the CLSID of the OLE DB Provider for ODBC. See Table 2, later in this document, for a list of values that can exist in the OLEDB_Services registry entry.

Determining the Number of Available Pools

Connection pooling allows more than a single pool. The number of pools is equal to the number of processors. This helps to avoid conflicts when multiple threads are in use on the server. With only one pool and 50 threads running, there would be locking conflicts on the pool as the pools were locked for updating. However, with multiple pools available, the request for a connection simply moves to the next pool if the first pool is locked. (Note that a given pool can have connections to different data sources for one or more different drivers.)

When a request for a new connection comes into the pool, the pooling logic must look through the available connections for an existing connection that matches the properties of the connection attributes used to request the connection. This is accomplished by finding a pool that is not locked, locking the pool, and looking at each available connection in the pool to determine whether there is a match between the pooled connection information and the requesting connection. If a match is found, the pooled connection will be handed to the application requesting the connection and the pool will be unlocked. If a connection with the proper connection information cannot be found, a new connection will be created.

Monitoring Connection Pooling

ODBC connection pooling activity can be monitored through the Performance Monitor in Windows NT 4.0. Keep in mind that this tool should be used to check pooling on an occasional basis and not be left on indefinitely: It can and will have an adverse effect on system and pool performance, even if Performance Monitor isn't actually running. (For ODBC version 3.51, pool monitoring can be enabled from within the ODBC Data Source Administrator. After starting the Data Source Administrator either from the Control Panel or by using Odbcad32.exe, you will find a Connection Pooling tab (for ODBC 3.5 and later). Unlike the Wait Retry Time value on the Connection Pooling page, the Performance Monitor settings are for all drivers, not just the selected driver.) To enable pool monitoring, open the ODBC icon in the Control Panel. In the ODBC Data Source Administrator dialog box, click the Connection Pooling tab. Under Performance Monitor, select the Enable option.

Enabling Pool Monitoring Under ODBC 3.5

Connection pooling monitoring under ODBC 3.5 requires that you make changes to the registry on your machine.(If you are running ODBC 3.51, you will not need to take these steps.) As with any changes you make to the registry, it is extremely important to back up the registry first. To do this, use the backup facilities provided in the registry editor. First, verify that you are running ODBC 3.5. If you are, the files mentioned in the steps below are included with the Pooling Toolkit in the Connpool.reg directory.

Verify what version of ODBC you are running. There are two important ODBC-related versions that you need to check. One is the version of the ODBC core components that is installed on your machine. The other is the version of the ODBC driver that you are using. Both can be determined by opening the ODBC Data Source Administrator in the Control Panel. To check the core ODBC version, click the About tab. To check the version of the driver you are using, click the Drivers tab and then check the appropriate driver version.

After confirming that you are indeed using ODBC 3.5, follow these steps:

  1. The Pooling Toolkit has a directory labeled Misc. Copy the files in that directory to your Windows NT 4.0 system directory.
  2. In the Windows NT system directory, run the command RegEdit CPMonitor.reg. This adds the necessary keys to the registry to track the connection pooling performance counters.
  3. In the Windows NT system directory, run lodctr odbcperf.ini. This loads the information about the performance counters that you will view with Performance Monitor.
  4. Restart your server.

Pooling Counters and What They Mean

To monitor connection pooling:

Start Performance Monitor (PerfMon.exe), either from the command prompt or by clicking Start and then clicking Run.

Click the Add counter button (+).

In the Add to Chart dialog box, you will now have an entry for ODBC Connection Pooling under the process list box. (If you don't, either pooling isn't enabled in the Data Source Administrator or you need to restart your machine because existing applications or services have ODBC loaded in memory, preventing any changes in the Data Source Administrator from taking immediate effect.) There will now be six counters that can be monitored by performance monitor. The naming of the counters is based on the distinction of hard versus soft connections. Hard actions are taken by the connection pooling process. Soft actions are taken by the application that uses connection pooling. See Table 1, ODBC Connection Pooling Performance Monitor Counters, for a description of each counter.

The counter that you see the most activity on should be the ODBC Soft Connection Counter. This indicates the number of connections that are satisfied by the connection pool. In this case, a live connection comes from the pool rather than requiring your application to make a new connection to the database.

Table 1. ODBC Connection Pooling Performance Monitor Counters

Counter | Definition
ODBC Hard Connection Counter per Second | The number of actual connections per second that are made to the server. The first time your environment carries a heavy load, this counter will go up very quickly. After a few seconds, it will drop to zero. This is the normal situation when connection pooling is working. When the connections to the server have been established, they will be used and placed in the pool for reuse.
ODBC Hard Disconnect Counter per Second | The number of hard disconnects per second issued to the server. These are actual connections to the server that are being released by connection pooling. This value will increase from zero when you stop all clients on the system and the connections start to time out.
ODBC Soft Connection Counter per Second | The number of connections satisfied by the pool per second—in other words, connections from that pool that were handed to users. This counter indicates whether pooling is working. Depending on the load on your server, it is not uncommon for this to show 40–60 soft connections per second.
ODBC Soft Disconnection Counter per Second | The number of disconnects per second issued by the applications. When the application releases or disconnects, the connection is placed back in the pool.
ODBC Current Active Connection Counter | The number of connections in the pool that are currently in use.
ODBC Current Free Connection Counter | The current number of free connections available in the pool. These are live connections that are available for use.

Connection Pooling and SQL Server Temporary Stored Procedures

Creating stored procedures to process prepared statements with connection pooling enabled can have unexpected results. Before elaborating on unexpected behavior with connection pooling and temporary stored procedures, it is important to first understand when temporary stored procedures are created and, more importantly, deleted.

Creating temporary stored procedures for prepared SQL statements is an option that is configurable from the ODBC Data Source Administrator. By default, this setting is "On" for the SQL Server 2.65 and 3.5 drivers. This means that when an SQL statement is prepared, a temporary stored procedure is created and compiled. When the prepared command is called from an OLE DB consumer or an ODBC application, the temporary stored procedure is executed, saving the overhead of parsing and compiling the SQL statement. If used properly, this feature can improve the performance of your application. If your SQL statement is going to be executed more than two times or contains parameters and will be called multiple times, the statement should be prepared. Remember that there is a price to be paid up front to prepare an SQL statement and that preparation is lost as soon as you disconnect from the database.

With connection pooling enabled, the issue becomes how to determine when these temporary stored procedures should be deleted. With the SQL Server 2.65 driver, they will be released when the connection is released. With the SQL Server 3.5 or later driver, you can choose whether to delete them when the connection is released, or both when the connection is released and as appropriate while connected.

Unexpected behavior can occur with temporary stored procedures and connection pooling if you are using the default setting. In that case, you run the risk of running out of room in the SQL Server Temporary Database (TempDB), where temporary stored procedures are created and stored. When connection pooling is enabled, a connection is made to the database, but when the client is done with it and releases it, the connection goes back into the pool. When the connection goes back into the pool, the connection is not released at the server level and the stored procedures are not deleted from TempDB. Effectively, you create temporary stored procedures in TempDB that are no longer associated with a client and will never be called again.

With SQL Server 7.0, the effects of the stored procedures being created in TempDB can be a little different. In SQL Server 7.0, the TempDB can be configured to automatically grow when out of space. If this configuration was set to drop stored procedures when disconnecting, your TempDB might not run out of space but can become very large.

When you are running the SQL Server 2.65 driver, it might be advisable to disable the creation of stored procedures while preparing a command when running with connection pooling. With the SQL Server 3.5 and newer drivers, when adding or configuring a DSN in the Data Source Administrator, you will have the option of creating temporary stored procedures. This option should be disabled or set When you disconnect and as appropriate while you are connected when running connection pooling. Setting this option means that the ODBC SQL Server driver will drop the connection when the command that created the interface goes out of scope. If your client code is ADO, the stored procedure would be released when the ADODB.Recordset and command objects are closed. SQL Server 7.0 now supports prepared commands, so it will not create temporary stored procedures when you have requested that your command be prepared.

Changing the Database Context

If you change the database context to another while you are connected to the database and do not set it back to the original context, you may cause problems in the pool. Consider the case where you use a stored procedure to change the database context to TempDB and then disconnect at the application level. The connection, now pointing to TempDB rather than to your original database, is then returned to the pool. The next time a request for a connection to your original database is made, the pooled connection pointing to TempDB might be handed to it. To avoid this, always maintain the original database context before disconnecting.

OLE DB Resource Pooling

OLE DB resource pooling, also known as OLE DB session pooling, is handled by the OLE DB core components. To take advantage of resource pooling, an OLE DB consumer must invoke the data provider by using either the IDataInitialize or the IDBPromptInitialize interface. (ADO will attempt to do this automatically.) OLE DB resource pooling can be turned on for one provider and off for another.

Note Performing CoCreateInstance on IDBInitialize is traditionally used in OLE DB to open a data source object. To use resource pooling, you perform CoCreateInstance on either IDataInitialize or IDBPromptInitialize. Both interfaces are part of the OLE DB service components and not your OLE DB data provider. You can use IDataInitialize and IDBPromptInitialize to retrieve an instance of IDBInitialize from your data provider so that the service components can be used. The two interfaces make it possible to use service component features such as pooling, transaction enlistment, and the Client Cursor Engine.

When the application creates an OLE DB data source object (via ADO or an OLE DB consumer), OLE DB services query the data provider for supported information and provide a proxy data source object to the application. To the consuming application, this proxy data source object looks like any other data source object, but in this case setting properties merely caches the information in the local proxy. When the application calls IDBInitialize::Initialize in OLE DB or opens a connection in ADO, the proxy data source object checks whether any connections already exist that match the specified connection information and are not in use. If so, rather than creating a new object, setting properties, and establishing a new connection to the database, the proxy data source object uses the existing initialized data source object. When the application releases the data source object, it is returned to the pool. Any data source object that is released by the application and not reused after 60 seconds is automatically released from the pool.

Figure 4 shows how OLE DB resource pooling works.

Figure 4. The Benefits of OLE DB Resource Pooling

Note The name resource pooling is actually something of a misnomer. It implies that any kind of resource can be pooled, when in fact OLE DB resource pooling is just for the pooling of an OLE DB data source proxy object (DPO). Resource pooling does use the resource dispenser found with Microsoft Transaction Server, which is a more generic form of pool manager. More than a few OLE DB users have been confused by the term "resource pooling" into thinking that it pools more than it actually does.

Note Most of the functionality of Microsoft Transaction Server has been incorporated into Windows Component Services for Microsoft Windows 2000. References to Microsoft Transaction Server in this article may not reflect the equivalent behavior with Windows Component Services. If you are using Windows 2000, see your operating system documentation for more information.

Configuring OLE DB Resource Pooling

By default, service components are enabled for all Microsoft OLE DB providers if the provider is invoked by IDataInitialize or IDBPromptInitialize and if the provider is marked to work with pooling by using the OLEDB_Services registry key.

You can control pooling from your application in the following three ways:

  • You can disable pooling for an individual provider through the registry.
  • If you write directly to the OLE DB API, you can control pooling through the properties you set when connecting to the database.
  • You can control pooling from your connection string.

The time-out value is set to 60 seconds; this value cannot be configured in OLE DB resource pooling prior to the release of MDAC 2.5.

Retry Wait works much differently in resource pooling than in ODBC connection pooling. After the server has been determined to be unavailable, resource pooling blocks the pool. The next retry for a valid connection on the server occurs after one minute. If this attempt fails, the next retry occurs after two minutes, and again after five minutes. Thereafter, the retry occurs every five minutes until a valid connection is returned.

Configuring resource pooling from the registry

To determine whether individual core components can be invoked to satisfy extended functionality requested by the consumer, OLE DB compares the properties specified by the consumer to those supported by the provider. Table 2 lists values that can exist in the OLEDB_Services registry entry, which should be created during installation of the provider.

Table 2. Setting OLE DB_Services Registry Entry Under Provider's CLSID

Default services enabled | DWORD value
All services (the default) | 0xffffffff
All services except pooling | 0xfffffffe
All services except pooling and auto-enlistment | 0xfffffffc
All services except client cursor | 0xfffffffb
All services except client cursor and pooling | 0xfffffffa
No services | 0x00000000
No aggregation, all services disabled | No OLEDB_Services registry entry

With the release of MDAC 2.5, first included with Windows 2000 Beta 3, you can configure both connection time-out and the Retry Wait values by using the registry. Windows 2000 Beta 3 includes a beta release of OLE DB and instructions in the OLE DB Readme for doing so. Those instructions are summarized below.

When set under a provider's CLSID entry in the registry, the SPTimeout value controls the length of time, in seconds, that unused sessions are held in the pool. SPTimeout should be entered as a DWORD value under HKEY_CLASSES_ROOT/CLSID/ClassID where ClassID is the CLSID of the OLE DB provider. The default SPTimeout value is 60 seconds.

The Retry Wait value controls the length of time, in seconds, to wait between connection attempts. It is entered as a DWORD value under HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/DataAccess/Session Pooling. The default SPTimeout value is 60 seconds.

Configuring resource pooling from your application

If you write directly to the OLE DB API, you can set the DBPROP_INIT_OLEDBSERVICES property to enable or disable various core components, including OLE DB resource pooling. There is no way to configure connection time-out or Retry Wait programmatically in your application except by manipulating the registry entries directly. It is best not to have any services or applications using OLE DB when you make these changes, or the effects might not show up right away.

Table 3 lists values and the services enabled or disabled by the value settings. Code Example 2 is an excerpt of code that uses resource pooling in an OLE DB consumer.

Table 3. Setting OLE DB Services by Using the DBPROP_INIT_OLEDBSERVICES Property

Services enabled | Property value
All services | DBPROPVAL_OS_ENABLEALL
All services except pooling | (DBPROPVAL_OS_ENABLEALL & ~DBPROPVAL_OS_RESOURCEPOOLING)
All services except pooling and auto-enlistment | (DBPROPVAL_OS_ENABLEALL & ~DBPROPVAL_OS_RESOURCEPOOLING & ~DBPROPVAL_OS_TXNENLISTMENT)
All services except client cursor | (DBPROPVAL_OS_ENABLEALL & ~DBPROPVAL_OS_CLIENTCURSOR)
All services except client cursor and pooling | (DBPROPVAL_OS_ENABLEALL & ~DBPROPVAL_OS_RESOURCEPOOLING & ~DBPROPVAL_OS_CLIENTCURSOR)
No services | ~DBPROPVAL_OS_ENABLEALL

Code Example 2: OLE DB Consumer Code Using Pooling

long OLEDBConnect( char   *lpszInitString,
                   bool    bUsePooling,         // Flag to set pooling
                   ULONG   nCount )
{
   IDataInitialize   *pIDataInitialize = NULL;
   IDBProperties     *pDBProperties    = NULL;
   IDBInitialize     *pIDBInit         = NULL;
   HRESULT            hr               = S_OK;
   WCHAR              wszInitString[1024];

   AtoU( lpszInitString, &wszInitString[0], 1024 );
   hr = CoCreateInstance(CLSID_MSDAINITIALIZE,
                         NULL,
                         CLSCTX_INPROC_SERVER,
                         IID_IDataInitialize,
                         (void**)&pIDataInitialize);
   for (ULONG i=0;i<nCount && SUCCEEDED( hr ); i++)
   {
      hr=pIDataInitialize->GetDataSource(NULL,
                                         CLSCTX_INPROC_SERVER,
                                         wszInitString,
                                         IID_IDBInitialize,
                                         (IUnknown**)&pIDBInit);
      if (pIDBInit)
         hr=pIDBInit->Initialize();
      if ( bUsePooling == false )
         if (pIDBInit)
            pIDBInit->Uninitialize();   // Disables Pooling(!)
      if (pIDBInit) pIDBInit->Release();
         pIDBInit = NULL;
   }
   if (pIDataInitialize)
      pIDataInitialize->Release();
   return 0;
}

Note One of the key points to note in the preceding code is that if you call IDBInitialize::Uninitialize, you will turn off pooling! To release a connection, use IDBInitialize::Release instead.

The OLE DB property DBPROP_INIT_OLEDBSERVICES maps directly to a connection string attribute, "OLE DB Services", as shown in Table 4. Code Example 3 demonstrates this.

Table 4. Setting OLE DB Services by Using ADO Connection String Attributes

Services enabled | Value in connection string
All services (the default) | "OLE DB Services = -1;"
All services except pooling | "OLE DB Services = -2;"
All services except pooling and auto-enlistment | "OLE DB Services = -4;"
All services except client cursor | "OLE DB Services = -5;"
All services except client cursor and pooling | "OLE DB Services = -6;"
No services | "OLE DB Services = 0;"

Code Example 3: ADO Consumer Code Using Pooling

' This will take advantage of resource pooling.
Dim i As Integer
Dim c As ADODB.Connection
Dim r As ADODB.Recordset
Dim c as new ADODB.Connection
c.Open "DSN=LocalServer;UID=sa;PWD=;OLE DB Services=-1"
For i = 1 To 100
      Set r = New ADODB.Recordset
      r.Open "SELECT * FROM Authors", c
      Set r = Nothing
Next I
c.close
Set c = Nothing

Enabling OLE DB Resource Pooling

Resource pooling can be enabled in several ways:

  • Automatically, when a consumer accesses a provider from within Microsoft Transaction Server 2.0 or Internet Information Server 4.0.
  • By enabling or disabling OLE DB services components in the registry key for a given provider.
  • For an OLE DB-based consumer, by using the IDataInitialize or IDBPromptInitialize interface. If you use IDataInitialize::CreateDBInstance, you can enable or disable services components by using the DBPROP_INIT_OLEDBSERVICES property.
  • For an ADO-based consumer, by keeping one open instance of a Connection object for each unique user and using the OLEDB_SERVICES connection string attribute to enable or disable pooling. By default, ADO attempts to use pooling, but if you do not keep at least one instance of a Connection object open for each user, there will not be a persistent pool available to your application. (However, Microsoft Transaction Server keeps pools persistent as long as the connections in them have not been released and have not eventually timed out.)

Note Pooling will not be enabled if you call CoCreateInstance directly on the CLSID of the data provider.

Determining the Number of Available Pools

In OLE DB resource pooling, the following formula determines the number of pools (where N is the number of pools, P is the number of processors, and C is the number of distinct sets of connection attributes on the system):

N = (P + 1) * C

Using this formula, OLE DB resource pooling eliminates lock contentions on the pools.

OLE DB resource pooling keeps a map of which pools contain which users. From there, it is possible to jump to the right pool and start looking for an available connection. With OLE DB resource pooling, a given pool contains connections only for a single provider with a specific set of connection attributes. In other words, each pool contains only connections to one set of credentials and one data store.

Writing Data Consumers That Work With OLE DB Services

When developing an application using either ADO or native OLE DB, the following tips will help you to enable resource pooling or to avoid inadvertently disabling it. These rules apply whether you use ADO, OLE DB consumer templates, or native OLE DB code.

Tips for ADO Users

  • The ADO Connection object implicitly uses IDataInitialize. However, this means your application needs to keep at least one instance of a Connection object instantiated for each unique user—at all times. Otherwise, the pool will be destroyed when the last Connection object for that string is closed. (The exception to this is if you use Microsoft Transaction Server. In this case, the pool is destroyed only if all of the connections in the pool have been closed by client applications and are allowed to time out.)

    Note If you open a Connection object, remember to close it. Do not rely on garbage collection to implicitly close your connections.

  • By avoiding connection creep (discussed later in this article), you can also help your application use pooling.

Tips for OLE DB Users

  • Use IDataInitialize or IDBPromptInitialize to create an instance of your data provider. As long as one instance of either interface exists, a pool is available to your application. Otherwise, the pool will be destroyed.
  • Do not request provider-specific interfaces prior to connecting.
  • Do not request prompting.
  • Do not use IDBInitialize::Uninitialize. Use IDBInitialize::Release instead. If you call Uninitialize, the connection you were using will be flushed from the pool. This is because the user can change the properties of the connection after calling Uninitialize.
  • Do not release your last instance of IDataInitialize or IDBPromptInitialize while pooling is needed by your application.
  • Use only one resource per connection.
  • By avoiding connection creep (discussed below), you also help your application use pooling.

Writing Data Providers That Work with OLE DB Services

As mentioned above, ADO attempts to use or enable services implicitly by referencing the OLEDB_Services registry value when a connection is established. However, some older providers may not be able to support services. Simply creating this registry entry is not enough to enable services for your application. The data provider must support certain features or services will not work. Attempting to use services with a data provider that does not offer this functionality can create unexpected behavior in your application. Following is a brief list of the functionality a data provider must support for your consumer application to utilize services:

  • The provider must support aggregation of all objects.
  • Providers must support the free-threaded model or, at a minimum, the rental-threaded model. Services will determine the data provider’s thread model by using the DBPROP_DSOTHREADMODEL property.
  • If the provider has a global connection state that may change while the data source object is in an initialized state, it should support the DBPROP_RESETDATASOURCE property.
  • Providers that connect to a remote database and can detect whether that connection has been lost should support the DBPROP_CONNECTIONSTATUS property. This allows pooling to detect dead connections and to ensure that they are not returned to the pool.
  • Providers should support both pooling and transaction enlistment by using the DBPROP_INIT_OLEDBSERVICES property. The values used with this property also correspond to the OLEDB_Services registry key.

For more information, consult either the OLE DB Readme that shipped with the Data Access 2.0 SDK or the OLE DB Services section in the documentation with version 2.1 and later of the SDK.

Troubleshooting MDAC Pooling

Occasionally, MDAC pooling might not work as expected. When this happens, you need to look at what your application is doing that could have a negative impact on pooling, especially if the application has less than comprehensive error handling. At this time, OLE DB does not offer a way to trace activity on OLE DB providers. This makes robust error handling (see the discussion later in this article) even more important.

Connection Creep and Effective Server Tracing

Pooling trouble diagnosis often begins by using tools such as SQL Trace and SQL Server Performance Monitoring to track connection creep, or an increasein the number of open connections on a database server. However, this does not equate to a failure in pooling. Identifying connection creep is only the beginning of an analysis, not its conclusion.

Common causes of connection creep include:

  • The ADO Connection and Recordset objects are not actually closed. If you don't explicitly close them, they won't be released into the pool. This is probably the single most frequent cause of connection creep.
  • The wrong cursor location is used with a Recordset object you intend to disconnect. A disconnected Recordset cannot use a server-side CursorLocation property. You must explicitly set CursorLocation to be client-side.
  • A disconnected Recordset has not actually been disconnected. If you don't set the ActiveConnection property of the Recordset to Nothing/NULL, it is not disconnected and the connection is not released into the pool.
  • CursorLocation is set after the Recordset is opened, not before. After the Recordset is open, CursorLocation does not and cannot change.
  • An ADO object you created (especially a Connection object) was not explicitly released. Explicitly releasing objects you create is just good programming practice. If you allocate memory, release it. Depending on the language you are using, letting a variable go out of scope may or may not result in it being released.

Note If you have effective error trapping in your code, you can receive error messages that point to the real culprit (such as a failure in SQL Server's NetLib or Oracle's SQL*NET components).

Details of several of these problems are discussed in the sections that follow.

If You Explicitly Open It, Explicitly Close It

Pooling problems are frequently caused by an application that does not clean up its connections. Connections are placed in the pool at the time that the connection is closed and not before. To avoid this, always explicitly close an object you open. If you don't explicitly close it, chances are it won't ever be released to the pool.

Even if the language you use has effective and reliable garbage collection, an instance of an open ADO Connection or Recordset object going out of scope does not equate to the Close method of that object being implicitly invoked. You must close it explicitly.

Failing to close an open Connection or Recordset object is probably the single most frequent cause of connection creep and the single largest cause of incorrect diagnoses of pooling failure.

Disconnecting Recordset Objects

It is sometimes confusing in pooling to correctly disconnect a Recordset object. To release a connection into the pool, especially in three-tier environments, you must use a client-side, static cursor with BatchOptimistic locking. You must also release the Recordset ActiveConnection object. Otherwise, the Recordset object will not be disconnected and, more importantly, the connection will not be released into the pool. You cannot disconnect a Recordset object with a server-side cursor.

Code Example 4 demonstrates how to implement a disconnected Recordset object—in this case for a Microsoft Transaction Server business object. The environment in which the code is used is not as significant as the actual steps to disconnect the Recordset object.

Code Example 4: Disconnected Recordset in ADO

Public Function ServiceTest(connectStr As String, SqlText As String) As String
   Dim c As New ADODB.Connection
   Dim r As New ADODB.Recordset
   On Error GoTo handler

   c.ConnectionString = connectStr
   c.CursorLocation = adUseClient
   c.Open
   Set r.ActiveConnection = c
   r.Open SqlText, , adOpenKeyset, adLockBatchOptimistic, -1
   Set r.ActiveConnection = Nothing   ' This disconnects the recordset.
   c.Close
   Set c = Nothing

   ' Recordset is now in disconnected state; do something with it.
   r.Close
   Set r = Nothing
   Exit Function

handler:
   ServiceTest = ErrHandler(c, Nothing, r)
End Function

If you do not intend to update any data in the cursor, it is more efficient to use a server-side (rather than a client-side) cursor. This is because the creation of the client-side object is resource-intensive and can impact performance.

Temporary Tables and Resource Pooling

If the connection is returned to the pool, the temporary table will persist until the connection is actually released—not just returned to the pool. If your application uses many temporary tables, this might create a resource problem on the server. If you use a stored procedure on the server to create the temporary table, the temporary table will be destroyed when the stored procedure goes out of scope. Temporary tables not created by stored procedures will be destroyed only after the connection is actually released from the pool.

Connection Status

The DBPROP_CONNECTIONSTATUS property reveals whether a stale connection exists for a given OLE DB provider in the pool. The pool returns a connection to the client even if the status of the connection is stale. For example, this can happen if the underlying server accessed by the connection is no longer available on the network.

Table 5 summarizes the DBPROPVAL_CS_* properties defined in OLE DB. These values can be used to determine the status of a connection.

Table 5. Valid Values for Connection Status Dynamic Property

Connection status | Value | Comment
UNINITIALIZED | 0L | Connection object has not had Open method called yet.
INITIALIZED | 1L | A valid connection.
COMMUNICATIONFAILURE | 2L | The connection is stale. Close and reopen the Connection object.

Unintended Disabling of Resource Pooling

Resource pooling can be inadvertently disabled for your application. The persistence and behavior of resource pooling depend on several conditions that occur with a given set of user authentication criteria, such as the following:

  • A given resource pool is specific to the connection attributes of that set of authentication criteria.
  • Do not call IDBInitialize::Uninitialize, which implicitly disables pooling. Instead, use IDBInitialize::Release. While both IDBInitialize::Uninitialize and IDBInitialize::Release close the connection, calling Uninitialize will result in the pool being destroyed. (This is not an issue for ADO developers.)
  • The data source object must be created by using IDataInitialize or IDBPromptInitialize, and not by CoCreateInstance directly on the OLE DB data provider. For pools to exist for a given set of connection attributes, resource pooling requires at least one instance of an OLE DB data source object created per unique set of connection properties.
  • If using ADO, at least one ADO Connection object must remain open for each set of user authentication credentials. Although ADO cannot call Uninitialize, letting go of all ADO Connection objects for a given set of connection attributes accomplishes the same thing—that is, it releases the last instance of IDataInitialize.

Code Example 5 and Code Example 6 are examples of how you can inadvertently disable resource pooling.

Code Example 5: Disabling Pooling by Using IDBInitialize::Uninitialize

hr = CoCreateInstance(CLSID_MSDAINITIALIZE, NULL,
                     CLSCTX_INPROC_SERVER, IID_IDataInitialize,
                     (void**)&pIDataInitialize);
for (ULONG i=0;i<nCount && SUCCEEDED( hr ); i++)
{
   pIDataInitialize->GetDataSource(NULL, CLSCTX_INPROC_SERVER,
      wszInitString, IID_IDBInitialize,
      (IUnknown**)&pIDBInit);

   hr=pIDBInit->Initialize();   // Opens connection
      pIDBInit->Uninitialize();    // Disables Pooling(!)
      pIDBInit->Release();
}

Code Example 6: Disabling Pooling by Eliminating Instances of IDataInitialize

' This will not take advantage of resource pooling.
Dim i As Integer
Dim c As ADODB.Connection
Dim r As ADODB.Recordset
For i = 1 To 100
   Set c = New ADODB.Connection
   Set r = New ADODB.Recordset
   c.Open "DSN=LocalServer;UID=sa;PWD=;OLE DB Services=-1"
   r.Open "SELECT * FROM Authors", _
      "DSN=LocalServer;UID=sa;PWD=;"
   r.close
   c.close
   Set c = Nothing
   Set r = Nothing
Next I

Maintaining a Persistent Connection for Resource Pooling

This article has repeatedly emphasized the need for developers who use OLE DB resource pooling to keep at least one connection open for each set of user credentials used to access the data store. This is not to be confused with the mistake of opening up multiple and unnecessary Connection objects, or even multiple Recordset objects. It's also not to be confused with using just one Connection object for several hundred ASP pages.

ASP developers should open one connection per set of unique user credentials. However, an ASP developer can eliminate the benefits of pooling in this scenario in the following ways:

  • Instantiating dozens or even hundreds of instances of ADO objects at once.

    For example, you can easily circumvent any performance gains found in pooling by using code such as the following:

    Dim c(200) as ADODB.Connection
Dim r(200) as ADODB.Recordset

    Presently, these two lines of code appear to be frequently and incorrectly used on individual ASP pages. The overhead of instantiating, opening, and manipulating this many objects will eliminate any benefit of pooling, not to mention that it will swell the pool to unnecessary size just to hold each of those connections. If you use this technique on multiple ASP pages, which in turn are hit by multiple users, the amount of memory needed just to hold all of those ADO objects in memory can soon reach into the gigabytes.

  • Using just one Connection object for multiple ASP pages.

    If you don't want to circumvent the benefits of pooling, however, you can do this: Within a given ASP page, open the Connection object and one or more Recordset objects that you need. Then close and delete them.

    This also applies for non-ASP developers who are developing an application with multiple threads. Do not try to share a single Connection object between all threads. Instead, use one thread, one Connection object, and one persistent connection in your main application to keep the pool alive.

    For Microsoft Transaction Server developers, this is not an issue. Microsoft Transaction Server itself enables the pool, whether you implement a single Connection object or not. Because Microsoft Transaction Server is inherently stateless, trying to keep that persistent Connection object around, let alone actually use it, is redundant.

While pooling is a valuable tool that can give applications increased performance and scalability, it can also be sabotaged to the point of uselessness through coding techniques such as those just listed.

Robust Error Handling

When OLE DB encounters an error condition or if the underlying provider passes back informational messages, it will attempt to convey this information to your application. However, that does not mean the application is actually receiving it or logging it appropriately. Microsoft Visual Basic Scripting Edition (VBScript) applications, in particular, have loose error handling.

OLE DB error handling extends Automation error handling by adding the ability of an error object to contain multiple error records.

Chapter 16, "Errors," in the Microsoft OLE DB Programmer's Reference, has an excellent discussion of how to use these features. (The most up-to-date version is available in the MDAC SDK, and the OLE DB 2.0 version is available in print from Microsoft Press.) In the same reference, Appendix F, "Sample OLE DB Consumer Application," demonstrates practical code for the use of OLE DB error handling. In addition, the SQL Server 7.0 documentation has an excellent code example that shows how to use the optional interfaces ISQLErrorInfo and ISQLServerErrorInfo.

Code Example 7 shows OLE DB error handling.

Code Example 7: Error Handling with OLE DB

// Goes to CLEANUP on Failure_
#define CHECK_HR(hr)         \
     if(FAILED(hr))         \
          goto CLEANUP
// Goes to CLEANUP on Failure, and displays any ErrorInfo
#define XCHECK_HR(hr)                                             \
{                                                                \
     if(FAILED(ErrHandler(hr, LONGSTRING(__FILE__), __LINE__)))       \
     goto CLEANUP;                                               \
}

XCHECK_HR(hr = CoCreateInstance(CLSID_MSDAINITIALIZE,
                               NULL,
                               CLSCTX_INPROC_SERVER,
                               IID_IDataInitialize,
                               (void**)&pIDataInitialize));
CLEANUP:
     if (pIDBInitialize != NULL ) pIDBInitialize->Release();

ADO and RDS generate an error in the native language you use to implement your application. In Microsoft Visual Basic, this is an Err object. In Microsoft Visual C++®, this is usually an exception or a failed HRESULT. In Java, an instance of an exception class is raised.

Note In VBScript, no event is fired. This makes error handling in VBScript especially challenging.

If you run in an environment that uses Active Server Pages (ASP), it is a good practice to define a standard error handler and use this error handler unilaterally throughout your code. That way, if you need to change your error handler, you need only make that change in one place—not in every page that uses error handling.

Code Example 8 shows ADO error handling.

Code Example 8: Error Handler in ADO

Dim Con0 As ADODB.Connection
   On Error GoTo ErrHandler
   ...
   Exit Sub
ErrHandler:
   ' For this code, we do not have any Command or Recordset objects,
   ' but this error handler would close them down if they did exist.
   Call ErrHandler(Con0, Nothing, Nothing)

Top 10 Reasons Why OLE DB Resource Pooling Might Not Work

To summarize what this article has discussed, here is a list of the top 10 reasons why pooling might not be turned on. This text was first made available in the OLE DB readme.

  1. The registry value OLEDB_Services must be present under the provider's HKEY_CLASSES_ROOT/<CLSID> key. OLEDB_Services is most commonly set to 0xffffffff, or to desired bits of DBPROPVAL_OS_*. If this does not exist or is set to 0x00000000, pooling will never occur. For more information, refer to "Setting Provider Service Defaults" in the OLE DB Services documentation.
  2. The consumer can override the OLEDB_Services key and disable pooling by setting DBPROP_INIT_OLEDBSERVICES. For more information, refer to "Overriding Provider Service Defaults" in the OLE DB Services documentation.
  3. The provider must be free-threaded. If a provider developer uses the OLE DB provider templates for Visual C++ or Visual Basic, the templates will not create free-threaded providers by default. In those cases, pooling will be disabled regardless of what the property or registry indicates.
  4. Setting DBPROP_INIT_PROMPT and DBPROMP_INIT_HWND disables pooling of a data source object. These properties must be set to either VT_EMPTY or NOPROMPT.
  5. Consumers cannot use QueryInterface for any interface unknown to service components prior to initialization. That is, applications that need to pool should not use QI, as COM suggests, to see what interfaces the provider supports. For example, a simple QI for IDBAsynchStatus, to determine whether the provider is Asynch or not, eliminates the data source object from the pool.
  6. Calling IDBProperties::GetPropertyInfo prior to initialization disables pooling.
  7. If IDBInitialize::UnInitialize is called, the released data source object will not be pooled.
  8. Pooling does not occur on Windows 95.
  9. Providers must correctly implement aggregation.
  10. Do not use data source object notifications. If you use a QueryInterface for IConnectionPointContainer to advise a listener to the data source object, the object will never be pooled.

NOTE These items are cumulative. That is, any item will disable pooling for that data source object, regardless of what the other values are.

The MDAC Pooling Toolkit

This paper has presented numerous code examples, which (for the most part) were taken directly from the MDAC Pooling Toolkit. The Toolkit consists of samples that you can use as templates for taking advantage of pooling in your application as well as for verifying and diagnosing performance issues that arise due to pooling. Table 6 describes each of the samples and what it demonstrates.

Note By default, error handling for these applications will display both warnings and error messages. To disable display warnings, see the ErrHandler function in each sample.

Table 6. Contents of the MDAC Pooling Toolkit

Sample | Description
CODE/CONSOLE/ADO/ASP | Pooling.asp demonstrates how to invoke and inadvertently disable pooling from ADO to one of four providers. It uses a business object, GetTick, to return the current time.
CODE/CONSOLE/ADO/VB | PoolingVB.vbw is a Visual Basic 6.0 implementation of the same functionality as Pooling.asp.
CODE/CONSOLE/ADO/VC | PoolingVC.dsw is a Visual C++ 6.0 implementation of the same functionality as Pooling.asp.
CODE/CONSOLE/ODBC | Odbcpool.dsw is a Visual C++ 6.0 implementation of ODBC connection pooling. Command-line arguments specify DSN to open. Driver-based and environment handle–based pooling are demonstrated.
CODE/CONSOLE/OLE DB/PROPERTIES | Oledbpool.dsw is a Visual C++ 6.0 implementation of OLE DB resource pooling. Command-line arguments specify the provider to use. This version opens a connection to the data source by using IDataInitialize::CreateDBInstance.
CODE/CONSOLE/OLE DB/NOPROPERTIES | Oledbpool.dsw is a Visual C++ implementation identical to the Properties example, except that it uses IDataInitialize::GetDataSource.
CODE/MTS/MTSClient | This simple Microsoft Transaction Server client, written in Visual Basic, invokes a Microsoft Transaction Server business object. It can invoke the business object with pooling either enabled or disabled.
CODE/MTS/MTSBusObj | This simple Microsoft Transaction Server business object, written in Visual Basic, creates a disconnected Recordset and returns a count of how many records were in the Recordset.

These samples make these assumptions regarding your development environment:

  • You have MDAC 2.0 or later installed on your machine. ODBC connection pooling requires ODBC 3.0 or later. OLE DB resource pooling requires MDAC 2.0 or later.
  • In order to compile the Visual C++-based OLE DB samples, you need the Data Access SDK 2.1 or earlier or MDAC SDK 2.5 installed. Installing the SDK allows you to access header files for the OLE DB providers for SQL Server, Oracle, and Jet, as well as the OLE DB service components header file. In addition, you need to configure Visual C++ to reference the \Msdasdk\Include\Oledb directory (installed by the SDK) as an include path.
  • When running either the ODBC and OLE DB console applications inside of Visual C++, you need to set the command-line arguments to specify a data source to test pooling against in the Settings for the Project (on the Debug tab).
  • In order to build the Visual C++ ADO samples, you need to set the executable path in Visual C++ options to include C:\Program Files\Common Files\System\ADO. This assumes that drive C: is where you installed the operating system. Regardless, the path must be on the same drive as Windows.
  • In order to run the VBScript sample, you must have the Adovbs.inc file that comes with MDAC in the same directory as the ASP page.
  • The OLE DB samples require the Unicode version of the MFC DLLs to be installed on your machine. When you first installed Visual C++, these DLLs may not have been included in your installation options. By rerunning the Visual C++ setup, you can install these DLLs. In particular, Mfc42ud.dll is required if you are running the samples in the debugger.

Summary

OLE DB resource pooling is a key technique for achieving increased performance with Microsoft Data Access Components. In ODBC, OLE DB, and ADO/RDS programming, pooling is a key piece of your application's architecture, even if you don't actually write ODBC or OLE DB code. Table 7 summarizes the key differences in behavior and functionality between the two pooling models.

Table 7. Comparison of ODBC Connection Pooling and OLE DB Resource Pooling

Feature | Connection pooling | Resource pooling
Enabling pooling ODBC 3.5+: In the Data Source Administrator.

Optionally, ODBC 3.0+ can set this in the registry.

 Implicitly available in ADO.

Implicitly used by components running under Microsoft Transaction Server.

Consumer uses IDataInitialize or IDBPromptInitialize.
Disabling pooling By using registry setting.

By using ODBC Data Source Administrator.

By using SQLSetEnvAttr for ODBC API-based applications.

 By using registry setting.

Implicitly disabled if there is not at least one persistent ADO Connection object (or OLE DB IDataInitialize interface) open for each unique set of user credentials in the application.

If every connection has unique connection attributes or properties, n pools are created that contain one connection each. This does not disable pooling, but it does disable the benefits of pooling.

Provider does not support COM aggregation or have OLEDB_SERVICES registry key.
Configurable pool time-out | Yes | No
Number of pools If P = number of processors:

(P)

Connections using different drivers and user credentials can be and are stored in the same pool.

 If P = number of processors and C = number of unique sets of user credentials (the connection string plus any properties you set before opening the connection):

((P + 1) * C)

Each pool has a homogeneous set of connections with identical user credentials.
Support required within the driver or provider Driver must be thread-safe. No other specific requirements. Provider must be thread-safe (implicit with OLE DB).

Must support IDataInitialize and IDBPromptInitialize interfaces. Connection must be created through these interfaces.

Provider supports COM aggregation.

Provider supports OLEDB_Services registry key.

This article has discussed how to enable, disable, and configure MDAC pooling and has presented practical tips for using pooling and for diagnosing issues that developers may encounter. The primary advantage of pooling is performance. The time it takes to open a connection is a significant resource sink in any data operation. Pooling enables developers who use MDAC to maximize performance, whether using ODBC connection pooling or OLE DB resource pooling.

Bibliography

Aaron Cohen & Mike Woodring, Win32 Multithreaded Programming, O'Reilly & Associates, Inc., 1998.

Microsoft, Microsoft OLE DB 2.0 Programmer's Reference and Data Access SDK, MSPRESS, 1998.

Q193332 (FILE:MDACCON.EXE Using Connection Strings w/ ODBC/OLEDB/ADO/RDS) for details on connection strings for ODBC Drivers and OLE DB Providers.

Microsoft Data Access Web Site.

The Knowledge Base can be obtained at Support Online from Microsoft Product Support Services. You will have to register to use this site. Articles relating to pooling, Microsoft Data Access Components, ActiveX Data Objects, ODBC, OLE DB can be found by querying on any one of the following "products" available at this site:

  • Microsoft Data Access Components (MDAC)
  • ActiveX Data Objects (ADO)
  • Open Database Connectivity (ODBC)
  • OLE DB
  • Remote Data Service (RDS)

Keywords:

connection pooling (ODBC)

resource pooling (OLE DB)

session pooling (OLE DB)

MDAC pooling


Improving MDAC Application Performance

Suresh Kannan

August 1999

Introduction

This document provides suggestions and guidelines for improving the performance of your MDAC application. The suggestions offered here constitute good coding practice. Performance might not noticeably improve unless accessing data is a significant part of your application processing.

Where possible, sample code has been provided to illustrate key points. In studying the samples, it is important to keep in mind the following considerations:

  • The best way to improve the performance of your application is to understand where your bottlenecks are. If your goal is to improve the speed of an existing application, you should start by profiling and analyzing your application.
  • This document describes general guidelines for improving your application performance. Some suggestions work only under certain circumstances. Where possible, such constraints have been called out. After you implement a change suggested in this document, you should measure your application's performance to validate benefits. Some modifications could be detrimental to your application performance if they are inappropriate for your application scenario.
  • Although Microsoft® Visual Basic® for Applications has been used to illustrate most of the suggestions, you can also apply suggested techniques to clients writing to ADO in other languages, such as C, Microsoft Visual Basic Scripting Edition (VBScript), Microsoft Visual C++®, and Java.

General Considerations

Use Strongly Typed Variables

A strongly typed variable is explicitly declared to represent only one variable type. Once declared, it cannot be used to represent a different variable type, as can weakly typed variables.

Microsoft Visual Basic allows weakly typed variables through the use of the Variant type. The Variant type can store almost any other kind of variable or object. While this gives you automatic type conversions so that you don't have to pay attention to the variable types, it can make debugging difficult. Occasionally, this automatic conversion will transform the data into a type that you didn't expect or intend, and tracking down where that happened is very difficult.

Microsoft Visual Basic defaults to the Variant type for all variables that are declared without a specific type and for all variables that are not declared at all. However, for better performance (unless the Variant type is specifically required for a property or method), avoid using Variant variables. Instead, use the Option Explicit statement to require declarations for all variables, and provide a specific type declaration for all variables that you declare. Alternatively, you can use the Deftype statements to change the default type for variables that are created and not declared or that are declared without a specific type.

Declare All Variables

A variable that is not declared will be created by Visual Basic with a default type. As discussed above, normally it will be implicitly created as a Variant. However, if you've used the Deftype statements, you can change the default.

Following is an example where the variables are implicitly created:

Public Sub NoExplicitDeclare(cnn)
   Set rsl = cnn.Execute("SELECT * FROM Nonsense")
   txt = rs1!SomeText.Value
   Debug.Print txt
End Sub

This code has a problem that the compiler won't catch, so you probably won't find it until you run the code. What is supposed to happen is that rsl should be assigned to a Recordset returned from the open Connection object cnn. However, when you run it, you'll get an error on the line txt = rs1!SomeText.Value. Visual Basic detects that rs1 is an empty Variant and generates a run-time error, "Object Required." In a more complex algorithm, you might be led to believe that the Execute method encountered an error. The real problem is that the variable rsl is misspelled as rs1. This problem is masked because the syntax and the identifiers appear correct at first glance.

If you place the Option Explicit statement at the top of the module, Visual Basic will generate a compiler error, and after you've declared rsl as a Recordset object and txt as a String, Visual Basic will highlight rs1 with the error "Variable not defined."

Next is an example where the variables are explicitly created:

Option Explicit
Public Sub NoExplicitDeclare(cnn As Connection)
   Dim rsl As Recordset
   Dim txt As String
   Set rsl = cnn.Execute("SELECT * FROM Nonsense")
   txt = rsl!SomeText.Value
   Debug.Print txt
   rs1.Close
   Set rs1 = Nothing
End Sub

Use Strongly Typed Object Variables

Object variables represent pointers to COM objects. They can be declared in two ways, as follows:

  • Use the keywords As Object so that the variable can be used to represent any kind of object; this is a weakly typed object variable.
  • Use the As keyword with the specific type of the object; this is a strongly typed object variable.

Microsoft Visual Basic must use late binding for all weakly typed object variables. This means Visual Basic must indirectly make every method and property call as each call is encountered at run time. Visual Basic does this by using the IDispatch interface to retrieve the identifier of each method and property function at run time and then calling IDispatch again to actually invoke the method or property. Although Visual Basic caches some of the information from IDispatch, using the late binding approach is still the slowest way to invoke methods and properties.

To create a strongly typed object variable, use the As keyword with the Dim, Private, Public, or Static statements and use the name of an object from one of the object libraries selected in the Project References dialog box. Visual Basic will read the object library at compile time to avoid calling IDispatch to get the identifiers of the methods and properties of the object. This is called early binding. In early binding, Visual Basic still invokes the properties and methods of the object through the IDispatch interface.

If the objects described in the object library use dual interfaces, it means they support not only the IDispatch interface but also a table of function addresses that mirror the properties and methods available through IDispatch. Visual Basic can then call the functions that handle the methods and properties of the object directly, bypassing the IDispatch interface altogether. This is called vtable binding, and it is the fastest way to invoke the properties and methods of an object. A vtable is simply a table of function addresses. If the object does not support dual interfaces, Visual Basic will use early binding instead.

All objects in ADO use dual interfaces, so Visual Basic can work several times faster with your ADO objects if you make your object variables strongly typed with the Dim variable As type statement. The type of the object is typically the name of an object from the object library, although it can also be the name of an interface. If you use the name of the object, Visual Basic silently substitutes the name of the default interface for that object.

Visual Basic chooses early or late binding depending on how you declare the object variable and not on how you create the object. In other words, it doesn't matter whether you use the Set variable = New type statement or the CreateObject function to create the object. What matters is how you declare the object variable. If you declare it as a specific type, Visual Basic will use early binding. If you declare the object variable as Object, Variant, or leave it undeclared, Visual Basic will use late binding.

The code in the StronglyTyped subroutine below uses the faster vtable binding by declaring the variable con as a Connection object:

Sub StronglyTyped()
   Dim lngState As Long
   Dim con As Connection
   Set con = New Connection
   con.Open "Provider=SQLOLEDB;Data Source=persons;" _
      & "Initial Catalog=Performance;User ID=sa;Password=;"
   lngState = con.State
   con.Close
   Set con = Nothing
End Sub

In the NotStronglyTyped subroutine below, the type of the Connection variable isn't known at compile time, so it's declared As Object. Visual Basic uses the slower late binding method to call the methods and properties of the same Connection object used in the StronglyTyped subroutine.

Sub NotStronglyTyped()
   Dim lngState As Long
   Dim con As Object
   Set con = New Connection
   con.Open "Provider=SQLOLEDB;Data Source=persons;" _
      & "Initial Catalog=Performance;User ID=sa;Password=;"
   lngState = con.State
   con.Close
   Set con = Nothing
End Sub

Explicitly Create Objects

Although object variables represent pointers to COM objects, merely declaring an object variable does not automatically create an instance of a COM object. Visual Basic offers two ways to create an instance of a COM object: implicit and explicit. While implicit creation can save some time in development, it usually costs you much more time in debugging and doesn't help at all in performance.

To implicitly create an object, use the As New keyword of the Dim statement. This permits Visual Basic to create the object automatically when you use the object variable and when the object has not been created already. When you use the Dim...As New... feature, Visual Basic adds code before every object reference to determine at run time whether the object is instantiated. This code automatically creates the object if the object variable is either not initialized or set to Nothing.

By using Dim... As New..., you'll lose some of the control over object references. For simple procedures, this won't be an issue. Visual Basic will automatically release all objects after the procedure has ended. You will take a negligible performance hit for using the automatic object creation feature that As New provides. Only you can decide this trade-off is worthwhile for simple procedures, but once you decide, be very consistent.

The following example shows implicit object creation with the As New keywords:

Sub AutomaticCreation()
   Dim rsNS As New Recordset
   rsNS.ActiveConnection = "Provider=SQLOLEDB;Data Source=persons;" _
      & "Initial Catalog=Performance;User ID=sa;Password=;"
   rsNS.CursorLocation = adUseServer
End Sub

There is an important reason why you might want to avoid Dim...As New in some circumstances, even for simple procedures. There are times when having an object variable set to Nothing is a valid, testable state. The Dim...As New feature prevents an object variable from ever being set to Nothing when you test its state. For example, the following snippet will always print, "n is set" to the Debug window:

   Dim n As New Recordset
   If n Is Nothing Then
      Debug.Print "n is nothing"
   Else
      Debug.Print "n is set"
   End If

In the preceding example, the very act of testing to see whether the object is created creates the object! In cases where the object is set to Nothing to indicate a valid condition, as is the case with the NextRecordset method of the Recordset object, you will never be able to detect that the object is set to Nothing.

To explicitly create an object, do not use the As New feature. If you use the object variable and the object has not been created already, Visual Basic will raise an error. You must use the Set statement to create the object when you will use it.

The following example shows explicit object creation:

Sub ExplicitCreation()
   Dim rsNS As Recordset
   Set rsNS = New Recordset
   rsNS.ActiveConnection = "Provider=SQLOLEDB;Data Source=persons;" _
      & "Initial Catalog=Performance;User ID=sa;Password=;"
   rsNS.CursorLocation = adUseServer
   Set rsNS = Nothing
End Sub

Reuse Command Objects

If you are going to use a particular stored procedure, view, or SQL statement several times, don't create a new Command object each time. Use a static variable or a module-level variable to keep a reference to each Command object that you will reuse.

Note This technique might not work well for VBScript running in Active Server Pages, or for applications written for Microsoft Component Transaction Services.

Bind Columns to Field Objects When Looping Through the Records

There are at least two common ways to get the values for fields in a Recordset. One way is to look up each field by its name or ordinal position from the Fields collection of the Recordset object each time you need the value. The other way is to reuse the Field objects in the Fields collection as you iterate through the records in the Recordset.

The code in ColumnsNotBound looks up fields by ordinal position. This incurs the overhead of looking up each field in the Fields collection for each record in the Recordset. For a Recordset with many records, this can get quite expensive.

Sub ColumnsNotBound()
   Dim rsNS As Recordset
   Dim strText As String
   Dim strMoney As String

   Set rsNS = New Recordset
   rsNS.Open "SELECT SomeText, SomeNumber, SomeTime, SomeMoney " _
      "FROM Nonsense", "Provider=SQLOLEDB;Data Source=persons;" _
      & "Initial Catalog=Performance;User ID=sa;Password=;"
   Do Until rsNS.EOF
      strText = rsNS.Fields(0).Value
      strMoney = rsNS.Fields(3).Value
      rsNS.MoveNext
   Loop
   rsNS.Close
   Set rsNS = Nothing
End Sub

The code in ColumnsBound, however, obtains references to the Field objects at the beginning and simply uses those same references when looping through the records.

Sub ColumnsBound()
   Dim rsNS As Recordset
   Dim strText As String
   Dim strMoney As String
   Dim fldText As Field
   Dim fldMoney As Field

   Set rsNS = New Recordset
   rsNS.Open "SELECT SomeText, SomeNumber, SomeTime, SomeMoney " _
      "FROM Nonsense", "Provider=SQLOLEDB;Data Source=persons;" _
      & "Initial Catalog=Performance;User ID=sa;Password=;"

   Set fldText = rsNS.Fields(0)
   Set fldMoney = rsNS.Fields(3)
   Do Until rsNS.EOF
      strText = fldText.Value
      strMoney = fldMoney.Value
      rsNS.MoveNext
   Loop
   rsNS.Close
   Set rsNS = Nothing
End Sub

Use Cursor-Based Updating Only If Needed

Try to avoid the use of cursor-based updating. Although using an SQL statement to update data is not feasible in many scenarios, you should use it where possible. Although updating data through the Recordset object is often more convenient, it is also much more expensive. Despite being cumbersome to use, updating data through an SQL statement is well worth the trouble. The routine that uses SQL will update several dozen records in a 5,000 record table about 30 times faster than the routine that uses a cursor.

The following example shows cursor-based updating:

Sub ADOUpdate()
   Dim cnNS As Connection
   Dim rsNS As Recordset
   Dim fldText As Field
   Dim fldNumber As Field
   Dim lngUpdateEach As Long

   lngUpdateEach = Rnd * 99 + 1
   Set cnNS = New Connection
   cnNS.ConnectionString = "Provider=SQLOLEDB;Data Source=persons;" _
      & "Initial Catalog=Performance;User ID=sa;Password=;"
   cnNS.Open
   Set rsNS = New Recordset
   Set rsNS.ActiveConnection = cnNS
   rsNS.CursorLocation = adUseClient
   rsNS.CursorType = adOpenStatic
   rsNS.LockType = adLockBatchOptimistic
   rsNS.Open "Nonsense", , , , adCmdTable
   Set fldText = rsNS!SomeText
   Set fldNumber = rsNS!SomeNumber
   rsNS.MoveFirst

   Do Until rsNS.EOF
      If fldNumber.Value Mod lngUpdateEach = 0 Then
         fldText.Value = UCase$(fldText.Value)
      End If
      rsNS.MoveNext
   Loop
   rsNS.UpdateBatch
   rsNS.Close
   cnNS.Close
   Set rsNS = Nothing
   Set cnNS = Nothing
End Sub

The next example uses an SQL statement to update data:

Sub SQLUpdate()
   Dim cnNS As Connection
   Dim lngUpdateEach As Long
   Dim cmNS As Command
   Set cmNS = New Command

   lngUpdateEach = Rnd * 99 + 1
   Set cnNS = New Connection
   cnNS.Open "Provider=SQLOLEDB;Data Source=persons;" _
      & "Initial Catalog=Performance;User ID=sa;Password=;"
   Set cmNS.ActiveConnection = cnNS
   cmNS.CommandText = "UPDATE Nonsense SET SomeText = UPPER(SomeText) " _
      & "WHERE SomeNumber % " & lngUpdateEach & " = 0"
   cmNS.CommandType = adCmdText
   cmNS.Prepared = True
   cmNS.Execute , , adExecuteNoRecords
   cnNS.Close
   Set cnNS = Nothing
   Set cmNS = Nothing
End Sub

Use a Stored Procedure with Output Parameters Instead of Singleton Selects

When you know that the result of your query will yield only a single row of data, instead of opening a Recordset for fetching that data, you can use a stored procedure with output parameters.

When you use a Recordset, the query results returned by the data source object include data and metadata. Often the metadata is much larger than the data or is a significant part of the query results. Because of this, you may want to use a stored procedure with output parameters instead.

The following example shows a singleton select statement:

Sub SingletonSelect()
   Dim rs As Recordset
   Dim strText As String
   Dim timTime As Date
   Dim n As Integer

   Set rs = New Recordset
   rs.ActiveConnection = "Provider=SQLOLEDB;Data Source=persons;" _
      & "Initial Catalog=Performance;User ID=sa;Password=;"
   rs.CursorLocation = adUseServer
   rs.CursorType = adOpenForwardOnly
   rs.LockType = adLockReadOnly

   For n = 1 To 50
      rs.Source = "SELECT sometext, sometime FROM nonsense " & _
         "WHERE ID = '" & CStr(CLng(Rnd * 5000)) & "'"
      rs.Open
      strText = rs.Fields(0).Value
      timTime = rs.Fields(1).Value
      rs.Close
   Next
   Set rs = Nothing
End Sub

The next example retrieves two output parameters from a stored procedure that selects a single record:

Sub SingletonSp()
   Dim cmd As Command
   Dim strText As String
   Dim timTime As Date
   Dim n As Integer

   Set cmd = New Command
   cmd.ActiveConnection = "Provider=SQLOLEDB;Data Source=persons;" _
      & "Initial Catalog=Performance;User ID=sa;Password=;"
   cmd.CommandText = "GetTextTimeUsingID"
   cmd.CommandType = adCmdStoredProc
   cmd.Parameters.Append _
      cmd.CreateParameter("inID", adInteger, adParamInput, 4)
   cmd.Parameters.Append _
      cmd.CreateParameter("outText", adChar, adParamOutput, 32)
   cmd.Parameters.Append _
      cmd.CreateParameter("outTime", adDate, adParamOutput, 8)

   For n = 1 To 50
      cmd(0).Value = Rnd * 5000
      cmd.Execute , , adExecuteNoRecords
      strText = cmd(1).Value
      timTime = cmd(2).Value
   Next
   Set cmd = Nothing
End Sub

Here is the code for the stored procedure:

   CREATE PROCEDURE [GetTextTimeUsingID]
   @inID int = 0,
   @outText char(32) OUTPUT,
   @outTime datetime OUTPUT
   AS
   IF @inID = 0 RETURN 2
   SELECT @outText = SomeText, @outTime = SomeTime FROM Nonsense
      WHERE ID = @inID
   RETURN

If You Must Use a Cursor, Use the Collect Methods for Singleton Selects

The Collect method is a hidden method of the Recordset object that lets you quickly get and set the Value property of a Field object without having to obtain a field reference first. This method is appropriate to use when you aren't interested in obtaining or setting any properties other than Field.Value.

Following is an example using the Collect method:

Sub Collect()
   Dim rs As Recordset
   Dim strText As String
   Dim timTime As Date

   Set rs = New Recordset
   rs.ActiveConnection = "Provider=SQLOLEDB;Data Source=persons;" _
      & "Initial Catalog=Performance;User ID=sa;Password=;"
   rs.Source = "SELECT SomeText, SomeNumber, SomeTime, SomeMoney " & _
      "FROM Nonsense WHERE ID = '2500'"
   rs.Open
   strText = rs.Collect(0)
   timTime = rs.Collect(2)
   rs.Close
   Set rs = Nothing
End Sub

Query Only for the Data You Need

Although it is easy to fall into the habit of using SELECT * queries, consider asking only for the columns you need. Also, consider adding restrictions, such as WHERE clauses, to your query to limit the records returned.

Choose CursorLocation, CursorType, and LockType Properties Carefully

If you don't need scrolling or updatability, don't ask for it. The ADO defaults of adUseServer, adOpenForwardOnly, and adLockReadOnly offer you the best performance for doing a forward-only scan through the records. Don't ask for a more functional cursor if your application doesn't require it.

If you do want scrolling, don't default to server cursors as ADO does. The ADO CursorLocation default is adUseServer primarily for backward compatibility reasons. However, for most scrolling scenarios, you will be much better off with a client cursor. Only in specific scenarios, such as extremely large data sets, will you be better off with a server cursor. When using a client cursor, don't ask for anything other than a LockType value of adLockReadOnly unless you really need it. If you ask for updatability, the client cursor engine needs to get additional metadata, which can be very expensive to retrieve.

Tune the Recordset.CacheSize Property

ADO uses the Recordset.CacheSize property to determine the number of rows to fetch and cache. This especially affects server-side cursors. While you are within the range of cached rows, ADO just returns data from the cache. When you scroll out of the range of cached rows, ADO releases the cache and fetches the next CacheSize rows. The default value for the CacheSize property is 1.

How do you determine what value you should use for the CacheSize property in your application? Unfortunately, there isn't a single optimal CacheSize value for all applications. You should try tuning your application with different CacheSize values, and use the value that offers you the best performance. For example, knowing that a small CacheSize value significantly improves performance for fetching data from an Oracle data store might be an important factor for you.

Release Unused ADO Objects

Whenever possible, release ADO objects as soon as you're done with them. This frees up the database and other resources that might be expensive to hold for an extended period. Explicitly close all objects rather than allowing the object to close itself as it is destroyed.

Describe Command Parameters Yourself

In many data stores, getting command parameter information is often as expensive as executing the command. Describe the command parameters yourself instead of getting the parameter information from the provider.

The following example shows how to get the parameter information from the provider:

Sub ProviderDescribedParameters()
   Dim cmd As Command
   Dim lngRetVal As Long
   Dim strText As String
   Dim timTime As Date

   Set cmd = New Command
   cmd.ActiveConnection = "Provider=SQLOLEDB;Data Source=persons;" _
      & "Initial Catalog=Performance;User ID=sa;Password=;"

   cmd.CommandText = "GetTextTimeUsingID"
   cmd.CommandType = adCmdStoredProc

   cmd.Parameters.Refresh

   cmd.Parameters(1).Value = 100
   cmd.Execute
   lngRetVal = cmd.Parameters(0).Value
   strText = cmd.Parameters(2).Value
   timTime = cmd.Parameters(3).Value
   Set cmd = Nothing
End Sub

The next example shows how to describe the parameters manually:

Sub UserDescribedParameters()
   Dim cmd As Command
   Dim lngRetVal As Long
   Dim strText As String
   Dim timTime As Date

   Set cmd = New Command
   cmd.ActiveConnection = "Provider=SQLOLEDB;Data Source=persons;" _
      & "Initial Catalog=Performance;User ID=sa;Password=;"

   cmd.CommandText = "GetTextTimeUsingID"
   cmd.CommandType = adCmdStoredProc

   cmd.Parameters.Append _
      cmd.CreateParameter("Return Value", adInteger, adParamReturnValue)
   cmd.Parameters.Append _
      cmd.CreateParameter("inID", adInteger, adParamInput)
   cmd.Parameters.Append _
      cmd.CreateParameter("outText", adChar, adParamOutput, 32)
   cmd.Parameters.Append _
      cmd.CreateParameter("outTime", adDate, adParamOutput, 8)

   cmd.Parameters(1).Value = 100
   cmd.Execute
   lngRetVal = cmd.Parameters(0).Value
   strText = cmd.Parameters(2).Value
   timTime = cmd.Parameters(3).Value
   Set cmd = Nothing
End Sub

Use Native OLE DB Providers

MDAC ships with native providers for several data stores, including SQL Server, Oracle, and Microsoft Jet (.mdb). With earlier versions, you had to go through the OLE DB Provider for ODBC, which in turn used the appropriate ODBC driver to access these data stores. The provider used as the default for connections is still the OLE DB Provider for ODBC, but you should use these native OLE DB providers so that you can access your data faster and with lower disk and memory footprint. The SQL Server provider is written to TDS, the Oracle provider to OCI, and the Microsoft Jet provider to the Microsoft Jet Engine API.

Disconnect Your Client Cursor from the Connection for R/O and Long-Use Scenarios

Disconnected Recordset objects are supported by the client cursor engine. Use this feature when you are performing a time-consuming operation that doesn't require expensive database resources to be held open. If you need to, you can later reconnect the Recordset to the connection to perform updates.

Following is an example that shows how to disconnect a Recordset:

Sub DisconnectRS()
   Dim con As Connection
   Dim rs As Recordset

   Set con = New Connection
   Set rs = New Recordset
   con.CursorLocation = adUseClient
   con.Open "Provider=SQLOLEDB;Data Source=persons;" _
      & "Initial Catalog=Performance;User ID=sa;Password=;"
   Set rs = con.Execute("SELECT SomeText, SomeNumber, SomeTime, " &  _
      "SomeMoney FROM Nonsense")

   Set rs.ActiveConnection = Nothing
   Set con = Nothing
   '
   ' Process data
   '
   rs.Close
   Set rs = Nothing
End Sub

Anticipate Non–Row Returning Commands

ADO includes an ExecuteOptionEnum option called adExecuteNoRecords. Use this option for commands that do not return rows. When this option is specified, ADO does not create a Recordset object, does not set any cursor properties, and specifies IID_NULL REFIID on ICommand::Execute. Also, because IID_NULL is specified on ICommand::Execute, the provider can optimize for this case by not verifying any rowset properties.

The following example demonstrates how the adExecuteNoRecords option is used:

Sub ExecuteNoRecords()
   Dim con As Connection
   Set con = New Connection
   con.Open strConnection
   con.Execute "INSERT INTO Nonsense VALUES('" & Greeking(32) & _
      "', " & CStr(CLng(Rnd * &H7FFFFFFF)) & _
      ", " & CStr(CLng(Now)) & _
      ", " & CStr(CCur(curStart)) & ",DEFAULT ,DEFAULT, DEFAULT, " & _
      "DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT)", , _
      adExecuteNoRecords
   con.Close
   Set con = Nothing
End Sub

Use Connection.Execute for Single Execution

ADO has some optimizations for one-time command executions when the executions are done through the Execute method of the Connection object. This is a common user scenario in Microsoft Internet Information Server (IIS), Active Server Pages (ASP), and Microsoft Component Services (formerly Microsoft Transaction Server) environments, where the code typically opens a connection, executes a row returning or non–row returning command, processes results, and closes the connection. For such scenarios, use Connection.Execute instead of Recordset.Open or Command.Execute. When you use Connection.Execute, ADO doesn't preserve any command state information, which leads to an improvement in performance. However, if you need a more functional cursor or if you need to use Command.Parameters, you might still need to use Recordset.Open or Command.Execute.

Use ADO C++ Extensions (C++ Users)

ADO is an Automation server, which means it implements Automation interfaces. Therefore, it provides language-independent access to OLE DB. However, although ADO interfaces are accessible from C, C++, and Java, structures such as VARIANTs, which are easy to use from Visual Basic for Applications (VBA), are quite cumbersome to use from C, C++, and Java.

In versions of Microsoft Visual C++ earlier than 5.0, Automation interfaces were hard to use because of lack of language support for COM and VARIANTs. Now, with native compiler support for COM in Visual C++ 5.0, Automation interfaces are much easier to use from Visual C++. However, if you still want to fetch data into C types instead of VARIANTs, you can do so by using the ADO C++ extensions. Besides avoiding the VARIANT overhead, the C++ extensions offer good performance. When you use them, ADO doesn't need to get the column information from the provider. Instead, ADO uses the column information supplied at design time in the form of binding entries.

The following C++ code example shows how to use the ADO C++ extensions to get the values from three VARCHAR fields.

class CAuthor :
   public CADORecordBinding
{
BEGIN_ADO_BINDING(CCustomRs1)
   ADO_VARIABLE_LENGTH_ENTRY4(1, adVarChar, m_szau_id,
      sizeof(m_szau_id), FALSE)
   ADO_VARIABLE_LENGTH_ENTRY4(2, adVarChar, m_szau_fname,
      sizeof(m_szau_fname), FALSE)
   ADO_VARIABLE_LENGTH_ENTRY4(3, adVarChar, m_szau_lname,
      sizeof(m_szau_lname), FALSE)
END_ADO_BINDING()

protected:
   char m_szau_id[12];
   char m_szau_fname[21];
   char m_szau_lname[41];
};

void FetchAuthorData()
{
   CAuthor author;
   _RecordsetPtr pRs;
   IADORecordBinding *piAdoRecordBinding;

   pRs.CreateInstance(__uuidof(Recordset));
   pRs->Open("select au_id, au_fname, au_lname from Employees",
      "Provider=SQLOLEDB;Data Source=sureshk1;Database=pubs;"
      "User Id=sa;Password=;",
      adOpenForwardOnly, adLockReadOnly, adCmdText);

   pRs->QueryInterface(__uuidof(IADORecordBinding),
      (LPVOID *)&piAdoRecordBinding);
   piAdoRecordBinding->BindToRecordset(&author);

   while (VARIANT_FALSE == pRs->EOF)
   {
      printf("%s %s %s", author.m_szau_id, author.m_szau_fname,
         author.m_szau_lname);

      pRs->MoveNext();
   }

   piAdoRecordBinding->Release();
}

Middle-Tier Considerations

Use Session (OLE DB) or Connection (ODBC) Pooling

A database connection is an expensive resource to open and close. Therefore, pooling this resource offers a huge performance improvement for middle-tier applications.

When you use MDAC, you don't have to worry about how to pool your database connections. MDAC takes care of it for you. Pooling is supported at two levels: OLE DB sessions and ODBC connections. If you use ADO, your database connections are pooled automatically by OLE DB session pooling. If you use ODBC, the new Connection Pooling tab in the ODBC Data Source Administrator lets you control the ODBC connection pooling settings and the ODBC Driver Manager takes care of pooling for you.

Use ADO Like an Apartment Model

Although the ADO implementation is free-threaded, don't use it in that way in the middle tier. Don't cache an instance of an ADO object, such as a Connection, globally and invoke methods on it concurrently from multiple threads. If each client request in your application model invokes the Connection.Execute method on a globally cached Connection object on the middle tier, your application will not scale. This is because of synchronization in the Connection.Execute method.

You will get much better throughput by using an application model where each client request creates a new instance of a Connection object, calls Connection.Open and then Connection.Execute, and releases the Connection object on the middle tier. Each request does have the additional overhead of creating a new instance of a Connection object and obtaining a connection from the connection pool. However, because your Connection.Execute call isn't synchronized, the throughput is much better.

ASP Considerations

Don't cache ADO objects in Global.asa. See "Use ADO Like an Apartment Model" earlier in this document.

Conclusion

Improving the performance of any application is both a science and an art. The recommendations listed here will help with many areas, but every application has different circumstances. The environments may differ from one installation to the next. A setting that makes the application run faster on one machine may make it run slower on a different machine. Even different database schemas will affect many of the suggestions in this article.

There is no substitute for advanced techniques like code profiling, performance monitoring, and good old trial and error. As with any scientific approach, you should vary only one element at a time and note whether performance improves or worsens with each variation. If performance deteriorates, before assuming that the change was the cause of the problem, revert the change and verify that you can reproduce the original behavior.


ActiveX Data Objects (ADO) Technical Articles

  • Migrating from DAO to ADO
  • ADO and RDS Security Issues in Microsoft Internet Explorer
  • Mapping ADO Methods to OLE DB Interfaces
  • Mapping ADOX Properties and Methods to OLE DB Interfaces
  • Using the Customization Handler Feature in RDS 2.1
  • Saving ADO Recordsets in XML Format


Migrating from DAO to ADO

MDAC Technical Articles

Migrating from DAO to ADO: Using ADO with the Microsoft Jet Provider

Alyssa Henry

June 1999

Introduction

This document is a guide to revising code that uses Microsoft® Data Access Objects (DAO) into code that uses Microsoft ActiveX® Data Objects (ADO). It also guides those who are writing new code using ADO with the OLE DB Provider for Microsoft Jet (Microsoft Jet Provider). It compares the general differences between DAO and ADO and details the mapping of objects, properties, and methods from DAO to ADO. It also highlights functional or semantic differences between similarly named methods or properties.

This document assumes that you have already decided to use ADO instead of DAO to access a Microsoft Jet database, or that you are investigating what it will take to port your code from DAO to ADO. It covers some of the advantages of using ADO over DAO. It describes many features of the Microsoft Jet Provider and demonstrates how to use them with ADO. (The ADO documentation is meant to be provider-neutral, so it lacks much of this information.) This document does not attempt to provide in-depth detail on particular objects, properties, or methods. Refer to the online documentation provided with DAO and ADO for specific details on a particular item.

Contents

  • General Differences

    ADO: Data Manipulation

    ADOX: Data Definition and Security

    JRO: Replication

  • Getting Started
  • Opening a Database

    Microsoft Jet Databases

    Setting Microsoft Jet Options

    Secured Microsoft Jet Databases

    External Databases

    The Current Microsoft Access Database

  • Retrieving and Modifying Data

    Opening a Recordset

    Navigating Within a Recordset

    Finding Records in a Recordset

    Filtering and Sorting Data in a Recordset

    Updating Data in a Recordset

    Executing Queries

  • Defining and Retrieving a Database’s Schema

    Creating a Database

    Retrieving Schema Information

    Creating and Modifying Tables

    Creating an Index

    Defining Keys and Relationships

    Creating and Modifying Queries

  • Security

    Changing a Password

    Creating Users and Groups

    Adding a User to a Group

    Setting Permissions

    Determining an Object's Owner

  • Replication

    Making a Database Replicable

    Making Objects Local or Replicable

    Creating a Replica

    Creating a Partial Replica

    Listing Filters

    Synchronizing Data

    Listing Synchronization Conflict Tables

  • Miscellaneous

    Handling Errors

    Using Transactions

    Compacting a Database

    Refreshing the Cache

  • New Features in ADO, ADOX, and JRO

    Creatable Recordset Objects

    Microsoft Data Links

    User Roster

    Enhanced Auto-Increment (Counter) Columns

    Replication

  • Obsolete Properties and Methods
  • Conclusion
  • Appendix A: DAO to ADO Quick Reference
  • Appendix B: Microsoft Jet 4.0 OLE DB Properties Reference

    ADO Connection Properties

    ADO Recordset Properties

    ADOX Table Properties

    ADOX Column Properties

    ADOX Index Properties

  • Appendix C: Microsoft Jet 4.0 OLE DB Provider Defined Property Values
  • Appendix D: Microsoft Jet 4.0 ANSI Reserved Words


General Differences

Three distinct object models in ADO together provide the functionality found in DAO. These three models are ADO (ADODB), Microsoft ADO Extensions for DDL and Security (ADOX), and Microsoft Jet and Replication Objects (JRO). The functionality of DAO is divided among these three models because many applications will need just one of these subsets of functionality. By splitting the functionality out, applications do not need to incur the overhead of loading additional information into memory unnecessarily. The following sections provide an overview of these three object models.

ADO: Data Manipulation

ADO enables your client applications to access and manipulate data through any OLE DB provider. ADO contains objects for connecting to a data source and reading, adding, updating, or deleting data.

The ADO Connection object defines a session for a user of a particular data source. This differs from DAO in which the Workspace object defines the session for a user and the Database object defines the data source.

The ADO Command object is similar to the DAO QueryDef object in that both objects can be used to execute an SQL statement against a data source. Likewise, the Recordset object in both ADO and DAO can be used to view the contents of a table or the results from executing an SQL statement.

ADOX: Data Definition and Security

The ADOX model contains objects for data definition (such as tables, views, and indexes) and creating and modifying users and groups. With ADOX, an administrator can control database schema and grant and revoke permissions on objects to users and groups.

The Catalog object is the container for the data definition collections (Tables, Procedures, and Views) and the security collections (Users and Groups). This differs from DAO in which the Database object contains the data definition collections and the Workspace object contains the security collections. Each Catalog object is associated with only one Connection whereas a DAO Workspace may contain multiple Databases.

The Table, Index, and Column objects in ADO are roughly equivalent to the TableDef, IndexDef, and Field objects in DAO. Each of these objects also has a standard ADO Properties collection.

JRO: Replication

The JRO model contains objects, properties, and methods for creating, modifying, and synchronizing replicas. It is designed specifically for use with the Microsoft Jet Provider. Unlike ADO and ADOX, JRO cannot be used with data sources other than Microsoft Jet databases.

The primary object in the JRO model is the Replica object. The Replica object is used to create new replicas, to retrieve and modify properties of an existing replica, and to synchronize changes with other replicas. This differs from DAO in which the Database object is used for these tasks.

JRO also includes a JetEngine object for two specific Microsoft Jet database engine features: compacting the database and refreshing data from the memory cache.


Getting Started

To run the code examples in this document, you need references to the ADO, ADOX, and JRO type libraries in your database or project. By default, new Microsoft Access 2000 databases have a reference to ADO. However, to run these samples you'll need to add references to ADOX and JRO. If you converted an existing database to Access 2000 or are programming in Microsoft Visual Basic® or some other application, you will need to include all of the references yourself.

To add these references in Access 2000:

  1. Open a module.
  2. From the Tools menu select References…
  3. From the list, select "Microsoft ActiveX Data Objects 2.1 Library."
  4. From the list, select "Microsoft ADO Ext. 2.1 for DDL and Security."
  5. From the list, select "Microsoft Jet and Replication Objects 2.1 Library."
  6. Click OK.

To add these references in Visual Basic:

  1. Open a project.
  2. From the Project menu select References…
  3. From the list, select "Microsoft ActiveX Data Objects 2.1 Library."
  4. From the list, select "Microsoft ADO Ext. 2.1 for DDL and Security."
  5. From the list, select "Microsoft Jet and Replication Objects 2.1 Library."
  6. Click OK.

If you include references to both ADO and DAO in the same project, you need to explicitly specify which library to use when declaring objects because DAO and ADO include several objects with the same names. For example, both models include a Recordset object, so the following code is ambiguous:

Dim rst as Recordset

To specify which object model you want to use, include a qualifier as shown:

Dim rstADO As ADODB.Recordset
Dim rstDAO As DAO.Recordset

If the qualifier is omitted, Visual Basic for Applications will choose the object from the model that is referenced first. So if your list of references is ordered as follows in the References dialog box, an object declared as Recordset with no qualifier would be a DAO Recordset.

Visual Basic for Applications
Microsoft DAO 3.6 Object Library
Microsoft ActiveX Data Objects 2.1 Library
Microsoft ADO Ext. 2.1 for DDL and Security
Microsoft Jet and Replication Objects 2.1 Library


Opening a Database

Generally, one of the first steps in writing an application to access data is to open the data source. When using the Microsoft Jet database engine, you can open Microsoft Jet databases, other external data sources such as Microsoft Excel, Paradox, and dBASE with Microsoft Jet's ISAM components, and ODBC data sources.

Microsoft Jet Databases

The Microsoft Jet Provider can open Microsoft Jet 4.0 databases as well as databases created with previous versions of the Microsoft Jet database engine. These examples use only Microsoft Jet 4.0 databases.

The following code demonstrates how to open a Microsoft Jet database for shared, updateable access. Then the code immediately closes the database because this code is for demonstration purposes.

DAO

Sub DAOOpenJetDatabase()

   Dim db As DAO.Database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")
   db.Close

End Sub

ADO

Sub ADOOpenJetDatabase()

   Dim cnn As New ADODB.Connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"
   cnn.Close

End Sub

These two code listings for opening a database look somewhat different, but are not all that dissimilar. Aside from the fact that the objects have different names, the major difference is the format of the string passed to the method that opens the database.

The ADO connection string in this example has two parts: the provider tag and the data source tag. The provider tag indicates which OLE DB provider to use, and the data source tag indicates which database to open. With DAO, it is assumed that you want to use Microsoft Jet, whereas with ADO you must explicitly specify that you want to use Microsoft Jet.

By default, both DAO and ADO open a database for shared updateable access, when using the Microsoft Jet Provider. However, there may be times when you want to open the database exclusively or in read-only mode.

The following code listings show how to open (and then close) a shared, read-only database using DAO and ADO.

DAO

Sub DAOOpenJetDatabaseReadOnly()

   Dim db As DAO.Database

   ' Open shared, read-only.
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb", False, True)
   db.Close

End Sub

ADO

Sub ADOOpenJetDatabaseReadOnly()

   Dim cnn As New ADODB.Connection

   ' Open shared, read-only
   cnn.Mode = adModeRead
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"
   cnn.Close

End Sub

In the DAO listing, the second two parameters to the OpenDatabase method indicate exclusive and read-only access respectively. In the ADO listing, the Connection object's Mode property is set to the read-only constant (adModeRead). By default, ADO connections are opened for shared, updateable access unless another mode is set (for example, adModeShareExclusive).

Alternatively, the ADO listing could have been written in a single line of code as follows:

Sub ADOOpenJetDatabaseExclusive()

   Dim cnn As New ADODB.Connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;Mode=" & adModeRead
   cnn.Close

End Sub

In this listing, the Mode property was specified as a part of the connection string to the Open method rather than as a property of the Connection object. In ADO, you can set connection properties as a property or string them together with other properties to create the connection string. Even provider-specific properties (prefixed by "Jet OLEDB:" for Microsoft Jet–specific properties) can be set as part of the connection string or with the Connection object's Properties collection. For a description of the available properties, see Appendix B: Microsoft Jet 4.0 OLE DB Properties Reference later in this document.

Setting Microsoft Jet Options

The Microsoft Jet database engine exposes a number of settable options that will dictate how the engine will behave. These options often have a direct impact on performance. By default when the Microsoft Jet database engine is initialized, it uses the values set in the Windows registry under the \HKEY_LOCAL_MACHINES\Software\Microsoft\Jet key. At run time, it is possible to temporarily override these settings. In ADO, these values are set as part of the connection string.

The following listings demonstrate how to override the Page Timeout setting of the engine and open a database using that setting.

DAO

Sub DAOSetJetDBOption()

   Dim db As DAO.Database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")
   DBEngine.SetOption dbPageTimeout, 4000
   db.Close

End Sub

ADO

Sub ADOSetJetDBOption()

   Dim cnn As New ADODB.Connection

   cnn.Provider = "Microsoft.Jet.OLEDB.4.0;"
   cnn.Open ".\NorthWind.mdb"
   cnn.Properties("Jet OLEDB:Page Timeout") = 4000
   cnn.Close

End Sub

With DAO, you use the SetOption method to set the values for these database settings. There is no corresponding GetOption method to retrieve the values. With ADO, you use a property in the Connection object's Properties collection. You can read the value of the property using ADO; however this value is not accurate unless you have previously set the value for the property. For example, the Jet OLEDB:Page Timeout property will return the value 0 prior to setting this property even though the value defined for this property in the HKEY_LOCAL_MACHINE\Software\Microsoft\Jet\4.0\Engines\Jet 4.0\PageTimeout registry key is actually 5000.

Another minor difference between ADO and DAO is that with ADO the Connection must be opened before these properties are available. With DAO, these properties can be set on the DBEngine object prior to opening the database.

As shown in the listings, you can optionally set the provider in the Provider property, rather than in the connection string. The "Data Source=" section of the connection string can also be omitted, and ADO will assume this is the default value for the path in the connection string. This is simply an alternative method of opening a connection; with ADO there are sometimes many equally valid ways to accomplish a task. Later in this document, the section "Opening a Database with User Level Security," explains a scenario in which it is required that you indicate the provider in the Provider property rather than in the connection string.

The following table lists the values that can be set with DAO's SetOption method and the corresponding property to use with ADO.

DAO constant | ADO property
dbPageTimeout | Jet OLEDB:Page Timeout
dbSharedAsyncDelay | Jet OLEDB:Shared Async Delay
dbExclusiveAsyncDelay | Jet OLEDB:Exclusive Async Delay
dbLockRetry | Jet OLEDB:Lock Retry
dbUserCommitSync | Jet OLEDB:User Commit Sync
dbImplicitCommitSync | Jet OLEDB:Implicit Commit Sync
dbMaxBufferSize | Jet OLEDB:Max Buffer Size
dbMaxLocksPerFile | Jet OLEDB:Max Locks Per File
dbLockDelay | Jet OLEDB:Lock Delay
dbRecycleLVs | Jet OLEDB:Recycle Long-Valued Pages
dbFlushTransactionTimeout | Jet OLEDB:Flush Transaction Timeout

Secured Microsoft Jet Databases

Microsoft Jet databases can be secured in one of two ways: with either share-level security or user-level security. With share-level security, the database is secured with a password. Anyone attempting to open the database must specify the correct database password. With user-level security, each user is assigned a user name and password to open the database. Microsoft Jet uses a separate workgroup information file, typically named "system.mdw" to store user information and passwords. See the section, "Security" for more information about creating and using secured Microsoft Jet databases.

Share-Level (Password Protected) Databases

The following listings demonstrate how to open a Microsoft Jet database that has been secured at the share level.

DAO

Sub DAOOpenDBPasswordDatabase()

   Dim db As DAO.Database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb", _
      False, False, ";pwd=password")
   db.Close

End Sub

ADO

Sub ADOOpenDBPasswordDatabase()

   Dim cnn As New ADODB.Connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;" & _
      "Jet OLEDB:Database Password=password;"
   cnn.Close

End Sub

In DAO, the Connect parameter of the OpenDatabase method sets the database password when opening a database. With ADO, the Microsoft Jet Provider connection property Jet OLEDB:Database Password sets the password instead.

Opening a Database with User-Level Security

These next listings demonstrate how to open a database that is secured at the user level using a workgroup information file named "system.mdw".

DAO

Sub DAOOpenSecuredDatabase()

   Dim wks As DAO.Workspace
   Dim db As DAO.Database

   DBEngine.SystemDB = _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

   Set wks = DBEngine.CreateWorkspace("", "Admin", "")
   Set db = wks.OpenDatabase(".\NorthWind.mdb")

   db.Close
   wks.Close

End Sub

ADO

Sub ADOOpenSecuredDatabase()

   Dim cnn As New ADODB.Connection

   cnn.Provider = "Microsoft.Jet.OLEDB.4.0;"
   cnn.Properties("Jet OLEDB:System database") = _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

   cnn.Open "Data Source=.\NorthWind.mdb;User Id=Admin;Password=;"
   cnn.Close

End Sub

In ADO, a Microsoft Jet provider-specific connection property, Jet OLEDB:System database, specifies the system database. This is equivalent to setting the DBEngine object's SystemDB property before opening a database using DAO.

Notice that in this example, the Provider property is set as a property of the Connection object rather than as part of the ConnectionString argument to the Open method. That is because before you can reference provider-specific properties from the Connection object's Properties collection, it is necessary to indicate which provider you are using. If the first line of code had been omitted, error 3265 (adErrItemNotFound), "ADO could not find the object in the collection corresponding to the name or ordinal reference requested by the application." would have occurred when trying to set the value for the Jet OLEDB:System database property.

Note that in both DAO and ADO, setting the system database may not be necessary. You may omit the code that sets the system database if you want to use the current Microsoft Jet workgroup information file as specified in the SystemDB key in the Microsoft Jet registry entries. See the book Microsoft Jet Database Engine Programmer’s Guide for more information about Microsoft Jet Security.

External Databases

The Microsoft Jet database engine can be used to access other database files, spreadsheets, and textual data stored in tabular format through installable ISAM drivers.

The following listings demonstrate how to open a Microsoft Excel 2000 spreadsheet first using DAO, then using ADO and the Microsoft Jet provider.

DAO

Sub DAOOpenISAMDatabase()

   Dim db As DAO.Database

   Set db = DBEngine.OpenDatabase(".\Sales.xls", _
      False, False, "Excel 8.0;")

   db.Close

End Sub

ADO

Sub ADOOpenISAMDatabase()

   Dim cnn As New ADODB.Connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\Sales.xls" & _
      ";Extended Properties=Excel 8.0;"

   cnn.Close

End Sub

The DAO and ADO code for opening an external database is similar. In both examples, the name of the external file (Sales.xls) is used in place of a Microsoft Jet database file name. With both DAO and ADO you must also specify the type of external database you are opening, in this case, an Excel 2000 spreadsheet. With DAO, the database type is specified in the Connect argument of the OpenDatabase method. The database type is specified in the Extended Properties property of the Connection with ADO. The following table lists the strings to use to specify which ISAM to open.

Database | String
dBASE III | dBASE III;
dBASE IV | dBASE IV;
dBASE 5 | dBASE 5.0;
Paradox 3.x | Paradox 3.x;
Paradox 4.x | Paradox 4.x;
Paradox 5.x | Paradox 5.x;
Excel 3.0 | Excel 3.0;
Excel 4.0 | Excel 4.0;
Excel 5.0/Excel 95 | Excel 5.0;
Excel 97 | Excel 97;
Excel 2000 | Excel 8.0;
HTML Import | HTML Import;
HTML Export | HTML Export;
Text | Text;
ODBC ODBC;
DATABASE=database;
UID=user;
PWD=password;
DSN=datasourcename;

Note that if you are migrating from DAO 3.5 or earlier with the FoxPro ISAM to ADO with the Microsoft Jet Provider, you will need to use Visual FoxPro ODBC Driver as Microsoft Jet 4.0 does not support the FoxPro ISAM.

The Current Microsoft Access Database

When you open Microsoft Access, you are opening a Microsoft Jet database. When writing code within Access, you may often want to use the same connection to Microsoft Jet as Access is using. To allow you to do this, Microsoft Access 2000 exposes two mechanisms: CurrentDB() and CurrentProject.Connection allow you to get a DAO Database object and an ADO Connection object, respectively, for the database Access currently has open.

The following listings demonstrate how to get a reference to the database currently open in Microsoft Access.

DAO

Sub DAOGetCurrentDatabase()

   Dim db As DAO.Database

   Set db = CurrentDb()

End Sub

ADO

Sub ADOGetCurrentDatabase()

   Dim cnn As ADODB.Connection

   Set cnn = CurrentProject.Connection

End Sub


Retrieving and Modifying Data

Both DAO and ADO include a Recordset object that is the primary object used for retrieving and modifying data. A Recordset object represents a set of records in a table or a set of records that are a result of a query.

The Recordset object contains a Fields collection that contains Field objects, each of which represent a single column of data within the Recordset.

Opening a Recordset

Like DAO, ADO Recordset objects can be opened from several different objects. In ADO, a Recordset can be opened with the Connection object Execute method, the Command object Execute method, or the Recordset object Open method. ADO Recordset objects cannot be opened directly from Table, Procedure, or View objects. ADO Recordset objects opened with the Execute method are always forward-only, read-only recordsets. If you need to be able to scroll or update data within the Recordset you must use the Recordset object Open method.

The CursorType, LockType, and Options parameters of the Open method determine the type of Recordset that is returned. The table below shows how the parameters to the DAO Recordset object Open method can be mapped to ADO properties.

DAO Recordset type | ADO Recordset properties or parameters
dbOpenDynaset | CursorType=adOpenKeyset
dbOpenSnapshot | CursorType=adOpenStatic
dbOpenForwardOnly | CursorType=adOpenForwardOnly
dbOpenTable | CursorType=adOpenKeyset, Options=adCmdTableDirect
DAO Recordset Options values | ADO Recordset properties
dbAppendOnly | Properties("Append-Only Rowset")
dbSQLPassThrough | Properties("Jet OLEDB:ODBC Pass-Through Statement")
dbSeeChanges | No equivalent.
dbDenyWrite | No equivalent.
dbDenyRead | No equivalent.
dbInconsistent | Properties("Jet OLEDB:Inconsistent") = True
dbConsistent | Properties("Jet OLEDB:Inconsistent") = False
DAO Recordset LockType values | ADO Recordset LockType values
dbReadOnly | adLockReadOnly
dbPessimistic | adLockPessimistic
dbOptimistic | adLockOptimistic

The Microsoft Jet Provider does not support a number of combinations of CursorType and LockType — for example, CursorType=adOpenDynamic and LockType=adLockOptimistic. If you specify an unsupported combination, ADO will pass your request to the Microsoft Jet Provider, which will then degrade to a supported CursorType or LockType. Use the CursorType and LockType properties of the Recordset once it is opened to determine what type of Recordset was created.

The following listings demonstrate how to open a forward-only, read-only Recordset, then prints the values of each field.

DAO

Sub DAOOpenRecordset()

   Dim db As DAO.Database
   Dim rst As DAO.Recordset
   Dim fld As DAO.Field

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Open the Recordset
   Set rst = db.OpenRecordset _
      ("SELECT * FROM Customers WHERE Region = 'WA'", _
      dbOpenForwardOnly, dbReadOnly)

   ' Print the values for the fields in
   ' the first record in the debug window
   For Each fld In rst.Fields
      Debug.Print fld.Value & ";";
   Next

   Debug.Print

   ' Close the recordset
   rst.Close

End Sub

ADO

Sub ADOOpenRecordset()

   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset
   Dim fld As ADODB.Field

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Open the forward-only,
   ' read-only recordset
   rst.Open _
      "SELECT * FROM Customers WHERE Region = 'WA'", _
      cnn, adOpenForwardOnly, adLockReadOnly

   ' Print the values for the fields in
   ' the first record in the debug window
   For Each fld In rst.Fields
      Debug.Print fld.Value & ";";
   Next

   Debug.Print

   ' Close the recordset
   rst.Close

End Sub

In the DAO and ADO code above, the Recordset is opened and then the data in the first record of the Recordset is printed to the Debug window by iterating through each field in the Fields collection and printing its Value.

Using Client Cursors

ADO Recordset objects have an additional property, CursorLocation, not found in DAO that affects the functionality and performance of the Recordset. This property has two valid values: adUseServer and adUseClient. The default is adUseServer, which indicates that the provider's or data source's cursors should be used.

When the CursorLocation property is set to adUseClient, ADO will invoke the Microsoft Cursor Service for OLE DB to create the Recordset. The Cursor Service retrieves data from the underlying data provider using a forward-only, read-only cursor and stores all of the data in its own cache on the client. When data is requested through ADO, the Cursor Service returns the data from its own cache rather than passing the request down to the provider. This often results in fairly significant performance gains when the underlying data source is on a remote server as is often the case with SQL Server. However, when the data is stored in a local Microsoft Jet database, this can result in fairly significant performance degradation as the data is being cached twice on the client, once in Microsoft Jet and once in the Cursor Service.

While there may be a performance penalty for using the Cursor Service, it does provide some functionality found in DAO that is not currently exposed in the Microsoft Jet Provider. For example, adUseClient must be specified for CursorLocation in order to sort an existing Recordset. (See the section, "Filtering and Sorting Data in a Recordset" for more information about how to use the Cursor Service to sort a Recordset.)

When developing your application, you'll generally want to specify adUseServer as the CursorLocation to get performance and functionality similar to DAO. However, in the few cases where the Microsoft Jet Provider does not provide the functionality needed, consider using client cursors.

Navigating Within a Recordset

A Recordset object has a current position. The position may be before the first record (BOF), after the last record (EOF), or on a specific record within the Recordset. When retrieving information with the Field object, the information always pertains to the record at the current position.

Moving To Another Record

Both DAO and ADO contain several methods for moving from one record to another. These methods are Move, MoveFirst, MoveLast, MoveNext, and MovePrevious.

The following listings demonstrate how to use the MoveNext method to iterate through all of the records in the Recordset.

DAO

Sub DAOMoveNext()

   Dim db As DAO.Database
   Dim rst As DAO.Recordset
   Dim fld As DAO.Field

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Open the Recordset
   Set rst = db.OpenRecordset _
      ("SELECT * FROM Customers WHERE Region = 'WA'", _
      dbOpenForwardOnly, dbReadOnly)

   ' Print the values for the fields in
   ' the first record in the debug window
   Do Until rst.EOF
      For Each fld In rst.Fields
         Debug.Print fld.Value & ";";
      Next
      Debug.Print
      rst.MoveNext
   Loop

   ' Close the recordset
   rst.Close

End Sub

ADO

Sub ADOMoveNext()

   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset
   Dim fld As ADODB.Field

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" _
      & "Data Source=.\NorthWind.mdb;"

   ' Open the forward-only,
   ' read-only recordset
   rst.Open _
      "SELECT * FROM Customers WHERE Region = 'WA'", _
      cnn, adOpenForwardOnly, adLockReadOnly

   ' Print the values for the fields in
   ' the first record in the debug window
   Do Until rst.EOF
      For Each fld In rst.Fields
         Debug.Print fld.Value & ";";
      Next
      Debug.Print
      rst.MoveNext
   Loop

   ' Close the recordset
   rst.Close

End Sub

Notice that the code for iterating through the Recordset in DAO and ADO is identical.

ADO Shortcuts to Display and Open Recordsets

In the previous example, the ADO code could be rewritten to use the Recordset object's GetString method to print the data to the Debug window. This method returns a formatted string containing data from the records in the recordset. Using GetString, the While loop in the previous ADO example could be replaced with the single line:

   Debug.Print rst.GetString(adClipString, , ";")

This method is handy for debugging as well as populating grids and other controls that allow you to pass in a formatted string representing the data. GetString is also faster than looping through the Recordset and generating the string with Visual Basic for Applications code.

The ADO example could also have been rewritten more concisely by using the Recordset object's Open method's ActiveConnection parameter to specify the connection string rather than first opening a Connection object and then passing that object in as the ActiveConnection. The Recordset object's Open method call would look like this:

   rst.Open _
      "SELECT * FROM Customers WHERE Region = 'WA'", _
      "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;", _
      adOpenForwardOnly, adLockReadOnly

Internally, these two mechanisms are essentially the same. When you pass a connection string to the Recordset object's Open method (rather than assigning a Connection object to the Recordset object's ActiveConnection property), ADO creates a new, internal Connection object. If you plan on opening more than one Recordset from a given data source, or opening Command or Catalog objects, create a Connection object and use that object for the ActiveConnection. This will reduce the amount of resources consumed and increase performance.

Determining Current Position

When working with records in a Recordset it may be useful to know what the record number of the current record is. Both ADO and DAO have an AbsolutePosition property that can be used to determine the record number. The following code listings demonstrate how to use the AbsolutePosition property in both DAO and ADO.

DAO

Sub DAOGetCurrentPosition()

   Dim db As DAO.Database
   Dim rst As DAO.Recordset

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Open the Recordset
   Set rst = db.OpenRecordset("SELECT * FROM Customers", _
      dbOpenDynaset)

   ' Print the absolute position
   Debug.Print rst.AbsolutePosition

   ' Move to the last record
   rst.MoveLast

   ' Print the absolute position
   Debug.Print rst.AbsolutePosition

   ' Close the recordset
   rst.Close

End Sub

ADO

Sub ADOGetCurrentPosition()

   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Open the recordset
   rst.CursorLocation = adUseClient
   rst.Open "SELECT * FROM Customers", cnn, adOpenKeyset, _
      adLockOptimistic, adCmdText

   ' Print the absolute position
   Debug.Print rst.AbsolutePosition

   ' Move to the last record
   rst.MoveLast

   ' Print the absolute position
   Debug.Print rst.AbsolutePosition

   ' Close the recordset
   rst.Close

End Sub

The ADO and DAO code for determining the current position within the Recordset looks very similar. However, note that the results printed to the debug window are different. With DAO, the AbsolutePosition property is zero-based; the first record in the recordset has an AbsolutePosition of 0. With ADO, the AbsolutePosition property is one-based; the first record in the recordset has an AbsolutePosition of 1.

Note that in the ADO code example, the CursorLocation property is set to adUseClient. If the CursorLocation is not specified or is set to adUseServer, the AbsolutePosition property will return adUnknown (-1) because the Microsoft Jet Provider does not support retrieving this information. See the section, "Using Client Cursors" for more information about using the CursorLocation property.

In addition to the AbsolutePosition property, DAO also has a PercentPosition property that returns a percentage representing the approximate position of the current record within the Recordset. ADO does not have a property or method that provides the functionality equivalent to DAO's PercentPosition property. However, when using client cursors (adUseClient), the user can calulate an approximate percent position from the AbsolutePosition and RecordCount properties in ADO.

Finding Records in a Recordset

Both DAO and ADO have two mechanisms for locating a record in a Recordset: Find and Seek. With both mechanisms you specify criteria to use to locate a matching record. In general, for equivalent types of searches, Seek provides better performance than Find. However, because Seek uses an underlying index to locate the record, it is limited to Recordset objects that have associated indexes. For Microsoft Jet databases only, Recordset objects based on a table (DAO dbOpenTable, ADO adCmdTableDirect) with an index support Seek.

Using the Find Method

The following listings demonstrate how to locate a record using Find.

DAO

Sub DAOFindRecord()

   Dim db As DAO.Database
   Dim rst As DAO.Recordset

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Open the Recordset
   Set rst = db.OpenRecordset("Customers", dbOpenDynaset)

   ' Find the first customer whose country is USA
   rst.FindFirst "Country = 'USA'"

   ' Print the customer id's of all customers in the USA
   Do Until rst.NoMatch
      Debug.Print rst.Fields("CustomerId").Value
      rst.FindNext "Country = 'USA'"
   Loop

   ' Close the recordset
   rst.Close

End Sub

ADO

Sub ADOFindRecord()

   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Open the recordset
   rst.Open "Customers", cnn, adOpenKeyset, adLockOptimistic

   ' Find the first customer whose country is USA
   rst.Find "Country='USA'"

   ' Print the customer id's of all customers in the USA
   Do Until rst.EOF
      Debug.Print rst.Fields("CustomerId").Value
      rst.Find "Country='USA'", 1
   Loop

   ' Close the recordset
   rst.Close

End Sub

DAO includes four find methods: FindFirst, FindLast, FindNext, FindPrevious. You choose which method to use based on the point from which you want to start searching (beginning, end, or curent record) and in which direction you want to search (forward or backward).

ADO has a single method: Find. Searching always begins from the current record. The Find method has parameters that allow you to specify the search direction as well as an offset from the current record at which to beginning searching (SkipRows). The following table shows how to map the four DAO methods to the equivalent functionality in ADO.

DAO method | ADO Find with SkipRows | ADO search direction
FindFirst | 0 | adSearchForward (if not currently positioned on the first record, call MoveFirst before Find)
FindLast | 0 | adSearchBackward (if not currently positioned on the last record, call MoveLast before Find)
FindNext | 1 | adSearchForward
FindPrevious | 1 | adSearchBackward

DAO and ADO require a different syntax for locating records based on a Null value. In DAO if you want to find a record that has a Null value you use the following syntax:

"ColumnName Is Null"

or, to find a record that does not have a Null value for that column:

"ColumnName Is Not Null"

ADO, however, does not recognize the Is operator. You must use the = or <> operators instead. So the equivalent ADO criteria would be:

"ColumnName = Null"

or

"ColumnName <> Null"

So far, each of the criteria shown in the examples above are based on a value for a single column. However, with DAO, the Criteria parameter is like the WHERE clause in an SQL statement and can contain multiple columns and compare operators within the criteria.

This is not the case with ADO. The ADO Criteria parameter is a string containing a single column name, comparison operator, and value to use in the search. If you need to find a record based on multiple columns, use the Filter property (see the section, "Filtering and Sorting Data") to create a view of the Recordset that only contains those records matching the criteria.

DAO and ADO behave differently if a record that meets the specified criteria is not found. DAO sets the NoMatch property to True and the current record is not defined. If ADO does not find a record that meets the criteria, the current record is positioned either before the beginning of the Recordset if searching forward (adSearchForward) or after the end of the Recordset if searching backward (adSearchBackward). Use the BOF or EOF properties as appropriate to determine whether or not a match was found.

Using the Seek Method

The following listings demonstrate how to locate a record using Seek.

DAO

Sub DAOSeekRecord()

   Dim db As DAO.Database
   Dim rst As DAO.Recordset

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Open the Recordset
   Set rst = db.OpenRecordset("Order Details", dbOpenTable)

   ' Select the index used to order the data in the recordset
   rst.Index = "PrimaryKey"

   ' Find the order where OrderId = 10255 and ProductId = 16
   rst.Seek "=", 10255, 16

   ' If a match is found print the quantity of the order
   If Not rst.NoMatch Then
      Debug.Print rst.Fields("Quantity").Value
   End If

   ' Close the recordset
   rst.Close

End Sub

ADO

Sub ADOSeekRecord()

   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Open the recordset
   rst.Open "Order Details", cnn, adOpenKeyset, adLockReadOnly, _
      adCmdTableDirect

   ' Select the index used to order the data in the recordset
   rst.Index = "PrimaryKey"

   ' Find the order where OrderId = 10255 and ProductId = 16
   rst.Seek Array(10255, 16), adSeekFirstEQ

   ' If a match is found print the quantity of the order
   If Not rst.EOF Then
   Debug.Print rst.Fields("Quantity").Value
   End If

   ' Close the recordset
   rst.Close

End Sub

Because Seek is based on an index, it is important to specify an index before searching. In the previous example, this is not strictly necessary because Microsoft Jet will use the primary key if an index is not specified.

In the ADO example, the Visual Basic for Applications Array function is used when specifying a value for more than one column as part of the KeyValues parameter. If only one value is specified, it is not necessary to use the Array function.

As with the Find method, use the NoMatch property with DAO to determine whether or not a matching record was found. Use the BOF and EOF properties as appropriate with ADO.

The Seek method will work correctly only for Microsoft Jet 4.0 databases. It will fail with a run-time error for all earlier formats, even if you use the Microsoft Jet 4.0 database engine to open the database. This will cause a problem if the application is written to support older database formats. If so, use the Supports method of the Recordset object to determine whether the Seek method is available for the open Recordset. However, if all client applications use the newer format, this check is unnecessary. Use either Microsoft Access 2000 or the CompactDatabase method to convert older databases to the newer format.

Filtering and Sorting Data in a Recordset

In general, when using ADO with the Jet Provider, you will have better performance if you filter and sort data by specifying an SQL WHERE or ORDER BY clause in the SQL statement or stored query used to open the Recordset.

Using the Filter Property

The following listings demonstrate how to use the Filter property.

DAO

Sub DAOFilterRecordset()

   Dim db As DAO.Database
   Dim rst As DAO.Recordset
   Dim rstFlt As DAO.Recordset

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Open the Recordset
   Set rst = db.OpenRecordset("Customers", dbOpenDynaset)

   ' Set the Filter to be used for subsequent recordsets
   rst.Filter = "Country='USA' And Fax Is Not Null"

   ' Open the filtered recordset
   Set rstFlt = rst.OpenRecordset()
   Debug.Print rstFlt.Fields("CustomerId").Value

   ' Close the recordsets
   rst.Close
   rstFlt.Close

End Sub

ADO

Sub ADOFilterRecordset()

   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Open the recordset
   rst.Open "Customers", cnn, adOpenKeyset, adLockOptimistic

   ' Filter the recordset to include only those customers in
   ' the USA that have a fax number
   rst.Filter = "Country='USA' And Fax <> Null"
   Debug.Print rst.Fields("CustomerId").Value

   ' Close the recordset
   rst.Close

End Sub

The DAO and ADO Filter properties are used slightly differently. With DAO, the Filter property specifies a filter to be applied to any subsequently opened Recordset objects based on the Recordset for which you have applied the filter. With ADO, the Filter property applies to the Recordset to which you applied the filter. The ADO Filter property allows you to create a temporary view that can be used to locate a particular record or set of records within the Recordset. When a filter is applied to the Recordset, the RecordCount property reflects just the number of records within the view. The filter can be removed by setting the Filter property to adFilterNone.

Using the Sort Method

The following listings demonstrate how to sort records with the Sort method.

DAO

Sub DAOSortRecordset()

   Dim db As DAO.Database
   Dim rst As DAO.Recordset
   Dim rstSort As DAO.Recordset

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Open the Recordset
   Set rst = db.OpenRecordset("Customers", dbOpenDynaset)

   ' Sort the recordset based on Country and Region both in
   ' ascending order
   rst.Sort = "Country, Region"

   ' Open the sorted recordset
   Set rstSort = rst.OpenRecordset()
   Debug.Print rstSort.Fields("CustomerId").Value

   ' Close the recordsets
   rst.Close
   rstSort.Close

End Sub

ADO

Sub ADOSortRecordset()

   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Open the recordset
   rst.CursorLocation = adUseClient
   rst.Open "Customers", cnn, adOpenKeyset, adLockOptimistic

   ' Sort the recordset based on Country and Region both in
   ' ascending order
   rst.Sort = "Country, Region"
   Debug.Print rst.Fields("CustomerId").Value

   ' Close the recordset
   rst.Close

End Sub

Like the Filter property, the DAO and ADO Sort properties differ in that the DAO Sort applies to subsequently opened Recordset objects, and for ADO it applies to the current Recordset.

Note that the Microsoft Jet Provider does not support the OLE DB interfaces that ADO could use to filter and sort the Recordset (IViewFilter and IViewSort). In the case of Filter, ADO will perform the filter itself. However, for Sort, you must use the Cursor Service by specifying adUseClient for the CursorLocation property prior to opening the Recordset. The Cursor Service will copy all of the records in the Recordset to a cache on your local machine and will build temporary indexes in order to perform the sorting. In many cases, you may achieve better performance by re-executing the query used to open the Recordset and specifying an SQL WHERE or ORDER BY clause as appropriate.

Also, you may not get identical results with DAO and ADO when sorting Recordset objects. Different sort algorithms can create different sequences for records that have equal values in the sorted fields. In the example above, the DAO code gives 'RANCH' as the CustomerId for the first record, while the ADO code gives 'CACTU' as the CustomerId. Both results are valid.

Updating Data in a Recordset

Once you have opened an updateable recordset by specifying the appropriate DAO Recordset object Type or ADO CursorType and LockType you can change, delete, or add new records using methods of the Recordset object.

Adding New Records

Both DAO and ADO allow you to add new records to an updatable Recordset by first calling the AddNew method, then specifying the values for the fields, and finally committing the changes with the Update method. The following code shows how to add a new record using DAO and ADO.

DAO

Sub DAOAddRecord()

   Dim db As DAO.Database
   Dim rst As DAO.Recordset

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Open the Recordset
   Set rst = db.OpenRecordset _
      ("SELECT * FROM Customers", dbOpenDynaset)

   ' Add a new record
   rst.AddNew

   ' Specify the values for the fields
   rst!CustomerId = "HENRY"
   rst!CompanyName = "Henry's Chop House"
   rst!ContactName = "Mark Henry"
   rst!ContactTitle = "Sales Representative"
   rst!Address = "40178 NE 8th Street"
   rst!City = "Bellevue"
   rst!Region = "WA"
   rst!PostalCode = "98107"
   rst!Country = "USA"
   rst!Phone = "(425) 555-9876"
   rst!Fax = "(425) 555-8908"

   ' Save the changes you made to the
   ' current record in the Recordset
   rst.Update

   ' For this example, just print out
   ' CustomerId for the new record
   ' Position recordset on new record
   rst.Bookmark = rst.LastModified
   Debug.Print rst!CustomerId

   ' Close the recordset
   rst.Close

End Sub

ADO

Sub ADOAddRecord()

   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Open the recordset
   rst.Open "SELECT * FROM Customers", _
      cnn, adOpenKeyset, adLockOptimistic

   ' Add a new record
   rst.AddNew

   ' Specify the values for the fields
   rst!CustomerId = "HENRY"
   rst!CompanyName = "Henry's Chop House"
   rst!ContactName = "Mark Henry"
   rst!ContactTitle = "Sales Representative"
   rst!Address = "40178 NE 8th Street"
   rst!City = "Bellevue"
   rst!Region = "WA"
   rst!PostalCode = "98107"
   rst!Country = "USA"
   rst!Phone = "(425) 555-9876"
   rst!Fax = "(425) 555-8908"

   ' Save the changes you made to the
   ' current record in the Recordset
   rst.Update

   ' For this example, just print out
   ' CustomerId for the new record
   Debug.Print rst!CustomerId

   ' Close the recordset
   rst.Close

End Sub

DAO and ADO behave differently when a new record is added. With DAO, the record that was current before you used AddNew remains current. With ADO, the newly inserted record becomes the current record. Because of this, it is not necessary to explicitly reposition on the new record to get information such as the value of an auto-increment column for the new record. For this reason, in the ADO example above, there is no equivalent code to the rst.Bookmark = rst.LastModified code found in the DAO example.

ADO also provides a shortcut syntax for adding new records. The AddNew method has two optional parameters, FieldList and Values, that take an array of field names and field values respectively. The following example demonstrates how to use the shortcut syntax.

Sub ADOAddRecord2()

   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Open the recordset
   rst.Open "SELECT * FROM Shippers", _
      cnn, adOpenKeyset, adLockOptimistic

   ' Add a new record
   rst.AddNew Array("CompanyName", "Phone"), _
      Array("World Express", "(425) 555-7863")

   ' Save the changes you made to the
   ' current record in the Recordset
   rst.Update

   ' For this example, just print out the
   ' ShipperId for the new row.
   Debug.Print rst!ShipperId

   ' Close the recordset
   rst.Close

End Sub

Updating Existing Records

The following code demonstrates how to open a scrollable, updateable Recordset and modify the data in a record.

DAO

Sub DAOUpdateRecord()

   Dim db As DAO.Database
   Dim rst As DAO.Recordset

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Open the Recordset
   Set rst = db.OpenRecordset _
      ("SELECT * FROM Customers WHERE CustomerId = 'LAZYK'", _
      dbOpenDynaset)

   ' Put the Recordset in Edit Mode
   rst.Edit

   ' Update the Contact name of the
   ' first record
   rst.Fields("ContactName").Value = "New Name"

   ' Save the changes you made to the
   ' current record in the Recordset
   rst.Update

   ' Close the recordset
   rst.Close

End Sub

ADO

Sub ADOUpdateRecord()

   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" _
      & "Data Source=.\NorthWind.mdb;"

   ' Open the recordset
   rst.Open _
      "SELECT * FROM Customers WHERE CustomerId = 'LAZYK'", _
      cnn, adOpenKeyset, adLockOptimistic

   ' Update the Contact name of the
   ' first record
   rst.Fields("ContactName").Value = "New Name"

   ' Save the changes you made to the
   ' current record in the Recordset
   rst.Update

   ' Close the recordset
   rst.Close

End Sub

Alternatively, in both the DAO and ADO code examples, the explicit syntax

    rst.Fields("ContactName").Value = "New Name"

can be shortened to

    rst!ContactName = "New Name"

The ADO and DAO code for updating data in a Recordset is very similar. The major difference between the two examples above is that DAO requires that you put the Recordset into an editable state with the Edit method. ADO does not require you to explicitly indicate that you want to be in edit mode. With both DAO and ADO, you can verify the edit status of the current record by using the EditMode property.

One difference between DAO and ADO is the behavior when updating a record and then moving to another record without calling the Update method. With DAO, any changes made to the current record are lost when moving to another record without first calling Update. ADO automatically commits the changes to the current record when moving to a new record. You can explicitly discard changes to the current record with both DAO and ADO by using the CancelUpdate method.

Large Text and Binary Data Fields

Two kinds of fields in a Microsoft Jet database are used to store lengthy values. Pictures, OLE objects, or whole files can be stored in an OLE object field as binary data. An OLE object field is also called a Binary Large OBject (BLOB) field. A Memo field stores text data only. Memo fields are sometimes called BLOB fields, too, but that is somewhat misleading. A Memo field is merely a variable-length text field. Both fields are handled similarly.

Large variable-length fields like these must be handled differently than ordinary fixed-length fields, such as integer and date fields. Typically, a variant, a string, or a dynamic byte array is used to read or update the value of the field. However, sometimes the length of the data stored in the field will be too large to store at once in a variant. Breaking the data into smaller, manageable chunks reduces the memory that must be allocated to work with these values. The GetChunk and AppendChunk methods of the Field objects in DAO and ADO break up the data in these fields into smaller chunks.

The following listings demonstrate how to use GetChunk to read large values from a memo field.

DAO

Sub DAOReadMemo()

   Dim db As DAO.Database
   Dim rst As DAO.Recordset
   Dim sNotes As String
   Dim sChunk As String
   Dim cchChunkReceived As Long
   Dim cchChunkRequested As Long
   Dim cchChunkOffset As Long

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Open the Recordset
   Set rst = db.OpenRecordset _
      ("SELECT Notes FROM Employees", dbOpenDynaset)

   ' Initialize offset
   cchChunkOffset = 0

   ' cchChunkRequested artifically set low at 16
   ' to demonstrate looping
   cchChunkRequested = 16

   ' Loop through as many chunks as it takes
   ' to read the entire BLOB into memory
   Do
      ' Temporarily store the next chunk
      sChunk = rst!Fields("Notes").GetChunk _
         (cchChunkOffset, cchChunkRequested)

      ' Check how much we got
      cchChunkReceived = Len(sChunk)

      ' Adjust offset for next iteration
      cchChunkOffset = cchChunkOffset + cchChunkReceived

      ' If we got anything,
      ' concatenate it to the main BLOB
      If cchChunkReceived > 0 Then
         sNotes = sNotes & sChunk
      End If

   Loop While cchChunkReceived = cchChunkRequested

   ' For this example, print the value of
   ' the Notes field for just the first record
   Debug.Print sNotes

   ' Close the recordset
   rst.Close

End Sub

ADO

Sub ADOReadMemo()

   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset
   Dim sNotes As String
   Dim sChunk As String
   Dim cchChunkReceived As Long
   Dim cchChunkRequested As Long

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Open the recordset
   rst.Open "SELECT Notes FROM Employees ", _
      cnn, adOpenKeyset, adLockOptimistic

   ' cchChunkRequested artifically set low at 16
   ' to demonstrate looping
   cchChunkRequested = 16

   ' Loop through as many chunks as it takes
   ' to read the entire BLOB into memory
   Do
      ' Temporarily store the next chunk
      sChunk = rst.Fields("Notes").GetChunk(cchChunkRequested)

      ' Check how much we got
      cchChunkReceived = Len(sChunk)

      ' If we got anything,
      ' concatenate it to the main BLOB
      If cchChunkReceived > 0 Then
         sNotes = sNotes & sChunk
      End If

   Loop While cchChunkReceived = cchChunkRequested

   ' For this example, print the value of
   ' the Notes field for just the first record
   Debug.Print sNotes

   ' Close the recordset
   rst.Close

End Sub

The code to use GetChunk and AppendChunk in ADO is similar to the code in DAO. In the DAO example, the offset to read the next chunk of data from the field must be explicitly calculated and given as a parameter to the GetChunk method. In ADO, the Recordset stores the offset for consecutive GetChunk calls on the same field automatically. Any clones of the Recordset share this offset. This offset is automatically incremented as you call call GetChunk consecutively on a single field. Reading from a different field in the Recordset or moving the Recordset to a different record will reset the offset to the beginning of the field.

The following listings demonstrate how to update binary data in an OLE object field without using the GetChunk or AppendChunk methods.

DAO

Sub DAOUpdateBLOB()

   Dim db As DAO.Database
   Dim rst As DAO.Recordset
   Dim rgPhoto() As Byte

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Open the Recordset
   Set rst = db.OpenRecordset( _
      "SELECT Photo FROM Employees", dbOpenDynaset)

   ' Get the first photo
   rgPhoto = rst.Fields("Photo").Value

   ' Move to the next record
   rst.MoveNext

   ' Put the Recordset in Edit Mode
   rst.Edit

   ' Copy the photo into the next record
   rst.Fields("Photo").Value = rgPhoto

   ' Save the changes you made to the
   ' current record in the Recordset
   rst.Update

   ' Close the recordset
   rst.Close

End Sub

ADO

Sub ADOUpdateBLOB()

   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset
   Dim rgPhoto() As Byte

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Open the recordset
   rst.Open "SELECT Photo FROM Employees ", _
      cnn, adOpenKeyset, adLockOptimistic

   ' Get the first photo
   rgPhoto = rst.Fields("Photo").Value

   ' Move to the next record
   rst.MoveNext

   ' Copy the photo into the next record
   rst.Fields("Photo").Value = rgPhoto

   ' Save the changes you made to the
   ' current record in the Recordset
   rst.Update

   ' Close the recordset
   rst.Close

End Sub

The code for updating binary data without using GetChunk or AppendChunk is almost identical to the code for updating ordinary fields such as text, numeric, or date fields. The only difference is that it uses a dynamic byte array to store the values. The previous ADO code shows the same differences from DAO as was shown earlier in the example for Updating a Record.

Executing Queries

You can execute a query with ADO as easily as you can with DAO. ADO uses a slightly different technique for working with queries, but provides more functionality than DAO provides. ADO also has a number of abbreviated syntaxes that allow you to do the same thing as you would with DAO but with a lot less code.

Executing a Non-Parameterized Stored Query

A non-parameterized stored query is an SQL statement that has been saved in the database and does not require that additional variable information be specified in order to execute. The following listings demonstrate how to execute such a query.

DAO

Sub DAOExecuteQuery()

   Dim db As DAO.Database
   Dim rst As DAO.Recordset
   Dim fld As DAO.Field

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Open the Recordset
   Set rst = db.OpenRecordset("Products Above Average Price", _
      dbOpenForwardOnly, dbReadOnly)

   ' Display the records in the
   ' debug window
   Do Until rst.EOF
      For Each fld In rst.Fields
         Debug.Print fld.Value & ";";
      Next
      Debug.Print
      rst.MoveNext
   Loop

   ' Close the recordset
   rst.Close

End Sub

ADO

Sub ADOExecuteQuery()

   Dim cnn As New ADODB.Connection
   Dim rst As New ADODB.Recordset
   Dim fld As ADODB.Field

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Open the recordset
   rst.Open "[Products Above Average Price]", _
      cnn, adOpenForwardOnly, adLockReadOnly, adCmdStoredProc

   ' Display the records in the
   ' debug window
   Do Until rst.EOF
      For Each fld In rst.Fields
         Debug.Print fld.Value & ";";
      Next
      Debug.Print
      rst.MoveNext
   Loop

   ' Close the recordset
   rst.Close

End Sub

The code for executing a non-parameterized, row-returning query is almost identical. With ADO, if the query name contains spaces you must use square brackets ([ ]) around the name.

Executing a Parameterized Stored Query

A parameterized stored query is an SQL statement that has been saved in the database and requires that additional variable information be specified in order to execute. The code below shows how to execute such a query.

DAO

Sub DAOExecuteParamQuery()

   Dim db As DAO.Database
   Dim qdf As DAO.QueryDef
   Dim rst As DAO.Recordset
   Dim fld As DAO.Field

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Get the QueryDef from the
   ' QueryDefs collection
   Set qdf = db.QueryDefs("Sales by Year")

   ' Specify the parameter values
   qdf.Parameters _
      ("Forms!Sales by Year Dialog!BeginningDate") = #8/1/1997#
   qdf.Parameters _
      ("Forms!Sales by Year Dialog!EndingDate") = #8/31/1997#

   ' Open the Recordset
   Set rst = qdf.OpenRecordset(dbOpenForwardOnly, dbReadOnly)

   ' Display the records in the
   ' debug window
   Do Until rst.EOF
      For Each fld In rst.Fields
         Debug.Print fld.Value & ";";
      Next
      Debug.Print
      rst.MoveNext
   Loop

   ' Close the recordset
   rst.Close

End Sub

ADO

Sub ADOExecuteParamQuery()

   Dim cnn As New ADODB.Connection
   Dim cat As New ADOX.Catalog
   Dim cmd As ADODB.Command
   Dim rst As New ADODB.Recordset
   Dim fld As ADODB.Field

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Open the catalog
   cat.ActiveConnection = cnn

   ' Get the Command object from the
   ' Procedure
   Set cmd = cat.Procedures("Sales by Year").Command

   ' Specify the parameter values
   cmd.Parameters _
      ("Forms![Sales by Year Dialog]!BeginningDate") = #8/1/1997#
   cmd.Parameters _
      ("Forms![Sales by Year Dialog]!EndingDate") = #8/31/1997#

   ' Open the recordset
   rst.Open cmd, , adOpenForwardOnly, _
      adLockReadOnly, adCmdStoredProc

   ' Display the records in the
   ' debug window
   Do Until rst.EOF
      For Each fld In rst.Fields
         Debug.Print fld.Value & ";";
      Next
      Debug.Print
      rst.MoveNext
   Loop

   ' Close the recordset
   rst.Close

End Sub

Alternatively, the ADO example could be written more concisely by specifying the parameter values using the Parameters parameter with the Command object's Execute method. The following lines of code:

   ' Specify the parameter values
   cmd.Parameters _
      ("Forms![Sales by Year Dialog]!BeginningDate") = #8/1/1997#
   cmd.Parameters _
      ("Forms![Sales by Year Dialog]!EndingDate") = #8/31/1997#

   ' Open the recordset
   rst.Open cmd, , adOpenForwardOnly, _
      adLockReadOnly, adCmdStoredProc

could be replaced by the single line:

   ' Execute the Command, passing in the
   ' values for the parameters
   Set rst = cmd.Execute(, Array(#8/1/1997#, #8/31/1997#), _
      adCmdStoredProc)

In one more variation of the ADO code to execute a parameterized query, the example could be rewritten to not use any ADOX code.

Sub ADOExecuteParamQuery2()

   Dim cnn As New ADODB.Connection
   Dim cmd As New ADODB.Command
   Dim rst As New ADODB.Recordset
   Dim fld As ADODB.Field

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Create the command
   Set cmd.ActiveConnection = cnn
   cmd.CommandText = "[Sales by Year]"

   ' Execute the Command, passing in the
   ' values for the parameters
   Set rst = cmd.Execute(, Array(#8/1/1997#, #8/31/1997#), _
      adCmdStoredProc)

   ' Display the records in the
   ' debug window
   Do Until rst.EOF
      For Each fld In rst.Fields
         Debug.Print fld.Value & ";";
      Next
      Debug.Print
      rst.MoveNext
   Loop

   ' Close the recordset
   rst.Close

End Sub

Executing Bulk Operations

The ADO Command object's Execute method can be used for row-returning queries, as shown in the previous section, as well as for non row-returning queries—also known as bulk operations. The following code examples demonstrate how to execute a bulk operation in both DAO and ADO.

DAO

Sub DAOExecuteBulkOpQuery()

   Dim db As DAO.Database

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Execute the query
   db.Execute "UPDATE Customers SET Country = 'United States' " & _
      "WHERE Country = 'USA'"

   Debug.Print "Records Affected = " & db.RecordsAffected

   ' Close the database
   db.Close

End Sub

ADO

Sub ADOExecuteBulkOpQuery()

   Dim cnn As New ADODB.Connection
   Dim iAffected As Integer

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Execute the query
   cnn.Execute "UPDATE Customers SET Country = 'United States' " & _
      "WHERE Country = 'USA'", iAffected, adExecuteNoRecords

   Debug.Print "Records Affected = " & iAffected

   ' Close the connection
   cnn.Close

End Sub

Unlike DAO, which has two methods for executing SQL statements, OpenRecordset and Execute, ADO has a single method, Execute, that executes row-returning as well as bulk operations. In the ADO example, the constant adExecuteNoRecords indicates that the SQL statement is non row-returning. If this constant is omitted, the ADO code will still execute successfully, but you will pay a performance penalty. When adExecuteNoRecords is not specified, ADO will create a Recordset object as the return value for the Execute method. Creating this object is unnecessary overhead if the statement does not return records and should be avoided by specifying adExecuteNoRecords when you know that the statement is non row-returning.


Defining and Retrieving a Database's Schema

MDAC Technical Articles

Defining and Retrieving a Database’s Schema

A database contains a number of tables, indexes, constraints, and so on. Collectively the definition of these items is known as the database's schema. Both DAO and ADO offer an easy, object-based way to both create new objects in the database as well as to retrieve information about the definition of existing objects.

Creating a Database

Before tables or other objects can be defined, the database itself must be created. The following code creates and opens a new Microsoft Jet database.

DAO

Sub DAOCreateDatabase()

   Dim db As DAO.Database

   Set db = DBEngine.CreateDatabase(".\New.mdb", dbLangGeneral)

End Sub

ADOX

Sub ADOCreateDatabase()

   Dim cat As New ADOX.Catalog

   cat.Create "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\New.mdb;"

End Sub

As discussed earlier in this document (see the section titled "ADOX: Data Definition and Security"), the DAO Database object corresponds to the Catalog object in ADOX. So to create a new Jet database using ADOX, you use the Catalog objects Create method.

In the DAO code above, the Locale parameter is specified as dbLangGeneral. In the ADOX code, locale is not explicitly specified. The default locale for the Microsoft Jet Provider is equivalent to dbLangGeneral. Use the ADO Locale Identifier property to specify a different locale.

In DAO, CreateDatabase also can take a third Options parameter, specifying information for encrytion and database version. For example, the following line is used to create an encrypted, version 1.1 Microsoft Jet database:

   Set db = DBEngine.CreateDatabase(".\New.mdb", dbLangGeneral, _
      dbEncrypt + dbVersion11)

In ADO, encryption and database version information is specified by provider-specific properties. With the Microsoft Jet Provider, use the Encrypt Database and Engine Type properties, respectively. The following line of code specifies these values in the connection string to create an encrypted, version 1.1 Microsoft Jet database:

   cat.Create "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\New.mdb;" & _
      "Jet OLEDB:Encrypt Database=True;" & _
      "Jet OLEDB:Engine Type=2;"

Retrieving Schema Information

Both DAO and ADOX contain collections of objects that can be used to retrieve information about the database's schema. By iterating through the collections, it is easy to determine the structure of the objects in the database.

The following code demonstrates how to print the name of every table in the database by looping through the DAO TableDefs collection and the ADOX Tables collection.

DAO

Sub DAOListTables()

   Dim db As DAO.Database
   Dim tbl As DAO.TableDef

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Loop through the tables in the database and print their name
   For Each tbl In db.TableDefs
      Debug.Print tbl.Name
   Next

End Sub

ADOX

Sub ADOListTables()

   Dim cat As New ADOX.Catalog
   Dim tbl As ADOX.Table

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Loop through the tables in the database and print their name
   For Each tbl In cat.Tables
      If tbl.Type <> "VIEW" Then Debug.Print tbl.Name
   Next

End Sub

With DAO, the TableDef object represents a table in the database and the TableDefs collection contains a TableDef object for each table in the database. This is similar to ADO, in which the Table object represents a table and the Tables collection contains all the tables.

However, unlike DAO, the ADO Tables collection may contain Table objects that aren't actual tables in your Microsoft Jet database. For example, row-returning, non-parameterized Microsoft Jet queries (considered Views in ADO) are also included in the Tables collection. To determine whether or not the Table object represents a table in the database, use the Type property. The following table lists the possible values for the Type property when using ADO with the Microsoft Jet Provider.

Type | Description
ACCESS TABLE | The Table is a Microsoft Access system table.
LINK | The Table is a linked table from a non-ODBC data source.
PASS-THROUGH | The Table is a linked table from an ODBC data source.
SYSTEM TABLE | The Table is a Microsoft Jet system table.
TABLE | The Table is a table.
VIEW | The Table is a row-returning, non-parameterized query.

In addition to being able to retrieve schema information using collections in ADOX, you can use the ADO OpenSchema method to return a Recordset containing information about the tables in the database. See Appendix C: Microsoft Jet 4.0 OLE DB Provider Defined Property Values for the schema rowsets GUIDs that are available in ADO when using the Microsoft Jet Provider.

In general, it is faster to use the OpenSchema method rather than looping through the collection, because ADOX must incur the overhead of creating objects for each element in the collection. The following code demonstrates how to use the OpenSchema method to print the same information as the previous DAO and ADOX examples.

Sub ADOListTables2()

   Dim cnn As New ADODB.Connection
   Dim rst As ADODB.Recordset

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Open the tables schema rowset
   Set rst = cnn.OpenSchema(adSchemaTables)

   ' Loop through the results and print
   ' the names in the debug window
   Do Until rst.EOF
      If rst.Fields("TABLE_TYPE") <> "VIEW" Then
         Debug.Print rst.Fields("TABLE_NAME")
      End If
      rst.MoveNext
   Loop

End Sub

Creating and Modifying Tables

Microsoft Jet databases can contain two types of tables. The first type is a local table, in which the definition and data are stored within the database. The second type is a linked table in which the table resides in an external database, but a link along with a copy of the table's definition is stored in the database.

Creating Local Tables

The following example creates a new local table named "Contacts."

DAO

Sub DAOCreateTable()

   Dim db As DAO.Database
   Dim tbl As DAO.TableDef

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Create a new TableDef object.
   Set tbl = db.CreateTableDef("Contacts")

   With tbl
      ' Create fields and append them to the new TableDef object.
      ' This must be done before appending the TableDef object to
      ' the TableDefs collection of the Database.
      .Fields.Append .CreateField("ContactName", dbText)
      .Fields.Append .CreateField("ContactTitle", dbText)
      .Fields.Append .CreateField("Phone", dbText)
      .Fields.Append .CreateField("Notes", dbMemo)
      .Fields("Notes").Required = False
   End With

   ' Add the new table to the database.
   db.TableDefs.Append tbl

   db.Close

End Sub

ADOX

Sub ADOCreateTable()

   Dim cat As New ADOX.Catalog
   Dim tbl As New ADOX.Table

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Create a new Table object.
   With tbl
      .Name = "Contacts"
      ' Create fields and append them to the new Table
      ' object. This must be done before appending the
      ' Table object to the Tables collection of the
      ' Catalog.
      .Columns.Append "ContactName", adVarWChar
      .Columns.Append "ContactTitle", adVarWChar
      .Columns.Append "Phone", adVarWChar
      .Columns.Append "Notes", adLongVarWChar
      .Columns("Notes").Attributes = adColNullable
   End With

   ' Add the new table to the database.
   cat.Tables.Append tbl

   Set cat = Nothing

End Sub

The process for creating a table using DAO or ADOX is the same. First, create the object (TableDef or Table), append the columns (Field or Column objects), and finally append the table to the collection. Though the process is the same, the syntax is slightly different.

With ADOX, it is not necessary to use a "create" method to create the column before appending it to the collection. The Append method can be used to both create and append the column.

You'll also notice the data type names for the columns are different between DAO and ADOX. The following table shows how the DAO data types that apply to Microsoft Jet databases map to the ADO data types.

DAO data type | ADO data type
dbBinary | adBinary
dbBoolean | adBoolean
dbByte | adUnsignedTinyInt
dbCurrency | adCurrency
dbDate | adDate
dbDecimal | adNumeric
dbDouble | adDouble
dbGUID | adGUID
dbInteger | adSmallInt
dbLong | adInteger
dbLongBinary | adLongVarBinary
dbMemo | adLongVarWChar
dbSingle | adSingle
dbText | adVarWChar

Though not shown in this example, there are a number of other attributes of a table or column that you can set when creating the table or column, using the DAO Attributes property. The table below shows how these attributes map to ADO and Microsoft Jet Provider–specific properties.

DAO TableDef Property | Value | ADOX Table Property | Value
Attributes | dbAttachExclusive | Jet OLEDB:Exclusive Link | True
Attributes | dbAttachSavePWD | Jet OLEDB:Cache Link Name/Password | True
Attributes | dbAttachedTable | Type | "LINK"
Attributes | dbAttachedODBC | Type | "PASS-THROUGH"
DAO Field Property | Value | ADOX Column Property | Value
Attributes | dbAutoIncrField | AutoIncrement | True
Attributes | dbFixedField | ColumnAttributes | adColFixed
Attributes | dbHyperlinkField | Jet OLEDB:Hyperlink | True
Attributes | dbSystemField | No equivalent | n/a
Attributes | dbUpdatableField | Attributes (Field Object) | adFldUpdatable
Attributes | dbVariableField | ColumnAttributes | Not adColFixed

Creating a Linked Table

Linking (also known as attaching) a table from an external database allows you to read data, update and add data (in most cases), and create queries using the table in the same way as you would with a table native to the database.

With Microsoft Jet you can create links to Microsoft Jet data, ISAM data (Text, FoxPro, dBASE, etc.), and ODBC data. Tables that are linked through ODBC are sometimes called pass-through tables.

The following listings demonstrate how to create a table that is linked to a table in another Microsoft Jet database.

DAO

Sub DAOCreateAttachedJetTable()

   Dim db As DAO.Database
   Dim tbl As DAO.TableDef

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Create a new TableDef object.
   Set tbl = db.CreateTableDef("Authors")

   ' Set the properties to create the link
   tbl.Connect = ";DATABASE=.\Pubs.mdb;pwd=password;"
   tbl.SourceTableName = "authors"

   ' Add the new table to the database.
   db.TableDefs.Append tbl

   db.Close

End Sub

ADOX

Sub ADOCreateAttachedJetTable()

   Dim cat As New ADOX.Catalog
   Dim tbl As New ADOX.Table

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Set the name and target catalog for the table
   tbl.Name = "Authors"
   Set tbl.ParentCatalog = cat

   ' Set the properties to create the link
   tbl.Properties("Jet OLEDB:Create Link") = True
   tbl.Properties("Jet OLEDB:Link Datasource") = ".\Pubs.mdb"
   tbl.Properties("Jet OLEDB:Link Provider String") = ";Pwd=password"
   tbl.Properties("Jet OLEDB:Remote Table Name") = "authors"

   ' Append the table to the collection
   cat.Tables.Append tbl

   Set cat = Nothing

End Sub

To create a linked table, you must specify the external data source and the name of the external table. With DAO, the Connect and SourceTableName properties are used to specify this information. With ADOX, several Microsoft Jet provider-specific properties are used to create the link. When referencing the Table object's Properties collection prior to appending the Table to the Tables collection, you must first set the ParentCatalog property. This is necessary so ADOX knows from which OLE DB provider to receive the property information. See the section, Appendix B: Microsoft Jet 4.0 OLE DB Properties Reference for more information about the properties that are available in the Table object's Properties collection when using the Microsoft Jet Provider.

With ADOX, the Jet OLEDB:Link Datasource property contains only the file and pathname for the database. It does not contain the "database=;" prefix nor is it used to specify the database password or other connection options as the Connect property does in DAO. To specify other connection options in ADOX code, use the Jet OLEDB:Link Provider String property. You do not need to set this property unless you need to set extra connection options. In the previous example, if the pubs.mdb was not secured with a database password, you could omit the line of code that sets the Jet OLEDB:Link Provider String property.

When used for a user ID or a password, the value for this property follows the syntax for connection strings used for external data. The syntax is given in the Microsoft Jet Database Engine Programmer's Guide. Specifically, you must use "uid=;" and "pwd=;" to set the user ID and password, respectively, and not "User ID=;" or "Password=;".

Notice that when creating an attached table using both DAO and ADOX it is not necessary to create columns on the table. The Microsoft Jet database engine will automatically create the columns based on the definition of the table in the external data source.

This next example shows how to create a table that is linked to a table in an ODBC data source such as a Microsoft SQL Server database.

DAO

Sub DAOCreateAttachedODBCTable()

   Dim db As DAO.Database
   Dim tbl As DAO.TableDef

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Create a new TableDef object.
   Set tbl = db.CreateTableDef("Titles")

   ' Set the properties to create the link
   tbl.Connect = "ODBC;DSN=ADOPubs;UID=sa;PWD=;"
   tbl.SourceTableName = "titles"

   ' Add the new table to the database.
   db.TableDefs.Append tbl

   db.Close

End Sub

ADOX

Sub ADOCreateAttachedODBCTable()

   Dim cat As New ADOX.Catalog
   Dim tbl As New ADOX.Table

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Set the name and target catalog for the table
   tbl.Name = "Titles"
   Set tbl.ParentCatalog = cat

   ' Set the properties to create the link
   tbl.Properties("Jet OLEDB:Create Link") = True
   tbl.Properties("Jet OLEDB:Link Provider String") = _
      "ODBC;DSN=ADOPubs;UID=sa;PWD=;"
   tbl.Properties("Jet OLEDB:Remote Table Name") = "titles"

   ' Append the table to the collection
   cat.Tables.Append tbl

   Set cat = Nothing

End Sub

Unlike DAO, which has a single Connect property, ADOX with the Microsoft Jet Provider has a separate property that specifies the connection string for tables attached through ODBC. When creating tables attached through ODBC you may want to indicate that the password should be saved as part of the connection string (it is not saved by default). With ADOX, use the Jet OLEDB:Cache Link Name/Password property to indicate that the password should be cached. This is equivalent to setting the dbAttachSavePWD flag in the Table object's Attributes property using DAO.

Modifying an Existing Table

Once a table is created, you may want to modify it to add or remove columns, change the validation rule or refresh the link for a linked table.

The following listings demonstrate how to add a new auto-increment column to an existing table.

DAO

Sub DAOCreateAutoIncrColumn()

   Dim db As DAO.Database
   Dim tbl As DAO.TableDef
   Dim fld As DAO.Field

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Get the Contacts table
   Set tbl = db.TableDefs("Contacts")

   ' Create the new auto increment column
   Set fld = tbl.CreateField("ContactId", dbLong)
   fld.Attributes = dbAutoIncrField

   ' Add the new table to the database.
   tbl.Fields.Append fld

   db.Close

End Sub

ADOX

Sub ADOCreateAutoIncrColumn()

   Dim cat As New ADOX.Catalog
   Dim col As New ADOX.Column

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Create the new auto increment column
   With col
      .Name = "ContactId"
      .Type = adInteger
      Set .ParentCatalog = cat
      .Properties("AutoIncrement") = True
   End With

   ' Append the column to the table
   cat.Tables("Contacts").Columns.Append col

   Set cat = Nothing

End Sub

In the ADOX example, notice that the ParentCatalog property of the Column object is set before the AutoIncrement property in the Properties collection is set to True. In order to access any property in the Properties collection, the Column object must be associated with a provider.

The next example shows how to update an existing linked table to refresh the link. This involves updating the connection string for the table and then resetting the Jet OLEDB:CreateLink property to tell Microsoft Jet to reestablish the link.

DAO

Sub DAORefreshLinks()

   Dim db As DAO.Database
   Dim tbl As DAO.TableDef

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   For Each tbl In db.TableDefs
      ' Check to make sure table is a linked table.
      If (tbl.Attributes And dbAttachedTable) Then
         tbl.Connect = "MS Access;PWD=NewPassWord;" & _
            "DATABASE=.\NewPubs.mdb"
         tbl.RefreshLink
      End If
   Next

End Sub

ADOX

Sub ADORefreshLinks()

   Dim cat As New ADOX.Catalog
   Dim tbl As ADOX.Table

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   For Each tbl In cat.Tables
      ' Check to make sure table is a linked table.
      If tbl.Type = "LINK" Then
         tbl.Properties("Jet OLEDB:Create Link") = False
         tbl.Properties("Jet OLEDB:Link Provider String") = _
            ";pwd=NewPassWord"
         tbl.Properties("Jet OLEDB:Link Datasource") = _
            ".\NewPubs.mdb"
         tbl.Properties("Jet OLEDB:Create Link") = True
      End If
   Next

End Sub

Creating an Index

Indexes on a column or columns in a table specify the order of records accessed from database tables and whether or not duplicate records are accepted. The following code creates an index on the Country field of the Employees table.

DAO

Sub DAOCreateIndex()

   Dim db As DAO.Database
   Dim tbl As DAO.TableDef
   Dim idx As DAO.Index

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   Set tbl = db.TableDefs("Employees")

   ' Create Index object append Field object to the Index object.
   Set idx = tbl.CreateIndex("CountryIndex")
   idx.Fields.Append idx.CreateField("Country")

   ' Append the Index object to the
   ' Indexes collection of the TableDef.
   tbl.Indexes.Append idx

   db.Close

End Sub

ADOX

Sub ADOCreateIndex()

   Dim cat As New ADOX.Catalog
   Dim tbl As ADOX.Table
   Dim idx As New ADOX.Index

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   Set tbl = cat.Tables("Employees")

   ' Create Index object append table columns to it.
   idx.Name = "CountryIndex"
   idx.Columns.Append "Country"

   ' Allow Null values to be added in the index field
   idx.IndexNulls = adIndexNullsAllow

   ' Append the Index object to the Indexes collection of Table
   tbl.Indexes.Append idx

   Set cat = Nothing

End Sub

The process for creating an index is the same in ADO and DAO. Create the index, append columns to the index, and then append the index to the table. However, there are some differences in behavior between the Index objects in these two models. DAO has two properties, Required and IgnoreNulls, that together determine whether or not Null values can be inserted for fields in the index and whether or not index entries will be created when some of the fields in a multi-column index contain Null. By default, both of these properties are False, indicating that Null values are allowed in the index and that an index entry will be added. This differs from ADO, which has a single property, IndexNulls for this purpose. By default, the IndexNulls property is adIndexNullsDisallow that indicates that Null values are not allowed in the index and that no index entry will be added if a field in the index contains Null.

The table below shows the mapping between the DAO Required and IgnoreNulls properties to the ADOX IndexNulls property.

DAO Required | DAO IgnoreNulls | ADOX IndexNulls | Description
True | False | adIndexNullsDisallow | A Null value isn't allowed in the index field; no index entry added.
False | True | adIndexNullsIgnore | A Null value is allowed in the index field; no index entry added.
False | False | adIndexNullsAllow | A Null value is allowed in the index field; index entry added.

Note that ADO defines an additional value for the IndexNulls property, adIndexNullsIgnoreAny, that is not listed in the table above. The Microsoft Jet Provider does not support this type of index. Setting IgnoreNulls to adIndexNullsIgnoreAny when using the Microsoft Jet Provider will result in a run-time error. The purpose of adIndexNullsIgnoreAny, if it was to be supported by a provider, is to ignore an entry if any column of a multi-column index contains a Null value.

Defining Keys and Relationships

Once the structure of a table has been defined, it is useful to define keys for the table and the relationships between tables. Microsoft Jet will use the information provided in the key and relationship definitions to optimize queries

Creating a Primary Key

A table often has a column or combination of columns whose values uniquely identify a row in a table. This column (or combination of columns) is called the primary key of the table. When you define a primary key, the Microsoft Jet database engine will create an index to enforce the uniqueness of the key.

Using the Contacts table created in previous examples, the following listings demonstrate how to make the ContactId column the primary key.

DAO

Sub DAOCreatePrimaryKey()

   Dim db As DAO.Database
   Dim tbl As DAO.TableDef
   Dim idx As DAO.Index

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   Set tbl = db.TableDefs("Contacts")

   ' Create the Primary Key and append table columns to it.
   Set idx = tbl.CreateIndex("PrimaryKey")
   idx.Primary = True
   idx.Fields.Append idx.CreateField("ContactId")

   ' Append the Index object to the
   ' Indexes collection of the TableDef.
   tbl.Indexes.Append idx

   db.Close

End Sub

ADOX

Sub ADOCreatePrimaryKey()

   Dim cat As New ADOX.Catalog
   Dim tbl As ADOX.Table
   Dim pk As New ADOX.Key

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   Set tbl = cat.Tables("Contacts")

   ' Create the Primary Key and append table columns to it.
   pk.Name = "PrimaryKey"
   pk.Type = adKeyPrimary
   pk.Columns.Append "ContactId"

   ' Append the Key object to the Keys collection of Table
   tbl.Keys.Append pk

   Set cat = Nothing

End Sub

With DAO, the Index object is used to create primary keys. The key is created much like any other index except that the Primary property is set to True. ADO, however, has a Key object that is used to create new keys. The steps in creating a key are similar to creating an index. However, when creating a Key, you must specify the type of Key you want to create. In this case, the key type is adKeyPrimary which indicates that you want to create a primary key.

Alternatively, the ADOX code to create and append the key could have been written in a single line of code. The following code:

   ' Create the Primary Key and append table columns to it.
   pk.Name = "PrimaryKey"
   pk.Type = adKeyPrimary
   pk.Columns.Append "ContactId"

   ' Append the Key object to the Keys collection of Table
   tbl.Keys.Append pk

is equivalent to:

   ' Append the Key object to the Keys collection of Table
   tbl.Keys.Append "PrimaryKey", adKeyPrimary, "ContactId"

Creating One-to-Many Relationships (Foreign Keys)

One-to-many relationships between tables (where the primary key value in the primary table may appear in multiple rows in the foreign table) are established by creating foreign keys. A foreign key is a column or combination of columns whose values match the primary key of another table. Unlike a primary key, a foreign key does not have to be unique.

DAO

Sub DAOCreateForeignKey()

   Dim db As DAO.Database
   Dim rel As DAO.Relation
   Dim fld As DAO.Field

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' This key already exists in the Northwind database.
   ' For the purposes of this example, we're going to
   ' delete it and then recreate it
   db.Relations.Delete "CategoriesProducts"

   ' Create the relation
   Set rel = db.CreateRelation()
   rel.Name = "CategoriesProducts"
   rel.Table = "Categories"
   rel.ForeignTable = "Products"

   ' Create the field the tables are related on
   Set fld = rel.CreateField("CategoryId")

   ' Set ForeignName property of the field to the name of
   ' the corresponding field in the primary table
   fld.ForeignName = "CategoryId"

   rel.Fields.Append fld

   ' Append the relation to the collection
   db.Relations.Append rel

End Sub

ADOX

Sub ADOCreateForeignKey()

   Dim cat As New ADOX.Catalog
   Dim tbl As ADOX.Table
   Dim fk As New ADOX.Key

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Get the table for the foreign side of the relationship
   Set tbl = cat.Tables("Products")

   ' This key already exists in the Northwind database.
   ' For the purposes of this example, we're going to
   ' delete it and then recreate it
   tbl.Keys.Delete "CategoriesProducts"

   ' Create the Foreign Key
   fk.Name = "CategoriesProducts"
   fk.Type = adKeyForeign
   fk.RelatedTable = "Categories"

   ' Append column(s) in the foreign table to it
   fk.Columns.Append "CategoryId"

   ' Set RelatedColumn property to the name of the corresponding
   ' column in the primary table
   fk.Columns("CategoryId").RelatedColumn = "CategoryId"

   ' Append the Key object to the Keys collection of Table
   tbl.Keys.Append fk

   Set cat = Nothing

End Sub

Alternatively, the ADOX code to create and append the key could have been written in a single line of code. The following code:

   ' Create the Foreign Key
   fk.Name = "CategoriesProducts"
   fk.Type = adKeyForeign
   fk.RelatedTable = "Categories"

   ' Append column(s) in the foreign table to it
   fk.Columns.Append "CategoryId"

   ' Set RelatedColumn property to the name of the corresponding
   ' column in the primary table
   fk.Columns("CategoryId").RelatedColumn = "CategoryId"

   ' Append the Key object to the Keys collection of Table
   tbl.Keys.Append fk

is equivalent to:

   ' Append the Key object to the Keys collection of Table
   tbl.Keys.Append "CategoriesProducts", adKeyForeign, _
      "CategoryId", "Categories", "CategoryId"

Enforcing Referential Integrity

Referential integrity preserves the defined relationships between tables when records are added, updated, or deleted. Maintaining referential integrity within your database requires that there be no references to nonexistent values, and that if a key value changes, all references to it change consistently throughout the database.

When you enforce referential integrity users are prevented from adding new records to a related table when there is no associated record in the primary table, changing primary key values that would result in "orphaned" records in the related table, or deleting records in the primary table when there are associated records in the related table.

By default, Microsoft Jet enforces relationships created by DAO or ADOX. A trappable error will occur if you make changes that violate referential integrity. When defining a new relationship, you can also specify that Microsoft Jet should cascade updates or deletes. With cascading updates, when a change is made to the primary key in a record in the primary table, Microsoft Jet will automatically update the foreign key in all related records in the related foreign table or tables. Similarly with cascading deletes, when a record is deleted from the primary table, Microsoft Jet will automatically delete all related records in the related foreign table or tables.

In the following example, the code from the preceding section is modified to create a foreign key that supports cascading updates and deletes.

DAO

Sub DAOCreateForeignKeyCascade()

   Dim db As DAO.Database
   Dim rel As DAO.Relation
   Dim fld As DAO.Field

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' This key already exists in the Northwind database.
   ' For the purposes of this example, we're going to
   ' delete it and then recreate it
   db.Relations.Delete "CategoriesProducts"

   ' Create the relation
   Set rel = db.CreateRelation()
   rel.Name = "CategoriesProducts"
   rel.Table = "Categories"
   rel.ForeignTable = "Products"

   ' Specify cascading updates and deletes
   rel.Attributes = dbRelationUpdateCascade Or _
      dbRelationDeleteCascade

   ' Create the field the tables are related on
   Set fld = rel.CreateField("CategoryId")
   ' Set ForeignName property of the field to the name of
   ' the corresponding field in the primary table
   fld.ForeignName = "CategoryId"

   rel.Fields.Append fld

   ' Append the relation to the collection
   db.Relations.Append rel

End Sub

ADOX

Sub ADOCreateForeignKeyCascade()

   Dim cat As New ADOX.Catalog
   Dim tbl As ADOX.Table
   Dim fk As New ADOX.Key

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
   "Data Source=.\NorthWind.mdb;"

   ' Get the table for the foreign side of the relationship
   Set tbl = cat.Tables("Products")

   ' This key already exists in the Northwind database.
   ' For the purposes of this example, we're going to
   ' delete it and then recreate it
   tbl.Keys.Delete "CategoriesProducts"

   ' Create the Foreign Key
   fk.Name = "CategoriesProducts"
   fk.Type = adKeyForeign
   fk.RelatedTable = "Categories"

   ' Specify cascading updates and deletes
   fk.UpdateRule = adRICascade
   fk.DeleteRule = adRICascade

   ' Append column(s) in the foreign table to it
   fk.Columns.Append "CategoryId"
   ' Set RelatedColumn property to the name of the corresponding
   ' column in the primary table
   fk.Columns("CategoryId").RelatedColumn = "CategoryId"

   ' Append the Key object to the Keys collection of Table
   tbl.Keys.Append fk

   Set cat = Nothing

End Sub

The following table shows how the values for the DAO Attributes property of a Relation object map to properties of the ADOX Key object.

Note The following values for the DAO Attributes property of a Relation object have no corresponding properties in ADOX: dbRelationDontEnforce, dbRelationInherited, dbRelationLeft, dbRelationRight.

DAO Relation Object Property | Value | ADOX Key Object Property | Value
Attributes | dbRelationUnique | Type | adKeyUnique
Attributes | dbRelationUpdateCascade | UpdateRule | adRICascade
Attributes | dbRelationDeleteCascade | DeleteRule | adRICascade

Creating and Modifying Queries

As discussed in the section, "Executing Queries" the ADO Command object is similar to the DAO QueryDef object in that it specifies an SQL string and parameters and executes the query. However, unlike the DAO QueryDef object, the ADO Command object cannot be used directly to persist a query. By specifying a name for the QueryDef when it is created, the DAO QueryDef is automatically appended to the QueryDefs collection and persisted in the database. This differs from ADO in which all Command objects are temporary queries. You must explicitly append the Command to the ADOX Procedures or Views collection in order to persist it in the database.

The Microsoft Jet Provider defines Microsoft Jet queries as Views if the query is a row-returning, non-parameterized query. The provider defines a procedure as either a non row-returning query (a bulk operation) or a parameterized row-returning query.

Creating a Stored Query

The following listings demonstrate how to create a row returning, non-parameterized query.

DAO

Sub DAOCreateQuery()

   Dim db As DAO.Database
   Dim qry As DAO.QueryDef

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Create query
   Set qry = db.CreateQueryDef("AllCategories", _
      "SELECT * FROM Categories")

   db.Close

End Sub

ADOX

Sub ADOCreateQuery()

   Dim cat As New ADOX.Catalog
   Dim cmd As New ADODB.Command

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Create the query
   cmd.CommandText = "SELECT * FROM Categories"
   cat.Views.Append "AllCategories", cmd

   Set cat = Nothing

End Sub

In this example, because the SQL statement is a non-parameterized, row-returning query, the ADO Command object is appended to the ADOX Views collection. Note, that when using the Microsoft Jet Provider, it is possible to append a Command object to either the Views or Procedures collection regardless of the type of query that is being created. However, if a query such as the one in this example is appended to the Procedures collection, then the Procedures and Views collections are refreshed, you'll notice that the query is no longer in the Procedures collection, but is now in the Views collection.

Likewise, you can append a parameterized query, or a non row-returning bulk operation query to either the Views or Procedures collection. However, ADOX will actually store these types of queries in the Procedures collection. If you append to the Views collection, then refresh both the Views and Procedures collections, you'll find that the newly appended query is now in the Procedures collection.

Creating a Parameterized Stored Query

The following listings demonstrate how to create a parameterized query and save it in the database.

DAO

Sub DAOCreateParameterizedQuery()

   Dim db As DAO.Database
   Dim qry As DAO.QueryDef

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Create query
   Set qry = db.CreateQueryDef("Employees by Region", _
      "PARAMETERS [prmRegion] TEXT(255);" & _
      "SELECT * FROM Employees WHERE Region = [prmRegion]")

   db.Close

End Sub

ADOX

Sub ADOCreateParameterizedQuery()

   Dim cat As New ADOX.Catalog
   Dim cmd As New ADODB.Command

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Create the Command
   cmd.CommandText = "PARAMETERS [prmRegion] TEXT(255);" & _
      "SELECT * FROM Employees WHERE Region = [prmRegion]"

   ' Create the Procedure
   cat.Procedures.Append "Employees by Region", cmd

   Set cat = Nothing

End Sub

The code for creating a parameterized query is very similar using DAO and ADOX. Note, though that although the ADO Command object allows you to create parameters using the CreateParameter method, this information will not be saved when creating or updating a Procedure. You must specify the parameters as part of the SQL string.

Also note that Microsoft Jet will interpret the SQL statement differently when a query is created with ADOX and the Microsoft Jet Provider rather than DAO. The Microsoft Jet Provider always sets a Microsoft Jet database engine option for ANSI compliance. This may cause differences in behavior between DAO and ADO when creating or executing queries. For example, if the SQL statement in the code above had been written as follows:

   "PARAMETERS [prmRegion] TEXT;" & _
      "SELECT * FROM Employees WHERE Region = [prmRegion]"

Omitting the (255) after the Text keyword, the parameter would be created as a text field (dbText, adVarWChar) when using DAO, but as a memo field (dbMemo, adLongVarWChar) when using ADO.

Further, some SQL statements that execute when using DAO will fail to execute when using ADO due to additional reserved words. For a list of reserved words, see Appendix D: Microsoft Jet 4.0 ANSI Reserved Words.

Modifying a Stored Query

The following listings demonstrate how to modify an existing query.

DAO

Sub DAOModifyQuery()

   Dim db As DAO.Database
   Dim qry As DAO.QueryDef

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Get the query
   Set qry = db.QueryDefs("Employees by Region")

   ' Update the SQL and save the updated query
   qry.SQL = "PARAMETERS [prmRegion] TEXT(255);" & _
      "SELECT * FROM Employees WHERE Region = [prmRegion] " & _
      "ORDER BY City"

   db.Close

End Sub

ADO

Sub ADOModifyQuery()

   Dim cat As New ADOX.Catalog
   Dim cmd As ADODB.Command

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Get the query
   Set cmd = cat.Procedures("Employees by Region").Command

   ' Update the SQL
   cmd.CommandText = "PARAMETERS [prmRegion] TEXT(255);" & _
      "SELECT * FROM Employees WHERE Region = [prmRegion] " & _
      "ORDER BY City"

   ' Save the updated query
   Set cat.Procedures("Employees by Region").Command = cmd

   Set cat = Nothing

End Sub

In the ADO code, setting the Procedure object's Command property to the modified Command object saves the changes. If this last step were not included, the changes would not have been persisted to the database. This difference results from the fact that ADO Command objects are designed as temporary queries while DAO QueryDef objects are designed as saved queries. You need to be aware of this when working with Commands, Procedures, and Views. You may think that the following ADO code examples are equivalent:

   Set cmd = cat.Procedures("Employees by Region").Command
   cmd.CommandText = "PARAMETERS [prmRegion] TEXT(255);" & _
      "SELECT * FROM Employees WHERE Region = [prmRegion] " & _
      "ORDER BY City"
   Set cat.Procedures("Employees by Region").Command = cmd

and

   cat.Procedures("Employees by Region").CommandText = _
      "PARAMETERS [prmRegion] TEXT;" & _
      "SELECT * FROM Employees WHERE Region = [prmRegion] " & _
      "ORDER BY City"

However, they are not. Both will compile, but the second piece of code will not actually update the query in the database. In the second example, ADOX will create a tear-off command object and hand it back to Visual Basic for Applications. Visual Basic for Applications will then ask ADOX to update the CommandText property, which it does. Finally, Visual Basic for Applications moves to execute the next line of code and the Command object is lost. ADOX is never asked to update the Procedure with the changes to the modified Command object.

Creating an SQL Pass-Through Query

SQL pass-through queries are SQL statements that are sent directly to the database server without interpretation by the Microsoft Jet database engine. When creating an SQL pass-through query, you must specify the SQL statement to execute as well as an ODBC connection string.

With DAO, pass-through queries provide a means of improving performance when accessing external ODBC data. With ADO, it is not necessary to create SQL pass-through queries in your Microsoft Jet database in order to have good performance when accessing external data. With ADO, you can use the Microsoft OLE DB Provider for SQL Server to directly access SQL Server without the overhead of Microsoft Jet or ODBC. You can also use the Microsoft OLE DB Provider for ODBC to access data in any ODBC data source.

While it is no longer necessary to create SQL pass-through queries in your Microsoft Jet database, it is still possible to do so using ADOX and the Microsoft Jet Provider. The following code demonstrates how to create an SQL pass-through query.

DAO

Sub DAOCreateSQLPassThrough()

   Dim db As DAO.Database
   Dim qry As DAO.QueryDef

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Create query
   Set qry = db.CreateQueryDef("Business Books", _
      "SELECT * FROM Titles WHERE Type = 'business'")

   qry.Connect = "ODBC;DSN=ADOPubs;UID=sa;PWD=;"
   qry.ReturnsRecords = True

   db.Close

End Sub

ADOX

Sub ADOCreateSQLPassThrough()

   Dim cat As New ADOX.Catalog
   Dim cmd As New ADODB.Command

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Create the Command
   Set cmd.ActiveConnection = cat.ActiveConnection
   cmd.CommandText = "SELECT * FROM Titles WHERE Type = 'business'"
   cmd.Properties("Jet OLEDB:ODBC Pass-Through Statement") = True
   cmd.Properties("Jet OLEDB:Pass Through Query Connect String") = _
      "ODBC;DSN=ADOPubs;database=pubs;UID=sa;PWD=;"

   ' Create the Procedure
   cat.Procedures.Append "Business Books", cmd

   Set cat = Nothing

End Sub


Security

Microsoft Jet databases can be secured in one of two ways: share-level security or user-level security. For share-level security, the database is secured with a password. Anyone attempting to open the database must specify the correct database password. For user-level security, each user is given a user name and password to open the database.

Changing a Password

The first step in securing a Microsoft Jet database is to change the password for the Admin user, if using user-level security, or changing the database password if using share-level security. When changing a password for a user or database, you must supply both the existing and new passwords. When changing the database or Admin user's password for the first time, use an empty string ("") as the existing password.

The following code shows how to enable user level security by setting the password for the Admin user to "password".

DAO

Sub DAOChangePassword()

   Dim wks As Workspace
   Dim usr As DAO.User

   ' Open the workspace, specifying the system database to use

   DBEngine.SystemDB = _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"
   Set wks = DBEngine.CreateWorkspace("", "Admin", "")

   ' Change the password for the user Admin
   wks.Users("Admin").NewPassword "", "password"

End Sub

ADOX

Sub ADOChangePassword()

   Dim cat As New ADOX.Catalog

   ' Open the catalog, specifying the system database to use
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;Jet OLEDB:System database=" & _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

   ' Change the password for the user Admin
   cat.Users("Admin").ChangePassword "", "password"

End Sub

DAO and ADOX both have a method on the User object to change the user's password. The method takes the user's current password and the new password as parameters. In DAO this method is called NewPassword while in ADOX it is called ChangePassword.

Note The Microsoft Jet Provider will not error on the line of code that opens the catalog if the system database specified is incorrect. However, it will error when attempting to change the password or perform any other security related operations with the following error if the system database was not correctly specified: "The operation requested by the application is not supported by the provider."

The following code shows how to change the database password for enabling security at the share level.

DAO

Sub DAOChangeDatabasePassword()

   ' Make sure there isn't already a file with the
   ' name of the compacted database.
   If Dir(".\NewNorthWind.mdb") <> "" Then _
      Kill ".\NewNorthWind.mdb"

   ' Basic compact - creating new database named newnwind
   DBEngine.CompactDatabase ".\NorthWind.mdb", _
      ".\NewNorthWind.mdb", , , ";pwd=password;"

   ' Delete the original database
   Kill ".\NorthWind.mdb"

   ' Rename the file back to the original name
   Name ".\NewNorthWind.mdb" As ".\NorthWind.mdb"

End Sub

JRO

Sub JROChangeDatabasePassword()

   Dim je As New JRO.JetEngine

   ' Make sure there isn't already a file with the
   ' name of the compacted database.
   If Dir(".\NewNorthWind.mdb") <> "" Then _
      Kill ".\NewNorthWind.mdb"

   ' Compact the database specifying the new database password
   je.CompactDatabase "Data Source=.\NorthWind.mdb;", _
      "Data Source=.\NewNorthWind.mdb;" & _
      "Jet OLEDB:Database Password=password"

   ' Delete the original database
   Kill ".\NorthWind.mdb"

   ' Rename the file back to the original name
   Name ".\NewNorthWind.mdb" As ".\NorthWind.mdb"

End Sub

Note JRO, not ADOX, is used to change a database password at share level.

Both DAO and JRO allow you to change the database password when compacting the database. The syntax is slightly different: in DAO, specify ";pwd=password;" in the Password parameter of CompactDatabase. In JRO, specify the provider-specific "Jet OLEDB:Database Password=password" in the destination connection parameter of CompactDatabase.

Alternatively, the DAO code could be rewritten to use the NewPassword method of the Database object.

Sub DAOChangeDatabasePassword2()

   Dim db As DAO.Database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb", True)
   db.NewPassword "", "password"
   db.Close

End Sub

A similar mechanism is not currently available in JRO or ADOX. You must use the CompactDatabase method in order to change the database password.

Creating Users and Groups

A User object represents a user account that has specific access permissions while a Group object represents a group of user accounts that have common access permissions. Creating users and groups allows you to easily control and maintain users' access to the database and objects within the database.

The following code example shows how to create a new user.

DAO

Sub DAOCreateUser()

   Dim wks As DAO.Workspace

   ' Open a workspace
   DBEngine.SystemDB = _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"
   Set wks = DBEngine.CreateWorkspace("", "Admin", "password")

   ' Create the user and append it to the Users collection
   wks.Users.Append wks.CreateUser("MyUser", "xNewUser", "password")

End Sub

ADOX

Sub ADOCreateUser()

   Dim cat As New ADOX.Catalog

   ' Open the catalog, specifying the system database to use
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;Jet OLEDB:System database=" & _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW;" & _
      "User Id=Admin;Password=password;"

   ' Create the new user and append it to the users collection
   cat.Users.Append "MyUser", "password"

End Sub

Unlike with DAO, with ADOX you do not have to create a User object before adding the user to the database with the Append method. With ADOX you can create a new user simply by passing the name and password to the Append method of the Users collection.

Setting PID values

When creating a user or group in DAO, you must provide a value for the PID parameter or property. This parameter is not required when creating a new user or group in ADOX because the Microsoft Jet Provider automatically generates PID values. This PID value is not retrievable using ADOX or DAO.

This presents problems if the workgroup information file, such as system.mdw, becomes corrupted or destroyed. In order to recreate users and groups and restore permissions to your databases and their objects, you must know the PID values. Microsoft Jet combines the user or group name and the PID value to create the identifier, called the SID, that it uses to control the permissions of databases and objects. By recreating a user or group with its original name and PID value, Microsoft Jet will create a SID value identical to the original value, so all permissions set originally using that SID will work again. Since you cannot set the PID value using ADOX, the Microsoft Jet Provider effectively creates a new SID that does not have any database or object permissions set for it.

There are two methods for working around this problem if you choose not to explicitly set the PID values. One is to make frequent backups of the workgroup information file, and restore the file from the backup when necessary. This is usually the most efficient way to avoid the problem. The second method is to recreate all of the permissions in the database to work with the new users and groups that you will create. This is usually difficult to accomplish efficiently. Both methods do not require you to keep track of the PID values for users and groups.

Another way to avoid this problem is to use the new SQL Data Definition Language (DDL) security commands available for Microsoft Jet 4.0. The following code example demonstrates how to create a user and specify a PID value using ADO and DDL.

ADO

Sub ADOCreateUser2()

   Dim cmd As New ADODB.Command

   ' Create the Command
   cmd.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;Jet OLEDB:System database=" & _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW;" & _
      "User Id=Admin;Password=password;"

   ' Execute the DDL security command
   cmd.CommandText = "CREATE USER MyUser MyPW MyPID"
   cmd.Execute

End Sub

Adding a User to a Group

Adding users to a group makes maintaining permissions easier. Because users within a group inherit the permissions of the group you can set permissions once and have it apply to an entire group of users. For example, you can assign update permissions for the Salary table to all managers by simply granting the Managers group update permission.

The following code example demonstrates how to create a new group and add an existing user to that group.

DAO

Sub DAOAddUserToNewGroup()

   Dim wks As DAO.Workspace

   ' Open the workspace
   DBEngine.SystemDB = _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"
   Set wks = DBEngine.CreateWorkspace("", "Admin", "password")

   ' Create a new group
   wks.Groups.Append wks.CreateGroup("MyGroup", "xMyGroup")

   ' Add the user to the new group
   wks.Users("MyUser").Groups.Append _
      wks.Users("MyUser").CreateGroup("MyGroup")

End Sub

ADOX

Sub ADOAddUserToNewGroup()

   Dim cat As New ADOX.Catalog

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;User Id=Admin;" & _
      "Password=password;Jet OLEDB:System database=" & _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

   ' Create a new group
   cat.Groups.Append "MyGroup"

   ' Add the user to the new group
   cat.Users("MyUser").Groups.Append "MyGroup"

End Sub

Both DAO and ADOX have a Groups collection on the Users object that can be used to add the user to a group as well as to determine what groups the user belongs to. However, note that with DAO you must create a new Group object using the User object's CreateGroup method before appending the Group object to the User object's Groups collection. With ADOX it is neither necessary nor valid to create a new Group object; just append the name of the group to the User object's Groups collection.

Note Before using this technique to create a Group, see the comments about setting PID values in the section "Creating Users and Groups."

Setting Permissions

By setting permissions you can control a user's access to an object. For example, you can allow one user to read an object's contents, but not change them. Permissions can be set for a specific user or an entire group of users. When permissions are set for a group, every user in that group inherits those permissions.

In the example below, the user created in the section, "Creating Users and Groups" is granted permissions to read, insert, update, and delete data.

DAO

Sub DAOSetUserObjectPermissions()

   Dim db As DAO.Database
   Dim wks As DAO.Workspace
   Dim doc As DAO.Document

   ' Open the database
   DBEngine.SystemDB = _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"
   Set wks = DBEngine.CreateWorkspace("", "Admin", "password")
   Set db = wks.OpenDatabase(".\NorthWind.mdb")

   ' Set permissions for MyUser on the Customers table
   Set doc = db.Containers("Tables").Documents("Customers")
   doc.UserName = "MyUser"
   doc.Permissions = dbSecRetrieveData Or dbSecInsertData _
      Or dbSecReplaceData Or dbSecDeleteData

End Sub

ADOX

Sub ADOSetUserObjectPermissions()

   Dim cat As New ADOX.Catalog

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;User Id=Admin;" & _
      "Password=password;Jet OLEDB:System database=" & _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

   ' Set permissions for MyUser on the Customers table
   cat.Users("MyUser").SetPermissions "Customers", adPermObjTable, _
      adAccessSet, adRightRead Or adRightInsert Or adRightUpdate _
      Or adRightDelete

End Sub

The process for setting permissions with ADOX is essentially the inverse of the DAO process. With DAO, you first select the object and then indicate the user for whom to set permissions. With ADOX, you first select the user and then specify the object on which to set permissions.

In addition, with DAO you set a series of properties in order to set permissions on an object. In the example above, you set the UserName property followed by the Permissions property. With ADOX, a single method, SetPermissions, is used to set permissions on an object. The SetPermissions method has parameters that map to the properties used in DAO.

With the DAO Permissions property, which maps to the Rights parameter of the ADOX SetPermissions method, you supply a constant or combination of constants that represent the permissions to set. The table below shows how the DAO Security constants map to the ADOX Rights constants.

DAO | ADOX
dbSecNoAccess | adRightNone
dbSecFullAccess | adRightFull
dbSecDelete | adRightDrop
dbSecReadSec | adRightReadPermissions
dbSecWriteSec | adRightWritePermissions
dbSecWriteOwner | adRightWriteOwner
dbSecCreate | adRightCreate
dbSecReadDef | adRightReadDesign
dbSecWriteDef | adRightWriteDesign
dbSecRetrieveData | adRightRead
dbSecInsertData | adRightInsert
dbSecReplaceData | adRightUpdate
dbSecDeleteData | adRightDelete
dbSecDBAdmin | adRightFull
dbSecDBCreate | adRightCreate
dbSecDBExclusive | adRightExclusive
dbSecDBOpen | adRightRead

As shown in the table above, DAO has specific security constants for setting permissions on a database. These constants are used with the Databases container or a database object. In the following listings, you can see how to use both DAO and ADOX to set permissions for a user on a database object.

DAO

Sub DAOSetDatabasePermissions()

   Dim db As DAO.Database
   Dim wks As DAO.Workspace
   Dim doc As DAO.Document

   ' Open the database
   DBEngine.SystemDB = _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"
   Set wks = DBEngine.CreateWorkspace("", "Admin", "password")
   Set db = wks.OpenDatabase(".\NorthWind.mdb")

   ' Set permissions for MyUser on the current database
   Set doc = db.Containers("Databases").Documents("MSysDB")
   doc.UserName = "MyUser"
   doc.Permissions = dbSecDBExclusive

End Sub

ADOX

Sub ADOSetDatabasePermissions()

   Dim cat As New ADOX.Catalog

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;User Id=Admin;" & _
      "Password=password;Jet OLEDB:System database=" & _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

   ' Set permissions for MyUser on the current database
   cat.Users("MyUser").SetPermissions "", adPermObjDatabase, _
      adAccessSet, adRightExclusive

End Sub

Setting permissions for a database differs slightly from other objects. When using DAO, you must specify "MSysDb" as the document name when you want to specify permissions for the current database. To do the equivalent in ADOX, specify an empty string ("") as the name of the database.

In addition to granting permissions to a user on specific objects you may also want to specify permissions for a class/container of objects such as Tables. When specifying permissions on a container, you can indicate whether new objects of that class created by the user should inherit those permissions by default.

DAO

Sub DAOSetUserContainerPermissions()

   Dim db As DAO.Database
   Dim wks As DAO.Workspace
   Dim ctr As DAO.Container

   ' Open the database
   DBEngine.SystemDB = _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"
   Set wks = DBEngine.CreateWorkspace("", "Admin", "password")
   Set db = wks.OpenDatabase(".\NorthWind.mdb")

   ' Set permissions for MyUser on the Tables Container
   Set ctr = db.Containers("Tables")
   ctr.UserName = "MyUser"
   ctr.Inherit = True
   ctr.Permissions = dbSecRetrieveData Or dbSecInsertData _
      Or dbSecReplaceData Or dbSecDeleteData

End Sub

ADOX

Sub ADOSetUserContainerPermissions()

   Dim cat As New ADOX.Catalog

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;User Id=Admin;" & _
      "Password=password;Jet OLEDB:System database=" & _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

   ' Set permissions for MyUser on the Tables Container
   cat.Users("MyUser").SetPermissions Null, adPermObjTable, _
      adAccessSet, adRightRead Or adRightInsert Or adRightUpdate _
      Or adRightDelete, adInheritNone

End Sub

With DAO, the Container object was used to specify permissions for a class of objects. With ADOX, setting the Name parameter of the SetPermissions object to Null sets permissions for the class of objects specified by the ObjectType parameter. The InheritType parameter of the ADOX SetPermissions method indicates whether new objects should inherit the permissions. This is equivalent to setting the DAO Inherit property. In the ADOX example, the InheritType parameter must be set to adInheritNone since it is setting the permissions for an existing object instead of a new object.

Use the Containers and Documents collections in DAO to set permissions on Access-specific objects, such as Forms, Reports, and Macros. Although ADOX supports setting permissions for Access-specific objects, the Microsoft Jet 4.0 Provider doesn't properly map the ADOX permissions flags to Microsoft Jet permissions flags. If you need to set permissions for Access-specific objects, you must use DAO until this problem is corrected.

Determining an Object's Owner

The database, and every object in the database, has an owner. By default, the owner is the user that created that object. The object owner has special priveleges for that object in that he or she can always assign or revoke permissions for that object.

The following listings demonstrate how to get the user name of the object owner.

DAO

Sub DAOGetObjectOwner()

   Dim db As DAO.Database
   Dim wks As DAO.Workspace

   ' Open the database
   DBEngine.SystemDB = _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"
   Set wks = DBEngine.CreateWorkspace("", "Admin", "password")
   Set db = wks.OpenDatabase(".\NorthWind.mdb")

   ' Print the owner of the Customers table
   Debug.Print db.Containers("Tables").Documents("Customers").Owner

End Sub

ADOX

Sub ADOGetObjectOwner()

   Dim cat As New ADOX.Catalog

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;User Id=Admin;" & _
      "Password=password;Jet OLEDB:System database=" & _
      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

   ' Print the owner of the Customers table
   Debug.Print cat.GetObjectOwner("Customers", adPermObjTable)

End Sub

With DAO, you use the Owner property of a Document or Container object to retrieve the user name of the object owner. With ADOX, you use the GetObjectOwner method of a Catalog object. This method takes the object's name and type as parameters.


Replication

Replication enables users at different locations to easily share the changes they are making to a database. Copies of a database, called replicas, can be made and distributed to users at different locations. Users at each location can work on their local copy and then share, or synchronize, their changes with users at other locations.

Note Use JRO, not ADO, to implement replication in your application.

Making a Database Replicable

The first step in enabling replication is to create a design master. A design master is the only replica in the replica set which can make both schema and data changes, all other replicas can only make data changes to replicated objects. Making a database replicable makes the database a design master.

The following listings demonstrate how to make an existing database replicable.

DAO

Sub DAOMakeDesignMaster()

   Dim dbsNorthwind As DAO.Database
   Dim prpNew As DAO.Property

   ' Open database for exclusive access.
   Set dbsNorthwind = DBEngine.OpenDatabase(".\NorthWind.mdb", True)

   With dbsNorthwind
      ' If Replicable property doesn't exist, create it.

      ' Turn on error handling in case property exists.
      On Error Resume Next
      Set prpNew = .CreateProperty("Replicable", dbText, "T")
      .Properties.Append prpNew

      ' Set database Replicable property to True.
      .Properties("Replicable") = "T"

      .Close
   End With

End Sub

JRO

Sub JROMakeDesignMaster()

   Dim repMaster As New JRO.Replica

   ' Make the Northwind database replicable.
   ' If successful, this will create a connection to the
   ' database.
   repMaster.MakeReplicable ".\NorthWind.mdb", False

   Set repMaster = Nothing

End Sub

The JRO model simplifies the code for making a database replicable. To make a database replicable using DAO, the database must be opened, the Replicable property created and appended to the Properties collection of the database, and then the property set to "T". With JRO, a database can be made replicable with a single method, MakeReplicable.

The MakeReplicable method in JRO has an optional second parameter named ColumnTracking set to False in the example above. It indicates whether or not changes are tracked at the column level or row level. DAO did not expose the ability to track changes at the column level. Therefore, this parameter must be set to False if you want the same behavior as DAO. See the section, "New Features in ADO, ADOX, and JRO" for more information on column level tracking.

As with DAO, the process of making a database replicable using JRO cannot be reversed. It is recommended that you make a backup of your database before performing this operation.

Making Objects Local or Replicable

By default, when a database is made replicable all objects in that database will be replicated. If you do not want an object replicated you must indicate that the object should not be replicated (that is, it should remain local) before you make the database replicable.

In contrast, when you create a new table, query, form, report, macro, or module at a replica, the object is considered local and is stored only at that replica. If you want users at other replicas to be able to use the object, you must make it replicable.

The following listings demonstrate how to indicate that an object should be kept local when the database is made replicable.

DAO

Sub DAOKeepObjectLocal()

   Dim dbsNorthwind As DAO.Database
   Dim docTemp As DAO.Document
   Dim prpTemp As DAO.Property

   Set dbsNorthwind = DBEngine.OpenDatabase(".\NorthWind.mdb")

   Set docTemp = _
      dbsNorthwind.Containers("Tables").Documents("Contacts")
   Set prpTemp = docTemp.CreateProperty("KeepLocal", dbText, "T")

   docTemp.Properties.Append prpTemp

   dbsNorthwind.Close

End Sub

JRO

Sub JROKeepObjectLocal()

   Dim repMaster As New JRO.Replica

   repMaster.ActiveConnection = ".\NorthWind.mdb"

   repMaster.SetObjectReplicability "Contacts", "Tables", False

   Set repMaster = Nothing

End Sub

This next example shows how to make a new object in a replica replicable.

DAO

Sub DAOMakeObjectReplicable(strTable As String)

   Dim dbsNorthwind As DAO.Database
   Dim tdfTemp As DAO.TableDef

   Set dbsNorthwind = DBEngine.OpenDatabase(".\NorthWind.mdb")
   Set tdfTemp = dbsNorthwind.TableDefs(strTable)

   On Error GoTo ErrHandler

   tdfTemp.Properties("Replicable") = "T"

   On Error GoTo 0

   dbsNorthwind.Close

   Exit Sub

ErrHandler:

   Dim prpNew As DAO.Property

   If Err.Number = 3270 Then
      Set prpNew = tdfTemp.CreateProperty("Replicable", dbText, "T")
      tdfTemp.Properties.Append prpNew
   Else
      MsgBox "Error " & Err & ": " & Error
   End If

End Sub

JRO

Sub JROMakeObjectReplicable(strTable As String)

   Dim repMaster As New JRO.Replica

   repMaster.ActiveConnection = ".\NorthWind.mdb"

   repMaster.SetObjectReplicability strTable, "Tables", True

   Set repMaster = Nothing

End Sub

With DAO, two properties, Replicable and KeepLocal determine whether or not an object is or will be replicated. Use the KeepLocal property prior to making the database replicable to indicate that the object should not be made replicable when the database is made replicable. Use the Replicable property after the database is made replicable to indicate whether or not the object should be replicated. DAO requires you to create the properties using the CreateProperty method of the object's Properties collection before you can set them.

With JRO, the GetObjectReplicability and SetObjectReplicability methods are used, both before and after the database is made replicable, to determine or set whether the object is or will be replicated. The method takes the name of the object you wish to get or set replicability for, the type of the object, and a Boolean value that indicates whether it should be kept local or made replicable.

The following pseudocode is the algorithm for mapping the DAO KeepLocal and Replicable properties to the ADO ObjectReplicability.

If DAO.Database.Replicable = 'T'
   If DAO.Object.Replicable = 'T'
      JRO.ObjectReplicability = True
   Else
      JRO.ObjectReplicability = False
Else
   If DAO.Object.KeepLocal = 'T'
      JRO.ObjectReplicability = False
   Else
      JRO.ObjectReplicability = True

Creating a Replica

The following listings demonstrate how to create a full, read/write replica of an existing replica using DAO and then using JRO.

DAO

Function DAOMakeAdditionalReplica(strReplicableDB As String, _
   strNewReplica As String) As Integer

   Dim dbsTemp As DAO.Database

   Set dbsTemp = DBEngine.OpenDatabase(strReplicableDB)

   dbsTemp.MakeReplica strNewReplica, "Replica of " & strReplicableDB

   dbsTemp.Close

End Function

JRO

Function JROMakeAdditionalReplica(strReplicableDB As String, _
   strNewReplica As String) As Integer

   Dim repMaster As New JRO.Replica

   repMaster.ActiveConnection = strReplicableDB

   repMaster.CreateReplica strNewReplica, "Replica of " & _
      strReplicableDB

   Set repMaster = Nothing

End Function

The code for creating a replica with JRO is similar to the DAO code. Both examples begin with opening or connecting to the design master. In DAO, the design master is opened with the DBEngine object's OpenDatabase method. In ADO, setting the Replica object's ActiveConnection property opens the design master. Once it is open, the new replica is creating by calling a method to create the replica. The JRO equivalent to the DAO MakeReplica method is CreateReplica.

The DAO MakeReplica method has an optional parameter named Options. This parameter allows you to indicate the type of replica to create: full or partial, read-only or read/write.

In JRO there are two optional parameters named Type and Updatability. The Type parameter allows the user to indicate whether the replica should be full or partial. The Updatability parameter allows the user to indicate whether the replica is read-only or fully updatable.

The following table shows how the optional parameters and constants for the DAO MakeReplica method map to those for the JRO CreateReplica method.

DAO Parameter | DAO Constant | JRO Parameter | JRO Constant
Options | dbRepMakePartial | Type | jrRepTypePartial
Options | dbRepMakeReadOnly | Updatability | jrRepUpdReadOnly

The JRO CreateReplica method has two additional, optional parameters named Visibility and Priority. These parameters are omitted in the JRO code example above indicating that the default value should be used. Visibility and Priority are new in JRO and provide additional control over how synchronizations with the replica will be performed. The default value for each of these parameters maps to the DAO behavior. See the section, "New Features in ADO, ADOX, and JRO" for more information about replica visibility and priority.

Creating a Partial Replica

Sometimes, it is necessary to create replicas that contain a subset of the data contained in another replica. For example, a business might store its entire sales database at the headquarters office but replicate only regional data to its regional offices across the country. You can create a separate replica for each regional office that contains only the data relating to that region. The database at the headquarters office would be a full replica, with which each partial replica would be synchronized.

There are two ways to filter the data in a partial replica. The first method is by an expression, similar to an SQL WHERE clause (without the word WHERE). With an expression-based filter, the records in the table are limited to those that satisfy the expression. The second method to filter data is with a relationship filter. Relationship filters allow you to enforce the relationship when replicating data. It is generally used in conjunction with an expression-based filter.

The following listings demonstrate how to create a new partial replica and then populate the data in the partial replica limited by both an expression based filter and a relationship based filter.

DAO

Sub DAOCreatePartial()

   Dim dbsFull As DAO.Database
   Dim dbsPartial As DAO.Database
   Dim tdfCustomers As DAO.TableDef
   Dim relCustOrders As DAO.Relation

   ' Create partial replica.
   Set dbsFull = DBEngine.OpenDatabase(".\NorthWind.mdb")
   dbsFull.MakeReplica ".\FY96.mdb", "Partial Sales Replica", _
      dbRepMakePartial
   dbsFull.Close

   ' Create an expression based filter in the partial replica.
   Set dbsPartial = DBEngine.OpenDatabase(".\FY96.mdb", True)
   Set tdfCustomers = dbsPartial.TableDefs("Customers")
   tdfCustomers.ReplicaFilter = "Region = 'CA'"

   ' Create a filter based on a relationship in the partial replica.
   Set relCustOrders = dbsPartial.Relations("CustomersOrders")
   relCustOrders.PartialReplica = True

   ' Repopulate the partial replica based on the filters.
   dbsPartial.PopulatePartial ".\NorthWind.mdb"

   dbsPartial.Close

End Sub

JRO

Sub JROCreatePartial()

   Dim repFull As New JRO.Replica
   Dim repPartial As New JRO.Replica

   ' Create partial replica.
   repFull.ActiveConnection = ".\NorthWind.mdb"
   repFull.CreateReplica ".\FY96.mdb", "Partial Sales Replica", _
      jrRepTypePartial
   Set repFull = Nothing

   ' Create an expression based filter in the partial replica.
   ' The PopulatePartial method requires an exclusive connection
   repPartial.ActiveConnection = _
      "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\FY96.mdb;Mode=Share Exclusive"
   repPartial.Filters.Append "Customers", jrFilterTypeTable, _
      "Region = 'CA'"

   ' Create a filter based on a relationship in the partial replica.
   repPartial.Filters.Append "Orders", jrFilterTypeRelationship, _
      "CustomersOrders"

   ' Repopulate the partial replica based on the filters.
   repPartial.PopulatePartial ".\NorthWind.mdb"

   Set repPartial = Nothing

End Sub

The process for creating a partial replica is the same in JRO as it is in DAO. With both models the process is as follows: create the partial replica, create the filter(s), populate the partial replica using the filters. The primary difference between the two models is in creating the filters. DAO exposes properties of the Table and Relation objects for creating filters. JRO has a Filters collection. Use the Filters collection Append method to create new filters.

Listing Filters

The following listings demonstrate how to list all of the filters for a partial replica.

DAO

Sub DAOListFilters()

   Dim dbPartial As DAO.Database
   Dim tbl As DAO.TableDef
   Dim rel As DAO.Relation

   Set dbPartial = DBEngine.OpenDatabase(".\FY96.mdb")

   For Each tbl In dbPartial.TableDefs
      If tbl.ReplicaFilter <> "" Then
         Debug.Print tbl.Name & " : Table Filter : " & _
            tbl.ReplicaFilter
      End If
   Next

   For Each rel In dbPartial.Relations
      Debug.Print rel.Name & " : Relationship Filter";
      If rel.PartialReplica Then
         Debug.Print " : Partial";
      End If
      Debug.Print
   Next

   dbPartial.Close

End Sub

JRO

Sub JROListFilters()

   Dim repPartial As New JRO.Replica
   Dim flt As JRO.Filter
   Dim strFilterType As String

   repPartial.ActiveConnection = _
      "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=.\FY96.mdb"

   For Each flt In repPartial.Filters
      If flt.FilterType = jrFilterTypeTable Then
         strFilterType = "Table Filter"
      Else
         strFilterType = "Relationship Filter"
      End If
      Debug.Print flt.TableName & " : " & strFilterType & " : " & _
         flt.FilterCriteria
   Next

   Set repPartial = Nothing

End Sub

Synchronizing Data

Synchronizing two replicas involves exchanging data and design changes. Synchronization can be bi-directional, (that is, changes in each replica are propagated to the other) or can occur in a single direction.

The following listing demonstrates how to synchronize changes between two replicas. The first example shows how to do a direct, two-way synchronization.

DAO

Sub DAOTwoWayDirectSync()

   Dim dbsNorthwind As DAO.Database

   Set dbsNorthwind = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Sends changes made in each replica to the other.
   dbsNorthwind.Synchronize ".\FY96.mdb", dbRepImpExpChanges

   dbsNorthwind.Close

End Sub

JRO

Sub JROTwoWayDirectSync()

   Dim repMaster As New JRO.Replica

   repMaster.ActiveConnection = ".\NorthWind.mdb"

   ' Sends changes made in each replica to the other.
   repMaster.Synchronize ".\FY96.mdb", jrSyncTypeImpExp, _
      jrSyncModeDirect

   Set repMaster = Nothing

End Sub

The following example shows how to do a two-way synchronization over the Internet.

DAO

Sub DAOInternetSync()

   Dim dbsTemp As DAO.Database

   Set dbsTemp = DBEngine.OpenDatabase(".\NewNorthWind.mdb")

   ' Synchronize the local database with the replica on
   ' the Internet server.
   dbsTemp.Synchronize "sampleserver/files/Northwind.mdb", _
      dbRepImpExpChanges + dbRepSyncInternet

   dbsTemp.Close

End Sub

JRO

Sub JROInternetSync()

   Dim repMaster As New JRO.Replica

   repMaster.ActiveConnection = ".\NorthWind.mdb"

   ' Synchronize the local database with the replica on
   ' the Internet server.
   repMaster.Synchronize "sampleserver/files/Northwind.mdb", _
      jrSyncTypeImpExp, jrSyncModeInternet

   Set repMaster = Nothing

End Sub

For more information on Internet synchronization, read the technical article "Internet Synchronization with Microsoft Jet 4.0" available on the Microsoft Developer Network (MSDN) Library.

The JRO and DAO code for performing a two way, direct synchronization between two replicas is similar. However, note that the JRO Synchronize method has an additional parameter that specifies jrSyncModeDirect. For functionality equivalent to DAO you must specify jrSyncModeDirect when calling the Synchronize method. In JRO, if the SyncMode parameter is omitted, the synchronization will be performed indirectly. The ability to perform indirect synchronizations is a new feature in JRO designed to increase performance when synchronizing over a Wide Area Network (WAN). See the section, "New Features in ADO, ADOX, and JRO" for more information about performing indirect synchronizations.

The following table shows the mapping between the DAO Exchange parameter of the Synchronize method and the JRO SyncType and SyncMode parameters.

DAO Parameter | DAO Constant | JRO Parameter | JRO Constant
Exchange | dbRepExportChanges | SyncType | jrSyncTypeExport
Exchange | dbRepImportChanges | SyncType | jrSyncTypeImport
Exchange | dbRepImExpChanges | SyncType | jrSyncTypeImpExp
Exchange | dbRepSyncInternet | SyncMode | jrSyncModeInternet

Listing Synchronization Conflict Tables

If two users at two separate replicas each make a change to the same record in the database, a conflict may occur. If changes are being tracked at the row level, a conflict will occur if two users make a change to the same record. If changes are being tracked at the column level, a conflict will occur if two users make a change to the same column with a record. When a conflict occurs, the changes made by one user will fail to be applied to the other replica. Information regarding the conflict will be replicated to both replicas.

Information about the conflict is contained in a conflict table in each replica. Conflict tables contain the information that would have been placed in the table if the change had been successful. You can examine these conflict tables and work through them row by row, resolving the conflicts as appropriate.

The following listings demonstrate how to determine whether conflicts occurred during synchronization and, if conflicts did occur, how to retrieve the names of the conflict tables that were created.

DAO

Sub DAOConflictTables()

   Dim dbsNorthwind As DAO.Database
   Dim tdfTest As DAO.TableDef
   Dim bConflict As Boolean

   Set dbsNorthwind = DBEngine.OpenDatabase(".\NorthWind.mdb")

   bConflict = False

   ' Enumerate TableDefs collection and check ConflictTable
   ' property of each.
   For Each tdfTest In dbsNorthwind.TableDefs
      If tdfTest.ConflictTable <> "" Then
         ' There was a conflict with this table
         Debug.Print tdfTest.Name & " had a conflict."
         bConflict = True
      End If
   Next tdfTest

   ' If bConflict is still false then we didn't find any
   ' tables that had conflicts.
   If Not bConflict Then Debug.Print "No conflicts."

   dbsNorthwind.Close

End Sub

JRO

Sub JROConflictTables()

   Dim repMaster As New JRO.Replica
   Dim rstConflicts As ADODB.Recordset

   repMaster.ActiveConnection = ".\NorthWind.mdb"

   Set rstConflicts = repMaster.ConflictTables

   If rstConflicts.BOF And rstConflicts.EOF Then
      ' There are no conflict tables so no conflicts occurred.
      Debug.Print "No conflicts."
   Else
      Do Until rstConflicts.EOF
         Debug.Print rstConflicts.Fields(0) & " had a conflict."
         rstConflicts.MoveNext
      Loop
   End If

End Sub

With JRO, the ConflictTables property of the Replica object is used to determine which tables had conflicts. This property returns an ADO Recordset object that contains one row for each table containing conflicts. With the ConflictTables property it is easy to determine whether or not conflicts occurred. If the Recordset is empty (the BOF and EOF properties of the Recordset are both true), then no errors occurred. This differs from DAO in that with DAO you must check the ConflictTable property for each table in the TableDefs collection to determine whether conflicts occurred and what the name of the related conflict table is.


Miscellaneous

The next few sections show the differences between DAO and ADO for these tasks:

  • Handling Errors
  • Using Transactions
  • Compacting a Database
  • Refreshing the Cache

Handling Errors

There are two types of errors that can occur when executing ADO, ADOX, or JRO code: ADO errors and provider errors. ADO errors occur when you attempt to perform an invalid operation such as trying to retrieve the tenth Field from the Recordset object's Field collection when the Fields collection only contains five fields.

Provider errors are errors generated by the OLE DB provider or underlying data source. For example, specifying an invalid file name as the data source when trying to open a Microsoft Jet database will result in a provider error.

ADO errors are exposed by the run-time exception handling mechanism. In Visual Basic for Applications, an ADO error will trigger the On Error event and the Err object will contain information about the error. The ADO error will not create a new Error object in the Errors collection of the ADO Connection. OLE DB provider errors will create new Error objects in the Errors Collection of the ADO Connection.

The Error object in both DAO and ADO is unlike the error variables and functions in Visual Basic in that more than one error can be generated by a single operation. The set of Error objects in the Errors collection describes one error.

The following code attempts to open a database that doesn't exist and then displays the error(s) that result.

DAO

Sub DAODatabaseError()

   On Error GoTo DAODatabaseError_Err

   Dim db As DAO.Database
   Dim errDB As DAO.Error

   Set db = DBEngine.OpenDatabase(".\NonExistent.mdb")

   Exit Sub

DAODatabaseError_Err:
   For Each errDB In DBEngine.Errors
      Debug.Print "Description: " & errDB.Description
      Debug.Print "Number: " & errDB.Number
      Debug.Print "JetErr: " & errDB.Number
   Next

End Sub

ADO

Sub ADODatabaseError()

   On Error GoTo ADODatabaseError_Err

   Dim cnn As New ADODB.Connection
   Dim errDB As ADODB.Error

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NonExistent.mdb"

   Exit Sub

ADODatabaseError_Err:
   For Each errDB In cnn.Errors
      Debug.Print "Description: " & errDB.Description
      Debug.Print "Number: " & errDB.Number & " (" & _
         Hex$(errDB.Number) & ")"
      Debug.Print "JetErr: " & errDB.SQLState
   Next

End Sub

The code is very similar. Note, however, that the ADO code will print two different error numbers. The first number is the ADO/OLE DB error code. This error code will be the same for similar errors regardless of the provider being used. This allows you to write ADO applications that can handle errors even when the provider is changed. The second number is a provider-specific error code. When using the Microsoft Jet Provider, this error number will be the same error number that DAO returns. However, other providers may return different numbers for this type of error.

Using Transactions

A transaction is defined as a "logical unit of work". Use transactions to enforce data integrity by making sure that multiple, related database operations are committed in an all or nothing manner. Microsoft Jet allows you to include both DML and DDL operations within a single transaction.

The following listing demonstrates how to use a transaction. It combines DML and DDL operations within a single transaction. If any part of the code fails, all changes will be rolled back. The code creates a new table named Contacts, populates it with data from the Customers table, adds a new column named ContactId to the Customers table, and then deletes the columns containing contact information from the Customers table.

DAO

Sub DAOTransactions()

   On Error GoTo DAOTransactions_Err

   Dim wks As DAO.Workspace
   Dim db As DAO.Database
   Dim tbl As DAO.TableDef
   Dim bTrans As Boolean

   ' Get the default workspace
   Set wks = DBEngine.Workspaces(0)

   ' Open the database
   Set db = wks.OpenDatabase(".\NorthWind.mdb")

   ' Begin the Transaction
   wks.BeginTrans
   bTrans = True

   ' Create the Contacts table.
   Set tbl = db.CreateTableDef("Contacts")
   With tbl
      ' Create fields and append them to the new TableDef object.
      ' This must be done before appending the TableDef object to
      ' the TableDefs collection of the Database.
      .Fields.Append .CreateField("ContactId", dbLong)
      .Fields("ContactId").Attributes = dbAutoIncrField
      .Fields.Append .CreateField("ContactName", dbText)
      .Fields.Append .CreateField("ContactTitle", dbText)
      .Fields.Append .CreateField("Phone", dbText)
      .Fields.Append .CreateField("Notes", dbMemo)
      .Fields("Notes").Required = False
   End With
   db.TableDefs.Append tbl

   ' Populate the Contacts table with information from the
   ' customers table
   db.Execute "INSERT INTO Contacts (ContactName, ContactTitle," & _
      "Phone) SELECT DISTINCTROW [Customers].[ContactName], " & _
      "[Customers].[ContactTitle], [Customers].[Phone] " & _
      "FROM Customers;"

   ' Add a ContactId field to the Customers Table
   Set tbl = db.TableDefs("Customers")
   tbl.Fields.Append tbl.CreateField("ContactId", dbLong)

   ' Populate the Customers table with the appropriate ContactId
   db.Execute "UPDATE DISTINCTROW Contacts INNER JOIN Customers " & _
      "ON Contacts.ContactName = Customers.ContactName SET " & _
      "Customers.ContactId = [Contacts].[ContactId];"

   ' Delete the ContactName, ContactTitle, and Phone columns from
   ' Customers
   tbl.Fields.Delete "ContactName"
   tbl.Fields.Delete "ContactTitle"
   tbl.Fields.Delete "Phone"

   ' Commit the transaction
   wks.CommitTrans

   Exit Sub

DAOTransactions_Err:
   If bTrans Then wks.Rollback

   Debug.Print DBEngine.Errors(0).Description
   Debug.Print DBEngine.Errors(0).Number

End Sub

ADO

Sub ADOTransactions()

   On Error GoTo ADOTransactions_Err

   Dim cnn As New ADODB.Connection
   Dim cat As New ADOX.Catalog
   Dim tbl As New ADOX.Table
   Dim bTrans As Boolean

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Begin the Transaction
   cnn.BeginTrans
   bTrans = True

   Set cat.ActiveConnection = cnn

   ' Create the Contacts table
   With tbl
      .Name = "Contacts"
      Set .ParentCatalog = cat
      .Columns.Append "ContactId", adInteger
      .Columns("ContactId").Properties("AutoIncrement") = True
      .Columns.Append "ContactName", adWChar
      .Columns.Append "ContactTitle", adWChar
      .Columns.Append "Phone", adWChar
      .Columns.Append "Notes", adLongVarWChar
      .Columns("Notes").Attributes = adColNullable
   End With
   cat.Tables.Append tbl

   ' Populate the Contacts table with information from the
   ' customers table
   cnn.Execute "INSERT INTO Contacts (ContactName, ContactTitle," & _
      "Phone) SELECT DISTINCTROW Customers.ContactName," & _
      "Customers.ContactTitle, Customers.Phone FROM Customers;"

   ' Add a ContactId field to the Customers Table
   Set tbl = cat.Tables("Customers")
   tbl.Columns.Append "ContactId", adInteger

   ' Populate the Customers table with the appropriate ContactId
   cnn.Execute "UPDATE DISTINCTROW Contacts INNER JOIN Customers " _
      & "ON Contacts.ContactName = Customers.ContactName SET " & _
      "Customers.ContactId = [Contacts].[ContactId];"

   ' Delete the ContactName, ContactTitle, and Phone columns
   ' from Customers
   tbl.Columns.Delete "ContactName"
   tbl.Columns.Delete "ContactTitle"
   tbl.Columns.Delete "Phone"

   ' Commit the transaction
   cnn.CommitTrans

   Exit Sub

ADOTransactions_Err:
   If bTrans Then cnn.RollbackTrans

   Debug.Print cnn.Errors(0).Description
   Debug.Print cnn.Errors(0).Number
   Debug.Print cnn.Errors(0).SQLState

End Sub

Both DAO and ADO have similar methods for beginning, committing, and rolling back a transaction. One difference to note however is that because DAO transactions are tied to the Workspace object, it is possible to use DAO to perform a transaction that spans multiple Microsoft Jet databases. ADO transactions are tied to the Connection object, which limits the transaction to a single data source.

DAO also supports an additional parameter to the CommitTrans method: dbForceOSFlush. This forces the database engine to immediately flush all updates to disk, instead of caching them temporarily. The Microsoft Jet Provider exposes a property, "Jet OLEDB:Transaction Commit Mode", in the Connection object's Properties collection that allows you to specify that transactions within that connection should flush all updates to disk upon commit. Setting this property to 1 is equivalent to using the dbForceOSFlush parameter.

Compacting a Database

As a database file is used, it can become fragmented as objects and records are created and deleted. Periodic defragmentation reduces the amount of wasted space in the file and can enhance performance. Compacting can also repair a corrupted database.

The following listings demonstrate how to compact a database.

Note Use JRO, not ADO to compact a database.

DAO

Sub DAOCompactDatabase()

   ' Make sure there isn't already a file with the
   ' name of the compacted database.
   If Dir(".\NewNorthWind.mdb") <> "" Then Kill ".\NewNorthWind.mdb"

   ' Basic compact - creating new database named newnwind
   DBEngine.CompactDatabase ".\NorthWind.mdb", ".\NewNorthWind.mdb"

   ' Delete the original database
   Kill ".\NorthWind.mdb"

   ' Rename the file back to the original name
   Name ".\NewNorthWind.mdb" As ".\NorthWind.mdb"

End Sub

JRO

Sub JROCompactDatabase()

   Dim je As New JRO.JetEngine

   ' Make sure there isn't already a file with the
   ' name of the compacted database.
   If Dir(".\NewNorthWind.mdb") <> "" Then Kill ".\NewNorthWind.mdb"

   ' Compact the database
   je.CompactDatabase "Data Source=.\NorthWind.mdb;", _
      "Data Source=.\NewNorthWind.mdb;"

   ' Delete the original database
   Kill ".\NorthWind.mdb"

   ' Rename the file back to the original name
   Name ".\NewNorthWind.mdb" As ".\NorthWind.mdb"

End Sub

The JRO CompactDatabase method takes two connection strings that indicate the source database and destination database respectively. See the JRO online help for more information on the JRO CompactDatabase method.

In addition to defragmenting or repairing your database, CompactDatabase can also be used to change the database password, convert the database from an older Microsoft Jet version to a new version, to encrypt or decrypt the database, or to change the locale of the database. The following code demonstrates how to encrypt a database.

DAO

Sub DAOEncryptDatabase()

   ' Use compact to create a new, encrypted version of the database
   DBEngine.CompactDatabase ".\NorthWind.mdb", _
      ".\NewNorthWind.mdb", , dbEncrypt

End Sub

JRO

Sub JROEncryptDatabase()

   Dim je As New JRO.JetEngine

   ' Use compact to create a new, encrypted version of the database
   je.CompactDatabase "Data Source=.\NorthWind.mdb;", _
      "Data Source=.\NewNorthWind.mdb;" & _
      "Jet OLEDB:Encrypt Database=True"

End Sub

Refreshing the Cache

Microsoft Jet maintains an internal cache of records for each Microsoft Jet session. Caching records provides a significant performance improvement, but it means that other sessions may not immediately see changes.

In DAO a session is associated with a DBEngine object. As each application can only have one DBEngine object, it means that each application will have its own session. A given application using DAO will always see its own changes, but other applications may not see the changes immediately. In ADO a session is associated with a Connection object. A single application using ADO may have multiple Connection objects. So within a single application, changes may not been seen immediately.

There may be instances where performance is less important than guaranteeing that a Recordset contains the latest data. In those instances, it makes sense to force a refresh of Microsoft Jet's internal cache. Both DAO and JRO provide a mechanism for this. In DAO, use the DBEngine object's Idle method with dbRefreshCache to force Microsoft Jet to refresh its cache. With JRO, use the JetEngine object's RefreshCache method passing in the ADO connection as a parameter.

The following listings demonstrate how to refresh the cache using DAO and JRO.

DAO

Sub DAORefreshCache()

   Dim db As DAO.Database
   Dim rst As DAO.Recordset
   Dim fld As DAO.Field

   ' Open the database
   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   ' Refresh the cache to ensure that the latest data
   ' is available.
   DBEngine.Idle dbRefreshCache

   Set rst = db.OpenRecordset("SELECT * FROM Shippers")
   Do Until rst.EOF
      For Each fld In rst.Fields
         Debug.Print fld.Value;
      Next
      Debug.Print
      rst.MoveNext
   Loop
   rst.Close

End Sub

ADO

Sub JRORefreshCache()

   Dim cnn As New ADODB.Connection
   Dim rst As ADODB.Recordset
   Dim fld As ADODB.Field
   Dim je As New JRO.JetEngine

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Refresh the cache to ensure that the latest data
   ' is available.
   je.RefreshCache cnn

   ' Open a recordset and read the data
   Set rst = cnn.Execute("SELECT * FROM Shippers")
   Do Until rst.EOF
      For Each fld In rst.Fields
         Debug.Print fld.Value;
      Next
      Debug.Print
      rst.MoveNext
   Loop
   rst.Close

End Sub

This example above is somewhat contrived because the cache will most likely already contain the latest data as the Database and Connection are being opened for the first time immediately before attempting to open the Recordset. The ability to refresh the cache is generally more useful when a Database or Connection is opened when the application is first launched and then at some later point a Recordset with the latest data needs to be opened.


New Features in ADO, ADOX, and JRO

The following sections describe new features in ADO, ADOX, JRO, and the Microsoft Jet Provider. The functionality exposed by these features is not available in DAO. This is not intended as a complete list of additional features exposed in ADO, but rather this section serves to highlight some of the new functionality.

Creatable Recordset Objects

Often, a developer finds a need for a place to temporarily store some data, or wants some data to act like it came from a server so it can participate in data binding in a user interface.

ADO (in conjunction with the Cursor Service for OLE DB) enables the developer to build an empty Recordset object by specifying column information and calling Open. The following code demonstrates this:

Sub ADOCreateRecordset()

   Dim rst As New ADODB.Recordset

   rst.CursorLocation = adUseClient

   ' Add Some Fields
   rst.Fields.Append "dbkey", adInteger
   rst.Fields.Append "field1", adVarChar, 40, adFldIsNullable
   rst.Fields.Append "field2", adDate

   ' Create the Recordset
   rst.Open , , adOpenStatic, adLockBatchOptimistic

   ' Add Some Rows
   rst.AddNew Array("dbkey", "field1", "field2"), _
      Array(1, "string1", Date)
   rst.AddNew Array("dbkey", "field1", "field2"), _
      Array(2, "string2", #1/6/1992#)

   ' Look at the values -
   ' a value of 1 for status column = newly record
   rst.MoveFirst
   Debug.Print "Status", "dbkey", "field1", "field2"
   Do Until rst.EOF
      Debug.Print rst.Status, rst!dbkey, rst!field1, rst!field2
      rst.MoveNext
   Loop

   ' Commit the rows without ActiveConnection
   ' set resets the status bits
   rst.UpdateBatch adAffectAll

   ' Change the first of the two rows
   rst.MoveFirst
   rst!field1 = "changed"

   ' Now look at the status, first row shows 2 (modified row),
   ' second shows 8 (no modifications)
   ' Also note that the OriginalValue property shows the value
   ' before the modification
   rst.MoveFirst
   Do Until rst.EOF
      Debug.Print
      Debug.Print rst.Status, rst!dbkey, rst!field1, rst!field2
      Debug.Print , rst!dbkey.OriginalValue, _
         rst!field1.OriginalValue, rst!field2.OriginalValue
      rst.MoveNext
   Loop

End Sub

Another feature of a creatable recordset is that pending operations can be committed to the recordset. Any time UpdateBatch is called on a client cursor that has no ActiveConnection set, the changes in the affected row (controlled by the AffectedRows parameter) will be committed to the buffer and the Status flags will be reset. The same applies to CancelBatch, except the changes in the buffer will be reverted and the flag will be reset.

Microsoft Data Links

Microsoft Data Links provides a graphical user interface that enables the user to create, edit, and organize connections to a data source. A Data Link file for c:\nwind.mdb can be created as follows:

Windows 95 / Windows 98 / Windows NT 4.0

  1. In Windows Explorer, select the folder in which you want to create the new data link. For example, select the C:\ folder to create the data link file in the root directory of the C drive.
  2. Choose New from the File menu of Windows Explorer.
  3. Choose Microsoft Data Link.
  4. Rename the file nwind.udl.
  5. Double-click on the new file to open the Data Link Properties window.
  6. Select the Provider tab.
  7. Select "Microsoft Jet 4.0 OLE DB Provider" from the list.
  8. Select the Connection tab.
  9. Enter the path to the Northwind database (for example, c:\nwind.mdb) in the first text box.

Windows 2000

  1. In Windows Explorer, select the folder in which you want to create the new data link. For example, select the C:\ folder to create the data link file in the root directory of the C drive.
  2. Choose Folder Options from the Tools menu of Windows Explorer.
  3. Select the View tab.
  4. In the Advanced Settings list, clear the checkbox titled "Hide file extensions for known file types"
  5. Click OK.
  6. Choose New from the File menu of Windows Explorer.
  7. Choose Text Document.
  8. Rename the file nwind.udl. You may be asked to confirm the change to the file's extension.
  9. Double-click on the new file to open the Data Link Properties window.
  10. Select the Provider tab.
  11. Select "Microsoft Jet 4.0 OLE DB Provider" from the list.
  12. Select the Connection tab.
  13. Enter the path to the Northwind database (for example, c:\nwind.mdb) in the first text box.

The following code shows how to use the data link to open the connection rather than providing the connection information directly.

Sub ADOUseExistingDataLink()

   ' Opens an ADO Connection using a Data Links file (UDL)

   Dim cnn As New ADODB.Connection
   Dim rs As New ADODB.Recordset

   cnn.Open "File Name=.\NorthWind.udl;"

   rs.Open "Customers", cnn, adOpenKeyset, adLockReadOnly

   rs.MoveLast
   Debug.Print rs.RecordCount

   rs.Close
   cnn.Close

End Sub

It is also possible to use Microsoft Data Links to prompt the user for connection information. The following code demonstrates how to launch the Microsoft Data Links UI from code. In order to run this code, you'll need to add a reference to Microsoft OLE DB Service Component 1.0 Type Library in your project.

Private Sub Command1_Click()

   Dim cnn     As New ADODB.Connection
   Dim dl      As New DataLinks

   On Error Resume Next

   dl.hWnd = Me.hWnd
   If dl.PromptEdit(cnn) Then
      cnn.Open
   End If

   cnn.Close

End Sub

The previous code example could be modified to first specify a default value for the provider and data source.

Private Sub Command2_Click()

   Dim cnn     As New ADODB.Connection
   Dim dl      As New DataLinks

   On Error Resume Next

   cnn.Provider = "Microsoft.Jet.OLEDB.4.0;"
   cnn.Properties("Data Source") = ".\NorthWind.mdb"
   dl.hWnd = Me.hWnd
   If dl.PromptEdit(cnn) Then
      cnn.Open
   End If

   cnn.Close

End Sub

User Roster

Many database maintenance activities require that the administrator have exclusive access to the database. The database cannot be opened exclusively if other people already have the database open. With DAO, the administrator had no way of determining who was logged in to the database, making it difficult to determine who was blocking the administrator's attempt to open the database exclusively.

ADO and the Microsoft Jet Provider expose a schema rowset that contains information about who currently has the database open. This is a provider specific schema rowset named DBSCHEMA_JETOLEDB_USERROSTER. The following code demonstrates how to open this schema rowset using ADO.

Sub ADOUserRoster()

   Dim cnn As New ADODB.Connection
   Dim rst As ADODB.Recordset

   ' Open the connection
   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Open the user roster schema rowset
   Set rst = cnn.OpenSchema(adSchemaProviderSpecific, , _
      JET_SCHEMA_USERROSTER)

   ' Print the results to the debug window
   Debug.Print rst.GetString

   cnn.Close

End Sub

The first parameter, QueryType, to the ADO OpenSchema method takes an enumeration value. Values are defined for the schema rowsets defined in the OLE DB specification. To use a provider-specific schema rowset such as DBSCHEMA_JETOLEDB_USERROSTER, you must specify adProviderSpecific and then provide the GUID for the schema rowset as the last parameter. In this example, the constant JET_SCHEMA_USERROSTER is used in place of the GUID. This constant is contained in the JetOLEDBConstants.txt file included in Appendix C.

The following table describes the information contained in each column of the schema rowset.

Column | Description
COMPUTER_NAME | The name of the workstation as specified using the network icon in the control panel.
LOGIN_NAME | The name of the user used to log into the database if the database has been secured. Otherwise the default value will be Admin.
CONNECTED | True if there is a corresponding user lock in the LDB file.
SUSPECTED_STATE | True if the user has left the database in a suspect state; otherwise Null.

Enhanced Auto-Increment (Counter) Columns

Microsoft Jet 4.0 includes enhanced support for auto-increment columns that allows you to specify an initial value for the column, also known as the seed value, as well as a value by which to increment the column.

The following code demonstrates how to create a new auto-increment column with an initial value of 10 and an increment value of 100. It assumes the Contacts table already exists. To create this table, run the ADOCreateTable example code in the section, "Creating and Modifying Tables" earlier in this document.

Sub ADOCreateEnhancedAutoIncrColumn()

   Dim cat As New ADOX.Catalog
   Dim col As New ADOX.Column

   ' Open the catalog
   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
      "Data Source=.\NorthWind.mdb;"

   ' Create the new auto increment column
   With col
      .Name = "ContactId"
      .Type = adInteger
      Set .ParentCatalog = cat
      .Properties("AutoIncrement") = True
      .Properties("Seed") = CLng(10)
      .Properties("Increment") = CLng(100)
   End With

   ' Append the column to the table
   cat.Tables("Contacts").Columns.Append col

   Set cat = Nothing

End Sub

In addition to specifying seed and increment values when the column is created, they can be modified for existing auto-increment columns. Use caution when modifying these values for existing columns as it is possible to create conflicts with existing values. For example, if the table already contains values 1 through 10 in the column, it is possible to set the seed value to 5.

Replication

Replica Visibility

JRO introduces a new property of a replica that is used to indicate the visibility of a replica. The visibility determines which replicas that replica can synchronize with. A replica's visibility may be Global, Local, or Anonymous. The replica's visibility is set when the replica is first created. Once the replica is created the visibility cannot be changed.

A global replica can synchronize with any other replica in the set. Changes at a global replica are fully tracked. From a global replica, you can create replicas that are global, local, or anonymous. Replicas created from a global replica are global by default.

A local replica can synchronize only with its parent, a global replica, and will not be permitted to synchronize with other replicas in the replica set. The parent will proxy any replication conflicts and errors for the local replica. Other replicas will not be aware of the local replica. The parent replica can schedule a synchronization with a local replica. All replicas created from a local replica will also be local and inherit the same parent replica.

An anonymous replica can synchronize with its parent, a global replica. These are replicas who, say, subscribe by way of the Internet, who do not have any particular identity, but instead proxy their identity for updates to the publishing replica. A global replica will not be able to schedule synchronizations to an anonymous replica. Anonymous replicas provide a way of getting around the "limit on number of replicas" problem. In addition, it helps to keep out unnecessary topology information about replicas that participate only occasionally. All replicas created from an anonymous replica will also be anonymous and inherit the same parent replica.

The following code demonstrates how to create a new Anonymous replica:

Function JROMakeAnonReplica(strReplicableDB As String, _
   strNewReplica As String) As Integer

   Dim repMaster As New JRO.Replica

   repMaster.ActiveConnection = strReplicableDB

   repMaster.CreateReplica strNewReplica, "Replica of " & _
      strReplicableDB, , jrRepVisibilityAnon

   Set repMaster = Nothing

End Function

Replica Priority

JRO introduces a new property of a replica that is used to indicate the relative importance of a replica during synchronization. If conflicts are encountered during synchronization the replica with the highest priority wins.

The following code demonstrates how to set the priority when creating a new replica:

Function JROMakeAdditionalReplica2(strReplicableDB As String, _
   strNewReplica As String, intPriority As Integer) As Integer

   Dim repMaster As New JRO.Replica

   repMaster.ActiveConnection = strReplicableDB

   repMaster.CreateReplica strNewReplica, "Replica of " & _
      strReplicableDB, , , intPriority

   Set repMaster = Nothing

End Function

Indirect Synchronization

With a direct synchronization, your machine is tied up until the synchronization is complete. On fast Local Area Networks (LANs) this may not be an issue. However, synchronization over a slow Wide Area Network (WAN) may take many minutes or more. Indirect synchronization was designed for this scenario. For an indirect synchronization, the synchronizer leaves the changes in a dropbox and control returns to the application. The synchronizer for the other replica will then pick up the changes and apply them.

The following code demonstrates how to perform an indirect synchronization:

Sub JROTwoWayIndirectSync()

   Dim repMaster As New JRO.Replica

   repMaster.ActiveConnection = ".\NorthWind.mdb"

   ' Sends changes made in each replica to the other.
   repMaster.Synchronize ".\NewNorthWind.mdb", jrSyncTypeImpExp, _
      jrSyncModeIndirect

   Set repMaster = Nothing

End Sub

Synchronizing Changes with a Microsoft SQL Server

JRO supports synchronizing changes between a Microsoft SQL Server and a Microsoft Jet database. Note, the Microsoft Jet database and its synchronizer must already be configured to support the replication to SQL Server.

The following code demonstrates how to perform a Microsoft Jet to SQL synchronization:

Sub JROJetSQLSync()

   Dim repMaster As New JRO.Replica

   repMaster.ActiveConnection = ".\Pubs.mdb"

   ' Sends changes made in each replica to the other.
   repMaster.Synchronize "", jrSyncTypeImpExp, jrSyncModeDirect

   Set repMaster = Nothing

End Sub

Notice the TargetReplica parameter for the Synchronize method is an empty string ("") and the SyncMode is jrSyncModeDirect. Leaving the TargetReplica blank indicates that this is a Microsoft Jet to SQL Server synchronization. All Microsoft Jet to SQL Server synchronizations are direct.

Column Level Conflict Resolution

Column level conflict resolution lets you merge two records and only report a conflict if simultaneous changes have been to the same field. If you frequently have overlapping updates in the same row, setting this option could increase performance.

This option is set when a database is made replicable, it cannot be changed once the process of making the database replicable is complete. Column level conflict resolution is turned on by default.

The following code demonstrates how to turn on column level tracking when making a database replicable:

Sub JROMakeDesignMaster2()

   Dim repMaster As New JRO.Replica

   repMaster.MakeReplicable ".\NorthWind.mdb", True

   Set repMaster = Nothing

End Sub

For an example of how to turn off column level tracking when making a database replicable, see the JRO code example in the section "Making a Database Replicable".


Obsolete Properties and Methods

This following section describes DAO properties and methods that don't map to properties or methods in ADO, ADOX, or JRO. However, that does not imply that the functionality provided by the DAO properties and methods is not available in ADO, ADOX, or JRO.

Below, each property or method not exposed is listed and followed by a description of why it is not exposed and, if applicable, how to get the equivalent functionality using ADO, ADOX, or JRO.

Object | Property/Method | Explanation
DBEngine | DefaultType | DAO 3.5 introduced ODBCDirect as a means to work with ODBC data sources without loading the Microsoft Jet database engine. To use ODBCDirect, you set the DefaultType and/or Type properties to dbUseODBC. As discussed in the "Introduction" section, ADO has a different approach to enabling access to ODBC data sources as well as enabling native access to various data sources such as Microsoft SQL Server. ADO allows users to choose which OLE DB provider they want to use to access the data. So, to work with ODBC data sources without loading the Microsoft Jet database engine, specify MSDASQL rather than Microsoft.Jet.OLEDB.4.0 as the provider name.
DBEngine | DefaultPassword | In DAO, the DefaultPassword property assigns the default password for a new workspace created on the DBEngine object. Such a property doesn't make sense in the context of the ADO connection model.
DBEngine | DefaultUser | In DAO, the DefaultUser property assigns the default user id for a new workspace created on the DBEngine object. Such a property doesn't make sense in the context of the ADO connection model.
DBEngine | RegisterDatabase | The ODBC API provides this functionality. Users should call the ODBC API directly.
DBEngine | RepairDatabase | The functionality found in RepairDatabase has been incorporated into CompactDatabase in Microsoft Jet 4.0. Compacting a database will also repair it.
Workspace | Name | The Name property identifies a Workspace in the Workspaces collection. Since there is no concept of a collection of connections in ADO, this property doesn’t apply.
Workspace | Type | See comments for DBEngine DefaultType property.
Database | V1xNullBehavior | Support for version 1.0 Null behavior has been dropped. This functionality is obsolete.
Recordset | CacheStart | Using ADO and the Microsoft Jet provider, it is not necessary to explicitly set the CacheStart property. The Microsoft Jet provider will start caching from the current record.
Recordset | Edit | The process of updating records has been simplified with ADO such that Edit is not needed. With DAO, you had to call the Edit method to put the Recordset into edit mode before modifying a value otherwise an error would occur. With ADO, modifying a value automatically puts the Recordset in edit mode.
Recordset | FillCache | As with the CacheStart property, it is not necessary with ADO and the Microsoft Jet provider to explicitly set the FillCache property. The Microsoft Jet provider will start caching from the current record.
Recordset | LastModified | After modifying a record (or creating a new one) and calling the Update method to save the changes, DAO users had to set the Bookmark property to the LastModified property to ensure that the current record was the record they had just modified. With ADO, this is not necessary as ADO automatically ensures that the current record stays the same after a call to Update.
Recordset | Name | ADO does not have a recordset collection so there is no need for a name apart from the name the user has given the Recordset when it was declared.
Recordset | Restartable | All ADO Recordset objects are restartable so a property indicating whether or not it can be resarted is not necessary.
QueryDef | ReturnsRecords | With DAO QueryDef objects it was necessary to know whether the query returned records in order to execute the query. If the query returned records, you had to use the OpenRecordset method to execute the query. If it did not return records, you had to use the Execute method. With ADO you no longer need to know whether or not the query returns records in order to execute it because the Execute method is used in either case. If the query returns records, the Execute method returns a Recordset object otherwise it returns Nothing.
Container | Name | The Container object's functionality can be found as part of the User and Group objects.
Document | Name | The Document object's functionality can be found as properties on the objects themselves or as part of the User and Group objects.
User | Password | The user's password is part of the connection string.
User | PID | PIDs are only required to rebuild a corrupted or destroyed workgroup information file. Keep a current backup of workgroup information files to avoid this, or use SQL DDL commands to specify the PID for a new user.
Group | PID | PIDs are only required to rebuild a corrupted or destroyed workgroup information file. Keep a current backup of workgroup information files to avoid this, or use SQL DDL commands to specify the PID for a new group.


Conclusion

In the past, DAO made working with tabular databases easier than calling DLL functions by wrapping up those functions with objects. This made data access relatively simple for most developers. ADO uses this strength of DAO and expands on it. ADO uses OLE DB to work with data sources, so you will be able to reach many more data sources than with DAO. Many of these data sources won't be stored as traditional, tabular databases. In those cases, using DAO won't be possible. ADO is similar enough to DAO that you won't need to relearn an entire object model to handle these new data sources. Once you've learned ADO, you're ready for the future of data access.


Appendix A: DAO to ADO Quick Reference

The table provided below is intended to be a quick reference for determining how to map DAO properties and methods to ADO, ADOX, and JRO properties and methods. However, it is not intended to imply a direct, one-to-one mapping between the properties and methods listed. There may be subtle, or not so subtle, differences between the mapped properties and methods. For more detailed information on the ADO, ADOX, and JRO properties and methods see the documentation for the object model. Use the information provided earlier in this document to map the code for common tasks that are performed using DAO to ADO, ADOX, and JRO code.

DAO Object | Property/Method | ADO/ADOX/JROModel | Object | Property/Method
DBEngine | DefaultType1 | N/A | N/A | N/A
DBEngine | DefaultPassword1 | N/A | N/A | N/A
DBEngine | DefaultUser1 | N/A | N/A | N/A
DBEngine | IniPath | ADO | Connection | Jet OLEDB:Registry Path2
DBEngine | LoginTimeout | ADO | Connection | ConnectionTimeout
DBEngine | SystemDB | ADO | Connection | Jet OLEDB:System Database2
DBEngine | Version | ADO | Connection | Version
DBEngine | BeginTrans | ADO | Connection | BeginTrans
DBEngine | CommitTrans | ADO | Connection | CommitTrans
DBEngine | Rollback | ADO | Connection | RollbackTrans
DBEngine | CompactDatabase | JRO | JetEngine | CompactDatabase
DBEngine | CreateDatabase | ADOX | Catalog | Create
DBEngine | CreateWorkspace | ADO | Connection | Open
DBEngine | Idle | JRO | JetEngine | RefreshCache
DBEngine | OpenDatabase | ADO | Connection | Open
DBEngine | RegisterDatabase1 | N/A | N/A | N/A
DBEngine | RepairDatabase1 | N/A | N/A | N/A
DBEngine | SetOption | ADO | Connection | Properties3
Workspace | IsolateODBCTrans | ADO | Connection | Isolation Levels2
Workspace | LoginTimeout | ADO | Connection | ConnectionTimeout
Workspace | Name1 | N/A | N/A | N/A
Workspace | Type1 | N/A | N/A | N/A
Workspace | UserName | ADO | Connection | User Id2
Workspace | BeginTrans | ADO | Connection | BeginTrans
Workspace | CommitTrans | ADO | Connection | CommitTrans
Workspace | Rollback | ADO | Connection | RollbackTrans
Workspace | Close | ADO | Connection | Close
Workspace | CreateDatabase | ADOX | Catalog | Create
Workspace | CreateGroup | ADOX | Groups | Append
Workspace | CreateUser | ADOX | Users | Append
Workspace | OpenDatabase | ADO | Connection | Open
Database | CollatingOrder | ADO | Connection | Locale Identifier2
Database | Connect | ADO | Connection | ConnectionString
Database | Name | ADO | Connection | Data Source2
Database | QueryTimeout | ADO | Connection | CommandTimeout
Database | Replicable | JRO | Replica | MakeReplicable
Database | ReplicaId | JRO | Replica | ReplicaId
Database | ReplicationConflictFunction | JRO | Replica | ConflictFunction
Database | RecordsAffected | ADO | Connection | Execute(RecordsAffected)
Database | Transactions | ADO | Connection | Transaction DDL2
Database | Updatable | ADO | Connection | Mode
Database | V1xNullBehavior | N/A | N/A | N/A
Database | Version | ADO | Connection | DBMS Version2
Database | Close | ADO | Connection | Close
Database | CreateProperty | N/A | N/A | Not supported in this release
Database | CreateQueryDef | ADOX | Command | Dim New4
Database | CreateRelation | ADOX | Key | Dim New4
Database | CreateTableDef | ADOX | Table | Dim New4
Database | Execute | ADO | Connection | Execute
Database | MakeReplica | JRO | Replica | CreateReplica
Database | NewPassword | ADOX | Catalog | Modify
Database | OpenRecordset | ADO | Recordset | Open
Database | PopulatePartial | JRO | Replica | PopulatePartial
Database | Synchronize | JRO | Replica | Synchronize
Recordset | AbsolutePosition | ADO | Recordset | AbsolutePosition
Recordset | BOF | ADO | Recordset | BOF
Recordset | EOF | ADO | Recordset | EOF
Recordset | Bookmark | ADO | Recordset | Bookmark
Recordset | Bookmarkable | ADO | Recordset | Supports
Recordset | CacheSize | ADO | Recordset | Jet OLEDB:Fat Cursor Cache Size2
Recordset | CacheStart1 | N/A | N/A | N/A
Recordset | DateCreated | ADOX | Table | DateCreated
Recordset | LastUpdated | ADOX | Table | DateModified
Recordset | EditMode | ADO | Recordset | EditMode
Recordset | Filter | ADO | Recordset | Filter
Recordset | Index | ADO | Recordset | Index
Recordset | LastModified1 | N/A | N/A | N/A
Recordset | LockEdits | ADO | Recordset | LockType
Recordset | Name1 | N/A | N/A | N/A
Recordset | NoMatch | ADO | Recordset | Find
Recordset | PercentPosition | N/A | N/A | Not supported in this release.
Recordset | RecordCount | ADO | Recordset | RecordCount
Recordset | RecordStatus | ADO | Recordset | EditMode
Recordset | Restartable1 | N/A | N/A | N/A
Recordset | Sort | ADO | Recordset | Sort
Recordset | Transactions1 | N/A | N/A | N/A
Recordset | Type | ADO | Recordset | CursorType
Recordset | Updatable | ADO | Recordset | Recordset.Supports(adUpdate)
Recordset | ValidationRule | ADOX | Table | ValidationRule
Recordset | ValidationText | ADOX | Table | ValidationText
Recordset | AddNew | ADO | Recordset | AddNew
Recordset | CancelUpdate | ADO | Recordset | CancelUpdate
Recordset | Clone | ADO | Recordset | Clone
Recordset | Close | ADO | Recordset | Close
Recordset | CopyQueryDef | ADO | Recordset | Source
Recordset | Delete | ADO | Recordset | Delete
Recordset | Edit1 | N/A | N/A | N/A
Recordset | FillCache1 | N/A | N/A | N/A
Recordset | FindFirst | ADO | Recordset | Find
Recordset | FindLast | ADO | Recordset | Find
Recordset | FindNext | ADO | Recordset | Find
Recordset | FindPrevious | ADO | Recordset | Find
Recordset | GetRows | ADO | Recordset | GetRows
Recordset | Move | ADO | Recordset | Move
Recordset | MoveFirst | ADO | Recordset | MoveFirst
Recordset | MoveLast | ADO | Recordset | MoveLast
Recordset | MoveNext | ADO | Recordset | MoveNext
Recordset | MovePrevious | ADO | Recordset | MovePrevious
Recordset | OpenRecordset | ADO | Recordset | Open
Recordset | Requery | ADO | Recordset | Requery
Recordset | Seek | ADO | Recordset | Seek
Recordset | Update | ADO | Recordset | Update
QueryDef | CacheSize | ADO | Command | Jet OLEDB:Fat Cursor Cache Size2
QueryDef | Connect | ADO | Command | Jet OLEDB:Link datasource2
QueryDef | DateCreated | ADOX | Procedure | DateCreated
QueryDef | LastUpdated | ADOX | Procedure | DateModified
QueryDef | KeepLocal | JRO | Replica | Get/SetObjectReplicability
QueryDef | LogMessages | N/A | N/A | Not supported in this release.
QueryDef | MaxRecords | ADO | Command | MaxRecords
QueryDef | Name | ADOX | Procedure | Name
QueryDef | ODBCTimeout | ADO | Command | Jet OLEDB:ODBC Command Timeout2
QueryDef | RecordsAffected | ADO | Command | Execute(RecordsAffected)
QueryDef | Replicable | JRO | Replica | Get/SetObjectReplicability
QueryDef | ReturnsRecords1 | N/A | N/A | N/A
QueryDef | SQL | ADO | Command | CommandText
QueryDef | Type | N/A | N/A | Not supported in this release.
QueryDef | Updatable | N/A | N/A | N/A
QueryDef | Close | ADO/X | Command / Procedure | Set to Nothing
QueryDef | CreateProperty | N/A | N/A | Not supported in this release
QueryDef | Execute | ADO | Command | Command.Execute
QueryDef | OpenRecordset | ADO | Recordset | Open
TableDef | Attributes | ADOX | Table | Properties5
TableDef | ConflictTable | JRO | Replica | ConflictTables
TableDef | Connect | ADOX | Table | Jet OLEDB:Link Datasource2
TableDef | DateCreated | ADOX | Table | DateCreated
TableDef | LastUpdated | ADOX | Table | DateModified
TableDef | KeepLocal | JRO | Replica | Get/SetObjectReplicability
TableDef | Name | ADOX | Table | Name
TableDef | RecordCount | ADO | Connection | OpenSchema6
TableDef | Replicable | JRO | Replica | Get/SetObjectReplicability
TableDef | ReplicaFilter | JRO | Filter | FilterCriteria
TableDef | SourceTableName | ADOX | Table | Jet OLEDB:Remote Table Name2
TableDef | Updatable | N/A | N/A | N/A
TableDef | ValidationRule | ADOX | Table | Jet OLEDB:Table Validation Rule2
TableDef | ValidationText | ADOX | Table | Jet OLEDB:Table Validation Text2
TableDef | CreateField | ADOX | Columns | Append
TableDef | CreateIndex | ADOX | Indexes | Append
TableDef | CreateProperty | N/A | N/A | Not supported in this release.
TableDef | OpenRecordset | ADO | Recordset | Open
TableDef | RefreshLink | ADOX | Table | Jet OLEDB:Create Link2
Field | AllowZeroLength | ADOX | Column | Jet OLEDB:Allow Zero Length2
Field | Attributes | ADOX | Column | Properties5
Field | CollatingOrder | ADO/X | Field/Column | Collation Name2
Field | DataUpdatable | ADO | Field | Attributes
Field | DefaultValue | ADOX | Column | DefaultValue
Field | FieldSize | ADO | Field | ActualSize
Field | ForeignName | ADO | Column | RelatedColumn
Field | Name | ADO/X | Field/Column | Name
Field | OrdinalPosition | N/A | N/A | Not supported in this release.
Field | Required | ADO/X | Field/Column | Attributes
Field | Size | ADO/X | Field/Column | DefinedSize
Field | SourceField | N/A | N/A | Not supported in this release.
Field | SourceTable | N/A | N/A | Not supported in this release.
Field | Type | ADO/X | Field/Column | Type
Field | ValidateOnSet | ADOX | Column | Jet OLEDB:Validate On Set2
Field | ValidationRule | ADOX | Column | Jet OLEDB:Column Validation Rule2
Field | ValidationText | ADOX | Column | Jet OLEDB:Column Validation Text2
Field | Value | ADO | Field | Value
Index | Clustered | ADOX | Index | Clustered
Index | DistinctCount | ADO | Connection | OpenSchema6
Index | Foreign | ADOX | Key | Type
Index | IgnoreNulls | ADOX | Index | IndexNulls
Index | Name | ADOX | Index | Name
Index | Primary | ADOX | Index | PrimaryKey
Index | Required | ADOX | Index | Index.IndexNulls
Index | Unique | ADOX | Index | Unique
Index | CreateField | ADOX | Column | Dim New3
Index | CreateProperty | N/A | N/A | Not supported in this release
Relation | Attributes | ADOX | Key | Properties5
Relation | ForeignTable | ADOX | Key | RelatedTable
Relation | Name | ADOX | Key | Name
Relation | PartialReplica | JRO | Filter | FilterCriteria
Relation | Table | ADOX | Key | Parent Table Object7
Relation | CreateField | ADOX | Column | Dim New3
User | Name | ADOX | User | Name
User | Password | N/A | N/A | N/A
User | PID | N/A | N/A | N/A
User | CreateGroup | ADOX | Groups | Append
User | NewPassword | ADOX | User | ChangePassword
Group | Name | ADOX | Group | Name
Group | PID | N/A | N/A | N/A
Group | CreateUser | ADOX | Users | Append
Container | AllPermissions | ADOX | User/Group | GetPermissions8
Container | Inherit | ADOX | User/Group | Get/SetPermissions
Container | Name1 | N/A | N/A | N/A
Container | Owner | ADOX | Catalog | Get/SetObjectOwner
Container | Permissions | ADOX | User/Group | Get/SetPermissions
Container | UserName | ADOX | User/Group | Get/SetPermissions
Document | AllPermissions | ADOX | User | GetPermissions8
Document | Container1 | N/A | N/A | N/A
Document | DateCreated | ADOX | Applicable Object | DateCreated
Document | LastUpdated | ADOX | Applicable Object | DateModified
Document | KeepLocal | JRO | Replica | Get/SetObjectReplicability
Document | Name1 | N/A | N/A | N/A
Document | Owner | ADOX | Catalog | Get/SetObjectOwner
Document | Permissions | ADOX | User/Group | Get/SetPermissions
Document | Replicable | JRO | Replica | Get/SetObjectReplicability
Document | UserName | ADOX | User/Group | Get/SetPermissions

1 This property or method does not map to ADO, ADOX, or JRO. See the section "Obsolete Properties and Methods" earlier in this document.

2 This property is part of the object's Properties collection.

3 See the section "Setting Microsoft Jet Options" for more information on mapping the SetOption method to the Connection properties.

4 The object is creatable. Use the Visual Basic for Applications Dim New syntax to create a new object.

5 The DAO Attributes property is a bitmask of a number of constants that map to several properties in the ADOX model. For a detailed mapping of the DAO constants to ADOX properties, see the sections "Creating and Modifying Tables" and "Enforcing Referential Integrity."

6 The record count for a table can be retrieved via the Cardinality column in the TABLES_INFO schema rowset and the distinct count for an index can be retrieved via the Cardinality column in the INDEXES schema rowset.

7 The primary table in a relationship is represented in ADOX by the Table object that contains a primary Key object in its Keys collection. Primary keys are specified by a Type property value of adKeyPrimary.

8 Unlike the AllPermissions property in DAO, the GetPermissions property in ADOX does not add in the permissions that the user inherits from the groups to which that user belongs. This information must be extracted from the Group objects.


Appendix B: Microsoft Jet 4.0 OLE DB Properties Reference

The Properties collections in ADO contain a dynamic set of properties returned by the OLE DB provider being used. The tables below contain the list of properties, both standard OLE DB and provider-specific, that are available in the Properties collections of ADO and ADOX objects when using the Microsoft Jet 4.0 OLE DB Provider.

The Property Name column below is the name of the property used when accessing the property in the collection. For example, the Data Source property below is accessed as follows:

Dim cnn   As New ADODB.Connection
cnn.Properties("Data Source") = "c:\nwind.mdb"

The Type column indicates the ADO data type for the column. For properties on ADO objects (Connection, Recordset), ADO will automatically try to coerce the value specified when setting the property. For example, if the property is type adBStr and you set the value to 5, ADO will coerce the value to "5". ADOX will not automatically attempt to coerce property values. If you attempt to set a property of type adBStr in an ADOX collection to 5, you'll receive a run-time error. When developing in VBA you can indicate the type for the property value either explicitly or implicitly. To explicitly specify the data type, use the VBA built-in functions CStr, CLng, CInt, and CBool when setting properties of type adBStr, adInteger, adSmallInt, and adBoolean respectively. For properties of type adBStr, adSmallInt, and adBoolean you can specify the data type implicitly by using quotes around the string, specifying a number, or using True or False respectively.

The Default column indicates the default value for the property.

The Attributes column is a bitmask that is used to indicate whether the property can be read, set, or is required. If adPropWrite is not listed as an attribute, that property is for information only and cannot be modified when using the Microsoft Jet 4.0 OLE DB provider.

The Description column contains a brief description of the property. In many cases the property is not meaningful for ADO programmers and should only be used when writing directly to the OLE DB provider. When the property is not useful for ADO programmers the column will contain "N/A".

ADO Connection Properties

Property Name | Type | Default | Attributes | Description
Cache Authentication | adBoolean | True | adPropRead adPropRequired | Indicates whether the provider is allowed to cache sensitive authentication information such as a password in an internal cache.
Data Source | adBStr | "" | adPropRead adPropWrite adPropRequired | The name of the database to connect to.
Encrypt Password | adBoolean | False | adPropRead adPropRequired | Indicates whether the password must be sent to the data source in an encrypted form.
Extended Properties | adBStr | "" | adPropRead adPropWrite adPropRequired | A string containing connection information for opening external databases.
Locale Identifier | adInteger | 1033 | adPropRead adPropWrite adPropRequired | The locale ID of preference.
Mask Password | adBoolean | False | adPropRead adPropRequired | Indicates whether the password must be sent to the data source in a masked form.
Mode | adInteger | 16 | adPropRead adPropWrite adPropRequired | A bitmask specifying access permissions. A combination or zero or more of the following: 0: (adShareModeExclusive) Prevents others from opening in read/write mode. 1: (adShareModeReadOnly) Read-only. 2: (adShareModeWrite) Write-only. 3: (adShareModeReadWrite) Read/write 4: (adShareModeDenyRead) Prevents others from opening in read mode. 8: (adShareModeDenyWrite) Prevents other from opening in write mode. 16: (adShareModeDenyNone) Neither read nor write access can be denied to others.
OLE DB Services | adInteger | -6 (All services except pooling and Client Cursor Engine) | adPropRead adPropWrite adPropRequired | A bitmask specifying the OLE DB services to enable. One of the following values: 0: No services -1: All services -4: All except pooling and automatic transaction enlistment -5: All except Client Cursor Engine -8: All except pooling, automatic transaction enlistment, and Client Cursor Engine 3: Pooling and automatic transaction enlistment only, session level aggregation only
Password | adBStr | "" | adPropRead adPropWrite adPropRequired | The password to be used when connecting to the data source. When the value of this property is retrieved, the provider may return a mask or an empty string instead of the actual password.
Persist Encrypted | adBoolean | False | adPropRead adPropRequired | Indicates whether the provider must persist sensitive authentication information in an encrypted form.
Persist Security Info | adBoolean | False | adPropRead adPropWrite adPropRequired | Indicates whether the provider is allowed to persist sensitive authentication information such as a password along with other authentication information.
Prompt | adSmallInt | 2 | adPropRead adPropWrite adPropRequired | Whether to prompt the user during initialization. One of the following values: 1: (adPromptAlways) Always prompt the user for initialization information. 2: (adPromptComplete) Prompt the user only if more information is needed. 3: (adPromptCompleteRequired) Prompt the user only if more information is needed. Do not allow the user to enter optional information. 4: (adPromptNever) Do not prompt the user.
User Id | adBStr | "" | adPropRead adPropWrite adPropRequired | The user ID to be used when connecting to the data source.
Window Handle | adInteger | 0 | adPropRead adPropWrite adPropRequired | The window handle to be used if the data source needs to prompt for additional information.
Jet OLEDB:Compact Reclaimed Space Amount | adInteger | 0 | adPropRead adPropRequired | Approximate amount of space that would be reclaimed by compacting this database. This value is only updated when the database is opened and is not guaranteed to be exact.
Jet OLEDB:Compact Without Replica Repair | adBoolean | False | adPropRead adPropWrite adPropRequired | Used with the JRO CompactDatabase method. Ignored when used with the ADO Connection object or the ADOX Create method.
Jet OLEDB:Create System Database | adBoolean | False | adPropRead adPropWrite adPropRequired | Used with the ADOX Catalog object's Create method. Ignored when used with the ADO Connection object or JRO CompactDatabase method.
Jet OLEDB:Database Locking Mode | adInteger | 0 | adPropRead adPropWrite adPropRequired | Scheme to be used when locking the database. Note that a database can only be open in one mode at a time. The first user to open the database gets determines the locking mode used while the database is open. 0: (JET_DATABASELOCKMODE_PAGE) Locks are taken at the page level. 1: (JET_DATABASELOCKMODE_ROW) Locks are taken at the row level.
Jet OLEDB:Database Password | adBStr | "" | adPropRead adPropWrite adPropRequired | Password used to open the database. This differs from the user password in that the database password is per file, while a user password is per user.
Jet OLEDB:Don't Copy Locale on Compact | adBoolean | False | adPropRead adPropWrite adPropRequired | Used with the JRO CompactDatabase method. Ignored when used with the ADO Connection object or the ADOX Create method.
Jet OLEDB:Encrypt Database | adBoolean | False | adPropRead adPropWrite adPropRequired | Used with the ADOX Catalog object's Create method and the JRO CompactDatabase method. Ignored when used with the ADO Connection object.
Jet OLEDB:Engine Type | adInteger | 0 | adPropRead adPropWrite adPropRequired | An enumeration defining the storage engine currently in use to access this database/store. See Appendix C: Microsoft Jet 4.0 Provider Defined Property Values for the list of valid values.
Jet OLEDB:Global Bulk Transactions | adInteger | 1 | adPropRead adPropWrite adPropRequired | Determines if SQL bulk operations are transacted. This property determines the default for all operations in the current connection. 1: (JET_BULKTRAN_NOBULK) Use partial updates like Access. 2: (JET_BULKTRAN_BULK) Use no partial behavior. All or nothing.
Jet OLEDB:Global Partial Bulk Ops | adInteger | 2 | adPropRead adPropWrite adPropRequired | This property determines the behavior of Microsoft Jet when SQL DML bulk operations fail. It can be overridden on a per-rowset basis by setting the Jet OLEDB:Partial Bulk Ops property. 0: (JET_BULKPARTIAL_DEFAULT) 1: (JET_BULKPARTIAL_PARTIAL) Allow partial completion of the bulk operation. Could result in inconsistent changes because operations on some rows could succeed and others could fail. 2: (JET_BULKPARTIAL_NOPARTIAL) Fail the bulk operation on a single error.
Jet OLEDB:New Database Password | adBStr | "" | adPropRead adPropWrite adPropRequired | This property is ignored. It is used with the OLE DB IDataSourceAdmin::ModifyDataSource interface which is not currently exposed in ADO.
Jet OLEDB:Registry Path | adBStr | "" | adPropRead adPropWrite adPropRequired | Path to the registry key to use for Microsoft Jet information. This does not include the HKEY_LOCAL_MACHINE tag. This value can be changed to a secondary location to store registry values for a particular application that are not shared with other applications that use Microsoft Jet on the machine. For example, the setting for Access 2000 is: SOFTWARE\Microsoft\Office\9.0\Access\Jet\4.0\Engines
Jet OLEDB:SFP | adBoolean | False | adPropRead adPropWrite adPropRequired | N/A
Jet OLEDB:System database | adBStr | "" | adPropRead adPropWrite adPropRequired | Location of the Microsoft Jet system database to use for authenticating users. This overrides the value set in the registry or the corresponding systemdb registry key used when Jet OLEDB:Registry Path is used. This can include the path to the file.

In addition to the properties in the preceding table, the following properties are available once the Connection has been opened.

Property Name Type Default Attributes Description
Active Sessions adInteger 128 adPropRead

adPropRequired

 The maximum number of sessions that can exist at the same time.
Alter Column Support adInteger 36 adPropRead

adPropRequired

 A bitmask that describes what column properties can be updated when modifying a column on a table. A combination of one or more of the following:

1: Modify the data type of the column.

2: Modify the type library entry for an abstract data type (ADT).

4: Modify the column properties using an array of properties and values to be set.

8: Modify the class ID for COM objects in the column.

16: If the type of the column is adVarChar, modify the maximum length in characters for values in this column. If the type of the column is adBinary, modify the maximum length in bytes for values in this column. For all other types, , this is ignored.

32: Modify the name of the column.

64: Modify the data type of the column.

128: Modify the maximum precision of data values in the column when type is adNumeric.

256: Modify the scale of data values in the column when type is adNumeric.
Asynchable Commit adBoolean False adPropRead

adPropRequired

 Whether transactions can be committed asynchronously.
Asynchable Abort adBoolean False adPropRead

adPropRequired

 Whether transactions can be aborted asynchronously.
Autocommit Isolation Levels adInteger 4096 adPropRead

adPropRequired

 The transaction isolation level while in auto-commit mode.
Catalog Location adInteger 1 adPropRead

adPropRequired

 The position of the catalog name in a qualified table name in a text command. One of the following:

1: The catalog name is at the start of the fully qualified name.

2: The catalog name is at the end of the fully qualified name.
Catalog Term adBStr Database adPropRead

adPropRequired

 The name the data source uses for a catalog; for example, “catalog”, “database”, or “directory”.
Column Definition adInteger 1 adPropRead

adPropRequired

 A bitmask defining the valid clauses for the definition of a column. A combination of zero or more of the following:

1: Columns can be created non-nullable.
Current Catalog adBStr “” adPropRead

adPropRequired

 The name of the current catalog. The consumer can use the CATALOGS schema rowset to enumerate catalogs. Jet databases do not have distinct catalogs. This field will be blank and the CATALOGS schema empty when using a Jet database.
Data Source Name adBStr “” adPropRead

adPropRequired

 The name of the datasource.
Data Source Object Threading Model adInteger 1 adPropRead

adPropRequired

 A bitmask specifying the threading models supported by the data source object. One of the following.

1: Free threaded

2: Apartment threaded

4: Single threaded
DBMS Name adBStr MS Jet adPropRead

adPropRequired

 The name of the product accessed by the provider.
DBMS Version adBStr 04.00.0000 adPropRead

adPropRequired

 The version of the product accessed by the provider. The version is of the form ##.##.#### in which 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.
GROUP BY Support adInteger 4 adPropRead

adPropRequired

 The relationship between the columns in a GROUP BY clause and the non-aggregated columns in the select list. One of the following:

1: Not supported

2: The GROUP BY clause must contain all non-aggregated columns in the select list.

4: The GROUP BY clause must contain all non-aggregated columns in the select list.

8: The columns in the GROUP BY clause and the select list are not related.
Heterogeneous Table Support adInteger 2 adPropRead

adPropRequired

 A bitmask specifying whether the provider can join tables from different catalogs or providers. A combination of one or more of the following:

1: Different catalogs

2: Different providers
Identifier Case Sensitivity adInteger 8 adPropRead

adPropRequired

 Indicates how identifiers treat case. One of the following:

1: Identifiers in SQL are case insensitive and are stored in uppercase in the system catalog.

2: Identifiers in SQL are case insensitive and are stored in lowercase in the system catalog.

4: Identifiers in SQL are case sensitive and are stored in mixed case in the system catalog.

8: Identifiers in SQL are case insensitive and are stored in mixed case in the system catalog.
Isolation Levels adInteger 4096 adPropRead

adPropRequired

 A bitmask specifying the supported transaction isolation levels. A combination of zero or more of the following:

256: (adXactRead Uncommitted) A transaction can see uncommitted changes made by other trandactions. At this level of isolation, dirty reads, nonrepeatable reads, and phantoms are all possible.

4096: (adXactReadCommitted) A transaction can 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.

65536: (adXactRepeatableRead) A transaction is guaranteed not to see any changes made by other transactions in values it has already read. At this level of isolation, dirty reads are not possible, but phantoms are possible.

1048576: (adXactSerializable) All concurrent transactions are guaranteed to 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.
Isolation Retention adInteger 9 adPropRead

adPropRequired

 A bitmask specifying the supported transaction isolation retention levels. A combination of zero or more of the following:

1: The transaction may either preserve or dispose of isolation context across a retaining commit.

2: The transaction preserves its isolation context (that is, it preserves its locks, if that is how isolation is implemented) across a retaining commit.

4: The transaction is explicitly not to preserve isolation across a retaining commit.

8: The transaction may either preserve or dispose of isolation context across a retaining abort.

16: The transaction preserves its isolation context across a retaining abort.

32: The transaction is explicitly not to preserve isolation across a retaining abort.

64: The transaction may preserve or dispose of isolation context across a retaining commit or abort.

132: Isolation is preserved across both a retaining commit or abort.

256: Isolation is explicitly not to be retained across either a retaining commit or abort.

512: Optimistic concurrency control is to be used. For 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.
Maximum Index Size adInteger 255 adPropRead

adPropRequired

 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.
Maximum Row Size adInteger 4049 adPropRead

adPropRequired

 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.
Maximum Row Size Includes BLOB adBoolean False adPropRead

adPropRequired

 Indicates whether the maximum row size returned includes the length of all BLOB data.
Maximum Tables in SELECT adInteger 0 adPropRead

adPropRequired

 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.
Multiple Parameter Sets adBoolean True adPropRead

adPropRequired

 Indicates whether the provider supports multiple parameter sets per execution.
Multiple Results adInteger 0 adPropRead

adPropRequired

 A bitmask specifying whether the provider supports multiple results from the execution of a command. A combination of zero or more of the following:

0: Not supported

1: Supported

2: More than one Recordset created by the same command can exist concurrently.
Multiple Storage Objects adBoolean False adPropRead

adPropRequired

 Indicates whether the provider supports multiple open storage objects at the same time.
Multi-Table Update adBoolean True adPropRead

adPropRequired

 Indicates whether the provider can update Recordset objects derived from multiple tables.
NULL Collation Order adInteger 4 adPropRead

adPropRequired

 Indicates where null values are sorted in a list. One of the following:

1: Null values are sorted at the end of the list, regardless of sort order.

2: Null values are sorted at the high end of the list.

4: Null values are sorted at the low end of the list.

8: Null values are sorted at the start of the list, regardless of the sort order.
NULL Concatenation Behavior adInteger 2 adPropRead

adPropRequired

 Indicates how the data sources handles the concatenation of null-valued character data type columns with non-null-valued character data type columns. One of the following:

1: The result is null valued.

2: The result is the concatenation of the non-null-valued column or columns.
OLE DB Version adBStr 02.10 adPropRead

adPropRequired

 The version of OLE DB supported by the provider.
OLE Object Support adInteger 1 adPropRead

adPropRequired

 N/A
Open Rowset Support adInteger 2 adPropRead

adPropRequired

 Indicates support for opening objects.

0: The provider supports opening tables directly using adCmdTableDirect.

1: The provider supports opening an index directly.

2: The provider supports specifying both a table and an index. (Set the Recordset object's Index property and then use adCmdTableDirect.)

4: The provider supports opening a Recordset over a stored procedure by specifying the name of the stored procedure.
ORDER BY Columns in Select List adBoolean False adPropRead

adPropRequired

 Whether columns in an ORDER BY clause must be in the select list.
Output Parameter Availability adInteger 1 adPropRead

adPropRequired

 The time at which output parameter values become available. One of the following:

1: Not supported.

2: Immediately after command execution.

4: If a command returns a single result that is a Recordset, output parameter data is available at the time the Recordset is completely released. If a command returns multiple Recordset objects, output parameter data is available when all of the Recordset objects have been retrieved and released.
Pass By Ref Accessors adBoolean False adPropRead

adPropRequired

 N/A
Persistent ID Type adInteger 4 adPropRead

adPropRequired

 N/A
Prepare Abort Behavior adInteger 1 adPropRead

adPropRequired

 Indicates how aborting a transaction affects prepared commands. One of the following:

1: Aborting a transaction deletes prepared commands. The application must reprepare the commands before executing them.

2: Aborting a transaction preserves prepared commands. The application can reexecute commands without repreparing them.
Prepare Commit Behavior adInteger 2 adPropRead

adPropRequired

 Indicates how committing a transaction affects prepared commands. One of the following:

1: Committing a transaction deletes prepared commands. The application must reprepare the commands before executing them.

2: Committing a transaction preserves prepared commands. The application can reexecute commands without repreparing them.
Procedure Term adBStr STORED QUERY adPropRead

adPropRequired

 A character string with the data source vendor's name for a procedure.
Provider Friendly Name adBStr Microsoft OLE DB Provider for Jet adPropRead

adPropRequired

 The friendly name of the provider.
Provider Name adBStr MSJETOLEDB40.DLL adPropRead

adPropRequired

 The filename of the provider.
Provider Version adBStr 04.00.2521 adPropRead

adPropRequired

 The version of the provider.
Read-Only Data Source adBoolean False adPropRead

adPropRequired

 Whether the data source is read-only or updatable.
Rowset Conversions on Command adBoolean True adPropRead

adPropRequired

 N/A
Schema Term adBStr Schema adPropRead

adPropRequired

 The name the data source uses for a schema.
Schema Usage adInteger 0 adPropRead

adPropRequired

 A bitmask specifying how schema names can be used in text commands. A combination of zero or more of the following:

0: Schema names are not supported.

1: Schema names are supported in all data manipulation language statements.

2: Schema names are supported in all table definition statements.

4: Schema names are supported in all index definition statements.

8: Schema names are supported in all privilege definition statements.
SQL Support adInteger 512 adPropRead

adPropRequired

 A bitmask specifying the level of support for SQL. A combination of zero or more of the following:

0: SQL is not supported.

1: Minimum support as defined by the ODBC version 2.5 SQL conformance level.

2: Core support as defined by the ODBC version 2.5 SQL conformance level.

4: Extended support as defined by the ODBC version 2.5 SQL conformance level.

8: The provder supports the ANSI89 Integrity Enhancement Facility.

16: Entry level support 256:The provider supports the ODBC escape clause syntax.

32: FIPS Transitional support as defined by ANSI SQL 92.

64: Intermediate level support as defined by ANSI SQL 92.

132: Full support as defined by ANSI SQL 92.

256:The provider supports the ODBC escape clause syntax.

512: The provider supports the SQL dialect and parses the command text according to SQL rules, but does not support either the minimum ODBC level nor the ANSI SQL 92 Entry level. This level is not accumulative; providers that support at least the minimal ODBC Level or ANSI SQL 92 Entry Level do not set this bit.
Structured Storage adInteger adPropRead

adPropRequired

 N/A
Subquery Support adInteger 63 adPropRead

adPropRequired

 A bitmask specifying the predicates in text commands that support subqueries. A combination of zero or more of the following:

1: Correlated subqueries

2: Comparison

4: Exists

8: In

16: Quantified

32: Table
Table Term adBStr Table adPropRead

adPropRequired

 The name the data source uses for a table.
Transaction DDL adInteger 16 adPropRead

adPropRequired

 Indicates whether data definition language (DDL) statements are supported in transactions. One of the following:

0: Transactions are not supported.

1: Transactions can only contain DML statements. DDL statements within a transaction cause an error to occur.

2: Transactions can only contain DML statements. DDL statements within a transaction cause the transaction to be committed.

4: Transactions can only contain DML statements. DDL statements within a transaction are ignored.

8: Transactions can contain DDL and DML statements in any order.

16: Transactions can contain both DML and table or index modifications, but modifying a table or index within a transaction causes the table or index to be locked until the transaction completes.
User Name adBStr “” adPropRead

adPropRequired

 A character string with the name used in a particular database, which can be different than a login name.
Jet OLEDB:Connection Control adInteger 2 adPropRead

adPropWrite

adPropRequired

 This property can be set at run-time to lock out new users from the database. This is useful for administrators who want to take the database off-line for maintenance.

1: (JET_CONNCONTROL_PASSIVESHUTDOWN) Prevent new users from accessing the database.

2: (JET_CONNCONTROL_NORMAL) Allow new users to access the database.
Jet OLEDB:Exclusive Async Delay adInteger 0 adPropRead

adPropWrite

adPropRequired

 The maximum time Jet can delay asynchronous writes to disk (in milliseconds) when the database is opened in multi-user mode. This property is ignored unless the Jet OLEDB:Flush Transaction Timeout property is 0.
Jet OLEDB:Flush Transaction Timeout adInteger 0 adPropRead

adPropWrite

adPropRequired

 The amount of time of inactivity before the asynchronous write cache is flushed to disk. This setting precludes the values for Shared Async Delay and Exclusive Async Delay.
Jet OLEDB:Implicit Commit Sync adBoolean False adPropRead

adPropWrite

adPropRequired

 Indicates whether changes made in internal/implicit transactions are written in synchronous or asynchronous mode.
Jet OLEDB:Lock Delay adInteger 0 adPropRead

adPropWrite

adPropRequired

 Indicates how long to wait, in milliseconds, before attempting to acquire a lock after the previous attempt has failed.
Jet OLEDB:Lock Retry adInteger 0 adPropRead

adPropWrite

adPropRequired

 The number of times to repeat attempts to access a locked page.
Jet OLEDB:Max Buffer Size adInteger 0 adPropRead

adPropWrite

adPropRequired

 The largest amount of memory, in kilobytes, that Jet can use before it starts flushing changes to disk.
Jet OLEDB:Max Locks Per File adInteger 0 adPropRead

adPropWrite

adPropRequired

 The maximum number of locks that Jet can place on a database. The default for this property is 9500.
Jet OLEDB:ODBC Command Time Out adInteger 0 adPropRead

adPropWrite

adPropRequired

 Number of seconds before remote ODBC queries from Jet time out.
Jet OLEDB:ODBC Parsing adBoolean False adPropRead

adPropWrite

adPropRequired

 Indicates whether Jet should attempt to parse ODBC SQL syntax or use only Jet's native syntax parser.
Jet OLEDB:Page Locks to Table Lock adInteger 0 adPropRead

adPropWrite

adPropRequired

 The number of pages that need to be locked in a transaction before Jet tries to promote the locks to an exclusive table lock. Zero implies that Jet will never promote the lock.
Jet OLEDB:Page Timeout adInteger 0 adPropRead

adPropWrite

adPropRequired

 The amount of time, in milliseconds, that Jet will wait before checking to see if its cache is out of date with the database file.
Jet OLEDB:Recycle Long-Valued Pages adBoolean False adPropRead

adPropWrite

adPropRequired

 Whether Jet should aggressively try to reclaim BLOB pages when they are freed.
Jet OLEDB:Reset ISAM Stats adBoolean True adPropRead

adPropWrite

adPropRequired

 Determines if the ISAM stats schema rowset should reset its performance counters after returning performance information.
Jet OLEDB:Sandbox Mode adBoolean False adPropRead

adPropWrite

adPropRequired

 N/A
Jet OLEDB:Shared Async Delay adInteger 0 adPropRead

adPropWrite

adPropRequired

 The maximum time Jet can delay asynchronous writes to disk (in milliseconds) when the database is opened in multi-user mode. This property is ignored unless the Jet OLEDB:Flush Transaction Timeout property is 0.
Jet OLEDB:Transaction Commit Mode adInteger 0 adPropRead

adPropWrite

adPropRequired

 Determines the mode that the Jet Engine uses when committing transactions to disk. The values for this property are a bitmask of zero or more of the following:

0: (JET_TCM_ASYNCFLUSH) Asynchronously write modified pages in the transaction to disk.

1: (JET_TCM_SYNCFLUSH) Synchronously flush modified pages in the transaction to disk.
Jet OLEDB:User Commit Sync adBoolean True adPropRead

adPropWrite

adPropRequired

 Indicates whether changes made in user/explicit transactions are written in synchronous or asynchronous mode.

ADO Recordset Properties

ADO uses a number of the properties exposed in the Recordset object's Properties collection in order to open a Recordset. For instance, ADO will always set the Bookmarkable property to True if you request an updatable Recordset. As a result, ADO may overwrite existing values for these properties.

In general, most of these properties are specific to the behavior of the underlying OLE DB rowset and are not of significant interest or use to the ADO programmer. Of the properties listed below, the Microsoft Jet Provider–specific properties and the Append-Only Rowset property are of the most use to the ADO/Microsoft Jet programmer.

Property Name Type Default Attributes Description
Access Order
Append-Only Rowset adBoolean False adPropRead

adPropWrite

adPropRequired

 Indicates whether the Recordset will initially exclude existing records. It prevents editing or deleting existing records in the table or query results.
Blocking Storage Objects adBoolean True adPropRead

adPropRequired

 Indicates whether storage objects (adLongVarWChar or adLongBinary fields) may prevent the use of some methods.
Bookmark Type adInteger 1 adPropRead

adPropRequired

 The bookmark type supported by the Recordset. One of the following:

1: 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. The validity of numeric bookmarks is not changed by modifying the rows columns.

2: The bookmark type is key. Key bookmarks are based on the values of one or more of the row's columns. A key bookmark may be left dangling if the key values of the corresponding row are changed.
Bookmarkable adBoolean False adPropRead

adPropWrite

adPropRequired

 Indicates whether the Recordset supports bookmarks.
Bookmarks Ordered adBoolean False adPropRead

adPropRequired

 Indicates whether bookmarks can be compared to determine the relative position of their records in a Recordset.
Cache Deferred Columns adBoolean False adPropRead

adPropRequired

 Indicates whether the provider caches the value of a deferred column when the consumer first gets a value from that column.
Change Inserted Rows adBoolean True adPropRead

adPropRequired

 Indicates whether new rows can be changed or modified.
Column Privileges adBoolean True adPropRead

adPropRequired

 Indicates whether access rights are restricted on a column-by-column basis. If this property is True, the provider will not execute a query that would specify a column for which the user has no read access rights.
Column Set Notification adInteger 3 adPropRead

adPropRequired

 A bitmask specifying whether the notification phase is cancelable. A combination of zero or more of the following:

1: Ok to do

2: About to do

4: Synch after
Column Writable adBoolean True adPropRead

adPropRequired

 Indicates whether a particular column is writable or not. This information can also be obtained through the Field object's Attributes property.
Defer Column adBoolean True adPropRead

adPropRequired

 N/A
Delay Storage Object Updates adBoolean True adPropRead

adPropRequired

 In delayed update mode, storage objects are also used in delayed update mode. Changes to the object are not transmitted to the data source until Update is called.
Fetch Backwards adBoolean False adPropRead

adPropWrite

adPropRequired

 Indicates whether the Recordset can fetch backwards.
Hold Rows adBoolean False adPropRead

adPropWrite

adPropRequired

 Indicates whether the Recordset allows the user to retrieve more records or change the position while holding previously retrieved records with pending changes.
Immobile Rows adBoolean False adPropRead

adPropRequired

 If the Recordset is ordered (table-type with a defined index), inserted and updated rows (when one or more of the columns in the ordering criteria are updated) obey the ordering criteria. If the Recordset is not ordered, then inserted rows are not guaranteed to appear in a determinate position and the position of updated rows is not changed.
IAccessor

IColumnsInfo

IColumnsRowset

IConnectionPointContainer

IConvertType

ILockBytes

IRowset

IRowsetChange

IRowsetCurrentIndex

IRowsetIdentity

IRowsetIndex

IRowsetInfo

IRowsetLocate

IRowsetResynch

IRowsetScroll

IRowsetUpdate

ISequentialStream

IStorage

IStream

ISupportErrorInfo

 adBoolean N/A adPropRead

adPropRequired

 N/A
Literal Bookmarks adBoolean False adPropRead

adPropRequired

 Bookmarks cannot be compared as a sequence of bytes.
Literal Row Identity adBoolean False adPropRead

adPropRequired

 N/A
Lock Mode adInteger 1 adPropRead

adPropWrite

adPropRequired

 The level of locking performed by the recordset. One of the following:

1: 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.

2: 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.

ADO sets this property (among others) based on the LockType of the Recordset.
Maximum Open Rows adInteger 1 adPropRead

adPropRequired

 The maximum number of rows that can be active at the same time. This limit does not reflect resource limitations such as RAM.
Maximum Pending Rows adInteger 1 adPropRead

adPropRequired

 The maximum number of rows that can have pending changes at the same time. This limit does not reflect resource limitations such as RAM.
Maximum Rows adInteger 0 adPropRead

adPropRequired

 The maximum number of rows that can be returned in a Recordset. If there is no limit, this value is zero.
Memory Usage adInteger 0 adPropRead

adPropRequired

 The estimated amount of memory that can be used by a Recordset. If it is 0, the Recordset can use unlimited memory. If it is between 1 and 99 inclusive, the Recordset can use the specified percentage of total available virtual memory (physical and page file). If it is greater than or equal to 100, the Recordset can use up to the specified number of kilobytes in memory.
Notification Granularity adInteger 2 adPropRead

adPropRequired

 Indicates when the consumer is notified for methods that operate on multiple rows. One of the following:

1: The consumer is notified 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 those rows.

2: The consumer is notified 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.

Notification Phases adInteger 27 adPropRead

adPropRequired

 A bitmask specifying the notification phases supported by the provider. These are used internally by ADO to support EventNotifications.
Objects Transacted adBoolean True adPropRead

adPropRequired

 Indicates whether any object created on the specified column is transacted.
Others' Inserts Visible adBoolean False adPropRead

adPropRequired

 Indicates whether the Recordset can see records inserted by others.
Others' Changes Visible adBoolean False adPropRead

adPropWrite

adPropRequired

 Indicates whether the Recordset can see updates and deletes made by others.
Own Changes Visible adBoolean False adPropRead

adPropWrite

adPropRequired

 Indicates whether the Recordset can see its own updates and deletes.
Own Inserts Visible adBoolean False adPropRead

adPropWrite

adPropRequired

 Indicates whether the Recordset can see its own inserts.
Preserve on Abort adBoolean False adPropRead

adPropRequired

 Indicates whether after aborting a transaction, the Recordset remains active. That is, whether it is possible to fetch new rows, update, delete, and insert rows, and so on.
Preserve on Commit adBoolean True adPropRead

adPropRequired

 Indicates whether after committed a transaction, the Recordset remains active. That is, whether it is possible to fetch new rows, update, delete, and insert rows, and so on.
Quick Restart adBoolean True adPropRead

adPropRequired

 N/A
Reentrant Events adBoolean False adPropRead

adPropRequired

 Indicates whether the provider supports reentrancy on callbacks during notifications.
Remove Deleted Rows adBoolean True adPropRead

adPropRequired

 Indicates whether the provider removes records it detects as having been deleted from the Recordset. That is, retrieving a block of records that formerly included a deleted record does not include the deleted record.
Report Multiple Changes adBoolean True adPropRead

adPropRequired

 Indicates whether an update or delete can affect multiple records and the provider can detect that multiple records have been updated or deleted.
Return Pending Inserts adBoolean False adPropRead

adPropRequired

 Indicates whether methods that fetch rows can return pending insert rows.
Row Delete Notification

Row First Change Notification

Row Insert Notification

Row Resynchronization Notification

Row Undo Change Notification

Row Undo Delete Notification

Row Undo Insert Notification

Row Update Notification

Rowset Fetch Position Change Notification

Rowset Release Notification

 adInteger 3 adPropRead

adPropRequired

 A bitmask specifying whether the notification phase is cancelable.
Row Privileges adBoolean False adPropRead

adPropRequired

 Indicates whether access rights are restricted on a row-by-row basis. If the Recordset supports updates, some, but not all of the rows may be updated.
Row Threading Model adInteger 1 adPropRead

adPropRequired

 A bitmask specifying the threading models supported by the Recordset. A combination of one or more of the following:

1: Free Threaded

2: Apartment Threaded

4: SingleThreaded
Scroll Backwards adBoolean False adPropRead

adPropWrite

adPropRequired

 Indicates whether the Recordset can scroll backward.
Server Data on Insert adBoolean True adPropRead

adPropWrite

adPropRequired

 Indicates whether at the time an insert or update is sent to the data source the provider retrieves data from the data source to update the local row cache.
Skip Deleted Bookmarks adBoolean False adPropRead

adPropRequired

 N/A
Strong Row Identity adBoolean False adPropRead

adPropRequired

 N/A
Updatability adInteger 0 adPropRead

adPropWrite

adPropRequired

 A bitmask specifying the supported methods for modifying the Recordset.

1: Modifying an existing record is supported.

2: Deleting records is supported.

4: Inserting records is supported.
Use Bookmarks adBoolean False adPropRead

adPropWrite

adPropRequired

 Indicates whether the Recordset supports bookmarks.
Jet OLEDB:Bulk Transactions adInteger 0 adPropRead

adPropWrite

adPropRequired

 Determines if SQL bulk operations are transacted. This property determines if the current command execution is transacted.

0: (JET_BULKTRAN_DEFAULT) Default

1: (JET_BULKTRAN_NOBULK) Use partial updates like Access

2: (JET_BULKTRAN_BULK) Use no partial behavior. All or nothing.
Jet OLEDB:Enable Fat Cursors adBoolean False adPropRead

adPropWrite

adPropRequired

 Indicates whether Microsoft Jet should cache multiple rows when populating the cursor for remote row sources.
Jet OLEDB:Fat Cursor Cache Size adInteger 0 adPropRead

adPropWrite

adPropRequired

 Number of rows which should be cached when using remote data source row caching. Only used if Jet OLEDB:Enable Fat Cursors is True.
Jet OLEDB:Grbit Value adInteger 0 adPropRead

adPropWrite

adPropRequired

 N/A
Jet OLEDB:Inconsistent adBoolean False adPropRead

adPropWrite

adPropRequired

 Allows inconsistent updates on query results. Equivalent to DAO's dbInconsistent flag.
Jet OLEDB:Locking Granularity adInteger 2 adPropRead

adPropWrite

adPropRequired

 Determines if a table is opened using row-level locking. This property is ignored unless Jet OLEDB:Database Locking Mode is set to 1 (JET_LOCKMODE_ROW).
Jet OLEDB:ODBC Pass-Through Statement adBoolean False adPropRead

adPropWrite

adPropRequired

 Tells Microsoft Jet that SQL text in a Command object should be passed to the backend unaltered.
Jet OLEDB:Partial Bulk Ops adInteger 0 adPropRead

adPropWrite

adPropRequired

 Th behavior of Microsoft Jet when SQL DML bulk operations fail.

0: (JET_BULKPARTIAL_DEFAULT) Default

1: (JET_BULKPARTIAL_PARTIAL) Allow partial completion of the bulk operation. Could result in inconsistent changes since operations on some rows could succeed and others could fail.

2: (JET_BULKPARTIAL_NOPARTIAL) Fail the bulk operation on a single error.
Jet OLEDB:Pass Through Query Bulk-Op adBoolean False adPropRead

adPropWrite

adPropRequired

 Indicates whether the Pass-Through Query is a bulk operation (non-row-returning).
Jet OLEDB:Pass Through Query Connect String adBStr "" adPropRead

adPropWrite

adPropRequired

 Indicates the Microsoft Jet Connect String to be used to connect to the remote data source. This property is ignored unless the Jet OLEDB:ODBC Pass-Through Statement is True.
Jet OLEDB:Stored Query adBoolean False adPropRead

adPropWrite

adPropRequired

 Whether the CommandText of the Command object should be interpreted as a stored query instead of an SQL command.
Jet OLEDB:Use Grbit adInteger 0 adPropRead

adPropWrite

adPropRequired

 N/A
Jet OLEDB:Validate Rules On Set adBoolean False adPropRead

adPropWrite

adPropRequired

 Indicates whether Microsoft Jet Validation Rules are evaluated when columns are set or when changes are being committed to the database.

ADOX Table Properties

Property Name | Type | Default | Attributes | Description
Temporary Table | adBoolean | False | adPropRead adPropRequired | Indicates whether the table is destroyed when the connection is released.
Jet OLEDB:Cache Link Name/Password | adBoolean | False | adPropRead adPropWrite adPropRequired | Indicates whether the User Id and password used to open the external database are saved with the connection information. This property is ignored if Jet OLEDB:Create Link is False.
Jet OLEDB:Create Link | adBoolean | False | adPropRead adPropWrite adPropRequired | Indicates whether the table is a linked table (formerly known as an attached table). A linked table is a table in another database linked to a Microsoft Jet database. Data for linked tables remains in the external database where it can be manipulated by other applications.
Jet OLEDB:Exclusive Link | adBoolean | False | adPropRead adPropWrite adPropRequired | Indicates whether the external database is opened exclusively when the linked table is created or used. The value is True if the external database will be opened exclusively and False if the external database will be opened for multi-user access. This property is ignored if Jet OLEDB:Create Link is False.
Jet OLEDB:Link Datasource | adBStr | "" | adPropRead adPropWrite adPropRequired | The external database to link to. The default value is an empty string (""). This property is ignored if Jet OLEDB:Create Link is False.
Jet OLEDB:Link Provider String | adBStr | "" | adPropRead adPropWrite adPropRequired | Additional connection options used when connecting to the external database. It is similar to the Extended Properties property in the Connection's Properties collection. See the section on External Databases for more information on options that can be specified.
Jet OLEDB:Remote Table Name | adBStr | "" | adPropRead adPropWrite adPropRequired | The name of the table to link to. This may be different than the local name of the table/link as specified in the Table object's Name property. The default value is an empty string (""). This property is ignored if Jet OLEDB:Create Link is False.
Jet OLEDB:Table Hidden In Access | adBoolean | False | adPropRead adPropWrite adPropRequired | Indicates whether the Table will be hidden in the Microsoft Access user interface.
Jet OLEDB:Table Validation Rule | adBStr | "" | adPropRead adPropWrite adPropRequired | An expression used to validate data when a record is changed or added to the table. This property is read-only if Jet OLEDB:Create Link is True.
Jet OLEDB:Table Validation Text | adBStr | "" | adPropRead adPropWrite adPropRequired | The message to be displayed to the user when the validation rule is violated. This property is read-only if Jet OLEDB:Create Link is True.

ADOX Column Properties

Property Name | Type | Default | Attributes | Description
AutoIncrement | adBoolean | False | adPropRead adPropWrite adPropRequired | Indicates whether the values of the column are automatically incremented as a new record is added.
Default | adEmpty1 | Empty | adPropRead adPropWrite adPropRequired | The default value for the column. It can be either text or an expression.
Description | adBStr | "" | adPropRead adPropWrite adPropRequired | A description of the column.
Fixed Length | adBoolean | False | adPropRead adPropWrite adPropRequired | Indicates whether the column is fixed length or variable length.
Increment | adInteger | 1 | adPropRead adPropWrite adPropRequired | The value by which an autoincrement column is incremented.
Nullable | adBoolean | False | adPropRead adPropWrite adPropRequired | Indicates whether the column can contain a null value.
Seed | adInteger | 1 | adPropRead adPropWrite adPropRequired | The value that will be used in an autoincrement column for the next added record.
Jet OLEDB:Allow Zero Length | adBoolean | False | adPropRead adPropWrite adPropRequired | Indicates whether a zero-length string ("") can be inserted into this field. Ignored for data types that are not strings.
Jet OLEDB:AutoGenerate | adBoolean | False | adPropRead adPropWrite adPropRequired | Indicates whether a GUID should be automatically generated for the column. This property is ignored unless the column type is adGUID.
Jet OLEDB:Column Validation Rule | adBStr | "" | adPropRead adPropWrite adPropRequired | Expression used to validate the data in a field when it's changed or added to a table. The expression must be in the form of an SQL WHERE clause without the WHERE reserved word.
Jet OLEDB:Column Validation Text | adBStr | "" | adPropRead adPropWrite adPropRequired | The text that will be displayed if a user tries to enter a value that does not satisfy the validation rule.
Jet OLEDB:Compressed UNICODE Strings | adBoolean | False | adPropRead adPropWrite adPropRequired | Indicates whether Microsoft Jet will compress UNICODE strings on the disk. Ignored if the database is not a Microsoft Jet version 4.0 database.
Jet OLEDB:Hyperlink | adBoolean | False | adPropRead adPropWrite adPropRequired | Indicates whether the data in the column is a hyperlink. This property is ignored unless the column's data type is adLongVarWChar.
Jet OLEDB:IISAM Not Last Column | adBoolean | False | adPropRead adPropWrite adPropRequired | For Installable-ISAMs, this property informs the I-ISAM that there are more columns that are going to be added to the table after this one. If you are using ITableDefinition::AddColumn or ITableDefintion::CreateTable, it is required that you set this property for every column except the last column
Jet OLEDB:One BLOB per Page | adBoolean | False | adPropRead adPropWrite adPropRequired | Indicates whether the data in the column is stored on a single page (True) or can share database pages (False) to conserve space. Ignored unless the column's data type is adLongVarBinary.

1 Treat the property "Default" as though the type were really adVariant.

ADOX Index Properties

Property Name | Type | Default | Attributes | Description
Auto-Update | adBoolean | True | adPropRead adPropRequired | Indicates whether the index is maintained automatically when changes are made to the corresponding base table.
Clustered | adBoolean | False | adPropRead adPropRequired | Indicates whether the index is clustered.
Fill Factor | adInteger | 100 | adPropRead adPropRequired | The storage utilization factor of page nodes during the creation of the index. The value ranges from 1 to 100 representing the percentage of use of an index node.
Initial Size | adInteger | 4196 | adPropRead adPropRequired | The total number of bytes allocated to this structure at creation time.
Null Collation | adInteger | 4 | adPropRead adPropRequired | Indicates that null valuess in the index are collated at the low end of the list.
Null Keys | adInteger | 0 | adPropRead adPropWrite adPropRequired | This property corresponds to the IgnoreNulls property of the Index object. See the ADOX documentation for a description of this property.
Primary Key | adBoolean | 0 | adPropRead adPropWrite adPropRequired | Indicates whether the index represents the primary key on the table.
Sort Bookmarks | adBoolean | False | adPropRead adPropRequired | Indiates whether the index sorts repeated keys by bookmark.
Index Type | adInteger | 1 | adPropRead adPropRequired | The type of the index. One of the following: 1: The index is a B+-tree 2: The index is a hash file using linear or extensible hashing. 3: The index is a content index. 4: The index is some other type of index.
Unique | adBoolean | False | adPropRead adPropWrite adPropRequired | Indicates whether index keys must be unique.
Temporary Index | adBoolean | False | adPropRead adPropRequired | Indicates whether the index is a temporary index. That is, the index is destroyed when the session is released.


Appendix C: Microsoft Jet 4.0 OLE DB Provider Defined Property Values

The Microsoft Jet Provider defines a number of GUIDs and property values that are for provider specific features and properties. Because they are provider specific values, ADO does not expose them in enumeration values or constants.

Use the attached file, JetOLEDBConstants.bas, or copy and paste the following code into a module in your project, to make working with these values easier in a Visual Basic for Applications development environment.

Attribute VB_Name = "JetOLEDBConstants"
Option Explicit

'  Microsoft Jet         OLEDB:Engine Type property values
Global Const JET_ENGINETYPE_UNKNOWN = 0
Global Const JET_ENGINETYPE_JET10 = 1
Global Const JET_ENGINETYPE_JET11 = 2
Global Const JET_ENGINETYPE_JET20 = 3
Global Const JET_ENGINETYPE_JET3X = 4
Global Const JET_ENGINETYPE_JET4X = 5
Global Const JET_ENGINETYPE_DBASE3 = 10
Global Const JET_ENGINETYPE_DBASE4 = 11
Global Const JET_ENGINETYPE_DBASE5 = 12
Global Const JET_ENGINETYPE_EXCEL30 = 20
Global Const JET_ENGINETYPE_EXCEL40 = 21
Global Const JET_ENGINETYPE_EXCEL50 = 22
Global Const JET_ENGINETYPE_EXCEL80 = 23
Global Const JET_ENGINETYPE_EXCEL90 = 24
Global Const JET_ENGINETYPE_EXCHANGE4 = 30
Global Const JET_ENGINETYPE_LOTUSWK1 = 40
Global Const JET_ENGINETYPE_LOTUSWK3 = 41
Global Const JET_ENGINETYPE_LOTUSWK4 = 42
Global Const JET_ENGINETYPE_PARADOX3X = 50
Global Const JET_ENGINETYPE_PARADOX4X = 51
Global Const JET_ENGINETYPE_PARADOX5X = 52
Global Const JET_ENGINETYPE_PARADOX7X = 53
Global Const JET_ENGINETYPE_TEXT1X = 60
Global Const JET_ENGINETYPE_HTML1X = 70

'       Microsoft Jet OLEDB:Global Partial Bulk Ops and
' Microsoft Jet OLEDB:Partial Bulk Ops property values
Global Const JET_BULKPARTIAL_DEFAULT = 0
Global Const JET_BULKPARTIAL_PARTIAL = 1
Global Const JET_BULKPARTIAL_NOPARTIAL = 2

' Jet OLEDB:Global Bulk Transactions  and
' Jet OLEDB:Bulk Transactions property values
Global Const JET_BULKTRAN_DEFAULT = 0 'not valid for Global
Global Const JET_BULKTRAN_NOBULK = 1
Global Const JET_BULKTRAN_BULK = 2

'        Microsoft Jet OLEDB:Database Locking Mode property values
Global Const JET_DATABASELOCKMODE_PAGE = 0
Global Const JET_DATABASELOCKMODE_ROW = 1

'        Microsoft Jet OLEDB:Connection Control property values
Global Const JET_CONNCONTROL_PASSIVESHUTDOWN = 1
Global Const JET_CONNCONTROL_NORMAL = 2

' Microsoft Jet OLEDB:Transaction Commit Mode property values
Global Const JET_TCM_ASYNCFLUSH = 0
Global Const JET_TCM_SYNCFLUSH = 1

' Security GUIDS for Access Objects
Global Const JET_SECURITY_FORMS = _
    "{c49c842e-9dcb-11d1-9f0a-00c04fc2c2e0}"
Global Const JET_SECURITY_REPORTS = _
    "{c49c8430-9dcb-11d1-9f0a-00c04fc2c2e0}"
Global Const JET_SECURITY_MACROS = _
    "{c49c842f-9dcb-11d1-9f0a-00c04fc2c2e0}"
Global Const JET_SECURITY_MODULES = _
    "{c49c8432-9dcb-11d1-9f0a-00c04fc2c2e0}"

' Jet OLE DB Provider Defined Schema Rowsets
Global Const JET_SCHEMA_REPLPARTIALFILTERLIST = _
    "{e2082df0-54ac-11d1-bdbb-00c04fb92675}"
Global Const JET_SCHEMA_REPLCONFLICTTAGBLES = _
    "{e2082df2-54ac-11d1-bdbb-00c04fb92675}"
Global Const JET_SCHEMA_USERROSTER = _
    "{947bb102-5d43-11d1-bdbf-00c04fb92675}"
Global Const JET_SCHEMA_ISAMSTATS = _
    "{8703b612-5d43-11d1-bdbf-00c04fb92675}"


Appendix D: Microsoft Jet 4.0 ANSI Reserved Words

Microsoft Jet 4.0 provides enhanced support for ANSI 92 keywords. For example, with Microsoft Jet 4.0 you can use the ANSI CREATE PROCEDURE syntax to create a new query. As a result of this support there are a number of new reserved words. If you have table or column names that conflict with one of the reserved words, you will now get a syntax error when referencing it in a query.

ABSOLUTE | DECIMAL | IS | ROWS
ACTION | DECLARE | ISOLATION | SCHEMA
ADD | DEFAULT | JOIN | SCROLL
ALL | DEFERRABLE | KEY | SECOND
ALLOCATE | DEFERRED | LANGUAGE | SECTION
ALTER | DELETE | LAST | SELECT
AND | DESCRIBE | LEADING | SESSION
ANY | DESC | LEFT | SESSION_USER
ARE | DESCRIPTOR | LEVEL | SET
AS | DIAGNOSTICS | LIKE | SIZE
ASC | DISCONNECT | LOCAL | SMALLINT
ASSERTION | DISTINCT | LOWER | SOME
AT | DOMAIN | MATCH | SQL
AUTHORIZATION | DOUBLE | MAX | SQLCODE
AVG | DROP | MIN | SQLERROR
BEGIN | ELSE | MINUTE | SQLSTATE
BETWEEN | END | MODULE | SUBSTRING
BIT | END-EXEC | MONTH | SUM
BIT_LENGTH | ESCAPE | NAMES | SYSTEM_USER
BOTH | EXCEPT | NATIONAL | TABLE
BY | EXCEPTION | NATURAL | TEMPORARY
CASCADE | EXEC | NCHAR | THEN
CASCADED | EXECUTE | NEXT | TIME
CASE | EXISTS | NO | TIMESTAMP
CAST | EXTERNAL | NOT | TIMEZONE_HOUR
CATALOG | EXTRACT | NULL | TIMEZONE_MINUTE
CHAR | FALSE | NULLIF | TO
CHARACTER | FETCH | NUMERIC | TRAILING
CHAR_LENGTH | FIRST | OCTET_LENGTH | TRANSACTION
CHARACTER_LENGTH | FLOAT | OF | TRANSLATE
CHECK | FOR | ON | TRANSLATION
CLOSE | FOREIGN | ONLY | TRIM
COALESCE | FOUND | OPEN | TRUE
COLLATE | FROM | OPTION | UNION
COLLATION | FULL | OR | UNIQUE
COLUMN | GET | ORDER | UNKNOWN
COMMIT | GLOBAL | OUTER | UPDATE
CONNECT | GO | OUTPUT | UPPER
CONNECTION | GOTO | OVERLAPS | USAGE
CONSTRAINT | GRANT | PARTIAL | USER
CONSTRAINTS | GROUP | POSITION | USING
CONTINUE | HAVING | PRECISION | VALUE
CONVERT | HOUR | PREPARE | VALUES
CORRESPONDING | IDENTITY | PRESERVE | VARCHAR
COUNT | IMMEDIATE | PRIMARY | VARYING
CREATE | IN | PRIOR | VIEW
CROSS | INDICATOR | PRIVILEGES | WHEN
CURRENT | INITIALLY | PROCEDURE | WHENEVER
CURRENT_DATE | INNER | PUBLIC | WHERE
CURRENT_TIME | INPUT | READ | WITH
CURRENT_TIMESTAMP | INSENSITIVE | REAL | WORK
CURRENT_USER | INSERT | REFERENCES | WRITE
CURSOR | INT | RELATIVE | YEAR
DATE | INTEGER | RESTRICT | ZONE
DAY | INTERSECT | REVOKE |
DEALLOCATE | INTERVAL | RIGHT |
DEC | INTO | ROLLBACK |


ADO and RDS Security Issues in Microsoft Internet Explorer

November 1998 (updated: April 2000)

By Kamaljit Bath

With millions of people surfing the Internet and downloading vast quantities of information every day, security is now a vital issue. Microsoft recognizes this and has taken precautions to ensure that the information you seek is not only easy to obtain but also arrives safely on your computer.

The goal of security development in Microsoft® Internet Explorer is to ensure that the following conditions are met:

  • Your personal computer cannot be harmed while you are viewing or accessing important data.
  • Internet applications cannot gain access to sensitive data on your personal computer or on any other computer within the same domain or internal network as your computer.

To promote this goal, some ADO and RDS objects have been modified so that they will adhere to security settings in a host environment such as Internet Explorer. Users who access Web sites that contain ADO and RDS applications should adjust their Internet Explorer security settings so that they can access these applications and at the same time protect their computers and data from potentially damaging controls or applications.

This document examines the following features of Internet Explorer 4.0 or later:

  • Internet Explorer security zones
  • Internet Explorer security levels
  • Customized security settings and local computer access
  • Restrictions in a trusted environment
  • Security behavior differences between MDAC 2.0 and 2.1/2.5

Developers and users of Web applications should understand the security zones, security levels, and how to customize the zones when working with ADO and RDS Web applications. Developers should understand the restrictions Internet Explorer places on ADO and RDS objects and the security differences between MDAC 2.0 and 2.1/2.5

Internet Explorer Security Zones

Each of the four zones and its default security level in Internet Explorer 4.x and 5.0 are described in the following table.

Zone | Description
Local Intranet | Contains Web sites that can be accessed without using a proxy server or a modem. (These are the Web sites on your intranet.) The default security level is Medium in Internet Explorer 4.x. In Internet Explorer 5.0, the default security level is Medium-Low.
Trusted Sites | Contains Web sites that you trust—sites that you believe you can download or run files from without worrying about damage to your computer or data. You can assign sites to this zone. The default security level is Low in Internet Explorer 4.x and 5.0.
Internet | Contains anything that is not on your computer or an intranet or assigned to any other zone. The default security level for the Internet zone is Medium in Internet Explorer 4.x and 5.0.
Restricted Sites | Contains sites you don't trust—that is, sites that you're not sure whether you can download or run files from without damage to your computer or data. You can assign sites to this zone. The default security level is High in Internet Explorer 4.x and 5.0.

For more detailed information about Internet zones, see your Internet Explorer documentation.

Internet Explorer Security Levels

For each zone, you can specify a security level. For Internet Explorer 4.x and 5.0, the available security levels are described in the following table.

Security level | Description
High | Excludes content that could damage your computer.
Medium | Warns you before running potentially damaging content.
Medium-Low | Available only in Internet Explorer 5.0. Does not prompt you each time potentially damaging content appears. Most content will run without prompts, and unsigned ActiveX® controls will not be downloaded to your computer.
Low | Does not warn you before running potentially damaging content.
Custom | A security setting of your own design. Use this level to customize the behavior of ADO and RDS objects in a specific zone.

The following illustration shows the four security levels:

For more detailed information about security levels, see your Internet Explorer documentation.

Note ADO and RDS objects will not work in Internet Explorer 4.x with ActiveX controls unless the security level is set to either Medium or Low.

Customized Security Settings and Local Computer Access

You can customize your security settings in Internet Explorer to allow trusted sites to run ADO and RDS objects while still protecting your computer and data from potentially damaging controls. You can assign sites that you trust to your trusted sites zone and set the security level to Low or Medium so that applications on those sites can run.

Following are instructions for customizing security behavior in both Internet Explorer 4.x and 5.0.

Internet Explorer 4.x Security Behavior

The following table shows how Internet Explorer 4.x responds when ADO and RDS objects are encountered in different security settings.

Security zone | Default security level | Respnse to ADO/RDS objects inside Internet Explorer 4.x
Local Intranet zone | Medium | When an unsafe operation is attempted, Internet Explorer displays a warning message. You are prompted to either cancel or continue with the operation on your computer.
Trusted Sites zone | Low | When an unsafe operation is attempted, Internet Explorer displays a warning message. You are prompted to either cancel or continue with the operation on your computer.
Internet Sites zone | Medium | Unsafe operations are not allowed. Internet Explorer prevents the operation.
Restricted Sites zone | High | Unsafe operations are not allowed. Internet Explorer prevents the operation.

Internet Explorer displays the following dialog box whenever an unsafe operation is attempted:

The default settings listed can be changed. You can change the security level to another level, or you can specify the custom security setting for various actions.

Note By changing the security level, you affect all security related behavior of all objects (including ADO and RDS objects) running in the browser in that particular zone.

Follow these steps to change the default setting for a specific security zone:

  1. In Internet Explorer, from the View menu, choose Internet Options.
  2. From the Security tab, select the security zone for which you wish to change the security setting.
  3. Click the High, Medium, or Low security level, and then click OK.

If you want to enable unsafe operations and do not want a prompt message box to appear each time such an operation is attempted, you should assign your safe, trusted sites to the Trusted Sites Zone and customize the behavior of the ActiveX controls for that zone. This will enable ActiveX objects from trusted sites to be more functional than all other zones.

To assign trusted sites to the Trusted Sites Zone:

  1. In Internet Explorer, from the View menu, choose Internet Options.
  2. From the Security tab, select the trusted sites zone. Click Add Sites.
  3. In the Add this Web Site to the Zone box, type the address of the Web site you wish to add. Click Add. The Web site address will appear in the box listing your trusted sites.
  4. Click OK.

To control the behavior of ADO and RDS objects in a specific zone, you need to create a custom security level and assign it to the zone in which the sites that contain the objects reside. Follow these steps:

  1. In Internet Explorer, from the View menu, choose Internet Options.
  2. On the Security tab, select the security zone for which you wish to customize the security setting.
  3. Choose the Custom level, and then click Settings.
  4. The Security Settings box appears. To customize ADO and RDS behavior in the selected zone, select only one of the following options under Initialize and script ActiveX controls not marked as safe:
    • Select Enable to allow unsafe operations on ADO and RDS objects without a warning, and then click OK.
    • Select Prompt to display a warning whenever an unsafe operation on ADO and RDS objects is attempted, and then click OK.

      When this option is selected and an unsafe operation is attempted, you will see the message, "The page is accessing a data source on another domain. Do you want to allow this?" If you select Yes, the operation is completed. If you select No, the operation is canceled.

    • Select Disable to disable unsafe operations on ADO and RDS objects, and then click OK.

      Caution By setting Enable for Initialize and script ActiveX controls not marked as safe, you are allowing any ActiveX control to be used from within Web pages. This potentially exposes user computers to malicious controls or malicious scripting code. It is recommended that this be done only in the Intranet or Trusted Sites security zones and not in the Internet zone.

  5. Repeat steps 1–4 if you need to customize the security settings for other security zones.
  6. When you are finished, click OK in the Internet Options dialog box.

The following illustration shows the security levels and settings in Internet Explorer:

Your ADO and RDS objects will now behave consistently with the specified custom mode. These settings affect the following behavior of ADO and RDS objects in the specified security zone:

  • Opening local two-tier connections
  • Working via DCOM
  • Connecting to a server other than the one from which the page was originally downloaded
  • Saving or opening a recordset to or from files on the local computer

There are two exceptions to be considered when running ADO and RDS objects in Internet Explorer 4.x:

  • Even though the ADO and RDS security behavior is controlled by the same option that controls the behavior of all other unsafe ActiveX objects, there is one distinct difference when these objects are used in the Local Intranet zone. Even when the setting for Initialize and script ActiveX controls not marked as safe option is Disable, Internet Explorer will still prompt you when ADO and RDS objects are encountered. Therefore, if you still want to disable unsafe operations of ADO and RDS objects in the Local Intranet zone, assign the specific sites to the Restricted Sites zone.
  • If you want to attempt an unsafe operation on Recordset objects that are obtained from an RDS DataControl object, you must set the value for the option to Enable. If the value is set to Prompt, the recordset will act as if the value is set to Disable.

    For example, for the following code to work inside Internet Explorer, the security setting for Initialize and script ActiveX controls not marked as safe must be set to Enable:

    dc.server="http://fred"
dc.SQL="select * from authors"
dc.connect="dsn=books;uid=rU*?;pwd=bcml;"
dc.refresh
rs = dc.recordset
rs.save "c:\mydirectory\myrecordset.xml", adPersistXML

Internet Explorer 5.0 Security Behavior

The following table shows how Internet Explorer 5.0 responds when ADO and RDS objects are encountered in different security settings.

Note A Medium-Low security level is introduced in Internet Explorer 5.0.

Security zone | Default security level | Response to ADO/RDS objects inside Internet Explorer 5.0
Trusted Sites Zone | Low | Unsafe operations are allowed. None of the described restrictions apply in this setting.
Local Intranet Zone | Medium-Low | When an unsafe operation is attempted, Internet Explorer displays a warning message. You are prompted to either cancel or continue with the operation on your computer.
Internet Sites Zone | Medium | Unsafe operations are not allowed. Internet Explorer prevents the operation.
Restricted Sites Zone | High | Unsafe operations are not allowed. Internet Explorer prevents the operation.

The following illustration shows the security zones in Internet Explorer 5.0:

Internet Explorer displays the same dialog box whenever an unsafe operation is attempted as is illustrated in "Internet Explorer 4.x Security Behavior."

You can change default settings by changing the security level or by specifying the Custom security setting for various actions. In Internet Explorer 5.0, the behavior of ADO and RDS objects is controlled by setting the value of the Access data sources across domains option.

When you change the security level, you can also change the behavior of disconnected ADO Recordset objects running in the browser. If you want to enable unsafe operations and do not want a prompt to appear each time such an operation is attempted, you can assign trusted sites to the Trusted Sites Zone.

By using a new option introduced in Internet Explorer 5.0, you can also customize the behavior of ADO and RDS objects by directly customizing the security settings, without affecting how other ActiveX objects behave in that specific zone.

To customize ADO and RDS security settings for a specific security zone, follow these steps using Internet Explorer 5.0:

  1. On the Tools menu, click Internet Options.
  2. On the Security tab, select the required zone. Then select a security level for the zone. To apply this level, click OK or Apply.
  3. To customize your settings for different options in that zone, select Custom and then click Settings.
  4. In the Security Settings dialog box, to customize ADO and RDS behavior in that zone, select one of the following options under Access data sources across domains:
    • Select Enable to allow unsafe operations on ADO and RDS objects without a warning being displayed, and then click OK.
    • Select Prompt to display a warning whenever an unsafe operation on ADO and RDS objects is attempted, and then click OK.

      When this option is selected and an unsafe operation is attempted, you will see the message, "The page is accessing a data source on another domain. Do you want to allow this?" If you select Yes, the operation is completed. If you select No, the operation is canceled.

    • Select Disable to disable unsafe operations on ADO and RDS objects, and then click OK.
  5. Repeat steps 1–4 if you need to customize the security settings for other security zones.
  6. When you are finished, click OK in the Internet Options dialog box.

The following illustration shows the security levels and settings in Internet Explorer 5.0:

Your ADO and RDS objects will now behave consistently with the specified custom mode. These settings affect the following behavior of ADO and RDS objects in the specified security zone:

  • Opening local two-tier connections
  • Working via DCOM
  • Connecting to a server other than the one from which the page was originally downloaded
  • Saving or opening a Recordset to or from files on the local computer

Local Computer Access

Typically, HTML pages are accessed directly from local computers, without going through a Web server, because this is how Web browser applications are designed. When the HTML pages are ready, they are published on a Web server. Pages loaded from a local computer receive special treatment for security considerations. For example, for a page address such as c:\inetpub\wwwroot\msadc (and not a World Wide Web link, http://), unsafe operations are allowed.

In Internet Explorer 4.x, unsafe operations are always allowed on pages loaded from the local computer. Also, in Internet Explorer 5.0, the default is the same for pages loaded from the local computer zone and therefore unsafe operations are allowed. However, in Internet Explorer 5.0, you can change the behavior of the pages loaded from the local computer with the Internet Explorer Administration Kit (IEAK). Refer to your Internet Explorer documentation for more details about the administration kit.

Note If you save a Web page on your local computer, you are giving permission for that particular page to allow data access operations on your computer. In Internet Explorer 5.0, the original address of a page is saved along with the particular page you are viewing. It is not allowed exclusive permissions when saved to the local computer by default.

Restrictions in a Trusted Environment

You can use ADO and RDS to build applications that run in traditional client/server environments or in Internet and intranet environments. When you create an ADO or RDS application hosted in Microsoft Visual Basic®, Microsoft Visual C++®, Microsoft Visual J++®, or an ASP script (on the server side of a distributed application), all operations are allowed because no Internet security restrictions apply.

But when you create an application in which ADO or RDS objects are used in script code running inside a browser, it is important to ensure that these applications do not gain unauthorized access to information and that users of the applications can trust them.

A trusted environment is Internet Explorer in which the user has set the security level to either Medium or High. Because the ADO and RDS objects automatically adhere to security settings, Internet Explorer will alert the user when controls or applications containing these objects are about to run on the user's computer. If the security level has been set to High, Internet Explorer will not allow the control to run.

The following sections, "Safe Objects," "Unsafe Objects," and "Unsafe Operations," describe possible ADO and RDS behaviors in detail.

Safe Objects

A safe object is one that will not damage a user's computer or gain unauthorized access to data. The following objects are considered safe to run in a trusted environment:

  • RDS.DataControl object
  • RDS.DataSpace object
  • ADO Connection object
  • ADO Recordset object
  • ADO Record object (ADO 2.5 and later)
  • ADO Stream object (ADO 2.5 and later)

In the Internet Explorer environment, the developer can directly create and work with these safe objects by using scripting code.

Unsafe Objects

An unsafe object is one that may damage a user's computer or gain unauthorized access to data. When the security level in Internet Explorer is set to High, unsafe objects are not allowed. The following objects are considered unsafe when the security level is set to High:

  • RDSServer.DataFactory object
  • ADO Command object

When the security level is set to High, Internet Explorer will not allow these unsafe objects to be created directly or indirectly on the user's computer.

Unsafe Operations

Unsafe operations are those that could damage a user's computer or gain unauthorized access to data. Internet Explorer is designed to protect a user's computer and data with the security zones and security levels. No code running inside the browser should be able to damage the computer or gain unauthorized access to data on the client computer or any other computer on the same network as the client. For this reason, certain ADO and RDS functions are restricted or not allowed when running inside Internet Explorer. However, if you trust the author of a Web page, you can specifically allow these ADO and RDS functions to run inside that page by customizing the security settings as previously discussed.

In most cases, unsafe operations include those in which an object accessed from a Web page attempts to access data on the local computer or attempts to access another computer on the local intranet of which the application is not a part. These local two-tier or DCOM connections could allow unauthorized access to data. The ADO and RDS objects are restricted in which connections they can make and are often allowed to connect back only to the server on which they reside.

RDS.DataControl

When the DataControl object runs inside Internet Explorer, the following restrictions are applied against its operations:

  • All two-tier and DCOM scenarios on the RDS.DataControl object are considered unsafe. The DataControl object cannot be used to open connections to local OLE DB data stores on the client computer or to open DCOM-based connections to another computer within the local domain or network.

    For example, the following connections are not allowed when running inside Internet Explorer using a Medium or High security setting:

    ' Open a connection to a database on the local computer
dc.server=""
dc.SQL="select * from authors"
dc.connect="dsn=books;uid=rU*?;pwd=bcml;"
dc.refresh
' or...
' Open a connection over DCOM to a remote computer
dc.server="fred"
dc.SQL="select * from authors"
dc.connect="dsn=books;uid=rU*?;pwd=bcml;"
dc.refresh
  • All three-tier operations over HTTP are considered unsafe if the connection is not being made to the server from which the Web page has been downloaded. The ADO and RDS applications can connect over HTTP only to OLE DB data stores that are on the same server from which the Web page has been downloaded. This means that the Server property on the RDS.DataControl object should be equal to the host name (http://server) from which the page has been downloaded.

    For example, if the page has been downloaded from a URL that begins with http://server1, then dc.server must be equal to http://server1.

RDS.DataSpace

When the DataSpace object runs inside Internet Explorer, the following restrictions are applied against its operations:

  • All two-tier and DCOM scenarios on the RDS.DataSpace object are considered unsafe. The DataSpace object is not allowed to invoke objects on the local computer or over a DCOM connection on another computer within the client's domain or network.
  • For example, the following operations are not allowed when running inside Internet Explorer using a Medium or High security setting:
    ' Invoke a business object on another computer within the current domain
set myobjproxy = ds.createobject ("busobj.class", "fred")
' or...
' Invoke a business object on the local computer
set myobjproxy = ds.createobject ("busobj.class", "")
  • All three-tier scenarios over HTTP are considered unsafe if not connecting back to the server from which the page has been downloaded.

This means that the second parameter in the CreateObject method call on the DataSpace object should point to the same server from which the page has been downloaded.

For example, if the page has been downloaded from a URL that begins with http://server1, the second parameter in the CreateObject method call on the DataSpace object must be equal to http://server1.

  • To run in a browser environment, all objects invoked through the DataSpace object must be safe for scripting and initialization. All local-computer objects and objects that are to be invoked over DCOM must adhere to this security requirement. To learn how to make objects safe for scripting and initialization, refer to the Internet Explorer documentation.

Connection

When the Connection object runs inside Internet Explorer, the following restrictions are applied against its operations:

  • Making any connection where the OLE DB provider is neither the Microsoft OLE DB Remoting Provider nor the Microsoft Data Shaping Service for OLE DB is considered unsafe.
  • The connection string must start with "Provider=MS Remote" or "Provider=MSDataShape".
  • If a connection is being made using the Microsoft Data Shaping Service for OLE DB ("provider=MSDataShape"), the underlying data provider must be the Microsoft OLE DB Remoting Provider ("data provider=MS Remote"). All other combinations are considered unsafe.
  • The following connection is considered safe:
    "provider= MSDataShape; data provider=MS Remote;Remote server=fred;Remote Provider=sqloledb;database=books;uid=rU*?;pwd=bcml;"
  • If the connection specifies either "provider=MS Remote" or "data provider=MS Remote", the "Remote server=" tag in the connection string must also be the same name as the server from which the page has been downloaded. Therefore, the following connection is considered safe if the page in which this code is running has been downloaded from a server called "http://fred":
    cn.open "provider=ms remote;remote provider=sqloledb;remote server=http://fred;dsn=books;uid=rU*?;pwd=bcml;"

Recordset

When the Recordset object runs inside Internet Explorer, the following restrictions are applied against its operations:

  • Making any connection where the OLE DB provider is not the Microsoft OLE DB Remoting Provider or Microsoft Data Shaping Service for OLE DB is considered unsafe.

    The connection string must start with either "Provider=MS Remote" or "Provider=MSDataShape".

  • Local persistence operations, such as saving a Recordset to the local file system and opening a Recordset from file on the local computer, are not allowed:

    You can still load a Recordset from the same Internet server from which the page originally came. Therefore, the following code is considered safe if it runs in a page that has been downloaded from a server named "http://fred":

    rs.Open "http://fred/recordsets/myrs.xml",,,,adCmdFile
  • If a connection is being made using the Microsoft Data Shaping Service for OLE DB ("provider=MSDataShape"), the underlying data provider must be the Microsoft OLE DB Remoting Provider ("data provider=MS Remote"). All other combinations are considered unsafe.
  • The following connection is considered safe:
    "provider= MSDataShape; data provider=MS Remote;Remote server=fred;Remote Provider=sqloledb;database=books;uid=rU*?;pwd=bcml;"
  • If the connection specifies either "provider=MS Remote" or "data provider=MS Remote", the "Remote server=" tag in the connection string must be the same name as the server from which the page has been downloaded.

For example, the following connection is considered safe if the page from which the code is running has been downloaded from a server named "http://fred":

cn.Open "provider=ms remote;remote provider=sqloledb;remote server=http://fred;dsn=books;uid=rU*?;pwd=bcml;"

Record

When the Record object runs inside Internet Explorer, the following restrictions are applied against its operations:

  • Opening a Record object using a URL is considered unsafe if the record is not opened from the server from which the Web page was downloaded.

    For example, the following operation is allowed if the page from which the code is running has been downloaded from a server named "http://fred":

    rec.Open "mydoc.htm", "URL=http://fred/public"
  • The destination URL of the CopyRecord and MoveRecord methods of the Record object must use the same server name as the server from which the page has been downloaded.

    For example, the following operation is not allowed if the page from which the code is running has not been downloaded from a server named "http://fred":

    rec.CopyRecord "", "http://derf/public"

Stream

When the Stream object runs inside Internet Explorer, the following restrictions are applied against its operations:

  • Opening a Stream object using a URL is considered unsafe of the stream is not opened from the server from which the Web page was downloaded.

    For example, the following operation is allowed if the page from which the code is running has been downloaded from a server named "http://fred":

    strm.Open "URL=http://fred/public/mydoc.htm", adModeRead
  • The LoadFromFile and SaveToFile methods of the Stream object are considered unsafe operations.

    For example, the following operation is not allowed when running inside Internet Explorer using a Medium or High security setting:

    strm.LoadFromFile "\\zawdoc01\docs\fred\booklist.xml"

Security Behavior Differences Between MDAC 2.0 and MDAC 2.1

Security behavior in Internet Explorer 4.x environments that is changed in MDAC 2.1 and later includes the following:

  • With MDAC 2.1, if you set Initialize and script ActiveX controls not marked as safe to Disable in the Local Intranet zone, Internet Explorer will prompt you when any unsafe operation is attempted. You will always be prompted for unsafe operations unless this option is set to Enable. If unsafe operations must be completely disabled for a specific site, assign the site to the Restricted Sites zone.

    Note This behavior pertains only to Internet Explorer 4.0.

  • In MDAC 2.1, the ADO Connection object is also considered a safe object.
  • In MDAC 2.0, local computer zone access is always given unrestricted permissions. In MDAC 2.1, this access is governed by the current setting for the local computer zone but it can be changed through the Internet Explorer Administration Kit.


Mapping ADO Methods to OLE DB Interfaces

Use the following information as a guide for conformance testing.

OLE DB Interfaces ADO Requires

IAccessor
IColumnsInfo
IConvertType
IDBCreateSession
IDBInitialize
IDBProperties
IOpenRowset
IRowset
IRowsetInfo
ISourcesRowset

Note ISourcesRowset is implemented in the OLE DB enumerator provided in the MSDA SDK. It is required only if the provider friendly name is used instead of ProgID.

OLE DB Interfaces ADO Uses, If Available

IColumnsRowset
ICommand
ICommandPrepare
ICommandProperties
ICommandText
ICommandWithParameters
IDBCreateCommand
IDBDataSourceAdmin
IDBInfo
IDBSchemaRowset
IErrorInfo
IErrorRecords
IIndexDefinition
IRowsetChange
IRowsetIndex
IRowsetLocate
IRowsetNotify
IRowsetResynch
IRowsetScroll
IRowsetUpdate
IRowsetView
ISQLErrorInfo
ITableDefinition
ITransaction
ITransactionLocal
IViewChapter
IViewFilter
IViewSort

OLE DB Interfaces Not Used by ADO

IGetDataSource
IRowsetIdentity
ISessionProperties
ISupportErrorInfo
ITransactionJoin
ITransactionObject
ITransactionOptions

OLE DB Interfaces Used by ADO 2.1

IDBDataSourceAdmin
IIndexDefinition
IRowsetIndex
ITableDefinition

OLE DB Interfaces Used by ADO 2.5

IBindResource
IColumnsInfo
ICreateRow
IDBBinderProperties
IRow

CursorType to Rowset Properties Mapping

adOpenForwardOnly | All cursor types, DBPROP_IRowset, DBPROP_ISequentialStream, DBPROP_IColumnsRowset
adOpenKeyset | DBPROP_OWNUPDATEDELETE, DBPROP_OWNINSERT, DBPROP_OTHERUPDATEDELETE, DBPROP_IRowsetLocate, DBPROP_CANSCROLLBACKWARDS, DBPROP_CANFETCHBACKWARDS, DBPROP_CANHOLDROWS, DBPROP_IRowsetRefresh, DBPROP_REMOVEDELETED
adOpenStatic | DBPROP_OWNUPDATEDELETE, DBPROP_OWNINSERT, DBPROP_IRowsetLocate, DBPROP_CANSCROLLBACKWARDS, DBPROP_CANFETCHBACKWARDS, DBPROP_CANHOLDROWS, DBPROP_IRowsetRefresh
adOpenDynamic | DBPROP_OWNUPDATEDELETE, DBPROP_OWNINSERT, DBPROP_OTHERUPDATEDELETE, DBPROP_OTHERINSERT, DBPROP_CANSCROLLBACKWARDS, DBPROP_CANFETCHBACKWARDS, DBPROP_REMOVEDELETED

LockType to Rowset Properties Mapping

adLockReadOnly | None
adLockOptimistic | DBPROP_IRowsetChange, DBPROP_UPDATABILITY, DBPROP_IRowsetUpdate
adLockPessimistic | DBPROP_IRowsetChange, DBPROP_UPDATABILITY, DBPROP_IRowsetUpdate
adLockBatchOptimistic | DBPROP_IRowsetChange, DBPROP_UPDATABILITY, DBPROP_IRowsetUpdate

ADO Objects to OLE DB Methods

The following table maps the methods, properties, and collections of ADO objects to the corresponding OLE DB methods called.

Note ADO caches information and defers the call to the OLE DB method.

ADO object | OLE DB method
Command object
Method
Cancel | None
CreateParameter | None
Execute | ICommand::Execute
Property
ActiveConnection | None
CommandText ICommandText::GetCommandText and
ICommandText::SetCommandText
CommandTimeout ICommandProperties::SetProperties
(DBPROP_COMMANDTIMEOUT)
CommandType | None
Name | None
Prepared ICommandPrepare::Prepare and
ICommandPrepare::Unprepare
State | None
Collection
Parameters Get: ICommandWithParameters::GetParameterInfo or
DBSCHEMA_PROCEDURE_PARAMETERS schema rowset

Set: ICommandWithParameters::SetParameterInfo
Properties IDBProperties::GetPropertyInfo,
ICommandProperties::GetProperties and
ICommandProperties::SetProperties
Connection object
Method
BeginTrans | ITransactionLocal::StartTransaction
Cancel | None
Close | None
CommitTrans | ITransactionLocal::Commit
Execute ICommand::Execute or
IOpenRowset::OpenRowset
Open IDBInitialize::Initialize and
IDBCreateSession::CreateSession
OpenSchema | IDBSchemaRowset::GetRowset
RollBack | ITransactionLocal::Abort
Event
BeginTransComplete | None
CommitTransComplete | None
ConnectComplete | None
Disconnect | None
ExecuteComplete | None
InfoMessage | None
RollbackTransComplete | None
WillConnect | None
WillExecute | None
Property
Attributes | ITransactionLocal::StartTransaction
CommandTimeout ICommandProperties::SetProperties
(DBPROP_COMMANDTIMEOUT)
ConnectionString | None
ConnectionTimeout IDBProperties::SetProperties
(DBPROP_INIT_TIMEOUT)
CursorLocation | None
DefaultDatabase IDBProperties::GetProperties
(DBPROP_CURRENTCATALOG) and
IDBProperties::SetProperties
(DBPROP_CURRENTCATALOG)
IsolationLevel | ITransactionLocal::StartTransaction
Mode IDBProperties::GetProperties
(DBPROP_INIT_MODE) and
IDBProperties::SetProperties
(DBPROP_INIT_MODE)
Provider | ISourcesRowset::GetSourcesRowset
State | None
Version | None
Collection
Errors | IErrorRecords
Properties IDBProperties::GetPropertyInfo,
IDBProperties::GetProperties and
IDBProperties::SetProperties
Error object
Method
None | None
Property
Description | IErrorRecords::GetErrorInfo
HelpContext | IErrorRecords::GetErrorInfo
HelpFile | IErrorRecords::GetErrorInfo
NativeError IErrorRecords::GetCustomErrorObject and
ISQLErrorInfo::GetSQLInfo
Number | None
Source | IErrorRecords::GetErrorInfo
SQLState IErrorRecords::GetCustomErrorObject and
ISQLErrorInfo::GetSQLInfo
Collection
None | None
Field object
Method
AppendChunk | ISequentialStream::Write
GetChunk IStream::Seek and IStream::Read,
ILockBytes::ReadAt or
ISequentialStream::Read
Property
ActualSize IAccessor::CreateAccessor and
IRowset::GetData
Attributes | IColumnsInfo::GetColumnInfo
DataFormat | IColumnsInfo::GetColumnInfo
DefinedSize | IColumnsInfo::GetColumnInfo
Name | IColumnsInfo::GetColumnInfo
NumericScale | IColumnsInfo::GetColumnInfo
OriginalValue | IRowsetUpdate::GetOriginalData
Precision | IColumnsInfo::GetColumnInfo
Type | IColumnsInfo::GetColumnInfo
UnderlyingValue IRowsetRefresh::GetLastVisibleData or
IRowsetResynch::GetVisibleData
Value IAccessor::CreateAccessor and
IRowset::GetData and IRowsetChange::SetData
Collection
Properties IDBProperties::GetPropertyInfo,
IRowsetInfo::GetProperties and
IRowsetInfo::SetProperties
Parameter object
Method
AppendChunk | None
Property
Attributes Get: ICommandWithParameters::GetParameterInfo or
DBSCHEMA_PROCEDURE_PARAMETERS schema rowset

Set: ICommandWithParameters::SetParameterInfo
Direction Get: ICommandWithParameters::GetParameterInfo or
DBSCHEMA_PROCEDURE_PARAMETERS schema rowset

Set: ICommandWithParameters::SetParameterInfo
Name Get: ICommandWithParameters::GetParameterInfo or
DBSCHEMA_PROCEDURE_PARAMETERS schema rowset

Set:
ICommandWithParameters::SetParameterInfo
NumericScale Get: ICommandWithParameters::GetParameterInfo or
DBSCHEMA_PROCEDURE_PARAMETERS schema rowset

Set: ICommandWithParameters::SetParameterInfo
Precision Get: ICommandWithParameters::GetParameterInfo or
DBSCHEMA_PROCEDURE_PARAMETERS schema rowset

Set: ICommandWithParameters::SetParameterInfo
Size Get: ICommandWithParameters::GetParameterInfo or
DBSCHEMA_PROCEDURE_PARAMETERS schema rowset

Set: ICommandWithParameters::SetParameterInfo
Type Get: ICommandWithParameters::GetParameterInfo or
DBSCHEMA_PROCEDURE_PARAMETERS schema rowset

Set: ICommandWithParameters::SetParameterInfo
Value IAccessor::CreateAccessor and
ICommand::Execute
Collection
Properties | None
Record object
Method
Cancel | None
Close | None
CopyRecord | IScopedOperations::Copy
DeleteRecord | IScopedOperations:Delete
GetChildren | IBindResource::Bind
MoveRecord | IScopedOperations::Move
Open | IBindResource::Bind
Property
ActiveConnection | None
Mode | None
ParentURL | IRow::GetColumns
RecordType | None
Source | None
State | IDBAsynchStatus::GetStatus
Collection
Fields | None
Properties | None
Recordset object
Method
AddNew | IRowsetChange::InsertRow
Cancel | None
CancelBatch | IRowsetUpdate::Undo
CancelUpdate | None
Clone | IRowsetLocate
Close IAccessor::ReleaseAccessor,
IRowset::ReleaseRows
CompareBookmarks | None
Delete | IRowsetChange::DeleteRows
Find | None
GetRows IAccessor::CreateAccessor,
IRowsetLocate::GetRowsAt,
IRowset::GetNextRows, and
IRowset::GetData
GetString | None
Move IRowsetLocate::GetRowsAt or
IRowset::GetNextRows
MoveFirst IRowsetLocate::GetRowsAt or
IRowset::RestartPosition
MoveLast | IRowsetLocate::GetRowsAt
MoveNext IRowsetLocate::GetRowsAt or
IRowset::GetNextRows
MovePrevious IRowsetLocate::GetRowsAt or
IRowset::GetNextRows
NextRecordset | IMultipleResults::GetResult
Open IOpenRowset::OpenRowset or
ICommand::Execute
Requery IOpenRowset::OpenRowset or
ICommand::Execute
Resync | IRowsetRefresh::RefreshVisibleData
Supports | IRowsetInfo::GetProperties
Save | None
Seek | None
Update IRowsetChange::SetData and/or
IRowsetUpdate::Update
UpdateBatch | IRowsetUpdate::Update
Event
EndOfRecordset | None
FetchComplete | None
FetchProgress | None
FieldChangeComplete | None
MoveComplete | None
RecordChangeComplete | None
RecordsetChangeComplete | None
WillChangeField | None
WillChangeRecord | None
WillChangeRecordset | None
WillMove | None
Property
AbsolutePage Get: IRowsetScroll::GetApproximatePosition

Set: IRowsetScroll::GetRowsAtRatio or
IRowsetLocate::GetRowsAt
AbsolutePosition Get: IRowsetScroll::GetApproximatePosition

Set: IRowsetScroll::GetRowsAtRatio or
IRowsetLocate::GetRowsAt
ActiveCommand | None
ActiveConnection IDBInitialize::Initialize and
IDBCreateSession::CreateSession
BOF | None
Bookmark IAccessor::CreateAccessor and
IRowsetLocate::GetRowsAt
CacheSize cRows argument to IRowsetLocate::GetRowsAt or
IRowset::GetNextRows
CursorLocation | None
CursorType | ICommandProperties::SetProperties
DataMember | None
DataSource | None
EditMode | IRowsetUpdate::GetRowsStatus
EOF | None
Filter IRowsetUpdate::GetPendingRows and
IRowsetLocate::GetRowsByBookmark OR
IRowsetView::CreateView and
IViewChapter::OpenViewChapter and
IViewFilter::SetFilter
Index | None
LockType | ICommandProperties::SetProperties
MarshalOptions | None
MaxRecords IOpenRowset::OpenRowset
(DBPROP_MAXROWS) or
ICommandProperties::SetProperties
(DBPROP_MAXROWS)
PageCount | IRowsetLocate::GetRowsAt
PageSize | None
RecordCount | IRowsetLocate::GetRowsAt
Sort | None
Source | None
State | None
Status | IRowsetUpdate::GetRowStatus
StayInSync | None
Collection
Fields | IColumnsInfo::GetColumnInfo
Properties IDBProperties::GetPropertyInfo,
IRowsetInfo::GetProperties and
IRowsetInfo::SetProperties
Stream object
Method
Cancel | None
Close | None
CopyTo | IStream::CopyTo
Flush | None
LoadFromFile | None
Open | IBindResource::Bind
Read | IStream::Stat, IStream::Read
ReadText | None
SaveToFile | None
SetEOS | IStream::Seek, IStream::SetSize
SkipLine | IStream::Seek, IStream::Read
Write | IStream::Write
WriteText | None
Property
Charset | None
EOS | IStream::Stat, IStream::Seek
LineSeparator | None
Mode | None
Position | IStream::Seek
Size | IStream::SetSize
State | IDBAsynchStatus::GetStatus
Type | None

ADO Collections to OLE DB Methods

The following table maps the methods and properties of ADO collections to the corresponding OLE DB methods called.

ADO collection | OLE DB method
Errors collection
Method
Clear | None
Refresh | None
Property
Count | None
Item | None
Fields collection
Method
Append | None
Delete | None
Refresh | None
Update IRowSchemaChange::AddColumns and
IRowSchemaChange::DeleteColumns
Property
Count | None
Item IRow::Open,
IRowChange::SetColumns,
IRow::GetColumns,
IColumnsInfo::GetColumnInfo
Parameters collection
Method
Append | None
Delete | None
Refresh | None
Property
Count | None
Item | None
Properties collection
Method
Refresh | None
Property
Count | None
Item | None


Mapping ADOX Properties and Methods to OLE DB Interfaces

Catalog

Property | OLE DB Interface
ActiveConnection | See paper on mapping ADO to OLE DB for information on which interfaces ADO uses to create a connection.
Groups | None
Tables | None
Users | None
Views | None
Method | OLE DB Interface
Create | IDBProperties::GetProperties IDBDataSourceAdmin::CreateDataSource
GetObjectOwner | IObjectAccessControl::GetObjectOwner
SetObjectOwner | IObjectAccessControl::SetObjectOwner
Groups Collection | OLE DB Interface
Append | ITrusteeAdmin::CreateTrustee
Delete | ITrusteeAdmin::DeleteTrustee
Item | None
Users Collection | OLE DB Interface
Append | ITrusteeAdmin::CreateTrustee
Delete | ITrusteeAdmin::DeleteTrustee
Item | None

Table

Property | OLE DB Interface
Columns | None
DateCreated | IDBSchemaRowset::GetRowset TABLES, DATE_CREATED
DateModified | IDBSchemaRowset::GetRowset TABLES, DATE_MODIFIED
Indexes | None
Keys | None
Name | IDBSchemaRowset::GetRowset TABLES, TABLE_NAME
ParentCatalog | None
Properties | IDBProperties::GetProperties DBPROPSET_TABLEALL
Type | IDBSchemaRowset::GetRowset TABLES, TABLE_TYPE

The interfaces listed above are used to retrieve values for existing tables. When creating new tables, ITableDefinition::CreateTable is used to set the property values. For modifying properties of existing tables IAtlerTable::AlterTable is used.

Tables Collection | OLE DB Interface
Append | ITableDefinition::CreateTable
Delete | ITableDefinition::DropTable
Item | None
Refresh | ADOConnection::OpenSchema(DBSCHEMA_TABLES)
Columns Collection | OLE DB Interface
Append | ITableDefinition::CreateTable or ITableDefinition::AddColumn
Delete | ITableDefinition::DropColumn
Item | None
Refresh | ADOConnection::OpenSchema(DBSCHEMA_COLUMNS)

Column (of a Table)

Property | OLE DB Interface
Attributes | IDBSchemaRowset::GetRowset COLUMNS, COLUMN_FLAGS
DefinedSize | IDBSchemaRowset::GetRowset COLUMNS, COLUMN_FLAGS
Name | IDBSchemaRowset::GetRowset COLUMNS, COLUMN_NAME
NumericScale | IDBSchemaRowset::GetRowset COLUMNS, NUMERIC_SCALE
ParentCatalog | None
Precision | IDBSchemaRowset::GetRowset COLUMNS, NUMERIC_PRECISION
Properties | IDBProperties::GetProperties DBPROPSET_COLUMNALL
RelatedColumn | N/A
SortOrder | N/A
Type | IDBSchemaRowset::GetRowset COLUMNS, DATA_TYPE

The interfaces listed above are used to retrieve values for existing tables. When creating columns on new tables, ITableDefinition::CreateTable is used to set the property values. For adding columns on existing tables, ITableDefinition::AddColumn is used. For modifying properties of existing columns IAtlerTable::AlterColumn is used.

Index

Property | OLE DB Interface
Clustered | IDBSchemaRowset::GetRowset INDEXES, CLUSTERED
Columns | None (refers to columns collections)
IndexNulls | IDBSchemaRowset::GetRowset INDEXES, NULLS
Name | IDBSchemaRowset::GetRowset INDEXES, INDEX_NAME
PrimaryKey | IDBSchemaRowset::GetRowset INDEXES, PRIMARY_KEY
Properties | IDBProperties::GetProperties DBPROPSET_INDEXALL
Unique | IDBSchemaRowset::GetRowset INDEXES, UNIQUE

The interfaces listed above are used to retrieve values for existing tables. When creating new indexes, IIndexDefinition::CreateIndex is used to set the property values. For modifying properties of existing indexes IAtlerIndex::AlterIndex is used.

Indexes Collection | OLE DB Interface
Append | IIndexDefinition::CreteIndex
Delete | IIndexDefinition::DropIndex
Item | None
Refresh | ADOConnection::OpenSchema(DBSCHEMA_INDEXES)

Column (of an Index)

Property | OLE DB Interface
Attributes | N/A
DefinedSize | N/A
Name | IDBSchemaRowset::GetRowset INDEXES, COLUMN_NAME
NumericScale | N/A
ParentCatalog | None
Precision | N/A
Properties | N/A
RelatedColumn | N/A
SortOrder | IDBSchemaRowset::GetRowset INDEXES, COLLATION
Type | N/A

The interfaces listed above are used to retrieve values for existing tables. When creating columns on new tables, IIndexDefinition::CreateIndex is used to set the property values.

Key

Property | OLE DB Interface
Columns | None
DeleteRule | IDBSchemaRowset::GetRowset FOREIGN_KEYS, DELETE_RULE
Name | IDBSchemaRowset::GetRowset TABLE_CONSTRAINTS, CONSTRAINT_NAME
RelatedTable | IDBSchemaRowset::GetRowset FOREIGN_KEYS, PK_TABLE_NAME
Type | IDBSchemaRowset::GetRowset TABLE_CONSTRAINTS, CONSTRAINT_TYPE
UpdateRule | IDBSchemaRowset::GetRowset FOREIGN_KEYS, UPDATE_RULE

The interfaces listed above are used to retrieve values for existing keys. When creating new keys, ITableDefinitionWithConstraints::AddConstraint is used to set the property values.

Keys Collection | OLE DB Interface
Append | ITableDefinitionWithConstraints::AddConstraint
Delete | ITableDefinitionWithConstraints::DropConstraint
Item | None
Refresh | ADOConnection::DBSCEMA_TABLE_CONSTRAINTS

Column (of a Key)

Property | OLE DB Interface
Attributes | N/A
DefinedSize | N/A
Name (of Foreign Key) | IDBSchemaRowset::GetRowset FOREIGN_KEYS, PK_COLUMN_NAME
Name (of Primary Key) | IDBSchemaRowset::GetRowset CONSTRAINT_COLUMN_USAGE, COLUMN_NAME
Name (of Unique Key) | IDBSchemaRowset::GetRowset CONSTRAINT_COLUMN_USAGE, COLUMN_NAME
NumericScale | N/A
ParentCatalog | None
Precision | N/A
Properties | N/A
RelatedColumn | IDBSchemaRowset::GetRowset FOREIGN_KEYS, FK_COLUMN_NAME
SortOrder | N/A
Type | N/A

The interfaces listed above are used to retrieve values for existing tables. When creating columns on new key, ITableDefinitionWithCosntratints::AddConstraint is used to set the property values.

Group

Users Collection | OLE DB Interface
Append | ITrusteeGroupAdmin::AddMember
Delete | ItrusteeGroupAdmin::DeleteMember
Item | ITrusteeGroupAdmin::GetMembers
Refresh | ADOConnection::OpenSchema(DBSCHEMA_DBSCHEMA_TRUSTEE)

View

Property | OLE DB Interface
Command | ICommandPersist::LoadCommand and ICommandPersist::SaveCommand
DateCreated | IDBSchemaRowset::GetRowset VIEWS, DATE_CREATED
DateModified | IDBSchemaRowset::GetRowset VIEWS, DATE_MODIFIED
Name | IDBSchemaRowset::GetRowset VIEWS, TABLE_NAME
Views Collection | OLE DB Interface
Append | ICommandPersist::SaveCommand
Delete | ICommandPersist::DeleteCommand
Item | None
Refresh | ADOConnection::OpenSchema(DBSCHEMA_VIEWS)

Procedures

Property | OLE DB Interface
Command | ICommandPersist::LoadCommand and ICommandPersist::SaveCommand
DateCreated | IDBSchemaRowset::GetRowset PROCEDURES, DATE_CREATED
DateModified | IDBSchemaRowset::GetRowset PROCEDURES, DATE_MODIFIED
Name | IDBSchemaRowset::GetRowset PROCEDURES, PROCEDURE_NAME
Procedures Collection | OLE DB Interface
Append | ICommandPersist::SaveCommand
Delete | ICommandPersist::DeleteCommand
Item | None
Refresh | ADOConnection::OpenSchema(DBSCHEMA_PROCEDURES)

Users

Property | OLE DB Interface
Groups | None
Name | IDBSchemaRowset::GetRowset TRUSTEES, TRUSTEE_NAME

The interfaces listed above are used to retrieve values for existing users. When creating new users, ITrusteeAdmin::CreateTrustee is used to set the property values.

Method | OLE DB Interface
ChangePassword | ITrusteeAdmin::SetTrusteeProperties
GetPermissions | IObjectAccessControl::GetObjectAccessRights
SetPermissions | IObjectAccessControl::SetObjectAccessRights
Groups Collection | OLE DB Interface
Append | ITrusteeGroupAdmin::AddMember
Delete | ItrusteeGroupAdmin::DeleteMember
Item | ITrusteeGroupAdmin::GetMemberships
Refresh | ITrusteeGroupAdmin::GetMembers

Groups

Property | OLE DB Interface
Users | None
Name | IDBSchemaRowset::GetRowset TRUSTEES, TRUSTEE_NAME

The interfaces listed above are used to retrieve values for existing users. When creating new groups, ITrusteeAdmin::CreateTrustee is used to set the property values.

Method | OLE DB Interface
GetPermissions | IObjectAccessControl::GetObjectAccessRights
SetPermissions | IObjectAccessControl::SetObjectAccessRights


Using the Customization Handler Feature in RDS 2.1

January 1999

by Kamaljit Bath

Summary: The customization handler feature, which was first introduced in RDS 2.0, enables custom logic to be executed on the server when RDS is used to work directly with remote OLE DB providers. This article describes the scenarios that are enabled by this feature and documents the steps needed to make use of it.

The following topics are discussed in this document:

  • Introduction to the customization handler feature
  • Implementation details for the customization handler feature
  • Using the default handler shipped with RDS
  • Writing a new custom handler

Introduction to the Customization Handler Feature

RDS 1.5, which ships with Microsoft® Internet Information Server 4.0, enables ADO consumers to work with remote OLE DB providers running in separate processes or on separate machines connected over HTTP or DCOM protocols. Client-side applications have the option of working directly with an OLE DB provider on the remote process/machine or with a COM component on the remote process/machine that uses ADO recordsets as input/output parameters or return values.

When working in the first mode (directly with a remote OLE DB provider, as described above), the client-side application supplies all the information needed by the specified provider, such as the connection string and the command string. In a sense, this extends the traditional client/server model to work over distributed networks. The client specifies the data store to work with and the commands to be executed, and the server simply executes these commands and returns the results, as ADO recordsets, to the client.

In the second case (working with a COM component that uses ADO recordsets), the client-side application invokes public methods only on the middle-tier component and uses the ADO recordset just as a parameter value. This is true three-tier computing, where the presentation and user interface parts stay in the client-side application, business logic in the middle-tier COM component, and the persisted state of data and referential integrity checking remains in the data store.

Starting with RDS 2.0, developers are provided with another data access paradigm, where they can write their own server-side business-rules logic and still have their client-side applications work directly with the remote OLE DB provider without going through a business object. This enables the following scenarios:

  • Web server administrators can control access to OLE DB data stores from RDS clients. Because RDS enables consumers, connected over HTTP, to work directly with OLE DB providers, there is a security risk exposed. RDS works over proxies and firewalls. This means that the OLE DB data store (typically an ODBC connection to a database such as SQL Server or Microsoft® Access) is exposed to all Internet clients that have relevant connection information, such as the database name and the user ID/password. The customization feature in RDS 2.0 allows server administrators to control Internet connections to OLE DB providers hosted on the server.
  • Custom business logic, such as validation checks, can be added to applications using RDS to connect to remote OLE DB providers.
  • Server administrators can easily write their own customized handler logic. To help administrators start using the customization feature, a simple handler that works with most SQL-based OLE DB providers is shipped. The behavior of this handler is driven by an .ini file installed on the server.
  • To help administrators write their own customized handlers, the DA SDK also includes source code that is very similar to the shipped simple handler.

Implementation Details for the Customization Handler Feature

When RDS is used by client-side applications to work directly with remote OLE DB providers, an RDSServer.DataFactory object on the Web server handles client requests. The DataFactory object implements Query and Execute methods that can open a new recordset against a user-specified OLE DB data source object by running a user-specified command. It also implements the methods SubmitChanges and Synchronize, which can be used to apply the changes made to a recordset to a user-specified OLE DB data source object. (The update feature works only against SQL-based OLE DB providers—for example, the OLE DB Provider for ODBC and the OLE DB Provider for SQL Server.)

Writing a custom handler object that implements a specific interface called IDataFactoryHandler can enhance the DataFactory functionality. Developers can build their own custom objects to implement the IDataFactoryHandler interface. These objects are then implicitly called by DataFactory and used to extend the default functionality. This is illustrated by the following code snippet, where a client-side application is opening an ADO Recordset based on a remote OLE DB provider.

Dim AdoRs as ADODB.Recordset
AdoRs.ActiveConnection = "Handler=MyHandler.obj;Provider=MS Remote;Remote Server=http://Hyperspace;DSN=AdvWorks;"
AdoRs.Source = "Select * from Products"
AdoRs.Open

In the above code segment, the developer has specified that the default handler object to be used on the server side is the MyHandler.obj object. When this code is executed, the DataFactory object is called by default on the server. However, before the Query method on the DataFactory object opens the recordset, it creates the MyHandler.obj object and then calls QueryInterface for its IDataFactoryHandler interface. On this interface, it calls the GetRecordset method, passing in the connect and command strings. The user-written code inside the handler will open an ADO Recordset and pass it back to DataFactory. The user can use ADO events inside the handler to customize the behavior of the Connection and Recordset objects.

The following table shows which IDataFactoryHandler methods are called by each of the DataFactory methods.

DataFactory method | IDataFactoryHandler method called
Query | GetRecordset
SubmitChanges | Reconnect
Execute | OpenConnection
Synchronize | OpenConnection

By default, MDAC 2.1 clients will use the new DataFactory methods Execute and Synchronize. If the MDAC version on the server is older than version 2.1, it will return an error stating that the method does not exist. If a 2.1 client gets this error, it either will retry with Query or SubmitChanges or, if these older methods cannot satisfy the request, will return an error. For example, if the client wants to execute a parameterized query, the client must use Execute, because Query does not support this.

Architecture Diagram

When the client code uses RDS, without specifying the handler, to work with remote OLE DB providers, the DataFactory executes the client request. The DataFactory uses ADO to work with the underlying OLE DB provider.

When the client code uses RDS to work with remote OLE DB providers and also specifies the handler to be used, the client request is still executed by the DataFactory. The DataFactory in turn uses the specified handler to service the client requests.

Custom Handler

An RDS server custom handler is a COM Automation object that implements the IDataFactoryHandler interface. It must have its ProgID registered in the server's registry as NewHandler Progid under the following key:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\DataFactory\HandlerInfo \safehandlerlist

To use the specified handler, the client code used to open an ADO Recordset must include the tag "handler=<handler progid>;" in the connection string. If RDS.Datacontrol is used by the client to open the Recordset, the Handler property must be set to the handler's ProgID. If the client is coding directly to OLE DB, the connection string must contain the "handler=<handler progid>;" tag.

Server administrators can require the use of a handler with an RDS server simply by inserting the entry "HandlerRequired=1" and/or the DefaultHandler entry in the following key in the server's registry:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\DataFactory\HandlerInfo

If the entry "HandlerRequired=1" is inserted and there is no DefaultHandler, then all RDS requests that do not include the "handler=<handler progid>;" tag will be rejected. And <handler progid> must point to a valid custom handler object that is included in the safehandlerlist registry entry described above.

MSDFMAP.Handler

RDS is typically used to work with OLE DB providers for SQL-based data stores. The DataFactory object can directly open and update rowsets from such data stores. To secure such data stores from direct exposure to Internet clients, MDAC 2.0 installs a default custom handler object—MSDFMAP.Handler. The behavior of this handler is driven by the file Msdfmap.ini, also installed with MDAC. The Msdfmap.ini file is installed in the Windows directory (C:\Winnt, for example). This .ini file can be configured by the server administrator to customize the behavior of the MSDFMAP.Handler object.

Configuring MSDFMAP.Handler

Changing the parameters in the supplied .ini file can modify the behavior of this handler. The .ini file(s) must reside in the Windows directory (C:\Winnt, for example).

Sample Msdfmap.ini file:

;[connect name] will modify the connection if rdsControl.connect="name"
;[connect default] will modify the connection if name is not found
;[sql name] will modify the Sql if RDSCONTROL.sql="name(args)"
;[sql default] will modify the Sql if name is not found
;Override strings: Connect, UserId, Password, Sql.
;Only the Sql strings support parameters using "?"
;The override strings must not equal "" or they are ignored
;A Sql entry must exist in each sql section or the section is ignored
;An Access entry must exist in each connect section or the section is ignored
;Access=NoAccess
;Access=ReadOnly
;Access=ReadWrite
;[userlist name] allows specific users to have special access
;The Access is computed as follows:
;  (1) First take the access of the connect section
;  (2) If a user entry is found, it will override

[connect default]
;If we want to disable unknown connect values, we set Access to NoAccess
Access=NoAccess
[sql default]
;If we want to disable unknown sql values, we set Sql to an invalid query.
Sql=" "

[connect CustomerDatabase]
Access=ReadWrite
Connect="DSN=AdvWorks"

[sql CustomerById]
Sql="SELECT * FROM Customers WHERE CustomerID = ?"
[connect AuthorDatabase]
Access=ReadOnly
Connect="DSN=MyLibraryInfo;UID=MyUserID;PWD=MyPassword"

[userlist AuthorDatabase]
Administrator=ReadWrite

[sql AuthorById]
Sql="SELECT * FROM Authors WHERE au_id = ?"

The administrator can change this .ini file to install customized DataFactory behavior on the server. For example, when a client sends in the following request, it is handled as described below:

Rs.open "CustomerById(2)", "Handler=MSDFMAP.Handler;Provider=MS Remote;Remote Server=http://mysite;Data Source=CustomerDatabase"

With the sample .ini file shown above, this is equivalent to the following command:

Rs.open "SELECT * FROM Customers WHERE CustomerId = 2", "DSN=AdvWorks"

The administrator can add new sections to the .ini file or change existing settings to define new mappings. Another option is to write a completely new handler object. The process for writing a new handler is described later in this article. The administrator can also have multiple .ini files. If a different .ini file is to be used (instead of the supplied Msdfmap.ini, which is used by default), the connection string at the client must specify the .ini file name. Example:

RDSCONTROL.Handler = "MSDFMAP.Handler,myOtherNameSpace.ini"

Or:

Rs.open "CustomerById(2)", "Provider=MS Remote;Remote Server=http://hyperspace;Handler=MSDFMAP.Handler,myOtherNameSpace.ini;Data Source=CustomerDatabase"

Note All .ini files to be used with MSDFMAP.Handler must exist in the %SYSTEMROOT% directory.

Modifying the .ini File

The following points explain the workings of the .ini file:

  • The default .ini file is installed in the %SYSTEMROOT% directory (c:\winnt, for example) and is called Msdfmap.ini.
  • Four kinds of sections can be contained in the file. They are all identified by their keywords—Connect, used to specify connection string mappings; SQL, used to specify command string mappings; UserList, used to override default access permissions for a list of users; and Logs, used to specify a file name where all errors can be logged.

    The keywords Connect, SQL, and Userlist are explained in the following sections. The Logs keyword is used to specify a file name where all run-time errors associated with the handler can be logged. The syntax for specifying the Logs keyword is as follows:

    [Logs]
err='c:\temp\HandlerLog.err'

    If the file specified under the Logs keyword already exists, new errors are appended to it. Otherwise, a new file is created.

  • The values ConnectionString and CommandString in the client request are converted to those specified in the matching Connect and SQL sections, respectively.
  • Userlist works in a slightly different way than the Connect and SQL keywords. The Userlist keyword works as a subsection inside the connect or sql section. (The matching is done based on the unique identifier for sql or connect.) For example:
    [connect customerdatabase]
connect=dsn=pubs;uid=sa;pwd=;
access=readonly

[userlist customerdatabase]
administrator=readwrite

    Thus, a customerdatabase identifier is used with the Connect keyword. When the client comes in with the data store value in connectionstring set to customerdatabase, the connection is mapped to "dsn=pubs;uid=sa;pwd=;". By default, access is set to readonly for this connection unless the user is the administrator, who gets a readwrite permission.

    The userlist subsection is matched with the connect section by matching the identifier customerdatabase. The same is true for the Userlist and SQL keywords.

  • To use the Userlist option in the .ini file, the server must be able to ascertain the user credentials of RDS clients. This is not a problem when the client uses DCOM protocol to connect to the server. But if HTTP is used, all users connect to the server under the credentials of a default user specified under the "Anonymous" authentication scheme of Internet Information Server (IIS). The server administrator may choose to run a different authentication scheme to capture more information about Internet clients and to provide them specific services based on their identity.

    To change the authentication scheme, do the following: On the server, start the IIS service manager and select the virtual directory under the default Web site. Right-click the selected directory. In the Properties dialog box, click the Directory Security tab, and then click Edit on Anonymous Access and Authentication control. Next, disable Anonymous Access and select Basic Authentication or NT Challenge/Response. Users can now log in to the IIS Web server by using their client credentials. If Userlist subsections are present in the .ini file, the actions specified therein are applied to users matching the Userlist keyword.

  • If the same identifier appears more than once with the same keyword, only the first occurrence is used. The subsequent occurrences are ignored. Therefore, if the following lines are present:
    [sql customerbyid]
select * from customers where cust_id = ?
…
…
[sql customerbyid]
select * from customer

    only the first occurrence of customerbyid will be used. All sql strings containing customerbyid will be converted to the "select * from customers where cust_id = ?" format.

  • The default handler can be invoked only if the client's connection string contains "Handler=msdfmap.handler" or if the DefaultHandler registry entry is msdfmap.handler. In the absence of this substring, DataFactory continues work in its default (nonhandler) mode. To work without a handler, server administrators can add the entry "HandlerRequired=0" to the following key in the server registry:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\DataFactory \HandlerInfo

Absence of this key—or if the value of the HandlerRequired key is 0—means that the use of the "Handler=" tag is not mandatory. RDS is thus enabled to work in the same manner as it did in RDS 1.5.

Using the Default Handler Shipped with RDS

A DataFactory handler is a COM object that implements the IDataFactoryHandler interface. The following files are included in the Data Access SDK so that users can create their own DataFactory handlers:

  • Msdfhdl.idl (interface definition file).
  • Msdfhdl.tlb (type library generated from Msdfhdl.idl for the Microsoft Visual Basic® sample.

    Note Msdfhdl.tlb is version specific. For example, the version shipped in the Data Access SDK 2.0 will not work properly with Visual Basic when MDAC 2.1 is installed.)

Source code (for the Visual Basic and Microsoft Visual C++® versions) similar to that of the default handler, MSDFMAP.Handler, is also included in the SDK. This can be used as a reference for implementing new handlers.

Msdfhdl.idl

[
   uuid(D80DE8B3-0001-11d1-91E6-00C04FBBBFB3),
   version(1.0)
]
library MSDFHDL
{
   importlib("stdole32.tlb");
   importlib("stdole2.tlb");

   // ********** MSADO15.DLL must be registered on your machine ********
   // TLib : Microsoft ActiveX Data Objects 2.1 Library
   // {00000200-0000-0010-8000-00AA006D2EA4}
   importlib("C:\Program Files\Common Files\System\ado\msado15.dll");

   [
      odl,
      uuid(D80DE8B5-0001-11d1-91E6-00C04FBBBFB3),
      version(1.0)
   ]
   interface IDataFactoryHandler : IUnknown
   {
      // The Recordset returned from GetRecordset:
      // must be created with CursorLocation = AdUseClientBatch,
      // and adRunAsync must NOT be used! (Use adUnknown or 8
      // in rs.Open.)
      HRESULT _stdcall GetRecordset(
         [in] BSTR conn,
         [in] BSTR args,
         [in] BSTR query,
         [out, retval] _Recordset **ppRS);

      // DataFactory will use the ActiveConnection property
      // on the recordset after calling Reconnect.
      HRESULT _stdcall Reconnect(
         [in] BSTR conn,
         [in] BSTR args,
         [in] _Recordset *pRS);

      // New method for MDAC 2.1 only
      HRESULT _stdcall OpenConnection(
         [in] BSTR conn,
         [in] BSTR args,
         [in] long lFlags,  // 1=WriteAccess, 2=ModifyQuery
         [in, out] BSTR * pQuery,
         [out, retval] _Connection **ppConnection);
    };
};

Handler Properties

None.

Handler Methods

GetRecordset

This function will create a new ADO Recordset using the arguments provided. The Recordset must be opened with adLockBatchOptimistic. The Connection and Recordset objects must have the Cursor Location property set to adUseClientBatch.

  • The conn argument is the connection string.
  • The args argument is the string after the first comma in the Handler value. Examples:
    • "Handler=progid,arg1,arg2;" will pass "args=arg1,arg2".
    • "Handler=progid" will pass "args=NULL".
  • The query argument is the command text.
  • The ppRS argument is where the function returns the new ADO Recordset.

Reconnect

This function will create a new ADO Connection object and attach the given ADO Recordset object to it. The Connection must have the Cursor Location property set to adUseClientBatch.

  • The conn argument is the connection string.
  • The args argument is the string after the first comma in the Handler value. Examples:
    • "Handler=progid,arg1,arg2;" will pass "args=arg1,arg2".
    • "Handler=progid" will pass "args=NULL".
    • The pRS argument is an ADO Recordset.

OpenConnection

This function will create a new ADO Connection using the arguments provided. The Connection object must have the Cursor Location property set to adUseClientBatch.

  • The conn argument is the connection string.
  • The args argument is the string after the first comma in the Handler value. Examples:
    • "Handler=progid,arg1,arg2;" will pass "args=arg1,arg2".
    • "Handler=progid" will pass "args=NULL".
  • The lFlags argument tells OpenConnection what mode to operate in. The DataFactory Execute method will call with a value of 2, which tells OpenConnection that pQuery can be changed. The DataFactory Synchronize method will call with a value of 1.
  • The pQuery argument is the command text. The DataFactory Synchronize method passes NULL for this argument.
  • The ppConnection argument is where the function returns the new ADO Connection.

Writing a New Custom Handler

The easy way to write a new custom handler is to start with the Visual Basic or Visual C++ sample handler in the SDK, and then modify it to your liking. The following example uses Visual Basic:

  1. Make a new ActiveX® DLL project.
  2. Select the Unattended Execution option in the Project:Properties dialog box. You should use the Free Threaded model for best performance on a server, but it's is not available in Visual Basic, so you'll have to use Apartment model for now.
  3. Include the "Microsoft ActiveX Data Objects Library" in the project references.
  4. Include the Msdfhdl.tlb (type library for the Interface IDataFactoryHandler) in the project references.
  5. Add "Implements IDataFactoryHandler" in the Declarations section of your class code.
  6. Add code to all of the methods and property set/get procedures on the interface.

Using ADO Events in a Visual Basic Handler

Inside the handler, the Connection object can be created using the WithEvents keyword. The developer can write custom events to be associated with this object.

Assume the following class names:

  • Class1 is the name of the class that implements IDataFactoryHandler.
  • Class2 is the name of the class that implements the ADO event handlers.

To implement ADO events in your DataFactory Handler:

  1. Add the following to the Declarations section of Class1:
    Private c2 as new Class2
  2. Add the following to the Declarations section of Class2:
    Public conn As New ADODB.Connection
Private WithEvents connEvh As ADODB.Connection
  3. Add the following to the Class_Initialize function of Class2:
    Set connEvh = conn
  4. Implement whatever event functions you wish in Class2.
  5. Use c2.conn in Class1.

Note The sample Visual Basic handler, Msdfmap, has ADO event handlers, but they are commented out. See the "Events" class.

Error Strings and Descriptions

When working with a custom handler object, the DataFactory object might generate one of the errors described in the following table. The error messages were changed in MDAC 2.1.

RDS 2.0 error string | RDS 2.1 error string | Explanation
A Handler is required and one was not specified. | This page failed due to a server error. The server requires that a Remote Data Service Handler be specified when accessing this page. No handler has been specified. Please see the server administrator to update the page to use a handler. | If the registry is marked to require that a handler be used with RDS requests and no default handler is specified, all requests that do not include the "Handler=" tag will receive this error.
The specified Handler is not marked safe. | This page failed due to a server error. The server does not recognize this Remote Data Service Handler as a safe handler. Please see the server administrator to add the handler to the safe handlers list. | The registry entry for all safe handlers does not include the specified handler.
The specified Handler ProgID was not found. | This page failed due to a server error. The specified Remote Data Service Handler was not found. Please see the server administrator. | The specified handler is not found in the server registry. (CLSIDFromProgID failed.)
The specified Handler was not registered. | This page failed due to a server error. The specified Remote Data Service Handler was not registered. Please see the server administrator. | The specified handler is not found in the server registry. (CoCreateInstance returned REGDB_E_CLASSNOTREG.)
The ProgID specified is not a valid Handler because it has no IDataFactoryHandler interface. | This page failed due to a server error. The progId specified is not a valid Remote Data Service Handler because it has no IDataFactoryHandler interface. Please see the server administrator. | The specified ProgID points to a COM object that does not implement the IDataFactoryHandler interface. (CoCreateInstance returned E_NOINTERFACE.)
The specified Handler has been denied access. | This page failed due to a server error. The server has specifically denied access to the Remote Data Service Handler trying to access this page. Please see the server administrator to grant access to the specified handler. | The handler specified in the connection string returned E_ACCESSDENIED from one of its methods.
N/A | This page failed due to a server error. The server has specifically denied access to the default Remote Data Service Handler trying to access this page. Please see the server administrator to grant access to the default handler. | The default handler returned E_ACCESSDENIED from one of its methods. In this case, a handler was not specified in the connection string.


Saving ADO Recordsets in XML Format

January 2000

By Kamaljit Bath and Dax Hawkins

Summary: Microsoft® ActiveX® Data Objects (ADO) 2.0 allowed you to persist Recordset objects both to and from files in ADTG (Advanced Data Table Gram) format. Using this feature, you could save a Recordset to a disk file and later reopen it from the file. ADO 2.1 extended Recordset persistence by allowing you to persist Recordset objects in XML format, albeit with limited support for XML. ADO 2.1 did not support persistence of hierarchical Recordset objects or persistence of Recordset objects with pending changes.

For ADO versions 2.5 and later, some of these restrictions on XML support have been removed. Furthermore, ADO now allows persistence of both the XML and ADTG formats to any object that supports the COM IStream interface, such as the ADO Stream object, the IIS 5.0 Response object, and the XML DOM Document object. Additionally, this release provides built-in integration between ADO and Active Server Pages (ASP) in Internet Information Server (IIS) 5.0, and between Remote Data Service (RDS) and ASP.

This paper includes the following sections:

  • ADO Recordset Persistence Describes the feature in brief and how you can take advantage of it to write scalable distributed applications. Includes some code examples.
  • XML Persistence Format Describes the XML format generated by ADO in detail to help developers understand the format so that they can transform it using XSL/DOM or other techniques.
  • Integration with ASP Describes how ADO can be used with ASP to generate XML directly from the server.
  • XSL Transformations Describes how XSL can be applied to generated XML.
  • Integration with DOM Describes how the XML document object model can be used to manipulate the generated XML.
  • Security Considerations Describes security issues that affect the behavior of a Recordset used in a control in an Internet zone.
  • Appendix Contains troubleshooting information and the definition of urn:schemas-microsoft-com:rowset.

The paper assumes that you are familiar with Microsoft Visual C++®, Microsoft Visual Basic®, ADO, OLE DB, COM, and XML.


ADO Recordset Persistence

Recordset persistence is implemented in ADO with the Microsoft OLE DB Persistence Provider. This provider generates a forward-only, read-only rowset from a saved XML file or stream that contains the schema information generated by ADO. Similarly, it can take an ADO Recordset, generate XML, and save it to a file or any object that implements the IStream interface. (In fact, a file is just another example of an object that supports IStream.) For versions 2.5 and later, ADO relies on the Microsoft XML parser to load the XML into the Recordset; therefore msxml.dll is required. For version 2.5, msxml shipped with Internet Explorer 5. For version 2.6, msxml shipped with SQL Server 2000.

ADO 2.0 and 2.1 allowed Recordset persistence into flat files only. With ADO 2.5 and later, Recordset objects can be persisted into any object that implements the IStream interface. One such object is the ADO Stream object. If you write an IStream implementation to persist and load from, you do not need to support asynchronous fetching or be free threaded. Recordset objects cannot be loaded in asynchronous mode when loading from an IStream object.

The easiest way to persist data into XML and load it back again is through ADO. The following ADO code example demonstrates saving the data in the Titles table to a file named Titles.sav.

Dim rs as new Recordset
Dim rs2 as new Recordset
Dim c as new Connection
Dim s as new Stream

' Query the Titles table.
c.Open "provider=sqloledb;data source=mydb;initial catalog=pubs;user id=sa;password="
rs.cursorlocation = adUseClient
rs.Open "select * from titles", c, adOpenStatic

' Save to the file in the XML format. Note that if you don't specify
' adPersistXML, a binary format (ADTG) will be used by default.
rs.Save "titles.sav", adPersistXML

' Save the Recordset into the ADO Stream object.
rs.save s, adPersistXML
rs.Close
c.Close

set rs = nothing

' Reopen the file.
rs.Open "titles.sav",,,,adCmdFile
' Open the Stream back into a Recordset.
rs2.open s

ADO always persists the entire Recordset object. If you wish to only persist a subset of rows of the Recordset object, use the Filter method to narrow down the rows or change your selection clause. However, you must open a Recordset object with client-side cursors (cursorlocation = adUseClient) to use the Filter method for saving a subset of rows. For example, to retrieve titles that start with the letter "b," you can apply a filter to an open Recordset object:

rs.Filter "title_id like 'B*'"
rs.Save "btitles.sav", adPersistXML

Note that you can open a Recordset object from a URL as well as a filename:

rs.open http://MyWebServer/XMLSamples/titles.sav,,,,adCmdFile

ADO always loads the XML into a Cursor Engine rowset on a call to the Open method (that is, when the CursorLocation property on the Recordset is set to adUseClient). This allows ADO to surface a scrollable, bookmarkable Recordset object on top of the forward-only rowset generated by the Persistence Provider.

Saving an OLE DB Rowset from C/C++

Recordset persistence is only exposed through ADO, so you cannot call the Save method directly on an OLE DB rowset. Similarly, you cannot open an OLE DB rowset by loading it from a file or stream. The IADORecordsetConstruction interface makes it easy to wrap a rowset with an ADO Recordset object or to get to the rowset from an ADO Recordset object.

To save a rowset, the following function takes a rowset and transfers it to an ADO Recordset object. Then it uses the Save method on the Recordset object to save the rowset to a file.

HRESULT SaveMyRowset(IRowset *pRowset, BSTR bstrFileName)
{
    // Get the Recordset interface.
    IADORecordset * pRs = NULL;
    IADORecordsetConstruction * pADORsCt = NULL;
    HRESULT hr;

    // Get an ADO Recordset and ask for the Recordset construction
    // interface.
    if (FAILED (hr = ::CoCreateInstance(
                            CLSID_ADORecordset,
                            NULL,
                            CLSCTX_INPROC,
                            IID_IADORecordsetConstruction,
                            (LPVOID *) &pADORsCt)))
        return hr;

    // Give the rowset to ADO.
    if (SUCCEEDED (hr = pADORsCt->put_Rowset(pRowset))
    {
        hr = pADORsCt->QueryInterface(IID_IADORecordset, (void **)&pRs);
        // Note that you can pass Save() a variant containing a BSTR
        // (filename), or an object VT_UNKNOWN that supports IStream.
        hr = pRs->Save(vDest, adPersistXML);
        }

    // Cleanup: (or use ATL CComPtr class).
    pRs->Release();
    pADORsCt->Release();

    return hr;
}

Loading OLE DB Rowsets from C/C++

To load a rowset, the following function creates a Recordset object and uses the Open method to open the file. Then it retrieves the rowset out of the Recordset object using the IADORecordsetConstruction interface and returns it to the user.

HRESULT LoadMyFile(BSTR bstrFileName, IRowset **ppRs)
{
    IADORecordsetConstruction * pRsCt = NULL;
    IADORecordset * pADORs = NULL;
    HRESULT hr;

    // Create the ADO Recordset.
    if (SUCCEEDED (hr = ::CoCreateInstance(CLSID_ADORecordset,
                    NULL,
                    CLSCTX_INPROC,
                    IID_IADORecordset,
                    (LPVOID *) &pADORs)))
    {
        VARIANT varEmpty;

        varEmpty.vt = VT_ERROR;
        varEmpty.scode = DISP_E_PARAMNOTFOUND;

        hr = pADORs->Open(bstrFilename, varEmpty, -1,-1,-1);
        hr = pADORs->QueryInterface(IID_IADORecordsetConstruction,
                                (void **)&pRsCt);
        hr = pRsCt->get_Rowset (ppRs);
    }
    pADORs->Release();
    pRsCt->Release();

    return hr;
}

C++ users also have the option of using ADO directly in their programs instead of OLE DB. The "#import" utility in Microsoft Visual C++ 6.0 makes it easy to do that. Refer to the Visual Studio or ADO documentation for more information about #import.


XML Persistence Format

The XML format written into the stream is returned in UTF-8 format. In ADO, this is the only encoding used.

A quick look at the format will show that it is broken into two sections, a schema section followed by the data section. The following is an example XML file for the Shippers table from the Northwind database. Various parts of the XML are discussed following the example.

<xml xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
xmlns:rs="urn:schemas-microsoft-com:rowset"
xmlns:z="#RowsetSchema">
  <s:Schema id="RowsetSchema">
    <s:ElementType name="row" content="eltOnly" rs:updatable="true">
      <s:AttributeType name="ShipperID" rs:number="1"
        rs:basetable="shippers" rs:basecolumn="ShipperID"
        rs:keycolumn="true">
        <s:datatype dt:type="int" dt:maxLength="4" rs:precision="10"
          rs:fixedlength="true" rs:maybenull="false"/>
      </s:AttributeType>
      <s:AttributeType name="CompanyName" rs:number="2"
        rs:nullable="true" rs:write="true" rs:basetable="shippers"
        rs:basecolumn="CompanyName">
        <s:datatype dt:type="string" dt:maxLength="40" />
      </s:AttributeType>
      <s:AttributeType name="Phone" rs:number="3" rs:nullable="true"
        rs:write="true" rs:basetable="shippers"
        rs:basecolumn="Phone">
        <s:datatype dt:type="string" dt:maxLength="24"/>
      </s:AttributeType>
      <s:extends type="rs:rowbase"/>
    </s:ElementType>
  </s:Schema>

  <rs:data>
    <z:row ShipperID="1" CompanyName="Speedy Express"
      Phone="(503) 555-9831"/>
    <z:row ShipperID="2" CompanyName="United Package"
      Phone="(503) 555-3199"/>
    <z:row ShipperID="3" CompanyName="Federal Shipping"
      Phone="(503) 555-9931"/>
  </rs:data>
</xml>

The schema shows the declarations of namespaces, the schema section, and the data section. The schema section contains definitions for row, ShipperID, CompanyName, and Phone.

Schema definitions conform to the XML-Data specification and are able to be fully validated (though validation will not occur in Internet Explorer 5). You can view this specification at http://www.w3.org/TR/1998/NOTE-XML-data/. XML-Data is the only supported schema format for Recordset persistence in the current release.

The data section has three rows containing information about shippers. For an empty rowset, the data section may be empty, but the <rs:data> tags must be present. With no data, you could write the tag shorthand as simply <rs:data/>. Any tag prefixed with "rs" indicates that it is in the namespace defined by urn:schemas-microsoft-com:rowset. The full definition of this schema is defined in the appendix to this document.

Namespaces

The XML persistence format in ADO uses the following four namespaces.

Prefix | Description
s | Refers to the "XML-Data" namespace containing the elements and attributes that define the schema of the current Recordset.
dt | Refers to the data type definitions specification.
rs | Refers to the namespace containing elements and attributes specific to ADO Recordset properties and attributes.
z | Refers to the schema of the current rowset.

A client should not add its own tags to these namespaces as defined by the specification. For example, a client should not define a namespace as "urn:schemas-microsoft-com:rowset" and then write out something like "rs:MyOwnTag." To learn more about namespaces, see http://www.w3.org/TR/REC-xml-names/.

Important The id for the schema tag must be "RowsetSchema" and the namespace used to refer to the schema of the current rowset must point to "#RowsetSchema."

Note that the prefix of the namespace, that part to the right of the colon and to the left of the equal sign, is arbitrary.

xmlns:rs="urn:schemas-microsoft-com:rowset"

The user can define this to be any name as long as this name is consistently used throughout the XML document. ADO always writes out "s", "rs", "dt", and "z," but these prefix names are not hard coded into the loading component.

Schema Section

The schema section is required. As the previous sample shows, ADO writes out detailed metadata about each column to preserve the semantics of the data values as much as possible for updating. However, to load in the XML, ADO only requires the names of the columns and the rowset to which they belong. Here is an example of a minimal schema:

<xml xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
    xmlns:rs="urn:schemas-microsoft-com:rowset"
    xmlns:z="#RowsetSchema">
  <s:Schema id="RowsetSchema">
    <s:ElementType name="row" content="eltOnly">
      <s:AttributeType name="ShipperID"/>
      <s:AttributeType name="CompanyName"/>
      <s:AttributeType name="Phone"/>
      <s:Extends type="rs:rowbase"/>
    </s:ElementType>
  </s:Schema>
  <rs:data>
...
  </rs:data>
</xml>

In the case above, ADO will treat the data as variable length strings since no type information is included in the schema.

Creating Aliases for Column Names

The rs:name attribute allows you to creat and alias for a column name so that a friendly name may appear in the column information exposed by the rowset but a shorter (or less friendly) name may be used in the data section. For example, the schema above could be modified to map ShipperID to s1, CompanyName to s2, and Phone to s3 as follows:

<s:Schema id="RowsetSchema">
<s:ElementType name="row" content="eltOnly" rs:updatable="true">
<s:AttributeType name="s1" rs:name="ShipperID" rs:number="1" ...>
...
</s:AttributeType>
<s:AttributeType name="s2" rs:name="CompanyName" rs:number="2" ...>
...
</s:AttributeType>
<s:AttributeType name="s3" rs:name="Phone" rs:number="3" ...>
...
</s:AttributeType>
...
</s:ElementType>
</s:Schema>

Then, in the data section, the row would use the name attribute, not rs:name, to refer to that column:

"<row s1="1" s2="Speedy Express" s3="(503) 555-9831"/>

Creating aliases for column names is required whenever a column name is not a legal attribute or tag name in XML. For example, "Last Name" must have an alias because names with embedded spaces are illegal attributes. The following line won't be correctly handled by the XML parser, so you must create an alias to some other name that does not have an embedded space.

<row last name="Jones"/>

Whatever value you use for the name attribute (not rs:name) must be used consistently in each place that column is referenced in both the schema and data sections of the XML document. The following example shows the consistent use of "s1":

<s:Schema id="RowsetSchema">
  <s:ElementType name="row" content="eltOnly">
    <s:attribute type="s1"/>
    <s:attribute type="CompanyName"/>
    <s:attribute type="s3"/>
    <s:extends type="rs:rowbase"/>
  </s:ElementType>
  <s:AttributeType name="s1" rs:name="ShipperID" rs:number="1"
    rs:maydefer="true" rs:writeunknown="true">
    <s:datatype dt:type="i4" dt:maxLength="4" rs:precision="10"
      rs:fixedlength="true" rs:maybenull="true"/>
  </s:AttributeType>
</s:Schema>
<rs:data>
  <z:row s1="1" CompanyName="Speedy Express" s3="(503) 555-9831"/>
</rs:data>

Similarly, because there is no alias defined for CompanyName above, CompanyName must be used consistently throughout the document.

Data Types

You can apply a data type to a column with the dt:type attribute. For the definitive guide to allowable XML types, please see http://www.w3.org/TR/1998/NOTE-XML-data-0105/#Datatypes. You can specify a data type in two ways: Either specify the dt:type attribute directly on the column definition itself, or use the s:datatype construct as a nested element of the column definition. For example,

<s:AttributeType name="Phone" >
  <s:datatype dt:type="string"/>
</s:AttributeType>

is equivalent to

<s:AttributeType name="Phone" dt:type="string"/>

If you omit the dt:type attribute entirely from the row definition, by default the column's type will be a variable length string.

If you have more type information than simply the type name (for example, dt:maxLength) it makes it more readable to use the s:datatype child element. This is merely a convention, however, and not a requirement.

The following examples show further how to include type information in your schema:

<!-- 1. String with no max length -->
<s:AttributeType name="title_id"/>
<!—or -->
<s:AttributeType name="title_id" dt:type="string"/>

<!—- 2. Fixed length string with max length of 6 -->
<s:AttributeType name="title_id">
    <s:datatype dt:type="string" dt:maxLength="6" rs:fixedlength="true" />
</s:AttributeType>

<!—- 3. Variable length string with max length of 6 -->
<s:AttributeType name="title_id">
    <s:datatype dt:type="string" dt:maxLength="6" />
</s:AttributeType>

<!—- 4. Integer -->
<s:AttributeType name="title_id" dt:type="int"/>

There is a subtle use of the rs:fixedlength attribute in the second example. A column with the rs:fixedlength attribute set to "true" means that the data must have the length defined in the schema. In this case, a legal value for title_id is "123456," as is "123 ." However, "123" would not be valid because its length is 3, not 6. See the OLE DB specification for a more complete description of the fixedlength property.

Handling Nulls

Null values are handled by the rs:maybenull attribute. If this attribute is set to true, the contents of the column may contain a null value. Furthermore, if the column is not found in a row of data, the user reading the data back from the rowset will get a null status back from IRowset::GetData(). Consider the following column definitions from the Shippers table:

<s:AttributeType name="ShipperID">
  <s:datatype dt:type="int" dt:maxLength="4"/>
</s:AttributeType>
<s:AttributeType name="CompanyName">
  <s:datatype dt:type="string" dt:maxLength="40" rs:maybenull="true"/>
</s:AttributeType>

The definition allows CompanyName to be null, but ShipperID cannot contain a null value. If the data section contained the following row, the Persistence Provider would set the status of data for the CompanyName column to DBSTATUS_S_ISNULL:

<z:row ShipperID="1"/>

If the row was entirely empty, as follows, the Persistence Provider would return a status of DBSTATUS_E_UNAVAILABLE for ShipperID and DBSTATUS_S_ISNULL for CompanyName.

<z:row/>

Note that a zero-length string is not the same as null.

<z:row ShipperID="1" CompanyName=""/>

For the previous row, the Persistence Provider will return DBSTATUS_S_OK for both columns. The CompanyName in this case is simply "" (a zero-length string).

For further information about the OLE DB constructs available for use within the schema of an XML document for OLE DB, please refer to the definition of "urn:schemas-microsoft-com:rowset" and the OLE DB specification.

Data Section

The data section defines the data of the rowset along with any pending updates, insertions, or deletions. The data section may contain zero or more rows. The data section may only contain data from one rowset where the row is defined by the schema. Also as noted above, columns without any data may be omitted. If an attribute or sub-element is used in the data section and that construct has not been defined in the schema section, it is silently ignored.

String

Reserved XML characters in text data must be replaced with appropriate character entities. For example, in the company name "Joe's Garage," the single quote character must be replaced by an entity. The actual row would look like:

<z:row CompanyName="Joe&apos;s Garage"/>

The following characters are reserved in XML and must be replaced by character entities: {',",&,<,>}.

Binary

Binary data is bin.hex encoded (that is, one byte maps to two characters, one character per nibble).

DateTime

The variant VT_DATE format is not directly supported by XML-Data data types. The correct format for dates with both a data and time component is yyyy-mm-ddThh:mm:ss.

For more information about date formats specified by XML, please refer to http://www.w3.org/TR/1998/NOTE-XML-data-0105/#Datatypes.

When the XML-Data specification defines two equivalent data types (for example, i4 == int), ADO will write out the friendly name but read in both.

Managing Pending Changes

A Recordset can be opened in immediate or batch update mode. When opened in batch update mode with client-side cursors, all changes made to the Recordset are in a pending state until the UpdateBatch method is called. Pending changes are also persisted when the Recordset is saved. In XML, they are represented by the use of the "update" elements defined in urn:schemas-microsoft-com:rowset. In addition, if a rowset can be updated, the updateable property must be set to true in the definition of the row. For example, to define that the Shippers table contains pending changes, the row definition would look like the following:

<s:ElementType name="row" content="eltOnly" updatable="true">
  <s:attribute type="ShipperID"/>
  <s:attribute type="CompanyName"/>
  <s:attribute type="Phone"/>
  <s:extends type="rs:rowbase"/>
</s:ElementType>

This tells the Persistence Provider to surface a rowset that supports IRowsetUpdate and IRowsetChange so that ADO can construct an updateable Recordset object.

The following sample data shows how insertions, changes, and deletions look in the persisted file:

<rs:data>
  <z:row ShipperID="2" CompanyName="United Package"
    Phone="(503) 555-3199"/>
<rs:update>
  <rs:original>
    <z:row ShipperID="3" CompanyName="Federal Shipping"
      Phone="(503) 555-9931"/>
  </rs:original>
  <z:row Phone="(503) 552-7134"/>
</rs:update>
<rs:insert>
  <z:row ShipperID="12" CompanyName="Lightning Shipping"
    Phone="(505) 111-2222"/>
  <z:row ShipperID="13" CompanyName="Thunder Overnight"
    Phone="(505) 111-2222"/>
  <z:row ShipperID="14" CompanyName="Blue Angel Air Delivery"
    Phone="(505) 111-2222"/>
</rs:insert>
<rs:delete>
  <z:row ShipperID="1" CompanyName="Speedy Express" Phone="(503) 555-9831"/>
</rs:delete>
</rs:data>

An update always contains the entire original row data followed by the changed row data. The changed row may contain all of the columns or only those columns that have actually changed. In the previous example, the row for Shipper 2 is not changed, while only the Phone column has changed values for Shipper 3 and is therefore the only column included in the changed row. The inserted rows for Shippers 12, 13, and 14 are batched together under one rs:insert tag. Note that deleted rows may also be batched together, though not shown above.

Hierarchical Recordsets in XML

ADO allows persistence of hierarchical Recordset objects into XML. With hierarchical Recordset objects, the value of a field in the parent Recordset is another Recordset. Such fields are represented as child elements in the XML stream rather than an attribute. The following example demonstrates this case:

Rs.Open "SHAPE {select stor_id, stor_name, state from stores} APPEND ({select stor_id, ord_num, ord_date, qty from sales} AS rsSales RELATE stor_id TO stor_id)", "Provider=MSDataShape;DSN=pubs;UID=sa;PWD=;"

The following is the XML format of the persisted Recordset:

<xml xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"     xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"     xmlns:rs="urn:schemas-microsoft-com:rowset"
    xmlns:z="#RowsetSchema">
  <s:Schema id="RowsetSchema">
    <s:ElementType name="row" content="eltOnly" rs:updatable="true">
      <s:AttributeType name="stor_id" rs:number="1"
        rs:writeunknown="true">
        <s:datatype dt:type="string" dt:maxLength="4"
          rs:fixedlength="true" rs:maybenull="false"/>
      </s:AttributeType>
      <s:AttributeType name="stor_name" rs:number="2" rs:nullable="true"
        rs:writeunknown="true">
          <s:datatype dt:type="string" dt:maxLength="40"/>
      </s:AttributeType>
      <s:AttributeType name="state" rs:number="3" rs:nullable="true"
        rs:writeunknown="true">
        <s:datatype dt:type="string" dt:maxLength="2"
          rs:fixedlength="true"/>
      </s:AttributeType>
      <s:ElementType name="rsSales" content="eltOnly"
        rs:updatable="true" rs:relation="010000000100000000000000">
        <s:AttributeType name="stor_id" rs:number="1"
          rs:writeunknown="true">
          <s:datatype dt:type="string" dt:maxLength="4"
            rs:fixedlength="true" rs:maybenull="false"/>
        </s:AttributeType>
        <s:AttributeType name="ord_num" rs:number="2"
          rs:writeunknown="true">
          <s:datatype dt:type="string" dt:maxLength="20"
            rs:maybenull="false"/>
        </s:AttributeType>
        <s:AttributeType name="ord_date" rs:number="3"
          rs:writeunknown="true">
            <s:datatype dt:type="dateTime" dt:maxLength="16"
              rs:scale="3" rs:precision="23" rs:fixedlength="true"
              rs:maybenull="false"/>
        </s:AttributeType>
        <s:AttributeType name="qty" rs:number="4" rs:writeunknown="true">
          <s:datatype dt:type="i2" dt:maxLength="2" rs:precision="5"
            rs:fixedlength="true" rs:maybenull="false"/>
        </s:AttributeType>
        <s:extends type="rs:rowbase"/>
      </s:ElementType>
      <s:extends type="rs:rowbase"/>
    </s:ElementType>
  </s:Schema>
  <rs:data>
    <z:row stor_id="6380" stor_name="Eric the Read Books" state="WA">
      <rsSales stor_id="6380" ord_num="6871"
        ord_date="1994-09-14T00:00:00" qty="5"/>
      <rsSales stor_id="6380" ord_num="722a"
        ord_date="1994-09-13T00:00:00" qty="3"/>
    </z:row>
    <z:row stor_id="7066" stor_name="Barnum's" state="CA">
      <rsSales stor_id="7066" ord_num="A2976"
        ord_date="1993-05-24T00:00:00" qty="50"/>
      <rsSales stor_id="7066" ord_num="QA7442.3"
        ord_date="1994-09-13T00:00:00" qty="75"/>
    </z:row>
    <z:row stor_id="7067" stor_name="News & Brews" state="CA">
      <rsSales stor_id="7067" ord_num="D4482"
        ord_date="1994-09-14T00:00:00" qty="10"/>
      <rsSales stor_id="7067" ord_num="P2121"
        ord_date="1992-06-15T00:00:00" qty="40"/>
      <rsSales stor_id="7067" ord_num="P2121"
        ord_date="1992-06-15T00:00:00" qty="20"/>
      <rsSales stor_id="7067" ord_num="P2121"
        ord_date="1992-06-15T00:00:00" qty="20"/>
    </z:row>
...
  </rs:data>
</xml>

The exact order of the columns in the parent Recordset is not obvious when it is persisted in this manner. Any field in the parent may contain a child Recordset. The Persistence Provider persists out all scalar columns first as attributes and then persists out all child Recordset "columns" as child elements of the parent row. The ordinal position of the field in the parent Recordset can be obtained by looking at the schema definition of the Recordset. Every field has an OLE DB property, rs:number, defined in the Recordset schema namespace that contains the ordinal number for that field.

The names of all fields in the child Recordset are concatenated with the name of the field in the parent Recordset that contains this child. This is to ensure that there are no name collisions in cases where parent and child Recordsets both contain a field that is obtained from two different tables but is named singularly.

When saving hierarchical Recordsets into XML, you should be aware of the following restrictions in ADO:

  • A hierarchical Recordset with pending updates cannot be persisted into XML.
  • A hierarchical Recordset created with a parameterized shape command cannot be persisted (in either XML or ADTG format).
  • ADO currently saves the relationship between the parent and the child Recordsets as a binary large object (BLOB). XML tags to describe this relationship have not yet been defined in the rowset schema namespace.
  • When a hierarchical Recordset is saved, all child Recordsets are saved along with it. If the current Recordset is a child of another Recordset, then its parent is not saved. All child Recordsets that form the subtree of the current Recordset on are saved.

When a hierarchical Recordset is reopened from its XML-persisted format, you must be aware of the following limitations:

  • If the child record contains records for which there are no corresponding parent records, these rows are not written out in the XML representation of the hierarchical Recordset. Thus, these rows will be lost when the Recordset is reopened from its persisted location.
  • If a child record has references to more than one parent record, then on reopening the Recordset, the child Recordset may contain duplicate records. However, these duplicates will only be visible if the user works directly with the underlying child rowset. If a chapter is used to navigate the child Recordset (that is the only way to navigate through ADO) then the duplicates are not visible.

Recordset Properties in XML

The following Recordset properties are currently persisted into the XML format:

  • Update Resync
  • Unique Table
  • Unique Schema
  • Unique Catalog
  • Resync Command
  • IRowsetChange
  • IRowsetUpdate
  • CommandTimeout
  • BatchSize
  • UpdateCriteria
  • Reshape Name
  • AutoRecalc

These properties are saved in the schema section as attributes of the Element definition for the Recordset being persisted. These attributes are defined in the rowset schema namespace and must have the prefix "rs:".


Integration with ASP

In IIS 5.0 (available with Windows® 2000 Server), ADO Recordset objects can be directly persisted to or from the ASP response and request objects. Use ASP on Web servers to dynamically generate XML from ADO Recordsets. The following example shows how this can be done:

<%@ codepage="65001" %>
<object id="rstCustomers" progid="ADODB.Recordset"
    runat="Server"></object>
<!--metadata name="Microsoft ActiveX Data Objects 2.5 Library"
type="TypeLib" uuid="{00000205-0000-0010-8000-00AA006D2EA4}"-->
<%
    Option Explicit
    Dim strSQL, strConnection
    Response.ContentType = "text/xml"
    strSQL = "SELECT CustomerID, CompanyName, ContactName FROM Customers ORDER By CompanyName"
    strConnection = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & Server.MapPath("NWind.mdb")
rstCustomers.CursorLocation = adUseClient
rstCustomers.Open strSQL, strConnection, adOpenStatic, adLockOptimistic
    Response.Write "<?xml version="1.0" encoding="ISO-8859-1"?>" & vbCRLF
    rstCustomers.save response, adPersistXML
    rstCustomers.Close
%>

As shown, the Recordset can be directly saved to the ASP response object, thereby avoiding any expensive disk I/O operations. When this ASP page is opened in a user's browser, the XML representing the ADO Recordset is sent and displayed directly in the browser. ASP has extended its response object to expose an IStream interface directly over the buffered stream.


XSL Transformations

XSL can be applied to the generated XML to transform it into another format. Understanding the XML format in ADO helps in developing XSL templates that can transform it into a more user-friendly form.

For example, you know that each row of the Recordset is saved as the "z:row" element inside the "rs:data" element. Similarly, each field of the Recordset is saved as an attribute-value pair for this element.

The following XSL script can be applied to the XML generated by the ASP page shown in the previous section to transform it into an HTML table to be displayed in the browser:

<?xml version="1.0" encoding="ISO-8859-1"?>
<html xmlns:xsl="http://www.w3.org/TR/WD-xsl">
<body STYLE="font-family:Arial, helvetica, sans-serif; font-size:12pt; background-color:white">
<table border="1" style="table-layout:fixed" width="600">
  <col width="200"></col>
  <tr bgcolor="teal">
    <th><font color="white">CustomerId</font></th>
    <th><font color="white">CompanyName</font></th>
    <th><font color="white">ContactName</font></th>
  </tr>
<xsl:for-each select="xml/rs:data/z:row">
  <tr bgcolor="navy">
    <td><font color="white"><xsl:value-of select="@CustomerID"/></font></td>
    <td><font color="white"><xsl:value-of select="@CompanyName"/></font></td>
    <td><font color="white"><xsl:value-of select="@ContactName"/></font></td>
  </tr>
</xsl:for-each>
</table>
</body>
</html>

The XSL converts the XML stream generated by the ADO Save method into an HTML table which displays each field of the Recordset along with a table heading. Table headings and rows also are assigned different fonts and colors.


Integration with DOM

Just as an ADO Recordset can be saved directly into the ASP Response object, it can also be saved directly into the XML DOM Document object. This avoids having to save the Recordset into an intermediate stream and then loading that stream into the DOM. The following program shows how DOM can be used to completely modify XML generated by the ADO Save method:

Private Sub Command1_Click()
    Dim rsXML As New ADODB.Recordset
    Dim sSQL As String, sConn As String

    sSQL = "SELECT customerid, companyname, contactname FROM customers"
    sConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\Program Files\Common Files\System\msadc\samples\NWind.mdb"
    rsXML.CursorLocation = adUseClient
    rsXML.Open sSQL, sConn, adOpenStatic

    Dim xDOM As New MSXML.DOMDocument
    Dim x2DOM As New MSXML.DOMDocument
    Dim dataNodeList As IXMLDOMNodeList
    Dim dataNode, rowNode, newNode, newRoot, newNode2 As IXMLDOMNode

    rsXML.Save "C:\temp\rsDOM.xml", adPersistXML
    ' Save Recordset directly into a DOM tree.
    rsXML.Save xDOM, adPersistXML
    Set dataNodeList = xDOM.getElementsByTagName("rs:data")
    Set dataNode = dataNodeList.Item(0)

    ' Create a new Node under new DOM.
    Set newRoot = x2DOM.createNode(NODE_ELEMENT, "recordset", "")
    ' Append root node.
    Set newRoot = x2DOM.appendChild(newRoot)

    i = 0
    ' Loop through all z:row nodes.
    While i < dataNode.childNodes.length
        ' Get to the z:row node.
        Set rowNode = dataNode.childNodes.Item(i)

        Set newNode = x2DOM.createNode(NODE_ELEMENT, "row", "")
        ' Append row node under new root.
        Set newNode = newRoot.appendChild(newNode)

        j = 0
        ' Loop through all attributes of z:row.
        While j < rowNode.Attributes.length

            ' Create new element for column name.
            Set newNode2 = x2DOM.createNode(NODE_ELEMENT, rowNode.Attributes.Item(j).baseName, "")
            ' Assign column value to new node value.
            newNode2.Text = rowNode.Attributes.Item(j).Text

            ' Append new column node under new node.
            Set newNode2 = newNode.appendChild(newNode2)

            j = j + 1
        Wend

        i = i + 1
    Wend

    x2DOM.Save "C:\temp\rsCustData.xml"
End Sub


Security Considerations

The ADO Save and Open methods on the Recordset object are not considered safe operations to run in Internet Explorer. Thus, if these methods are used in a script code that is running in an application or control that is hosted in a browser, the security configuration of the browser will have an effect on its behavior.

Internet Explorer 5 provides security restrictions for such operations by default in the Internet zones. Under that configuration, the Recordset cannot make any access to the local file system on the client or access any data sources outside the domain of the server from which the page has been downloaded. Specifically, when running inside the browser host, a Recordset can be saved back to a file only if it is on the same server from which the page was downloaded. Similarly, you can open a Recordset by loading it from a file only if that file is on the same server from which the page was downloaded.

For more information about security in Internet Explorer, see the technical article ADO and RDS Security Issues in Microsoft Internet Explorer.


Appendix

Troubleshooting Q&A

Q: I can save the Recordset into XML, but I can't read it back in using ADO. I keep receiving errors such as "Class not registered" or "The application requested an operation on an object with a reference to a closed or invalid connection object" or "Unable to find the specified provider." What am I doing wrong?

A: Check to make sure that both Msdaprst.dll and Msxml.dll are on your computer and registered. Be sure to use the adCmdFile flag when opening the Recordset so that ADO can give you a better runtime error in case the file is missing or corrupt. That is, instead of writing

rs.Open "myfile.sav"

try

rs.Open "myfile.sav",,,,adCmdFile.

Q: I registered the required files, made sure my file is present, and now I receive "Unspecified error" when trying to open the Recordset. Now what?

A: Remember that the Persistence Provider requires the XML parser in Msxml.dll to be version 2.5 (from Internet Explorer 5) or later. Most likely the Persistence Provider can't load the XML parser because it is requesting a version that is not on your computer.

Q: I saved the file but when I try to look at it using WordPad, it's all garbage! What is wrong with the XML format?

A: Remember that the default format for Recordsets when saving is adPersistADTG, which is a binary format. Be sure to save with adPersistXML.

Q: I know that I am persisting into XML, but some of my text seems unreadable now (especially my umlauts and accent characters).

A: The XML written out is UTF-8 encoded. This means that characters greater than 0x007f are encoded so that they may be safely transmitted over the wire. Also remember that characters reserved by XML are substituted with character entities. See the section on string data types earlier in this paper.

Q: Why can't the Internet Explorer 5 XML parser validate the XML as generated by ADO?

A: The "extends" clause has been added so that rowset schemas could be legally validated. The row definition must extend the rs:rowbase element defined by Microsoft's rowset schema by including the <s:extends type="rs:rowbase"/> construct. However, the shipped version of the Microsoft XML parser doesn't support the "extends" clause, so this is now redundant and actually fails on validation.

Definition of urn:schemas-microsoft-com:rowset

<s:Schema xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
    xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882">
    <!-- rowset level properties -->
    <s:AttributeType name="updatable" dt:type="boolean"/>
    <s:attribute type="updatable"/>
    <s:AttributeType name="updateresync" dt:type="i4"/>
    <s:attribute type="updateresync"/>
    <s:AttributeType name="uniquetable" dt:type="string"/>
    <s:attribute type="uniquetable"/>
    <s:AttributeType name="uniqueschema" dt:type="string"/>
    <s:attribute type="uniqueschema"/>
    <s:AttributeType name="uniquecatalog" dt:type="string"/>
    <s:attribute type="uniquecatalog"/>
    <s:AttributeType name="customresync" dt:type="string"/>
    <s:attribute type="customresync"/>
    <s:AttributeType name="irowsetchange" dt:type="boolean"/>
    <s:attribute type="irowsetchange"/>
    <s:AttributeType name="irowsetupdate" dt:type="boolean"/>
    <s:attribute type="irowsetupdate"/>
    <s:AttributeType name="commandtimeout" dt:type="i4"/>
    <s:attribute type="commandtimeout"/>
    <s:AttributeType name="batchsize" dt:type="i4"/>
    <s:attribute type="batchsize"/>
    <s:AttributeType name="updatecriteria" dt:type="i4"/>
    <s:attribute type="updatecriteria"/>
    <s:AttributeType name="reshapename" dt:type="string"/>
    <s:attribute type="reshapename"/>
    <s:AttributeType name="autorecalc" dt:type="i4"/>
    <s:attribute type="autorecalc"/>

    <!-- column level properties -->
    <s:AttributeType name="autoincrement" dt:type="boolean"/>
    <s:AttributeType name="baseschema" dt:type="string"/>
    <s:AttributeType name="basetable" dt:type="string"/>
    <s:AttributeType name="basecolumn" dt:type="string"/>
    <s:AttributeType name="basecatalog" dt:type="string"/>
    <s:AttributeType name="number" dt:type="i4"/>
    <s:AttributeType name="name" dt:type="string"/>
    <s:AttributeType name="precision" dt:type="ui1"/>
    <s:AttributeType name="scale" dt:type="ui1"/>
    <s:AttributeType name="fixedlength" dt:type="boolean"/>
    <s:AttributeType name="long" dt:type="boolean"/>
    <s:AttributeType name="nullable" dt:type="boolean"/>
    <s:AttributeType name="maybenull" dt:type="boolean"/>
    <s:AttributeType name="keycolumn" dt:type="boolean"/>
    <s:AttributeType name="writeunknown" dt:type="boolean"/>
    <s:AttributeType name="write" dt:type="boolean"/>
    <s:AttributeType name="rowid" dt:type="boolean"/>
    <s:attribute type="number"/>
    <s:attribute type="name"/>
    <s:attribute type="precision"/>
    <s:attribute type="scale"/>
    <s:attribute type="fixedlength"/>
    <s:attribute type="long"/>
    <s:attribute type="nullable"/>
    <s:attribute type="maybenull"/>
    <s:attribute type="keycolumn"/>
    <s:attribute type="write"/>
    <s:attribute type="writeunknown"/>
    <s:attribute type="rowid"/>
    <s:attribute type="baseschema"/>
    <s:attribute type="basetable"/>
    <s:attribute type="basecolumn"/>
    <s:attribute type="basecatalog"/>

    <!-- baserow definition -->
    <s:ElementType name="baserow" content="eltOnly">
        <s:attribute type="updatable"/>
    </s:ElementType>
    <s:element type="baserow"/>

    <!-- update definitions -->
    <s:ElementType name="original" content="eltOnly">
        <s:element type="rowbase" minOccurs="1" maxOccurs="1"/>
    </s:ElementType>
    <s:ElementType name="update" content="eltOnly">
        <s:element type="original" minOccurs="1" maxOccurs="1"/>
        <s:element type="rowbase" minOccurs="1" maxOccurs="1"/>
    </s:ElementType>
    <s:ElementType name="insert" content="eltOnly">
        <s:element type="rowbase" minOccurs="1" maxOccurs="*"/>
    </s:ElementType>
    <s:ElementType name="delete" content="eltOnly">
        <s:element type="rowbase" minOccurs="1" maxOccurs="*"/>
    </s:ElementType>
    <s:element type="original"/>
    <s:element type="update"/>
    <s:element type="delete"/>
    <s:element type="insert"/>

    <!-- data definition -->
    <s:ElementType name="data" content="eltOnly">
        <s:element type="baserow" minOccurs="0" maxOccurs="*"/>
        <s:element type="update" minOccurs="0" maxOccurs="*"/>
        <s:element type="insert" minOccurs="0" maxOccurs="*"/>
        <s:element type="delete" minOccurs="0" maxOccurs="*"/>
    </s:ElementType>
    <s:element type="data"/>
</s:Schema>


OLE DB Technical Articles

  • OLE DB Security
  • Optimistic Remote Updating in the Client Cursor Engine
  • OLE DB for the ODBC Programmer
  • OLE DB Minimum Levels of Consumer and Provider Functionality (formerly titled "OLE DB Leveling")
  • Mapping ADO Methods to OLE DB Interfaces
  • Microsoft OLE DB Provider for Oracle: Tips, Tricks, and Traps


OLE DB Security

Security Administration Interfaces for OLE DB Providers

June 1999

Microsoft Corporation

Summary: This guide documents the optional OLE DB interfaces that can be implemented to support the administration of security information in a data store. These interfaces are used by Microsoft® ActiveX® Data Objects Extensions for Data Definition Language and Security (ADOX), versions 2.1 or later.

You can add support for security administration interfaces to your OLE DB data provider by using COM interfaces. When these COM interfaces are implemented, an OLE DB data consumer can manage the security policies enforced on the data store.

This guide does not discuss techniques for enforcing security policies nor does it discuss tools. It focuses primarily on the administration of the access-control security policies and includes only a brief discussion on privacy and authentication.

Important Because Microsoft is investigating other possible techniques for data access security administration, the techniques documented here do not represent any officially supported mechanism in OLE DB for administering database security. This guide will be updated when any officially supported mechanism for security administration for OLE DB is released.

Contents

  • Introduction
  • Programmer's Guide
    • Overview of Security Administration
    • Providing General Security Information Using ISecurityInfo
    • Trustee Administration
    • Access Control Administration
  • Programmer's Reference
    • Interfaces
    • Property Groups and Properties
    • Structures
    • Constants
    • Schemas
  • Appendix: IObjectAccessControl vs. IAccessControl


Introduction to OLE DB Security

MDAC Technical Articles

Introduction

This guide was created for developers who write OLE DB data providers. It could also be useful for developers writing OLE DB consumers and using a language, such as Microsoft® Visual C++®, that supports COM custom interfaces. It is assumed that the reader is knowledgeable in the following areas:

  • Visual C++
  • OLE DB
  • COM
  • Microsoft Windows NT®/Windows® 2000 security

Implementing security in a data provider comprises two distinct processes. The first process, called security administration, defines the security policy. The second process is the enforcement of that security. This guide covers only the first process.

Security administration is the process of defining the intended security policies of the data store so that unauthorized access to the data can be prevented. This is done by creating trustee objects that represent the users that will access the data and by setting properties of existing database objects to restrict access to certain users. The security administration process also includes specifying the communication protocol between the data provider and the data store.

Security administration is usually performed by one of two common methods. The first method is to use the tools created by the vendor that created the data store. For example, Microsoft Access manages security administration for Microsoft Jet databases, and the Enterprise Manager does the same for Microsoft SQL Server® 7.0. This is often the preferred method, because these vendor-created tools provide useful interfaces specifically tailored to their products.

The second method, which is the focus of this guide, is to write programs using an application programming interface (API) for security administration. For example, the Data Access Objects (DAO) library provides automation interfaces for Microsoft Jet databases, and the SQL Distributed Management Objects (DMO) library does likewise for Microsoft SQL Server 7.0. In cases where a software component must manage security on a variety of data source objects, this second method is preferred over using product-specific tools. This guide shows how to support programmatic security administration by implementing some COM interfaces in your OLE DB data provider.

Note Another programmatic technique uses SQL Data Definition Language (DDL) security statements, such as GRANT and REVOKE, and schema rowsets to administer security settings. This technique is fundamentally the same as supporting ordinary SQL DDL statements through OLE DB command objects and therefore is not documented in this guide.

The interfaces that the data provider will implement are used by the ActiveX Data Objects Extensions for Data Definition Language and Security (ADOX) library. Client programs using ADOX will probably be the most common consumers to request these interfaces from your data provider. When you have finished implementing the interfaces described in this guide, you can use ADOX to test your data provider.

All data in the data source object, as well as the users of that data, are represented by objects in the security administration process. This guide uses the general term database objects to refer to objects such as tables, views, queries, stored procedures, schemas, or whatever encapsulated objects in the data source object are used to store data for the provider. Normally, the provider partially conceals these objects from the consumer. However, for security administration, the consumer must know exactly how they are organized and identified.

In this guide, the term trustee is used to mean the users, groups, and roles under whose security context an application or component runs. Proper security administration of a data store will describe the trustees of the data, the database objects those trustees can access, and how the data is sent to and from those trustees.

Three parts of security administration must be managed to successfully secure the data:

  • Authorization control sets up properties so that the data provider's security enforcement system can allow or deny access by trustees to the database objects stored in the data source object.
  • Authentication confirms the identity of a trustee.
  • Privacy, or connection security, prevents unauthorized trustees from observing the data moving between a trustee and the data source object.

When all three of the preceding parts are properly implemented by the data provider, the data in the data source object can be completely secured from unauthorized access.

While briefly covering authentication and privacy, this guide primarily addresses authorization control and shows how to implement the interfaces needed to administer access control for consumers like ADOX. For complete information on making authentication and privacy properties available from your data provider, see Chapter 14: Properties, in the OLE DB Programmer's Reference. Although the OLE DB documentation found in the MDAC SDK does not address the administration of authorization control, it does provide for authentication and privacy management using standard OLE DB interfaces and property sets.


Programmer's Guide

This section of the OLE DB security administration guide explains the concepts and techniques involved in implementing the OLE DB security interfaces and includes the following topics:

  • Overview of Security Administration
  • Providing General Security Information Using ISecurityInfo
  • Trustee Administration
  • Access Control Administration

Important Before you decide to implement the security interfaces discussed in this guide, consider that the suggested techniques for OLE DB security administration are very likely to change. Also, if the data store contains relational data and the provider already implements the ICommand interfaces to parse SQL statements, you could easily add support for SQL DDL statements, such as GRANT and REVOKE, to control security settings. However, if the data store contains nonrelational data, if the provider doesn't implement ICommand, or if the data store must be available for use by ADOX, you should implement the interfaces described in this guide—but be prepared to port your code to a new implementation when Microsoft finalizes the design to replace this mechanism.


Overview of Security Administration

All security implementations work with essentially two types of entities. One type, the trustee, represents the users of the data as well as the applications that these users run. The other type, the database object, represents the data in the data source object. Database objects encapsulate objects, tables, views, and so on.

A trustee can be a user account, group account, or role, and are defined as follows:

  • User accounts include accounts that either human users or programs, such as Win32® services, use to log on to a workstation. A user account usually represents a single person, although it is possible for more than one person to use the same user account. Individual users can be members of any number of groups or roles.
  • Group accounts contain user accounts and other group accounts. They cannot be used to log on to a workstation but are useful when allowing or denying access rights to several user accounts at once.
  • Roles, like groups, also contain user and group accounts. However, while groups are hierarchical in design, roles can describe common functions either across or within hierarchical organizations. Therefore, roles are useful for specifying access rights across organizational boundaries.

Database objects are discrete entities that contain and expose data within the data store. Database objects can include tables, views, columns, stored procedures, forms, modules, queries, reports, object containers, schemas, and so on. Some providers might have the option of working with database object types rather than with specific objects. For example, the data source object might allow a provider to set access control properties for tables in general rather than requiring identification of a particular table.

Administering the security of the data source object consists of the following parts:

  • Authorization control permits or denies trustees access to specific database objects within the data source object.
  • Authentication verifies that a trustee is the actual trustee that the security administrator expects.
  • Privacy prevents unauthorized users from seeing communications between an authorized user and the data source object.

The three parts are independent of one another, but as a group, they determine the overall security of the data in the data source object. Each part can be implemented at a different security level than the others. For example, you could choose a strong implementation of authentication and a weak implementation of privacy. Such an approach would prevent unauthorized users from initializing the data source object, but it might allow sensitive data to be sent openly across a network, viewable by anyone with the right tools. Therefore, for the best security, you should choose implementations of each part that match the strength of the other parts. The security of the whole data source object is only as strong as the weakest of the security parts.

There are several ways to implement each of these areas of security administration. The following sections examine some of the available options for each part:

  • Authorization Control
  • Authentication
  • Privacy


Authorization Control

Authorization control is the process of controlling which users are allowed access to various database objects and the degree of access they can have. Typically, this is achieved by identifying all the trustees and the permissions they need when working with the data. The trustees are usually the same users, groups, and roles that are used for authentication. Common administration tasks for trustees include adding, modifying, and deleting users and groups.

Before you can set the permission properties for them, the trustees must be identifiable by the data provider. Trustees are identified by either a unique name or a security identifier. The data store should have some way to store information about each of the trustees within a collection. Normally, that information does not have any access control information stored with it but merely uniquely identifies the trustee. However, a trustee can have properties associated with it, such as a readable name and password.

Although the collection of trustees doesn't have any access control information stored with it, the database objects do. Each object has a list of access control structures. These access control structures pair a trustee identifier with a list of flags that either allow or deny access to the data. Whenever a trustee attempts to access a database object, the data provider uses the trustee's identifier to find the trustee in the list of access control structures associated with the object. If there is no list, all trustees can access the object. If there is a list and the trustee's identifier is not found in the list, access is denied to the trustee.

A trustee's identifier might exactly match an identifier in the list of trustees, or it could be found as a member of one or more trustees that represent groups. In the latter case, the provider gathers a collection of all the access control structures that apply to all the trustee identifiers. From this collection, all the flags that deny access are put into one collection and all the flags that allow access are put into another collection. Then the list of flags that deny access is examined for the particular action the trustee requested permission to perform. Any flags that deny access for the action cause the request to fail. If access is not denied by any of these flags, all the flags that allow access are examined. If no flags that allow the requested action are found, the request is denied. Otherwise, the request is allowed and the action is performed.

Note As the developer of the data provider, you might not have to implement the actual enforcement of the security as described above. This would be the case, for example, if your provider uses an external database engine library to work with the database objects in the data store and if that library enforces the security internally. However, if the database engine library does not enforce security or if the provider works directly with the database objects in the data store, you will need to programmatically implement the security control.

To create access control levels, various permission properties are available for each database object or type of database object. Each database object or type of database object is associated both with trustees and with access control structures. The access control structures are lists of permissions, sometimes called rights or access-control lists, that either allow or deny certain kinds of actions, such as reading, writing, or deleting objects.

Common access control administration tasks for objects in the data store include adding, modifying, and deleting access control structures for each database object or each kind of database object in the data store. The access control structures are used to store flags for the database object that allow or deny access to trustees.

Note All security violations are treated as normal errors within the data provider. No additional interfaces are defined in this guide to handle errors caused by security violations.

The OLE DB security interfaces described in this guide are meant to provide a standard way for consumers such as ADOX to manage the lists of trustees, trustee identifiers, and access control structures for all the database objects in any data store. You might have to translate error notifications for security violations in the data source object into the appropriate OLE DB error notification, as documented in Chapter 16, "Errors," of the OLE DB Programmer's Reference.


Authentication

Authentication is the process of verifying that a user with a unique name is the actual user that the name implies. This verification process is also applied to applications running in the security context of a user. Typically, authentication is implemented by giving the user a password.

Authentication of trustees can be accomplished in either of two ways. One way is to associate a trustee name with a password stored with the data store and require both name and password on the initial connection to the data source object. The second and preferred way is to use secure access tokens granted by the operating system only to authentic users. These access tokens contain a security identification descriptor (SID) that can be matched against a discretionary access control list stored in the data store. Although this is generally the preferred and most secure method, it can be more difficult to implement than the first method.

With Microsoft® Windows NT®/Windows® 2000, the data provider can authenticate trustees by using the Windows NT LAN Manager (NTLM) Security Support Provider Interface (SSPI). With Windows 2000, the provider might use the Kerberos security protocol instead of SSPI. If the provider can use SSPI or Kerberos security, the provider should implement the DBPROP_AUTH_INTEGRATED property. (A full discussion of securing objects with SSPI or Kerberos is beyond the scope of this guide. For more information, see the Access Control section of the Microsoft Platform SDK.)

The alternative to using Windows NT/Windows 2000 security is any proprietary technique for storing authorized users and groups. If the data provider uses such a technique, merely support the appropriate DBPROP_AUTH* properties in the IDBInitialize interface. The properties you support depend on the data source object. For more information on implementing the IDBInitialize interface, see IDBInitialize and OLE DB Properties in the OLE DB Programmer's Reference.


Privacy

Privacy, also called connection security, is the process of preventing unauthorized users from intercepting data being passed between authorized users and the data source object. This process also includes ensuring that no unauthorized users or applications tamper with the data. Normally, this means encrypting data as it is transported across the network but can also include other methods to check the data's integrity.

Because encrypting all data transported consumes significant processing time, the most efficient implementations encrypt only selected parts of the data. If DBPROP_AUTH_INTEGRATED is set to VT_EMPTY and DBPROP_AUTH_ENCRYPT_PASSWORD is set to VARIANT_TRUE, this means encrypting the password used to authenticate a user. And whatever the setting of the DBPROP_AUTH* properties, it might be necessary to encrypt particularly sensitive fields being sent to or from the data store, such as Social Security numbers and credit card numbers.

Regardless of which parts of the data must be kept private, the technique used by the data provider to communicate with the data store is predetermined by the vendor. It is not defined here. There are no interfaces defined in OLE DB at this time to manage encryption of the data between the data store and the data provider.


Providing General Security Information Using ISecurityInfo

The data provider implements the ISecurityInfo interface so that consumers can discover some general information about the security in the data store. The ISecurityInfo interface has the following methods that allow the consumer to identify the current trustee, to determine which types of database objects support security, and to determine which permissions are available for those objects:

  • ISecurityInfo::GetCurrentTrustee This method allows the consumer to identify the trustee currently connected to the data source object. Knowing the current trustee is important when performing some trustee administration and access control administration tasks. The data provider responds to GetCurrentTrustee by allocating memory for a TRUSTEE structure and returning a pointer to the structure. The consumer must free the structure when it is finished using it.
  • ISecurityInfo::GetObjectTypes This method allows the consumer to determine which types of database objects in the data store support access control structures for authorization. In response to GetObjectTypes, the data provider creates an array of GUIDs, each of which represents an object that supports access control. These object type GUIDs are listed in the programmer's reference section of this guide. The consumer releases the array when it is finished using it.
  • ISecurityInfo::GetPermissions This method allows consumers to discover the permissions that are available for a given type of object rather than for a specific object. The data provider responds to GetPermissions by filling out an ACCESS_MASK structure passed into the method. Use the ACCESS_MASK constants listed in the programmer's reference section of this guide to examine and change the ACCESS_MASK structure.

Most other useful security information can be determined through the trustee administration and access control administration tasks.


Trustee Administration

The data provider implements the following two interfaces to support administration tasks for trustees:

  • ITrusteeAdmin—Allows the consumer to manage individual trustees.
  • ItrusteeGroupAdmin—Allows the consumer to manage a trustee that represents a group of trustees.

These interfaces use a TRUSTEE structure to identify a trustee. The TRUSTEE structure is documented in the Access Control section of the Microsoft® Platform SDK. The TRUSTEE structure contains the unique identification of the trustee in the ptstrName element. As indicated by the value of the TrusteeForm element, the ptstrName element contains either a unique name or a system-generated SID for the trustee. As indicated by the value of the TrusteeType element, a TRUSTEE structure can represent a single trustee or a group of trustees.

Some Windows® functions aid in working with TRUSTEE structures. BuildTrusteeWithName and BuildTrusteeWithSid both initialize a TRUSTEE structure. GetTrusteeForm, GetTrusteeName, and GetTrusteeType each return the value of the appropriate element from a TRUSTEE structure.

The ITrusteeAdmin and ITrusteeGroupAdmin interfaces also use a new property group to modify information about trustees. The Trustee property group DBPROPSET_TRUSTEE contains three properties that allow the consumer to change the trustee's password and descriptive name. Three ITrusteeAdmin methods, CreateTrustee, GetTrusteeProperties, and SetTrusteeProperties, use the Trustee property group to get and change authentication information.

ITrusteeAdmin Methods

ITrusteeAdmin methods allow the consumer to create, delete, and compare trustees. These methods also allow the consumer to set or modify trustee properties used for authentication, such as passwords and a descriptive user name.

To add a trustee to the data source object, the consumer allocates a TRUSTEE structure and initializes it. The consumer also allocates and initializes an array of DBPROPSET structures, one for each trustee property that it sets. Generally, the consumer uses two structures: one for the trustee's descriptive name and another for the password. Then the consumer calls ITrusteeAdmin::CreateTrustee, and the provider adds the trustee to the data source object. Any errors are returned to the consumer by using the HRESULT.

To remove a trustee from the data source object, the consumer simply allocates a TRUSTEE structure and initializes it. Then the consumer calls ITrusteeAdmin::DeleteTrustee, and the provider removes the trustee from the data store. Any errors are returned to the consumer by using the HRESULT.

To compare two trustees, the consumer allocates two TRUSTEE structures and initializes them. Alternatively, the consumer may use a TRUSTEE structure returned from another method, such as ISecurityInfo::GetCurrentTrustee. Then the consumer calls ITrusteeAdmin::CompareTrustees, and the provider compares the trustees and returns the result of the comparison in the HRESULT, as well as any errors encountered.

To set or modify properties for a trustee, the consumer allocates and initializes both a TRUSTEE structure and an array of DBPROP structures. For example, to change the trustee's password, the consumer allocates two DBPROP structures. The first DBPROP contains a DBPROP_TRUSTEE_AUTHENTICATION property for the trustee's original password, and the second DBPROP contains a DBPROP_TRUSTEE_NEWAUTHENTICATION property for the new password. Then the consumer allocates and initializes a DBPROPSET structure, sets the rgProperties element to point to the array of DBPROP structures, and calls ITrusteeAdmin::SetTrusteeProperties. The provider changes the properties, and any errors are returned to the consumer by using the HRESULT and the dwStatus element of any DBPROP structure that had an error. (For more information about setting property values, see "Setting Property Values" in the OLE DB Programmer's Reference.)

To get the properties for a trustee, the consumer allocates a TRUSTEE structure and initializes it. If the consumer wants all the trustee properties, it can simply call ITrusteeAdmin::GetTrusteeProperties right away. Otherwise, the consumer allocates an array of DBPROPID values, one for each property that it wants to get. Then the consumer allocates and initializes a DBPROPIDSET structure, sets the rgPropertyIDs element in the DBPROPIDSET structure to point to the array of DBPROPID values, sets the cPropertyIDs element to the number of properties in the array, and sets the guidPropertySet element to DBPROPSET_TRUSTEE. The consumer then calls ITrusteeAdmin::GetTrusteeProperties.

The provider gets the properties and allocates an array of DBPROP structures, which it fills in, and a DBPROPSET structure. The provider sets the rgProperties element in the DBPROPSET structure to point to the array of DBPROP structures and returns a pointer to the DBPROPSET structure to the consumer. Any errors are returned to the consumer by using the HRESULT return code and the dwStatus element of any DBPROP structures that had an error. The consumer releases the DBPROPSET structure when it is finished with it.

For more information on getting property values, see "Getting Property Values" in the OLE DB Programmer's Reference.

ITrusteeGroupAdmin Methods

ITrusteeGroupAdmin methods allow the consumer to add a trustee to a group trustee, remove a trustee from the group, and determine whether a specific trustee is a member of a group. These methods also allow the consumer to get the complete list of trustees in a group and to find out which groups a trustee belongs to.

To add one trustee to another trustee that represents a group of trustees, the consumer allocates two TRUSTEE structures and initializes them. One trustee is the member trustee, and the other is the group trustee, also called the membership trustee. (A member trustee may be either a user trustee or a group trustee. Membership trustees are only group trustees; they never have the TrusteeType element in the TRUSTEE structure set to TRUSTEE_IS_USER because a user trustee cannot have members.) Then the consumer calls ITrusteeGroupAdmin::AddMember, and the provider adds the member trustee to the membership trustee. Any errors are returned to the consumer by using the HRESULT.

To remove a trustee from a membership trustee, the consumer allocates two TRUSTEE structures—one member trustee and one membership trustee—and initializes them. Then the consumer calls ITrusteeGroupAdmin::DeleteMember, and the provider removes the member trustee from the membership trustee. Any errors are returned to the consumer by using the HRESULT.

To determine whether a trustee is a member of a membership trustee, the consumer allocates two TRUSTEE structures—one member trustee and one membership trustee—and initializes them. Then the consumer calls ITrusteeGroupAdmin::IsMember, passing a pointer to a BOOL variable. The provider checks to determine whether or not the member trustee is in the membership trustee's collection of trustees and sets the BOOL variable depending on the outcome. Any errors are returned to the consumer by using the HRESULT.

To get a list of the trustees in a membership trustee, the consumer allocates a TRUSTEE structure for a membership trustee and initializes it and then calls ITrusteeGroupAdmin::GetMembers. The provider allocates an array of TRUSTEE structures and initializes them from the membership list of the membership trustee and then returns a pointer to the array of TRUSTEE structures. Any errors are returned to the consumer by using the HRESULT. The consumer releases the array of TRUSTEE structures when it is finished with it.

To get a list of the membership trustees to which a trustee belongs, the consumer allocates a TRUSTEE structure and initializes it and then calls ITrusteeGroupAdmin::GetMemberships. The provider allocates an array of TRUSTEE structures large enough to hold all the membership trustees to which the member trustee belongs and initializes the structures from the collection of trustees. The provider returns a pointer to the array of TRUSTEE structures. Any errors are returned to the consumer by using the HRESULT. The consumer releases the array of TRUSTEE structures when it is finished with it.


Access Control Administration

The final area of security administration that the provider must handle is access control administration. The data provider implements the IObjectAccessControl interface to handle all access control administration tasks.

The IObjectAccessControl interface is similar in purpose to the standard COM interface IAccessControl. However, IObjectAccessControl is somewhat more complicated because many data stores do not expose their database objects as COM objects. For more information about the difference between IObjectAccessControl and IAccessControl, see the Appendix of this guide.

To handle access control tasks, the data provider must be able to do the following:

  1. The data provider must be able to uniquely identify the object on which access control settings are being read or written. The SEC_OBJECT and SEC_OBJECT_ELEMENT structures are used to identify a database object or a type of database object in the data store. The SEC_OBJECT structure contains an array of SEC_OBJECT_ELEMENT structures.
    • Each SEC_OBJECT_ELEMENT structure pairs an object type with an object ID. The object type is always a GUID and is stored in the guidObjectType element. The object ID is a DBID structure and is stored in the ObjectID element. Two special container DBIDs have been defined to handle object containers so that security can also be controlled for new database objects created in the data store, not just for existing objects.
    • The list of SEC_OBJECT_ELEMENT structures in the SEC_OBJECT structure is a hierarchy of objects. The first item identifies the topmost object in the data store, such as a schema; the second item identifies the child object of the first object, such as a table; and so on until the correct object is identified. Used this way, one SEC_OBJECT structure can identify any specific database object or object container.
  2. The data provider must identify the permission settings for the object. The ACCESS_MASK structure controls which permissions are granted or denied on an object for a trustee. Each permission setting controls whether a specific action is allowed or denied. The ACCESS_MASK structure sets aside 16 bits for specific permission settings needed by the object. OLE DB uses 8 of these 16 bits to control specific permission settings on the object. Several PERM* constants are defined to simplify working with the bits in this structure. This structure is documented in the Access Control section of the Microsoft® Platform SDK.
  3. The data provider must associate each ACCESS_MASK with a specific trustee. The EXPLICIT_ACCESS structure is used to pair each trustee with changes to the permissions for an object. The trustee is identified with a TRUSTEE structure in the Trustee element of the EXPLICIT_ACCESS structure. The permissions are in an ACCESS_MASK structure stored in the grfAccessPermissions element. Two additional elements are used to determine the operation and the inheritance characteristics:
    • The grfAccessMode element stores an ACCESS_MODE enumerated value. This structure specifies the operation, which is how the permissions should be applied.
    • The grfInheritance element stores an ACCESS_INHERITANCE bitmask, which specifies how the permissions should be inherited.

EXPLICIT_ACCESS, ACCESS_MASK, ACCESS_MODE, and ACCESS_INHERITANCE are all fully documented in the Access Control section of the Microsoft Platform SDK. Microsoft Windows® includes the BuildExplicitAccessWithName function to assist with initializing an EXPLICIT_ACCESS structure.

IObjectAccessControl Methods

IObjectAccessControl methods can manage access control for database objects. By using these methods, the consumer can set and get the access permissions for a trustee on an object, set and get the owner of an object, and perform an access check.

To set the access permissions on an object, the consumer allocates a SEC_OBJECT structure and initializes it to specify the object. The consumer also allocates and initializes an array of EXPLICIT_ACCESS structures to indicate the trustees, the permissions being applied, and the operations to apply. Then the consumer calls IObjectAccessControl::SetObjectAccessRights, and the provider selects the specified database object in the data store. The provider iterates the array of EXPLICIT_ACCESS structures and sets the indicated permissions on the selected object. Any errors are returned to the consumer by using the HRESULT.

To get the access permissions on an object, the consumer allocates a SEC_OBJECT structure and initializes it to specify the object. To get all the explicit rights for the object, the consumer can call IObjectAccessControl::GetObjectAccessRights right away. Otherwise, the consumer allocates and initializes a TRUSTEE structure and an array of one or two EXPLICIT_ACCESS structures and then initializes the Trustee elements of each to point to the TRUSTEE structure. If the consumer uses only one EXPLICIT_ACCESS structure, it sets the grfAccessMode element to either SET_ACCESS or DENY_ACCESS. If the consumer uses two structures to get the full set of permissions, it sets one structure's grfAccessMode element to SET_ACCESS and the other to DENY_ACCESS and then calls IObjectAccessControl::GetObjectAccessRights.

The provider handles IObjectAccessControl::GetObjectAccessRights by examining the pcAccessEntries parameter, which identifies how many elements are in the array of EXPLICIT_ACCESS structures. If pcAccessEntries is zero, the provider allocates an array of EXPLICIT_ACCESS structures; otherwise, the provider uses the array that was passed to the method by the consumer. In either case, the provider selects the specified object and gathers the access permissions for the object, allocating the array of EXPLICIT_ACCESS structures if necessary. Then the provider sets the grfAccessPermissions and grfInheritance elements in the appropriate EXPLICIT_ACCESS structures. Any errors are returned to the consumer by using the HRESULT, and if errors occurred, any allocated memory is released by the provider. The consumer, if it requested all the rights for the object, releases the array of allocated EXPLICIT_ACCESS structures when it is finished with it.

To set the owner of an object, the consumer allocates a SEC_OBJECT structure and initializes it to specify a database object. The consumer also allocates and initializes a TRUSTEE structure and then calls IObjectAccessControl::SetObjectOwner. The provider sets the trustee as the owner of the specified object. Any errors are returned to the consumer by using the HRESULT.

To get the owner of an object, the consumer allocates a SEC_OBJECT structure, initializes it to specify a database object, and then calls IObjectAccessControl::GetObjectOwner. The provider allocates a TRUSTEE structure, gets the owner of the specified object, and initializes the TRUSTEE structure with the owner of the object. Any errors are returned to the consumer by using the HRESULT. The consumer releases the TRUSTEE structure when it is finished with it.

To perform an access check, the consumer allocates a SEC_OBJECT structure and initializes it to specify a database object. The consumer also allocates and initializes an EXPLICIT_ACCESS structure to indicate the trustee and the permissions being checked. Then the consumer calls IObjectAccessControl::IsObjectAccessAllowed, passing a pointer to a BOOL variable, and the provider determines whether the trustee has the permissions listed in the EXPLICIT_ACCESS structure. In doing so, the provider may include the permissions of any membership trustees to which the trustee belongs. The provider sets the BOOL variable depending on the outcome. Any errors are returned to the consumer by using the HRESULT.


Programmer's Reference

The programmer's reference for the OLE DB security administration guide is divided into the following sections:

  • Interfaces
    • IObjectAccessControl
    • ISecurityInfo
    • ITrusteeAdmin
    • ITrusteeGroupAdmin
  • Property Groups and Properties
  • Structures
  • Constants
  • Schemas


Interfaces

This section of the OLE DB security administration guide defines the methods and arguments of the following interfaces for OLE DB security administration:

  • IObjectAccessControl
  • ISecurityInfo
  • ITrusteeAdmin
  • ITrusteeGroupAdmin


IObjectAccessControl

The IObjectAccessControl interface enables the management of access to objects and containers managed by data providers.

IObjectAccessControl is based on the general form of the COM interface IAccessControl. The main difference between the two interfaces is that IObjectAccessControl requires the object identity as an explicit parameter in the various methods, while for IAccessControl the object is implicit. OLE DB has a different requirement than COM, in that access control operations may be directed to data items that are not necessarily COM objects.

When to Implement

The provider will implement IObjectAccessControl on the data source object to expose security administration features of the data store. This interface is optional for all OLE DB providers.

When to Use

When the data source object has been initialized, the consumer uses this interface to examine or change specific details about access control for the data source objects and their trustees.

Method | Description
GetObjectAccessRights | Gets a list of all access rights or the rights of a specific trustee for a specific object.
GetObjectOwner | Returns the owner of a database object.
IsObjectAccessAllowed | Determines whether the given trustee has specific permissions (access rights) to the database object.
SetObjectAccessRights | Sets, adds, or revokes access rights of a trustee for a specific object.
SetObjectOwner | Sets the owner of an object.


GetObjectAccessRights

MDAC Technical Articles

IObjectAccessControl::GetObjectAccessRights

Gets a list of all access rights or the rights of a specific trustee for a specific object.

HRESULT GetObjectAccessRights (
   SEC_OBJECT *         pObject,
   ULONG *              pcAccessEntries,
   EXPLICIT_ACCESS **   prgAccessEntries
);

Parameters

pObject
[in]
Address of an initialized SEC_OBJECT structure that indicates the database object on which permissions are to be retrieved.
pcAccessEntries
[in, out]
Address of a ULONG variable that contains the number of access entries requested or receives the number of access entries returned. See Comments.
prgAccessEntries
[in, out]
Address of an array of EXPLICIT_ACCESS structures.

Return Code

S_OK
The access rights were successfully retrieved.
DB_S_ERRORSOCCURRED
An error occurred while retrieving access entries, but at least one access entry was successfully returned. Access entries that could not be returned are marked with grfAccessMode set to NOT_USED_ACCESS.
SEC_E_INVALIDACCESSENTRYLIST
One or more elements of *prgAccessEntries was invalid.
SEC_E_INVALIDOBJECT
The object that was supplied is invalid or unknown to the provider.
SEC_E_PERMISSIONDENIED
Caller does not have enough permission to complete the operation.
E_INVALIDARG
pObject, pcAccessEntries, or prgAccessEntries was a null pointer, or *pcAccessEntries was not equal to zero and *prgAccessEntries was a null pointer.
E_FAIL
A provider-specific error occurred.

Comments

The caller initializes pObject to specify the database object being examined. See SEC_OBJECT for an explanation on initializing this structure.

This method can retrieve access permissions for one trustee, or it can retrieve all the access permissions for all trustees.

To retrieve access permissions for one trustee, *pcAccessEntries must be nonzero on input. *prgAccessEntries must be a pointer to an array of exactly one or two EXPLICIT_ACCESS structures. The Trustee element of each element in the array should point to the TRUSTEE that is being checked. If the caller sets *pcAccessEntries to one, the grfAccessMode element of that structure must be set to either SET_ACCESS or DENY_ACCESS, indicating which type of permissions the caller is interested in. Otherwise, *pcAccessEntries should be set to two and the grfAccessMode element will be set to SET_ACCESS in one structure and to DENY_ACCESS in the other structure.

If *pcAccessEntries is nonzero on input, the provider will fill in the EXPLICIT_ACCESS structures passed in using *prgAccessEntries. The provider should examine the grfAccessMode element of each structure to determine how to fill in the grfAccessPermissions element. The only expected ACCESS_MODE values in the grfAccessMode element are SET_ACCESS and DENY_ACCESS. The provider will not change any part of the TRUSTEE structures in the pTrustee elements.

To retrieve access permissions for all trustees of an object, *pcAccessEntries must be zero on input and prgAccessEntries must be the address of an EXPLICIT_ADDRESS pointer, which will be set by the provider.

If *pcAccessEntries is zero on input, the provider calls CoTaskMemAlloc to allocate an array of EXPLICIT_ACCESS structures large enough to return all explicit access rights for the object specified by pObject. The provider must set *pcAccessEntries to the number of structures in the array and set *prgAccessEntries to point to that array.

When the caller is finished with the array of EXPLICIT_ACCESS structures, it releases the array. It is possible that the pTrustee element of each EXPLICIT_ACCESS structure refers to multiple trustees. If so, *pMultipleTrustee in the TRUSTEE structure will not be set to NULL. The caller traverses the linked list of TRUSTEE structures that start with the head *pMultipleTrustee element and releases them in reverse order. The caller also frees the memory allocated for each ptstrName element. It should call CoTaskMemFree for all of these tasks.

See Also

EXPLICIT_ACCESS, IObjectAccessControl, IObjectAccessControl::IsObjectAccessAllowed, IObjectAccessControl::SetObjectAccessRights, SEC_OBJECT, TRUSTEE


GetObjectOwner

MDAC Technical Articles

IObjectAccessControl::GetObjectOwner

Returns the owner of a database object.

HRESULT GetObjectOwner (
   SEC_OBJECT *   pObject,
   TRUSTEE **     ppOwner
);

Parameters

pObject
[in]
Address of an initialized SEC_OBJECT structure that indicates the database object whose owner is to be retrieved.
ppOwner
[out]
Address of a TRUSTEE pointer variable that receives the trustee information of the owner of the database object.

Return Code

S_OK
The owner was successfully retrieved.
SEC_E_NOOWNER
No owner exists for the object.
SEC_E_INVALIDOBJECT
The object that was supplied is invalid or unknown to the provider.
SEC_E_PERMISSIONDENIED
Caller does not have enough permission to complete the operation.
E_INVALIDARG
pObject or ppOwner was a null pointer.
E_FAIL
A provider-specific error occurred.

Comments

The caller initializes pObject to specify the database object whose owner is being retrieved. See SEC_OBJECT for an explanation on initializing this structure.

The provider calls CoTaskMemAlloc to allocate a TRUSTEE structure, into which it places the identity of the owner of the database object. It places the address of this structure into the address indicated by *ppOwner.

When the caller is finished with the TRUSTEE structure, it calls CoTaskMemFree to release the ptstrName element and then the TRUSTEE structure itself.

See Also

IObjectAccessControl, IObjectAccessControl::SetObjectOwner, SEC_OBJECT, TRUSTEE


IsObjectAccessAllowed

MDAC Technical Articles

IObjectAccessControl::IsObjectAccessAllowed

Determines whether the given trustee has specific permissions (access rights) to the database object.

HRESULT IsObjectAccessAllowed (
   SEC_OBJECT *         pObject,
   EXPLICIT_ACCESS *    pAccessEntry,
   BOOL *               pfResult
);

Parameters

pObject
[in]
Address of an initialized SEC_OBJECT structure that indicates the database object on which permissions are to be checked.
pAccessEntry
[in]
Address of an initialized EXPLICIT_ACCESS structure that indicates the TRUSTEE and the permissions to check.
pfResult
[out]
Address of a BOOL variable that receives the result of the check.

Return Code

S_OK
The method succeeded, whether the TRUSTEE has the permissions requested or not.
SEC_E_PERMISSIONDENIED
Caller does not have enough permission to complete the operation.
SEC_E_INVALIDACCESSENTRY
One or more permissions in the grfAccessPermissions element of the EXPLICIT_ACCESS structure are invalid.
SEC_E_INVALIDOBJECT
The object that was supplied is invalid or unknown to the provider.
E_INVALIDARG
pObject, pAccessEntry, or pfResult was a null pointer.
E_FAIL
A provider-specific error occurred.

Comments

The caller initializes pObject to specify the database object being examined. See SEC_OBJECT for an explanation on initializing this structure.

The provider determines whether the trustee has all of the permissions specified in the grfAccessPermissions entry in the EXPLICIT_ACCESS structure given in pAccessEntry. When it has determined that each permission requested is allowed and not denied, the provider sets the BOOLEAN variable pointed to by *pfResult to TRUE. Otherwise, if any permission is denied or not specifically allowed, the provider sets *pfResult to FALSE.

The provider is permitted, while determining whether access is allowed, to use additional information related to the security attributes for the specified trustee. This includes any roles or groups of which the trustee is a member. The information used need not be given in the parameters to this method. The exact implementation of this is left to the provider.

For example, the consumer calls the provider to determine whether GENERIC_READ permission is allowed for trustee NewUser on the data source object NewTable. The provider checks the access control entry lists for NewTable and determines that NewUser does not have a GRANT_ACCESS entry for the GENERIC_READ permission. The provider also determines that NewUser does not have a DENY_ACCESS entry at all. If this were all the information provided in this call to the provider, NewUser would be denied read access to NewTable. However, the provider is allowed to check any groups to which NewUser belongs. The provider finds that NewUser is a member of the group trustee NewGroup. The provider checks the access control entry lists for NewTable and determines that NewGroup does have a SET_ACCESS entry for both GENERIC_READ and GENERIC_WRITE permissions, and after completing its check, the provider finds no DENY_ACCESS entries. Therefore, the provider chooses to allow NewUser to read NewTable and sets *pfResult to TRUE.

See Also

EXPLICIT_ACCESS, IObjectAccessControl, IObjectAccessControl::GetObjectAccessRights, IObjectAccessControl::SetObjectAccessRights, SEC_OBJECT, TRUSTEE


SetObjectAccessRights

MDAC Technical Articles

IObjectAccessControl::SetObjectAccessRights

Sets, adds, or revokes access rights of a trustee for a specific object.

HRESULT SetObjectAccessRights (
   SEC_OBJECT *         pObject,
   ULONG                cAccessEntries,
   EXPLICIT_ACCESS *    prgAccessEntries
);

Parameters

pObject
[in]
Address of an initialized SEC_OBJECT structure that indicates the database object on which permissions are to be set.
cAccessEntries
[in]
The number of access control entries listed in prgAccessEntries.
prgAccessEntries
[in, out]
Array of access control entries to set. See Comments.

Return Code

S_OK
The access rights were successfully set.
DB_S_ERRORSOCCURRED
One or more access control entries were not set, but at least one access control entry was successfully set. The consumer checks the array of EXPLICIT_ACCESS structures at prgAccessEntries; the entries that failed will have the grfAccessMode entry set to NOT_USED_ACCESS.
DB_E_ERRORSOCCURRED
No access control entries were set. Every EXPLICIT_ACCESS structure in prgAccessEntries will have the grfAccessMode entry set to NOT_USED_ACCESS.
SEC_E_PERMISSIONDENIED
Requester does not have permissions for the operation.
SEC_E_INVALIDACCESSENTRYLIST
One or more elements of *prgAccessEntries was invalid.
SEC_E_INVALIDOBJECT
The object that was supplied is invalid or unknown to the provider.
E_INVALIDARG
pObject was a null pointer, or cAccessEntries was not zero and prgAccessEntries was a null pointer.
E_FAIL
A provider-specific error occurred.

Comments

The caller initializes pObject to specify the database object whose permissions are being set. See SEC_OBJECT for an explanation on initializing this structure.

The grfAccessMode member of each EXPLICIT_ACCESS structure specifies whether to allow, deny, or revoke access rights for the trustee. This member can specify one of the following values from the ACCESS_MODE enumeration.

Value | Meaning
GRANT_ACCESS | Creates a new access-allowed entry that combines the specified rights with any existing rights of the trustee. The new entry replaces any existing access-allowed entry for the trustee. The provider, in implementing this method , should modify or delete any existing access-denied entry for the trustee that denies the specified rights.
SET_ACCESS | Similar to GRANT_ACCESS except that the new access-allowed entry allows only the specified rights, discarding any existing rights. This flag also removes any existing access-denied entry for the trustee.
DENY_ACCESS | Creates a new access-denied entry that replaces any existing access-denied entry for the trustee. The new entry denies the specified rights in addition to any currently denied rights of the trustee. The provider should modify or delete any existing access-allowed entry for the trustee that allows the specified rights.
REVOKE_ACCESS | Removes any existing entries for the specified trustee. The provider should ignore the rights specified in the grfAccessPermissions member of the EXPLICIT_ACCESS structure.

To set and deny properties in a single call to SetObjectAccessRights, the following restrictions apply to the access entries:

  • The elements of prgAccessEntries are processed in order.
  • Any EXPLICIT_ACCESS structures denying permissions need to come before any EXPLICIT_ACCESS structures that grant permissions.
  • The EXPLICIT_ACCESS structure granting permissions must use GRANT rather than SET.

See Also

EXPLICIT_ACCESS, IObjectAccessControl, IObjectAccessControl::GetObjectAccessRights, IObjectAccessControl::IsObjectAccessAllowed, SEC_OBJECT, TRUSTEE


SetObjectOwner

MDAC Technical Articles

IObjectAccessControl::SetObjectOwner

Sets the owner of an object.

The caller initializes pObject to specify the database object whose owner is being set. See SEC_OBJECT for an explanation on initializing this structure.

HRESULT SetObjectOwner (
   SEC_OBJECT *   pObject,
   TRUSTEE *      pOwner
);

Parameters

pObject
[in]
Address of an initialized SEC_OBJECT structure that indicates the database object whose owner is to be set.
pOwner
[in]
Address of an initialized TRUSTEE structure that has the trustee information of the new owner of the object.

Return Code

S_OK
The owner was successfully set.
SEC_E_PERMISSIONDENIED
Requester does not have permissions for the operation.
SEC_E_INVALIDOWNER
The trustee supplied as owner is invalid or unknown to the provider.
SEC_E_INVALIDOBJECT
The object that was supplied is invalid or unknown to the provider.
SEC_E_NOOWNER
An attempt was made to set ownership for an object that cannot be owned by any trustee.
E_INVALIDARG
pObject or pOwner was a null pointer.
E_FAIL
A provider-specific error occurred.

See Also

IObjectAccessControl, IObjectAccessControl::GetObjectOwner, SEC_OBJECT, TRUSTEE


ISecurityInfo

The ISecurityInfo interface provides information to the consumer about the security administration support given by the provider.

When to Implement

The provider will implement ISecurityInfo to expose general security information about the data source object. This interface is optional for all OLE DB providers but is required if the provider implements the IObjectAccessControl interface.

When to Use

When the data source object has been initialized, the consumer uses this interface to determine the current trustee, the available permissions, or the types of object that support permissions.

Method | Description
GetCurrentTrustee | Returns the current trustee.
GetObjectTypes | Returns an array of object types that can be secured with access control entries.
GetPermissions | Returns the available permissions for a given object type.


GetCurrentTrustee

MDAC Technical Articles

ISecurityInfo::GetCurrentTrustee

Returns the current trustee.

HRESULT GetCurrentTrustee (
   TRUSTEE **     ppTrustee
);

Parameters

ppTrustee
[out]
Address of a TRUSTEE pointer variable that receives the trustee information of the current user.

Return Code

S_OK
The current trustee was successfully retrieved.
SEC_E_PERMISSIONDENIED
Requester does not have permissions for the operation.
E_INVALIDARG
ppTrustee was a null pointer.
E_FAIL
A provider-specific error occurred.

Comments

The provider allocates memory for the TRUSTEE structure by calling CoTaskMemAlloc and returns the address of this memory in the *ppTrustee variable.

When the caller is finished with the structure, it is responsible for releasing the memory allocated. Before the caller releases the TRUSTEE structure, it should release the memory allocated in the ptstrName element. It is possible that the pTrustee element of each EXPLICIT_ACCESS structure refers to multiple trustees. If so, *pMultipleTrustee in the TRUSTEE structure will not be set to NULL. The caller traverses the linked list of TRUSTEE structures that start with the head *pMultipleTrustee element and releases them in reverse order. The caller should call CoTaskMemFree for all of these tasks.

If an error occurs, the provider does not allocate any memory and sets *ppTrustee to a null pointer.

See Also

ISecurityInfo, TRUSTEE


GetObjectTypes

MDAC Technical Articles

ISecurityInfo::GetObjectTypes

Returns an array of object types that can be secured with access control entries.

HRESULT GetObjectTypes (
   ULONG *     cObjectTypes,
   GUID **     rgObjectTypes
);

Parameters

cObjectTypes
[out]
Address of a ULONG variable that receives the number of types of objects.
rgObjectTypes
[out]
Address of an array of GUIDs that represent types of objects.

Return Code

S_OK
The array of object types was successfully returned.
SEC_E_PERMISSIONDENIED
Requester does not have permissions for the operation.
E_INVALIDARG
cObjectTypes or rgObjectTypes was a null pointer.
E_FAIL
A provider-specific error occurred.

Comments

The provider calls CoTaskMemAlloc to allocate an array of GUIDs that represent all the types of objects that can have permissions settings applied to them. OLE DB defines several standard object type GUIDs. Additionally, the provider may define provider-specific GUIDs to identify object types unique to the data store; these types should be clearly documented by the provider's developer.

The consumer releases the allocated array at rgObjectTypes by calling CoTaskMemFree when it is finished.

See Also

SEC_OBJECT


GetPermissions

MDAC Technical Articles

ISecurityInfo::GetPermissions

Returns the available permissions for a given object type.

The provider sets the appropriate bits in an ACCESS_MASK structure to identify the permissions for any trustee that can possibly be set on the object type given in ObjectType.

HRESULT GetPermissions (
   GUID              ObjectType,
   ACCESS_MASK *     pPermissions
);

Parameters

ObjectType
[in]
The GUID of the object type for which permissions are being obtained.
pPermissions
[out]
Address of an ACCESS_MASK structure that receives the permissions supported for the object.

Return Code

S_OK
The available permissions were successfully retrieved.
SEC_E_PERMISSIONDENIED
Requester does not have permissions for the operation.
SEC_E_INVALIDOBJECT
The object that was supplied is invalid or unknown to the provider.
E_INVALIDARG
pPermissions was a null pointer.
E_FAIL
A provider-specific error occurred.

Comments

The GUID in ObjectType can be either a standard object type GUID or a provider-specific GUID for nonstandard objects types.

See Also

ACCESS_MASK Constants, Standard Object Type GUIDs


ITrusteeAdmin

The ITrusteeAdmin interface allows the consumer to add, remove, and compare trustees. It also allows the consumer to modify trustee properties used for authentication, such as passwords and a descriptive user name.

When to Implement

The provider will implement the ITrusteeAdmin interface on the data source object if it supports administration of trustees. Trustees can be users, groups, roles, services, and so on. This interface is optional for all providers but is required if the ITrusteeGroupAdmin interface is implemented.

When to Call

After it initializes the data source object, the consumer uses this interface to examine or change trustees that are known by the provider.

Method | Description
CompareTrustees | Determines whether two trustees are identical.
CreateTrustee | Adds a trustee to the collection of trustees known by the data source object. Also sets initial properties for the trustee, such as authentication information and display name.
DeleteTrustee | Removes a trustee from the collection of trustees known by the data source object.
GetTrusteeProperties | Gets the properties for the trustee, such as the display name or authentication information.
SetTrusteeProperties | Sets properties for the trustee, such as the display name or authentication information.


CompareTrustees

MDAC Technical Articles

ITrusteeAdmin::CompareTrustees

Determines whether two trustees are identical.

The consumer passes in the addresses of two initialized TRUSTEE structures to this method. The provider compares the trustee in any manner it chooses and returns the result of that comparison in the HRESULT return code.

HRESULT CompareTrustees (
   TRUSTEE *   pTrustee1,
   TRUSTEE *   pTrustee2
);

Parameters

pTrustee1
[in]
Address of the first TRUSTEE structure to compare.
pTrustee2
[in]
Address of the second TRUSTEE structure to compare.

Return Code

S_OK
The two trustees are equal.
S_FALSE
The two trustees are not equal.
SEC_E_PERMISSIONDENIED
Requester does not have permissions for the operation.
SEC_E_BADTRUSTEEID
pTrustee1 or pTrustee2 did not point to a valid TRUSTEE structure.
SEC_E_NOTRUSTEEID
pTrustee1 or pTrustee2 was not a trustee for the current data source object.
E_INVALIDARG
pTrustee1 or pTrustee2 was a null pointer.
E_FAIL
A provider-specific error occurred.

See Also

ITrusteeAdmin, TRUSTEE


CreateTrustee

MDAC Technical Articles

ITrusteeAdmin::CreateTrustee

Adds a trustee to the collection of trustees known by the data source object. Also sets initial properties for the trustee, such as authentication information and display name.

HRESULT CreateTrustee (
   TRUSTEE *   pTrustee,
   ULONG       cPropertySets,
   DBPROPSET   rgPropertySets[]
);

Parameters

pTrustee
[in]
Address of the initialized TRUSTEE structure to add.
cPropertySets
[in]
Number of structures in the array rgPropertySets. Set to zero to add the trustee with no properties.
rgPropertySets
[in, out]
Array of DBPROPSET structures for the properties to be set on the trustee.

The property IDs specified in this structure must belong to the Trustee property group.

If cPropertySets is zero, rgPropertySets is ignored.

Return Code

S_OK
The trustee was successfully added to the collection of trustees known by the data source object.
DB_S_ERRORSOCCURRED
The trustee was created, but one or more optional properties could not be set. Optional properties have the dwOptions element of the DBPROP structure set to DBPROPOPTIONS_OPTIONAL. The consumer checks the dwStatus element of the DBPROP structures to determine which properties were not set.
DB_E_ERRORSOCCURRED
The trustee was not created because one or more required properties could not be set. Required properties have the dwOptions element of the DBPROP structure set to DBPROPOPTIONS_REQUIRED. The consumer checks the dwStatus element of the DBPROP structures to determine which properties were not set.
SEC_E_PERMISSIONDENIED
Requester does not have permissions for the operation.
SEC_E_BADTRUSTEEID
pTrustee did not point to a valid TRUSTEE structure.
E_INVALIDARG
pTrustee was a null pointer

-or-

cPropertySets was greater than zero and rgPropertySets was a null pointer

-or-

In a DBPROPSET structure in the array rgPropertySets, cProperties was not zero and rgProperties was a null pointer.

E_FAIL
A provider-specific error occurred.

Comments

If the same property is specified more than once in rgPropertySets, it is provider-specific which value is used.

The DBPROPSTATUS enumerated type in the OLE DB Programmer's Reference lists the possible status values returned in the dwStatus element of the DBPROP structures.

See Also

DBPROPSTATUS, ITrusteeAdmin, ITrusteeAdmin::DeleteTrustee, Properties Overview, TRUSTEE, Trustee property group


DeleteTrustee

MDAC Technical Articles

ITrusteeAdmin::DeleteTrustee

Removes a trustee from the collection of trustees known by the data source object.

HRESULT DeleteTrustee (
   TRUSTEE *   pTrustee
);

Parameters

pTrustee
[in]
Address of a TRUSTEE structure identifying the trustee to be removed.

Return Code

S_OK
The trustee was successfully removed.
SEC_E_PERMISSIONDENIED
Requester does not have permissions for the operation.
SEC_E_BADTRUSTEEID
pTrustee did not point to a valid TRUSTEE structure.
SEC_E_NOTRUSTEEID
pTrustee was not a trustee for the current data source object.
E_INVALIDARG
pTrustee was a null pointer.
E_FAIL
A provider-specific error occurred.

See Also

ITrusteeAdmin, ITrusteeAdmin::CreateTrustee, TRUSTEE


GetTrusteeProperties

MDAC Technical Articles

ITrusteeAdmin::GetTrusteeProperties

Gets the properties for the trustee, such as the display name or authentication information.

HRESULT GetTrusteeProperties (
   TRUSTEE *            pTrustee,
   const ULONG          cPropertyIDSets,
   const DBPROPIDSET    rgPropertyIDSets[],
   ULONG *              pcPropertySets,
   DBPROPSET **         prgPropertySets
);

Parameters

pTrustee
[in]
Address of an initialized TRUSTEE structure that identifies the trustee whose properties are to be retrieved.
cPropertyIDSets
[in]
Number of DBPROPIDSET structures in rgPropertyIDSets. Set to zero to retrieve the values of all the properties for the trustee.
rgPropertyIDSets
[in]
Array of DBPROPIDSET structures that describe the properties to be retrieved from the trustee.

The property IDs specified in this structure must belong to the Trustee property group.

If cPropertyIDSets is zero, rgPropertyIDSets is ignored.

pcPropertySets
[out]
Address of a ULONG variable that receives the number of DBPROPSET structures returned using prgPropertySets.
prgPropertySets
[out]
Address of a DBPROPSET pointer variable that receives the address of an array of DBPROPSET structures.

Return Code

S_OK
The properties for the trustee were successfully retrieved.
DB_S_ERRORSOCCURRED
No value was returned for one or more properties. The consumer checks dwStatus in the DBPROP structures of prgPropertySets to determine which properties were not returned and why.
DB_E_ERRORSOCCURRED
Values were not returned for any properties, although the provider still allocates memory for prgPropertySets and sets pcPropertySets. The consumer checks dwStatus in the DBPROP structures of prgPropertySets to determine why properties were not returned.
SEC_E_PERMISSIONDENIED
Requester does not have permissions for the operation.
SEC_E_BADTRUSTEEID
pTrustee did not point to a valid TRUSTEE structure.
SEC_E_NOTRUSTEEID
pTrustee was not a trustee for the current data source object.
E_INVALIDARG
pTrustee was a null pointer; or cPropertyIDSets was not zero and rgPropertyIDSets was a null pointer; or pcPropertySets or prgPropertySets was a null pointer.
E_FAIL
A provider-specific error occurred.

Comments

The consumer can call this method in one of two ways: It can request values for specific properties for the trustee, or it can request all of the trustee's properties and their values.

To request values for specific properties, the consumer will allocate and initialize an array of DBPROPIDSET structures. Since only the Trustee property group is allowed for this method, the array will have one element in it. The consumer will also set cPropertyIDSets to the number of DBPROPIDSET structures in the array at rgPropertySets. Again, because only the Trustee property group can be requested, cPropertyIDSets will be set to 1.

To request all of the properties for pTrustee and their values, the consumer sets cPropertyIDSets to zero. It does not need to initialize rgPropertySets.

The provider will allocate an array of DBPROPSET structures to return the properties and their values. How it initializes this array depends on whether the consumer requested specific property values or all of the properties.

If the consumer requested specific properties, cPropertyIDSets will be greater than zero. If so, the provider sets *pcPropertySets equal to cPropertyIDSets. The provider calls CoTaskMemAlloc to allocate an array of DBPROPSET structures. This array of DBPROPSET structures will correspond one-for-one with the array of DBPROPIDSET structures passed in using rgPropertyIDSets, so there will be *pcPropertySets elements in the DBPROPSET array. The provider sets the guidPropertySet element of each of the DBPROPSET structures so that they are in the same order as they are given in the DBPROPIDSET array.

For each DBPROPIDSET that has its cPropertyIDs element set greater than zero, the provider will set the cProperties element of the corresponding DBPROPSET structure equal to cPropertyIDs. It also calls CoTaskMemAlloc to allocate an array of DBPROP structures for the rgProperties elements of the DBPROPSET structures. This array of DBPROP structures will correspond one-for-one with the array of DBPROPID values in the rgPropertyIDs elements of the DBPROPIDSET structures, so there will be cProperties elements in the DBPROP array. The provider sets the dwPropertyID element of each DBPROP structure so that they are in the same order as they are given in the DBPROPID array.

If the consumer requested all of the properties and their values, cPropertyIDSets will be equal to zero. If so, one DBPROPSET structure is returned for each property set that contains at least one property belonging to the Trustee property group. The provider ignores rgPropertyIDSets. The provider sets *pcPropertySets to the total number of property sets for which the provider supports at least one property in the Trustee property group.

If any error occurs, other than DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the provider does not allocate any memory and sets *pcPropertySets to zero and sets *prgPropertySets to a null pointer on output.

The DBPROPSTATUS enumerated type in the OLE DB Programmer's Reference lists the possible status values returned in the dwStatus element of the DBPROP structures.

The consumer releases *prgPropertySets when it no longer needs the DBPROPSET structures. Before releasing this array, the consumer should release the rgProperties element within each element of *prgPropertySets. The consumer should call CoTaskMemFree for all these tasks.

See Also

ITrusteeAdmin, ITrusteeAdmin::SetTrusteeProperties, Properties Overview, TRUSTEE


SetTrusteeProperties

MDAC Technical Articles

ITrusteeAdmin::SetTrusteeProperties

Sets properties for the trustee, such as the display name or authentication information.

HRESULT SetTrusteeProperties (
   TRUSTEE *   pTrustee,
   ULONG       cPropertySets,
   DBPROPSET   rgPropertySets[]
);

Parameters

pTrustee
[in]
Address of an initialized TRUSTEE structure that identifies the trustee whose properties are to be retrieved.
cPropertySets
[in]
Number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets and the method returns S_OK without doing anything.
rgPropertySets
[in, out]
Array of DBPROPSET structures describing properties to be set on the trustee.

The property IDs specified in this structure must belong to the Trustee property group.

If cPropertySets is zero, rgPropertySets is ignored.

Return Code

S_OK
The properties for the trustee were successfully set.
DB_S_ERRORSOCCURRED
One or more of the properties could be set, but some could not be set. Properties not in error remain set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set and why.
DB_E_ERRORSOCCURRED
No properties were set. The consumer checks dwStatus in the DBPROP structures to determine why properties were not set.
SEC_E_PERMISSIONDENIED
Requester does not have permissions for the operation.
SEC_E_BADTRUSTEEID
pTrustee did not point to a valid TRUSTEE structure.
SEC_E_NOTRUSTEEID
pTrustee was not a trustee for the current data source object.
E_INVALIDARG
pTrustee was a null pointer; or cPropertySets was not zero and rgPropertySets was a null pointer; or, in a DBPROPSET structure in the array rgPropertySets, cProperties was not zero and rgProperties was a null pointer.
E_FAIL
A provider-specific error occurred.

Comments

If the same property is specified more than once in rgPropertySets, it is provider-specific which value is used.

The DBPROPSTATUS enumerated type in the OLE DB Programmer's Reference lists the possible status values returned in the dwStatus element of the DBPROP structures.

See Also

DBPROPSTATUS, ITrusteeAdmin, ITrusteeAdmin::GetTrusteeProperties, Properties Overview, TRUSTEE, Trustee property group


ITrusteeGroupAdmin

The ITrusteeGroupAdmin interface allows the consumer to manage trustees that have other trustees as members. These "collection" trustees are called membership trustees. The interface allows the consumer to add and remove trustees from a membership trustee and to determine whether a trustee is a member of a membership trustee. It also allows the consumer to get a list of the members of a membership trustee and to get a list of the membership trustee to which a trustee belongs.

The provider may or may not support membership trustees that can have other membership trustees as members. This is provider-specific behavior.

When to Implement

The provider will implement the ITrusteeGroupAdmin interface on the data source object if it supports membership trustees. This interface is optional for all providers.

When to Call

After it initializes the data source object, the consumer uses this interface to examine or change the members of a membership trustee known by the provider.

Method | Description
AddMember | Adds a member trustee to a membership trustee.
DeleteMember | Removes a trustee from a membership trustee.
GetMembers | Gets the members of a membership trustee.
GetMemberships | Gets the membership trustees to which a trustee belongs.
IsMember | Determines whether a trustee is a member of a membership trustee.


AddMember

MDAC Technical Articles

ITrusteeGroupAdmin::AddMember

Adds a member trustee to a membership trustee.

HRESULT AddMember (
   TRUSTEE *   pMembershipTrustee,
   TRUSTEE *   pMemberTrustee
);

Parameters

pMembershipTrustee
[in]
Address of the TRUSTEE structure that identifies the membership trustee to which pMemberTrustee will be added.
pMemberTrustee
[in]
Address of the TRUSTEE structure that identifies the trustee to be added to pMembershipTrustee.

Return Code

S_OK
The trustee was successfully added to the membership trustee.
SEC_E_PERMISSIONDENIED
Requester does not have permissions for the operation.
SEC_E_BADTRUSTEEID
pMembershipTrustee or pMemberTrustee did not point to a valid TRUSTEE structure.
SEC_E_NOTRUSTEEID
pMembershipTrustee or pMemberTrustee was not a trustee for the current data source.
SEC_E_NOMEMBERSHIPSUPPORT
The trustee pointed to by pMembershipTrustee does not support memberships.
E_INVALIDARG
pMembershipTrustee or pMemberTrustee was a null pointer.
E_FAIL
A provider-specific error occurred.

See Also

ITrusteeGroupAdmin, ITrusteeGroupAdmin::DeleteMember, TRUSTEE


DeleteMember

MDAC Technical Articles

ITrusteeGroupAdmin::DeleteMember

Removes a trustee from a membership trustee.

HRESULT DeleteMember (
   TRUSTEE *   pMembershipTrustee,
   TRUSTEE *   pMemberTrustee
);

Parameters

pMembershipTrustee
[in]
Address of the TRUSTEE structure that identifies the trustee from which pMemberTrustee is to be removed.
pMemberTrustee
[in]
Address of the TRUSTEE structure that identifies the trustee to be removed from pMembershipTrustee.

Return Code

S_OK
The trustee was successfully removed from the membership trustee.
SEC_E_PERMISSIONDENIED
Requester does not have permissions for the operation.
SEC_E_BADTRUSTEEID
pMembershipTrustee or pMemberTrustee did not point to a valid TRUSTEE structure.
SEC_E_NOTRUSTEEID
pMembershipTrustee or pMemberTrustee was not a trustee for the current data source object.
SEC_E_NOMEMBERSHIPSUPPORT
The trustee pointed to by pMembershipTrustee does not support memberships.
E_INVALIDARG
pMembershipTrustee or pMemberTrustee was a null pointer.
E_FAIL
A provider-specific error occurred.

See Also

ITrusteeGroupAdmin, ITrusteeGroupAdmin::AddMember, TRUSTEE


GetMembers

MDAC Technical Articles

ITrusteeGroupAdmin::GetMembers

Gets the members of a membership trustee.

This function is not applicable to trustees with the TRUSTEE_TYPE element set to TRUSTEE_IS_USER, because a user cannot have members.

HRESULT GetMembers (
   TRUSTEE *      pMembershipTrustee,
   ULONG *        pcMembers,
   TRUSTEE **     prgMembers
);

Parameters

pMembershipTrustee
[in]
Address of the TRUSTEE structure that identifies the membership trustee whose members will be returned.
pcMembers
[out]
Address of a ULONG variable that receives the number of TRUSTEE structures in prgMembers.
prgMembers
[out]
Address of a TRUSTEE pointer variable that receives the address of an array of TRUSTEE structures representing the trustees that are members of pMembershipTrustee.

Return Code

S_OK
The members of the membership trustee were successfully retrieved.
SEC_E_PERMISSIONDENIED
Requester does not have permissions for the operation.
SEC_E_BADTRUSTEEID
pMembershipTrustee did not point to a valid TRUSTEE structure.
SEC_E_NOMEMBERSHIPSUPPORT
The trustee pointed to by pMembershipTrustee does not support memberships.
SEC_E_NOTRUSTEEID
pMembershipTrustee was not a trustee for the current data source object.
E_INVALIDARG
pMembershipTrustee, pcMembers, or prgMembers was a null pointer.
E_FAIL
A provider-specific error occurred.

Comments

The provider calls CoTaskMemAlloc to allocate memory for the array of TRUSTEE structures that represent the members of pMembershipTrustee.

The consumer releases *prgMembers when it no longer needs the TRUSTEE structures. Before freeing this memory, the consumer should free any memory allocated in the pMultipleTrustee or ptstrName elements of each structure. The consumer should call CoTaskMemFree for all these tasks.

If pMembershipTrustee does not contain any members or is not a membership trustee, the provider sets *pcMembers to zero, *prgMembers to NULL, and does not allocate any memory for *prgMembers.

See Also

ITrusteeGroupAdmin, ITrusteeGroupAdmin::GetMemberships, ITrusteeGroupAdmin::IsMember, TRUSTEE


GetMemberships

MDAC Technical Articles

ITrusteeGroupAdmin::GetMemberships

Gets the membership trustees to which a trustee belongs.

HRESULT GetMemberships (
   TRUSTEE *      pTrustee,
   ULONG *        pcMemberships,
   TRUSTEE **     prgMemberships
);

Parameters

pTrustee
[in]
Address of an initialized TRUSTEE structure that identifies the trustee whose memberships will be returned.
pcMemberships
[out]
Address of a ULONG variable that receives the number of TRUSTEE structures returned using prgMemberships.
prgMemberships
[out]
Address of a TRUSTEE pointer variable that receives the address of an array of TRUSTEE structures representing the membership trustees that have pTrustee as a member.

Return Code

S_OK
The membership trustees were successfully retrieved.
SEC_E_PERMISSIONDENIED
Requester does not have permissions for the operation.
SEC_E_BADTRUSTEEID
pTrustee did not point to a valid TRUSTEE structure.
SEC_E_NOTRUSTEEID
pTrustee was not a trustee for the current data source object.
E_INVALIDARG
pTrustee, pcMemberships, or prgMemberships was a null pointer.
E_FAIL
A provider-specific error occurred.

Comments

The provider calls CoTaskMemAlloc to allocate memory for the array of TRUSTEE structures that represent the membership trustees that have pTrustee as a member.

The consumer releases *prgMemberships when it no longer needs the TRUSTEE structures. Before freeing this memory, the consumer should free any memory allocated in the pMultipleTrustee or ptstrName elements of each structure. The consumer should call CoTaskMemFree for all these tasks.

If the trustee identified by pTrustee does not belong to any membership trustees or if an error occurs, the provider ensures that no memory is allocated for prgMemberships, sets *pcMemberships to zero, and sets *prgMemberships to NULL.

See Also

ITrusteeGroupAdmin, ITrusteeGroupAdmin::GetMembers, ITrusteeGroupAdmin::IsMember, TRUSTEE


IsMember

MDAC Technical Articles

ITrusteeGroupAdmin::IsMember

Determines whether a trustee is a member of a membership trustee.

This function is not applicable to trustees with the TRUSTEE_TYPE element set to TRUSTEE_IS_USER, because a user cannot have members.

HRESULT IsMember (
   TRUSTEE *   pMembershipTrustee,
   TRUSTEE *   pMemberTrustee,
   BOOL *      pfStatus
);

Parameters

pMembershipTrustee
[in]
Address of the TRUSTEE structure that identifies the membership trustee whose membership will be checked.
pMemberTrustee
[in]
Address of the TRUSTEE structure that identifies the trustee to find in the membership trustee.
pfStatus
[out]
Address of a BOOL variable that receives the result of the check.

Return Code

S_OK
The method succeeded, whether or not pMemberTrustee is a member of pMembershipTrustee.
SEC_E_PERMISSIONDENIED
Requester does not have permissions for the operation.
SEC_E_BADTRUSTEEID
pMembershipTrustee or pMemberTrustee did not point to a valid TRUSTEE structure.
SEC_E_NOTRUSTEEID
pMembershipTrustee or pMemberTrustee was not a trustee for the current data source object.
SEC_E_NOMEMBERSHIPSUPPORT
The trustee pointed to by pMembershipTrustee does not support memberships.
E_INVALIDARG
pMembershipTrustee, pMemberTrustee, or pfStatus was a null pointer.
E_FAIL
A provider-specific error occurred.

See Also

ITrusteeGroupAdmin, ITrusteeGroupAdmin::GetMembers, ITrusteeGroupAdmin::GetMemberships, TRUSTEE


Property Groups and Properties

This section of the OLE DB security administration guide lists the following property group and properties that are defined for OLE DB security management:

  • Trustee Property Group
  • DBPROP_TRUSTEE_AUTHENTICATION
  • DBPROP_TRUSTEE_NEWAUTHENTICATION
  • DBPROP_TRUSTEE_USERNAME


Trustee Property Group

Property Set: DBPROPSET_TRUSTEE

Property Flag: DBPROPFLAGS_TRUSTEE

Properties

DBPROP_TRUSTEE_AUTHENTICATION | DBPROP_TRUSTEE_USERNAME
DBPROP_TRUSTEE_NEWAUTHENTICATION |


Authentication String

MDAC Technical Articles

DBPROP_TRUSTEE_AUTHENTICATION

Property group: Trustee

Property set: DBPROPSET_TRUSTEE

Column? N

Type: VT_BSTR

Typical R/W: W

Description: Authentication String

The authentication string is typically a password associated with the trustee.

Ordinarily, to set any other properties on the trustee, the authentication string must be specified.

The authentication string is generally not retrievable. Attempting to retrieve the authentication string returns DBPROPSTATUS_NOTAVAILABLE.


New Authentication String

MDAC Technical Articles

DBPROP_TRUSTEE_NEWAUTHENTICATION

Property group: Trustee

Property set: DBPROPSET_TRUSTEE

Column? N

Type: VT_BSTR

Typical R/W: W

Description: New Authentication String

The new authentication string is typically used to set a new password for the trustee.

To set a new authentication string, the consumer will ordinarily specify the current authentication string as well as the new authentication string. That is, the property set will contain both a DBPROP_TRUSTEE_AUTHENTICATION as well as a DBPROP_TRUSTEE_NEWAUTHENTICATION property.

Attempting to retrieve this property returns DBPROPSTATUS_ NOTAVAILABLE.


Trustee User Name

MDAC Technical Articles

DBPROP_TRUSTEE_USERNAME

Property group: Trustee

Property set: DBPROPSET_TRUSTEE

Column? N

Type: VT_BSTR

Typical R/W: R/W

Description: Trustee User Name

This is a descriptive, human-readable form of the user name associated with the trustee.


Structures

This section of the OLE DB security administration guide lists the following structures that are defined and used by OLE DB security interfaces:

  • SEC_OBJECT
  • SEC_OBJECT_ELEMENT


SEC_OBJECT

The SEC_OBJECT structure contains a counted array of SEC_OBJECT_ELEMENT structures that are used to identify the object on which security is being applied.

typedef struct _SEC_OBJECT {
   DWORD                   cObjects;
   SEC_OBJECT_ELEMENT*     prgObjects;
} SEC_OBJECT;
Element | Description
cObjects | The number of elements in the array pointed to by prgObjects.
prgObjects | Array of SEC_OBJECT_ELEMENT structures that identify a hierarchy of objects.

Comments

The array of SEC_OBJECT_ELEMENT structures is used to identify the specific object or type of object on which security is being applied. Each structure represents a deeper level in the hierarchy of object. The SEC_OBJECT_ELEMENT structure at index 0 represents the broadest object. The structure at index 1 represents a specific object contained within the first object, and so on.

For example, to apply security to a column object, the SEC_OBJECT structure may contain three SEC_OBJECT_ELEMENT structures, as follows:

SEC_OBJECT soColumn;
soColumn.cObjects = 3;
soColumn.prgObjects = CoTaskMemAlloc(3 * sizeof(SEC_OBJECT_ELEMENT));

The first item indicates the schema identified by name, as follows:

soColumn.prgObjects[0].guidObjectType = DBOBJECT_SCHEMA;
soColumn.prgObjects[0].ObjectID.eKind = DBKIND_NAME;
soColumn.prgObjects[0].ObjectID.pwszName = L"TheSchema";

The second item indicates the table within the schema by name, as follows:

soColumn.prgObjects[1].guidObjectType = DBOBJECT_TABLE;
soColumn.prgObjects[1].ObjectID.eKind = DBKIND_NAME;
soColumn.prgObjects[1].ObjectID.pwszName = L"TheTable";

The third item identifies the column in the table by property ID only, as follows:

soColumn.prgObjects[2].guidObjectType = DBOBJECT_COLUMN;
soColumn.prgObjects[2].ObjectID.eKind = DBKIND_PROPID;
soColumn.prgObjects[2].ObjectID.ulPropid = 3;

A SEC_OBJECT initialized this way would indicate the fourth column (from a zero-based index) in the table named "TheTable" in the schema named "TheSchema" in the currently initialized data source object.

By using an array to progressively specify the object on which security is being applied, a single SEC_OBJECT structure can uniquely identify an object at any depth in the hierarchy of objects within the data source object.

See Also

SEC_OBJECT_ELEMENT


SEC_OBJECT_ELEMENT

The SEC_OBJECT_ELEMENT structure is used to identify the specific database object in a group of the same kinds of objects at a particular level in the hierarchy of objects in the data source object.

typedef struct _SEC_OBJECT_ELEMENT {
   GUID     guidObjectType;
   DBID     ObjectID;
} SEC_OBJECT_ELEMENT;
Element | Description
guidObjectType | GUID that identifies the type of the object identified by ObjectID. See the list of Standard Object Type GUIDs.
ObjectID | DBID structure that identifies the specific object at the hierarchy level given by guidObjectType. For more information on the DBID structure, see Column IDs in the OLE DB Programmer's Reference.

Comments

Each SEC_OBJECT_ELEMENT structure in an array of SEC_OBJECT_ELEMENT structures identifies an object in a hierarchy of objects in the data source object.

OLE DB defines two special object IDs that are used to control permissions for objects that contain other controllable objects. These DBIDs allow you to set permissions for several database objects at once and also to set the default permissions for new objects.

See Also

SEC_OBJECT


Constants

This section of the OLE DB security administration guide describes the following types of defined constants:

  • Standard Object Type GUIDs
  • Database Object IDs
  • ACCESS_MASK Constants


Standard Object Type GUIDs

The following GUIDs are used to identify standard types of OLE DB data source objects:

DBOBJECT_CHARACTERSET | DBOBJECT_SCHEMA
DBOBJECT_COLLATION | DBOBJECT_SCHEMAROWSET
DBOBJECT_COLUMN | DBOBJECT_TABLE
DBOBJECT_DATABASE | DBOBJECT_TRANSLATION
DBOBJECT_DOMAIN | DBOBJECT_TRUSTEE
DBOBJECT_PROCEDURE | DBOBJECT_VIEW

See Also

ISecurityInfo::GetObjectTypes, SEC_OBJECT_ELEMENT


Database Object IDs

Two unnamed DBIDs, also called object IDs, can be used to reference object containers. Both of these DBIDs are of the type DBKIND_GUID_PROPID, as are DBCOLUMN* and DBROWCOL*. Use these DBIDs in the SEC_OBJECT_ELEMENT structure to identify object containers.

The following DBID refers to all existing and any new objects created in the container:

{DBGUID_CONTAINEROBJECT, DBKIND_GUID_PROPID, (LPOLESTR) 2}

The following DBID refers only to all new objects created in the container:

{DBGUID_CONTAINEROBJECT, DBKIND_GUID_PROPID, (LPOLESTR) 3}

See Also

SEC_OBJECT_ELEMENT


ACCESS_MASK Constants

OLE DB defines the following #define constants for bits within the ACCESS_MASK structure. The ACCESS_MASK structure is passed in as the grfAccessPermissions element in the EXPLICIT_ACCESS structure. The EXPLICIT_ACCESS structure is used in several of the methods of the IObjectAccessControl interface.

Bit | Permission | OLE DB Constant (#define) | ACCESS_MASK Constant
0–7 | reserved | N/A | N/A1
8 | Drop | PERM_DROP | N/A1
9 | Exclusive | PERM_EXCLUSIVE | N/A1
10 | Read Design | PERM_READDESIGN | N/A1
11 | Write Design | PERM_WRITEDESIGN | N/A1
12 | With Grant | PERM_WITHGRANT | N/A1
13 | Reference | PERM_REFERENCE | N/A1
14 | Create | PREM_CREATE | N/A1
15 | Insert | PERM_INSERT | N/A1
16 | Delete | PERM_DELETE | DELETE
17 | Read Control | PERM_READCONTROL | READ_CONTROL
18 | Write Permissions | PERM_WRITEPERMISSIONS | WRITE_DAC
19 | Set Owner | PERM_WRITEOWNER | WRITE_OWNER
20–24 | N/A2 | N/A2 | N/A2
25 | Max Allowed | PERM_MAXIMUM_ALLOWED | MAXIMUM_ALLOWED
26–27 | reserved2 | N/A2 | N/A2
28 | All | PERM_ALL | GENERIC_ALL
29 | Execute | PERM_EXECUTE | GENERIC_EXECUTE
30 | Update | PERM_UPDATE | GENERIC_WRITE
31 | Read | PERM_READ | GENERIC_READ

1 The ACCESS_MASK structure assigns bits 0 through 15 to rights specific to the object. OLE DB currently uses only bits 8 through 15 for specific rights.

2 See the Access Control section of the Microsoft Platform SDK for ACCESS_MASK assignments for these bits in the structure, and see this guide's Appendix for a comparison with COM and the Platform SDK security functions.

These permissions are defined as described in the following table.

Permission | Definition
Drop | Permission to delete or drop an object.
Exclusive | Permission to open an object exclusively.
Read Design | Permission to see object definitions.
Write Design | Permission to modify object definitions.
With Grant | Permission to grant permissions to others (WITH GRANT OPTION used).
Reference | Permission to add references from other tables to this table. For example, ORDERS and ORDER_DETAIL are created and owned by two different trustees. The owner of the ORDERS table must grant Reference permission to the owner of the ORDER_DETAIL table to enable the owner of the ORDER_DETAIL table to create a reference to the "OrderID" column of the ORDERS table.
Create | Permission to create new objects. This permission applies to container objects.
Insert | Permission to add new elements to objects (such as rows to a rowset).
Delete | Permission to remove elements from objects (such as rows from a rowset).
Read Control | Permission to read permissions and object metadata.
Write Permissions | Permission to grant or revoke privileges.
Set Owner | Permission to modify object owners.
Max Allowed | Maximum permission granted or possible.
All | Permission to execute, update, and read.
Execute | Permission to execute an object method.
Update | Permission to modify object data.
Read | Permission to read object data.


The TRUSTEE Schema

The TRUSTEE schema enumerates the trustees for a data source. For information about schema rowsets, see Appendix B, "Schema Rowsets," in the OLE DB Programmer's Reference.

To get the TRUSTEES rowset, use the GetRowset method of the IDBSchemaRowset interface.

This following table lists the GUID and the restrictions that are available for the TRUSTEES rowset.

GUID | Number of restrictions | Restriction columns
DBSCHEMA_TRUSTEE | 4 | TRUSTEE_NAME TRUSTEE_GUID TRUSTEE_PROPID TRUSTEE_TYPE

The TRUSTEES rowset identifies the trustees defined in the data source. It contains the columns described in the following table.

Column name | Type indicator | Description
TRUSTEE_NAME | DBTYPE_WSTR | Trustee name.
TRUSTEE_GUID | DBTYPE_GUID | GUID that uniquely identifies the trustee. Providers that do not use GUIDs to identify trustees should return NULL in this column.
TRUSTEE_PROPID | DBTYPE_UI4 | PropID that uniquely identifies the trustee. Providers that do not use PropIDs to identify trustees should return NULL in this column.
TRUSTEE_TYPE | DBTYPE_UI4 | Trustee type. One of the following: TRUSTEE_IS_UNKNOWN—The trustee is an unknown type but not necessarily an invalid type. TRUSTEE_IS_USER—The trustee account is a user account. TRUSTEE_IS_GROUP—The trustee is a group account.

Default Sort Order: TRUSTEE_NAME, TRUSTEE_GUID, TRUSTEE_PROPID, TRUSTEE_TYPE


Appendix: IObjectAccessControl versus IAccessControl

MDAC Technical Articles

Appendix: IObjectAccessControl vs. IAccessControl

In COM security, IAccessControl is usually implemented on the object whose access control is being governed. This does not work for OLE DB interfaces. Access control in OLE DB is applied to elements such as tables, columns, and procedures, which are not actual COM objects. OLE DB uses a new interface, IObjectAccessControl, which supports applying security to objects that are not COM objects. The IObjectAccessControl interface is generic enough that it can be applied to other types of security scenarios.

IAccessControl uses an ACTRL_ACCESS structure to represent the access being set or retrieved. In contrast, IObjectAccessControl uses the EXPLICIT_ACCESS structure defined by the Platform SDK security system because it is simpler and provides all the necessary functionality.

The following table shows how IAccessControl methods relate to IObjectAccessControl methods.

IAccessControl (COM) | IObjectAccessControl (OLE DB)
GrantAccessRights | SetObjectAccessRights with grfAccessMode = GRANT_ACCESS
SetAccessRights | SetObjectAccessRights with grfAccessMode = SET_ACCESS
SetOwner | SetObjectOwner
RevokeAccessRights | SetObjectAccessRights with grfAccessMode = REVOKE_ACCESS
GetAllAccessRights | GetObjectAccessRights
IsAccessAllowed | IsObjectAccessAllowed

The following table shows how the ACCESS_MASK permission values defined in COM relate to the security bits defined in OLE DB.

OLE ACCESS_MASK Bit | OLE DB Security Bit
0–15 Object Specific | 9 PERM_EXCLUSIVE 10 PERM_READDESIGN 11 PERM_WRITEDESIGN 12 PERM_WITHGRANT 13 PERM_REFERENCE 14 PERM_CREATE 15 PERM_INSERT
16 DELETE | 16 PERM_DELETE
17 READ_CONTROL | 17 PERM_READCONTROL
18 WRITE_DAC | 18 PERM_WRITEPERMISSIONS
19 WRITE_OWNER | 19 PERM_WRITEOWNER
20 SYNCHRONIZE | N/A
24 ACCESS_SYSTEM_SECURITY | N/A
25 MAXIMUM_ALLOWED | 25 PERM_MAXIMUM_ALLOWED
26–27 Reserved | N/A
28 GENERIC_ALL | 28 PERM_ALL
29 GENERIC_EXECUTE | 29 PERM_EXECUTE
30 GENERIC_WRITE | 30 PERM_WRITE
31 GENERIC_READ | 31 PERM_READ


Optimistic Remote Updating in the Client Cursor Engine

March 1999

by Greg Smith

Introduction

The Client Cursor Engine, or CCE, is an OLE DB (COM) rowset implementation that ships with ADO/RDS and OLE DB. It is the heart of the client-side cursor in ADO, a crucial piece of the remoting infrastructure in RDS, and the core object in the OLE DB Cursor Service. It is also the rowset implementation integrated into the Data Shaping Service for OLE DB.

The root mission of the CCE is to be the premier lightweight, remotable cache for client-size structured data chunks, providing fast and rich scrolling, sorting, filtering, shaping, and updating. It is an independently instantiable OLE DB rowset object, implementing all of the required rowset interfaces, many of the most interesting optional interfaces, and several unique interfaces that might make their way into future versions of the OLE DB specification.

An understanding of optimistic remote updating and its implementation in the Client Cursor Engine will help developers gain some useful insights into the actual updating results in any given real-life situation. Likewise, customers can use information about the update process in the CCE to help them better design their own projects for efficient updatability.

Updating plays a crucial role in projects in which the CCE is a component, and it often entails a highly complex and dependent behavior. In optimistic remote updating, the key to the complexity is that the CCE does not "own" the data it is ultimately updating. Instead, the data is owned by some specific provider, and the CCE must submit update requests in the form of SQL commands. (This is also sometimes referred to as query-based updating, or QBU.) Until these commands are successfully executed, all user-initiated changes are cached on a local copy of the remote data, in the CCE rowset object. At the moment that this local copy of the remote data is first generated—by fetching from the back-end database or reading from an HTTP stream—it is already stale, at least potentially. This is the nature of optimistic updating—for performance and scalability reasons, no server locks or resources are held that would enable the CCE to prevent or detect other users' concurrent changes. As a result, most of the difficulty in this type of multi-tier updating occurs because the component (the CCE) that answers to the user cannot guarantee that any update operations the user has performed locally will be successful by the time they are submitted to the back-end database. Failures can be caused by reasons as various as connectivity errors, metadata changes, data conflicts, permission violations, and so forth, and resolving these errors gracefully takes a high degree of flexibility and well-designed default behaviors.

The current body of CCE code, considered to be the "updating code" responsible for translating rowset changes into SQL commands, consists of three sections:

  • Code to gather and store update properties
  • Code to execute updates
  • Code to analyze results

Gathering and Storing Update Properties

The update properties provide extra information about the structure of a result set and enable the CCE to perform updates on the constituent tables of that result set. Essentially, each update property describes the relationship between a result set field and the true back-end base table field that the result set field represents. Property gathering is performed as the original result set is first fetched into the CCE. Along with the data, the gathered properties are stored internally and used when the command to attempt a back-end update is given. (If the CCE rowset is opened read-only, no attempt is made to gather or store the update properties because they will never be used.)

Update properties can be gathered in one of the following ways:

  • Using the OLE DB IColumnsRowset interface
  • Parsing the SQL statement that was used to create the rowset

Gathering the properties is tried first by using IColumnsRowset. If that approach fails to provide a coherent picture of the required properties or is unsupported in optional portions of the implementation, the fallback method is to parse the SQL statement.

Using IColumnsRowset

IColumnsRowset is an OLE DB interface that provides methods designed to get more information about the columns of a result set than is available from the simpler IColumnsInfo interface. (See IColumnsRowset in the OLE DB Programmers Reference for more information.) The CCE fetches its data from an underlying provider rowset with the IRowset interfaces, and as part of the fetching process, this rowset is queried for whether it supports the IColumnsRowset interface.

The following extended column attributes together provide all the properties necessary for updating:

  • Base table column name (DBCOLUMN_BASECOLUMNNAME)
  • Base table name (DBCOLUMN_BASETABLENAME)
  • Base table schema name (DBCOLUMN_BASESCHEMANAME)
  • Base table catalog name (DBCOLUMN_BASECATALOGNAME)
  • Key or non-key column status (DBCOLUMN_KEYCOLUMN)

The first four properties listed above are part of the original OLE DB specification, and the last (DBCOLUMN_KEYCOLUMN) was added in the 2.0 version. These properties are all currently part of the "optional metadata columns" returned by IColumnsRowset.

For the CCE, the first two properties (DBCOLUMN_BASECOLUMNNAME and DBCOLUMN_BASETABLENAME) are the only ones required when attempting an update. The second two properties (DBCOLUMN_BASESCHEMANAME and DBCOLUMN_BASECATALOGNAME) are essentially scoping qualifiers to the table name and might not be necessary for unique table identification in a given context with a given server. Finally, the key column information (DBCOLUMN_KEYCOLUMN) assures that a change intended to affect a single row does not inadvertently affect multiple rows on the server. (See the section "Row Identity," later in this document.)

If the IColumnsRowset method of gathering metadata fails, it will be necessary to parse the SQL statement. Any one of the following conditions will cause the CCE to abandon IColumnsRowset and proceed to parsing the SQL:

  • The QueryInterface call on the provider rowset for IColumnsRowset fails.
  • The provider does not support at least DBCOLUMN_BASECOLUMNNAME and DBCOLUMN_BASETABLENAME on its columns rowset (determined via IColumnsRowset::GetAvailableColumns).
  • The provider does support the DBCOLUMN_BASECOLUMNNAME and DBCOLUMN_BASETABLENAME attributes, but returns NULL values or empty values for these attributes on each column in the resultset.
  • A failure HRESULT is returned by the provider from any of the methods called while setting up and accessing the columns rowset data (for example, IColumnsRowset::GetAvailableColumns, IRowset::GetData, IAccessor::CreateAccessor, and so on).

If DBCOLUMN_BASESCHEMANAME and DBCOLUMN_BASECATALOGNAME are not offered on the columns rowset or are reported to have NULL values for every result set column, schema and catalog qualifiers are presumed to be unimportant in the current context. If DBCOLUMN_KEYCOLUMN is not offered on the columns rowset or is reported to have a NULL value for every result set column, the CCE will record this fact and try the alternate IDBSchemaRowset method of gathering key information, as described in the following section.

Note If the DBCOLUMN_KEYCOLUMN values are all Boolean FALSE—as opposed to NULL—that means no keys were present in the result set. There is no follow-up with IDBSchemaRowset as in the NULL case.)

Parsing the SQL Statement

If, by using IColumnsRowset, the attempt to obtain the required attributes fails, it will be necessary to parse the SQL statement. (This parsing model is appropriated from the Microsoft® Visual FoxPro® remote view design and is the only method available in Visual FoxPro to acquire update properties.) This method involves reading through the SQL statement that generated the result set, in an effort to interpret the semantics well enough to build the relationships between result set fields and fields in the base tables that constitute the elements of the result set.

The CCE attempts to determine the SQL text by using the IRowsetInfo::GetSpecification method to get to the command object used to create the rowset, and by using the ICommandText::GetCommandText method on this command object to get the SQL text. If that operation succeeds, parse the SQL statement using the following steps:

  1. Find the FROM clause, and issue a "SELECT * FROM [base table] WHERE 1=0" statement on each base table in the clause. This will generate column information and build an internal list of each table's fields without returning any table data.
  2. Look through the field list and alias portions of the result set's SELECT statement, and compare these field-by-field with the base table field lists, trying to make unambiguous matches for all the fields.

When successful, this parsing procedure provides the first two attributes required by the CCE for updating: base table name and base table column name. When present in the qualification of the table name (for example, "PUBS.DBO.AUTHORS"), the third and fourth attributes (schema and catalog) are collected as well. The fifth attribute, key column information, is then obtained through the OLE DB IDBSchemaRowset interface. This entails requesting the "primary key" schema rowset for each base table, which provides a list of primary keys. If this rowset is unavailable or empty, a list of unique indexes, which are then treated as the key columns, can be obtained by requesting the "indexes" schema rowset for each base table.

Trade-offs

When supported, the IColumnsRowset method is by far the superior alternative. It is currently supported by the Microsoft OLE DB providers for Oracle, SQL Server, and Microsoft Jet 4.0, and by the Cursor Service for OLE DB. Additionally, it is supported by the OLE DB Provider for ODBC when working with SQL Server databases (through the SQL Server ODBC Driver). For some providers, gathering this information is somewhat expensive and should not be done in cases where it won't be used. To this end, the OLE DB specification was extended to include the DBPROP_UNIQUEROWS rowset property, to be set on a provider rowset or command object before the rowset is opened. A value of 'TRUE' indicates that this extended metadata is important to the rowset consumer and that it should be provided if possible. The MDAC components that use the CCE for cursoring services (Service Components, ADO, and RDS) automatically set this property on the provider before invoking the CCE for updatable rowsets. With the providers for Oracle and SQL Server in particular, the required IColumnsRowset data will be unavailable unless DBPROP_UNIQUEROWS has been set beforehand.

The parsing method may be more generically available for SQL-based providers, because getting base table and column information requires only that a provider can execute SELECT statements, which is the way many result sets are generated in the first place. But the parsing method is useless for result sets where the SQL text is unavailable. Indeed, aside from whether or not a provider even implements the interfaces that allow the CCE to retrieve the defining SQL, there is no guarantee that the result set was generated by SQL in the first place. It might instead be the result of a stored procedure call or an OLE DB IOpenRowset call, or it might be a rowset from a non-SQL-based provider such as the file system.

But the main drawbacks to the parsing approach are brittleness and performance. The parsing approach is brittle because of the lack of a full-blown SQL parser (which would create an unacceptable level of overhead) that could unambiguously determine the shape of the result set from the SQL text. That is, there are many statements above a certain level of complexity, or containing provider-specific language, in which the parser built into the CCE gets lost and must give up.

Performance, using the parsing approach, varies widely under different circumstances and in some cases may be unacceptable. The problem is not in the CCE itself but in the wide range of potentially expensive calls it makes back into the provider. The first performance hit is caused by the set of "SELECT *" calls that the CCE makes on base tables to get the base table structures. Not only does this produce the provider-specific overhead of result set generation but, because some providers and drivers (notably Access ODBC) are not equipped to optimize for "WHERE 1=0", all the data is generated as well (only to be discarded). The second major hit comes with the catalog functions that implement the key column attributes. On some providers and drivers (notably Oracle ODBC), the key functions can be incredibly expensive (ranging from 15 to 30 seconds).

Therefore, while parsing works well and is time-tested in a large number of common cases, it is by no means foolproof.

Internal Update Property Structures

The internal update property structures are important because their design dictates many aspects of the behavior of the rest of the updating code. The way in which update properties are stored determines which operations will be efficient or expensive and which base table or result set relationships are ambiguous or easily dealt with. The basic information that needs to be stored is the set of mappings between a result set column and the base table columns to which the result set column corresponds. The following diagram illustrates these mappings.

Internal update property structures

The diagram shows the mappings from a 6-column result set selected from two base tables. The update properties handle points to the first base table structure (Base Table Structure #1). In this first structure, four fields—1, 2, 4, and 5—of the 6-column result set are mapped to three fields—"FieldName X", "FieldName Y", and "FieldName Z"—of "Table1". Two of the result set columns—4 and 5—derived from "Table1" actually represent the same base table column, "Table1.Z". Base Table Structure #1 links to the next base table structure (Base Table Structure #2). In this second structure, two fields—3 and 6—of the 6-column result set are mapped to two fields—"FieldName J" and "FieldName K"—of "Table2". Since Base Table Structure #2 does not link to any other base table structures and all the result set fields are mapped to base tables, the mapping ends.

The update properties are organized by base table and encoded into a linked list of base table structures. In this way, a pointer to the head base table in the list can be passed around freely as a "handle" to the entire set of update properties. Each field in the result set has a corresponding field structure in at most one of the base table structures. Some fields (such as calculated columns like those created by "SELECT Price * Quantity AS Total FROM Orders") have no single base table field to which updates can reasonably be applied, so those fields have no update properties and are not represented in the base table field structures.

The base table field structures contain the minimum information about base table columns that is needed to correctly update them. This includes the name to update with (when columns are aliased, this name might not be the same as the result set name) and whether it is a key column. The "Field Map" has an entry for each result set column—the entry either points to a base table field structure (indicating a one-to-one correspondence) or is empty (indicating no correspondence within this base table).

Executing Updates

In the CCE, remote updates are performed by traversing the changed rows in the rowset, analyzing the local changes, and then executing corresponding SQL statements (DELETE, INSERT, and UPDATE) directly against the provider's base tables. (This update model is based on code that has shipped in Microsoft Visual FoxPro for many years.)

The internal property structures (described earlier in the section "Internal Update Property Structures") make updating by base table fairly efficient. The first task in updating is to determine what columns in what base tables will be affected and then to send out column modification statements table by table. A standard UPDATE statement for a given base table must describe the following:

  1. New-valued columns, or which base table columns have new values (the SET clause)
  2. Row identity, or base table row in which colulmns are to be updated (the WHERE clause)

Nearly the same procedure is followed for INSERT and DELETE statements, except that these statements require only half the work of an UPDATE statement. INSERT statements just need to describe the inserted columns (as in item 1 of the preceding list), and DELETE statements just need to describe the row to be deleted (as in item 2).

In generating the text of the SQL statement, the gathered properties (from the metadata-gathering phase) map to the output statement in a straightforward way. The table to be affected by the operation is generated from any and all names and qualifiers corresponding to SCHEMANAME, CATALOGNAME, and TABLENAME that have been collected while gathering the update properties. These are strung together according to the reported qualifying character on the underlying OLE DB session object (DBLITERAL_CATALOG_SEPARATOR).

Note DBLITERAL_SCHEMA_SEPARATOR was not part of the original OLE DB specification and is not yet supported. A period (.) is automatically used for schema separation.

The column names inside the SQL text are simply the names collected as COLUMNNAME during the gathering of update properties.

Note To allow for the most robust use with more complicated extended character sets (for example, Japanese), every identifier that is placed into the statement text (table, schema, catalog, and column) is first wrapped in the quote character specified as DBLITERAL_QUOTE on the underlying session object.

The column values placed into the text are parameterized—that is, a question mark is inserted as a value placeholder (for example, "WHERE au_id = ?") and the actual value is sent out-of-band as strongly typed parameter data. The primary motivation for this is that the command parameter mechanism in OLE DB provides rich description of the parameter value and type and allows precision to be preserved much more consistently and cheaply than when values are converted back and forth into raw text.

New-Valued Columns

The CCE code first gathers a list of columns (in the form of a bitmap) that have changed in the result set for the row being updated. Then, for each base table in turn, the changed-column list is masked with the base table field map to get the set of columns to update for this base table. The resulting subset of columns is then iterated, inserting the base table field names and the new column values into the SQL statement and parameter list one by one.

Row Identity

In a disconnected environment, unambiguously identifying which back-end row to update is problematic. The time-honored method in an SQL context is to treat particular fields of a given row's data (the key fields) as uniquely characterizing that row. The idea is that the key for a given record rarely, if ever, changes, so it can be used as an identifier for the row in which to make updates to the non-key fields. Therefore, in the CCE, the key fields are used in the WHERE clause to identify the row update. Without key information for all of the base tables in a result set, attempting to update causes an error. The sole exception to this is a single-table query, where an update is attempted by sending every available (non-long-valued) column in the WHERE clause. This ability should be used cautiously because if the existing row values are not enough to uniquely identify the row, multiple rows will be affected by the update—an error will be generated, but any damage done must be undone manually.

The importance of being able to gather key field information for uniquely identifying rows should not be underestimated: Deliberate key-field support is a hallmark of good database design and is crucial in a disconnected environment. Of course, as a data tool, the CCE always has the goal of avoiding the enforcement of "arbitrary" restrictions on the updatability of a result set—it would be unfortunate to completely disable updating simply because of a poorly formed query or an incomplete back-end database design. This was the motivation behind the loophole described above—permitting updates to single tables without keys. But it is important to keep in mind that the more these restrictions are relaxed, the less predictable the update behavior becomes. Consistently using key fields in base tables and including them in the result set projections is the only way to be confident that a one-row change in the CCE will effect a one-row (per base table) change on the back end.

Conflict Detection

Beyond its use as a row identifier, the WHERE clause is also used to detect optimistic concurrency conflicts—to compensate for the disconnected nature of the user's local data copy.

In a multi-tier, optimistic-locking environment, in the absence of some server-change notification system, the user is forced to assume ("optimistically") that the field values with which he or she is working locally have not been changed on the server by another user working concurrently. Of course, whenever any time has elapsed between the read operation (fetch) and the write operation (update), that assumption can be wrong. Therefore, the user needs some way of guaranteeing that each update is performed on back-end data that has not meanwhile been changed beyond some specific level of acceptability, and the WHERE clause can be used for this purpose.

The approach taken in the CCE is to insert additional identifying fields and their original values in the WHERE clause—fields that the user is assuming haven't been changed yet by other users. If any of the fields have changed, the back-end engine will presumably be unable to find a row matching the obsolete identifying criteria and the update will not be performed.

Because the degree to which conflicts need detecting is a dependent quantity with no single correct value, the question of which extra fields to include—in addition to the key fields—should be left to the user. Currently, the CCE allows for the following options via the provider-specific DBPROP_ADC_UPDATECRITERIA property:

  • Fields the user has modified in this row (the default)
  • "Timestamp" fields
  • All fields
  • None (key fields only)

Long-valued columns (sometimes called BLOBS, memos, or TEXT columns) are not included in the WHERE clause because most providers do not support comparison operations on these columns.

Note For more information about the DBPROP_ADC_UPDATECRITERIA property, see Supported Properties of the Cursor Service in the Cursor Service for OLE DB documentation in the MDAC SDK.

Batch Updates

Each update statement sent represents a negotiated communication between the CCE and the server. Given the desire to provide good performance over a large set of network layouts and server capabilities, the potential expense of each communication cannot be ignored. The main response to this concern is in the batching of updates. Some providers allow multiple update statements to be sequentially chained together with semicolons and executed as one statement, and the CCE attempts to do this.

Given the high fixed cost of server executions and connection negotiation, especially over large networks such as the Internet, sending statements in batches can confer a very real performance advantage. The degree of advantage is highly network-specific and provider-specific—in fact, some providers do not accept multiple statements, or they require special syntax (for example, Oracle's BEGIN/END). This is another feature where control needs to be in the user's hands; therefore, the CCE can accept any integer value as a batch size. (The default is 15.) However, memory can quickly become a problem as the batch size grows—a single average update statement for one base table with several fields can easily take a kilobyte of memory to store before execution.

The process of performing these batch updates begins with an array of rows that need updating—the rows that the user has changed locally but has not yet instructed the CCE to submit. First, the linked list of base tables is traversed, to build a series of SQL statements for each table. Each base table ends up with a single SQL statement containing the concatenated updates for the first <batch size> rows on this table. Each table's batch statement is then executed against the back end. After all the statements for a single batch have been successfully sent, the local updates are performed on that same batch—that is, it is recorded in the client temp tables that this set of rows no longer has pending updates. This process is repeated for the next <batch size> rows, and so forth, until the original array is fully traversed.

Note Statements are sent in the order that the base tables appear in the internal linked list. This is interesting in cases where table relationships or server integrity constraints dictate that the ordering of updates might be significant. For example, when inserting a row into a two-table join, table A might have a foreign key constraint on table B, in which case the insert for table B needs to occur first. The internal linked list is built in reverse order from the order that the fields appear in the projection. That is, the base table belonging to the first result set column will be the last base table in the internal linked list (and thus the last table to get an update statement sent for a particular row). Therefore, in this example, including fields from table A before those from table B in the result set will ensure that the inserts are executed in the correct order.

Analyzing Results

Executing the update statements is only half the job. Each statement execution can fail in various ways, and to respond appropriately, the CCE must be able to handle a wide range of possible outcomes.

Return Codes

The first piece of information to analyze is the return code from the OLE DB ICommand::Execute call, which is an HRESULT that can be tested for success or failure. Failure codes are returned for a wide variety of reasons, from bad parameter values to back-end integrity constraint violations, and are often accompanied by explanatory text via the Automation error mechanisms. The CCE attempts to return this error intact to the user. A failed return code indicates that the update was not performed.

Rows Affected

A successful return code is not a guarantee that the statement execution resulted in the intended update behavior. Most importantly, problems in row identification because of key field changes or optimistic concurrency violations are not considered errors in execution.

The root problem is that while the CCE knows that each statement is meant to modify exactly one row—no more and no fewer—there is no accepted way to communicate this fact to a general-purpose SQL engine. Therefore, the second piece of information needed after a successful execution is the pcRowsAffected out-parameter for ICommand::Execute, in which the provider is supposed to indicate how many records were affected by the just-executed statement. A value of 1 is expected and is the best indication that the update was performed as intended. A value of 0 indicates that the row in the update statement no longer exists in the form in which it was described and therefore no update was performed. Depending on how much conflict-detection data was sent in the WHERE clause, this means either that the row was deleted or that certain of its values were already changed on the server. In either case, this should be considered an optimistic concurrency conflict and the user should be given control of reconciling the problem.

A value greater than 1, indicating that multiple rows were affected, means that the row-identity portion of the WHERE clause failed at its job of uniquely describing the row to be updated. Of course, it is not illegal in principle to submit a WHERE clause that identifies more than one row. However, the CCE generates each statement in response to user changes in a single result set row, so in this context, multiple-row changes on the back end are to be considered unintended behavior, caused by insufficient key field information. This error is reported to the user, but the CCE is unable to automatically undo the multiple-row update because the server considers it a legal, successful operation and commits it.

The only other legal return value in pcRowsAffected is –1, indicating that the provider cannot determine how many rows were affected. Dealing with this value is troublesome for the CCE because, as should now be clear, the number of rows affected is very important in determining the actual success or failure of the update. Furthermore, OLE DB does not have any provision for reporting the update status of a row as "unknown," so the CCE is even unable to report the uncertainty. Currently, success is assumed.

Batch Results

The issues that arise in interpreting the results of a batched update statement are the same as those for single update statements, except with the additional layer of complexity involved in returning multiple results from a single execution. The OLE DB mechanism devised for this situation is to request the IMultipleResults interface at ICommand::Execute time; successive calls to GetResults on this interface will then return HRESULTs and pcRowsAffected values for each update statement in the batch. The interpretation of these values proceeds in the same way as for single statements.

Additional problems that might occur when dealing with batch statements can be summarized as follows:

  • IMultipleResults is an optional interface. If it is not implemented, no information can be obtained about the success of any statement after (and sometimes including) the first one in the batch.
  • Even when IMultipleResults is implemented, because of incomplete provider support, pcRowsAffected values of –1 for later statements in the batch are more likely than for the first statement.

Behavior on Error

As the specification for array-based interfaces such as IRowsetUpdate::Update dictates, the sending and analyzing of row updates continues through all non-catastrophic errors, concurrently recording results in the status arrays. In the simple case, rows that had errors are left in a pending state and rows that succeeded are committed locally.

However, if a failure HRESULT from ICommand::Execute or IMultipleResults::GetResults is obtained, or an unexpected pcRowsAffected value (anything other than 1) is obtained while processing multi-table updates, there is a very good chance that the local data will be out of sync with the server data. For example, if the statement that produced the error was for the second of two base tables that were being updated, the following inconsistency results:

  • The result set columns belonging to the first base table have already been successfully updated for all rows in this batch.
  • The result set columns belonging to the second base table have probably not been successfully updated for any rows in this batch, presuming that the server is treating the batch atomically.

Currently, there is no choice but to leave the local status for all the rows in this batch unchanged, because no one row in it has been successfully updated across all of the base tables. In most cases, the best course of action at this point is to refresh the row values (Recordset.Resync at the ADO level or IRowsetRefresh::RefreshVisibleValues at the OLE DB level) and reapply the changes from there.


OLE DB for the ODBC Programmer

by Michael Pizzo and Jeff Cochran

Introduction

This paper presents an introduction to data access programming using Microsoft® OLE DB, part of the Microsoft Data Access Components (MDAC) Software Developer's Kit (SDK). The goal of this paper is to aid Open Database Connectivity (ODBC) programmers in understanding and using OLE DB. It discusses the relationship of ODBC calls to corresponding OLE DB methods and the related programming issues. Basic OLE DB concepts are discussed, whereas advanced OLE DB features, such as notifications, IPersist * objects, transaction objects, and coordinated transactions, are not covered.

OLE DB and ODBC are application programming interfaces (APIs) designed to provide access to a wide range of data sources. A data source consists of the data, its associated database management system (DBMS), the platform on which the DBMS exists, and the network used to access that platform.

ODBC is designed to provide access primarily to SQL data in a multiplatform environment. OLE DB is designed to provide access to all types of data in a Component Object Model (COM) environment. OLE DB includes the SQL functionality defined in ODBC but also defines interfaces suitable for gaining access to data other than SQL data.

COM facilitates application integration by defining a set of standard interfaces, groupings of semantically related functions through which one application accesses the services of another. Interfaces are the binary standard for component object interaction. Each interface contains a set of functions that define a contract between the object implementing the interface and the client using it.

OLE DB is a COM-based API; for more information about COM, see the Microsoft OLE Programmer's Reference. Each OLE DB interface consists of a set of related methods. The full functionality of OLE DB is factored into a number of different interfaces. Each data provider will implement some subset of these interfaces.

COM and OLE DB

Some of the key COM concepts that you should be aware of include the following:

  • Interface factoring

    A COM interface is a set of related methods. A single COM object may support multiple interfaces at the same time. The consumer of an object can move between any interfaces supported on that object by calling QueryInterface. (See the following bullet, "Interface negotiation.") If an object supports an interface, it supports all of the methods within that interface. Thus, once the consumer has determined that a particular interface is supported, it understands how to interact with the object. New interfaces that augment the functionality already supported by the existing interfaces can be added later, but methods can never be added or removed from existing interfaces.

    Because an object must support either all or none of the methods within an interface, interfaces are generally factored according to functionality. For example, if an object supports reading data and does or does not support writing data, the methods for reading data and those for writing data would appear in two different interfaces. Only the objects that supported writing data would support the interface containing the methods for writing data.

    OLE DB makes extensive use of interface factoring. Individually supportable functionality, such as different levels of scrollability in a result set or command preparation and parameter support, are factored into different interfaces. Each object has one or more required interfaces that encapsulate base functionality for that object and that can expose extended functionality by implementing one or more optional interfaces. The OLE DB consumer can determine what extended functionality the provider supports by querying for these optional interfaces.

  • Interface negotiation

    IUnknown is implemented by all COM objects. All other interfaces inherit, directly or indirectly, from this interface. It has three methods: QueryInterface, AddRef, and Release. Each interface is identified by a globally unique identifier (GUID), by which it is known at compile time. To determine if an object supports a particular interface, the client calls QueryInterface on that object. If an object supports the requested interface, QueryInterface returns a pointer to the interface. The interface identifier (IID) allows the client to dynamically determine, by way of a call to IUnknown::QueryInterface, the capabilities of other objects and to get the pointers to needed interfaces. Every interface that is obtained directly (by calls to QueryInterface) or indirectly (by calls to a method that returns an interface) must be released by calling the Release method of that object.

  • Reference counting

    Reference counts are kept on each instance of a pointer to an interface that is derived from IUnknown. This ensures that the object is not destroyed before all references to it are released.

  • Memory management

    COM uses two kinds of memory: local application task memory and shared memory. All task memory allocated by the COM libraries and by the object handlers is allocated using either an application-supplied allocator or the default allocator provided by COM.

    The standard memory management model in COM requires that the callee allocates and the caller frees. OLE DB generally follows this model, except for certain cases where performance can be gained by the caller allocating and reusing the same piece of memory or, in some special cases, by the callee giving the caller pointers to callee-owned data. In the latter case, the caller is not allowed to write to or free the memory.

  • Unicode

    All COM interface methods pass Unicode rather than ANSI strings. OLE DB follows this convention, except for getting and setting ANSI data that resides in tables.

Components of OLE DB

OLE DB providers can be classified as data providers and service providers. A data provider is an OLE DB component that owns data and exposes it in a tabular form. Some examples are relational database systems and spreadsheets. A service provider is any OLE DB component that does not own the data but encapsulates some service by producing and consuming data through OLE DB interfaces. Examples are query processors and transaction managers.

OLE DB Objects

The following illustration shows the core object model of OLE DB.

OLE DB Object Model

  • Data source object

    The data source object is the initial object returned from an enumerator (see the bullet, "Enumerator Object," that follows), generated by binding a file moniker or other moniker to a data source, or instantiated by calling the COM function CoCreateInstance with a given OLE DB data provider's unique class identifier (CLSID). It encapsulates the functionality of the ODBC environment as well as the connection and informational properties of the ODBC connection.

  • Session object

    A session object defines the scope of a transaction and generates rowsets from the data source. If the provider supports commands, the session also acts as a command factory. The data source object can also support interfaces for describing schema information and for creating tables and indexes for providers that support that functionality. Along with the data source object, the session encapsulates the functionality of the ODBC connection. Calling IDBCreateSession::CreateSession creates a session from the data source object. There can be multiple sessions associated with a data source object.

  • Command object

    If a provider supports building and executing queries, it exposes a command object. A command object is generated from a session object. It is used to specify, prepare, and execute a DML query or DDL definition and associated properties. The command encapsulates the general functionality of an ODBC statement in an unexecuted state. There may be multiple commands associated with a single session.

  • Rowset object

    A rowset object is a shared data object that represents tabular data, such as a result set returned by executing a query. Minimally, rowsets can be generated by calling IOpenRowset::OpenRowset on the session. All providers are required to support this minimal functionality. If the provider supports commands, rowsets are used to represent the results of row-returning queries. There are a number of other methods in OLE DB, such as the schema functions, that return information in the form of a rowset. A rowset encapsulates the general functionality of an ODBC statement in the executed state. There may be multiple rowsets associated with a single session or command.

Other OLE DB Objects

The following objects are also defined in OLE DB. They provide recursive data source enumeration, enhanced transaction control, and extended error retrieval.

  • Enumerator object

    Enumerator objects list the data sources and enumerators visible to that enumerator. This is similar to the information provided by SQLDataSources, except that the information can be recursive.

  • Transaction object

    In addition to supporting ITransactionLocal on the session, providers that support transactions can optionally support the creation of a transaction object. Transaction objects provide more advanced transaction functionality, such as the registration of transaction notifications.

  • Error object

    In addition to the return codes and status information returned by each method in OLE DB, providers can optionally expose an OLE DB error object for extended error information, such as a description of the error or the appropriate SQLSTATE. This is similar to the information returned by SQLError or SQLGetDiagRec.

Note explicitly stated otherwise, ODBC 3.0 functions and function names are used throughout. For information on mapping ODBC 1.x and 2.x to their ODBC 3.0 equivalents, see the current ODBC documentation.

Basic OLE DB

This section describes the basic concepts and procedures defined in OLE DB, such as initializing the environment, locating and connecting to a data source, executing a command, and retrieving data from a rowset.

Application Flow

The application flow in OLE DB is similar to the application flow in ODBC. In both cases, the application does the following:

  • Initializes the environment
  • Connects to a data source
  • Creates and executes a command
  • Processes results, if any
  • Cleans up

A typical OLE DB consumer may look like the following code example. For the complete source code listing, see Appendix A. The individual routines in this sample are described in more detail in the following sections. The flow of control is as follows:

  1. Initialize COM library.
  2. Connect to a data source object.
  3. Create and execute a command.
  4. Process the results.
  5. Release objects and uninitialize COM library.
/********************************************************************
* General OLE DB application main()
********************************************************************/
#define UNICODE
#define _UNICODE
#define DBINITCONSTANTS // Initialize constants...
#define INITGUID    // ...once in each app.

#include <windows.h>

#include <oledb.h>  // OLE DB include files
#include <oledberr.h>
#include <msdaguid.h> // ODBC provider include files
#include <msdasql.h>

// Prototypes
HRESULT myInitDSO(IDBInitialize** pIDBInitialize);
HRESULT myCommand(IDBInitialize *pIDBInitialize, IRowset** pIRowset);
HRESULT myGetData(IRowset* pIRowset);
void DumpError(char* text);

// Global task memory allocator
IMalloc* g_pIMalloc = NULL;

int main()
   {
   IDBInitialize* pIDBInitialize = NULL;
   IRowset* pIRowset = NULL;

   // Init COM library and set up the DLLs; see "Initializing the
   // Environment."
   CoInitialize(NULL);

// Get the task memory allocator.
   if (FAILED(CoGetMalloc(MEMCTX_TASK, &g_pIMalloc)))
      goto EXIT;

   // Connect to the data source; see "Initializing a Data Source."
   if (FAILED(myInitDSO(&pIDBInitialize)))
      goto EXIT;

   // Get a session, set and execute a command; see "Getting a
   // Session and Executing a Command."
   if (FAILED(myCommand(pIDBInitialize, &pIRowset)))
      goto EXIT;

   // Retrieve data from rowset; see "OLE DB Rowsets."
   myGetData(pIRowset);

EXIT:
   // Clean up and disconnect.
   if (pIRowset != NULL)
      pIRowset->Release();

   if (pIDBInitialize != NULL)
      {
      if (FAILED(pIDBInitialize->Uninitialize()))
      {
      // Uninitialize is not required, but it will fail if an
      // interface has not been released. We can use it for
      // debugging.
      DumpError("Someone forgot to release something!");
      }
      pIDBInitialize->Release();
      }

   if (g_pIMalloc != NULL)
      g_pIMalloc->Release();

   CoUninitialize();

   return (0);
   }

Initializing the Environment

In ODBC, the application generally dynamically links to the ODBC Driver Manager (Odbc32.dll). The Driver Manager loads and directs calls to the appropriate driver.

In OLE DB, initialization of the environment is achieved by a call to CoInitialize, which initializes the COM library. This is shown in the preceding code example. After the COM library is initialized, the proper data provider is loaded by the system according to its class ID and calls are made directly to the provider.

Initializing a Data Source

The data source object exposes the IDBInitialize and IDBProperties interfaces that contain the methods to connect to a data source. The authentication information such as user ID, password, and the name of the data source are specified as properties of the data source object by calling IDBProperties::SetProperties. The method IDBInitialize::Initialize uses the specified properties to connect to the data source.

In ODBC, establishing a connection involves the following steps:

  1. Call SQLAllocHandle to allocate a connection handle.
  2. Build a connection string containing keywords for authentication information, such as user ID, password, and the name of the data source.
  3. Call SQLDriverConnect providing the connection string and other information, such as level of prompting and the application's window handle where appropriate.

In OLE DB, the equivalent functionality is achieved by the following steps:

  1. Build an array of property structures describing the authentication information, such as user ID, password, and the name of the data source, as well as the level of prompting and the application's window handle when appropriate.
  2. Call IDBProperties::SetProperties to set initialization properties. (For more information about properties, see the following section, "Getting and Setting Properties.")
  3. Call IDBInitialize::Initialize to initialize the data source object.

As can be seen, the model in OLE DB is similar to the model in ODBC. The primary differences are:

  • OLE DB uses a well-defined set of property structures to represent the initialization and connection information, rather than building or parsing keywords within a connection string.
  • The set of property structures is used for all of the initialization/connection information.
  • The set of available initialization properties can be queried through IDBProperties::GetPropertyInfo.
  • Rather than returning a connection string, the user can simply request the current set of initialization property values.
  • Setting the initialization properties is separate from actually initializing (connecting) the data source. This allows the consumer to set, persist, and retrieve connection information without initializing (connecting) the data source.

The following code example shows a routine that initializes a data source object. The general flow of control is as follows:

  1. Create an instance of the OLE DB provider (in this case, the ODBC Provider for OLE DB).
  2. Set the initialization properties.
  3. Call Initialize to initialize the data source object, using the supplied properties.
/********************************************************************
* Initialize the data source.
********************************************************************/
HRESULT myInitDSO
   (
   IDBInitialize** ppIDBInitialize   // [out]
   )
   {
   // Create an instance of the MSDASQL (ODBC) provider.
   CoCreateInstance(CLSID_MSDASQL, NULL, CLSCTX_INPROC_SERVER,
      IID_IDBInitialize, (void**)ppIDBInitialize);

   if (*ppIDBInitialize == NULL)
      {
      return (E_FAIL);
      }

   // See "Getting and Setting Properties."
   if (FAILED(mySetInitProps(*ppIDBInitialize)))
      {
      return (E_FAIL);
      }

   if (FAILED((*ppIDBInitialize)->Initialize()))
      {
      DumpError("IDBInitialze->Initialize failed.");
      return (E_FAIL);
      }

   return (NOERROR);
   }

Getting and Setting Properties

Properties are used in OLE DB to specify options, such as initialization information on the data source object or supported properties of a rowset, as well as to discover properties of certain objects, such as the updatability of a rowset.

Properties in OLE DB are similar to the environment, connection, and statement attributes in ODBC, with the following exceptions:

  • In OLE DB, the provider can be queried for a list of all supported properties.
  • In OLE DB, properties are associated with property groups. Property groups are identified by a GUID. This allows third parties to define properties within their own property group rather than trying to reserve ranges within a single set of attribute values.
  • Instead of setting and retrieving properties individually, multiple properties can be set or retrieved from multiple groups in a single call. This is done by building an array of property sets, where each property set contains an array of property structures from a single property group.

OLE DB consumers can do the following:

  • Enumerate, set, and retrieve properties on a data source object using IDBProperties.
  • Set or retrieve properties on a session using ISessionProperties.
  • Set or retrieve properties on a command using ICommandProperties.
  • Retrieve properties and information about a rowset using IRowsetInfo.

The following table shows the property groups in OLE DB and their GUIDs.

Property group | Property group identifier (GUID)
Column | DBPROPFLAGS_COLUMN
Data Source | DBPROPFLAGS_DATASOURCE
Data Source Creation | DBPROPFLAGS_DATASOURCECREATE
Data Source Information | DBPROPFLAGS_DATASOURCEINFO
Data Source Initialization | DBPROPFLAGS_DBINIT
Index | DBPROPFLAGS_INDEX
Rowset | DBPROPFLAGS_ROWSET
Session | DBPROPFLAGS_SESSION
Table | DBPROPFLAGS_TABLE

The following structure contains an array of values of properties from a single property set:

typedef struct tagDBPROPSET
   {
   DBPROP __RPC_FAR* rgProperties;  // Pointer to an array of
                                    // DBPROP structures.
   ULONG cProperties;  // Count of properties
                       // (DBPROPS) in the array.
   GUID guidPropertySet;  // A GUID that identifies the
                          // property set to which the
                          // properties belong.
   } DBPROPSET;

The following structure contains information about a single property:

typedef struct tagDBPROP
   {
   DBPROPID dwPropertyID;  // ID of property within a
                           // property set.
   DBPROPOPTIONS dwOptions;  // Property is required?
                             // Optional?
   DBPROPSTATUS  dwStatus;  // Status returned by the
                            // provider indicating success
                            // or failure in setting or
                            // getting the property.
                            // Enumerated values are:
                            //  DBPROPSTATUS_OK
                            //  DBPROPSTATUS_NOTSUPPORTED
                            //  DBPROPSTATUS_BADVALUE
                            //  DBPROPSTATUS_BADOPTION
                            //  DBPROPSTATUS_BADCOLUMN
                            //  DBPROPSTATUS_NOTALLSETTABLE
                            //  DBPROPSTATUS_NOTSET
                            //  DBPROPSTATUS_NOTSETTABLE
                            //  DBPROPSTATUS_CONFLICTING
   DBID colid;    // Optional, ordinal column
                  // property applies to. If the
                  // property applies to all
                  // columns, colid should be set
                  // to DB_NULLID.
   VARIANT vValue;  // Value of the property.
   } DBPROP;

The following code example shows how an application sets initialization properties on a data source object. The code sets four properties within a single property group. The general flow of control is as follows:

  1. Allocate an array of property structures.
  2. Allocate an array of a single property set.
  3. Initialize common property elements for the properties.
  4. Fill in the following properties:
    • Level of desired prompting (similar to DriverCompletion argument in SQLDriverConnect)
    • Data source name (similar to DSN= element of the ODBC connection string)
    • User name (similar to the UID= element of the ODBC connection string)
    • Password (similar to the PWD= element of the ODBC connection string)
  5. Set the property set to the array of properties, and specify that the properties are from the initialization property group.
  6. Get the IDBProperties interface.
  7. Call SetProperties on the interface.
/********************************************************************
* Set initialization properties on a data source.
********************************************************************/
HRESULT mySetInitProps
   (
   IDBInitialize*  pIDBInitialize   // [in]
   )
   {
   const ULONG nProps = 4;
   IDBProperties* pIDBProperties;
   DBPROP InitProperties[nProps];
   DBPROPSET rgInitPropSet;
   HRESULT hr;

   // Initialize common property options.
   for (ULONG i = 0; i < nProps; i++ )
      {
      VariantInit(&InitProperties[i].vValue);
      InitProperties[i].dwOptions = DBPROPOPTIONS_REQUIRED;
      InitProperties[i].colid = DB_NULLID;
      }

   // Level of prompting that will be done to complete the
   // connection process.
   InitProperties[0].dwPropertyID = DBPROP_INIT_PROMPT;
   InitProperties[0].vValue.vt = VT_I2;
   InitProperties[0].vValue.iVal = DBPROMPT_NOPROMPT;

   // Data source name--see the sample source code included with the
   // Data Access SDK.
   InitProperties[1].dwPropertyID = DBPROP_INIT_DATASOURCE;
   InitProperties[1].vValue.vt = VT_BSTR;
   InitProperties[1].vValue.bstrVal =
      SysAllocString(OLESTR("OLE_DB_NWind_Jet"));

   // User ID
   InitProperties[2].dwPropertyID = DBPROP_AUTH_USERID;
   InitProperties[2].vValue.vt = VT_BSTR;
   InitProperties[2].vValue.bstrVal = SysAllocString(OLESTR(""));

   // Password
   InitProperties[3].dwPropertyID = DBPROP_AUTH_PASSWORD;
   InitProperties[3].vValue.vt = VT_BSTR;
   InitProperties[3].vValue.bstrVal = SysAllocString(OLESTR(""));

   rgInitPropSet.guidPropertySet = DBPROPSET_DBINIT;
   rgInitPropSet.cProperties = nProps;
   rgInitPropSet.rgProperties = InitProperties;

   // Set initialization properties.
   pIDBInitialize->QueryInterface(IID_IDBProperties, (void**)
      &pIDBProperties);
   hr = pIDBProperties->SetProperties(1, &rgInitPropSet);

   SysFreeString(InitProperties[1].vValue.bstrVal);
   SysFreeString(InitProperties[2].vValue.bstrVal);
   SysFreeString(InitProperties[3].vValue.bstrVal);

   pIDBProperties->Release();

   if (FAILED(hr))
      {
      DumpError("Set properties failed.");
      }

   return (hr);
   }

Getting a Session and Executing a Command

The data source object exposes the IDBCreateSession interface through which a session object can be created. A session defines transaction scope and acts mainly as a command generator by supporting the IDBCreateCommand interface. Commands contain a DML query or a DDL definition. The execution of a row-returning command yields a rowset object.

The session object in OLE DB is similar to the ODBC connection handle. However, the connection handle in ODBC is used for establishing connections as well as scoping transactions, so the application must allocate and connect a separate connection handle for each concurrent transaction. In OLE DB, you can have multiple session objects on one initialized data source object, which means you can have multiple concurrent transactions without having to make multiple connections. (Where necessary, the provider makes additional connections using the connection information provided in the initialization of the data source object.)

The command object in OLE DB is similar to the ODBC statement handle in the unexecuted state. Like the ODBC connection handle, which can have several statement handles, a session object can have several command objects.

An ODBC application performs the following steps to execute a command:

  1. Calls SQLAllocHandle to allocate a statement.
  2. Calls SQLSetStmtAttr to set various attributes that affect how the command is executed (such as query time-out) and how the cursor is opened (such as scrollability, updatability, and so on).
  3. Calls SQLPrepare if it wants to prepare the statement for repeated execution.
  4. Calls SQLExecute or SQLExecDirect to execute the query.

To use a command, an OLE DB consumer performs these steps:

  1. Calls IDBCreateCommand to create a command.
  2. Calls ICommandProperties::SetProperties to set various attributes that affect how the command is executed (such as query time-out), as well as requesting properties to be supported by the resulting rowset. Typical properties include scrollability, updatability, the number of active row handles a consumer can hold at one time, sensitivity to changes outside the rowset, and so on.
  3. Calls ICommandText::SetCommandText to specify the command text, along with a GUID representing the command's dialect. The standard dialect for ANSI SQL commands is DBGUID_DBSQL.
  4. Calls ICommandPrepare::Prepare if it wants to prepare the query for repeated execution.
  5. Calls ICommandText::Execute to execute the command.

The following code example shows setting and executing a command, and retrieving a rowset. The general flow of control is as follows:

  1. Obtain the IDBCreateSession interface.
  2. Call CreateSession to create a session object that scopes the transaction boundaries within the current connection.
  3. Call CreateCommand to create a command object within the transaction.
  4. Call SetCommandText to set the command text.
  5. Call Execute to execute the command.
  6. Release the command object.
  7. Return the rowset object.
/********************************************************************
* Execute a command, retrieve a rowset interface pointer.
********************************************************************/
HRESULT myCommand
   (
   IDBInitialize*  pIDBInitialize,  // [in]
   IRowset** ppIRowset  // [out]
   )
   {
   IDBCreateSession* pIDBCreateSession;
   IDBCreateCommand* pIDBCreateCommand;
   IRowset* pIRowset;
   ICommandText* pICommandText;
   LPCTSTR wSQLString = OLESTR("SELECT CompanyName,
                        City, Phone, Fax")
                        OLESTR(" FROM Customers")
                        OLESTR(" ORDER BY CompanyName,
                        City");
   LONG cRowsAffected;
   HRESULT hr;

   // Get the OLE DB session object.
   if (FAILED(pIDBInitialize->QueryInterface(IID_IDBCreateSession,
      (void**) &pIDBCreateSession)))
      {
      DumpError("Session initialization failed.");
      return (E_FAIL);
      }

   // Create the session, getting an interface for command creation.
   hr = pIDBCreateSession->CreateSession(NULL, IID_IDBCreateCommand,
      (IUnknown**) &pIDBCreateCommand);
   pIDBCreateSession->Release();
   if (FAILED(hr))
      {
      DumpError("Create session failed.");
      return (hr);
      }

   // Create the command object.
   hr = pIDBCreateCommand->CreateCommand(NULL, IID_ICommandText,
      (IUnknown**) &pICommandText);
   if (FAILED(hr))
      {
      DumpError("Create command failed.");
      return (hr);
      }
   pIDBCreateCommand->Release();

// The command requires the actual text as well as an indicator
   // of its language and dialect.
   pICommandText->SetCommandText(DBGUID_DBSQL, wSQLString);

   // Execute the command.
   hr = pICommandText->Execute(NULL, IID_IRowset, NULL,
      &cRowsAffected, (IUnknown**) &pIRowset);
   if (FAILED(hr))
      {
      DumpError("Command execution failed.");
      }
   pICommandText->Release();

   *ppIRowset = pIRowset;
   return (hr);
   }

OLE DB Rowsets

A rowset provides a standard way to work with a multiset of rows where each row contains one or more columns of data. This provides a standard way for all OLE DB data providers to expose data in tabular form.

A rowset can be obtained in several ways. The first method, supported by all providers, uses the IOpenRowset interface. IOpenRowset provides for the simple case of retrieving all the data from the table. This is equivalent to executing SELECT * FROM table. The second method, supported by SQL providers, involves creating and executing a command to get a rowset that meets specific criteria, such as SELECT * FROM table WHERE condition. The ICommandProperties interface on the command can be used to specify the interfaces and properties that must be supported by the rowsets returned by ICommand::Execute.

Conceptually, rowsets are similar to result sets in ODBC; however, their implementation is different.

In ODBC, when the application calls SQLFetch or SQLGetData to retrieve data, the data is read from the database into the application's memory. At that point, the application owns the data; neither the ODBC driver nor other components have access to that data. This model works fine provided that the following conditions are true:

  • The application is the only client of the data.
  • A single component provides all of the functionality needed by the application.
  • The application is in an environment in which it can maintain a connection to the database that keeps the state of the application's position within the data, concurrency information for data retrieved, and so on.

OLE DB rowsets are not only designed to provide a high performance solution for the preceding scenarios but are also designed to ensure the following behaviors:

  • Multiple controls can work together on the same data. For example, a grid control and a chart control can model the same results: If the grid control updates the data, the chart control gets a notification and can redraw the graph accordingly.
  • Multiple components can work together to add incremental functionality to a single instance of the data. The data does not have to be copied to multiple components, and method calls don't have to go through a hierarchy of components. Each component can operate directly on the data.
  • The application can work in a disconnected environment with a stand-alone data object that maintains state, concurrency, and other information.

The OLE DB rowset basically takes the memory buffer out of the application and puts it in a stand-alone, shared data object. Rather than the application buffering data in its own memory, components access data in this shared memory through high-performance binding descriptions known as accessors. Because, in many cases, the application merely retrieves pointers to existing data and multiple components can access the same copy of the data, data access can often be faster than copying the data into the application's own buffers. The rowset object also allows multiple components to coordinate their activities on this shared data object through notifications, and because the components are all sharing the same data, they can immediately see the changes made by other components. The rowset object exposes bookmarks, which let each individual component keep track of its own position, and the rowset object keeps track of concurrency information for deferred updating and optimistic concurrency control.

Retrieving Data from a Rowset

The main differences between how data is retrieved in ODBC and how data is retrieved in OLE DB are a direct result of the differences between the application-owned data model of ODBC and the shared-data model of OLE DB.

The most basic rowset in OLE DB exposes the following interfaces:

  • IColumnsInfo. Provides information about the columns of the rowset (metadata). This is similar to SQLDescribeCol in ODBC.
  • IRowsetInfo. Provides information about the rowset. This is similar to SQLGetStmtAttr in ODBC.
  • IAccessor. Permits definition of groups of column bindings describing the way in which tabular data is bound to program variables. This is similar to the bindings specified through descriptors by calling SQLSetDescField in ODBC version 3.0.
  • IRowset. Contains methods for iterating through the rows in the rowset sequentially. The methods in IRowset are similar to SQLFetch in ODBC.

Retrieving Rows

The provider manages the row buffers on behalf of the consumer. Rows are fetched from the data source into this row buffer using methods such as IRowset::GetNextRows, IRowsetLocate::GetRowsAt, and IRowsetLocate::GetRowsByBookmark. These methods are similar to SQLExtendedFetch in ODBC, except that instead of reading the data into the applications buffers, these functions read the data into the shared data object and return row handles (hRows) to the fetched data.

The consumer accesses the data from these row handles using IRowset::GetData. IRowset::GetData takes an accessor that maps fields of the row to and/or from fields in a structure on the consumer side. The types of the fields in the consumer's structure are indicated by the bindings in the accessor, and IRowset::GetData makes any necessary conversions between the buffered data and the consumer's data structure. If GetData encounters an error, it sets the status value for the column to the appropriate error.

To retrieve a row of data from the result set in ODBC, the application does the following:

  1. Calls SQLBindCol to bind the columns of the result set to storage locations, if not already done.
  2. Calls SQLFetch to move to the next row and retrieve data for all bound columns.
  3. Calls SQLGetData to retrieve data from unbound columns.

In OLE DB, the consumer performs the following functions to retrieve data:

  1. Calls IAccessor::CreateAccessor to specify binding information if not already done.
  2. Calls IRowset::GetNextRows to retrieve the next set of row handles.
  3. Calls IRowset::GetData to retrieve the data from the row handles according to the bindings specified by the accessor.

IRowset::GetData is similar to SQLGetData in ODBC, except that IRowset::GetData has the following capabilities:

  • Can be called for any held row handle, not just the current row.
  • Allows the retrieval of multiple columns in a single call.
  • Can return an interface to a live COM object, not just a binary large object (BLOB).
  • Can be used to return a pointer to data within the provider rather than a copy of the data.
  • Returns an interface to a stream over the object rather than retrieve long data through multiple calls to SQLGetData.

The following code example demonstrates data retrieval in OLE DB. The general flow of control is as follows:

  1. Get a description of the rowset.
  2. Build binding structures based on the description.
  3. Obtain the IAccessor interface on the rowset object.
  4. Call CreateAccessor to create an accessor.
  5. Call GetNextRows to retrieve the next set of row handles.
  6. Call GetData to access the data for each row.
  7. Release the set of retrieved row handles.
  8. Repeat steps 4 through 6 until all the data has been retrieved.
  9. Release the accessor.
/********************************************************************
* Retrieve data from a rowset.
********************************************************************/
void myGetData
   (
   IRowset* pIRowset  // [in]
   )
   {
   ULONG nCols;
   DBCOLUMNINFO* pColumnsInfo = NULL;
   OLECHAR* pColumnStrings = NULL;
   ULONG nCol;
   ULONG cRowsObtained;   // Count of rows
                          // obtained
   ULONG iRow;   // Row count
   HROW rghRows[NUMROWS_CHUNK];   // Row handles
   HROW* pRows = &rghRows[0];   // Pointer to the row
                                // handles
   IAccessor* pIAccessor;   // Pointer to the
                            // accessor
   HACCESSOR hAccessor;   // Accessor handle
   DBBINDSTATUS* pDBBindStatus = NULL;
   DBBINDING* pDBBindings = NULL;
   char* pRowValues;

   // Get the description of the rowset for use in binding structure
   // creation; see "Describing Query Results."
   if (FAILED(myGetColumnsInfo(pIRowset, &nCols, &pColumnsInfo,
      &pColumnStrings)))
      {
      return;
      }

   // Create the binding structures; see "Elements of an Accessor."
   myCreateDBBindings(nCols, pColumnsInfo, &pDBBindings,
      &pRowValues);
   pDBBindStatus = new DBBINDSTATUS[nCols];

   // Create the accessor; see "Creating Accessors."
   pIRowset->QueryInterface(IID_IAccessor, (void**) &pIAccessor);
   pIAccessor->CreateAccessor(
      DBACCESSOR_ROWDATA,// Accessor will be used to retrieve row
                         // data
   nCols,   // Number of columns being bound
      pDBBindings,   // Structure containing bind info
      0,   // Not used for row accessors
      &hAccessor,   // Returned accessor handle
      pDBBindStatus   // Information about binding validity
      );
   // Process all the rows, NUMROWS_CHUNK rows at a time.
   while (TRUE)
      {
      pIRowset->GetNextRows(
      0,   // Reserved
      0,   // cRowsToSkip
      NUMROWS_CHUNK,   // cRowsDesired
      &cRowsObtained,   // cRowsObtained
      &pRows );   // Filled in with row handles.

      // All done; there are no more rows left to get.
      if (cRowsObtained == 0)
      break;

      // Loop over rows obtained, getting data for each.
      for (iRow=0; iRow < cRowsObtained; iRow++)
      {
      pIRowset->GetData(rghRows[iRow], hAccessor, pRowValues);
      for (nCol = 0; nCol < nCols; nCol++)
         {
         wprintf(OLESTR("%s%s:"), pColumnsInfo[nCol].pwszName,
         wcslen(pColumnsInfo[nCol].pwszName) > 10 ?
         OLESTR("\t") : OLESTR("\t\t"));
         printf("\t%s\n",
         &pRowValues[pDBBindings[nCol].obValue]);
         }
      printf("\n");
      }

      // Release row handles.
      pIRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL,
      NULL);
      }   // End while

   // Release the accessor.
   pIAccessor->ReleaseAccessor(hAccessor, NULL);
   pIAccessor->Release();

   delete [] pDBBindings;
   delete [] pDBBindStatus;
   delete [] pRowValues;

   g_pIMalloc->Free( pColumnsInfo );
   g_pIMalloc->Free( pColumnStrings );

   return;
   }

Describing Query Results

In ODBC, to describe the results of a query, an application does the following:

  • Calls SQLNumResultCols to find out the number of columns in the result set.
  • Calls SQLDescribeCol or SQLColAttribute to describe each column in the result set.

In OLE DB, to describe the results of a query, an application does the following:

  • Calls IColumnsInfo or IColumnsRowset to describe the columns in the rowset.

In OLE DB, the consumer can also call IRowsetInfo to get information about properties supported on the rowset. This is similar to calling SQLGetStmtAttr on an executed statement handle in ODBC.

The following code example shows getting the metadata from the result set. Note that the ColumnsInfo interface passes off ownership of both the DBCOLUMNINFO structure array and the OLECHAR string that holds strings that are part of the rowset's metadata. These strings are freed using the task memory allocation interface, IMalloc.

/********************************************************************
* Get the characteristics of the rowset (the ColumnsInfo interface).
********************************************************************/
HRESULT myGetColumnsInfo
   (
   IRowset* pIRowset,   // [in]
   ULONG* pnCols,   // [out]
   DBCOLUMNINFO** ppColumnsInfo,   // [out]
   OLECHAR** ppColumnStrings   // [out]
   )
   {
   IColumnsInfo* pIColumnsInfo;
   HRESULT hr;

   if (FAILED(pIRowset->QueryInterface(IID_IColumnsInfo, (void**)
      &pIColumnsInfo)))
      {
      DumpError("Query rowset interface for IColumnsInfo failed");
      return (E_FAIL);
      }

   hr = pIColumnsInfo->GetColumnInfo(pnCols, ppColumnsInfo,
      ppColumnStrings);
   if (FAILED(hr))
      {
      DumpError("GetColumnInfo failed.");
      *pnCols = 0;
      }

   pIColumnsInfo->Release();
   return (hr);
   }

OLE DB Accessors

An accessor contains the binding information for one or more columns or parameters. The bindings supplied by the caller describe a data structure with offsets for each value. The consumer is responsible for correctly calculating the offsets. The consumer specifies the desired data type within the structure, and the provider agrees to the binding if the conversion is legal and supported. The accessors are created on the rowset or command, and their implementation is specific to the provider. They contain information or code to pack and unpack rows held by the provider. The consumer uses them like handles on the access functions of the row or column. Consumers can use more than one accessor, or a single accessor can define the mapping between a structure and multiple columns or parameters.

Accessors in OLE DB are similar to descriptor handles in ODBC. Each represents a reusable, extensible set of binding information for multiple columns of a result set or parameters to a statement. Multiple accessors can be created for a single rowset, just as multiple descriptors can be used on a single result set. Accessors can be created on a command object so that they can be shared between multiple rowsets the same way that descriptors can be shared between multiple result sets (hStmts).

Creating Accessors

In ODBC version 3.0, applications can create reusable descriptor handles that specify extendable binding information for a set of columns. To use a descriptor, the ODBC application does the following:

  • Calls SQLAllocHandle to allocate a descriptor handle.
  • Calls SQLSetDescField for each piece of binding information on each column.

In OLE DB, consumers create reusable accessor handles that specify binding information for a set of columns. To use an accessor, the consumer does the following:

  • Creates an array of binding structures, each describing the binding information for a single column.
  • Calls CreateAccessor to create an accessor handle using the binding information.

The main difference between descriptors and accessors is that SQLSetDescField is called multiple times to set individual properties for each column represented by the descriptor, while all of the binding information for an accessor is built into an array of binding structures that is passed in a single call to CreateAccessor. This is partially addressed in ODBC by the presence of "concise" functions, such as SQLBindCol and SQLSetDescRec, which allow the application to specify the most common subset of binding information in a single call per column.

Elements of an Accessor

The section of memory bound to a column or parameter must have at least one and can have up to three of the following parts:

  • Value. The actual column or parameter value.
  • Length. The actual length of the column or parameter data stored in the value part, in bytes.
  • Status. This serves a dual purpose. First, it indicates if the value is NULL. Second, it is used to return information about whether the value was successfully passed to or returned from the provider.

It is possible to have columns that are deferred. For a deferred column, the provider is not required to retrieve data until IRowset::GetData is called for that column or, if the column contains a COM object, until a method used to access the object is called. The advantage of deferred columns is that data retrieval can be postponed until the data is needed, which is very beneficial when the data is large.

The following structure defines the information specified for each column bound by an accessor.

typedef struct tagDBBINDING
   {
   ULONG iOrdinal;   // Ordinal of column or
                     // parameter to bind.
   ULONG obValue;   // Offset in consumer's buffer
                    // for value.
   ULONG obLength;   // Offset in consumer's buffer
                     // for length of value.
   ULONG obStatus;   // Offset in consumer's buffer
                     // for status of operation.
   ITypeInfo __RPC_FAR* pTypeInfo;   // Reserved, should be NULL.
   DBOBJECT __RPC_FAR* pObject;   // Pointer to object structure.
                                  // Structure describes access
                                  // to COM objects in the
                                  // column.
   DBBINDEXT __RPC_FAR* pBindExt;   // Reserved, should be NULL.
   DBPART dwPart;   // Enumerated parts to bind
                    // (value/length/status).
   DBMEMOWNER dwMemOwner;   // Memory is owned by consumer?
                            // Provider?
   DBPARAMIO eParamIO;   // Parameter type
                         // (input/output/not a
                         // parameter).
   ULONG cbMaxLen;   // Size of consumer's value
                     // buffer if memory is consumer
                     // owned.
   DWORD dwFlags;   // Reserved, should be 0.
   DBTYPE wType;   // Data type indicator.
   BYTE bPrecision;   // Precision for
                      // numeric/decimal data types.
   BYTE bScale;   // Scale for numeric/decimal
                  // data types.
   } DBBINDING;

The provider returns column or output parameter data to the consumer and also sets the status value of each column or output parameter.

Status value | Meaning
DBSTATUS_S_OK | Column/parameter value set/retrieved successfully.
DBSTATUS_S_ISNULL | Column/parameter value is the NULL value.
DBSTATUS_S_TRUNCATED | Column/parameter truncated.
DBSTATUS_S_DEFAULT | The provider should use the parameter's default value.
DBSTATUS_E_BADACCESSOR | The accessor used to read/write the value was invalid.
DBSTATUS_E_CANTCONVERT | The accessor specified an invalid conversion.
DBSTATUS_E_CANTCREATE | The provider could not create a storage object for large data.
DBSTATUS_E_DATAOVERFLOW | The conversion failed due to a data overflow.
DBSTATUS_E_INTEGRITYVIOLATION | The data value violated an integrity constraint.
DBSTATUS_E_PERMISSIONDENIED | The data value could not be set due to insufficient permissions.
DBSTATUS_E_SCHEMAVIOLATION | The data value violated the schema for the column.
DBSTATUS_E_SIGNMISMATCH | The data value had the incorrect sign.
DBSTATUS_E_UNAVAILABLE | The data was not available.

The following code example shows traversing a DBCOLUMNINFO structure array obtained from a rowset and creating a set of bindings based on that information. An accessor is created from the binding structure array in the previous code sample.

/********************************************************************
* Create binding structures from column information. Binding
* structures will be used to create an accessor that allows row value
* retrieval.
********************************************************************/
void myCreateDBBindings
   (
   ULONG nCols,   // [in]
   DBCOLUMNINFO* pColumnsInfo,    // [in]
   DBBINDING** ppDBBindings,    // [out]
   char** ppRowValues   // [out]
   )
   {
   ULONG nCol;
   ULONG cbRow = 0;
   DBBINDING* pDBBindings;
   char* pRowValues;

   pDBBindings = new DBBINDING[nCols];

   for (nCol = 0; nCol < nCols; nCol++)
      {
      pDBBindings[nCol].iOrdinal = nCol+1;
      pDBBindings[nCol].obValue = cbRow;
      pDBBindings[nCol].obLength = 0;
      pDBBindings[nCol].obStatus = 0;
      pDBBindings[nCol].pTypeInfo = NULL;
      pDBBindings[nCol].pObject = NULL;
      pDBBindings[nCol].pBindExt = NULL;
      pDBBindings[nCol].dwPart = DBPART_VALUE;
      pDBBindings[nCol].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
      pDBBindings[nCol].eParamIO = DBPARAMIO_NOTPARAM;
      pDBBindings[nCol].cbMaxLen = pColumnsInfo[nCol].ulColumnSize + 1;
      pDBBindings[nCol].dwFlags = 0;
      pDBBindings[nCol].wType = pColumnsInfo[nCol].wType;
      pDBBindings[nCol].bPrecision = pColumnsInfo[nCol].bPrecision;
      pDBBindings[nCol].bScale = pColumnsInfo[nCol].bScale;

      cbRow += pDBBindings[nCol].cbMaxLen;
      }

   pRowValues = new char[cbRow];

   *ppDBBindings = pDBBindings;
   *ppRowValues = pRowValues;

   return;
   }

Advanced Rowset Topics

OLE DB rowset interfaces and properties expose database cursor and bookmark support to the developer. In addition, OLE DB exposes support for client use of database resources such as scrollbar interface support.

The OLE DB cursor model is derived from that implemented in ODBC and includes support for cursor type, cursor scrolling capability, and transaction isolation levels.

Block Cursors

OLE DB supports block cursors by specifying the number of rows desired in the IRowset::GetNextRows method. In ODBC, a block cursor is defined by specifying the rowset size using the SQL_ATTR_ROW_ARRAY_SIZE attribute in a call to SQLSetStmtAttr prior to calling SQLFetchScroll.

Sequential Rowsets

Sequential rowsets do not support locating specific rows or fetching rows already retrieved. GetNextRows can retrieve one or more rows, but the rows must all be released before another GetNextRows call is permitted. This restriction is known as strict sequential cursor. This is similar to the forward-only cursor supported by ODBC. A more flexible model, relaxed sequential, allows prior rows to be held, but GetNextRows is still sequential and the consumer cannot revisit earlier parts of the rowset. The relaxed sequential model also allows changes and updates to rows that have already been passed but have been held. Sequential rowsets do not implement the IRowsetLocate interface.

GetNextRows is positional; that is, it fetches rows sequentially, at all times keeping track of the previous position. It can move forward, or backward if the rowset supports reversible direction. IRowset::RestartPosition repositions the next fetch position to the start of the rowset. GetNextRows keeps track of the next fetch position so that a sequence of calls to this method with no skips and no change of direction reads the entire rowset. This position is not connected to or disturbed by any other method that gets rows. Rowsets that support the property DBPROP_CANFETCHBACKWARDS can take a negative number for the count of requested rows. If the count of requested rows is negative, the fetch direction is backwards. GetNextRows increases the reference count of each row for which it returns a handle. ReleaseRows must be called to release the handles that have been retrieved.

GetNextRows is the same as calling SQLFetch in ODBC with no columns bound, except that it can move forward or backward relative to the current position and can return multiple row handles.

Scrollable Rowsets

Scrollable rowsets support IRowsetLocate. IRowsetLocate is equivalent to ODBC SQLFetchScroll. Scrollable rowsets are not positional; the consumer uses bookmarks to fetch relative to a previous position.

IRowsetLocate supports the following scrolling options:

  • Forward or backward
  • To the beginning or end of the rowset
  • Relative to a specific row

Bookmarks

A bookmark is a value that the consumer can use to quickly and easily reposition to a row in the result set without incurring the overhead of holding on to the row. Bookmarks are valid only during the lifetime of the rowset to which they refer. An application retrieves a bookmark as column zero, in the same way it retrieves data from any other column in the rowset.

Bookmark columns have the DBCOLUMNFLAG_ISBOOKMARK flag set in their column information. A bookmark may have a data type indicator of DBTYPE_I4 or DBTYPE_STR.

OLE DB provides some standard bookmarks:

  • DBBMK_INVALID. The bookmark is invalid. This is generally used for initializing variables.
  • DBBMK_FIRST. Indicates the first row of the rowset.
  • DBBMK_LAST. Indicates the last row of the rowset.

A bookmark becomes invalid if the row it points to is deleted, if it is based on a primary key and some of those key values were changed, or if the row it points to is no longer in the keyset. The validity of bookmarks after a transaction has been committed or aborted depends on the property DBPROP_COMMIT_PRESERVE. If this is set to TRUE, bookmarks remain valid; otherwise, they do not.

Scrollbar Support

IRowsetScroll is the interface for moving to approximate locations in a movable rowset. This method can be used for cases where precise positioning is not critical. IRowsetScroll supports the following methods:

  • IRowsetScroll::GetApproximatePosition. Gets the position of the row corresponding to the specified bookmark.
  • IRowsetScroll::GetRowsAtRatio. Gets rows starting from a fractional position in the rowset.

Cursor Types

OLE DB support for scrollable cursors is provided and determined by the values of the rowset properties.

Property | Meaning
DBPROP_CANSCROLLBACKWARDS | The rowset can return to previously read rows.
DBPROP_OWNINSERT | The rowset can see its own inserts.
DBPROP_OTHERINSERT | The rowset can see inserts made outside of the rowset.
DBPROP_OWNUPDATEDELETE | The rowset can see its own changes and deletions.
DBPROP_OTHERUPDATEDELETE | The rowset can see changes and deletions made outside of the rowset.

These options are used to specify the characteristics of the static, keyset, and dynamic cursors, defined in ODBC as follows:

  • Static cursor

    In a static cursor, the membership, ordering, and values of the rowset are fixed after the rowset is opened. Rows updated, deleted, or inserted after the rowset is opened are not visible to the rowset until the command is reexecuted.

    To obtain a static cursor, the application sets the properties as follows:

    • DBPROP_CANSCROLLBACKWARDS to VARIANT_TRUE
    • DBPROP_OTHERINSERT to VARIANT_FALSE
    • DBPROP_OTHERUPDATEDELETE to VARIANT_FALSE

    In ODBC, this is equivalent to specifying SQL_CURSOR_STATIC for the SQL_ATTR_CURSOR_TYPE attribute in a call to SQLSetStmtAttr.

  • Keyset-driven cursor

    In a keyset-driven cursor, the membership and ordering of rows in the rowset are fixed after the rowset is opened. However, values within the rows can change after the rowset is opened, including the entire row that is being deleted. Updates to a row are visible the next time the row is fetched, but rows inserted after the rowset is opened are not visible to the rowset until the command is reexecuted.

    To obtain a keyset-driven cursor, the application sets the properties:

    • DBPROP_CANSCROLLBACKWARDS to VARIANT_TRUE
    • DBPROP_OTHERINSERT to VARIANT_FALSE
    • DBPROP_OTHERUPDATEDELETE to VARIANT_TRUE

    In ODBC, this is equivalent to specifying SQL_CURSOR_KEYSET_DRIVEN for the SQL_ATTR_CURSOR_TYPE attribute in a call to SQLSetStmtAttr.

  • Dynamic cursor

    In a dynamic cursor, the membership, ordering, and values of the rowset can change after the rowset is opened. The row updated, deleted, or inserted after the rowset is opened is visible to the rowset the next time the row is fetched.

    To obtain a dynamic cursor, the application sets the properties as follows:

    • DBPROP_CANSCROLLBACKWARDS to VARIANT_TRUE
    • DBPROP_OTHERINSERT to VARIANT_TRUE
    • DBPROP_OTHERUPDATEDELETE to VARIANT_TRUE

    In ODBC, this is equivalent to specifying SQL_CURSOR_DYNAMIC for the SQL_ATTR_CURSOR_TYPE attribute in the call to SQLSetStmtAttr.

  • Cursor sensitivity

    If the rowset property DBPROP_OWNINSERT is set to VARIANT_TRUE, the rowset can see its own inserts; if the rowset property DBPROP_OWNUPDATEDELETE is set to VARIANT_TRUE, the rowset can see its own updates and deletes. These are equivalent to the presence of the SQL_CASE_SENSITIVITY_ADDITIONS bit and a combination of the SQL_CASE_SENSITIVITY_UPDATES and SQL_CASE_SENSITIVITY_DELETIONS bits that are returned in the ODBC SQL_STATIC_CURSOR_ATTRIBUTES2 SQLGetInfo request.

Handling Long Data Types

To a rowset, a BLOB or a storage object is a large sequence of uninterpreted bytes that a consumer stores in a column. It is the consumer's responsibility to interpret this sequence of bytes. BLOBs and storage objects are categorized as follows:

  • BLOB. Uninterpreted sequence of bytes. The type indicator of a BLOB column is DBTYPE_BYTES, DBTYPE_STR, or DBTYPE_WSTR.
  • IPersist* object. An object supporting either IPersistStream, IPersistStreamInit, or IPersistStorage. The type indicator is DBTYPE_UNKNOWN.

A consumer can determine what types of BLOBs, storage objects, and other types of OLE DB objects that a provider supports by calling IDBProperties with the DBPROP_OLEOBJECTS property.

BLOBs as Long Data

If the entire BLOB can be held in memory, it is treated as long in-memory data. To read the BLOB data, the consumer binds the column with type identifier DBTYPE_BYTES, DBTYPE_STR, or DBTYPE_WSTR and calls IRowset::GetData with an accessor containing this binding. The provider then returns the entire contents of the BLOB to the consumer.

BLOBs as Storage Objects

If a BLOB is too large to hold in memory, the consumer manipulates it through the ISequentialStream storage interface. The rows in the rowset are containers of the storage objects.

On retrieval, BLOB columns are deferred by default. Until GetData is called, BLOB column data is not retrieved and storage objects are not created. In particular, methods that retrieve rows, such as GetNextRows, do not return data for BLOB columns in the data cache.

A storage object created by the provider remains valid until one of the following occurs:

  • The consumer calls IRowset::ReleaseRows to release the row containing the storage object, or IRowset::Release to release the rowset containing the row.
  • The consumer calls ITransaction::Commit or ITransaction::Abort, and the retaining flag is not set.

It is the consumer's responsibility to release the storage object, even if the containing row has been released.

Accessing BLOB Data with Storage Objects

To bind to a BLOB data as a storage object, a consumer creates an accessor that includes a binding to the BLOB column. The consumer does the following:

  1. Sets the dwType 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.
  3. Sets the dwFlags element of the DBOBJECT structure in the binding.

To read data from a BLOB column using a storage object, the consumer does the following:

  1. Creates an accessor that includes a binding for the column.
  2. Calls IRowset::GetData with this accessor. The provider creates a storage object over the BLOB's data and returns a pointer to the requested storage interface (ISequentialStream) on this object.
  3. Calls methods on the storage interface to read the BLOB's data (ISequentialStream::Read).

If the consumer calls GetData, GetVisibleData, 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 a number of times and returning a different file handle each time. It is the consumer's responsibility to call Release on each of these storage interfaces.

For example, the following code example binds to a BLOB column and uses ISequentialStream::Read to read the data. For the complete source code listing, see Appendix B. The general flow of control is as follows:

  1. Create a binding structure to retrieve the ISequentialStream interface from a storage object.
  2. Obtain the IAccessor interface.
  3. Call CreateAccessor to create the accessor.
  4. Call GetNextRows to retrieve the row handles.
  5. Call GetData to retrieve the storage object from a row.
  6. Call ISequentialStream to read the data from the stream.
  7. Repeat steps 4, 5, and 6 to retrieve new storage objects and get the data.
/********************************************************************
* Retrieve data from an ODBC LONG_VARCHAR column (Notes in
* Employees).
********************************************************************/
void myGetBLOBData
   (
   IRowset* pIRowset   // [in]
   )
   {
   DBOBJECT ObjectStruct;    // For binding, retrieve
                             // an object pointer.
   DBBINDING   rgBinding[1];    // Bind a single column.

   IAccessor* pIAccessor = NULL;    // Accessor creation
   HACCESSOR hAccessor = NULL;
   ULONG ulErrorBinding;

   void* pData;    // Bound consumer buffer
   HROW rghRows[1];
   HROW* pRows = &rghRows[0];
   ULONG cRows;

   char szNotes[BLOCK_SIZE + 1];    // Text data from
                                    // "Notes"
   ULONG cbRead;    // Count of bytes read

   // Set up the object structure for accessor creation. Ask the
   // provider to return an ISequentialStream interface for reading.
   ObjectStruct.dwFlags = STGM_READ;
   ObjectStruct.iid = IID_ISequentialStream;

   // Set up the binding structure for the accessor.
   rgBinding[0].iOrdinal = 1;    // Only one column
   rgBinding[0].obValue  = 0;    // Offset to data
   rgBinding[0].obLength = 0;    // Ignore length
   rgBinding[0].obStatus = sizeof(IUnknown*);   // Offset to status
   rgBinding[0].pTypeInfo = NULL;    // Reserved
   rgBinding[0].pObject  = &ObjectStruct;    // Our interface
                                             // request
   rgBinding[0].pBindExt = NULL;    // Reserved
   rgBinding[0].dwPart = DBPART_VALUE |    // Get both VALUE...
                         DBPART_STATUS;    // ...and STATUS
                                           // parts.
   rgBinding[0].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
   rgBinding[0].eParamIO = DBPARAMIO_NOTPARAM;
   rgBinding[0].cbMaxLen = 0;    // Not applicable
   rgBinding[0].dwFlags  = 0;    // Reserved
   rgBinding[0].wType = DBTYPE_IUNKNOWN;    // Type
                                            // DBTYPE_IUNKNOWN
   rgBinding[0].bPrecision = 0;    // Not applicable
   rgBinding[0].bScale = 0;    // Not applicable

   // Get the accessor interface and create the accessor.
   pIRowset->QueryInterface(IID_IAccessor, (void**) &pIAccessor);

   if (FAILED(pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1,
      rgBinding, sizeof(IUnknown*) + sizeof(ULONG), &hAccessor,
      &ulErrorBinding)))
      {
      DumpError("CreateAccessor failed.");
      return;
      }

   // Allocate memory for the returned pointer and the status
   // field. The first sizeof(IUnknown*) bytes are for the pointer
   // to the object; the next sizeof(ULONG) bytes are for the
   // status.
   pData = new BYTE[sizeof(IUnknown*) + sizeof(ULONG)];

      while (TRUE)
      {
      // Get the next row.
      if (FAILED(pIRowset->GetNextRows(NULL, 0, 1, &cRows,
         &pRows)))
      {
      DumpError("GetNextRows failed.\n");
      break;
      }

      if (cRows == 0)
      {
      break;
      }

      // Get the row data, the pointer to an ISequentialStream*.
      if (FAILED(pIRowset->GetData(*pRows, hAccessor, pData)))
      {
      DumpError("GetData failed.\n");
      break;
      }

      // Read and process BLOCK_SIZE bytes at a time.
      if ((ULONG)((BYTE*)pData)[rgBinding[0].obStatus] ==
         DBSTATUS_S_ISNULL)
      {
      // Process NULL data.
      printf("<null>");
      }
      else if ((ULONG)((BYTE*)pData)[rgBinding[0].obStatus] ==
         DBSTATUS_S_OK)
      {
      do
         {
         (*((ISequentialStream**) pData))->Read(szNotes,
         BLOCK_SIZE, &cbRead);
         if (cbRead > 0)
            {
            // Process data.
            szNotes[cbRead] = (char) NULL;
            printf(szNotes);
            }
         }
      while (cbRead >= BLOCK_SIZE);

      (*((ISequentialStream**) pData))->Release();

      printf("\n\n");
      }

      pIRowset->ReleaseRows(cRows, pRows, NULL, NULL, NULL);
      }

   // Clean up.
   pIAccessor->ReleaseAccessor(hAccessor, NULL);
   pIAccessor->Release();

   delete [] pData;
   }

To write data to a BLOB column using a storage object, the consumer first creates an accessor that includes a binding for the column and then does the following:

  1. Calls IRowset::GetData with the accessor that binds the BLOB column. The provider creates a storage object over the BLOB's data and returns a pointer to the requested storage interface (ISequentialStream) on this object.
  2. Calls a method on the storage interface to write data (ISequentialStream::Write).

    The COM structured storage model supports both transacted and direct modes. In transacted mode, all changes are buffered, and the buffered changes are persisted or discarded only when an explicit commit or abort request is done. In direct mode, every change is followed by an automatic commit. 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 write the changes.

Alternately, the consumer calls IRowsetChange::SetData or IRowsetChange::InsertRow with the accessor that binds the BLOB column, passing a pointer to a storage interface on a separate storage object.

Data Manipulation

Data manipulation in OLE DB can be executed using SQL queries in commands or using the IRowsetChange interface.

IRowsetChange allows the consumer to do the following:

  • Update columns of a row by calling IRowsetChange::SetData.
  • Delete a row or rows by calling IRowsetChange::DeleteRows.
  • Insert a new row by calling IRowsetChange::InsertRow.

Delayed Updates

OLE DB supports delayed updates. With delayed updates, changes made to the rowset are not transmitted to the data source until IRowsetUpdate::Update is called.

A rowset can be in delayed or immediate update mode, depending on whether it exposes the IRowsetUpdate interface. The consumer specifies whether IRowsetUpdate should be supported prior to opening the rowset.

If the IRowsetUpdate interface is not included on the rowset, the rowset is said to be in immediate update mode and the changes are immediately transmitted to the data source. If IRowsetUpdate is present, the changes are not transmitted to the data source until IRowsetUpdate::Update is called.

For rowsets in delayed update mode, IRowsetUpdate allows the consumer to do the following:

  • Retrieve the initial value read for the row by calling IRowsetUpdate::GetOriginalData.
  • Get a list of pending changes by calling IRowsetUpdate::GetPendingRows.
  • Get the pending status of a given set of rows by calling IRowsetUpdate::GetRowStatus.
  • Undo some or all of the pending changes by calling IRowsetUpdate::Undo.
  • Commit some or all of the changes to the data source by calling IRowsetUpdate::Update.

Prepared Commands

If a command is to be executed multiple times, it is often more efficient to prepare it. Command preparation tells the query processor to save the execution plan so that it doesn't have to be rebuilt for each execution.

The ICommandPrepare interface supports methods for preparing a command. Calling ICommandPrepare::Prepare in OLE DB is equivalent to calling SQLPrepare in ODBC.

Commands with Parameters

Parameters are used to insert scalar values into a command at execute time. Parameters are generally used in conjunction with a prepared command so that the command can be executed multiple times, each time with a different value.

To specify parameters in ODBC, an application uses SQLBindParameter and SQLParamData in the following order:

  1. Calls SQLBindParameter for each parameter to specify the parameter type and bind buffers for the parameters' values.
  2. Calls SQLSetStmtAttr if multiple values are to be specified for each set of parameters.
  3. Places the values in appropriate buffers.
  4. Calls SQLExecute or SQLExecDirect.

To specify parameters in OLE DB, an application uses the ICommandWithParameters interface in the following order:

  1. Creates an accessor describing the binding information for the set of parameters.
  2. Calls ICommandWithParameters::SetParameterInfo to specify the types of the parameters.
  3. Calls ICommand::Execute to execute the command, passing a structure containing the accessor, number of parameter sets, and a pointer to data for the parameters.

Binding Parameters

The consumer specifies parameter descriptions by setting information in the DBPARAMBINDINFO structure passed to ICommandWithParameters::SetParameterInfo. This is similar to the type, precision, and scale information specified in SQLBindParameter in ODBC.

The DBPARAMBINDINFO structure is as follows:

typedef struct tagDBPARAMBINDINFO
   {
   LPOLESTR pwszDataSourceType;    // Data type name (OLESTR)
   LPOLESTR pwszName;    // Name of the parameter
   ULONG ulParamSize;    // Maximum length of data
                           // accepted
   DBPARAMFLAGS dwFlags;    // Input/output/signed/nullable/object
   BYTE bPrecision;    // Precision for numeric data
                       // types
   BYTE bScale;    // Scale for numeric data types
   } DBPARAMBINDINFO;

The ICommand::Execute method takes a pointer to the DBPARAMS structure as an argument. This structure includes a pointer to the data as well as an accessor that describes the layout of the data. Bindings described in the accessor are similar to the bindings specified in SQLBindParameter in ODBC. OLE DB allows the specification of multiple sets of parameters in a single call by specifying the number of sets of parameters in the cParamSets element of the DBPARAMS structure. This is similar to calling SQLSetStmtAttr in ODBC.

The DBPARAMS structure is as follows:

typedef struct tagDBPARAMS
   {
   void __RPC_FAR* pData;    // Data, array containing
                             // parameter values
   ULONG cParamSets;    // Count of sets of parameter
                        // values in the data array
   HACCESSOR hAccessor;    // Handle of parameter
                           // describing accessor
   } DBPARAMS;

The following code example shows parameter passing in OLE DB. For the complete source code listing, see Appendix C. The general flow of control is as follows:

  1. Create bindings describing the parameters.
  2. Obtain the IDBCreateSession interface.
  3. Call CreateSession to create a session object that scopes the transaction boundaries within the current connection.
  4. Call CreateCommand to create a command object within the transaction.
  5. Call SetCommandText to set the command text.
  6. Obtain the ICommandWithParameters interface on the command object.
  7. Call SetParameterInfo to specify the parameter information.
  8. Prepare the command.
  9. Create a parameter accessor.
  10. Build a structure containing the parameter information.
  11. Call Execute, providing the parameter accessor and parameter information, to execute the command.
  12. Release the command object.
/********************************************************************
*  Execute a prepared INSERT statement with parameters.
********************************************************************/
HRESULT myInsertWithParameters
   (
   IDBInitialize* pIDBInitialize
   )
   {
   IDBCreateSession* pIDBCreateSession;
   IDBCreateCommand* pIDBCreateCommand;
   ICommandText* pICommandText;
   ICommandPrepare* pICommandPrepare;
   ICommandWithParameters* pICmdWithParams;
   IAccessor* pIAccessor;
   WCHAR wSQLString[] =
      OLESTR("insert into Customers (CustomerID,
               CompanyName, ContactName,")
      OLESTR(" ContactTitle, Address, City, Region,
               PostalCode, Country,")
      OLESTR(" Phone, Fax)")
      OLESTR(" values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
   DBPARAMS Params;
   long cRowsAffected;
   HACCESSOR hParamAccessor;

   NEWCUSTOMERDATA aNewCustomers[] =
      {
      "YOUME",
      "You and Me Grocers",
      "William Smith",
      "General Manager",
      "383 15th Ave. N.",
      "New York",
      "NY",
      "10018",
      "USA",
      "(212) 555-8847",
      "(212) 555-9988",

      "YORBL",
      "Yorbalinda's",
      "Mary Jones",
      "Owner",
      "22 Sunny Vale Rd.",
      "San Diego",
      "CA",
      "93122",
      "USA",
      "(605) 555-4322",
      "(605) 555-4323"
      };
   NEWCUSTOMER   NewCustomer;

   ULONG nParams = 11;
   DBPARAMBINDINFO rgParamBindInfo[] =
      {
      OLESTR("DBTYPE_CHAR"), OLESTR("CustomerID"), 5,
      DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("CompanyName"), 40,
      DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("ContactName"), 30,
      DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("ContactTitle"), 30,
      DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("Address"), 60,
      DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("City"), 15,
      DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("Region"), 15,
      DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("PostalCode"), 10,
      DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("Country"), 15,
      DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("Phone"), 24,
      DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("FAX"), 24,
      DBPARAMFLAGS_ISINPUT, 0, 0,
      };
   ULONG rgParamOrdinals[] =
      {1,2,3,4,5,6,7,8,9,10,11};

   // Get the session.
   pIDBInitialize->QueryInterface(IID_IDBCreateSession,
      (void**)&pIDBCreateSession);
   pIDBCreateSession->CreateSession(NULL, IID_IDBCreateCommand,
      (IUnknown**) &pIDBCreateCommand);
   pIDBCreateSession->Release();

   // Create the command.
   pIDBCreateCommand->CreateCommand(NULL, IID_ICommandText,
      (IUnknown**) &pICommandText);
   pIDBCreateCommand->Release();

   // The command requires the actual text as well as an indicator
   // of its language.
   pICommandText->SetCommandText(DBGUID_DBSQL, wSQLString);

   // Set parameter information.
   pICommandText->QueryInterface(IID_ICommandWithParameters,
      (void**)&pICmdWithParams);
   pICmdWithParams->SetParameterInfo(nParams, rgParamOrdinals,
      rgParamBindInfo);
   pICmdWithParams->Release();

   // Prepare the command.
   pICommandText->QueryInterface(IID_ICommandPrepare,
      (void**)&pICommandPrepare);
   if (FAILED(pICommandPrepare->Prepare(0)))
      {
      pICommandPrepare->Release();
      pICommandText->Release();
      return (E_FAIL);
      }
   pICommandPrepare->Release();

   // Create parameter accessors.
   if (FAILED(myCreateParamAccessor(pICommandText, &hParamAccessor,
      &pIAccessor)))
      {
      pICommandText->Release();
      return (E_FAIL);
      }

   Params.pData = &NewCustomer;    // pData is the buffer pointer
   Params.cParamSets = 1;    // Number of sets of parameters
   Params.hAccessor = hParamAccessor;    // Accessor to the parameters

   // Specify the parameter information.
   for (UINT nCust = 0; nCust < 2; nCust++)
      {
      strcpy(NewCustomer.acCustomerID,
         aNewCustomers[nCust].szCustID);
      strcpy(NewCustomer.acCompanyName,
         aNewCustomers[nCust].szCompanyName);
      strcpy(NewCustomer.acContactName,
         aNewCustomers[nCust].szContactName);
      strcpy(NewCustomer.acContactTitle,
         aNewCustomers[nCust].szContactTitle);
      strcpy(NewCustomer.acAddress,
         aNewCustomers[nCust].szAddress);
      strcpy(NewCustomer.acCity, aNewCustomers[nCust].szCity);
      strcpy(NewCustomer.acRegion, aNewCustomers[nCust].szRegion);
      strcpy(NewCustomer.acPostalCode,
         aNewCustomers[nCust].szPostalCode);
         strcpy(NewCustomer.acCountry,
         aNewCustomers[nCust].szCountry);
      strcpy(NewCustomer.acPhone, aNewCustomers[nCust].szPhone);
      strcpy(NewCustomer.acFAX, aNewCustomers[nCust].szFAX);

      // Execute the command.
      pICommandText->Execute(NULL, IID_NULL, &Params,
         &cRowsAffected, NULL);

      printf("%ld rows inserted.\n", cRowsAffected);
      }

   pIAccessor->ReleaseAccessor(hParamAccessor, NULL);
   pIAccessor->Release();
   pICommandText->Release();

   return (NOERROR);
   }

Creating Parameter Accessors

Input parameter data is read from, and output parameter data is written to, the specified locations within Params.pData according to the bindings specified by the accessor. An array of parameter sets can be passed in pParamData, and cParamSets indicates the number of elements of the array.

The following code example shows parameter binding for the NEWCUSTOMER structure used in the previous example. The general flow of control is as follows:

  1. Specify the common binding information for all the parameters.
  2. Specify the specific binding information for each parameter.
  3. Call CreateAccessor to create the parameter accessor.
  4. Return the accessor handle along with the interface used to create it (so that it can be freed later).
/********************************************************************
*  Create parameter accessor.
********************************************************************/
HRESULT myCreateParamAccessor
   (
   ICommand* pICmd,   // [in]
   HACCESSOR*  phAccessor,    // [out]
   IAccessor** ppIAccessor   // [out]
   )
   {
   IAccessor* pIAccessor;
   HACCESSOR hAccessor;
   const ULONG nParams = 11;
   DBBINDING Bindings[nParams];
   DBBINDSTATUS rgStatus[nParams];    // Returns information for
                                      // individual binding
                                      // validity.
   HRESULT hr;

   ULONG acbLengths[] = {5, 40, 30, 30, 60, 15, 15, 10,
                         15, 24, 24};

   for (ULONG i = 0; i < nParams; i++)
      {
      Bindings[i].iOrdinal = i + 1;
      Bindings[i].obLength = 0;
      Bindings[i].obStatus = 0;
      Bindings[i].pTypeInfo = NULL;
      Bindings[i].pObject = NULL;
      Bindings[i].pBindExt = NULL;
      Bindings[i].dwPart = DBPART_VALUE;
      Bindings[i].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
      Bindings[i].eParamIO = DBPARAMIO_INPUT;
      Bindings[i].cbMaxLen = acbLengths[i];
      Bindings[i].dwFlags = 0;
      Bindings[i].wType = DBTYPE_STR;
      Bindings[i].bPrecision = 0;
      Bindings[i].bScale = 0;
      }

   Bindings[0].obValue = offsetof(NEWCUSTOMER, acCustomerID);
   Bindings[1].obValue = offsetof(NEWCUSTOMER, acCompanyName);
   Bindings[2].obValue = offsetof(NEWCUSTOMER, acContactName);
   Bindings[3].obValue = offsetof(NEWCUSTOMER, acContactTitle);
   Bindings[4].obValue = offsetof(NEWCUSTOMER, acAddress);
   Bindings[5].obValue = offsetof(NEWCUSTOMER, acCity);
   Bindings[6].obValue = offsetof(NEWCUSTOMER, acRegion);
   Bindings[7].obValue = offsetof(NEWCUSTOMER, acPostalCode);
   Bindings[8].obValue = offsetof(NEWCUSTOMER, acCountry);
   Bindings[9].obValue = offsetof(NEWCUSTOMER, acPhone);
   Bindings[10].obValue = offsetof(NEWCUSTOMER, acFAX);

   pICmd->QueryInterface(IID_IAccessor, (void**)&pIAccessor);

   hr = pIAccessor->CreateAccessor(
      DBACCESSOR_PARAMETERDATA,   // Accessor that will be used
                                 // to specify parameter data
      nParams,   // Number of parameters being
                 // bound
      Bindings,   // Structure containing bind
                  // information
      sizeof(NEWCUSTOMER),    // Size of parameter structure
      &hAccessor,   // Returned accessor handle
      rgStatus   // Information about binding
                 // validity
      );

   if (FAILED(hr))
      {
      DumpError("Parameter accessor creation failed.");
      }
   else
      {
      *ppIAccessor = pIAccessor;
      *phAccessor = hAccessor;
      }

   return (hr);
   }

Advanced OLE DB Topics

This section describes advanced OLE DB topics, such as data source location, catalog and schema functions, transactions, error handling, and data types.

Locating a Data Source

In ODBC, the application generally calls SQLDataSources to enumerate the different ODBC data sources installed on the computer. The application can also call SQLDrivers to enumerate the specific drivers and connect to them directly, without a data source. ODBC version 3.0 defines the concept of a file DSN, which is a data source definition that lives as a file in the file system.

In OLE DB, consumers can browse for data sources using an enumerator. Enumerators provide a recursive, hierarchical model for browsing data sources. This model is similar to browsing for files in the file system, where a folder is represented by an enumerator and a file is represented by a data source. Just as folders can enumerate other folders as well as files, an enumerator can enumerate other enumerators as well as data sources.

A root enumerator is provided as part of OLE DB that generates a rowset of available data providers and enumerators. Binding to a data source returns an uninitialized data source object for that data source.

Once created, a data source object can be persisted as a file in the file system. Binding a file moniker to one of these persisted data sources generates an uninitialized data source object with a particular state, such as a data source name or location. Persisted data sources are equivalent to file DSNs in ODBC version 3.0. The consumer must still initialize the data source object after it is loaded.

Catalog and Schema Functions

Schema information in OLE DB is retrieved using rowsets in the same manner as other types of data. The schema information specified in OLE DB is based on the ANSI SQL concepts of catalog and schema. A schema is a collection of database objects that are owned or have been created by a particular user, and a catalog contains one or more schemas.

The IDBSchemaRowset interface provides a mechanism to the consumer for retrieving information about the database without knowing its structure. Schema rowsets are identified by GUIDs. Each schema GUID has a set of restrictions that can be specified to reduce the result set to the desired information. For example, to get a list of columns in a particular table, the schema name, database name, and table name can be specified as restrictions. The order and data type of these restrictions are fixed and are listed in the Microsoft OLE DB Programmer's Reference. It is not necessary to specify all the restrictions; however, the unspecified restrictions must be set to type DBTYPE_EMPTY or VT_EMPTY.

The IDBSchemaRowset interface has the following methods:

  • IDBSchemaRowset::GetRowset. Returns one of the standard schema information rowsets.
  • IDBSchemaRowset::GetSchema. Returns a list of schema information views accessible through the GetRowset method.

The IDBSchemaRowset interface encapsulates the functionality of the catalog functions in ODBC, such as SQLTables, SQLColumns, SQLProcedures, SQLProcedureColumns, SQLStatistics, SQLPrimaryKeys, SQLForeignKeys, and SQLGetTypeInfo.

Transactions

A session can be inside or outside a transaction at any point in time. When a session is created, it is outside a transaction and all work done under the scope of that session is immediately committed on each method call. This is referred to as the auto-commit or implicit-commit mode.

If the provider supports transactions, the session supports the ITransactionLocal interface. Calling ITransactionLocal::StartTransaction begins a transaction on the session. ITransactionLocal inherits from the ITransaction interface, which supports the Commit and Abort methods. When a session enters a transaction, all work done by the session, its command and rowsets, is part of that transaction.

The following code example shows calling commit or abort on a session:

ITransactionLocal*  pITransactionlocal;

pIDBCreateCommand->QueryInterface(IID_ITransactionLocal,
   (void**) &pITransactionlocal);
pITransactionlocal->StartTransaction(
   ISOLATIONLEVEL_READCOMMITTED, 0, NULL, NULL);

...   // Do some work...setting bSave for the following.

if (bSave) // For commit
   pITransactionlocal->Commit(FALSE, XACTTC_SYNC_PHASEONE, 0);
else   // For abort
   pITransactionlocal->Abort(NULL, FALSE, FALSE);    // For abort

pITransactionlocal->Release();

Transactions have two important properties: retention and preservation. Retention indicates whether another transaction is implicitly started on committing or aborting the transaction. Preservation indicates whether the rowset capabilities are preserved on committing or aborting the transaction. Committing or aborting a transaction with the fRetention flag set to TRUE will implicitly start another transaction. After a commit or abort, the full functionality of the rowset is preserved or enters a zombie state, depending on whether the rowset properties DBPROP_COMMITPRESERVE and DBPROP_ABORTPRESERVE are set to TRUE or FALSE. A rowset in a zombie state is an object whose functionality is virtually lost. It supports only IUnknown operations and releasing row handles. A preserved rowset has all its capabilities intact.

Isolation Levels

OLE DB defines the following isolation levels. These are similar to the isolation levels in ODBC.

  • Read Uncommitted. The values in a record being accessed can change at any time.
  • Read Committed. The values in a record do not change from the time the record is read until it is rewritten or passed over.
  • Repeatable Read. The relationship between values in different records doesn't change while the transaction is executing.
  • Serializable. The existence of reference records does not change while the transaction is executing.

Error Handling

Methods return error information in two ways. The code returned by a method indicates the overall success or failure of a method while error objects provide detailed information. Success and warning return codes begin with S_ or DB_S_ and indicate that the method completed successfully. If a single warning condition occurs, the method returns the return code for that condition. If multiple warning conditions occur, a hierarchy is defined to determine which code is returned. Error return codes begin with E_ or DB_E_ and indicate that the method failed completely. Error objects in OLE DB are an extension of the Automation error objects.

  • The ISupportErrorInfo interface, defined by Automation, determines whether an object can return OLE DB error objects and which interfaces on that object can return OLE DB error objects.
  • The IErrorInfo interface, defined by Automation, returns the error message, the name of the component and the GUID of the interface in which the error occurred, and the name and topic of the help file that applies to the error.
  • The IErrorLookup interface is exposed by a provider supporting OLE DB error objects. The provider creates an error lookup service and the lookup service exposes the IErrorLookup interface. This service is used by IErrorRecords and IErrorInfo interfaces.
  • The IErrorRecords interface is the main interface through which OLE DB error objects are accessed.
  • The ISQLErrorInfo interface is used to return the SQLSTATE and native error. All ODBC-related providers can expose this interface.

Data Types

OLE DB uses standard COM-based and Windows-based data types and uses DBTYPE as a type indicator. A type indicator denotes the data type of a piece of memory. A type indicator is used to describe data types in metadata, method calls, and data and parameter binding.

Type indicators | Physical type
DBTYPE_EMPTY |
DBTYPE_NULL |
DBTYPE_RESERVED |
DBTYPE_I1 | signed char
DBTYPE_I2 | SHORT
DBTYPE_I4 | LONG
DBTYPE_I8 | LARGE_INTEGER
DBTYPE_UI1 | BYTE
DBTYPE_UI2 | unsigned short
DBTYPE_UI4 | unsigned int
DBTYPE_UI8 | ULARGE_INTEGER
DBTYPE_R4 | float
DBTYPE_R8 | double
DBTYPE_CY | LARGE_INTEGER
DBTYPE_DECIMAL | DBDECIMAL
DBTYPE_NUMERIC | DBNUMERIC
DBTYPE_DATE | DATE
DBTYPE_BOOL | VARIANT_BOOL
DBTYPE_BYTES | BYTE[cbMaxlen]
DBTYPE_BSTR | BSTR
DBTYPE_STR | char[cbMaxLen]
DBTYPE_WSTR | wchar_t[cbMaxLen]
DBTYPE_VARIANT | VARIANT
DBTYPE_IDISPATCH | IDispatch *
DBTYPE_IUNKNOWN | IUnknown *
DBTYPE_GUID | GUID
DBTYPE_ERROR | SCODE
DBTYPE_BYREF | void *
DBTYPE_ARRAY | SAFEARRAY *
DBTYPE_VECTOR | DBVECTOR
DBTYPE_UDT | undefined
DBTYPE_DBDATE | DBDATE
DBTYPE_DBTIME | DBTIME
DBTYPE_TIMESTAMP | DBTIMESTAMP

Each OLE DB provider chooses a default type indicator for each of its native types and documents them in the PROVIDER_TYPES schema rowset. Providers are required to transfer data in the C type that corresponds to that type indicator and to convert data to and from WCHAR, if such a conversion is defined in the conversion tables. For the conversion table and guidelines, see Microsoft OLE DB Programmer's Reference, Appendix A.

ODBC Attributes and Corresponding OLE DB Properties

OLE DB and ODBC are different interfaces. A one-to-one correspondence between the properties of one interface and the attributes of the other does not exist. OLE DB contains a rich definition of property sets and interface method parameters that provide services significantly different from those provided by ODBC. For more information, see the OLE DB documentation of schema rowsets, property sets, and methods.

The following tables show ODBC attributes and their OLE DB property equivalents. Where a one-to-one mapping cannot be made easily, the OLE DB property set and property columns are left blank.

Column Attributes

ODBC developers use SQLDescribeCol, SQLColAttribute, or SQLGetDescField to retrieve a given attribute. The interfaces and retrieval methods in OLE DB are also shown.

ODBC attribute | OLE DB property set, schema rowset, or method | OLE DB property
SQL_DESC_AUTO_UNIQUE_VALUE | IColumnsRowset::GetColumnsRowset | DBCOLUMN_ISAUTOINCREMENT
SQL_DESC_BASE_COLUMN_NAME | IColumnsRowset::GetColumnsRowset | DBCOLUMN_BASECOLUMNNAME
SQL_DESC_BASE_TABLE_NAME | IColumnsRowset::GetColumnsRowset | DBCOLUMN_BASETABLENAME
SQL_DESC_CASE_SENSITIVE | IColumnsRowset::GetColumnsRowset | DBCOLUMN_ISCASESENSITIVE
SQL_DESC_CATALOG_NAME | IColumnsRowset::GetColumnsRowset | DBCOLUMN_BASECATALOGNAME
SQL_DESC_CONCISE_TYPE
SQL_DESC_COUNT | IColumnsInfo::GetColumnInfo | The value returned in the pcColumns argument
SQL_DESC_DISPLAY_SIZE
SQL_DESC_FIXED_PREC_SCALE | IDBSchemaRowset::GetRowset DBSCHEMA_PROVIDER_TYPES rowset restricted by DBCOLUMN_TYPE property from IColumnsRowset::GetColumnsRowset | FIXED_PREC_SCALE column
SQL_DESC_LABEL | IColumnsRowset::GetColumnsRowset | DBCOLUMN_NAME
SQL_DESC_LENGTH | IColumnsInfo::GetColumnInfo | Derived from ulColumnSize
SQL_DESC_LITERAL_PREFIX | IDBSchemaRowset::GetRowset DBSCHEMA_PROVIDER_TYPES rowset restricted by DBCOLUMN_TYPE property from IColumnsRowset::GetColumnsRowset | LITERAL_PREFIX column
SQL_DESC_LITERAL_SUFFIX | IDBSchemaRowset::GetRowset DBSCHEMA_PROVIDER_TYPES rowset restricted by DBCOLUMN_TYPE property from IColumnsRowset::GetColumnsRowset | LITERAL_SUFFIX column
SQL_DESC_LOCAL_TYPE_NAME | IDBSchemaRowset::GetRowset DBSCHEMA_PROVIDER_TYPES rowset restricted by DBCOLUMN_TYPE property from IColumnsRowset::GetColumnsRowset | LOCAL_TYPE_NAME column
SQL_DESC_NAME | IColumnsRowset::GetColumnsRowset | DBCOLUMN_NAME
SQL_DESC_NULLABLE IColumnsRowset::GetColumnsRowset
IColumnsInfo::GetColumnInfo 		DBCOLUMN_FLAGS or dwFlags flag DBCOLUMNFLAGS_ISNULLABLE
SQL_DESC_NUM_PREX_RADIX
SQL_DESC_OCTET_LENGTH | IColumnsRowset::GetColumnsRowset | DBCOLUMN_OCTETLENGTH
SQL_DESC_PRECISION IColumnsRowset::GetColumnsRowset
IColumnsInfo::GetColumnInfo 		DBCOLUMN_PRECISION or bPrecision
SQL_DESC_SCALE IColumnsRowset::GetColumnsRowset
IColumnsInfo::GetColumnInfo 		DBCOLUMN_SCALE or bScale
SQL_DESC_SCHEMA_NAME | IColumnsRowset::GetColumnsRowset | DBCOLUMN_BASESCHEMANAME
SQL_DESC_SEARCHABLE | IColumnsRowset::GetColumnsRowset | DBCOLUMN_ISSEARCHABLE
SQL_DESC_TABLE_NAME | IColumnsRowset::GetColumnsRowset | DBCOLUMN_BASETABLENAME
SQL_DESC_TYPE IColumnsRowset::GetColumnsRowset
IColumnsInfo::GetColumnInfo 		DBCOLUMN_TYPE or wType
SQL_DESC_TYPE_NAME | IDBSchemaRowset::GetRowset DBSCHEMA_PROVIDER_TYPES rowset restricted by DBCOLUMN_TYPE property from IColumnsRowset::GetColumnsRowset | TYPE_NAME column
SQL_DESC_UNNAMED
SQL_DESC_UNSIGNED | IDBSchemaRowset::GetRowset DBSCHEMA_PROVIDER_TYPES rowset restricted by DBCOLUMN_TYPE property from IColumnsRowset::GetColumnsRowset | UNSIGNED_ATTRIBUTE column
SQL_DESC_UPDATABLE IColumnsRowset::GetColumnsRowset
IColumnsInfo::GetColumnInfo 		DBCOLUMN_FLAGS or dwFlags flag DBCOLUMNFLAGS_WRITE

Connection Attributes

ODBC connection attributes that control the behavior of the ODBC drivers, such as SQL_ATTR_ASYNC_ENABLE, are not included. OLE DB database and schema properties are indicated.

ODBC attribute | OLE DB property set, schema rowset, or method | OLE DB property or flag
SQL_ATTR_ACCESS_MODE | IDBProperties::SetProperties | DBPROP_DATASOURCEREADONLY
SQL_ATTR_AUTOCOMMIT |  | Auto-commit mode is on if the session is outside of a transaction
SQL_ATTR_CONNECTION_TIMEOUT |  |
SQL_ATTR_CURRENT_CATALOG | IDBProperties::SetProperties | DBPROP_CURRENTCATALOG
SQL_ATTR_LOGIN_TIMEOUT | IDBProperties::SetProperties | DBPROP_INIT_TIMEOUT
SQL_ATTR_QUIET_MODE | IDBProperties::SetProperties | DBPROP_INIT_HWND
SQL_ATTR_TXN_ISOLATION | ITransactionLocal::StartTransaction | isoLevel

Statement Attributes

OLE DB command properties and rowset methods are indicated.

ODBC statement attributes that control driver behaviors, such as SQL_ATTR_NOSCAN, are not included in the table. Statement attributes that are used to set bound parameter and column behaviors are also not included. OLE DB uses the DBBINDING structure together with data accessors to control arrays of parameters and bound columns. For more information, see "Getting and Setting Data" in the OLE DB Programmer's Reference.

ODBC attribute | OLE DB property set, schema rowset, or method | OLE DB property or flag
SQL_ATTR_ASYNC_ENABLE | Multiple concurrent operations are performed by different threads
SQL_ATTR_CONCURRENCY ITransactionLocal::StartTransaction
ISessionProperties::SetProperties
DBPROPSET_SESSION 		isoLevel value for the StartTransaction method, DBPROP_SESS_AUTOCOMMIT_ISOLEVELS when set for auto-commit mode on the session
SQL_ATTR_CURSOR_SCROLLABLE ICommandProperties::SetProperties
DBPROPSET_ROWSET 		DBPROP_CANSCROLLBACKWARDS
SQL_ATTR_CURSOR_SENSITIVITY ICommandProperties::SetProperties
DBPROPSET_ROWSET 		DBPROP_OTHERINSERT and DBPROP_OTHERUPDATEDELETE
SQL_ATTR_CURSOR_TYPE ICommandProperties::SetProperties
DBPROPSET_ROWSET 		DBPROP_CANSCROLL-BACKWARDS, DBPROP_OTHER-INSERT, and DBPROP_OTHERUPDATEDELETE
SQL_ATTR_KEYSET_SIZE
SQL_ATTR_MAX_LENGTH
SQL_ATTR_MAX_ROWS ICommandProperties::SetProperties
DBPROPSET_ROWSET 		DBPROP_MAXROWS
SQL_ATTR_QUERY_TIMEOUT ICommandProperties::SetProperties
DBPROPSET_ROWSET 		DBPROP_COMMANDTIMEOUT
SQL_ATTR_RETRIEVE_DATA | IRowset::GetData | Data is not retrieved until GetData method is called on the rowset
SQL_ATTR_ROW_ARRAY_SIZE | IRowset::GetNextRows | cRows value
SQL_ATTR_ROW_NUMBER

SQLGetInfo

SQLGetInfo returns information about a specific ODBC data source. OLE DB offers provider property sets and other entry points to expose provider-specific behaviors.

OLE DB allows providers to define additional property sets. For example, the ODBC provider shipped with the OLE DB SDK defines the DBPROPSET_PROVIDERROWSET and DBPROPSET_PROVIDERDATASOURCEINFO property sets, and some SQLGetInfo InfoType parameters are supported through these provider-specific property sets.

In the following table, SQLGetInfo InfoType parameters that have no meaning in OLE DB have been eliminated (for example, SQL_DM_VER, which returns the ODBC Driver Manager version number).

Where a one-to-one match does not exist, a tip is provided to help you discover the information. For example, SQLGetInfo InfoType SQL_ALTER_DOMAIN has no direct support in OLE DB. However, you can use the provider's IDBInfo::GetKeywords method to determine whether or not the provider recognizes the SQL ALTER statement.

SQLGetInfo InfoType value | OLE DB property set, schema rowset, or method | OLE DB property or flag
SQL_ACCESSIBLE_PROCEDURES
SQL_ACCESSIBLE_TABLES
SQL_ACTIVE_ENVIRONMENTS
SQL_AGGREGATE_FUNCTIONS | IDBInfo::GetKeywords
SQL_ALTER_DOMAIN | IDBInfo::GetKeywords
SQL_ALTER_TABLE | IDBInfo::GetKeywords
SQL_ASYNC_MODE
SQL_BATCH_ROW_COUNT IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_MULTIPLEPARAMSETS
SQL_BATCH_SUPPORT
SQL_BOOKMARK_PERSISTENCE | Bookmarks are valid for the lifetime of the rowset to which they apply
SQL_CATALOG_LOCATION IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_CATALOGLOCATION
SQL_CATALOG_NAME | IDBInfo::GetLiteralInfo | DBLITERAL_CATALOG_NAME
SQL_CATALOG_NAME_SEPARATOR | IDBInfo::GetLiteralInfo | DBLITERAL_CATALOG_SEPARATOR
SQL_CATALOG_TERM IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_CATALOGTERM
SQL_CATALOG_USAGE IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_CATALOGUSAGE
SQL_COLLATION_SEQ IDBSchemaRowset::GetRowset
COLLATIONS 		COLLATION_NAME column
SQL_COLUMN_ALIAS
SQL_CONCAT_NULL_BEHAVIOR IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_CONCATNULLBEHAVIOR
SQL_CONVERT (all types) | IConvertType::CanConvertType
SQL_CONVERT_FUNCTIONS | IDBInfo::GetKeywords
SQL_CORRELATION_NAME
SQL_CREATE_ASSERTION | IDBInfo::GetKeywords
SQL_CREATE_CHARACTER_SET | IDBInfo::GetKeywords
SQL_CREATE_COLLATION | IDBInfo::GetKeywords
SQL_CREATE_DOMAIN | IDBInfo::GetKeywords
SQL_CREATE_SCHEMA | IDBInfo::GetKeywords
SQL_CREATE_TABLE | IDBInfo::GetKeywords
SQL_CREATE_TRANSLATION | IDBInfo::GetKeywords
SQL_CREATE_VIEW | IDBInfo::GetKeywords
SQL_CURSOR_COMMIT_BEHAVIOR IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO

ICommandProperties::GetProperties
DBPROPSET_ROWSET

 DBPROP_PREPARE-COMMITBEHAVIOR and DBPROP_COMMITPRESERVE
SQL_CURSOR_ROLLBACK_BEHAVIOR IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO

ICommandProperties::GetProperties
DBPROPSET_ROWSET

 DBPROP_PREPARE-ABORTTBEHAVIOR and DBPROP_ABORTPRESERVE
SQL_CURSOR_SENSITIVITY
SQL_DATA_SOURCE_NAME IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_DATASOURCENAME
SQL_DATA_SOURCE_READ_ONLY IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_DATASOURCEREADONLY
SQL_DATABASE_NAME IDBProperties::GetProperties
DBPROPSET_DATASOURCE 		DBPROP_CURRENTCATALOG
SQL_DATETIME_LITERALS
SQL_DBMS_NAME IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_DBMSNAME
SQL_DBMS_VER IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_DBMSVER
SQL_DDL_INDEX | IDBInfo::GetKeywords
SQL_DEFAULT_TXN_ISOLATION | Transaction isolation is explicitly specified
SQL_DESCRIBE_PARAMETER
SQL_DRIVER_NAME IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_PROVIDERFILENAME

Note Prior to MDAC 2.5, this property was named DBPROP_PROVIDERNAME.
SQL_DRIVER_ODBC_VER IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_PROVIDEROLEDBVER
SQL_DRIVER_VER IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_PROVIDERVER
SQL_DROP_ASSERTION | IDBInfo::GetKeywords
SQL_DROP_CHARACTER_SET | IDBInfo::GetKeywords
SQL_DROP_COLLATION | IDBInfo::GetKeywords
SQL_DROP_DOMAIN | IDBInfo::GetKeywords
SQL_DROP_SCHEMA | IDBInfo::GetKeywords
SQL_DROP_TABLE | IDBInfo::GetKeywords
SQL_DROP_TRANSLATION | IDBInfo::GetKeywords
SQL_DROP_VIEW | IDBInfo::GetKeywords
SQL_DYNAMIC_CURSOR_ATTRIBUTES1 ICommandProperties::GetProperties
DBPROPSET_ROWSET 		DBPROP_CANSCROLLBACKWARDS, DBPROP_OTHERINSERT, DBPROP_OTHERUPDATEDELETE, DBPROP_OWNINSERT, and DBPROP_OWNUPDATEDELETE
SQL_DYNAMIC_CURSOR_ATTRIBUTES2 ICommandProperties::GetProperties
DBPROPSET_ROWSET 		DBPROP_CANSCROLLBACKWARDS, DBPROP_OTHERINSERT, DBPROP_OTHERUPDATEDELETE, DBPROP_OWNINSERT, and DBPROP_OWNUPDATEDELETE
SQL_EXPRESSIONS_IN_ORDERBY
SQL_FETCH_DIRECTION ICommandProperties::GetProperties
DBPROPSET_ROWSET 		DBPROP_CANSCROLLBACKWARDS
SQL_FILE_USAGE
SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 ICommandProperties::GetProperties
DBPROPSET_ROWSET 		DBPROP_OTHERINSERT, DBPROP_OTHERUPDATEDELETE, DBPROP_OWNINSERT, and DBPROP_OWNUPDATEDELETE
SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 ICommandProperties::GetProperties
DBPROPSET_ROWSET 		DBPROP_OTHERINSERT, DBPROP_OTHERUPDATEDELETE, DBPROP_OWNINSERT, and DBPROP_OWNUPDATEDELETE
SQL_GETDATA_EXTENSIONS
SQL_GROUP_BY IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_GROUPBY
SQL_IDENTIFIER_CASE IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_IDENTIFIERCASE
SQL_IDENTIFIER_QUOTE_
CHAR 		IDBInfo::GetLiteralInfo DBLITERAL_QUOTE
SQL_INDEX_KEYWORDS | IDBInfo::GetKeywords
SQL_INFO_SCHEMA_VIEWS
SQL_INSERT_STATEMENT
SQL_INTEGRITY
SQL_KEYSET_CURSOR_ATTRIBUTES1 ICommandProperties::GetProperties
DBPROPSET_ROWSET 		DBPROP_CANSCROLLBACKWARDS, DBPROP_OWNINSERT, and DBPROP_OWNUPDATEDELETE
SQL_KEYSET_CURSOR_ATTRIBUTES2 ICommandProperties::GetProperties
DBPROPSET_ROWSET 		DBPROP_CANSCROLLBACKWARDS, DBPROP_OWNINSERT, and DBPROP_OWNUPDATEDELETE
SQL_KEYWORDS | IDBInfo::GetKeywords
SQL_LIKE_ESCAPE_CLAUSE | IDBInfo::GetLiteralInfo | DBLITERAL_ESCAPE_PERCENT and DBLITERAL_ESCAPE_UNDERSCORE
SQL_MAX_ASYNC_CONCURRENT_STATEMENTS
SQL_MAX_BINARY_LITERAL_LEN | IDBInfo::GetLiteralInfo | DBLITERAL_BINARY_LITERAL
SQL_MAX_CATALOG_NAME_LEN | IDBInfo::GetLiteralInfo | DBLITERAL_CATALOG_NAME
SQL_MAX_CHAR_LITERAL_LEN | IDBInfo::GetLiteralInfo | DBLITERAL_CHAR_LITERAL
SQL_MAX_COLUMN_NAME_LEN | IDBInfo::GetLiteralInfo | DBLITERAL_COLUMN_NAME
SQL_MAX_COLUMNS_IN_GROUP_BY
SQL_MAX_COLUMNS_IN_INDEX
SQL_MAX_COLUMNS_IN_ORDER_BY
SQL_MAX_COLUMNS_IN_SELECT
SQL_MAX_COLUMNS_
IN_TABLE
SQL_MAX_CONCURRENT_ACTIVITIES
SQL_MAX_CURSOR_NAME_LEN
SQL_MAX_DRIVER_CONNECTIONS IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_ACTIVESESSIONS
SQL_MAX_IDENTIFIER_
LEN
SQL_MAX_INDEX_SIZE IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_MAXINDEXSIZE
SQL_MAX_PROCEDURE_NAME_LEN | IDBInfo::GetLiteralInfo | DBLITERAL_PROCEDURE_NAME
SQL_MAX_ROW_SIZE IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_MAXROWSIZE
SQL_MAX_ROW_SIZE_INCLUDES_LONG IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_MAXROWSIZEINCLUDES-BLOB
SQL_MAX_STATEMENT_LEN | IDBInfo::GetLiteralInfo | DBLITERAL_TEXT_COMMAND
SQL_MAX_TABLE_NAME_LEN | IDBInfo::GetLiteralInfo | DBLITERAL_TABLE_NAME
SQL_MAX_TABLES_IN_SELECT IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_MAXTABLESINSELECT
SQL_MAX_USER_NAME_LEN | IDBInfo::GetLiteralInfo | DBLITERAL_USER_NAME
SQL_MULT_RESULT_SETS IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_MULTIPLERESULTS
SQL_MULTIPLE_ACTIVE_TXN | Provider is responsible for spawning connections, if necessary, to provide multiple transactions from one data source object
SQL_NEED_LONG_DATA_LEN
SQL_NON_NULLABLE_COLUMNS IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_COLUMNDEFINITION
SQL_NULL_COLLATION IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_NULLCOLLATION
SQL_NUMERIC_FUNCTIONS
SQL_OJ_CAPABILITIES
SQL_ORDER_BY_COLUMNS_IN_SELECT IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_ORDERBYCOLUMNSIN-SELECT
SQL_OUTER_JOINS
SQL_OWNER_TERM IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_SCHEMATERM
SQL_OWNER_USAGE IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_SCHEMAUSAGE
SQL_PARAM_ARRAY_ROW_COUNTS
SQL_PARAM_ARRAY_SELECTS
SQL_PROCEDURE_TERM IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_PROCEDURETERM
SQL_PROCEDURES | IDBSchemaRowset::GetSchemas returns GUID for PROCEDURES schema rowset
SQL_QUOTED_IDENTIFIER_CASE IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_QUOTEDIDENTIFIERCASE
SQL_ROW_UPDATES ICommandProperties::GetProperties
DBPROPSET_ROWSET 		DBPROP_IRowsetResynch
SQL_SCHEMA_TERM IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_SCHEMATERM
SQL_SCHEMA_USAGE IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_SCHEMAUSAGE
SQL_SCROLL_OPTIONS ICommandProperties::GetProperties
DBPROPSET_ROWSET 		DBPROP_CANSCROLLBACKWARDS, DBPROP_OTHERINSERT, DBPROP_
OTHERUPDATEDELETE, DBPROP_OWNINSERT, DBPROP_
OWNUPDATEDELETE
SQL_SEARCH_PATTERN_ESCAPE
SQL_SERVER_NAME IDBProperties::GetProperties
DBPROPSET_INIT 		DBPROP_INIT_LOCATION
SQL_SPECIAL_CHARACTERS | IDBInfo::GetLiteralInfo | Value of pwszInvalidChars when inquiring about identifiers, and so on
SQL_STATIC_CURSOR_ATTRIBUTES1 ICommandProperties::GetProperties
DBPROPSET_ROWSET 		DBPROP_CANSCROLLBACKWARDS, DBPROP_OWNINSERT, and DBPROP_OWNUPDATEDELETE
SQL_STATIC_CURSOR_ATTRIBUTES2 ICommandProperties::GetProperties
DBPROPSET_ROWSET 		DBPROP_CANSCROLLBACKWARDS, DBPROP_OWNINSERT, and DBPROP_OWNUPDATEDELETE
SQL_STRING_FUNCTIONS
SQL_SUBQUERIES IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_SUBQUERIES
SQL_SYSTEM_FUNCTIONS
SQL_TABLE_TERM IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_TABLETERM
SQL_TIMEDATE_ADD_INTERVALS
SQL_TIMEDATE_DIFF_INTERVALS
SQL_TIMEDATE_FUNCTIONS
SQL_TXN_CAPABLE | IUnknown::QueryInterface on session for IID_ITransactionLocal
SQL_TXN_ISOLATION_OPTION IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_SUPPORTEDTXNISORETAIN
SQL_UNION
SQL_USER_NAME IDBProperties::GetProperties
DBPROPSET_DATASOURCEINFO 		DBPROP_USERNAME

The following interfaces also support setting and getting of property information:

  • ICommandProperties. Registers with the command the properties from the Rowset Property Group. The consumer can specify whether these properties specify optional or required support on the returned rowset.
  • IRowsetInfo::GetProperties. Returns the current settings of all properties supported by the rowset. The value returned by this method can be different from the value returned by ICommandProperties::GetProperties for a property. This is because the provider's ability to support the property might be affected by the current transaction or current query.

Mapping ODBC Functions to OLE DB Methods

Because a direct mapping between ODBC functions and OLE DB methods does not exist in all cases, only similar functionality is provided. When more than one method can be used to achieve the same thing, the methods are separated with the word or—for example, MethodA or MethodB. When more than one method must be called in sequence, the methods are numbered; for example, 1. MethodA, 2. MethodB, and so on.

ODBC function | OLE DB method | Remarks/property
SQLAllocHandle Environment Connection Descriptor Statement | CoInitialize CoCreateInstance IAccessor::CreateAccessor IDBCreateCommand::CreateCommand |
SQLBindCol | IAccessor::CreateAccessor |
SQLBindParameter | IAccessor::CreateAccessor |
SQLBrowseConnect | ISourcesRowset::GetSourcesRowset |
SQLBulkOperations Insertions Deletions Updates Retrieve by bookmark | IRowsetChange::InsertRows IRowsetChange::DeleteRows IRowsetChange::SetData IRowsetLocate::GetRowsByBookmark | If IRowsetUpdate is requested, all operations are batched until IRowsetUpdate::Update is called
SQLCancel | ICommand::Cancel |
SQLCloseCursor | IRowset::Release |
SQLColAttribute | IColumnsInfo::GetColumnInfo or IColumnsRowset::GetColumnsRowset |
SQLColumnPrivileges | IDBSchemaRowset::GetRowset | DBSCHEMA_COLUMN_PRIVILEGES
SQLColumns | IDBSchemaRowset::GetRowset | DBSCHEMA_COLUMNS
SQLConnect | 1. IDBProperties::SetProperties 2. IDBInitialize::Initialize |
SQLDataSources | ISourcesRowset::GetSourcesRowset |
SQLDescribeCol | IColumnsInfo::GetColumnInfo |
SQLDescribeParam | ICommandWithParameters::GetParameterInfo |
SQLDisconnect | IDBInitialize::Uninitialize |
SQLDriverConnect | 1. IDBProperties::SetProperties 2. IDBInitialize::Initialize |
SQLDrivers | ISourcesRowset::GetSourcesRowset |
SQLEndTran | ITransaction::Commit or ITransaction::Abort |
SQLError | 1. GetErrorInfo 2. IErrorRecords 3. IErrorInfo |
SQLExecDirect | ICommand::Execute |
SQLExecute | ICommand::Execute |
SQLFetch | 1. IRowset::GetNextRows, IRowsetLocate::GetRowsAt, or IRowsetLocate::GetRowsByBookmark 2. IRowset::GetData |
SQLFetchScroll | 1. IRowset::GetNextRows, IRowsetLocate::GetRowsAt, or IRowsetLocate::GetRowsByBookmark 2. IRowset::GetData |
SQLForeignKeys | IDBSchemaRowset::GetRowset | DBSCHEMA_FOREIGN_KEYS
SQLFreeHandle Environment Connection Descriptor Statement | OleUninitialize IDBInitialize::Release IAccessor::ReleaseAccessor IRowset::Release |
SQLFreeStmt SQL_DROP SQL_CLOSE SQL_UNBIND SQL_RESET_ PARAMS | ICommand::Release IRowset::Release IAccessor::ReleaseAccessor IAccessor::ReleaseAccessor |
SQLGetConnectAttr | IDBProperties::GetProperties |
SQLGetCursorName | Not applicable | OLE DB performs updates through update methods, as opposed to positioned cursor operations
SQLGetData | IRowset::GetData (for all data except BLOBs) or ILockBytes, ISequentialStream, IStorage, or IStream (for BLOB data) |
SQLGetDescField | IAccessor::GetBindings | Elements within binding structure
SQLGetDescRec | IAccessor::GetBindings | Elements within binding structure
SQLDiagField | 1. GetErrorInfo 2. IErrorRecords 3. IErrorInfo |
SQLDiagRec | 1. GetErrorInfo 2. IErrorRecords 3. IErrorInfo |
SQLGetEnvAttr | IDBProperties::GetProperties |
SQLGetFunctions | IUnknown::QueryInterface |
SQLGetInfo | IDBProperties::GetProperties |
SQLGetStmtAttr | ICommandProperties::GetProperties or IRowsetInfo::GetProperties |
SQLGetTypeInfo | IDBSchemaRowset::GetRowset | DBSCHEMA_PROVIDER_TYPES
SQLMoreResults | IMultipleResults |
SQLNativeSql | ICommandText::GetCommandText | With NULL as pguidDialect
SQLNumParams | ICommandWithParams::GetParameterInfo |
SQLNumResultCols | IColumnsInfo::GetColumnInfo |
SQLParamData | Not applicable |
SQLPrepare | ICommand::Prepare |
SQLPrimaryKeys | IDBSchemaRowset::GetRowset | DBSCHEMA_PRIMARY_KEYS
SQLProcedureColumns | IDBSchemaRowset::GetRowset | DBSCHEMA_PROCEDURE_PARAMETERS and DBSCHEMA_PROCEDURE_COLUMNS
SQLProcedures | IDBSchemaRowset::GetRowset | DBSCHEMA_PROCEDURES
SQLPutData | IRowsetChange::SetData (for all data except BLOBs) or ISequentialStream (for BLOB data) |
SQLRowCount | cRowsAffected in ICommand::Execute |
SQLSetConnectAttr | IDBProperties::SetProperties |
SQLSetCursorName | Not applicable | OLE DB performs updates through update methods, as opposed to positioned cursor operations
SQLSetDescField | IAccessor::CreateAccessor | Elements within binding structure
SQLSetDescRec | IAccessor::CreateAccessor | Elements within binding structure
SQLSetEnvAttr | IDBProperties::SetProperties |
SQLSetParam | ICommandWithParameters::SetParameterInfo |
SQLSetPos SQL_POSITION SQL_REFRESH SQL_UPDATE SQL_DELETE SQL_ADD | Not applicable IRowset::GetData or IRowsetResynch IRowsetChange::SetData IRowsetChange::Delete IRowsetChange::InsertRow IRowsetUpdate::Update |
SQLSetStmtAttr | ICommandProperties::SetProperties |
SQLSpecialColumns | IColumnsInfo::GetColumnInfo | DBCOLUMNFLAGS_ISROWID and DBCOLUMNFLAGS_ISROWVER
SQLStatistics | IDBSchemaRowset::GetRowset | DBSCHEMA_STATISTICS
SQLTablePrivileges | IDBSchemaRowset::GetRowset | DBSCHEMA_TABLE_PRIVILEGES
SQLTables | IDBSchemaRowset::GetRowset | DBSCHEMA_TABLES
SQLTransact | ITransaction::Abort and ITransaction::Commit |

Conclusion

ODBC and OLE DB both provide a rich application programming interface for the database developer. OLE DB and the component object model present the ODBC developer with an entirely new way of writing. The ODBC developer, familiar with procedural programming, must become comfortable with the world of COM objects and the interface negotiation required to manipulate those objects.

In addition, OLE DB often presents a new implementation of familiar development concepts like tabular data structures. The ODBC developer will need to rethink application structure to make effective use of OLE DB's sessions, property sets, rowsets, and accessors.

Becoming comfortable with COM objects is a first step toward successful OLE DB application development. In this paper, tasks typically executed by an ODBC application are implemented through OLE DB object creation and manipulation. The information presented builds on the ODBC database developer's existing knowledge to smooth the introduction to this new and different technology.

Finding More Information

For more information about OLE DB and ODBC, see the following publications:

Brockschmidt, Kraig. Inside OLE. Microsoft Press®, 1995.

Geiger, Kyle. Inside ODBC. Microsoft Press, 1995.

Microsoft ODBC 3.0 Programmer's Reference and SDK Guide. Microsoft Press, 1997.

Microsoft OLE 2 Programmer's Reference. Volume 1. Microsoft Press, 1994.

Microsoft OLE DB Programmer's Reference.

Appendix A

The following is a complete source code listing for the example presented in "Basic OLE DB." The example selects the CompanyName, City, Phone, and FAX columns from the Customers table of the Access Northwind sample database. (This database was formerly shipped with the MDAC SDK.)

To build the file using Microsoft® Visual C++® 4.0 or later:

  1. Create a new console application.
  2. Copy the following code to a new .cpp file.
  3. Ensure that your build directory settings reference the Data Access SDK include directory.
  4. Alter the build, link settings to include linking to Oledb.lib.
/********************************************************************
* OLE DB
********************************************************************/
#define UNICODE
#define _UNICODE
#define DBINITCONSTANTS   // Initialize OLE constants...
#define INITGUID          // ...once in each app

#include <windows.h>
#include <stdio.h>

#include <oledb.h>  // OLE DB include files
#include <oledberr.h>
#include <msdaguid.h> // ODBC provider include files
#include <msdasql.h>

// Macros--number of row identifiers to retrieve
#define NUMROWS_CHUNK     35

// Prototypes
HRESULT myInitDSO(IDBInitialize** ppIDBI);
HRESULT mySetInitProps(IDBInitialize* pIDBInitialize);
HRESULT myCommand(IDBInitialize* pIDBI, IRowset** ppIRowset);
void  myGetData(IRowset* pIRowset);
void  DumpError(LPSTR lpStr);
HRESULT myGetColumnsInfo(IRowset* pIRowset, ULONG* pnCols,
   DBCOLUMNINFO** ppColumnsInfo, OLECHAR** ppColumnStrings);
void  myCreateDBBindings(ULONG nCols, DBCOLUMNINFO* pColumnsInfo,
   DBBINDING** ppDBBindings, char** ppRowValues);

// Global task memory allocator
IMalloc*    g_pIMalloc = NULL;

/********************************************************************
* General OLE DB application main()
********************************************************************/
int main()
   {
   IDBInitialize* pIDBInitialize = NULL;
   IRowset* pIRowset = NULL;

   // Init COM library and set up the DLLs
   CoInitialize(NULL);

   // Get the task memory allocator.
   if (FAILED(CoGetMalloc(MEMCTX_TASK, &g_pIMalloc)))
      goto EXIT;

   // Connect to the data source.
   if (FAILED(myInitDSO(&pIDBInitialize)))
      goto EXIT;

   // Get a session, set and execute a command.
   if (FAILED(myCommand(pIDBInitialize, &pIRowset)))
      goto EXIT;

   // Retrieve data from rowset.
   myGetData(pIRowset);

EXIT:
   // Clean up and disconnect.
   if (pIRowset != NULL)
      pIRowset->Release();

   if (pIDBInitialize != NULL)
      {
      if (FAILED(pIDBInitialize->Uninitialize()))
      {
      // Uninitialize is not required, but it will fail if an
      // interface has not been released; we can use it for
      // debugging.
      DumpError("Someone forgot to release something!");
      }
      pIDBInitialize->Release();
      }

   if (g_pIMalloc != NULL)
      g_pIMalloc->Release();

   CoUninitialize();

   return (0);
   }

/********************************************************************
* Initialize the data source.
********************************************************************/
HRESULT myInitDSO
   (
   IDBInitialize** ppIDBInitialize  // [out]
   )
   {
   // Create an instance of the MSDASQL (ODBC) provider.
   CoCreateInstance(CLSID_MSDASQL, NULL, CLSCTX_INPROC_SERVER,
      IID_IDBInitialize, (void**)ppIDBInitialize);

   if (*ppIDBInitialize == NULL)
      {
      return (E_FAIL);
      }

   if (FAILED(mySetInitProps(*ppIDBInitialize)))
      {
      return (E_FAIL);
      }

   if (FAILED((*ppIDBInitialize)->Initialize()))
      {
      DumpError("IDBInitialze->Initialize failed.");
      return (E_FAIL);
      }

   return (NOERROR);
   }

/********************************************************************
* Set initialization properties on a data source.
********************************************************************/
HRESULT mySetInitProps
   (
   IDBInitialize*  pIDBInitialize  // [in]
   )
   {
   const ULONG nProps = 4;
   IDBProperties* pIDBProperties;
   DBPROP InitProperties[nProps];
   DBPROPSET rgInitPropSet;
   HRESULT hr;

   // Initialize common property options.
   for (ULONG i = 0; i < nProps; i++ )
      {
      VariantInit(&InitProperties[i].vValue);
      InitProperties[i].dwOptions = DBPROPOPTIONS_REQUIRED;
      InitProperties[i].colid = DB_NULLID;
      }

   // Level of prompting that will be done to complete the
   // connection process
   InitProperties[0].dwPropertyID = DBPROP_INIT_PROMPT;
   InitProperties[0].vValue.vt = VT_I2;
   InitProperties[0].vValue.iVal = DBPROMPT_NOPROMPT;

   // Data source name--see the sample source included with the
   // MDAC SDK.
   InitProperties[1].dwPropertyID = DBPROP_INIT_DATASOURCE;
   InitProperties[1].vValue.vt = VT_BSTR;
   InitProperties[1].vValue.bstrVal =
      SysAllocString(OLESTR("OLE_DB_NWind_Jet"));

   // User ID
   InitProperties[2].dwPropertyID = DBPROP_AUTH_USERID;
   InitProperties[2].vValue.vt = VT_BSTR;
   InitProperties[2].vValue.bstrVal = SysAllocString(OLESTR(""));

   // Password
   InitProperties[3].dwPropertyID = DBPROP_AUTH_PASSWORD;
   InitProperties[3].vValue.vt = VT_BSTR;
   InitProperties[3].vValue.bstrVal = SysAllocString(OLESTR(""));

   rgInitPropSet.guidPropertySet = DBPROPSET_DBINIT;
   rgInitPropSet.cProperties = nProps;
   rgInitPropSet.rgProperties = InitProperties;

   // Set initialization properties.
   pIDBInitialize->QueryInterface(IID_IDBProperties, (void**)
      &pIDBProperties);
   hr = pIDBProperties->SetProperties(1, &rgInitPropSet);

   SysFreeString(InitProperties[1].vValue.bstrVal);
   SysFreeString(InitProperties[2].vValue.bstrVal);
   SysFreeString(InitProperties[3].vValue.bstrVal);

   pIDBProperties->Release();

   if (FAILED(hr))
      {
      DumpError("Set properties failed.");
      }

   return (hr);
   }

/********************************************************************
* Execute a command, retrieve a rowset interface pointer.
********************************************************************/
HRESULT myCommand
   (
   IDBInitialize*  pIDBInitialize,    // [in]
   IRowset** ppIRowset   // [out]
   )
   {
   IDBCreateSession* pIDBCreateSession;
   IDBCreateCommand* pIDBCreateCommand;
   IRowset* pIRowset;
   ICommandText* pICommandText;
   LPCTSTR wSQLString = OLESTR("SELECT CompanyName,
                               City, Phone, Fax")
                               OLESTR(" FROM Customers")
                               OLESTR(" ORDER BY CompanyName,
                               City");
   LONG cRowsAffected;
   HRESULT hr;

   // Get the DB session object.
   if (FAILED(pIDBInitialize->QueryInterface(IID_IDBCreateSession,
      (void**) &pIDBCreateSession)))
      {
      DumpError("Session initialization failed.");
      return (E_FAIL);
      }

   // Create the session, getting an interface for command creation.
   hr = pIDBCreateSession->CreateSession(NULL, IID_IDBCreateCommand,
      (IUnknown**) &pIDBCreateCommand);
   pIDBCreateSession->Release();
   if (FAILED(hr))
      {
      DumpError("Create session failed.");
      return (hr);
      }

   // Create the command object.
   hr = pIDBCreateCommand->CreateCommand(NULL, IID_ICommandText,
      (IUnknown**) &pICommandText);
   if (FAILED(hr))
      {
      DumpError("Create command failed.");
      return (hr);
      }
   pIDBCreateCommand->Release();

   // The command requires the actual text as well as an indicator
   // of its language and dialect.
   pICommandText->SetCommandText(DBGUID_DBSQL, wSQLString);

   // Execute the command.
   hr = pICommandText->Execute(NULL, IID_IRowset, NULL,
      &cRowsAffected, (IUnknown**) &pIRowset);
   if (FAILED(hr))
      {
      DumpError("Command execution failed.");
      }
   pICommandText->Release();

   *ppIRowset = pIRowset;
   return (hr);
   }

/********************************************************************
* Get the characteristics of the rowset (the ColumnsInfo interface).
********************************************************************/
HRESULT myGetColumnsInfo
   (
   IRowset* pIRowset,   // [in]
   ULONG* pnCols,   // [out]
   DBCOLUMNINFO** ppColumnsInfo,   // [out]
   OLECHAR** ppColumnStrings   // [out]
   )
   {
   IColumnsInfo* pIColumnsInfo;
   HRESULT hr;

   if (FAILED(pIRowset->QueryInterface(IID_IColumnsInfo, (void**)
      &pIColumnsInfo)))
      {
      DumpError("Query rowset interface for IColumnsInfo failed");
      return (E_FAIL);
      }

   hr = pIColumnsInfo->GetColumnInfo(pnCols, ppColumnsInfo,
      ppColumnStrings);
   if (FAILED(hr))
      {
      DumpError("GetColumnInfo failed.");
      *pnCols = 0;
      }

   pIColumnsInfo->Release();
   return (hr);
   }

/********************************************************************
* Create binding structures from column information. Binding
* structures will be used to create an accessor that allows row value
* retrieval.
********************************************************************/
void myCreateDBBindings
   (
   ULONG nCols,   // [in]
   DBCOLUMNINFO* pColumnsInfo,  // [in]
   DBBINDING** ppDBBindings,   // [out]
   char** ppRowValues   // [out]
   )
   {
   ULONG nCol;
   ULONG cbRow = 0;
   DBBINDING* pDBBindings;
   char* pRowValues;

   pDBBindings = new DBBINDING[nCols];

   for (nCol = 0; nCol < nCols; nCol++)
      {
      pDBBindings[nCol].iOrdinal = nCol+1;
      pDBBindings[nCol].obValue = cbRow;
      pDBBindings[nCol].obLength = 0;
      pDBBindings[nCol].obStatus = 0;
      pDBBindings[nCol].pTypeInfo = NULL;
      pDBBindings[nCol].pObject = NULL;
      pDBBindings[nCol].pBindExt = NULL;
      pDBBindings[nCol].dwPart = DBPART_VALUE;
      pDBBindings[nCol].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
      pDBBindings[nCol].eParamIO = DBPARAMIO_NOTPARAM;
      pDBBindings[nCol].cbMaxLen = pColumnsInfo[nCol].ulColumnSize + 1;
      pDBBindings[nCol].dwFlags = 0;
      pDBBindings[nCol].wType = pColumnsInfo[nCol].wType;
      pDBBindings[nCol].bPrecision = pColumnsInfo[nCol].bPrecision;
      pDBBindings[nCol].bScale = pColumnsInfo[nCol].bScale;

      cbRow += pDBBindings[nCol].cbMaxLen;
      }

   pRowValues = new char[cbRow];

   *ppDBBindings = pDBBindings;
   *ppRowValues = pRowValues;

   return;
   }

/********************************************************************
* Retrieve data from a rowset.
********************************************************************/
void myGetData
   (
   IRowset* pIRowset   // [in]
   )
   {
   ULONG nCols;
   DBCOLUMNINFO* pColumnsInfo = NULL;
   OLECHAR* pColumnStrings = NULL;
   ULONG nCol;
   ULONG cRowsObtained;   // Number of rows obtained
   ULONG iRow;   // Row count
   HROW rghRows[NUMROWS_CHUNK];   // Row handles
   HROW* pRows = &rghRows[0];   // Pointer to the row
                                // handles
   IAccessor* pIAccessor;   // Pointer to the accessor
   HACCESSOR hAccessor;   // Accessor handle
   DBBINDSTATUS* pDBBindStatus = NULL;
   DBBINDING* pDBBindings = NULL;
   char* pRowValues;

   // Get the description of the rowset for use in binding structure
   // creation.
   if (FAILED(myGetColumnsInfo(pIRowset, &nCols, &pColumnsInfo,
      &pColumnStrings)))
      {
      return;
      }

   // Create the binding structures.
   myCreateDBBindings(nCols, pColumnsInfo, &pDBBindings,
      &pRowValues);
   pDBBindStatus = new DBBINDSTATUS[nCols];

   // Create the accessor.
   pIRowset->QueryInterface(IID_IAccessor, (void**) &pIAccessor);
   pIAccessor->CreateAccessor(
      DBACCESSOR_ROWDATA, // Accessor will be used to retrieve row
                          // data.
      nCols,   // Number of columns being bound
      pDBBindings,   // Structure containing bind info
      0,   // Not used for row accessors
      &hAccessor,   // Returned accessor handle
      pDBBindStatus   // Information about binding validity
      );

   // Process all the rows, NUMROWS_CHUNK rows at a time
   while (TRUE)
      {
      pIRowset->GetNextRows(
      0,   // Reserved
      0,   // cRowsToSkip
      NUMROWS_CHUNK,   // cRowsDesired
      &cRowsObtained,   // cRowsObtained
      &pRows );   // Filled in w/row handles.

      // All done; there are no more rows left to get.
      if (cRowsObtained == 0)
      break;

      // Loop over rows obtained, getting data for each.
      for (iRow=0; iRow < cRowsObtained; iRow++)
      {
      pIRowset->GetData(rghRows[iRow], hAccessor, pRowValues);
      for (nCol = 0; nCol < nCols; nCol++)
         {
         wprintf(OLESTR("%s%s:"), pColumnsInfo[nCol].pwszName,
            wcslen(pColumnsInfo[nCol].pwszName) > 10 ?
            OLESTR("\t") :
            OLESTR("\t\t"));
         printf("\t%s\n",
            &pRowValues[pDBBindings[nCol].obValue]);
         }
      printf("\n");
      }

      // Release row handles.
      pIRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL,
      NULL);
      }   // End while

   // Release the accessor.
   pIAccessor->ReleaseAccessor(hAccessor, NULL);
   pIAccessor->Release();

   delete [] pDBBindings;
   delete [] pDBBindStatus;
   delete [] pRowValues;

   g_pIMalloc->Free( pColumnsInfo );
   g_pIMalloc->Free( pColumnStrings );

   return;
   }

/********************************************************************
* Dump an error to the console.
********************************************************************/
void DumpError(LPSTR lpStr)
   {
   printf(lpStr);
   printf("\n");
   }

Appendix B

The following is a complete source code listing for the example presented in "Handling Long Data Types." The example selects the Notes column (LONG_VARCHAR) from the Employees table of the Access Northwind sample database. (This database was formerly shipped with the MDAC SDK.)

To build the file using Microsoft Visual C++ 4.0 or later:

  1. Create a new console application.
  2. Copy the following code to a new .cpp file.
  3. Ensure that your build directory settings reference the OLE DB include directory.
  4. Alter the build link settings to include linking to Oledb.lib.
/********************************************************************
* Using ISequentialStream to retrieve LONG_VARCHAR/ LONG_VARBINARY
* (BLOB) data
********************************************************************/
#define UNICODE
#define _UNICODE
#define DBINITCONSTANTS
#define INITGUID

#include <windows.h>
#include <stdio.h>

#include <oledb.h>   // OLE DB include files
#include <oledberr.h>
#include <msdasql.h>

#define  BLOCK_SIZE 250

// Prototypes
HRESULT myInitDSO(IDBInitialize** ppIDBI);
HRESULT mySetInitProps(IDBInitialize* pIDBInitialize);
HRESULT myCommand(IDBInitialize* pIDBI, IRowset** ppIRowset);
void myGetBLOBData(IRowset* pIRowset);
void DumpError(LPSTR lpStr);

IMalloc* g_pIMalloc = NULL;

/********************************************************************
* OLE DB application main()
********************************************************************/
int main()
   {
   IDBInitialize*  pIDBInitialize = NULL;
   IRowset* pIRowset = NULL;

   // Init COM library and set up the DLLs.
   CoInitialize(NULL);

   // Get the task memory allocator.
   if (FAILED(CoGetMalloc(MEMCTX_TASK, &g_pIMalloc)))
      goto EXIT;

   // Connect to the data source.
   if (FAILED(myInitDSO(&pIDBInitialize)))
      goto EXIT;

   // Get a session, set and execute a command.
   if (FAILED(myCommand(pIDBInitialize, &pIRowset)))
      goto EXIT;

   myGetBLOBData(pIRowset);

EXIT:
   if (pIRowset != NULL)
      pIRowset->Release();

   if (pIDBInitialize != NULL)
      {
      if (FAILED(pIDBInitialize->Uninitialize()))
      {
      // Uninitialize is not required, but it will fail if an
      // interface has not been released. We can use it for
      // debugging.
      DumpError("Someone forgot to release something!");
      }
      pIDBInitialize->Release();
      }

   if (g_pIMalloc != NULL)
      g_pIMalloc->Release();

   CoUninitialize();

   return (0);
   }

/********************************************************************
* Initialize the data source.
********************************************************************/
HRESULT myInitDSO
   (
   IDBInitialize** ppIDBInitialize   // [out]
   )
   {
   // Create an instance of the MSDASQL (ODBC) provider.
   CoCreateInstance(CLSID_MSDASQL, NULL, CLSCTX_INPROC_SERVER,
      IID_IDBInitialize, (void**)ppIDBInitialize);

   if (*ppIDBInitialize == NULL)
      {
      return (E_FAIL);
      }

   if (FAILED(mySetInitProps(*ppIDBInitialize)))
      {
      return (E_FAIL);
      }

   if (FAILED((*ppIDBInitialize)->Initialize()))
      {
      DumpError("IDBInitialze->Initialize failed.");
      return (E_FAIL);
      }

   return (NOERROR);
   }

/********************************************************************
* Set initialization properties on a data source.
********************************************************************/
HRESULT mySetInitProps
   (
   IDBInitialize* pIDBInitialize   // [in]
   )
   {
   const ULONG nProps = 4;
   IDBProperties* pIDBProperties;
   DBPROP InitProperties[nProps];
   DBPROPSET rgInitPropSet;
   HRESULT hr;

   // Initialize common property options.
   for (ULONG i = 0; i < nProps; i++ )
      {
      VariantInit(&InitProperties[i].vValue);
      InitProperties[i].dwOptions = DBPROPOPTIONS_REQUIRED;
      InitProperties[i].colid = DB_NULLID;
      }

   // Level of prompting that will be done to complete the
   // connection process.
   InitProperties[0].dwPropertyID = DBPROP_INIT_PROMPT;
   InitProperties[0].vValue.vt = VT_I2;
   InitProperties[0].vValue.iVal = DBPROMPT_NOPROMPT;

   // Data source name--see the sample source included with the
   // MDAC SDK.
   InitProperties[1].dwPropertyID = DBPROP_INIT_DATASOURCE;
   InitProperties[1].vValue.vt = VT_BSTR;
   InitProperties[1].vValue.bstrVal =
      SysAllocString(OLESTR("OLE_DB_NWind_Jet"));

   // User ID
   InitProperties[2].dwPropertyID = DBPROP_AUTH_USERID;
   InitProperties[2].vValue.vt = VT_BSTR;
   InitProperties[2].vValue.bstrVal = SysAllocString(OLESTR(""));

   // Password
   InitProperties[3].dwPropertyID = DBPROP_AUTH_PASSWORD;
   InitProperties[3].vValue.vt = VT_BSTR;
   InitProperties[3].vValue.bstrVal = SysAllocString(OLESTR(""));

   rgInitPropSet.guidPropertySet = DBPROPSET_DBINIT;
   rgInitPropSet.cProperties = nProps;
   rgInitPropSet.rgProperties = InitProperties;

   // Set initialization properties.
   pIDBInitialize->QueryInterface(IID_IDBProperties, (void**)
      &pIDBProperties);
   hr = pIDBProperties->SetProperties(1, &rgInitPropSet);

   SysFreeString(InitProperties[1].vValue.bstrVal);
   SysFreeString(InitProperties[2].vValue.bstrVal);
   SysFreeString(InitProperties[3].vValue.bstrVal);

   pIDBProperties->Release();

   if (FAILED(hr))
      {
      DumpError("Set properties failed.");
      }

   return (hr);
   }

/********************************************************************
* Execute a command selecting Notes from Employees.
********************************************************************/
HRESULT myCommand
   (
   IDBInitialize* pIDBInitialize,   // [in]
   IRowset** ppIRowset   // [out]
   )
   {
   IDBCreateSession* pIDBCreateSession;
   IDBCreateCommand* pIDBCreateCommand;
   IRowset* pIRowset;
   ICommandText* pICommandText;
   LPCTSTR wSQLString = OLESTR("SELECT Notes ")
                        OLESTR("FROM Employees");
   LONG cRowsAffected;
   HRESULT hr;

   // Set for failure.
   *ppIRowset = NULL;

   // Get the OLE DB session object.
   hr = pIDBInitialize->QueryInterface(IID_IDBCreateSession,
      (void**) &pIDBCreateSession);
   if (FAILED(hr))
      {
      DumpError("Session initialization failed.");
      return (hr);
      }

   // Create the session.
   hr = pIDBCreateSession->CreateSession(NULL, IID_IDBCreateCommand,
      (IUnknown**) &pIDBCreateCommand);
   pIDBCreateSession->Release();
   if (FAILED(hr))
      {
      DumpError("Create session failed.");
      return (hr);
      }

   // Create the command object.
   hr = pIDBCreateCommand->CreateCommand(NULL, IID_ICommandText,
      (IUnknown**) &pICommandText);
   if (FAILED(hr))
      {
      DumpError("Create command failed.");
      return (hr);
      }
   pIDBCreateCommand->Release();

   // The command requires the actual text as well as an indicator
   // of its language and dialect.
   pICommandText->SetCommandText(DBGUID_DBSQL, wSQLString);

   // Execute the command.
   hr = pICommandText->Execute(NULL, IID_IRowset, NULL,
      &cRowsAffected, (IUnknown**) &pIRowset);
   if (FAILED(hr))
      {
      DumpError("Command execution failed.");
      }

   pICommandText->Release();

   *ppIRowset = pIRowset;
   return (hr);
   }

/********************************************************************
* Retrieve data from an ODBC LONG_VARCHAR column (Notes in
* Employees).
********************************************************************/
void myGetBLOBData
   (
   IRowset*    pIRowset   // [in]
   )
   {
   DBOBJECT ObjectStruct;   // For binding, retrieve an
                            // object pointer.
   DBBINDING rgBinding[1];   // Bind a single column.

   IAccessor* pIAccessor = NULL;   // Accessor creation
   HACCESSOR hAccessor = NULL;
   ULONG ulErrorBinding;

   void* pData;   // Bound consumer buffer
   HROW rghRows[1];
   HROW* pRows = &rghRows[0];
   ULONG cRows;

   char szNotes[BLOCK_SIZE + 1];   // Text data from "Notes"
   ULONG cbRead;   // Count of bytes read

   // Set up the object structure for accessor creation. Ask the
   // provider to return an ISequentialStream interface for reading.
   ObjectStruct.dwFlags = STGM_READ;
   ObjectStruct.iid = IID_ISequentialStream;

   // Set up the binding struct for the accessor.
   rgBinding[0].iOrdinal = 1;   // Only one column
   rgBinding[0].obValue  = 0;   // Offset to data
   rgBinding[0].obLength = 0;   // Ignore length
   rgBinding[0].obStatus = sizeof(IUnknown*);   // Offset to status
   rgBinding[0].pTypeInfo = NULL;   // Reserved
   rgBinding[0].pObject  = &ObjectStruct;   // Our interface
                                            // request
   rgBinding[0].pBindExt = NULL;   // Reserved
   rgBinding[0].dwPart = DBPART_VALUE |   // Get both VALUE
                         DBPART_STATUS;   // and STATUS
                   // parts.
   rgBinding[0].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
   rgBinding[0].eParamIO = DBPARAMIO_NOTPARAM;
   rgBinding[0].cbMaxLen = 0;   // Not applicable
   rgBinding[0].dwFlags  = 0;   // Reserved
   rgBinding[0].wType = DBTYPE_IUNKNOWN;   // Type
                                           // DBTYPE_IUNKNOWN
   rgBinding[0].bPrecision = 0;   // Not applicable
   rgBinding[0].bScale = 0;   // Not applicable

   // Get the accessor interface and create the accessor.
   pIRowset->QueryInterface(IID_IAccessor, (void**) &pIAccessor);

   if (FAILED(pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1,
      rgBinding, sizeof(IUnknown*) + sizeof(ULONG), &hAccessor,
      &ulErrorBinding)))
      {
      DumpError("CreateAccessor failed.");
      return;
      }

   // Allocate memory for the returned pointer and the status field.
   // The first sizeof(IUnknown*) bytes are for the pointer to the
   // object; the next sizeof(ULONG) bytes are for the status.
   pData = new BYTE[sizeof(IUnknown*) + sizeof(ULONG)];

   while (TRUE)
      {
      // Get the next row.
      if (FAILED(pIRowset->GetNextRows(NULL, 0, 1, &cRows,
         &pRows)))
      {
      DumpError("GetNextRows failed.\n");
      break;
      }

      if (cRows == 0)
      {
      break;
      }

      // Get the row data, the pointer to an ISequentialStream*.
      if (FAILED(pIRowset->GetData(*pRows, hAccessor, pData)))
      {
      DumpError("GetData failed.\n");
      break;
      }

      // Read and process BLOCK_SIZE bytes at a time.
      if ((ULONG)((BYTE*)pData)[rgBinding[0].obStatus] ==
         DBSTATUS_S_ISNULL)
      {
      // Process NULL data.
      printf("<null>");
      }
      else if ((ULONG)((BYTE*)pData)[rgBinding[0].obStatus] ==
         DBSTATUS_S_OK)
      {
      do
         {
         (*((ISequentialStream**) pData))->Read(szNotes,
         BLOCK_SIZE, &cbRead);
         if (cbRead > 0)
            {
            // process data
            szNotes[cbRead] = (char) NULL;
            printf(szNotes);
            }
         }
      while (cbRead >= BLOCK_SIZE);

      (*((ISequentialStream**) pData))->Release();

      printf("\n\n");
      }

      pIRowset->ReleaseRows(cRows, pRows, NULL, NULL, NULL);
      }

   // Clean up.
   pIAccessor->ReleaseAccessor(hAccessor, NULL);
   pIAccessor->Release();

   delete [] pData;
   }

/********************************************************************
* Dump an error to the console.
********************************************************************/
void DumpError(LPSTR lpStr)
   {
   printf(lpStr);
   printf("\n");
   }

Appendix C

The following is a complete source code listing for the example presented in "Data Manipulation." The example inserts two new rows into the Customers table of the Access Northwind sample database. (This database was formerly shipped with the MDAC SDK.)

To build the file using Microsoft Visual C++ 4.0 or later:

  1. Create a new console application.
  2. Copy the following code to a new .cpp file.
  3. Ensure that your build directory settings reference the MDAC SDK include directory.
  4. Alter the build, link settings to include linking to Oledb.lib.
/********************************************************************
* Parameterized execution in OLE DB
********************************************************************/
#define UNICODE
#define _UNICODE
#define DBINITCONSTANTS
#define INITGUID

#include <windows.h>
#include <stdio.h>
#include <stddef.h>   // For offset of macro
#include <cguid.h>   // IID_NULL

#include <oledb.h>   // OLE DB include files
#include <oledberr.h>
#include <msdaguid.h>
#include <msdasql.h>

// Types--new customer struct
typedef struct tagNEWCUSTOMER
   {
   char acCustomerID[6];
   char acCompanyName[40];
   char acContactName[30];
   char acContactTitle[30];
   char acAddress[60];
   char acCity[15];
   char acRegion[15];
   char acPostalCode[10];
   char acCountry[15];
   char acPhone[24];
   char acFAX[24];
   } NEWCUSTOMER;

// Types--new customer data struct
typedef struct tagNEWCUSTOMERDATA
   {
   PSTR szCustID;
   PSTR szCompanyName;
   PSTR szContactName;
   PSTR szContactTitle;
   PSTR szAddress;
   PSTR szCity;
   PSTR szRegion;
   PSTR szPostalCode;
   PSTR szCountry;
   PSTR szPhone;
   PSTR szFAX;
   } NEWCUSTOMERDATA;

// Prototypes
HRESULT myInitDSO(IDBInitialize** ppIDBI);
HRESULT mySetInitProps(IDBInitialize* pIDBInitialize);
HRESULT myCreateParamAccessor(ICommand* pICmd, HACCESSOR* phAccessor,
   IAccessor** ppIAccessor);
HRESULT myInsertWithParameters(IDBInitialize* pIDBInitialize);
void myDelete(IDBInitialize* pIDBInitialize);
void DumpError(LPSTR lpStr);

IMalloc* g_pIMalloc = NULL;

/********************************************************************
* main()--control flow
********************************************************************/
int main()
   {
   IDBInitialize* pIDBInitialize = NULL;
   HRESULT hr;

   // Init COM libraries and set up the DLLs.
   CoInitialize(NULL);

   // Get the task memory allocator.
   if (FAILED(CoGetMalloc(MEMCTX_TASK, &g_pIMalloc)))
      goto EXIT;

   if (FAILED(myInitDSO(&pIDBInitialize)))
      goto EXIT;

   // Execute a prepared statement with parameters.
   if (FAILED(myInsertWithParameters(pIDBInitialize)))
      goto EXIT;

   // Delete rows just added.
   myDelete(pIDBInitialize);

EXIT:
   // Clean up and disconnect.
   if (pIDBInitialize != NULL)
      {
      hr = pIDBInitialize->Uninitialize();
      pIDBInitialize->Release();
      }

   if (g_pIMalloc != NULL)
      g_pIMalloc->Release();

   CoUninitialize();

   return (0);
   }

/********************************************************************
*  Execute a prepared INSERT statement with parameters.
********************************************************************/
HRESULT myInsertWithParameters
   (
   IDBInitialize*  pIDBInitialize
   )
   {
   IDBCreateSession* pIDBCreateSession;
   IDBCreateCommand* pIDBCreateCommand;
   ICommandText* pICommandText;
   ICommandPrepare* pICommandPrepare;
   ICommandWithParameters* pICmdWithParams;
   IAccessor* pIAccessor;
   WCHAR wSQLString[] =
      OLESTR("insert into Customers (CustomerID,
         CompanyName, ContactName,")
      OLESTR(" ContactTitle, Address, City, Region,
         PostalCode, Country,")
      OLESTR(" Phone, Fax)")
      OLESTR(" values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
   DBPARAMS Params;
   long cRowsAffected;
   HACCESSOR hParamAccessor;

   NEWCUSTOMERDATA aNewCustomers[] =
      {
      "YOUME",
      "You and Me Grocers",
      "William Smith",
      "General Manager",
      "383 15th Ave. N.",
      "New York",
      "NY",
      "10018",
      "USA",
      "(212) 555-8847",
      "(212) 555-9988",

      "YORBL",
      "Yorbalinda's",
      "Mary Jones",
      "Owner",
      "22 Sunny Vale Rd.",
      "San Diego",
      "CA",
      "93122",
      "USA",
      "(605) 555-4322",
      "(605) 555-4323"
      };
   NEWCUSTOMER NewCustomer;

   ULONG nParams = 11;
   DBPARAMBINDINFO rgParamBindInfo[] =
      {
      OLESTR("DBTYPE_CHAR"), OLESTR("CustomerID"), 5,
         DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("CompanyName"), 40,
         DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("ContactName"), 30,
         DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("ContactTitle"), 30,
         DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("Address"), 60,
         DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("City"), 15,
         BPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("Region"), 15,
         DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("PostalCode"), 10,
         DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("Country"), 15,
         DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("Phone"), 24,
         DBPARAMFLAGS_ISINPUT, 0, 0,
      OLESTR("DBTYPE_VARCHAR"), OLESTR("FAX"), 24,
         DBPARAMFLAGS_ISINPUT, 0, 0,
      };
   ULONG rgParamOrdinals[] =
      {1,2,3,4,5,6,7,8,9,10,11};
   // Get the session.
   pIDBInitialize->QueryInterface(IID_IDBCreateSession,
      (void**)&pIDBCreateSession);
   pIDBCreateSession->CreateSession(NULL, IID_IDBCreateCommand,
      (IUnknown**) &pIDBCreateCommand);
   pIDBCreateSession->Release();

   // Create the command.
   pIDBCreateCommand->CreateCommand(NULL, IID_ICommandText,
      (IUnknown**) &pICommandText);
   pIDBCreateCommand->Release();

   // The command requires the actual text as well as an indicator
   // of its language.
   pICommandText->SetCommandText(DBGUID_DBSQL, wSQLString);

   // Set parameter information.
   pICommandText->QueryInterface(IID_ICommandWithParameters,
      (void**)&pICmdWithParams);
   pICmdWithParams->SetParameterInfo(nParams, rgParamOrdinals,
      rgParamBindInfo);
   pICmdWithParams->Release();

   // Prepare the command.
   pICommandText->QueryInterface(IID_ICommandPrepare,
      (void**)&pICommandPrepare);
   if (FAILED(pICommandPrepare->Prepare(0)))
      {
      pICommandPrepare->Release();
      pICommandText->Release();
      return (E_FAIL);
      }
   pICommandPrepare->Release();

   // Create parameter accessors.
   if (FAILED(myCreateParamAccessor(pICommandText, &hParamAccessor,
      &pIAccessor)))
      {
      pICommandText->Release();
      return (E_FAIL);
      }

   Params.pData = &NewCustomer;   // pData is the buffer pointer
   Params.cParamSets = 1;   // Number of sets of parameters
   Params.hAccessor = hParamAccessor;   // Accessor to the parameters

   // Specify the parameter information.
   for (UINT nCust = 0; nCust < 2; nCust++)
      {
      strcpy(NewCustomer.acCustomerID,
         aNewCustomers[nCust].szCustID);
      strcpy(NewCustomer.acCompanyName,
         aNewCustomers[nCust].szCompanyName);
      strcpy(NewCustomer.acContactName,
         aNewCustomers[nCust].szContactName);
      strcpy(NewCustomer.acContactTitle,
         aNewCustomers[nCust].szContactTitle);
      strcpy(NewCustomer.acAddress,
         aNewCustomers[nCust].szAddress);
      strcpy(NewCustomer.acCity, aNewCustomers[nCust].szCity);
      strcpy(NewCustomer.acRegion, aNewCustomers[nCust].szRegion);
      strcpy(NewCustomer.acPostalCode,
         aNewCustomers[nCust].szPostalCode);
      strcpy(NewCustomer.acCountry,
         aNewCustomers[nCust].szCountry);
      strcpy(NewCustomer.acPhone, aNewCustomers[nCust].szPhone);
      strcpy(NewCustomer.acFAX, aNewCustomers[nCust].szFAX);

      // Execute the command.
      pICommandText->Execute(NULL, IID_NULL, &Params,
         &cRowsAffected, NULL);
      printf("%ld rows inserted.\n", cRowsAffected);
      }

   pIAccessor->ReleaseAccessor(hParamAccessor, NULL);
   pIAccessor->Release();
   pICommandText->Release();

   return (NOERROR);
   }

/********************************************************************
* Delete rows just added using simple execution.
********************************************************************/
void myDelete
   (
   IDBInitialize* pIDBInitialize
   )
   {
   IDBCreateSession* pIDBCreateSession;
   IDBCreateCommand* pIDBCreateCommand;
   ICommandText* pICommandText;
   WCHAR wSQLDelete1[] =
      OLESTR("delete from Customers where CustomerID = 'YOUME'");
   WCHAR wSQLDelete2[] =
      OLESTR("delete from Customers where CustomerID = 'YORBL'");
   long cRowsAffected;

   // Get the session.
   pIDBInitialize->QueryInterface(IID_IDBCreateSession, (void**)&pIDBCreateSession);
   pIDBCreateSession->CreateSession(NULL, IID_IDBCreateCommand,
      (IUnknown**) &pIDBCreateCommand);
   pIDBCreateSession->Release();

   // Create the command.
   pIDBCreateCommand->CreateCommand(NULL, IID_ICommandText, (IUnknown**)
      &pICommandText);
   pIDBCreateCommand->Release();

   // Set the command text for first delete statement and execute
   // the command.
   pICommandText->SetCommandText(DBGUID_DBSQL, wSQLDelete1);
   pICommandText->Execute(NULL, IID_NULL, NULL, &cRowsAffected, NULL);

   printf("%ld rows deleted.\n", cRowsAffected);

   // Do it again.
   pICommandText->SetCommandText(DBGUID_DBSQL, wSQLDelete2);
   pICommandText->Execute(NULL, IID_NULL, NULL, &cRowsAffected, NULL);

   printf("%ld rows deleted.\n", cRowsAffected);

   pICommandText->Release();

   return;
   }

/********************************************************************
*  Create parameter accessor.
********************************************************************/
HRESULT myCreateParamAccessor
   (
   ICommand* pICmd,   // [in]
   HACCESSOR*  phAccessor,   // [out]
   IAccessor** ppIAccessor   // [out]
   )
   {
   IAccessor* pIAccessor;
   HACCESSOR hAccessor;
   const ULONG nParams = 11;
   DBBINDING Bindings[nParams];
   DBBINDSTATUS rgStatus[nParams];   // Returns information for
                                     // individual binding
                                     // validity.
   HRESULT hr;

   ULONG acbLengths[] = {5, 40, 30, 30, 60, 15, 15, 10,
      15, 24, 24};

   for (ULONG i = 0; i < nParams; i++)
      {
      Bindings[i].iOrdinal = i + 1;
      Bindings[i].obLength = 0;
      Bindings[i].obStatus = 0;
      Bindings[i].pTypeInfo = NULL;
      Bindings[i].pObject = NULL;
      Bindings[i].pBindExt = NULL;
      Bindings[i].dwPart = DBPART_VALUE;
      Bindings[i].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
      Bindings[i].eParamIO = DBPARAMIO_INPUT;
      Bindings[i].cbMaxLen = acbLengths[i];
      Bindings[i].dwFlags = 0;
      Bindings[i].wType = DBTYPE_STR;
      Bindings[i].bPrecision = 0;
      Bindings[i].bScale = 0;
      }

   Bindings[0].obValue = offsetof(NEWCUSTOMER, acCustomerID);
   Bindings[1].obValue = offsetof(NEWCUSTOMER, acCompanyName);
   Bindings[2].obValue = offsetof(NEWCUSTOMER, acContactName);
   Bindings[3].obValue = offsetof(NEWCUSTOMER, acContactTitle);
   Bindings[4].obValue = offsetof(NEWCUSTOMER, acAddress);
   Bindings[5].obValue = offsetof(NEWCUSTOMER, acCity);
   Bindings[6].obValue = offsetof(NEWCUSTOMER, acRegion);
   Bindings[7].obValue = offsetof(NEWCUSTOMER, acPostalCode);
   Bindings[8].obValue = offsetof(NEWCUSTOMER, acCountry);
   Bindings[9].obValue = offsetof(NEWCUSTOMER, acPhone);
   Bindings[10].obValue = offsetof(NEWCUSTOMER, acFAX);

   pICmd->QueryInterface(IID_IAccessor, (void**)&pIAccessor);

   hr = pIAccessor->CreateAccessor(
      DBACCESSOR_PARAMETERDATA,   // Accessor that will be used
                                  // to specify parameter data
      nParams,   // Number of parameters being
                 // bound
      Bindings,   // Structure containing bind
                  // information
      sizeof(NEWCUSTOMER),   // Size of parameter structure
      &hAccessor,   // Returned accessor handle
      rgStatus   // Information about binding
                 // validity
      );

   if (FAILED(hr))
      {
      DumpError("Parameter accessor creation failed.");
      }
   else
      {
      *ppIAccessor = pIAccessor;
      *phAccessor = hAccessor;
      }

   return (hr);
   }

/********************************************************************
* Initialize the data source.
********************************************************************/
HRESULT myInitDSO
   (
   IDBInitialize** ppIDBInitialize   // [out]
   )
   {
   // Create an instance of the MSDASQL (ODBC) provider.
   CoCreateInstance(CLSID_MSDASQL, NULL, CLSCTX_INPROC_SERVER,
      IID_IDBInitialize, (void**)ppIDBInitialize);

   if (*ppIDBInitialize == NULL)
      {
      return (E_FAIL);
      }

   if (FAILED(mySetInitProps(*ppIDBInitialize)))
      {
      return (E_FAIL);
      }

   if (FAILED((*ppIDBInitialize)->Initialize()))
      {
      DumpError("IDBInitialze->Initialize failed.");
      return (E_FAIL);
      }

   return (NOERROR);
   }

/********************************************************************
* Set initialization properties on a data source.
********************************************************************/
HRESULT mySetInitProps
   (
   IDBInitialize* pIDBInitialize   // [in]
   )
   {
   const ULONG   nProps = 4;
   IDBProperties* pIDBProperties;
   DBPROP InitProperties[nProps];
   DBPROPSET rgInitPropSet;
   HRESULT hr;

   // Initialize common property options.
   for (ULONG i = 0; i < nProps; i++ )
      {
      VariantInit(&InitProperties[i].vValue);
      InitProperties[i].dwOptions = DBPROPOPTIONS_REQUIRED;
      InitProperties[i].colid = DB_NULLID;
      }

   // Level of prompting that will be done to complete the
   // connection process.
   InitProperties[0].dwPropertyID = DBPROP_INIT_PROMPT;
   InitProperties[0].vValue.vt = VT_I2;
   InitProperties[0].vValue.iVal = DBPROMPT_NOPROMPT;

   // Data source name--see the sample source included with the OLE
   // DB SDK.
   InitProperties[1].dwPropertyID = DBPROP_INIT_DATASOURCE;
   InitProperties[1].vValue.vt = VT_BSTR;
   InitProperties[1].vValue.bstrVal =
      SysAllocString(OLESTR("OLE_DB_NWind_Jet"));

   // User ID
   InitProperties[2].dwPropertyID = DBPROP_AUTH_USERID;
   InitProperties[2].vValue.vt = VT_BSTR;
   InitProperties[2].vValue.bstrVal = SysAllocString(OLESTR(""));

   // Password
   InitProperties[3].dwPropertyID = DBPROP_AUTH_PASSWORD;
   InitProperties[3].vValue.vt = VT_BSTR;
   InitProperties[3].vValue.bstrVal = SysAllocString(OLESTR(""));

   rgInitPropSet.guidPropertySet = DBPROPSET_DBINIT;
   rgInitPropSet.cProperties = nProps;
   rgInitPropSet.rgProperties = InitProperties;

   // Set initialization properties.
   pIDBInitialize->QueryInterface(IID_IDBProperties, (void**)
      &pIDBProperties);
   hr = pIDBProperties->SetProperties(1, &rgInitPropSet);

   SysFreeString(InitProperties[1].vValue.bstrVal);
   SysFreeString(InitProperties[2].vValue.bstrVal);
   SysFreeString(InitProperties[3].vValue.bstrVal);

   pIDBProperties->Release();

   if (FAILED(hr))
      {
      DumpError("Set properties failed.");
      }

   return (hr);
   }

/********************************************************************
* Dump an error to the console.
********************************************************************/
void DumpError(LPSTR lpStr)
   {
   printf(lpStr);
   printf("\n");
   }


OLE DB Minimum Levels of Consumer and Provider Functionality

Abstract

OLE DB defines a comprehensive set of interfaces for accessing a diverse range of data types located in a variety of relational and nonrelational data stores. OLE DB data providers need to expose only the minimum number of interfaces that reflect the natural functionality of their data stores. This makes OLE DB providers much easier to write. Above that minimal level, common OLE DB service components provide interoperability through generic implementations of extensions such as scrolling or predicate-based positioning. This means that applications consuming OLE DB, directly or through ADO, can achieve a richer level of functionality because the OLE DB service components offer extended functionality that is not necessarily provided by the data store.

Introduction: OLE DB as a Component Technology

The purpose of this article is to define the minimal levels of functionality that an OLE DB consumer should expect from a general-purpose OLE DB provider. It describes two levels of functionality. One is the base level, which represents the most basic level of functionality that will enable very simple data transfer between providers and consumers. The other level is the extended level, which represents a higher, value-added level of functionality that most general-purpose providers will want to provide for most common data consumer applications.

The key to making this work is to separate the most essential functions from the more sophisticated functions. This is possible because OLE DB is designed as a component-based technology, building on the framework established by Microsoft's Component Object Model (COM). In COM, an interface is used to collect a group of functions. COM clearly defines the expected behavior of components when requested interfaces are not implemented. In COM, only the IUnknown interface must be implemented. All other interfaces are optional.

This design makes a few very important features possible. Components can be designed to interact correctly at different levels of functionality without limiting new features or requiring an all-or-nothing implementation. Additionally, multiple components can be combined in a seamless fashion, which reduces the work needed to achieve an advanced level of functionality. A component, by supporting aggregration, can appear to inherit interfaces from another component without actually knowing anything about that other component.

As COM components, OLE DB components also allow optional levels of functionality. The OLE DB Programmer's Reference describes all the possible interfaces, and the resulting levels of functionality, that OLE DB developers can use. However, only some of these interfaces are needed for the most basic functionality, and this article identifies those interfaces. Any other interfaces defined by the OLE DB Programmer's Reference are completely optional, and it is the developer's discretion whether to implement them.

Consumers and Providers

OLE DB describes the relationship between two components by labeling one component a data provider and the other a consumer. Even though a single component can be both a data provider and a consumer, what is important is the relationship between two components, and in such cases one component is always acting in the role of the data provider and the other as the consumer.

An OLE DB provider exposes OLE DB interfaces over some type of data. OLE DB providers include everything from a full SQL DBMS to a text file or data stream. Therefore, because data providers have different functionality, it's important not to limit that functionality. However, not all providers should be expected to expose simple tabular data to implement a full-blown query engine. A general-purpose provider exhibits a minimal level of functionality that most consumers can use, regardless of the source of data.

Implementing this minimum level does not limit a provider's ability to expose interfaces that are specific to the type of data it can access. All OLE DB providers, regardless of their level of functionality, have the ability to expose any OLE DB object or interface that the developer thinks is important to the design of the provider.

Note Custom COM interfaces that are not part of the OLE DB provider specification should not be implemented. Instead, any features that are unique to a provider should be exposed through OLE DB properties and objects. An OLE DB provider that exposes custom COM interfaces will cause it to be tightly coupled to any consumer that uses those interfaces. A design using proprietary COM interfaces contradicts one of the primary goals of OLE DB: to keep the methods used to access data consistent across all providers. To keep training costs and code complexity low, developers writing OLE DB consumers prefer to work with standardized data access techniques.

Consumers

Developers writing OLE DB consumers can choose their level of interoperability with OLE DB providers. Consumers may be written to consume a specific provider, in which case they are designed to be aware of the functionality specific to that provider, such as its ability to update data or its dynamic properties. Consumers may also be written to consume generic providers. This article focuses on the features these generic providers should have. From the consumer's perspective, there are two levels of functionality for generic OLE DB providers: base, and extended. To consume generic providers, the consumer can choose from a range of strategies, such as the following:

  • Consume a base level of functionality and work with all OLE DB providers.
  • Consume an extended level of functionality and query the provider for support of extended functionality.
  • Consume an extended level of functionality and invoke service components to implement missing functionality.

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

  • The consumer can invoke common services to implement extended functionality, where such service components are available.
  • The consumer can implement extra code within the application to compensate for missing functionality.
  • The consumer can reduce the functionality available to the user based on missing functionality in the provider.
  • The consumer can return an error indicating that the provider is not capable of supporting required functionality.

The following section describes the entire base level of functionality that all generic consumers can expect all generic OLE DB providers to support. In some cases, OLE DB service components implement the functionality rather than the OLE DB providers.

Required Consumer Objects and Interfaces

The following objects and their respective interfaces are always required. Some interfaces and the properties they support are available only if the OLE DB provider supports updating data. If the OLE DB provider does not support these optional interfaces, the provider should be considered a read-only provider.

Root Enumerator Object

The following interfaces for this object are always supported by common components in OLE DB. They are never implemented directly by providers.

IDBInitialize
IParseDisplayName
ISourcesRowset

Data Links Object

The following required interfaces for this object are always supported by common components in OLE DB. They are never implemented directly by providers.

IDataInitialize
IPromptInitialize

Row Position Object

The following required interface for this object is always supported by common components in OLE DB. It is never implemented directly by providers.

IRowPosition

Data Source Object

The following interfaces are required for this object:

IDBCreateSession
IDBInitialize
IDBProperties (see "Required Consumer Properties," below)
IPersist

Session Object

IGetDataSource
IOpenRowset
ISessionProperties

Rowset Object

The following interfaces are required for this object:

IAccessor
IColumnsInfo
IConvertType
IRowset
IRowsetChange (required for updatable providers only)
IRowsetFind
IRowsetIdentity
IRowsetInfo (see "Required Consumer Properties," below)
IRowsetLocate
IRowsetScroll

Required Consumer Properties

Some objects support interfaces that expose dynamic properties for the object. This section lists all the properties that a consumer can expect all OLE DB providers to support at the base level of functionality.

Some properties are available only if the OLE DB provider supports updating data. If the OLE DB provider does not support these optional properties, it should be considered a read-only provider.

IDBProperties Interface

This interface must support the following property:

DBPROP_INIT_PROMPT

The DBPROP_INIT_PROMPT property has a special condition. Support for DBPROP_INIT_PROMPT is required only if any initialization properties are required for connecting to the provider. If calling IDBInitialize::Initialize can succeed on a freshly created data source object, the provider does not need to support this property.

A limited exception to this requirement may be made in specific circumstances. Some providers may require initialization properties for IDBInitialize::Initialize to succeed but for some specific reason, such as memory or user interface requirements, cannot prompt the user. In such a case, the correct action is for the provider to support only the DBPROMPT_NOPROMPT value for the DBPROP_INIT_PROMPT property. This behavior does not bring the provider to the minimum level of functionality and should be used only for exceptional cases. The documentation for the provider should clearly document this exception as well as the actual behavior of the provider when given a value other than DBPROMPT_NOPROMPT. The suggested behavior is that setting DBPROP_INIT_PROMPT to values other than DBPROMPT_NOPROMPT should cause either a DB_S_ERRORSOCCURRED or a DB_E_ERRORSOCCURRED result, with the DBPROPSTATUS value for the DBPROP_INIT_PROMPT property set to DBPROPSTATUS_NOTSUPPORTED.

IRowsetInfo Interface

This interface must support the following properties:

DBPROP_CANFETCHBACKWARDS
DBPROP_CANHOLDROWS
DBPROP_CANSCROLLBACKWARDS
DBPROP_OWNINSERT (for updatable providers)
DBPROP_OWNUPDATEDELETE (for updatable providers)
DBPROP_REMOVEDELETED (for updatable providers)

Providers

Developers of general-purpose OLE DB providers must offer consumers at least the base level of functionality. There are two ways to achieve this. One way is to write the provider to a minimum level and rely upon OLE DB service components to complete the functionality to the base level. The other way is to write the provider to implement the base level of functionality entirely and not use the OLE DB service components.

The levels discussed in the sections that follow are minimal levels. Additional interfaces can always be implemented wherever the developer of the OLE DB provider sees fit. The extended level of functionality represents the level that most common general-purpose OLE DB providers will have.

Data providers written in Microsoft® Visual Basic®, Microsoft Visual J++®, Microsoft Visual C++®, or C, using the OLE DB Simple Provider (OSP) Toolkit, automatically support all of the base level of functionality and more.

General Behavior

All providers must be apartment, rental, or free threaded and must support aggregation of the data source, session, and rowset objects. If applicable, providers must support the aggregation of the command and view objects.

Providers must support data conversions to the types returned in IColumnsInfo::GetColumnsInfo or IColumnsRowset::GetColumnsRowset for rowset columns, and ICommandWithParameters::GetParameterInfo for parameters, as well as DBTYPE_WSTR for all column or parameter values other than those described as objects. Providers that support binding to objects as IStream or ILockBytes must also support binding to those columns as ISequentialStream.

Minimum Level Providers

It is important for provider writers to implement the full set of interfaces that apply to their particular type of data. At a minimum, the provider must implement the objects, interfaces, and behavior listed in this section to be considered a generic OLE DB provider. Providers implementing the minimum provider functionality can rely on OLE DB service components available in the Microsoft Data Access Components (MDAC) SDK to implement the base consumer functionality, as shown in Figure 1.

Figure 1. Base application using service components to talk to a minimum provider

Required Provider Objects and Interfaces

OLE DB providers at the minimum level of functionality must support the following objects and their interfaces. Some interfaces are required only for OLE DB providers that support updating data. Providers that do not support these interfaces are considered read-only providers.

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

Data Source Object

The following interfaces are required for this object:

IDBCreateSession
IDBInitialize
IDBProperties
IPersist

Session Object

The following interfaces are required for this object:

IGetDataSource
IOpenRowset
ISessionProperties

Rowset Object

The following interfaces are required for this object:

IAccessor
IColumnsInfo
IConnectionPointContainer for IRowsetNotify (for updatable providers)
IConvertType(for updatable providers)
IRowset
IRowsetChange (for updatable providers)
IRowsetIdentity
IRowsetInfo (see "Required Provider Properties," below)

Supporting IConnectionPointContainer for IRowsetNotify is not strictly required for OLE DB providers to satisfy the minimum requirements for an updatable provider. However, updatable providers are strongly encouraged to expose this connection point because some consumers will be forced to treat providers as read-only if they do not support rowset notifications by exposing the connection point for IRowsetNotify.

Required Provider Properties

Some objects support interfaces that expose dynamic properties for the object. This section lists all the properties that all OLE DB providers must support at the minimum level of functionality.

Some properties are required only if the OLE DB provider supports updating data. If the OLE DB provider does not support these optional properties, the provider will be considered a read-only provider.

IRowsetInfo Interface

This interface must support the following properties:

DBPROP_CANHOLDROWS
DBPROP_DATASOURCEREADONLY
DBPROP_IACCESSOR
DBPROP_ICOLUMNSINFO
DBPROP_ICONVERTTYPE
DBPROP_IROWSET
DBPROP_IROWSETIDENTITY
DBPROP_IROWSETINFO
DBPROP_OWNINSERT (for updatable providers)
DBPROP_OWNUPDATEDELETE (for updatable providers)
DBPROP_REMOVEDELETED (for updatable providers)

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

Base Level Providers

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

Figure 2. Base application talking directly to a base provider

Required Objects and Interfaces for Base Level Provider Implementation

OLE DB providers at the base level of functionality must support the following objects and their interfaces. Some interfaces are required only for OLE DB providers that support updating data. Providers that do not support these interfaces are considered read-only providers.

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

Rowset Object

The following interfaces are required for this object:

IRowsetFind
IRowsetLocate
IRowsetScroll

Required Properties for Base Level Provider Implementation

Some objects support interfaces that expose dynamic properties for the object. This section lists all the properties that all OLE DB providers must support at the base level of functionality.

Some properties are required only if the OLE DB provider supports updating data. If the OLE DB provider does not support these optional properties, the provider will be considered a read-only provider.

IRowsetInfo Interface

This interface must support the following properties:

DBPROP_CANFETCHBACKWARDS
DBPROP_CANSCROLLBACKWARDS

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

Extended Level Providers

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

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

  • Rowset processing—Exposed by providers that support native filter or sort capabilities.
  • Commands—Exposed by providers that can execute more sophisticated queries or statements.
  • Transactions—Exposed by providers that can support transactional capabilities.
  • Index navigation—Exposed by providers built on indexed data to expose index functionality to service components such as query processors. OLE DB consumers other than service components generally do not directly consume these interfaces.

Required Objects and Interfaces for Extended Level Provider Implementation

OLE DB providers at the extended level of functionality must support the following objects and their interfaces. Some interfaces are required only for OLE DB providers that support updating data. Providers that do not support these interfaces are considered read-only providers.

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

Data Source Object

The following interfaces are required for this object:

IConnectionPointContainer for IDBAsynchNotify
IDBAsynchStatus
IDataSourceAdmin
IDBInfo (extended functionality that may be exposed by providers that support rowset processing; see "Required Properties for Extended Level Provider Implementation," below)
IPersistFile
ISupportErrorInfo

Session Object

The following interfaces are required for this object:

IAlterIndex
IAlterTable
IDBCreateCommand (extended functionality that may be exposed by providers that support commands)
IDBSchemaRowset
IDBView (extended functionality that may be exposed by providers that support rowset processing)
IIndexDefinition
ISupportErrorInfo
ITableDefinition
ITransactionJoin (for transacted providers)
ITransactionLocal (for transacted providers)
ITransactionObject (for transacted providers)

View Object

The view object, available through the IDBView interface, represents extended functionality that may be exposed by providers that support rowset processing. The following interfaces are required for this object:

IAccessor
IColumnsInfo
ISupportErrorInfo
IViewChapter
IViewFilter
IViewRowset
IViewSort

Command Object

The command object, available through the IDBCreateCommand interface, represents extended functionality that may be exposed by providers that support commands. The following interfaces are required for this object:

IAccessor
IColumnsInfo
IColumnsRowset
ICommand
ICommandPersist
ICommandPrepare
ICommandProperties (see "Required Properties for Extended Level Provider Implementation," below)
ICommandText
ICommandWithParameters
IConvertType
ISupportErrorInfo

Custom Error Object

The following interface is required for this object:

ISQLErrorInfo

Multiple Results Object

The following interface is required for this object:

IMultipleResults

Rowset Object

The following interfaces are required for this object:

IChapteredRowset (extended functionality that may be exposed by providers that support rowset processing)
IColumnsRowset
IConnectionPointContainer for IDBAsynchNotify
IConnectionPointContainer for IRowsetNotify
IDBAsynchStatus
IRowsetChapterMember
IRowsetIdentity
IRowsetIndex (extended functionality that may be exposed by providers that support index navigation)
IRowsetLocate
IRowsetResynch (for updatable providers)
IRowsetUpdate (for updatable providers)
IRowsetView (extended functionality that may be exposed by providers that support rowset processing)
ISupportErrorInfo

BLOB Support

The following interfaces are required for this object (applies to rowset objects):

ILockBytes
ISequentialStream
IStorage
IStream

Required Properties for Extended Level Provider Implementation

Some objects support interfaces that expose dynamic properties for the object. This section lists all the properties that all OLE DB providers must support at the extended level of functionality.

Some properties are required only if the OLE DB provider supports updating data. If the OLE DB provider does not support these optional properties, the provider will be considered a read-only provider.

ICommandProperties Interface

This interface must support the following property:

DBPROP_MAXTABLESINSELECT > 1

IRowsetInfo Interface

This interface must support the following properties:

DBPROP_ABORTPRESERVE (for transacted providers)
DBPROP_BOOKMARKSKIPPED
DBPROP_CHANGEINSERTEDROWS (for updatable providers)
DBPROP_COMMITPRESERVE (for transacted providers)
DBPROP_IMMOBILEROWS (for updatable providers)
DBPROP_LITERALBOOKMARKS
DBPROP_LITERALIDENTITY
DBPROP_ORDEREDBOOKMARKS
DBPROP_OTHERINSERT
DBPROP_OTHERUPDATEDELETE
DBPROP_RETURNPENDINGROWS (for updatable providers)
DBPROP_STRONGIDENTITY

BLOB Support

BLOBs must support the following properties (applies to IRowset interface on rowset objects):

DBPROP_BLOCKING STORAGEOBJECTS = FALSE
DBPROP_MULTIPLE STORAGEOBJECTS
DBPROP_OLEOBJECTS
DBPROP_STRUCTURED STORAGE


Microsoft OLE DB Provider for Oracle: Tips, Tricks, and Traps

July 1998

By Steve Starck

Introduction

The Microsoft® OLE DB Provider for Oracle is a native provider: It only exposes whatever Oracle can expose through the Oracle Call Interface API.

A native provider does not have as much overhead as one that exposes all the interfaces and behaviors defined for OLE DB. Thus, applications that do not need the full set of functionality gain performance. The disadvantage is that it limits the flexibility of the application developer.Fortunately, several service components provide functionality not available from the Microsoft OLE DB Provider for Oracle. Because they add a layer of overhead, however, they should be avoided whenever possible to get the best performance.

This white paper explains some tricks you can use to squeeze the best performance from the provider, and describes how to use the service components shipping with Microsoft® Data Access Components (MDAC) 2.0 to get behavior not implemented by the provider. Then, it covers some issues about how to work with stored procedures. Finally, this paper lists some Oracle features that are not supported in the provider at this time.

Getting the Best Performance Possible

Here are some tips on how to achieve the best performance with the Microsoft OLE DB Provider for Oracle.

Use OLE DB Session Pooling

In any Oracle application, a significant amount of time is spent establishing connections to the Oracle server itself. The Session Pooling service component manages pools of similar connections to optimize them for a high-volume application. It is recommended that this component be used in all cases.

Session pooling is automatically enabled if you use ADO, but when writing an application directly to OLE DB you must initialize your data source by using the IDataInitialize interface instead of using CoCreateInstance on the provider itself. Here’s an example of how to write this in C++:

#define DBINITCONSTANTS

#include <oledb.h>
#include <msdasc.h>
#include <crtdbg.h>

static LPCWSTR   s_pwszDataSource =
                     L"Provider=MSDAORA;"
                     L"Data Source=goliath;"
                     L"User ID=scott;"
                     L"Password=tiger;";

HRESULT UseSession (IUnknown* pUnkSession);

main ()
{
   HRESULT           hr;
   IUnknown*         pUnkDataSource = NULL;
   IDataInitialize*  pIDataInitialize = NULL;

   hr = CoInitialize (NULL);
   _ASSERT ( SUCCEEDED (hr) );

   hr = CoCreateInstance
            (
            CLSID_MSDAINITIALIZE,
            NULL,
            CLSCTX_INPROC_SERVER,
            IID_IDataInitialize,
            (LPVOID*)&pIDataInitialize
            );

   if (pIDataInitialize)
   {
      hr = pIDataInitialize->GetDataSource
               (
               NULL,
               CLSCTX_INPROC_SERVER,
               s_pwszDataSource,
               IID_IUnknown,
               &pUnkDataSource
               );

      pIDataInitialize->Release ();
   }

   if (pUnkDataSource)
   {
      IDBInitialize*   pIDBInitialize = NULL;

      hr = pUnkDataSource->QueryInterface
                  (
                  IID_IDBInitialize,
                 (LPVOID*)&pIDBInitialize
                  );
      if (pIDBInitialize)
      {
         hr = pIDBInitialize->Initialize ();

         if ( SUCCEEDED(hr) )
         {
            IDBCreateSession*   pIDBCreateSession = NULL;

            hr = pIDBInitialize->QueryInterface
                       (
                       IID_IDBCreateSession,
                       (LPVOID*)&pIDBCreateSession
                       );

            if (pIDBCreateSession)
            {
               IUnknown*   pUnkSession = NULL;
               hr = pIDBCreateSession->CreateSession
                        (
                        NULL,
                        IID_IOpenRowset,
                        (IUnknown**)&pUnkSession
                        );

               if (pUnkSession)
               {
                  hr = UseSession (pUnkSession);
               }
               pUnkSession->Release ();
            }
         }
         pIDBInitialize->Release ();
      }
      pUnkDataSource->Release ();
   }
   return 0;
}

Avoid the Use of Scrolling or Updatable Rowsets

Oracle does not expose a scrollable server cursor. In addition, Oracle does not expose a way to update a row without using a SQL UPDATE statement.

As of this writing, the Microsoft OLE DB Provider for Oracle only exposes forward-only, read-only rowsets. Therefore, the provider itself does not implement the IRowsetFind, IRowsetLocate, IRowsetScroll, and IRowsetChange interfaces, nor does it support fetching or scrolling backwards.

When using IDataInitialize to create your data source object, however, you can get these interfaces by simply setting their corresponding DBPROPSET_ROWSET property value to VARIANT_TRUE. Doing so will force the Client Cursor Engine service component to automatically perform the work necessary to expose them.

Naturally, there is some amount of overhead incurred to use the client cursor engine, so if you do not need to fetch or scroll backward through the rowset or plan to update data in the rowset, you should request a forward-only, read-only rowset for optimal performance.

Continuing with our previous example of writing directly to OLE DB in C++, you would replace the call to UseSession with a call to the following routine to guarantee a forward-only, read-only rowset:

HRESULT GetForwardOnlyRowset (IUnknown* pUnkSession)
{
   HRESULT hr = S_OK;
   LPCWSTR   pwszTable = L"EMP";

   IOpenRowset*   pIOpenRowset = NULL;
   hr = pUnkSession->QueryInterface (IID_IOpenRowset,
                              (LPVOID*)&pIOpenRowset);

   if (pIOpenRowset)
   {
      IRowset*   pIRowset = NULL;
      DBID      dbidTemp;

      dbidTemp.eKind = DBKIND_NAME;
      dbidTemp.uGuid.guid = GUID_NULL;
      dbidTemp.uName.pwszName = (LPWSTR)pwszTable;

      hr = pIOpenRowset->OpenRowset
               (
               NULL,
               &dbidTemp,
               NULL,
               IID_IRowset,
               0,
               NULL,
               (IUnknown **)&pIRowset
               );

      if (pIRowset)
      {
         hr = FetchRowsetForward (pIRowset);
         pIRowset->Release ();
      }
      pIOpenRowset->Release ();
   }
   return hr;
}

If you are using ADO to write your application, you request a CursorLocation of adUseServer, a CursorType of adOpenForwardOnly, and a LockType of adLockReadOnly. Here’s an example written in Microsoft® Visual Basic®:

Sub ForwardOnly()
    Dim cn As New ADODB.Connection
    Dim rs As New ADODB.Recordset

    cn.Open "Provider=MSDAORA;Data Source=goliath;User ID=scott;Password=tiger"
    rs.CursorLocation = adUseServer
    rs.Open "emp", cn, adOpenForwardOnly, adLockReadOnly

    rs.MoveFirst

    While rs.EOF <> True
        Debug.Print rs(0).Value, rs(1).Value
        rs.MoveNext
    Wend

    rs.Close
    cn.Close
End Sub

Consider Asynchronous Fetches When Using Client Cursors

If you must have a scrollable rowset, the Client Cursor Engine service component can provide a snapshot cursor. The cursor engine will fully materialize the rowset on the client for you. However, unless you specifically request that the client cursor engine should use asynchronous fetches, it will wait until it completes its fetches before returning control to your application. In the case of a large rowset, this could take quite a bit of time.

In many cases, your application may start to fetch forward but will need to fetch backward as well. In that case, you may wish to request that the materialization of the rowset occur asynchronously.

If writing directly to OLE DB in C++, set the DBPROP_ROWSET_ASYNCH property in DBPROPSET_ROWSET to DBPROPVAL_ASYNCH_BACKGROUNDPOPULATION before instantiating the rowset:

HRESULT GetScrollableRowset (IUnknown* pUnkSession)
{
   HRESULT hr = S_OK;
   LPCWSTR   pwszTable = L"EMP";

   IOpenRowset*   pIOpenRowset = NULL;
   hr = pUnkSession->QueryInterface (IID_IOpenRowset,
                              (LPVOID*)&pIOpenRowset);

   if (pIOpenRowset)
   {
      IRowset*      pIRowset = NULL;
      DBID         dbidTemp;
      DBPROPSET      rgPropset[1];
      DBPROP         rgProps[3];

      dbidTemp.eKind = DBKIND_NAME;
      dbidTemp.uGuid.guid = GUID_NULL;
      dbidTemp.uName.pwszName = (LPWSTR)pwszTable;

      rgPropset[0].rgProperties = rgProps;
      rgPropset[0].cProperties = 3;
      rgPropset[0].guidPropertySet = DBPROPSET_ROWSET;

      rgProps[0].dwPropertyID = DBPROP_CANFETCHBACKWARDS;
      rgProps[0].dwOptions = DBPROPOPTIONS_REQUIRED;
      rgProps[0].dwStatus = 0;
      VariantInit (&rgProps[0].vValue);
      rgProps[0].vValue.vt = VT_BOOL;
      rgProps[0].vValue.boolVal = VARIANT_TRUE;

      rgProps[1].dwPropertyID = DBPROP_CANSCROLLBACKWARDS;
      rgProps[1].dwOptions = DBPROPOPTIONS_REQUIRED;
      rgProps[1].dwStatus = 0;
      VariantInit (&rgProps[1].vValue);
      rgProps[1].vValue.vt = VT_BOOL;
      rgProps[1].vValue.boolVal = VARIANT_TRUE;

      rgProps[2].dwPropertyID = DBPROP_ROWSET_ASYNCH;
      rgProps[2].dwOptions = DBPROPOPTIONS_REQUIRED;
      rgProps[2].dwStatus = 0;
      VariantInit (&rgProps[2].vValue);
      rgProps[2].vValue.vt = VT_I4;
      V_I4(&rgProps[2].vValue) = DBPROPVAL_ASYNCH_BACKGROUNDPOPULATION;

      hr = pIOpenRowset->OpenRowset
               (
               NULL,
               &dbidTemp,
               NULL,
               IID_IRowset,
               1,
               rgPropset,
               (IUnknown **)&pIRowset
               );

      if (pIRowset)
      {
         hr = FetchRowsetBackward (pIRowset);
         pIRowset->Release ();
      }
      pIOpenRowset->Release ();

      VariantClear (&rgProps[0].vValue);
      VariantClear (&rgProps[1].vValue);
      VariantClear (&rgProps[2].vValue);
   }
   return hr;
}

If you were using ADO, you would request the adAsyncFetch option when opening the recordset. Here’s a Visual Basic example:

Sub ScrollableAsync()
    Dim cn As New ADODB.Connection
    Dim rs As New ADODB.Recordset

    cn.Open "Provider=MSDAORA;Data Source=goliath;User ID=scott;Password=tiger"
    rs.CursorLocation = adUseClient
    rs.Open "emp", cn, adOpenStatic, adLockReadOnly, adAsyncFetch

    rs.MoveFirst

    While rs.EOF <> True
        Debug.Print rs(0).Value, rs(1).Value
        rs.MoveNext
    Wend

    rs.Close
    cn.Close
End Sub

Use Singleton Selects to Get LONG and LONG RAW Data

The provider allows you to fetch LONG and LONG RAW data in a rowset. Since Oracle only exposes forward-only rowsets, and OLE DB requires that you be able to get data from a rowset at any time, deferred fetching (that is, fetching data only when it is requested) is not possible.

At this time, the provider retrieves all data for a LONG or LONG RAW column for each row it fetches. In the case where you only occasionally require the data from the LONG or LONG RAW column, it is much more efficient to simply request LONG and LONG RAW columns in a second query statement.

If writing directly to OLE DB in C++, you would use code as follows:

HRESULT GetDeferredBLOBRowset (IUnknown* pUnkSession)
{
   HRESULT hr = S_OK;
   LPCWSTR pwszStmt1 = L"select owner, view_name from all_views";
   LPCWSTR pwszStmt2 = L"select text from all_views where owner = ? and view_name = ?";

   IDBCreateCommand*   pIDBCreateCommand = NULL;
   ICommandText*      pICommand1 = NULL;
   ICommandText*      pICommand2 = NULL;
   hr = pUnkSession->QueryInterface (IID_IDBCreateCommand,
                              (LPVOID*)&pIDBCreateCommand);

   if (pIDBCreateCommand)
   {
      hr = pIDBCreateCommand->CreateCommand (NULL, IID_ICommandText,
                                    (IUnknown **)&pICommand1);
      _ASSERT ( SUCCEEDED(hr) );
      if (pICommand1)
      {
         hr = pICommand1->SetCommandText(DBGUID_SQL, pwszStmt1);
         _ASSERT ( SUCCEEDED(hr) );
      }

      hr = pIDBCreateCommand->CreateCommand (NULL, IID_ICommandText,
                                    (IUnknown **)&pICommand2);
      _ASSERT ( SUCCEEDED(hr) );
      if (pICommand2)
      {
         hr = pICommand2->SetCommandText(DBGUID_SQL, pwszStmt2);
         _ASSERT ( SUCCEEDED(hr) );
      }
   }

   if (pICommand1 && pICommand2)
   {
      IRowset*   pIRowset = NULL;

      hr = pICommand1->Execute
               (
               NULL,
               IID_IRowset,
               NULL,
               NULL,
               (IUnknown **)&pIRowset
               );

      if (pIRowset)
      {
         hr = FetchRowsetDeferredBLOB (pIRowset, pICommand2);
         pIRowset->Release ();
      }
   }
   if (pICommand1)
   {
      pICommand1->Release ();
   }
   if (pICommand2)
   {
      pICommand2->Release ();
   }
   return hr;
}
HRESULT FetchRowsetDeferredBLOB (IRowset *   pIRowset, ICommandText * pICommand)
{
   HRESULT      hr = S_OK;
   HACCESSOR   hAccessor;
   HACCESSOR   hAccessorBLOB;
   HACCESSOR   hAccessorParam;
   ULONG      cbData;
   BYTE *      pData;
   BYTE *      pBLOB;
   HROW      hrow;
   HROW *      phrow = &hrow;
   ULONG      cFetched;
   ULONG      cColumns;
   DBBINDING *   rgBinding;

   hr = CreateFastAccessor
         (
         pIRowset,
         &hAccessor,
         &cColumns,
         &rgBinding,
         &cbData
         );

   if (pICommand)
   {
      // Get an accessor for the BLOB rowset
      IAccessor *   pIAccessor;
      DBBINDING    BLOBBinding;
      DBBINDING   ParamBinding[2];
      ULONG      i;

      hr = pICommand->QueryInterface (IID_IAccessor, (LPVOID*)&pIAccessor);
      _ASSERT ( pIAccessor );

      memset ((LPVOID)&BLOBBinding, 0, sizeof(BLOBBinding));

      BLOBBinding.iOrdinal   = 1;

      BLOBBinding.obValue      = 0;
      BLOBBinding.obStatus   = 4000;
      BLOBBinding.obLength   = BLOBBinding.obStatus + sizeof (DBSTATUS);
      BLOBBinding.dwPart      = DBPART_STATUS | DBPART_LENGTH | DBPART_VALUE;

      BLOBBinding.dwMemOwner   = DBMEMOWNER_CLIENTOWNED;
      BLOBBinding.eParamIO   = DBPARAMIO_NOTPARAM;
      BLOBBinding.wType      = DBTYPE_STR | DBTYPE_BYREF;
      BLOBBinding.cbMaxLen   = 4000;

      hr = pIAccessor->CreateAccessor
               (
               DBACCESSOR_ROWDATA,
               1,
               &BLOBBinding,
               0,
               &hAccessorBLOB,
               NULL
               );
      _ASSERT ( SUCCEEDED(hr) );

      // Get a Parameter Accessor for the two parameters
      memset ((LPVOID)&ParamBinding, 0, sizeof(ParamBinding));

      for (i = 0; i < 2; i++)
      {
         ParamBinding[i] = rgBinding[i];
         ParamBinding[i].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
         ParamBinding[i].eParamIO = DBPARAMIO_INPUT;
      }
      hr = pIAccessor->CreateAccessor
               (
               DBACCESSOR_PARAMETERDATA,
               2,
               ParamBinding,
               0,
               &hAccessorParam,
               NULL
               );
      _ASSERT ( SUCCEEDED(hr) );

   }

   if ( SUCCEEDED (hr) )
   {
      pData = (BYTE *)malloc (cbData);
      pBLOB = (BYTE *)malloc (4000 + sizeof (ULONG) + sizeof(DBSTATUS));
      if (pData)
      {
         while ( hr == S_OK )
         {
            hr = pIRowset->GetNextRows (NULL, 0, 1, &cFetched, &phrow );

            if ( SUCCEEDED(hr) && cFetched )
            {
               hr = pIRowset->GetData ( hrow, hAccessor, pData );

               if ( SUCCEEDED(hr) )
               {
                  CHAR *   pszOwner;
                  CHAR *   pszView;

                  pszOwner   = (CHAR *)*( (BYTE **)(pData + rgBinding[0].obValue) );
                  pszView      = (CHAR *)*( (BYTE **)(pData + rgBinding[1].obValue) );

                  printf ("OWNER=%-30.30s VIEW_NAME=%-30.30s ", pszOwner, pszView);
                  if ( strcmp (pszOwner, "SYS") == 0)
                  {
                     printf ("(BLOB Skipped...)\n");
                  }
                  else
                  {
                     hr = FetchBLOB (pICommand, hAccessorParam, pData, hAccessorBLOB, pBLOB);
                  }
               }
               pIRowset->ReleaseRows  (1, &hrow, NULL, NULL, NULL);
            }
         }
      }
      free (rgBinding);
      free (pData);
      free (pBLOB);
   }
   return hr;
}

HRESULT FetchBLOB (ICommandText * pICommand, HACCESSOR hAccessorParam, BYTE *pData, HACCESSOR hAccessorBLOB, BYTE *pBLOB)
{
   HRESULT      hr = S_OK;
   HROW      hrow;
   HROW *      phrow = &hrow;
   ULONG      cFetched;
   IRowset*   pIRowset = NULL;
   DBPARAMS   dbParams;

   dbParams.pData = pData;
   dbParams.cParamSets = 1;
   dbParams.hAccessor = hAccessorParam;

   hr = pICommand->Execute
            (
            NULL,
            IID_IRowset,
            &dbParams,
            NULL,
            (IUnknown **)&pIRowset
            );

   if (pIRowset)
   {
      if ( SUCCEEDED (hr) )
      {
         hr = pIRowset->GetNextRows (NULL, 0, 1, &cFetched, &phrow );

         if ( SUCCEEDED(hr) && cFetched )
         {
            hr = pIRowset->GetData ( hrow, hAccessorBLOB, pBLOB );

            if ( SUCCEEDED(hr) )
            {
               printf ("VIEW_TEXT=%s\n\n", *(CHAR **)pBLOB);
            }
            pIRowset->ReleaseRows  (1, &hrow, NULL, NULL, NULL);
         }
      }
      pIRowset->Release ();
   }
   return hr;
}

Here’s another example using ADO, written in Visual Basic. Note the use of the parameterized query and how parameters are defined rather than derived.

Sub DeferredBLOBs()
    Dim cn As New ADODB.Connection
    Dim rs As New ADODB.Recordset
    Dim cmd As New ADODB.Command
    Dim rs2 As New ADODB.Recordset

    cn.Open "Provider=MSDAORA;Data Source=goliath;User ID=scott;Password=tiger"
    rs.CursorLocation = adUseServer
    rs.Open "select owner, view_name from all_views", cn, adOpenForwardOnly, adLockReadOnly

    cmd.ActiveConnection = cn
    cmd.CommandText = "select text from all_views where owner = ? and view_name = ?"
    cmd.Parameters.Append cmd.CreateParameter("owner", adVarChar, adParamInput, 30)
    cmd.Parameters.Append cmd.CreateParameter("viewname", adVarChar, adParamInput, 30)

    rs.MoveFirst

    While rs.EOF <> True
        Debug.Print rs(0).Value, rs(1).Value,

        If (rs(0).Value <> "SYS") Then
            cmd(0) = rs(0).Value
            cmd(1) = rs(1).Value
            Set rs2 = cmd.Execute

            Debug.Print rs2(0).Value
        Else
            Debug.Print "Skipped..."
        End If
        rs.MoveNext
    Wend

    rs.Close
    cn.Close
End Sub

Set Your Own Parameter Information

Oracle does not expose a way to determine parameter information for SQL DML (Data Manipulation Language) commands (SELECT, INSERT, UPDATE, and DELETE), so the provider does not derive it. If you call ICommandWithParameters::GetParameterInfo for a Command object with a SELECT, INSERT, UPDATE, or DELETE statement, you will get a return value of DB_E_PARAMUNAVAILABLE.

That being said, Oracle does expose a way to derive parameter information for PL/SQL stored procedures and functions. There is a significant amount of overhead in the Oracle Call Interface function that is used to do this, so it is strongly recommended that you do not ask the provider to derive this information.

If your application were written for ADO you would simply append parameter definitions instead of calling Command.Parameter.Refresh. See the DeferredBLOB example for the best way to do this.

If your application is written directly to OLE DB, do not call ICommandWithParameters::GetParameterInfo without having first called ICommandWithParameters::SetParameterInfo.

Try to Use BYREF, PROVIDEROWNED Bindings

Although not a recommendation specific to the OLE DB Provider for Oracle, it is always better to create DBTYPE_BYREF accessor bindings and use DBMEMOWNER_PROVIDEROWNED. This will cause the provider to hand you a pointer to the data in its row buffer, rather than making a copy in your row buffer.

Note The Client Cursor Engine service component does not support binding data with DBMEMOWNER_PROVIDEROWNED at this time. However, as this paper points out, that is not the best performance scenario.

The following C++ code illustrates how to create an accessor with BYREF, PROVIDEROWNED bindings using the information returned from the IColumnsInfo interface. The FetchRowsetForward function will fetch all the data for rowsets created by our GetForwardOnlyRowset routine.

HRESULT CreateFastAccessor
   (
   IRowset*      pIRowset,
   HACCESSOR *      phAccessor,
   ULONG *         pcColumns,
   DBBINDING **   prgBinding,
   ULONG *         pcbData
   )
{
   HRESULT         hr = S_OK;
   IAccessor *      pIAccessor;
   IColumnsInfo *   pIColumnsInfo;
   ULONG         cColumns;
   DBCOLUMNINFO *   rgInfo;
   OLECHAR *      pStringsBuffer;
   ULONG         i;

   DBBINDING *      rgBinding;

   *pcColumns = 0;
   *prgBinding = NULL;
   *pcbData = 0;

   hr = pIRowset->QueryInterface (IID_IAccessor, (LPVOID*)&pIAccessor);
   _ASSERT ( pIAccessor );

   hr = pIRowset->QueryInterface (IID_IColumnsInfo, (LPVOID*)&pIColumnsInfo);
   _ASSERT ( pIColumnsInfo );

   hr = pIColumnsInfo->GetColumnInfo
            (
            &cColumns,
            &rgInfo,
            &pStringsBuffer
            );

   if ( SUCCEEDED(hr) )
   {
      rgBinding = (DBBINDING *)malloc (cColumns * sizeof(DBBINDING));

      if (rgBinding)
      {
         memset (rgBinding, 0, (cColumns * sizeof(DBBINDING)) );

         for (i = 0; i < cColumns; i++)
         {
            rgBinding[i].iOrdinal   = rgInfo[i].iOrdinal;

            rgBinding[i].obValue   = *pcbData;
            (*pcbData) += sizeof (void *);

            rgBinding[i].obLength   = *pcbData;
            (*pcbData) += sizeof (void *);

            rgBinding[i].obStatus   = *pcbData;
            (*pcbData) += sizeof (void *);

            rgBinding[i].dwPart   = (DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS);
            rgBinding[i].dwMemOwner = DBMEMOWNER_PROVIDEROWNED;
            rgBinding[i].eParamIO   = DBPARAMIO_NOTPARAM;
            rgBinding[i].wType      = rgInfo[i].wType | DBTYPE_BYREF;
         }

         hr = pIAccessor->CreateAccessor
                  (
                  DBACCESSOR_ROWDATA,
                  cColumns,
                  rgBinding,
                  0,
                  phAccessor,
                  NULL
                  );

         if ( FAILED(hr) )
         {
            free (rgBinding);
            rgBinding = NULL;
            cColumns = 0;
         }
      }
   }

   g_pIMalloc->Free (rgInfo);
   g_pIMalloc->Free (pStringsBuffer);
   pIColumnsInfo->Release ();
   pIAccessor->Release ();

   *prgBinding = rgBinding;
   *pcColumns = cColumns;
   return hr;
}

HRESULT FetchRowsetForward (IRowset* pIRowset)
{
   HRESULT      hr = S_OK;
   HACCESSOR   hAccessor;
   ULONG      cbData;
   BYTE *      pData;
   HROW      hrow;
   HROW *      phrow = &hrow;
   ULONG      cFetched;
   ULONG      cColumns;
   DBBINDING *   rgBinding;

   hr = CreateFastAccessor
         (
         pIRowset,
         &hAccessor,
         &cColumns,
         &rgBinding,
         &cbData
         );

   if ( SUCCEEDED (hr) )
   {
      pData = (BYTE *)malloc (cbData);
      if (pData)
      {
         while ( hr == S_OK )
         {
            hr = pIRowset->GetNextRows (NULL, 0, 1, &cFetched, &phrow );

            if ( SUCCEEDED(hr) && cFetched )
            {
               hr = pIRowset->GetData ( hrow, hAccessor, pData );

               if ( SUCCEEDED(hr) )
               {
                  hr = PrintColumn ("ENAME", pData, &rgBinding[1]);
               }
               pIRowset->ReleaseRows  (1, &hrow, NULL, NULL, NULL);
            }
         }
      }
      free (rgBinding);
      free (pData);
   }
   return hr;
}

Avoid Unnecessary Conversions to WIDE CHAR data

There is no support for Unicode in the Oracle Version 7 Call Interface. All data is therefore returned as MultiByte. When fetching character data that does not need to be in Unicode, you can improve performance slightly by choosing DBTYPE_STR bindings instead of DBTYPE_WSTR. In every case, the information returned by IColumnsInfo::GetColumnInfo indicates the optimal binding.

Note that because character data in the schema rowsets are specified as DBTYPE_WSTR, they will be converted for you automatically. Binding them as DBTYPE_STR would force the conversion back to MultiByte strings.

Use ICommandPrepare

Each time a statement is executed, the provider does some work to prepare for its execution, such as determining rowset columns and preparing for parameter binding. Whenever a statement will be executed more than once, consider using ICommandPrepare to force the preparation of the command to occur only once. When you do not use ICommandPrepare, each execution will have to redo the work.

Getting Additional Behavior

At this time, the provider implements the base level functionality, and allows you to use service components to get additional behavior.

These are OLE DB interfaces implemented by the provider itself:

Data Source Objects:
   CoType TDataSource {
      interface IDBCreateSession;
      interface IDBInfo;
      interface IDBInitialize;
      interface IDBProperties;
      interface IPersist;
      interface ISupportErrorInfo;
   }
Sessions:
   CoType TSession {
      interface IDBCreateCommand;
      interface IDBSchemaRowset;
      interface IGetDataSource;
      interface IOpenRowset;
      interface ISessionProperties;
      interface ISupportErrorInfo;
      interface ITransaction;
      interface ITransactionJoin;
      interface ITransactionLocal;
   }
Commands:
   CoType TCommand {
      interface IAccessor;
      interface IColumnsInfo;
      interface ICommand;
      interface ICommandPrepare;
      interface ICommandProperties;
      interface ICommandText;
      interface ICommandWithParameters;
      interface IConvertType;
      interface ISupportErrorInfo;
   }
Rowsets:
   CoType TRowset {
      interface IAccessor;
      interface IColumnsInfo;
      interface IConnectionPointContainer;
      interface IConvertType;
      interface IRowset;
      interface IRowsetIdentity;
      interface IRowsetInfo;
      interface ISupportErrorInfo;
   }
Transaction Options:
   CoType TTransactionOptions {
      interface ISupportErrorInfo;
      interface ITransactionOptions;
   };

These are additional OLE DB interfaces implemented by service components:

Rowsets:
   CoType TRowset {
      interface IRowsetChange;
      interface IRowsetFind;
      interface IRowsetLocate;
      interface IRowsetScroll;
   };
};

You must create your instance with IDataInitialize to use any of these additional interfaces. An example of how to do this is included in the example of how use OLE DB session pooling.

Getting a Scrollable Rowset

The provider does not natively implement fetching backwards, scrolling backwards, or the IRowsetFind, IRowsetLocate, or IRowsetScroll interfaces. Therefore, applications that require scrollable rowsets must use the client cursor engine to construct one. To do this, set both DBPROP_CANFETCHBACKWARDS and DBPROP_CANSCROLLBACKWARDS to VARIANT_TRUE before creating the rowset.

In our C++ example, you replace the call to UseSession with a call to the GetScrollableRowset function instead:

HRESULT GetScrollableRowset (IUnknown* pUnkSession)
{
   HRESULT hr = S_OK;
   LPCWSTR   pwszTable = L"EMP";

   IOpenRowset*   pIOpenRowset = NULL;
   hr = pUnkSession->QueryInterface (IID_IOpenRowset,
                              (LPVOID*)&pIOpenRowset);

   if (pIOpenRowset)
   {
      IRowset*      pIRowset = NULL;
      DBID         dbidTemp;
      DBPROPSET      rgPropset[1];
      DBPROP         rgProps[3];

      dbidTemp.eKind = DBKIND_NAME;
      dbidTemp.uGuid.guid = GUID_NULL;
      dbidTemp.uName.pwszName = (LPWSTR)pwszTable;

      rgPropset[0].rgProperties = rgProps;
      rgPropset[0].cProperties = 3;
      rgPropset[0].guidPropertySet = DBPROPSET_ROWSET;

      rgProps[0].dwPropertyID = DBPROP_CANFETCHBACKWARDS;
      rgProps[0].dwOptions = DBPROPOPTIONS_REQUIRED;
      rgProps[0].dwStatus = 0;
      VariantInit (&rgProps[0].vValue);
      rgProps[0].vValue.vt = VT_BOOL;
      rgProps[0].vValue.boolVal = VARIANT_TRUE;

      rgProps[1].dwPropertyID = DBPROP_CANSCROLLBACKWARDS;
      rgProps[1].dwOptions = DBPROPOPTIONS_REQUIRED;
      rgProps[1].dwStatus = 0;
      VariantInit (&rgProps[1].vValue);
      rgProps[1].vValue.vt = VT_BOOL;
      rgProps[1].vValue.boolVal = VARIANT_TRUE;

      rgProps[2].dwPropertyID = DBPROP_ROWSET_ASYNCH;
      rgProps[2].dwOptions = DBPROPOPTIONS_REQUIRED;
      rgProps[2].dwStatus = 0;
      VariantInit (&rgProps[2].vValue);
      rgProps[2].vValue.vt = VT_I4;
      V_I4(&rgProps[2].vValue) = DBPROPVAL_ASYNCH_BACKGROUNDPOPULATION;

      hr = pIOpenRowset->OpenRowset
               (
               NULL,
               &dbidTemp,
               NULL,
               IID_IRowset,
               1,
               rgPropset,
               (IUnknown **)&pIRowset
               );

      if (pIRowset)
      {
         hr = FetchRowsetBackward (pIRowset);
         pIRowset->Release ();
      }
      pIOpenRowset->Release ();

      VariantClear (&rgProps[0].vValue);
      VariantClear (&rgProps[1].vValue);
      VariantClear (&rgProps[2].vValue);
   }
   return hr;
}

Notice that the rowset is instantiated with DBPROP_ROWSET_ASYNCH set to DBPROPVAL_ASYNCH_BACKGROUNDPOPULATION. This instructs the client cursor engine to materialize the rowset asynchronously, enabling your application to regain control much more quickly when the rowset is very large.

If you write your application in ADO, you request a CursorLocation of adUseClient and a CursorType of adOpenStatic. Here’s an example written in Visual Basic:

Sub Scrollable()
    Dim cn As New ADODB.Connection
    Dim rs As New ADODB.Recordset

    cn.Open "Provider=MSDAORA;Data Source=goliath;User ID=scott;Password=tiger"
    rs.CursorLocation = adUseClient
    rs.Open "emp", cn, adOpenStatic, adLockReadOnly, adAsyncFetch

    rs.MoveLast

    While rs.BOF <> True
        Debug.Print rs(0).Value, rs(1).Value
        rs.MovePrevious
    Wend

    rs.Close
    cn.Close
End Sub

In this example, the use of adAsyncFetch instructs the client cursor engine to materialize the rowset asynchronously, enabling your application to regain control much more quickly when the rowset is very large.

Getting an Updatable Rowset

The provider does not natively implement the IRowsetChange interface. Therefore, applications that must perform updates and do not wish to make discreet update statements must ask the client cursor engine to construct an updatable rowset. To do this, set DBPROP_IRowsetChange to VARIANT_TRUE before creating the rowset.

In our C++ example, you replace the call to UseSession with a call to the GetUpdatableRowset function instead:

HRESULT GetUpdatableRowset (IUnknown* pUnkSession)
{
   HRESULT hr = S_OK;
   LPCWSTR   pwszTable = L"EMP";

   IOpenRowset*   pIOpenRowset = NULL;
   hr = pUnkSession->QueryInterface (IID_IOpenRowset,
                              (LPVOID*)&pIOpenRowset);

   if (pIOpenRowset)
   {
      IRowset *      pIRowset = NULL;
      DBID         dbidTemp;
      DBPROPSET      rgPropset[1];
      DBPROP         rgProps[2];

      dbidTemp.eKind = DBKIND_NAME;
      dbidTemp.uGuid.guid = GUID_NULL;
      dbidTemp.uName.pwszName = (LPWSTR)pwszTable;

      rgPropset[0].rgProperties = rgProps;
      rgPropset[0].cProperties = 2;
      rgPropset[0].guidPropertySet = DBPROPSET_ROWSET;

      rgProps[0].dwPropertyID = DBPROP_IRowsetChange;
      rgProps[0].dwOptions = DBPROPOPTIONS_REQUIRED;
      rgProps[0].dwStatus = 0;
      VariantInit (&rgProps[0].vValue);
      rgProps[0].vValue.vt = VT_BOOL;
      rgProps[0].vValue.boolVal = VARIANT_TRUE;

      rgProps[1].dwPropertyID = DBPROP_ROWSET_ASYNCH;
      rgProps[1].dwOptions = DBPROPOPTIONS_REQUIRED;
      rgProps[1].dwStatus = 0;
      VariantInit (&rgProps[1].vValue);
      rgProps[1].vValue.vt = VT_I4;
      V_I4(&rgProps[1].vValue) = DBPROPVAL_ASYNCH_BACKGROUNDPOPULATION;

      hr = pIOpenRowset->OpenRowset
               (
               NULL,
               &dbidTemp,
               NULL,
               IID_IRowset,
               1,
               rgPropset,
               (IUnknown **)&pIRowset
               );

      if (pIRowset)
      {
         hr = FetchRowsetForward (pIRowset);
         pIRowset->Release ();
      }
      pIOpenRowset->Release ();

      VariantClear (&rgProps[0].vValue);
      VariantClear (&rgProps[1].vValue);
   }
   return hr;
}

Notice that the rowset is instantiated with DBPROP_ROWSET_ASYNCH set to DBPROPVAL_ASYNCH_BACKGROUNDPOPULATION. This instructs the client cursor engine to materialize the rowset asynchronously, enabling your application to regain control much more quickly when the rowset is very large.

If you write your application in ADO, you request a CursorLocation of adUseClient a CursorType of adOpenStatic, and a LockType of adLockOptimistic. Here’s an example written in Visual Basic:

Sub Updatable()
    Dim cn As New ADODB.Connection
    Dim rs As New ADODB.Recordset

    cn.Open "Provider=MSDAORA;Data Source=goliath;User ID=scott;Password=tiger"
    rs.CursorLocation = adUseClient
    rs.Open "emp", cn, adOpenStatic, adLockOptimistic, adAsyncFetch

    rs.MoveFirst

    While rs.EOF <> True
        Debug.Print rs(0).Value, rs(1).Value
        rs(0).Value = rs(0).Value
        rs.Update
        rs.MoveNext
    Wend

    rs.Close
    cn.Close
End Sub

In this example, the use of adAsyncFetch instructs the client cursor engine to materialize the rowset asynchronously, enabling your application to regain control more quickly when the rowset is large.

Working with Oracle Stored Procedures

Using Oracle Native Syntax for Stored Procedure Calls

The provider supports two methods of calling a stored procedure or function: the canonical syntax defined in the ODBC specification; and the Oracle native syntax, PL/SQL.

Canonical syntax:

{call spfoo [(arg…)]}

Oracle native (PL/SQL) syntax:

begin
   spfoo [{arg…)]
end;

When using canonical syntax, parameters can be derived. When using Oracle native syntax, however, it is impractical to derive parameter information because it would require parsing of the PL/SQL code in the provider.

Likewise, when using the canonical syntax, default parameter values can be specified by binding them with a status of DBSTATUS_S_DEFAULT. When using the PL/SQL syntax, you must use named notation and not include bindings for those parameters you wish to specify defaults for.

LONG/LONG RAW Limitations

Oracle PL/SQL LONG and LONG RAW data types cannot hold more then 32760 bytes of data, even though their name implies that they can support up to 2 gigabytes. This limitation is well documented by Oracle, but it is a common mistake to attempt to insert BLOB data into the database through a stored procedure, where it is truncated to 32760 bytes.

If you need to work with LONG or LONG RAW values that exceed this limitation, you must use SQL SELECT, INSERT, or UPDATE statements instead of a PL/SQL stored procedure.

Features Not Currently Supported

A few features are not currently exposed in the Microsoft OLE DB Provider for Oracle. Some are Oracle 8 features that cannot be exposed through the Oracle 7 Call Interface used by the provider.

NCHAR, NVARCHAR

There is no way to expose an NCHAR or NVARCHAR through the Oracle 7 Call Interface. Therefore, you cannot insert into or update values in fields defined as NCHAR or NVARCHAR. A SELECT operation should work, however, because Oracle will perform the necessary conversion.

VARCHARs Larger than 2000 Bytes

In Oracle version 8, the maximum size of a VARCHAR column was increased from 2000 to 4000 bytes. However, the Oracle 7 client software has no way to bind a parameter value larger 2000 bytes. If you create a table with a VARCHAR column of larger than 2000 bytes, you will be unable to perform parameterized inserts, updates, deletes, and queries against it with data that exceeds the 2000-byte limit of the client software. Because both the ODBC Driver for Oracle and the OLE DB Provider for Oracle support parameterized inserts, updates, deletes, and queries, they will report ORA-01026 errors in this case. Data that is within the limits enforced by the Oracle client software will work, however.

The workaround is to update your client software to Oracle 8 (8.0.4.1.1c or higher).

REF CURSOR Parameters

Oracle exposes a method to pass cursor references as input and output parameters to PL/SQL stored procedures. When you create a stored procedure and specify a parameter with data type REF CURSOR, you can bind an OCI cursor to the parameter.

You might expect the OLE DB provider for Oracle to let you define these as DBTYPE_IUNKNOWN, then bind them with the pObject element in the DBBINDING structure set to IID_IRowset. However, because some issues remain to be resolved, this version of the provider does not expose this feature.

{resultset…} Escape from the ODBC Driver

Use the OLE DB Provider for ODBC Drivers and the Microsoft ODBC Driver for Oracle to get this behavior.

Save Points

At this time, the provider does not support nested transactions, which is how it would expose save points.


Open Database Connectivity (ODBC) Technical Articles

  • ODBC Security


ODBC Security

August 1999 (combined)

Author unknown.

Summary: This technical article combines two security articles relevant to ODBC in an effort provide faster access to this information and a place where future and ongoing ODBC security issues can be presented. The topics of this article are:

  • Understanding ODBC Security Issues
  • Securing Windows to Prevent ODBC Tracing


Understanding ODBC Security Issues

Articles in the computer press claim that new security problems were introduced with the Microsoft ODBC 3.0 software release. Microsoft ODBC (Open Database Connectivity) 3.0 and later conforms to the ODBC database access standard administered by The Open Group (formerly X/Open and the Open Software Foundation).

These erroneous articles are based on a misunderstanding of a troubleshooting feature that has existed since the Microsoft ODBC 2.0 release. This "call tracing" feature was implemented at the request of developers and support organizations to allow the logging of information to a file for troubleshooting. Since tracing tools can be used to decode sensitive information under certain circumstances, this feature and security concerns are discussed in the ODBC Programmer's Reference.

A variety of methods are available for enhancing security when using ODBC for client/server database connections. Applications can use secure methods for authenticating users, instead of sending plain text logon information that is vulnerable to tracing and sniffing. Programming methods are available to prevent tracing. Sites requiring enhanced security can use new ODBC components that obscure logon information and provide additional security features.

Sensible security policies can be combined with these technical methods to provide secure data access using ODBC. Microsoft takes security issues seriously and will aggressively address any concerns regarding the use of debugging and troubleshooting tools.

Who Needs to Understand ODBC Security and Tracing Issues?

Application developers must thoroughly understand the use of ODBC development techniques for developing secure applications.

Database system administrators, network security specialists and troubleshooters for database applications should understand security issues regarding communications protocols, including ODBC and ODBC call tracing.

End users of Microsoft Office and other applications should simply follow the security guidelines outlined for their organizations.

Sources for Further Information

Microsoft Press Books

Inside ODBC, by Kyle Geiger

Microsoft ODBC 3.0 Programmer's Reference

From the Microsoft Data Access Web site: http://www.microsoft.com/data/

You can download the MDAC SDK, which includes the Microsoft ODBC 3.51 Programmer's Reference.

The ODBC pages on the Data Access Web site have other information, such as white papers.

Microsoft Knowledge Base

You can also find ODBC information in the Microsoft Knowledge Base. This support database is produced by Microsoft Technical Support and provides information about Microsoft products, including problem reports, troubleshooting tips, fix lists, and general information.

Technical Summary

Call tracing enables troubleshooting information to be logged to a file on the client machine. It can be turned on using the ODBC Driver Manager application in the Control Panel. Trace entries are in text format and include parameters such as the connect string. Some drivers include the user ID and password in this string for authentication on the server.

The call tracing feature is off by default. Under normal circumstances a developer or support technician turns on tracing, runs the application that accesses the database, and then examines the trace file. When tracing is enabled, it causes severe performance degradation and can quickly generate a huge log file.

A malicious user would have to take extreme measures to obtain authentication information using ODBC tracing. First, an assumption is made that applications don't use security methods to encrypt logon information. Then the malicious user must have physical access to the machine, enable call tracing, and wait for a legitimate user to log on to a secured database. Another assumption is made that the legitimate user doesn't notice performance slowing to a crawl and investigate the problem. The malicious user would need to share the file on the net or return to the machine to examine the file. It would probably be far easier for a malicious user to sniff the network.

ODBC trace information can be obtained by other methods, including the use of the tracing utility in the ODBC Data Source Administrator; third-party ODBC debugging tools, which include call tracing; and debuggers, which can intercept calls to the ODBC Driver Manager and inspect parameters.

Enhancing Security in an ODBC Environment

  • Applications use Windows NT security

    Applications using Windows NT security do not pass user IDs and passwords across ODBC connections. For example, Microsoft SQL Server can be configured to use "integrated" Windows NT security. IBM DB/2 has a similar configuration. Applications certified to carry the BackOffice logo have the option to use Windows NT security.

  • Applications use custom security with ODBC

    Some applications implement their own security mechanisms on top of ODBC. In this case user IDs and passwords are encrypted before being transmitted via ODBC. Troubleshooting tools capturing the ODBC calls only see encrypted information.

  • Application programmers prevent tracing

    Applications should prevent tracing of any sensitive commands by setting the connection attribute SQL_ATTR_TRACE to SQL_OPT_TRACE_OFF, and then re-enable tracing by setting SQL_ATTR_TRACE to SQL_OPT_TRACE_ON afterwards. Tracing can be completely disabled by leaving SQL_ATTR_TRACE off.

  • Windows 95 or Windows NT administrators modify DLL

    On Windows 95 or Windows NT, the trace DLL, Odbctrac.dll, can simply be deleted. This should be done as part of an automated log-on script, as future ODBC installations may reinstall the DLL.

  • Windows NT security special enhancements

    On Windows NT, registry security and default registry settings can be employed to disallow modification of the trace options for all users except for administrators. Details of these procedures will be made available in the Microsoft Knowledge Base (see the Sources for Further Information).


Securing Windows to Prevent ODBC Tracing

The Microsoft® Windows NT®/Windows® 2000 operating system provides a rich set of security features. This article describes functionality in Windows NT 4.0 and Windows 2000 that provides a system administrator who requires enhanced security with the ability to keep non-administrative users from initiating an ODBC trace. This functionality is not available in Microsoft Windows 95 or Windows 98.

The following steps should be followed while logged in as an administrator:

  1. Log in to the machine you are protecting as the machine or domain administrator. Using Regedt32.exe, take ownership of the following key:

    HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBC.INI

  2. Set the value Trace to 0. The Trace value can be found under the following registry key:

    HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBC.INI\ODBC

  3. Set the value TraceDll to an empty string. The TraceDll value can be found under the following registry key:

    HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBC.INI\ODBC

  4. Set the permissions for Everyone to READ in the ODBC key.
  5. Remove explicit permissions on the ODBC key for any nonadministrative users.

For each user, there is a registry file. This file is named %SYSTEMROOT%\Profiles\Username\Ntuser.dat. These files can be loaded into Regedt32.exe by using the Load Hive command on the Registry menu:

  1. Make the HKEY_USERS window active, and click HKEY_USERS. Use the Load Hive command on the Registry menu to find the appropriate hive. When prompted for the key name, use the username you are editing.
  2. Take ownership of the ODBC key and its subkeys as was done in the preceding steps. The key will be found in the following location:

    HKEY_USERS\Username\SOFTWARE\ODBC\ODBC.INI\ODBC

  3. Set the value Trace to 0. The Trace value can be found under the following registry key:

    HKEY_LOCAL_MACHINE\Username\SOFTWARE\ODBC\ODBC.INI\ODBC

  4. Set the value TraceDll to an empty string. The TraceDll value can be found under the following registry key:

    HKEY_LOCAL_MACHINE\Username\SOFTWARE\ODBC\ODBC.INI\ODBC

  5. Set the permissions for Everyone to READ on the ODBC key.
  6. Remove explicit permissions on the ODBC key for any nonadministrative users.
  7. Unload the hive you just loaded.

The preceding steps secured currently existing registry keys. When creating new users, the seven steps above need to be followed.


XML (Extensible Markup Language)

Extensible Markup Language (XML) is a meta-markup language that provides a format for describing structured data. This facilitates more precise declarations of content and more meaningful search results across multiple platforms. In addition, XML is enabling a new generation of Web-based data viewing and manipulation applications.

Main Components of the XML SDK Documentation

XML Developer's Guide

This section of the Microsoft XML SDK documentation contains information about getting started with XML, as well as using the document object model.

XML Reference

Contains reference topics for each DOM object and corresponding interface, each method, property, event, and enumeration in the document object model. Also includes topics about XML errors, schema, and data types.

XML Tutorial

A set of interactive lessons that demonstrate XML authoring and development tasks.

XSLT Developer's Guide

This section contains information about using XSLT.

XSLT Reference

Contains reference topics for XSLT elements, methods, and pattern syntax.

XML and XSL Samples

This section lists samples that are available on MSDN Online.

Feedback

Send error reports, feature requests, and comments about the XML documentation or samples directly to the Microsoft XML documentation team.

See Also

  • Overview of XML and Internet Support in the SQL Server Books Online (Search for “Overview of XML and Internet Support”). To view the SQL Server Books Online, click this icon
  • Microsoft Data Access Components (MDAC)
  • Web Services, part of the Platform SDK documentation


What's New

The Microsoft XML documentation is updated frequently. The following topics provide details about which topics were added or expanded upon for each release:

  • What's New in the XML 2.6 SDK
  • What's New in the XML 2.5 SDK


What's New in the XML 2.6 SDK

Syntax Block Popups

The XML DOM Reference provides a new feature in the syntax block of selected reference topics. The parameters within code syntax are presented as links to their descriptions in the topic. These links are provided by a DHTML script that reads the information from the topic, so the pop-up windows contain text that is identical to that in the topic body. This feature provides one-click access to information to which you might otherwise have to scroll. The information is retained in the body of the topic for ease in printing.

XML Developer's Guide

The following topics are new:

  • White Space and the XML Declaration
  • Using In-line Schemas

XML DOM Reference

The following objects and interfaces are newly documented:

Objects | Interfaces
XMLDOMDocument2 | IXMLDOMDocument2
XMLSchemaCache | IXMLDOMSchemaCollection
XMLDOMSelection | IXMLDOMSelection
XSLProcessor | IXSLProcessor
XSLTemplate | IXSLTemplate

The following properties and methods are newly documented:

Properties | Methods
context Property | add Method
expr Property | addCollection Method
input Property | addObject Method
length Property (XMLSchemaCache) | addParameter Method
namespaces Property | clone Method
namespaceURI Property (XMLSchemaCache) | createProcessor Method
onreadystatechange Property (XMLHttpRequest) | get Method
output Property | getProperty Method (XMLDOMDocument2)
ownerTemplate Property | getProperty Method (XMLDOMSelection)
readyState Property (XSLProcessor) | item Method (XMLDOMNamedNodeMap)
schemas Property | matches Method
startMode Property | nextNode Method (XMLDOMNamedNodeMap)
startModeURI Property | peekNode Method
stylesheet Property (XSLProcessor) | remove Method
stylesheet Property (XSLTemplate) | removeAll Method
 | removeNext Method
 | reset Method (XSLProcessor)
 | selectNodes Method (XMLDOMDocument2)
 | setProperty Method
 | setStartMode Method
 | transform Method
 | validate Method

XSLT Reference

This version of the XML documentation includes full XPath function support. For more information, see XPath Functions.

The following sections and topics are also new:

XSLT Elements

The following element topics are new:

  • xsl:copy-of Element
  • xsl:include Element
  • xsl:number Element
  • xsl:output Element
  • xsl:param Element
  • xsl:preserve-space Element
  • xsl:processing-instruction Element
  • xsl:sort Element
  • xsl:strip-space Element
  • xsl:text Element
  • xsl:transform Element
  • xsl:variable Element
  • xsl:with-param Element

XPath

The following XPath topics are new:

  • XPath Syntax
    • Location Paths
    • XPath Functions
    • Sample Data
    • XPath Examples
  • Qualified Names
  • NaN Values
  • Expressions
  • Patterns

XML and XSL Samples

The following topics are new:

  • XML Sample Applications
  • XML Code Examples


What's New in the XML 2.5 SDK

Information contained in this topic was current when the XML 2.5 SDK was released. Some topics listed below might have since been replaced with newer information.

General

"Frequently Asked Questions About XML," which can be found at http://msdn.microsoft.com/xml/general, contains a new "Issues and solutions" section that explains several commonly encountered problems and their solutions.

XML Developer's Guide

A new topic, "White Space Handling," explains how white space is handled by the XML parser in element content and attributes, and how it is handled by the XML object model.

The "Content Model" topic has been expanded and now provides more detailed information about ElementType and AttributeType attributes used to define the content model for an element.

The topic "Data Types" provides information about the new attribute data type features added to Internet Explorer 5.0. These new data types include id, and simple data types such as int, boolean, and float.

The topic "XML DOM Persistence" lists the various methods and properties used for persistence in the XML Document Object Model.

Tutorials

A new tutorial, "Lesson 5: Using XML Namespaces," explains how to declare explicit and default XML namespaces.

The tutorial "Lesson 7: Using Data Types Within XML Documents" has been modified to better introduce data types.

The tutorial "Lesson 8: Accessing Typed XML Values" has been modified to better describe accessing typed values.

XSL Reference

The topic "xsl:script Element" now includes an example of how to instantiate COM objects with XSL scripts in Internet Explorer 5.01.

XML and XSL Samples

Many samples referenced in this documentation are available from the MSDN Online XML Developer Center at http://msdn.microsoft.com/xml.


Copyright and Legal Information

Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, people and events depicted herein are fictitious and no association with any real company, organization, product, person or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property.

© 1999 – 2000 Microsoft Corporation. All rights reserved.

Microsoft, MS-DOS, Windows, Windows NT, Visual Studio, Visual C++, and Visual Basic are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries/regions.

The names of actual companies and products mentioned herein may be the trademarks of their respective owners.


XML Developer's Guide

The XML Developer's Guide includes these sections:

  • Introduction to XML
  • XML DOM User Guide
  • Introduction to Namespaces
  • Using XML on the Server
  • XML Security
  • XML Data Islands
  • Binding the XML Data Source Object to Data
  • XML Schema Developer's Guide
  • The XML Object Model in Internet Explorer 4.0
  • Microsoft XML Parser in C++
  • XML Glossary

See Also

XML DOM Reference | XML Schema Reference | XML Data Types Reference | XML Error Messages


Introduction to XML

    Extensible Markup Language (XML) is a meta-markup language that provides a format for describing structured data. This facilitates more precise declarations of content and more meaningful search results across multiple platforms. In addition, XML is enabling a new generation of Web-based data viewing and manipulation applications.

    In XML you can define an unlimited set of tags. While HTML tags can be used to display a word in bold or italic, XML provides a framework for tagging structured data. An XML element can declare its associated data to be a retail price, a sales tax, a book title, the amount of precipitation, or any other desired data. As XML tags are adopted throughout an organization's intranet, and by others across the Internet, there will be a corresponding ability to search for and manipulate data regardless of the applications within which it is found. Once data has been located, it can be delivered over the wire and presented in a browser such as Internet Explorer in any number of ways, or it can be handed off to other applications for further processing and viewing.

    Structural Representation of Data

    XML provides a structural representation of data that can be broadly implemented and is easy to deploy. Industrial implementations in the Standard Generalized Markup Language (SGML) community and elsewhere demonstrate the intrinsic quality and industrial strength of XML's tree-structured data format.

    XML is a subset of SGML that is optimized for delivery over the Web. It is defined by the World Wide Web Consortium (W3C), ensuring that structured data will be uniform and independent of applications or vendors. This resulting interoperability is kick-starting a new generation of business and electronic-commerce Web applications.

    XML, which provides a data standard that can encode the content, semantics, and schemas for a wide variety of cases ranging from simple to complex, can be used to mark up the following:

    • An ordinary document.
    • A structured record, such as an appointment record or purchase order.
    • An object with data and methods, such as the persistent form of a Java object or ActiveX control.
    • A data record, such as the result set of a query.
    • Meta-content about a Web site, such as Channel Definition Format (CDF).
    • Graphical presentation, such as an application's user interface.
    • Standard schema entities and types.
    • All links between information and people on the Web.

    Once the data is on the client desktop, it can be manipulated, edited, and presented in multiple views, without return trips to the server. Servers can then become more scalable, due to lower computational and bandwidth loads. Also, because data is exchanged in the XML format, it can be easily merged from different sources.

    XML is valuable to the Internet, as well as to large corporate intranet environments, because it provides interoperability using a flexible, open, standards-based format, with new ways of accessing legacy databases and delivering data to Web clients. Applications can be built more quickly, are easier to maintain, and can easily provide multiple views on the structured data.

    Separates Data from its Presentation

    XML is powerful because it maintains the separation of the user interface from structured data. Hypertext Markup Language (HTML) specifies how to display data in a browser, but XML defines the content. For example, in HTML you use tags to tell the browser to display data as bold or italic; in XML you use tags only to describe data, such as city name, temperature, and barometric pressure. In XML, you use style sheets such as Extensible Stylesheet Language (XSL) and Cascading Style Sheets (CSS) to present the data in a browser. XML separates the data from the presentation and the process, enabling you to display and process the data as you wish by applying different style sheets and applications.

    This separation of data from presentation enables the seamless integration of data from diverse sources. Customer information, purchase orders, research results, bill payments, medical records, catalog data, and other information can be converted to XML on the middle tier, allowing data to be exchanged online as easily as HTML pages display data today. Data encoded in XML can then be delivered over the Web to the desktop. No retrofitting is necessary for legacy information stored in mainframe databases or documents, and because HTTP is used to deliver XML over the wire, no changes are required for this function.

    XML documents are easy to create, especially if you are familiar with HTML. In this example, XML is used to describe a weather report:

    <weather-report>
   <date>March 25, 1998</date>
   <time>08:00</time>
   <area>
      <city>Seattle</city>
      <state>WA</state>
      <region>West Coast</region>
      <country>USA</country>
   </area>
   <measurements>
      <skies>partly cloudy</skies>
      <temperature>46</temperature>
      <wind>
         <direction>SW</direction>
         <windspeed>6</windspeed>
      </wind>
      <h-index>51</h-index>
      <humidity>87</humidity>
      <visibility>10</visibility>
      <uv-index>1</uv-index>
   </measurements>
</weather-report>

    You can display this data in many different ways, or pass it to other applications for further processing. You can also apply a style sheet to display this information in a browser.

    XML Character Set and Encoding

    All information in XML is Unicode text. This includes the contents of elements and element names themselves. As a result, XML supports representation of all international character sets.

    Unicode can be transmitted directly as 16-bit characters, but more commonly is transferred using encoding that is more convenient or compact for certain languages. XML supports a range of encodings (the default is UTF-8), subject only to the restriction that an entire document must share the same one.


XML Process

XML can be generated from existing databases using a scalable three-tier model. With XML, structured data is maintained separately from the business rules (or process) and the display. As shown in the following diagram, the underlying process of XML includes four steps:

  • Data Integration
  • Data Delivery
  • Data Manipulation
  • Data Display


Data Integration

XML namespaces allow developers to qualify element names in a recognizable manner to avoid conflicts between elements with the same name. Elements referenced in a single document, such as a purchase order, can be defined in different schemas on the Web. Namespaces ensure that element names do not conflict and clarify their origins, but do not determine how to process elements. Parsers must know what elements mean and how to process them.

Tags from multiple namespaces can be mixed, which is essential with data coming from multiple sources across the Web. With namespaces, both elements could exist in the same XML-based document instance but could refer back to two different schemas, uniquely qualifying their semantics. For instance, in a bookstore purchase order, one title element could contain a book title, and another title element could contain the author's title.

The W3C has released XML namespaces as a recommendation, allowing elements to be subordinate to a URI. This ensures that names remain unambiguous even if chosen by multiple authors. Just as anyone can publish their own Web page or view those of others, the namespace facility allows users to define private dictionaries of terms, or use a public namespace of common terms.

The following code tells any reader that if an element name begins with "dsig:" its meaning is defined by whoever owns the "http://www.dsig.org" namespace. Similarly, elements beginning with the "person:" prefix have meanings defined by the "http://www.schemas.org/people" namespace.

<orders xmlns:person="http://www.schemas.org/people"
        xmlns:dsig="http://dsig.org">
  <order>
    <sold-to>
      <person:name>
        <person:last-name>Layman</person:last-name>
        <person:first-name>Andrew</person:first-name>
      </person:name>
    </sold-to>
    <sold-on>1997-03-17</sold-on>
    <dsig:digital-signature>1234567890</dsig:digital-signature>
  </order>
</orders>

Namespaces ensure that element names do not conflict, and clarify who defined which term. They do not give instructions about how to process the elements. Readers still need to know what the elements mean and decide how to process them.

An author can specify an element's data type (number, date, and so on) and the format of the string's contents. One can use the dt attribute from the data types namespace at "urn:schemas-microsoft-com:datatypes" for this purpose.

<sold-on dt:dt="date"
         xmlns:dt="urn:schemas-microsoft-com:datatypes">1997-03-17</sold-on>

Here, "date" specifies that the sold-on element's contents are a date in the standard format specified by the "data types" namespace. As with element names, authors will eventually be able to design their own data types, and also use types shared publicly. Microsoft is working with the W3C to define a set of standard types, and has provided an initial list as part of XML Schema support in Internet Explorer 5.

See Also

Data Delivery | Data Manipulation | Data Display


Data Delivery

Because XML is an open, text-based format, it can be delivered through HTTP in the same way as HTML. Agents will also support the ability to generate XML updates, which can be sent in both directions to inform clients of changes made to data on the middle tier or database server and vice versa. Consequently, the agents will be able to receive updates from the client and send them to a storage server.

See Also

Data Manipulation | Data Integration | Data Display


Data Manipulation

Data now on the desktop can be manipulated using the DOM. The DOM is essentially an Application Programming Interface (API) that defines a standard way in which developers can interact with the elements of the XML structured tree. The object model controls how users communicate with XML structured trees, and exposes all tree elements as objects, which can be accessed programmatically without any return trips to the server.

See Also

Data Delivery | Data Integration | Data Display


Data Display

The Microsoft XML parser in Internet Explorer 5 can read a string of XML data, process it, generate a structured tree, and expose all data elements as objects using the DOM. The parser displays this data using a Cascading Style Sheet (CSS) or an XSL style sheet; makes the data available for further manipulation by script; or hands it off to other applications or objects for further processing. Namespaces, data types, queries, and XSL transformations are supported, and extended methods are available in the DOM.

See Also

Data Delivery | Data Manipulation | Data Integration


XML Guidelines

The following topics outline some guidelines for using XML, including:

  • Making XML Data Self-Describing
  • Writing Well-Formed XML Documents
  • Handling White Space
  • Transforming and Querying XML
  • Using XML with HTML


Making XML Data Self-Describing

Document Type Definitions (DTDs) can accompany an XML document, essentially defining the rules of the document, such as which elements are present and the structural relationship between the elements. DTDs help to validate data when the receiving application does not have a built-in description of the incoming data, but DTDs are optional.

Data sent along with a DTD is known as valid XML. In this case, an XML parser could check incoming data against the rules defined in the DTD to make sure the data was structured correctly. Data sent without a DTD is known as well-formed. In this case an XML-based document instance, such as the hierarchically structured weather data shown in Writing Well-Formed XML Documents, can be used to implicitly describe itself.

With both valid and well-formed XML, XML-encoded data is self-describing because descriptive tags are intermingled with the data. The open and flexible format used by XML allows it to be employed anywhere a need exists for the exchange and transfer of information. This makes it extremely powerful.

For instance, XML can be used to describe information about HTML pages, or it can be used to describe data contained in business rules or objects in an electronic-commerce transaction, such as invoices, purchase orders, and order forms. Because XML is separate from HTML, XML also can be included within HTML documents. The World Wide Web Consortium (W3C) has defined a format by which XML-based data, or XML data islands, can be encapsulated in HTML pages. By embedding XML data inside an HTML page, multiple views can be generated from the delivered data, using the semantic information contained in the XML. Moreover, XML can be used for applications such as distributed printing, database searches, and others.

A schema is a formal specification of the rules of an XML document, specifically the element names, that indicates which elements are allowed in a document and in what combinations. New schema languages, such as defined in the XML-Data Working Group XML-Data and Document Content Description (DCD) proposals submitted to the W3C, provide the same functionality as a DTD. However, because these schema languages are extensible, developers can augment them with additional information, such as data types, inheritance, and presentation rules. This makes these new schema languages far more powerful than DTDs.

With XML-Data and DCD, Microsoft and others have proposed vocabularies for expressing the schema for an XML document using XML itself. This allows XML data to describe its own structure. Expressing schemas within XML adds great power to the XML format, because it is then possible for software examining certain data to understand its structure without having any prior built-in descriptions of the data's structure.

Using a schema, an author can define precisely which element names are permitted in a document and, within each element, which child elements, attributes, and relations are allowed. An author can import fragments from other schemas, and extend types through inheritance. This allows complex relationships between elements, while retaining the simplicity of a lexical tree structure.

Authors can invent their own schemas, or they can share schemas created by other authors. Readers can check the schema references to verify that the document they have received is the correct type. They can also use the information in the schema to validate the structure of the document automatically.


Writing Well-Formed XML Documents

An XML document is well-formed if it follows some basic rules. The XML format has a simpler set of parsing rules than HTML, allowing an XML parser to read and expose XML data without an external description or special knowledge about the meaning of the XML data. Following are the basic rules for writing well-formed XML documents.

Start tags and end tags must match

XML elements can contain text and other elements, with the exact rules for a specific document type given in its schema. However, elements must be nested: Each start tag must have a corresponding end tag.

Elements cannot overlap

The following example does not adhere to proper XML syntax:

<title>Evolution of Culture <sub>in Animals
      </title> by John T. Bonner</sub>

The following syntax corrects the overlap problem:

<title>Evolution of Culture
  <sub>in Animals</sub>
    <author>by John T. Bonner</author>
</title>

XML tags are case-sensitive

The following tags represent different elements:

<City> <CITY> <city>

Empty elements

XML has a shorthand for an empty element: A sole tag ending with /> signals that the element has no contents. For example, the following two lines are equivalent:

<title/>
<title></title>

Reserved characters

Several characters are part of the syntactic structure of XML and will not be interpreted as themselves if simply placed within an XML data source. You need to substitute the following case-sensitive special character sequences (called "entities" by XML).

Desired character | Character sequence
< | &lt;
& | &amp;
> | &gt;
" | &quot;
' | &apos;

For example, the phrase "Melons cost < $1 at the A&P" would be encoded as follows:

Melons cost &lt; $1 at the A&amp;P

Each XML document must have a unique root element

In the weather report example from Introduction to XML, the <weather-report> element denotes the unique root element of the XML document.


Handling White Space

Unlike HTML, which, in most cases, ignores white space (spaces, tabs, new lines, and so on), XML is for data, and thus has the capability through the reserved xml:space attribute to retain all white space. However, because handling white space in XML is not that simple, the following topics provide additional information and examples:

  • White Space and the XML Declaration
  • White Space in Element Content
  • White Space in Attributes
  • White Space Handling with the XML Object Model


White Space and the XML Declaration

Each XML document must begin with a special line referred to as the XML declaration. It declares the version of XML to which the file's syntax conforms. At the time of this writing, there is only a version 1.0 of the XML standard. According to the XML 1.0 standard, no white space is allowed before the XML declaration. To prevent errors while loading an XML document, make sure that no white space precedes the XML declaration, as shown in the following example.

<?xml version="1.0"?>
  <BOOK>
    <BOOKNAME>XML</BOOKNAME>
  </BOOK>


White Space in Element Content

The XML DOM has three methods for accessing the text content of elements:

Property | Behavior
nodeValue | Returns the original text content (including white space) on TEXT, CDATA, COMMENT, and PI nodes as specified in the original XML source. Returns null on ELEMENT nodes and on the DOCUMENT itself.
data | Same as nodeValue.
text | Recursively concatenates multiple TEXT and CDATA nodes in a specified subtree and returns the combined result.

Note White space consists of newline, tab, and space characters.

The nodeValue property always returns what is in the original document independent of how the document is loaded and current xml:space scope.

The text property concatenates all text in the specified subtree and expands entities. This is dependant upon how the document is loaded, the current state of the preserveWhiteSpace switch, and the current xml:space scope, as follows:

preserveWhiteSpace = true when the document is loaded

preserveWhiteSpace=true | preserveWhiteSpace=true | preserveWhiteSpace=false | preserveWhiteSpace=false
xml:space=preserve | xml:space=default | xml:space=preserve | xml:space=default
preserved | preserved | preserved | preserved and trimmed

preserveWhiteSpace = false when the document is loaded

preserveWhiteSpace=true | preserveWhiteSpace=true | preserveWhiteSpace=false | preserveWhiteSpace=false
xml:space=preserve | xml:space=default | xml:space=preserve | xml:space=default
half preserved | half preserved and trimmed | half preserved | half preserved and trimmed

Where preserved means the exact original text content as found in the original XML document, trimmed means the leading and trailing spaces have been removed, and half preserved means that "significant white space" is preserved and "insignificant white space" is normalized. Significant white space is white space inside of text content. Insignificant white space is white space between tags as follows:

<name>\n
\t<first>    Jane</first>\n
\t<last>Smith     </last>\n
</name>

In this example, the red is insignificant white space and can be ignored, while the green is significant white space since it is part of the text content and therefore has a significant meaning and cannot be ignored. So in this example, the text property returns the following results:

State | Returned value
preserved | "\n\t Jane\n\tSmith \n"
preserved and trimmed | "Jane\n\tSmith"
half preserved | " Jane Smith "
half preserved and trimmed | "Jane Smith"

Notice that "half preserved" normalizes insignificant white space, for example, the newlines and tab characters are collapsed down into a single space character. You can change the xml:space attributes and the preserveWhiteSpace switch and the text property will return a different value accordingly.

CDATA and xml:space="preserve" subtree boundaries

In the following example, the contents of the CDATA node or the "preserved" node are concatenated as they are and do not participate in the insignificant white space normalization. For example:

<name>\n
\t<first> Jane </first>\n
\t<last><![CDATA[     Smith     ]></last>\n
</name>

In this case, the white space inside the CDATA node is never "merged" with "insignificant" white space and is never trimmed. Therefore, the "half preserved and trimmed" case will return the following:

"Jane     Smith     "

Here, the insignificant white space between the </first> and <last> tags is included independent of the contents of the CDATA node. The same result is returned if the CDATA is replaced with the following:

<last xml:space="preserve">     Smith     </last>

Entities are special

Entities are loaded and parsed as part of the DTD and appear under the DOCTYPE node. They do not necessarily have any xml:space scope. For example:

<!DOCTYPE foo [
<!ENTITY Jane "<employee>\n
\t<name> Jane </name>\n
\t<title>Software Design Engineer</title>\n
</employee>">
]>
<foo xml:space="preserve">&Jane;</foo>

Assuming that preserveWhiteSpace=false (in the scope of the DOCTYPE tag), the insignificant white space is lost when the entity is parsed. The entity will NOT have white space nodes. The tree will look like this:

DOCTYPE foo
    ENTITY: Jane
        ELEMENT: employee
            ELEMENT: name
                TEXT: Jane
            ELEMENT: title
                TEXT>:Software Design Engineer
    ELEMENT: foo
       ATTRIBUTE: xml:space="preserve"
       ENTITYREF: Jane

Notice that the DOM tree exposed under the ENTITY node inside the DOCTYPE does NOT contain any WHITESPACE nodes. This means that the children of the ENTITYREF node will also have no WHITESPACE nodes even though the entity reference is in the scope of xml:space="preserve".

Every instance of an ENTITY referenced in a given document always has the identical tree.

If an entity absolutely must preserve white space then it must specify its own xml:space attribute inside itself or the document preserveWhiteSpace switch must be set to True.


White Space in Attributes

There are several ways of accessing an attribute value. The IXMLDOMAttribute interface has a nodeValue property, a value property which is equal to nodeValue and a text property which is the Microsoft extension. These properties return the following:

property | text returned
attrNode.nodeValue attrNode.value getAttribute("name") | Returns exact content (with entities expanded) as found in the original document.
attrNode.nodeTypedValue | Null
attrNode.text | Same as nodeValue except the leading and trailing white space is trimmed.

The XML Language specification defines the following behavior for XML Applications:

Attribute type | Text returned
CDATA | ID, IDREF, IDREFS, ENTITY, ENTITIES, NOTATION, enumeration
half normalized | fully normalized

Where half normalized means that newlines and tab characters are converted to spaces, but multiple spaces are not collapsed into one space.


White Space Handling with the XML Object Model

Sometimes the XML Object Model will show TEXT nodes containing white space characters. This can be confusing when most of the time white space is stripped. For example, the following XML:

<?xml version="1.0" ?>
<!DOCTYPE person [
  <!ELEMENT person (#PCDATA|lastname|firstname)>
  <!ELEMENT lastname (#PCDATA)>
  <!ELEMENT firstname (#PCDATA)>
]>
<person>
  <lastname>Smith</lastname>
  <firstname>John</firstname>
</person>

Generates the following tree:

Processing Instruction: xml
DocType: person
ELEMENT: person
TEXT:
ELEMENT: lastname
TEXT:
ELEMENT: firstname
TEXT:

The first name and last name are surrounded by TEXT nodes containing only white space because the content model for the "person" element is MIXED; it contains the #PCDATA keyword. A MIXED content model indicates that the elements can have text interspersed between them. Therefore, the following is also valid:

<person>
My last name is <lastname>Smith</lastname> and my first name is
<firstname>John</firstname>
</person>

And this results in the following, similar-looking tree:

ELEMENT: person
TEXT: My last name is
ELEMENT: lastname
TEXT: and my first name is
ELEMENT: firstname
TEXT:

Without the white space after the word "is" and before <lastname>, and the white space after the </lastname> and before the word "and", that the sentence would be unintelligible. So, for MIXED content models, the combination of text, white space, and elements is relevant. For non-MIXED content models this is not the case.

To make the white-space-only TEXT nodes go away, remove the #PCDATA keyword from the "person" element declaration:

<!ELEMENT person (lastname,firstname)>

which results in the following clean tree:

Processing Instruction: xml
DocType: person
ELEMENT: person
ELEMENT: lastname
ELEMENT: firstname


Transforming and Querying XML

With the advent of XML as a standard way to interchange data on the Web, inevitably the need arises for mechanisms to query XML, shaping extracted data, including sorting and filtering, and transforming one XML grammar into another. XSL Transformations (XSLT) and the XPath language that is part of XSLT provide a measure of this capability today.

XPath provides a simple and concise syntax for identifying nodes in an XML document, based on the node's type, name, content, and context in relation to other nodes in the tree.

XSLT provides a grammar in which the results of XPath queries are associated with templates to describe the materialization of data in the XML source document as a new XML document. While this forms the basis for transforming data to display formats such as HTML, any XML grammar can be output, providing for sorting and filtering within a single XML grammar, or translating data from one schema to another.

Work on a more powerful query language for XML is being considered by the W3C, but no working group has yet been formed.

See Also

XSLT Developer's Guide | XPath


Using XML with HTML

The following topics discuss details of using XML with HTML:

  • Displaying XML-Based Data in HTML
  • Augmenting HTML


Displaying XML-Based Data in HTML

An XML document does not by itself specify whether or how its information should be displayed. The XML data merely contains the facts (such as who ordered which books at which prices). HTML is an ideal display language for presenting data to users. For example, an employee of an online bookstore can visit a Web page to find a list of order entries. On the back end, the individual data records are expressed in XML. However, on the front end, they are presented to the employee as an HTML page. To construct this Web page, either the Web server or the Web browser must convert the XML data records into an HTML presentation, such as a table.

The mechanisms of data binding and style sheets can be used to arrange XML data into a visual presentation and to add interactivity. Data binding is an aspect of Dynamic HTML (DHTML) that moves individual items of data from an information source (such as an XML document) into an HTML display, allowing HTML to be used as a template for displaying XML data. This is similar to a "mail merge" in word processing. Microsoft currently ships an XML Data Source Object (XML DSO) as part of Internet Explorer 5. The XML DSO can be invoked declaratively upon XML data islands.

XSL (Extensible Stylesheet Language) can add even greater power to this process. An XSL style sheet contains instructions for pulling information out of an XML document and transforming it into another format, such as HTML. The transformation of XML into other formats is done in a declarative way, often making it easier and more accessible than through scripting. In addition, XSL uses XML as its syntax, freeing XML authors from having to learn another markup language.

Cascading Style Sheets can still be used for simply structured XML data—and in such situations, they will be useful. However, CSS does not provide a display structure that deviates from the structure of the data source. With XSL, it is possible to generate presentation structures (in HTML, for instance) that are very different from the original XML data structures, as shown here.

XSL provides both semantic and structural independence from content and presentation.

See Also

Augmenting HTML | Choosing Between CSS and XSL


Augmenting HTML

Adding semantic information to HTML pages is not easy. Historically, various programs have attempted to deal with this problem by using nonstandard "tricks," such as hiding data inside HTML comments. However, these comments are awkward and, unlike XML, are not exposed to the object model.

To solve this, the W3C has defined a format for putting XML-based data (data islands) inside HTML pages. Extending HTML through the use of data islands will allow a wide range of applications to use HTML as the primary document or display format and also use XML embedded within these documents to hold data.

An HTML page could therefore include, among other things, specific data about the subject of the page. For instance, if the page displayed an advertisement for an author's most recent novel, the page could also contain XML data concerning that book, such as its ISBN number, publisher, or suggested retail price. It is not important that this information be displayed, but it is important that this information be accessible and understandable as data.

See Also

XML Data Islands


XML DOM User Guide

With the XML Document Object Model (DOM), you can load and parse XML files, gather information about those files, and navigate and manipulate those files. The four main objects exposed by the XML DOM are DOMDocument, XMLDOMNode, XMLDOMNodeList, and XMLDOMNamedNodeMap. Each of these objects exposes methods and properties that enable you to gather information about the instance of the object, manipulate the value and structure of the object, and navigate to other objects within the tree.

The XML DOM exposes the XML document as a tree structure that is composed of nodes; the DOM programming interfaces enable applications to traverse the tree and manipulate its nodes. Each node is defined as a specific node type, according to the DOMNodeType enumeration, which also defines valid parent and child nodes for each node type. For most XML documents, the most common node types are element, attribute, and text. Attributes occupy a special place in the model because they are not considered child nodes of a parent. A distinct programming interface, the XMLDOMNamedNodeMap, is provided for attributes.

The World Wide Web Consortium (W3C) Document Object Model Level 1 Specification defines two groups of DOM programming interfaces: fundamental and extended. The W3C fundamental interfaces include those needed to write applications that manipulate XML documents. The W3C defines extended interfaces as those that might make programming more convenient for developers.

The XML DOM provided with Msxml2 and later versions implements both the fundamental and extended interfaces. It also provides other methods to support XSLT, XPath, namespaces, and data types. For example, the selectNodes method supports XPath syntax to enable sophisticated queries for nodes within a particular context or subtree of the overall tree structure, and the transformNode method supports the use of XSLT to perform transformations.

For script developers, the most important programming object is DOMDocument. The DOMDocument object exposes properties and methods that allow you to navigate, query, and modify the content and structure of an XML document. Other important and commonly used objects include the XMLDOMNode, XMLDOMNodeList, XMLDOMNamedNodeMap, and XMLDOMParseError objects.

For developers using C, C++, or Microsoft® Visual Basic®, these objects are exposed as the COM interfaces IXMLDOMDocument, IXMLDOMNode, IXMLDOMNodeList, IXMLDOMNamedNodeMap, and IXMLDOMParseError.

Reference Documentation

For complete reference material about each object, and the methods and properties exposed by that object, see the reference documentation:

  • XML DOM Objects (ActiveX programming objects, for developers using script languages)
  • XML DOM Interfaces (COM programming interfaces, for C/C++ and Microsoft® Visual Basic® developers)

The following guide is a brief explanation of how to use the XML DOM objects and collections. It discusses the methods and properties exposed by each object, and also covers DOM persistence.

  • Using the DOMDocument Object
  • Using the XMLDOMNode Object
  • Using the XMLDOMNodeList Object
  • Using the XMLDOMNamedNodeMap Object
  • Using Collections
  • XML DOM Persistence


Using the DOMDocument Object

The DOMDocument object exposes properties and methods that allow you to navigate, query, and modify the content and structure of an XML document.

The following topics discuss ways to use DOMDocument:

  • Creating a DOMDocument Object
  • Loading and Saving XML
  • Getting and Setting Parse Flags
  • Gathering Document Information
  • Accessing the Document Tree
  • Creating New Nodes


Creating a DOMDocument Object

Using the XML Document Object Model begins when you create a DOMDocument object. Then you can begin to load, parse, navigate, and manipulate XML files.

To create a new, empty DOMDocument object

Using JScript

var mydoc = new ActiveXObject("Msxml2.DOMDocument");

Using VBScript

Dim xmldoc
Set xmldoc = CreateObject("Msxml2.DOMDocument")

Using Visual Basic

Dim xmldoc = new DOMDocument26


Loading and Saving XML

Use the load method to load an XML file by path name, by URL, or as an IIS Request object.

The following example loads an XML file by URL:

XMLDoc.load("http://xmlfiles/reports.xml")

The document object, XMLDoc, now contains a tree consisting of the parsed contents of reports.xml.

You can also load an XML file as a string using the loadXML method:

XMLDoc.loadXML("<customer><first_name>Joe</first_name>
  <last_name>Smith</last_name></customer>")

The loading and parsing of an XML file occurs asynchronously by default. You can choose to load the XML file synchronously by setting the async property to false. To abort an asynchronous download, call the abort method on the document object.

You can assign a new document to a document object by assigning the document a new root element using the documentElement property:

XMLDoc.documentElement = XMLNode

It is important to remember that assigning the document object a new document element does not change the other children of the document. For instance, the doctype element will not change.

To save a parsed XML document to a file or to another object, use the save method. The save method takes either a file name as a string, an Active Server Pages (ASP) Response object, an DOMDocument object, or any custom object that supports persistence. The following example is part of an ASP file that saves the XML document to a file:

<%
  dim xmldoc
  set xmldoc = Server.CreateObject("Msxml2.XMLDOM")
  xmldoc.async = false
  xmldoc.load(Request)
  xmldoc.save(Server.MapPath("sample.xml"))
%>


Getting and Setting Parse Flags

The DOMDocument object exposes four properties that allow the user to change parsing behavior at run time: async, validateOnParse, resolveExternals, and preserveWhiteSpace. Each of these properties takes and returns a Boolean value. The default value for async, validateOnParse, and resolveExternals is true; the default value for preserveWhiteSpace is false.

The following sample code loads and parses an XML file synchronously; however, the XML document is not validated as it is parsed:

XMLDoc.async = false
XMLDoc.validateOnParse = false
XMLDoc.load("sample.xml")

Note Although not explicitly indicated in this code, by default the parser will resolve externals and will not honor the value of the xml:space attribute.

Asynchronous parsing allows your application to do other things (such as display a progress bar) while the file is being parsed. After loading the XML document but before working with it, be sure to check the readyState property to ensure that the document has been fully loaded and that the DOM is available.


Gathering Document Information

The following properties allow you to retrieve information about the document: doctype, implementation, parseError, readyState, and url. You can access these properties to retrieve the Document Type Declaration (DTD) accompanying the document; the implementation for the document; the last error to occur while parsing; information about the state of the XML document; and the URL, if any, of the XML file being loaded and parsed. For more information about error reporting, see XMLDOMParseError.

The following JScript/HTML example demonstrates the sequence of ready states when asynchronously loading a document:

<script>
var xmldoc;
function Load()
{
    xmldoc = new ActiveXObject("Msxml2.XMLDOM");
    xmldoc.onreadystatechange = CheckState;
    xmldoc.load(URL.value);
}
function CheckState()
{
    var state = xmldoc.readyState;
    RESULTS.innerHTML += "readyState = " + state + "<BR>"
    if (state == 4)
    {
      var err = xmldoc.parseError;
        if (err.errorCode != 0)
          RESULTS.innerHTML += err.reason + "<BR>"
        else
          RESULTS.innerHTML +="success" + "<BR>"
    }
}
</script>
URL: <input type=text size=60 id=URL>
<input type=button value=LOAD onclick="jscript:Load()">
<div id=RESULTS style="color:red; font-weight:bold;"></div>


Accessing the Document Tree

You can access the tree for an XML document either by beginning at the root element and walking down the tree or by querying for a specific node or nodes. You can navigate to the root element of the document by using the documentElement property. This property returns the root element as an XMLDOMNode object.

Consider the following XML file (Books.xml):

<COLLECTION>
  <BOOK>
    <TITLE>Cosmos</TITLE>
    <AUTHOR>Carl Sagan</AUTHOR>
    <PUBLISHER>Ballantine Books</PUBLISHER>
 </BOOK>
 <BOOK>
    <TITLE>Catwings</TITLE>
    <AUTHOR>Ursula K. Le Guin</AUTHOR>
    <PUBLISHER>Scholastic</PUBLISHER>
 </BOOK>
 <BOOK>
    <TITLE>Home Town</TITLE>
    <AUTHOR>Tracy Kidder</AUTHOR>
    <PUBLISHER>Random House</PUBLISHER>
 </BOOK>
</COLLECTION>

You can walk the tree this way using VBScript:

Dim root
Dim xmlDoc
Dim child

Set xmlDoc = CreateObject("Msxml2.XMLDOM")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
'Set root to the XML document's root element, COLLECTION:
Set root = xmlDoc.documentElement
'Walk from the root to each of its child nodes:
For Each child In root.childNodes
  MsgBox child.text
Next

When this example runs, it displays the text of each of the root's child nodes (BOOK).

You can navigate to a specific node or nodes deep within the tree using either the nodeFromID method or getElementsByTagName method. The nodeFromID method takes a unique ID, defined within an XML Schema or DTD, and returns the node corresponding to that specific ID (see the Extreme XML column, "Cross-Reference Your XML Data for more information on how to use IDs and IDREFs"). The getElementsByTagName method takes a string representing a specific tag name and returns all element nodes with this tag name.

Using the file Books.xml, you can retrieve all nodes containing the AUTHOR tag with the following code:

Dim ElemList
Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.XMLDOM")
xmlDoc.async = False
xmlDoc.load("c:\xml\books.xml")
Set ElemList = xmlDoc.getElementsByTagName("AUTHOR")
  For i=0 To (ElemList.length -1)
    MsgBox ElemList.item(i).xml
  Next

The XMLDOMNode object's selectNodes method offers a more flexible way of accessing nodes.


Creating New Nodes

The DOMDocument object exposes the createNode method, which allows you to create a qualified node by supplying the node type, name, and namespaceURI of the new node.

The DOMDocument object also exposes the following eight methods, which allow you to create specific DOM nodes. (These methods are defined in the W3C DOM recommendation.)

  • createAttribute method
  • createCDATASection method
  • createComment method
  • createDocumentFragment method
  • createElement method
  • createEntityReference method
  • createProcessingInstruction method
  • createTextNode method

The methods that create specific DOM nodes return specific objects and interfaces, which are outlined in the XML DOM Reference. These specific objects and interfaces expose properties and methods associated with that node. For example, the following sample code assigns an XMLDOMElement object with the name of "item" to the variable "xmlElem":

xmlElem = xmlDoc.createElement("item")

You can then add an attribute to that element by calling the setAttribute method and passing that method a string containing the attribute name and a string containing the attribute value:

xmlElem.setAttribute("id","bar")

Using Books.xml again as an example (see Accessing the Document Tree), the following code creates a new element, PAGES, and uses the appendChild method to add it to the second child node of the root, COLLECTION. It then sets the text property for the new element to "400":

Dim xmlDoc
Dim root
Dim newElem
Set xmlDoc = CreateObject("Msxml2.XMLDOM")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
Set newElem = xmlDoc.createElement("PAGES")
root.childNodes.item(1).appendChild newElem
root.childNodes.item(1).lastChild.text = "400"
MsgBox root.childNodes.item(1).xml


Using the XMLDOMNode Object

The XMLDOMNode object is the principal object within the DOM. The DOMDocument object is itself an XMLDOMNode object, as are the members of node lists and named node maps. The following topics discuss how to use the XMLDOMNode object:

  • Gathering Information About a Node
  • Getting and Setting Data Within a Node
  • Navigating the Tree
  • Manipulating the Children of a Node
  • Transforming a Node


Gathering Information About a Node

The following properties allow you to get information about the node:

  • hasChildNodes
  • namespaceURI
  • nodeName
  • nodeType
  • nodeTypeString
  • parsed
  • specified
  • xml

You can access these properties to get information concerning the type of the node, the name of the node, whether the node has any children, whether the node is explicitly specified in the XML file or implied within the Document Type Definition (DTD) or XML Schema, the namespace to which the node belongs, whether the node has been parsed, and the string representation of the node.

The following sample code tests to see if the node is of type "element." If the node is of type "element," the string representation of the node is loaded into a document object through the loadXML method.

if (xmlNode.nodeTypeString == "element")
  XMLDoc.loadXML(xmlNode.xml)


Getting and Setting Data Within a Node

The data marked up within the XML file is exposed in the DOM as node values. These values might be the value of an attribute, for instance, or the text within an XML element. There are three ways to access the value of a node. The nodeValue property gives you access to values of certain nodes. The nodes that contain a value that can be accessed through the nodeValue property are attributes, text nodes, comments, processing instructions, and CDATA sections. The following code sets the value of an attribute to "hello world":

newAttNode = XMLDoc.createAttribute("newAtt")
newAttNode.nodeValue = "hello world"

There are three ways to access text within an element node:

  • Navigate down to the element's children (the text nodes within) and call nodeValue on them.
  • Call the text property. This property returns the text within the element. The text within an element can also be changed using the text property. The following sample code tests to see if the text within the element elem1 is equal to "hello world." If it is, it assigns that element the text "hi! world."
    if (elem1.text == "hello world")
  elem1.text = "hi! world"
  • Access the text through the nodeTypedValue property. This property is part of the data type support built into the Internet Explorer 5 XML parser and returns the typed value of the node. This property is strictly for typed elements. In conjunction with this property is the dataType property, which returns the data type of the element. The nodeTypedValue and datatype properties can both be set. However, the parser will return an error if the new nodeTypedValue is of the wrong data type or if the new data type does not match the nodeTypedValue. For more information about data type support, see the XML Data Types Reference.


Navigating the Tree

From the XMLDOMNode object, you can navigate directly to its parent node (parentNode), children (childNodes, firstChild, lastChild), siblings (previousSibling, nextSibling), or the document object to which the node belongs (ownerDocument). If the node is of type element, attribute, or entityReference, you can call the definition property to navigate to the schema definition of the node. In addition, if the node is of type element, processingInstruction, documentType, entity, or notation, you can navigate to the attributes on the node using the attributes property. The following code tests to see if the elem1 element has any element children. If so, it navigates from elem1 to that element's first child. The text of that first child is then changed to "I'm the first child of elem1."

if (elem1.hasChildNodes == true && elem1.firstChild.nodeTypeString == "element")
  elem1.firstChild.text = "I'm the first child of elem1"

It is also possible to navigate to other nodes in the tree using selectNodes and selectSingleNode. These methods take an XPath expression as an argument and return the node or nodes that match that query. For more information about XPath, see XPath Syntax.


Manipulating the Children of a Node

The XMLDOMNode object exposes four methods that allow you to manipulate the children of a node: appendChild, replaceChild, removeChild, and insertBefore. Each of these methods takes an XML node object as an argument. The following code takes a new element that is created using createElement and adds that as the last child of elem1:

newChild = XMLDoc.createElement("last_child")
elem1.appendChild(newChild)

The appendChild method always adds the node to the end of the list of children. To insert the node elsewhere in the child node list, use the insertBefore method. This method takes two parameters: the node to be inserted and the node before which the new child is to be inserted.

The following VBScript example loads Books.xml (see Accessing the Document Tree) and creates a new element, PAGES. Then, using a reference to the first child of a node as the reference point (refElem), it inserts the new element with the insertBefore method:

Dim xmlDoc
Dim root
Dim newElem
Dim refElem
Set xmlDoc = CreateObject("Msxml2.XMLDOM")
xmlDoc.async = False
xmlDoc.load("c:\xmlinact\books.xml")
Set root = xmlDoc.documentElement
Set newElem = xmlDoc.createElement("PAGES")
Set refElem = root.childNodes.item(1).firstChild
root.childNodes.item(1).insertBefore newElem, refElem
MsgBox root.childNodes.item(1).xml

The next example uses the removeChild method to remove a child node:

Dim xmlDoc
Dim root
Dim oldChild
Set xmlDoc = CreateObject("Msxml2.XMLDOM")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
Set oldChild = root.childNodes.item(1).firstChild
root.childNodes.item(1).removeChild oldChild
MsgBox root.childNodes.item(1).xml


Transforming a Node

The transformNode and transformNodeToObject methods can be used to transform an XML node into a string or object using XSLT. For more information about transforming nodes using XSLT, see the XSLT Developer's Guide.


Using the XMLDOMNodeList Object

The XMLDOMNodeList object is returned by the childNodes collection and the selectNodes and getElementsByTagName methods.

The following topics discuss how to use the XMLDOMNodeList object:

  • Gathering Information About the Node List
  • Navigating the Node List


Gathering Information About the Node List

You can get the length of the node list from the length property. Among other things, the length can be used for iterating through the list of children. For example, the following code loops through the children of elem1, searching for a child element with the text value of "hello world." If such a child exists, the index of that child is assigned to the variable "helloWorldIndex."

for (i = 0;i < elem1.childNodes.length;i++){
  if (elem1.childNodes.item(i).text == "hello world")
    helloWorldIndex = i;
  }


Navigating the Node List

You can get at a specific member of the node list by using the item method. The item method takes a number corresponding to a node's position within the node list. To get the first node in the node list, for instance, one would call the following method: item(0). It is also possible to navigate through the node list using the nextNode method. The nextNode method returns the next node in the node list.

The following code performs the same task as the previous code:

currentNode = elem1.childNodes.nextNode;
while (currentNode != null) {
  if (currentNode.text == "hello world")
    helloWorldNode = currentNode;
  currentNode = elem1.childNodes.nextNode;
  }

Notice that the currentNode is set using the node returned by elem1.childNodes.nextNode. The initial position of the node list is defined to be before the first node. Therefore, the first time nextNode is called on the node list, the first node is returned. The code tests for currentNode != null. If the current node is the last node in the list or the node list has no members, nextNode returns null.


Using the XMLDOMNamedNodeMap Object

An XMLDOMNamedNodeMap object is returned by the attributes property. The XMLDOMNamedNodeMap object differs from the node list because it is a collection of nodes that can also be accessed by name. The following topics discuss how to use the XMLDOMNamedNodeMap object:

  • Gathering Information About the Named Node Map
  • Navigating the Named Node Map
  • Manipulating a Named Node Map


Gathering Information About the Named Node Map

Just like the XMLDOMNodeList object, the XMLDOMNamedNodeMap object exposes the length property. This property returns the number of members in the named node map.


Navigating the Named Node Map

As with a node list, you can access members of the named node map by index using the item method. However, you can also access the members of a named node map name by using getNamedItem and getQualifiedItem. The getNamedItem method takes the name of the desired node as a parameter; the getQualifiedItem method takes the name and namespaceURI of the desired node. Each method returns an XMLDOMNode object. The following code retrieves the value of the ID attribute on the elem1 element and assigns that value to the variable "idValue":

idValue = elem1.attributes.getNamedItem("ID").nodeValue

The XMLDOMNamedNodeMap object, like the node list object, exposes the nextNode property.


Manipulating a Named Node Map

You can use one of three methods to manipulate named node maps: setNamedItem, removeNamedItem, or removeQualifiedItem. The setNamedItem method takes an XML node object as a parameter, adding that node to the named node map. If an attribute already exists with the same name as that specified by the nodeName of the node added, the old attribute is replaced. The following code creates a new attribute node with the name "ID" and adds it to the attributes of elem1:

idAtt = XMLDoc.createAttribute("ID")
elem1.setNamedItem(idAtt)

The removeNamedItem method takes a node name as a parameter, removing the node with that name. The removeQualifiedItem method takes a node name and namespaceURI as its parameters, removing the corresponding attribute.


Using Collections

In the XML DOM, collections are used to store lists of nodes or elements. The XMLDOMNodeList object is a collection; it is returned by childNodes, selectNodes, and getElementsByTagName. Unlike an array, a collection is dynamic, meaning that the addition or removal of nodes, and changes within nodes, are immediately reflected in the collection.

Accessing Collection Members

Use the item method to access individual nodes within a collection. For example, the following code returns the first member of the collection:

objNodeList.item(0)

As a shortcut, you can access a node by simply appending the appropriate index number in parentheses to a collection reference, like so:

objNodeList(0)

You can also use square brackets to enclose the index number (except in VBScript):

objNodeList[0]

Iterating a Collection

In Visual Basic, you can iterate through an XML DOM collection with a For Each…Next statement, like so:

Dim Item
For Each Item in objNodeList
   MsgBox Item.text
Next

Or you can use a For…Next statement:

Dim i
Dim members
members = objNodeList.length
For i = 0 To (members-1)
   MsgBox objNodeList(i).xml
Next

The length property returns the number of members in the collection.

Optimize your script by setting the node list to a variable and using that variable in a For loop. This way, the script engine does not have to examine the collection during each iteration.

You can iterate collections in JScript this way:

var members = objNodeList.length;
for (var i = 0; i < members; i++)
{
   alert (objNodeList(i).text);
}


XML DOM Persistence

For scripting purposes, persistence is handled by the following methods and property:

  • load method
  • loadXML method
  • save method
  • xml property

For Visual Basic and C/C++ programmers, DOM persistence is handled by the following methods and interfaces:

  • IXMLDOMDocument::load method
  • IXMLDOMDocument::loadXML method
  • IXMLDOMDocument::save method
  • IXMLDOMNode::xml method
  • IPersistStreamInit interface (see IXMLDOMDocument)
  • IPersistMoniker interface (see IXMLDOMDocument)
  • IStream interface (see IXMLDOMDocument)


Introduction to Namespaces

The Internet Explorer 5 XML parser supports the World Wide Web Consortium (W3C) XML Namespaces Recommendation specification. By using XML namespaces, developers can qualify element names uniquely on the Web and thus avoid conflicts between elements that have the same name. Associating a Universal Resource Identifier (URI) with a namespace is purely to ensure that two elements with the same name can remain unambiguous; it does not matter what, if anything, the URI points to.

Namespace support is integrated into Browsing XML Documents in Internet Explorer 5, XSL Transformations (XSLT), and the XML DOM programming interfaces. For descriptions of the specific methods that are supported, see the reference topics for the DOMDocument and XMLDOMNode objects.

The following topics provide more information about namespaces and describe the namespace support features in Internet Explorer 5:

  • Reasons for Namespaces
  • Declaring Namespaces
  • Object Model Support
  • Namespace Scope
  • Parsing Support


Reasons for Namespaces

The appeal of XML lies in the ability to invent tags that convey meaningful information. For example, XML allows you to represent information about a book like so:

<BOOK>
  <TITLE>A Suitable Boy</TITLE>
  <PRICE currency="US Dollar">22.95</PRICE>
</BOOK>

Similarly, you can represent information about an author as:

<AUTHOR>
  <TITLE>Mr</TITLE>
  <NAME>Vikram Seth</NAME>
</AUTHOR>

While the human reader can distinguish between the different interpretations of the TITLE element, a computer program does not have the context to tell them apart. Without additional information it cannot tell that the first TITLE element is intended to refer to a string representing the title of the book, and that the second element refers to an enumeration representing the title of the author: "Mr.," "Ms.," "Mrs.," and so on.

Namespaces solve this problem by associating a vocabulary (or namespace) with a tag name. For example, the titles can be written as follows:

<BookInfo:TITLE>A Suitable Boy</BookInfo:TITLE>
<AuthorInfo:TITLE>Mr.</AuthorInfo:TITLE>

The name preceding the colon, the prefix, refers to a namespace, or a Universal Resource Identifier (URI). The URI ensures global uniqueness when merging XML sources, while the associated prefix—a short name that substitutes for the namespace—must be unique only in the tightly scoped context of the document. With this scheme, no conflicts exist between tags and attributes, and two tags can be the same only if they are from the same namespace and have the same tag name. This allows a document to contain both book and author information without confusion about whether the TITLE element refers to the book or the author. If a computer program wanted to display the name of a book in a user interface, it would use the object model to look for the TITLE element of the "BookInfo" namespace.

For more information about namespaces, see the "Namespaces in XML" recommendation on the W3C Web site.


Declaring Namespaces

The Microsoft XML Parser (Msxml) provides support for associating elements and attributes with namespaces. There are two ways to declare namespaces: default declaration and explicit declaration.

Default Declaration

The default declaration declares a namespace for all elements within scope. For example:

<BOOK xmlns="urn:BookLovers.org:BookInfo">
  <TITLE></TITLE>
</BOOK>

The following example declares the BOOK element and all elements and attributes within it. (TITLE, PRICE, and currency are from the namespace "urn:BookLovers.org:BookInfo".) The attribute xmlns is an XML keyword and is understood by the parser to be a namespace declaration:

<BOOK xmlns="urn:BookLovers.org:BookInfo">
  <TITLE>A Suitable Boy</TITLE>
  <PRICE currency="US Dollar">22.95</PRICE>
</BOOK>

Default declarations are useful when a node (element and child elements) is from the same namespace.

Explicit Declaration

An explicit declaration defines a shorthand, or prefix, to substitute for the full name of a namespace. Use an explicit declaration to reference a node from a namespace separate from your default namespace.

The following example declares "bk" and "money" to be shorthand for the full names of their respective namespaces. All elements beginning with "bk:" or "money:" are considered to be from the namespace "urn:BookLovers.org:BookInfo" or "urn:Finance:Money," respectively.

<bk:BOOK xmlns:bk="urn:BookLovers.org:BookInfo"
         xmlns:money="urn:Finance:Money">
  <bk:TITLE>A Suitable Boy</bk:TITLE>
  <money:PRICE money:currency="US Dollar">22.95</money:PRICE>
</bk:BOOK>

Explicit declarations are useful when a node contains elements from different namespaces.


Object Model Support

The XML object model in Internet Explorer 5 provides support for dealing with namespaces. The following properties allow you to extract the namespace, prefix, and base name for a given node.

Property | Description
nodeName | Returns the qualified name for the node—that is, the tag name including the namespace prefix, if present.
namespaceURI | Returns the namespace for the element. If no namespace is specified on the node, "" is returned.
prefix | Returns the prefix specified on the element or attribute. If no prefix is specified, "" is returned.
baseName | Returns the name of the tag—that is, the text to the right of the colon.

The following examples show some XML data followed by a table showing each element and attribute, as well as their properties in the XML object model:

<BOOKS>
  <BOOK xmlns="urn:BookLovers.org:BookInfo">
    <TITLE>A Suitable Boy</TITLE>
    <PRICE currency="US Dollar">22.95</PRICE>
  </BOOK>
</BOOKS>
Node type | nodeName | namespaceURI | prefix | baseName
Element | BOOKS |  |  | BOOKS
Element | BOOK | urn:BookLovers.org:BookInfo |  | BOOK
Attribute | xmlns |  | xmlns |
Element | TITLE | urn:BookLovers.org:BookInfo |  | TITLE
Element | PRICE | urn:BookLovers.org:BookInfo |  | PRICE
Attribute | currency |  |  | currency
<BOOKS>
  <bk:BOOK xmlns:bk="urn:BookLovers.org:BookInfo" xmlns:money="urn:Finance:Money">
  <bk:TITLE>A Suitable Boy</bk:TITLE>
  <money:PRICE money:currency="US Dollar">22.95</money:PRICE>
  </bk:BOOK>
</BOOKS>
Node type | nodeName | namespaceURI | prefix | baseName
Element | BOOKS |  |  | BOOKS
Element | bk:BOOK | urn:BookLovers.org:BookInfo | bk | BOOK
Attribute | xmlns:bk |  | xmlns | bk
Attribute | xmlns:money |  | xmlns | money
Element | bk:TITLE | urn:BookLovers.org:BookInfo | bk | TITLE
Element | money:PRICE | urn:Finance:Money | bk | PRICE
Attribute | money:currency | urn:Finance:Money | money | currency

The object model also extends support for creating and removing elements and attributes by taking namespaces into account.


Namespace Scope

Namespace declarations are scoped using an element container. In the default declaration example, all unqualified elements (without an explicit namespace on the element) within the BOOK element belong to the "urn:BookLovers.org:BookInfo" namespace. In the following example, notice that all unqualified elements within the table element are scoped to the default namespace "http://www.w3.org/TR/REC-html40". Within the <details> tag, the default namespace is set to "urn:beerstuff.org:brandinfo". All unqualified tags within the details element are scoped to this namespace.

<Beers>
  <!-- the default namespace is now that of HTML -->
  <table xmlns="http://www.w3.org/TR/REC-html40">
    <tr><td>Name</td><td>Origin</td><td>Description</td></tr>
    <tr>
      <!-- drop the HTML namespace inside table cells -->
      <td><brandName xmlns="urn:beerstuff.org:brandinfo">Huntsman</brandName></td>
      <td><origin xmlns="urn:beerstuff.org:brandinfo">Bath, UK</origin></td>
      <td>
        <details xmlns="urn:beerstuff.org:brandinfo">
          <class>Bitter</class>
          <hop>Fuggles</hop>
          <pro>Wonderful hop, light alcohol, good summer beer</pro>
          <con>Fragile; excessive variance pub to pub</con>
        </details>
      </td>
    </tr>
  </table>
</Beers>

The following table shows the elements and attributes of the above XML example and their properties within the XML object model.

Node type | nodeName | namespaceURI | prefix | baseName
Element | Beers |  |  | Beers
Element | table | http://www.w3.org/TR/REC-html40 |  | table
Attribute | xmlns |  | xmlns |
Element | tr | http://www.w3.org/TR/REC-html40 |  | tr
Element | td | http://www.w3.org/TR/REC-html40 |  | td
Element | td | http://www.w3.org/TR/REC-html40 |  | td
Element | td | http://www.w3.org/TR/REC-html40 |  | td
Element | tr | http://www.w3.org/TR/REC-html40 |  | tr
Element | td | http://www.w3.org/TR/REC-html40 |  | td
Element | brandName | urn:beerstuff.org:brandinfo |  | brandName
Attribute | xmlns |  |  | xmlns
Element | td | http://www.w3.org/TR/REC-html40 |  | td
Element | origin | urn:beerstuff.org:brandinfo |  | origin
Attribute | xmlns |  | xmlns |
Element | td | http://www.w3.org/TR/REC-html40 |  | td
Element | details | urn:beerstuff.org:brandinfo |  | details
Attribute | xmlns |  | xmlns |
Element | class | urn:beerstuff.org:brandinfo |  | class
Element | hop | urn:beerstuff.org:brandinfo |  | hop
Element | pro | urn:beerstuff.org:brandinfo |  | pro
Element | con | urn:beerstuff.org:brandinfo |  | con

The namespace shorthands are valid within the element in which they are defined. In the explicit declaration example, the shorthands "bk" and "money" are valid only within the bk:BOOK element.


Parsing Support

The namespace is usually just a string. It provides the mechanism to tell two namespaces apart. Strings such as "urn:www.microsoft.com," "http://www.microsoft.com," and "uuid:1234567890" are all valid names for a namespace.

The Internet Explorer 5 XML parser is a validating parser. When there is an XML schema that can be used to validate the structure and content of an element, the XML parser is asked to use the schema by using the following syntax:

<BOOK xmlns="x-schema:http://www.microsoft.com/BookInfo.xml">
   <TITLE>A Suitable Boy</TITLE>
   <PRICE currency="US Dollar">22.95</PRICE>
</BOOK>

The "x-schema" instructs the XML parser to treat the reference as a valid reference to an XML Schema; it fetches the schema from the URL and validates against it.

For more information about XML schemas, see the XML Schema Developer's Guide.


Using XML on the Server

The XML engine included in the Microsoft® Internet Explorer 5 release can also be used on the server. This allows data-rich Web applications in which XML data can be created, manipulated, and transmitted between client and server.

The following topics provide an introduction to the use of the Internet Explorer 5 XML engine on the server:

  • Instantiating the XML Control
  • Choosing a Threading Model
  • Getting the XML Data
  • Sending XML Between the Client and Server
  • Saving XML
  • Converting XML to HTML


Instantiating the XML Control

Installing Microsoft® Internet Explorer 5 on the server also installs the MSXML.DLL, which contains all the components of the XML engine, including the Microsoft XML parser, the XSL processor, schema support, and so on.

Instantiate the XML control the way you would any other control on the server. For example, from an ASP page you would use code such as:

var xObj = Server.CreateObject("Microsoft.XMLDOM");

Alternatively, a server-side object could be used:

<OBJECT RUNAT="server" PROGID="Microsoft.XMLDOM" id="xObj"> </OBJECT>

The "xObj" is an empty DOMDocument object, ready for you to build in memory or load from an XML file or an XML stream.


Choosing a Threading Model

There are two versions of the XML control: a version with a rental model and a version with a free-threaded model. The rental model version is designed for single-threaded access. The free-threaded version is designed for multiple thread access.

The two versions are distinguished by their progIDs. To use the rental model control, use the "Microsoft.XMLDOM" progID. To use the free-threaded control, use the "Microsoft.FreeThreadedXMLDOM" progID.

If you plan for several threads to access your XML data from a single control, be sure to use the free-threaded control. If only one thread will access the XML data, use the rental model control since it will perform better.

The following is a sample global.asa file that creates session-level and application-level free-threaded versions of the XML control.

<SCRIPT LANGUAGE=VBScript RUNAT=Server>
  Sub Session_OnStart
    ON error RESUME next

    SET Application("AppFXMLdoc") = _
      server.CreateObject("Microsoft.FreeThreadedXMLDOM")
    SET Session("SessFXMLdoc") = _
      server.CreateObject("Microsoft.FreeThreadedXMLDOM")
  End Sub
  Sub Session_OnEnd
    ON error RESUME next

    SET Session("SessFXMLdoc") = nothing
    SET Application("AppFXMLdoc") = nothing
    Session("SessFXMLdoc") = empty
    Application("AppFXMLdoc") = empty
  End Sub
</SCRIPT>

Scripts accessing the Session and Application objects will be able to simultaneously access the "AppFXMLdoc" and "SessFXMLdoc" objects.


Getting the XML Data

There are many different ways in which you can get XML data on the server, including:

  • Using ADO 2.1 or later to get XML information directly from any database that supports ADO.
  • Reading data from a database or other source and constructing an XML tree using the DOM.
  • Loading an XML file using the XML control's load method.
  • Constructing a string and loading it into the XML control using the loadXML method.
  • Constructing an XML text string directly, without building an XML document object on the server.

Several of these methods are illustrated in the following sections.

Constructing an XML Tree Using the DOM

The XML Document Object Model (DOM) provides the ability to programmatically construct an XML document from data read from a database or other data source.

The following code reads data from a database and constructs an XML document using the DOM. The document is then sent to the client as an XML text stream.

<% Response.ContentType = "text/xml" %>
<?xml version="1.0" encoding="windows-1252" ?>
<%
' Check to see if a document has data. If it does, don't build it
if (xmldoc.childNodes.length = 0) then
  ' Build the XML document
  set root = xmldoc.createNode("element","SalesData","")
  xmldoc.appendChild(root)
  ' Queries the database for customer data
  SQL = "SELECT OrderID, CompanyName, OrderDate, ShippedDate, " & _
        "       ShipCity, ShipState " & _
        " FROM Customers " & _
        "INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID "
  set Conn = Server.CreateObject("ADODB.Connection")
  Conn.Open("pdc")
  set rs = Conn.Execute(SQL)
  ocount = 0
  rs.MoveFirst()
  while (not rs.EOF and ocount < 10)
    set onode = xmldoc.createNode("element","Order","")
    xmldoc.documentElement.appendChild(onode)
    for i = 0 to rs.Fields.Count - 1
      set child = xmldoc.createNode("element",rs.Fields(i).Name,"")
      child.text = rs.Fields(i)
      onode.appendChild(child)
    next
    pcount = 0
    set isnode = xmldoc.createNode("element","Items","")
    onode.appendChild(isnode)
    iSQL = "select ProductName, OrderDetails.UnitPrice as UnitPrice," & _
           "       Quantity from OrderDetails" & _
           " inner join Products on Products.ProductID = OrderDetails.ProductID" & _
           " where OrderID = "
    iSQL = iSQL & rs("OrderID")
    set irs = Conn.Execute(iSQL)
    irs.MoveFirst()
    while (not irs.EOF and pcount < 3)
      set inode = xmldoc.createNode("element","Item","")
      isnode.appendChild(inode)
      for i = 0 to irs.Fields.Count - 1
        set child = xmldoc.createNode("element",irs.Fields(i).Name,"")
        child.text = irs.Fields(i)
        inode.appendChild(child)
      next
      irs.MoveNext()
      pcount = pcount + 1
    wend
    rs.MoveNext()
    ocount = ocount + 1
  wend
end if
Response.Write(xmldoc.xml)
%>

The DOM methods createNode and appendChild and the text property are used to construct the XML tree. The xml property writes the document out as an XML stream, which is then sent to the client.

Loading XML from a File

The DOM load method can be used to build a DOM document from a URL.

xObj = Server.CreateObject("Microsoft.XMLDOM");
xObj.load(Server.MapPath("simple.xml"));

The MapPath method resolves the relative path to a full path.

Directly Constructing XML Text

If you do not need to process or cache the XML on the server, you can choose to simply construct the XML text without using the XML control. This does not require the XML control to be installed on the server.

The following example reads data from a database and injects it into the output stream.

<% Response.ContentType = "text/xml" %>
<?xml version="1.0" encoding="windows-1252" ?>
<SalesData>
<%
SQL = "SELECT OrderID, CompanyName, OrderDate, ShippedDate, " & _
      "       ShipCity, ShipState " & _
      " FROM Customers " & _
      "INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID " & _
      "ORDER BY ShipState"
set Conn = Server.CreateObject("ADODB.Connection")
set rs = Server.CreateObject("ADODB.Recordset")
Conn.Open("pdc")
set rs = Conn.Execute(SQL)
rs.MoveFirst()
while (not rs.EOF)
%>
  <Order>
    <OrderID> <%= rs("OrderID") %> </OrderID>
    <Customer> <%= rs("CompanyName") %> </Customer>
    <OrderDate> <%= rs("OrderDate") %> </OrderDate>
    <ShippedDate> <%= rs("ShippedDate") %> </ShippedDate>
    <ShipCity> <%= rs("ShipCity") %> </ShipCity>
    <ShipState> <%= rs("ShipState") %> </ShipState>
  </Order>
<%
    rs.MoveNext()
wend
rs.close()
conn.close()
%>
</SalesData>

Because the XML tags are generated directly as text, the overhead of building the DOM document object is avoided.


Sending XML Between the Client and Server

When sending XML to the client, you first need to create a textual version of the XML.

To create the textual version, you can use the xml property of the root node of the XML document, as shown in the following fragment:

Response.Write(xObj.xml);

You can also use the technique described in Getting the XML Data.

Once you have the XML text string, there are several approaches for sending it to the client in response to a client request:

  • Maintain the XML as a file on the server. The client can load this file directly using the load method, such as:
    xObj.load("http://www.microsoft.com/sample.xml");
  • Create and return the XML from an ASP page. The client can load the result using the load method, such as:
    xObj.load("http://www.microsoft.com/sample.asp");
  • Create and return XML information in response to a request from the XMLHTTP control.

Getting XML Back from the Client

You can send XML to the server using any normal technique for sending information to the server, including:

  • Posting information from a form.
  • Posting information from a form in a hidden frame.
  • Using the XMLHTTP control to send and receive XML information using HTTP requests.

See the DHTML documentation for details on posting data.


Saving XML

You can save XML on the server using the save method, as shown in the following excerpt:

<%
  Response.Expires = -1000;
  if (Request.ServerVariables("REQUEST_METHOD") == "POST")
  {
    // Load the posted XML data and save it to disk.
    xmldoc.load(Request);
    xmldoc.save(Server.MapPath("saved.xml"));
%>
Ok
<%
  }
%>

The MapPath method resolves relative paths into the full paths required on the server.


Converting XML to HTML

You might want to maintain XML information on the server and format it as HTML to send to the client. You can do so using any server-side techniques, such as from script in an ASP page. In addition, you can use XSL to format the information. See Using XSL in an Active Server Page.

If the client is Microsoft® Internet Explorer 5 or later, you can send an XML file with an embedded XSL reference, and the browser will automatically format the sent XML using the referenced style sheet. See XML and Style Sheets.

See Also

Serving Up XML


XML Security

This topic describes how you can use the security zones and XML parser of Microsoft® Internet Explorer 5 together.

Accessing Data Across Domains

The security scheme for the Internet Explorer 5 XML parser is based on the Internet Explorer security zones and the settings within each zone. The relevant setting in terms of the parser is the "Access data sources across domains" setting. This setting allows the user three options: Disable, Enable, or Prompt. The user can choose a different setting for each of the four security zones: Internet, Local intranet, Trusted sites, and Restricted sites. The default settings for the "Access data sources across domains" setting are Internet/Disable, Local intranet/Prompt, Trusted sites/Enable, and Restricted sites/Disable. This means that if the user loads a page in the Intranet zone that uses the XML parser to access data from another site (but still in the Local intranet zone), the user will be prompted and informed that data is being accessed across domains.

Accessing Data Across Protocols

In addition to the zone checking described above, there are also certain restrictions on cross-protocol access of data. The only cross-protocol access that is disallowed is http to http access and http to https access. In all cases, access will be denied if either of these two restrictions occurs.

Accessing Data Across Zones

Cross-zone access of data is allowed only in cases where more trusted zones are accessing data from less trusted zones. This allows intranet applications to access data on the intranet and on the Internet. The following table describes which zones have access to which other zones when the "Access data sources across domains" setting is set to Enable or Prompt (although Local isn't an official zone, the parser treats files on the local computer as if they are in their own zone).

 | Destination Zone
Originating Zone | Local | Internet | Local Intranet | Trusted Sites | Restricted Sites
Local | Access Granted | Access Granted | Access Granted | Access Granted | Access Denied
Internet | Access Denied | Access Granted | Access Denied | Access Denied | Access Denied
Local Intranet | Access Denied | Access Granted | Access Granted | Access Denied | Access Denied
Trusted Sites | Access Denied | Access Granted | Access Granted | Access Granted | Access Denied
Restricted Sites | Access Denied | Access Denied | Access Denied | Access Denied | Access Denied


XML Data Islands

There is an increasing need to be able to embed "islands" of data inside HTML pages. In Microsoft® Internet Explorer 5, these "data islands" can be written in Extensible Markup Language (XML).

The following topics describe the syntax used for embedding these data islands within a page, and detail the object model exposed by the browser to enable them to be used. This method of embedding XML in HTML follows the note published by the W3C as the "XML in HTML Meeting Report." The W3C expects to evolve the HTML specification to include the capability of embedding XML in HTML documents.

Embedding an XML Data Island into an HTML Page

An XML data island can be embedded using one of two methods:

  • Using the DHTML XML element within the HTML document
  • Overloading the HTML SCRIPT element

Using the XML Element Within the HTML Document

This syntax is valid for Internet Explorer 5.

There are two syntactically correct ways of using the XML element within the HTML document:

  • The XML data can exist inline, surrounded by XML open and close tags.
    <XML ID="XMLID">
  <XMLDATA>
    <DATA>TEXT</DATA>
  </XMLDATA>
</XML>
  • The XML element can have a SRC attribute, the value of which is the URL for an XML data source.
    <XML SRC="http://localhost/xmlFile.xml"></XML>

The XML element is present in the HTML Document Object Model. It is in the DHTML all collection and is seen by the browser as just a regular node. The XML data within the XML element can then be accessed by calling the DHTML XMLDocument property on the XML element.

The XMLDocument property returns the root node of the XML within the XML element or the root node of the XML referenced by the value of the SRC attribute. From this root, the XML data island can be navigated using the XML document object model. The following function returns the data from the data island with the ID of "XMLID".

function returnXMLData(){
  return document.all("XMLID").XMLDocument.nodeValue;
  }

The XML element can also be referenced by ID alone. For example, the following function has the identical functionality as the previous example.

function returnXMLData(){
  return XMLID.documentElement.text;
  }

Note that because the XMLDocument property was not used, the documentElement property will need to be called to retrieve the root element of the XML.

Overloading the HTML SCRIPT Element

This syntax has been deprecated and is intended only for down-level cases.

There are three syntactically correct ways of overloading the DHTML SCRIPT element:

  • The LANGUAGE attribute can be given the value "XML".
    <SCRIPT LANGUAGE="XML">
  • The TYPE attribute can be given the value "text/xml".
    <SCRIPT TYPE="text/xml">
  • As with the XML element, a SRC attribute can be added, the value of which is the URL for an XML data source.
    <SCRIPT LANGUAGE="XML" SRC="http://localhost/xmlFile.xml"></SCRIPT>

The following HTML fragment illustrates how to embed data by overloading the SCRIPT element.

<SCRIPT ID="XMLID" LANGUAGE="XML">
  <XMLDATA>
    <DATA>TEXT</DATA>
  </XMLDATA>
</SCRIPT>

The SCRIPT element is present in the HTML page's object model (it is in theDHTML all collection and is seen by the browser as a regular script node). The XML data within the SCRIPT elements can be accessed by calling the XMLDocument property on the SCRIPT object.

The following script accesses the XML data island in the above HTML fragment and returns the name of the root node of the XML data island.

function returnIslandRootName(){
  var islandRoot = document.all.("SCRIPT").XMLDocument;
  return islandRoot.nodeName;
  }

Note that a tag that uses the name "XML" cannot be nested within an XML data island.


Binding the XML Data Source Object to Data

The XML Data Source Object (DSO) allows you to bind HTML elements to an XML data set. By doing so, you can display a table of elements and attributes contained in the XML data. The contents of the table will then change as the XML data changes.

The following sections show how you can use the XML DSO for data binding:

  • Data Islands
  • Using the XML Data Source Object
  • Additional Data Binding and XML Resources


Data Islands

In Microsoft® Internet Explorer 5, you can the use XML element to create what is called a "data island." Data islands are XML data referenced or included in an HTML page. The XML data can be included within the HTML or it can be in an external file.

Using the XML element allows you to avoid using script; if the user has disabled scripting for security reasons, the object tag version will not work because script is required to initialize the XML.

Inline XML

Inline XML is enclosed within the <XML> and </XML> tags, as shown in the following example.

<XML ID="xmlData">
<?xml version="1.0" ?>
<trees>
<tree>
<name>Birch</name>
<height unit="foot">10</height>
</tree>
<tree>
<name>Aspen</name>
<height unit="foot">4</height>
</tree>
</trees>
</XML>

References to XML Files

External XML uses the SRC attribute on the <XML> tag to reference an external file. The SRC attribute can refer to a local file or specify a URL. By using URLs that point to external servers, data can be integrated from several sources. The following code shows using the SRC attribute with a local file.

<XML ID="xmlData" SRC="xmlData.xml"></XML>

The file "xmlData.xml" contains the XML. The SRC attribute can refer to a URL as well. For example:

<XML ID="xmlData" SRC="http://msdn.microsoft.com/workshop/author/composer.xml">
</XML>

Reading Data from Within a Data Island

The ID attribute given with the <XML> tag is used to reference the data island. By using HTML tags that can accept data source tags (binding the HTML to the data), you can format and display the data in the XML data island. The following example displays the XML items tagged with <meeting>.

<HTML><HEAD></HEAD><TITLE></TITLE>
<BODY>
<XML ID="xmlMeetings">
<?xml version="1.0" ?>
<meetings>
<meeting>
<date>1/1/99</date>
<time>9:00</time>
<location>104</location>
</meeting>
<meeting>
<date>2/1/99</date>
<time>13:00</time>
<location>34</location>
</meeting>
<meeting>
<date>3/1/99</date>
<time>14:30</time>
<location>20</location>
</meeting>
</meetings>
</XML>
<table datasrc="#xmlMeetings">
<tr>
<td><div datafld="date"></div></td>
<td><div datafld="time"></div></td>
<td><div datafld="location"></div></td>
</tr>
</table>
</BODY>
</HTML>

The <table> tag uses the DATASRC attribute to reference the inline XML. The DATASRC refers to the ID attribute used to identify the XML preceded by a pound sign (#). Note that the TD element cannot be bound to data so a tag that binds to data must be used. In this case, it is the DIV element. The DIV element also allows the DATAFLD attribute to refer to the XML element to place in this cell of the table. In this case it's DATAFLD="date" for the <date> XML element.

As the XML is read, additional rows are created for each element tagged with the <meeting> tag.


Using the XML Data Source Object

You can either use the OBJECT element to refer to the XML DSO object, or you can use data islands and the XML element.

The XML Data Source ActiveX® object can be inserted into an HTML page as follows:

<OBJECT width=0 height=0
    classid="clsid:550dda30-0541-11d2-9ca9-0060b0ec3d39"
    id="xmldso">
</OBJECT>

This can be used as an XML data provider in conjunction with the data binding features of Internet Explorer 5 for binding XML data to HTML elements on the page.

To load XML into the DSO, use the DHTML XMLDocument property to get a DOM object model, and then call the load method as follows:

<SCRIPT for=window event=onload>
    var doc = xmldso.XMLDocument;
    doc.load("books.xml");
    if (doc.documentNode == null)
    {
        HandleError(doc);
    }
</SCRIPT>

Inline XML

You can also provide the XML inline inside the OBJECT element. The following example shows how to do this.

<OBJECT width=0 height=0
    classid="clsid:550dda30-0541-11d2-9ca9-0060b0ec3d39"
    id="xmldso">
<favorites>
<favorite>
<name>Microsoft</name>
<url>http://www.microsoft.com</url>
</favorite>
</favorites>
</OBJECT>

You use script to load the DSO as follows:

<SCRIPT for=window event=onload>
    var doc = xmldso.XMLDocument;
    doc.loadXML(xmldso.altHtml);
    if (doc.documentNode == null)
    {
        HandleError(doc);
    }
</SCRIPT>

Events Used with the XML Data Source Object

The XML Data Source Object triggers events when the underlying XML data changes. These events are common among the XML DSO and the other supplied DSOs. For more information on events and the data source object, see DHTML Event Model Support for Data Binding in the DHTML documentation.

Viewing and Navigating a Subset of the Data

Sometimes you will want a table to display a small portion of your XML data set. To do so, use the DATAPAGESIZE attribute on your TABLE element. The DATAPAGESIZE attribute indicates how many records to display in the table.

To navigate the table you can use the nextPage, previousPage, firstPage, and lastPage methods to display different pages of the data.

Usually, you can provide buttons to view different pages of the dataset with nextPage, previousPage, firstPage, and lastPage. For example, a button to view the next page could be written like this:

<INPUT TYPE="button" VALUE="Next" ONCLICK="tbl.nextPage();">

To specify the table, you could use the following code:

<TABLE DATAPAGESIZE=1 ID=tbl DATASRC=#xmlData>
...Table...
</TABLE>

This example sets the table to display one record (DATAPAGESIZE=1), identifies itself as "tbl" (ID=tbl), and uses a data source called "xmlData".

To indicate which table the button refers to, use the ID attribute used with the TABLE element.

The ONCLICK attribute can also specify "previousPage", "firstPage", or "lastPage". For example, to create a button to display the first page, use the following:

<INPUT TYPE="button" VALUE="First Page" ONCLICK="tbl.firstPage();">

The $Text Data Field

When you bind data using the XML DSO, an automatic field called "$Text" is created. It contains the items in that record, concatenated. The following example demonstrates the $Text data field.

<HTML><HEAD></HEAD><TITLE></TITLE>
<BODY>
<XML ID="xmlParts">
<?xml version="1.0" ?>
<parts>
<part>
<partnumber>A1000</partnumber>
<description>Flat washer</description>
<quantity>1000</quantity>
</part>
<part>
<partnumber>S2300</partnumber>
<description>Machine screw</description>
<quantity>1000</quantity>
</part>
<part>
<partnumber>M2400</partnumber>
<description>Nail</description>
<quantity>500</quantity>
</part>
</parts>
</XML>
<table datasrc=#xmlParts>
<tr>
<td><div datafld="partnumber"></div></td>
<td><div datafld="$Text"></div></td>
</tr>
</table>
</BODY>
</HTML>

In this example, the table will consist of a column of part numbers (where datafld is equal to "partnumber") and a column containing the part number, description, and quantity concatenated (where datafld is equal to "$Text"). For example, the first row of the partnumber column will contain "S2300", while the second row of the $Text column will contain "S2300 Machine screw 1000". Note that the $Text column contains the part number.

Rules for Assigning XML Elements and Attributes to Columns and Rows

The XML DSO follows a procedure for assigning elements and attributes to columns and rows in databound applications. XML is modeled as a tree with one tag containing the entire hierarchy. For example, an XML description of a book could contain chapter tags, figure tags, and section tags. Containing all of these tags would be a book tag containing the subelements chapter, figure, and section. When the XML DSO assigns row and columns, the subelements, not the top level element, are converted.

The XML DSO uses this procedure for converting the subelements:

Each subelement and attribute corresponds to a column in some rowset in the hierarchy.

The name of the column is the same as the name of the subelement or attribute, unless the parent element has an attribute and a subelement with the same name, in which case a "!" is prepended to the sublement's column name.

Each column is either a simple column containing scalar values (usually strings) or a rowset column containing subrowsets.

Columns corresponding to attributes are always simple.

Columns corresponding to subelements are rowset columns if either the subelement has its own subelements and/or attributes, or the subelement's parent has more than one instance of the subelement as a child. Otherwise the column is simple.

When there are multiple instances of a subelement (under different parents), its column is a rowset column if any of the instances imply a rowset column; its column is simple only if all instances imply a simple column.

All rowsets have an additional column named $Text.

A simpler conversion takes place if you have set the JavaDSOCompatible flag to true. The JavaDSOCompatible flag makes the Internet Explorer 5 XML DSO compatible with the Java DSO supplied with Internet Explorer 4.0. To set the JavaDSOCompatible flag you can use the XML element as follows:

<xml id="xmldata" JavaDSOCompatible=true>
...XML data
</xml>

Or, you can use the following with the OBJECT element (using the XML element is recommended):

<OBJECT width=0 height=0
    classid="clsid:550dda30-0541-11d2-9ca9-0060b0ec3d39"
    id="xmldso">
<PARAM NAME="JavaDSOCompatible" value="true">
</OBJECT>

The following method is used for creating rows and columns when JavaDSOCompatible is true:

Any element that contains another element is automatically a rowset.

Elements that contain only text are columns.

Values stored in attributes are ignored.

For more information of the Java XML DSO, see XML Data Source in the DHTML documentation.

Using DTDs

If you use a DTD (document type definition) with your XML, the XML DSO uses the following method for converting elements and attributes to rows and columns:

Each subelement and attribute named by the DTD corresponds to a column in some rowset in the hierarchy.

The name of the column is the same as the name of the subelement or attribute, unless the parent element has an attribute and a subelement with the same name, in which case a "!" is prepended to the sublement's column name.

Each column is either a simple column containing scalar values (usually strings) or a rowset column containing subrowsets.

Columns corresponding to attributes are always simple.

Columns corresponding to subelements are rowset columns if either the DTD allows the subelement to have its own subelements and/or attributes, or the DTD allows the subelement's parent to have more than one instance of the subelement as a child. Otherwise the column is simple.

All rowsets have an additional column named $Text.

Content corresponding to the content model "ANY" is not included in the rowset hierarchy.


Additional Data Binding and XML Resources

For more information about data binding, see the following topics in the DHTML documentation:

  • Data Binding
  • Data Binding Architecture
  • Adding a Data Source Object to a Page
  • Binding HTML Elements to Data
  • DHTML Object Model Support for Data Binding
  • DHTML Event Model Support for Data Binding

For tutorials and demos about using the XML DSO, see the following resources:

  • Using the C++ XML DSO
  • Lesson 10: Using the Master/Detail Feature with the C++ XML DSO
  • XML Data Source Object sample on MSDN Online. See XML And XSL Samples.


XML Schema Developer's Guide

A schema specifies the structure of an XML document and constraints on its content.

While XML is the meta-language that defines the rules for defining tag languages, a schema is a formal specification of the grammar for one tag language. A schema is useful for validating the document content—to determine whether a document is a valid instance of the grammar expressed by that schema—and for describing your grammar for use by others.

See these topics for more information on authoring XML Schemas:

  • Introduction to Schemas
  • Defining Elements and Attributes
  • Content Model
  • Data Types
  • Extensibility
  • XML Schemas and the DOM
  • XML Schemas and Namespaces
  • Using In-line Schemas
  • Representing the XML Schema as a DTD


Introduction to Schemas

The schema defines the elements that can appear within the document and the attributes that can be associated with an element. It also defines the structure of the document: which elements are child elements of others, the sequence in which the child elements can appear, and the number of child elements. It defines whether an element is empty or can include text. The schema can also define default values for attributes.

Consider a simple XML document that contains only three elements, "PGROUP," "PERSONA," and "GRPDESCR," as in the following example:

<?xml version="1.0" ?>
<PGROUP>
  <PERSONA>MACBETH</PERSONA>
  <PERSONA>BANQUO</PERSONA>
  <GRPDESCR>generals of the king's army.</GRPDESCR>
</PGROUP>

In all documents of this type, the document element is "PGROUP." In these examples, "PGROUP" does not contain any text, but contains only one or more child elements named "PERSONA," and one child element named "GRPDESCR." The "PERSONA" and "GRPDESCR" elements contain only text and do not contain other elements.

One method for describing this schema is the Document Type Definition (DTD), a grammar that can be used to formally describe a particular schema. The DTD for the example document appears as follows:

<!DOCTYPE PGROUP [
<!ELEMENT PGROUP          (PERSONA+, GRPDESCR) >
<!ELEMENT PERSONA         (#PCDATA)  >
<!ELEMENT GRPDESCR        (#PCDATA)  >
]>

For a formal description of the DTD syntax, see the Extensible Markup Language (XML) 1.0 specification on the W3C Web site.

Introduction to XML Schemas

A second, and more interesting, method is available to users of Internet Explorer 5. XML Schema, like DTD, can be used to specify the schema of a particular class of documents. Unlike DTDs, however, XML Schema uses XML syntax. This is convenient because you are not required to learn a completely new syntax just to describe your grammar—although you do need to learn how to declare elements and attributes using XML Schema. In addition, XML Schemas offer a number of other significant advantages over using DTDs, which are described in the following topics.

  • Content Model
  • Data Types
  • Extensibility
  • XML Schemas and the DOM

The XML Schema implementation in Msxml is based primarily upon the XML-Data Note (http://www.w3.org/TR/1998/NOTE-XML-data-0105/), posted by the W3C in January 1998, and the Document Content Description (DCD) (http://www.w3.org/TR/NOTE-dcd) note. XML Schemas in Internet Explorer 5 provide support for the subset of XML-Data that coincides directly with the functionality expressed in DCD, albeit in a slightly different XML grammar.

The XML Schema implementation provided in Msxml focuses on syntactic schemas, without support for inheritance or other object-oriented design features. The Msxml implementation provided with Internet Explorer 5 is described in the XML Schema Reference.

To reference a schema from an XML document, include a schema namespace declaration, as in the following XML document:

<book xmlns="x-schema:yourshema.xml">
<title>Presenting XML</title>
<author>Richard Light</author>
</book>

In Msxml2 and later versions, a schema can be included in-line within the document that references it, as in the following example:

<root xmlns:data="x-schema:#inLineSchema">
   <Schema name ="inLineSchema"
           xmlns="urn:schemas-microsoft-com:xml-data">
   <ElementType name="foo"/>
   </Schema>
   <data:foo>inline example</data:foo>
</root>

The pound sign (#) before the schema name indicates the schema is in-line.

The following sample demonstrates how to use XML Schema to specify the schema for the sample document.

<?xml version="1.0"?>
<Schema name="schema_sample_1"
    xmlns="urn:schemas-microsoft-com:xml-data"
    xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <ElementType name="PERSONA" content="textOnly" model="closed"/>
  <ElementType name="GRPDESCR" content="textOnly" model="closed"/>
  <ElementType name="PGROUP" content="eltOnly" model="closed">
     <element type="PERSONA" minOccurs="1" maxOccurs="*"/>
     <element type="GRPDESCR" minOccurs="1" maxOccurs="1"/>
  </ElementType>
</Schema>


Defining Elements and Attributes

The most important part of any schema is the formal specification of what elements and attributes are allowed within a particular class of document, and how those elements and attributes are related to each other. Elements and attributes are defined in an XML Schema document by specifying an <ElementType ...> and <AttributeType ...>, respectively. These provide the definition and type of the element or attribute. An instance of an element or an attribute is declared using <element ...> or <attribute ...> tags. This can be thought of as being analogous to declaring a typedef in the C programming language and then declaring a variable of that type.

<?xml version="1.0"?>
<Schema xmlns="schemas-microsoft-com:xml-data">
  <ElementType name="title" />
  <ElementType name="author" />
  <ElementType name="pages" />
  <ElementType name="book" model="closed">
    <element type="title" />
    <element type="author" />
    <element type="pages" />

    <AttributeType name="copyright" />
    <attribute type="copyright" />
  </ElementType>
</Schema>

In this example, there are four <ElementType> elements: "title," "author," "pages," and "book." These are simply definitions of the elements. Within the ElementType for "book," however, you can declare the content model for a book. Each book contains "title," "author," and "pages" elements using the <element> with a type attribute that references the ElementType. You can also define an <AttributeType> for the copyright attribute and then declare its usage using the <attribute> element with a type attribute that references its definition.

Note also that the definition of the copyright attribute is contained within the ElementType for "book." Attribute definitions are distinct from ElementType definitions in that they can be declared within the scope of an ElementType, allowing different element types to declare attributes of the same name but with (potentially) different meaning. <AttributeType> elements can be declared globally by placing them outside the context of an ElementType. In this way, multiple elements can share the definition of a common attribute without having to redeclare the AttributeType within each ElementType.


Content Model

You can specify the content model for an ElementType by using <element> to reference other ElementTypes. For example:

<Schema xmlns="urn:schemas-microsoft-com:xml-data">
<ElementType name="title" content="textOnly"/>
<ElementType name="authors" content="textOnly"/>
<ElementType name="pages" content="textOnly"/>
<ElementType name="book" order="seq" content="eltOnly">
      <element type="title" />
      <element type="authors" />
      <element type="pages" />
</ElementType>
</Schema>

In the above example, content of a "book" element is defined to be the sequence of "title", "author" and "pages" elements. This allows you to write the following valid XML document:

<book xmlns="x-schema:book-schema.xml">
  <title>Applied XML: A Toolkit for Programmers</title>
  <authors>Alex Ceponkus and Faraz Hoodbhoy</authors>
  <pages>474</pages>
</book>

This is a very simple case. There are a number of mechanisms in XML Schema that allow you to build more complex content models, including being able to group elements and define cardinality.

Open Content Models

The most important innovation for content models in XML Schema is that content models are "open" by default. An open content model enables additional elements and/or attributes to be present within an element without having to declare each and every element in the XML Schema. This provides an extensibility mechanism not present when using Document Type Definitions (DTDs). For example:

<book xmlns="x-schema:book-schema.xml" xmlns:x="urn:some-new-namespace">
  <title x:id="123">Applied XML: A Toolkit for Programmers</title>
  <authors>Alex Ceponkus and Faraz Hoodbhoy</authors>
  <pages>474</pages>
  <x:publisher>Wiley Computer Publishing</x:publisher>
</book>

The following constraints apply on what is allowed in an open content model:

  1. You cannot add/remove content that will break the existing content model. For example, since <book> is defined as a sequence, the valid data must provide that exact sequence first, before adding any "open" content. So removing the <pages> element or providing two <title> elements next to each other would cause validation to fail.
  2. You can add undeclared elements so long as they are defined in a different namespace.
  3. You can add other elements declared in the same schema. For example, a second <title> element after the <pages> element will validate.

You can change this default behavior and specify a closed content model in instances where an open content model is not desired. In this case the model attribute can be used on the ElementType. For example:

<ElementType name="book" model="closed">

This indicates that a "book" element can only contain the content specified—"title," "author," and "pages" elements. In this case the above <book> example with the extended elements will not validate.

Content

An element can contain text, other elements, a mixture of text and elements, or nothing at all. Use the content attribute to specify what the element will contain. Possible values are "textOnly", "eltOnly", "empty", and "mixed". For example, to indicate that an element contains text but no sub-elements, write:

<ElementType name="street" content="textOnly" />

Content for an element can also be limited to contain only sub-elements. For example:

<ElementType name="PurchaseOrder" content="eltOnly">

The value "empty" for content indicates that text and sub-elements are not allowed. The value "mixed" allows both text and sub-elements.

If an ElementType has a datatype specified such as date, number, etc., then "textOnly" is implied, and not required.

minOccurs and maxOccurs

The minOccurs and maxOccurs attributes specify how many times an element can appear within another element.

<element type="Item" maxOccurs="*" />

The maxOccurs attribute is a constraint rule, specifying the maximum number of times that a sub-element may appear. Valid values for maxOccurs include integers and "*", which indicates that an unrestricted number of elements may appear. The default value for maxOccurs is "1"; however, when content="mixed", the default value is "*".

Similarly, you can specify a minimum number of times a sub-element may appear with minOccurs. For example, to make a sub-element optional, set minOccurs to "0". The default value for minOccurs is 1.

These attributes can be used for both element and group declarations.

minLength and maxLength

The dt:minLength and dt:maxLength attributes can be used to constrain the length of a string, number, bin.hex, or bin.base64 data type.

For string and number data types, dt:maxLength is specified in number of characters. For bin.hex and bin.base64, the maximum length is in terms of the number of bytes the binary object can be. The length is inclusive (that is, a string can be as long as dt:maxLength, but no longer). In the following example, the value of city attributes can be from 0 to 22 characters long:

<AttributeType name="city"
  xmlns:dt="urn:schemas-microsoft-com:datatypes"
  dt:type="string" dt:maxLength="22"/>

Conversely, dt:minLength can be used to provide a minimum length for a string, number, bin.hex, or bin.base64 data type. For string and number data types, the minimum length is specified in number of characters. For bin.hex and bin.base64, the minimum length is in terms of the number of bytes the binary object can be. The length is inclusive (that is, a string can be as short as the minLength, but no shorter). In the following example, the password element must have at least eight characters:

<ElementType name="password"
  xmlns:dt="urn:schemas-microsoft-com:datatypes"
  dt:type="string" dt:minLength="8"/>

dt:maxLength and dt:minLength are enforced at parse-time and run-time. Valid parent elements are <ElementType/>, <AttributeType/>, and <dataType/>.

Order

The order attribute specifies whether sub-elements are required to appear in a certain order, and if only one sub-element of a set can appear.

The "seq" value indicates that sub-elements must appear in the order listed in the schema. For example:

<ElementType name="PurchaseOrder" order="seq">

The "one" value specifies that only one sub-element can be used from a list of sub-elements. For example, to specify that an "Item" element may contain either a "product" element or a "backOrderedProduct" element, but not both:

<ElementType name="Item" order="one">
   <element type="product" />
   <element type="backOrderedProduct" />
</ElementType>

The "many" value specifies that the sub-elements may appear in any order, and in any quantity.

The default value for order is "seq" when the content attribute is set to "eltOnly", and the default is "many" when content is set to "mixed".

Group

The group element enables you to specify constraints on a specific set of sub-elements. For example, to indicate that the "Item" element has either a "product" or a "backOrderedProduct" element, and then a "quantity" and "price", you can use the following XML:

<ElementType name="Item">
    <group order="one">
        <element type="product" />
        <element type="backOrderedProduct" />
    </group>
    <element type="quantity"/>
    <element type="price"/>
</ElementType>

The group element accepts the order, minOccurs, and maxOccurs attributes.

Attributes

Attributes are more limited in some ways than elements. For instance, attributes cannot contain sub-elements, and you cannot require attributes to appear in any particular order; nor can you pose alternatives, such as a "product" or a "backOrderedProduct". You can specify whether an attribute is required or optional, but an attribute may only appear once per element.

However, attributes have some capabilities that elements do not: attributes may limit their legal values to a small set of strings, and may indicate a value to be inferred if the attribute is omitted from an element. Most importantly, different element types may have attributes with the same name. These attributes are considered to be independent and unrelated.

The following example specifies that the attribute is required:

<AttributeType name="shipTo" dt:type="idref" required="yes"/>

The following limits the value of an attribute to words from a small list:

<AttributeType name="priority" dt:type="enumeration" dt:values="high medium low" />

To give a default value to an attribute (that is, a value that an application should infer if the attribute is not present in the document instance) use the default attribute, as follows:

<AttributeType name="quantity" dt:type="int">
<attribute type="quantity" default="1"/>


Data Types

Unlike a Document Type Definition (DTD), XML Schema allows you to specify a data type for an element or attribute. Data types indicate the format of the data, provide for validation of the type by the XML parser, and enable processing specific to the data type in the XML Document Object Model (DOM).

Data type support includes primitive data types common to programming languages as well as the special attribute types included in the XML Language Specification (for example, ID, IDREF, and NMTOKEN). A complete list of the data types can be found in the XML Data Types Reference. For more information about data type support for attributes, see Attribute Data Types.

To use the data type support included with Internet Explorer 5, your XML Schema must include the datatypes namespace. The top-level <Schema> element declaration would look like this:

<Schema name="myschema"
        xmlns="urn:schemas-microsoft-com:xml-data"
        xmlns:dt="urn:schemas-microsoft-com:datatypes">
 <!-- ... -->
</Schema>

Within the <Schema> element, data types can be specified on an <ElementType> or <AttributeType> basis using one of two forms:

  • dt:type attribute
  • <datatype> element

Both forms are demonstrated below shown in a declaration—the samples are equivalent.

<ElementType name="pages" dt:type="int"/>
<ElementType name="pages">
  <datatype dt:type= "int"/>
</ElementType>


Attribute Data Types

Although XML Schema support in Internet Explorer 5 allows data types to be specified within attributes, only the following data types are supported within attributes by the parser and DOM: string, id, idref, idrefs, nmtoken, nmtokens, entity, entities, enumeration, notation, maxLength, and minLength.

In the following schema, the shipTo attribute has a data type of "idref."

<AttributeType name="shipTo" dt:type="idref"/>
<attribute type="shipTo"/>

An attribute whose data type is "idref" holds an identifying value in the document instance, a value that does not appear on any other idref attribute in the document.

An attribute of type "id" acts as a reference to the element with the matching id value. The data type "idrefs" is similar to "id," but it holds a list of ids, separated by spaces. For example,

<PurchaseOrder items="Item-1 Item-2">


Declaring Attribute Data Types on Elements

In Internet Explorer 5.01, elements support the id attribute data type. You can declare it on an element node through schema just as you would declare it on an attribute node. The two ways of doing this are with the dt:type attribute on the <ElementType> element, or with the <datatype> element within an <ElementType>.

Below, with the dt:type attribute on the <ElementType> element:

<ElementType name="Element1" dt:type="id"/>

or with the <datatype> element within an <ElementType> element:

<ElementType name="Element2">
   <datatype dt:type="id">
</ElementType>

In Msxml2 and Msxml3, elements also support the enumeration data type in the same fashion as attributes:

<ElementType name = "foo" dt:type="enumeration" dt:values="a b c"
xmlns:dt="urn:schemas-microsoft-com:datatypes"/>

The following element is a valid instance for the type declaration above:

<foo>b</foo>

As with other typed elements, the instance element cannot contain any child elements.

An element can also be typed as an enumeration on the instance. For example, the following element <foo> contains a type declaration and declares its values:

<foo dt:dt="enumeration" dt:values="a b c"
   xmlns:dt="urn:schemas-microsoft-com:datatypes"/>b</foo>

The enumeration data type can also be used as an element name:

<dt:enumeration dt:values="a b c"
   xmlns:dt="urn:schemas-microsoft-com:datatypes"/>b</dt:enumeration>

When typing the element on the instance, the values attribute is required, and must contain valid values. The contents of such an element are validated only at parse-time.

Beginning with Msxml2, elements also support the following attribute data types:

  • entity
  • entities


Declaring Simple Data Types on Attributes

In Internet Explorer 5.01, you can use simple data types (for example, int, boolean, float, and so on) when you declare an attribute. Use either the dt:type attribute on the <AttributeType> element, or a <datatype> element within an <AttributeType> element.

Below, the dt:type attribute on the <AttributeType> element:

<AttributeType name="att1" dt:type="int"/>
<ElementType name="Element1">
   <attribute type="att1"/>
</ElementType>

or with a <datatype> element within an <AttributeType> element:

<AttributeType name ="y"/>
  <datatype dt:type="int"/>
</AttributeType>
<ElementType name="x">
  <attribute type="y"/>
<ElementType>


Data Types as Elements

In Internet Explorer 5.01, you can use data types as elements in an XML document (though it is not possible to type such elements through schema). So rather than declaring a data type on the instance using the dt:dt attribute, or in the schema using the dt:type attribute or <datatype> element, you can use a data type declared as an element. The following example illustrates using the dt:dt attribute:

<shoes xmlns:dt="urn:schemas-microsoft-com:datatypes" id="f1121" sizes="mens"/>
<sizes id="mens">
  <size dt:dt="int">8</size>
  <size dt:dt="int">10</size>
  <size dt:dt="int">12</size>
  </sizes>
</shoes>

The next example is the same as the above XML, but the data types are declared as elements:

<shoes xmlns:dt="urn:schemas-microsoft-com:datatypes" id="f1121" sizes="mens"/>
<sizes id="mens">
  <dt:int>8</dt:int>
  <dt:int>10</dt:int>
  <dt:int>12</dt:int>
</sizes>
</shoes>

The datatype property on data type elements is read-only and returns the data type of the element. Use the nodeTypedValue property to return the typed value of the element. Getting the nodeTypedValue of an element that has no data type returns the string value of the element.

The contents of data type elements are validated against the type of the element. If a data type isn't supported by the parser it is disregarded, and no error occurs. A data type element cannot have child elements.


Extensibility

Unlike a Document Type Definition (DTD), XML Schemas are extensible. That is, they are built on an open content model. Schema authors are free to add their own elements and attributes to XML Schema documents. For example, you could add additional constraints to a declaration for a "pages" element. The following sample code declares the "pages" element and assigns it an "int" datatype. Extended tags from the "myExt" namespace are used to augment this information with an added limitation that books must have a minimum of 50 pages and a maximum of 100 pages.

<ElementType name="pages" xmlns:myExt="urn:myschema-extensions">
  <datatype dt:type="int" />
  <myExt:min>50</myExt:min>
  <myExt:max>100</myExt:max>
</ElementType>

While validation will only check that the value of a particular "pages" element is an integer, your application can use the information provided by the added elements in the "myExt" namespace to perform additional validation.

It's worth noting that the elements that Microsoft added to the schema were qualified by a namespace. Use of a namespace is a requirement for adding elements and attributes to a schema. For more information on namespaces, see Introduction to Namespaces.

Referencing Other Schemas

Custom schemas can be built out of standard parts. For example, suppose that a definition for an "Address" element from another schema has met your needs. With Namespaces, this schema can be used directly with a reference to the original schema rather than copying the definition.

To reference an element defined in another schema, you must first use a namespace declaration to introduce the other schema. This declaration consists of an attribute beginning with "xmlns:", followed by a name of your choice called the "prefix," and lastly with a value that is the Universal Resource Identifier (URI) of the other schema.

<ElementType name="PurchaseOrder"
             xmlns:abcde="http://electrocommerce.org/stuff.xml">

The schema has a namespace URI of "http://electrocommerce.org/stuff.xml" and a prefix of "abcde." The namespace URI identifies the other schema precisely; the prefix is a local name that you now use to refer to that schema and to the elements it defines as though they were defined in your schema. If the other schema has a suitable "shipTo" element defined, you can use it by writing,

<ElementType name="shipTo"
             xmlns:abcde="http://electrocommerce.org/stuff.xml">
   <element type="abcde:Address"/>
</ElementType>

In the type attribute of the first sub-element declaration for the value "abcde:Address," the prefix "abcde" is matched to the enclosing namespace declaration, and thus "abcde:Address" has the effect of meaning "the Address element" as defined in the schema "http://electrocommerce.org/stuff.xml."

However, this has an effect on the XML document as well. For example, in the following purchase order document you also need to reference the other schema namespace.

<PurchaseOrder xmlns:abcde="http://electrocommerce.org/stuff.xml">
  <shipTo>
    <abcde:Address>
      <abcde:name>Alice Smith</abcde:name>
      <abcde:street>123 Maple Street</abcde:street>
      <abcde:city>Mill Valley</abcde:city>
      <abcde:state>CA</abcde:state>
      <abcde:zip>90952</abcde:zip>
    </abcde:Address>
  </shipTo>
  <orderDate>1999-05-20</orderDate>
  <shipDate>1999-05-25</shipDate>
  <comments>
    Get these things to me in a hurry, my lawn is going wild!
  </comments>
  <Items>
    <Item>
      <productName>Lawnmower, model BUZZ-1</productName>
      <quantity>1</quantity>
      <price>148.95</price>
    </Item>
    <Item>
      <productName>Weed Wacker, model SNOOZE-2</productName>
      <quantity>1</quantity>
      <price>39.98</price>
    </Item>
  </Items>
</PurchaseOrder>

You can determine that the "Address" element is defined by the schema at "http://electrocommerce.org/stuff.xml." When you build specialized schemas from more widely-understood standard parts, like the "Address" element above, those portions can often be processed by standard software components. For example, if you have an XSL style sheet template available for the "Address" element, as defined in the schema "http://electrocommerce.org/stuff.xml," you can reuse this general-purpose address styling code anywhere that "Address" appears.

The same prefix is used in both the schema and in the document instance: in the schema for all purchase orders, and in a specific purchase order. But this isn't necessary, since the prefix is only a local alias for the namespace URI, and you can use any prefix you like in the document instance. You are only required to use the same prefix in a document both when you declare the namespace and when you use elements from that namespace. For example,

<PurchaseOrder xmlns:z="http://electrocommerce.org/stuff.xml">
  <shipTo>
    <z:Address>
      <z:name>Alice Smith</z:name>
      <z:street>123 Maple Street</z:street>
      <z:city>Mill Valley</z:city>
      <z:state>CA</z:state>
      <z:zip>90952</z:zip>
    </z:Address>
  </shipTo>
</PurchaseOrder>

The declaration of a namespace is scoped to the element on which it appears. It makes the namespace prefix available, and associates it with the namespace URI for that element and anything it contains, unless overridden by another, enclosed namespace declaration. Therefore, because the namespace is only used on the "Address" element, the namespace declaration could appear on the "Address" element instead, as follows,

<PurchaseOrder>
  <shipTo>
    <z:Address xmlns:z="http://electrocommerce.org/stuff.xml">
      <z:name>Alice Smith</z:name>
      <z:street>123 Maple Street</z:street>
      <z:city>Mill Valley</z:city>
      <z:state>CA</z:state>
      <z:zip>90952</z:zip>
    </z:Address>
  </shipTo>
</PurchaseOrder>

Omitting Prefixes

You can also omit a prefix to a namespace, which can make for more legible documents. In the following example, by omitting the prefix from the namespace declaration, you indicate that any sub-element without a prefix is defined by the specified namespace.

<PurchaseOrder>
  <shipTo>
    <Address xmlns="http://electrocommerce.org/stuff.xml">
      <name>Alice Smith</name>
      <street>123 Maple Street</street>
      <city>Mill Valley</city>
      <state>CA</state>
      <zip>90952</zip>
    </Address>
  </shipTo>
</PurchaseOrder>

Namespace prefixes are only for intra-document bookeeping. What matters is the corresponding namespace URI, so all four of the previous examples' "Address" elements mean exactly the same thing.

Although the "Address" element has been identified as being defined by the schema "http://electrocommerce.org/stuff.xml," "PurchaseOrder" has not been similarly tied to a schema. To do this, assuming a schema named "http://fabrikam.com/PO.xml" defines a "PurchaseOrder," you can write:

<PurchaseOrder xmlns="http://fabrikam.com/PO.xml">
  <shipTo>
    <Address xmlns="http://electrocommerce.org/stuff.xml"  >
      <name>Alice Smith</name>
      <street>123 Maple Street</street>
      <city>Mill Valley</city>
      <state>CA</state>
      <zip>90952</zip>
    </Address>
  </shipTo>
  <orderDate>1999-05-20</orderDate>
  <shipDate>1999-05-25</shipDate>
  <comments>
     Get these things to me in a hurry, my lawn is going wild!
  </comments>
  <Items>
    <Item>
      <productName>Lawnmower, model BUZZ-1</productName>
      <quantity>1</quantity>
      <price>148.95</price>
    </Item>
    <Item>
      <productName>Baby Monitor, model SNOOZE-2</productName>
      <quantity>1</quantity>
      <price>39.98</price>
    </Item>
  </Items>
</PurchaseOrder>

This declares that the "PurchaseOrder" element, and all elements it contains, are defined within the schema "http://fabrikam.com/PO.xml" except for "Address" and the elements it contains, which are defined within xmlns="http://electrocommerce.org/stuff.xml." In both cases, explicit prefixes are omitted.

Multiple Namespaces

You can declare multiple namespaces on a single element. You can also use several prefixes to refer to the same namespace. The following example shows this, merely to demonstrate that it can be done, and to make the point that this document is functionally equivalent to the previous one.

<PurchaseOrder xmlns:a="http://fabrikam.com/PO.xml"
xmlns:b="http://electrocommerce.org/stuff.xml"
xmlns:c="http://electrocommerce.org/stuff.xml">
  <a:shipTo>
    <b:Address >
      <c:name>Alice Smith</c:name>
      <b:street>123 Maple Street</b:street>
      <c:city>Mill Valley</c:city>
      <b:state>CA</b:state>
      <c:zip>90952</c:zip>
    </b:Address>
  </a:shipTo>
  <a:orderDate>1999-05-20</a:orderDate>
  <a:shipDate>1999-05-25</a:shipDate>
  <a:comments>
      Get these things to me in a hurry, my lawn is going wild!
  </a:comments>
  <a:Items>
    <a:Item>
      <a:productName>Lawnmower, model BUZZ-1</a:productName>
      <a:quantity>1</a:quantity>
      <a:price>148.95</a:price>
    </a:Item>
    <a:Item>
      <a:productName>Baby Monitor</a:productName>
      <a:quantity>1</a:quantity>
      <a:price>39.98</a:price>
    </a:Item>
  </a:Items>
</a:PurchaseOrder>

Open Content Model

If a document schema has elements with an open content model, you can include elements not mentioned in the original schema, provided they are assigned to a specific namespace, either with a default namespace in scope or with a prefix. You can also include undefined attributes in an element, provided the attribute is qualified by a prefix and the prefix is in scope. For example, suppose that a third schema defines an element called "trackingInformation." Because "PurchaseOrder" is defined as having an open content model, you can simply add this element directly to the instance:

<PurchaseOrder xmlns="http://fabrikam.com/PO.xml">
  <shipTo>
    <Address xmlns="http://electrocommerce.org/stuff.xml">
      <name>Alice Smith</name>
      <street>123 Maple Street</street>
      <city>Mill Valley</city>
      <state>CA</state>
      <zip>90952</zip>
    </Address>
  </shipTo>
  <orderDate>1999-05-20</orderDate>
  <shipDate>1999-05-25</shipDate>
  <comments>
      Get these things to me in a hurry, my lawn is going wild!
  </comments>
  <Items>
    <Item>
      <productName>Lawnmower, model BUZZ-1</productName>
      <quantity>1</quantity>
      <price>148.95</price>
    </Item>
    <Item>
      <productName>Baby Monitor, model SNOOZE-2</productName>
      <quantity>1</quantity>
      <price>39.98</price>
    </Item>
  </Items>
  <trackingInformation xmlns="http://somewhere.com/tracking.xml">
    <route>1234-5678</route>
    <signature>32143912850-3959159=1395</signature>
  </trackingInformation>
</PurchaseOrder>

Because namespace qualification is required for any undefined attribute or element, the "trackingInformation" element would not have been valid if its "xmlns" attribute had been omitted.


XML Schemas and the DOM

When you use XML Schema, the XML DOM provides a property on each element and attribute to quickly access its definition in the schema. The definition property on the XMLDOMNode object returns the corresponding ElementType or AttributeType from the schema document. Recall that because an XML schema document is itself an XML document, the schema can be parsed into a tree of nodes like any other XML document. The XML DOM can then be used to navigate the schema.

For example, consider the following example schema (Bookschema.xml) and XML documents:

Bookschema.xml

<?xml version="1.0"?>
<Schema xmlns="urn:schemas-microsoft-com:xml-data">
  <ElementType name="title" />
  <ElementType name="author" />
  <ElementType name="pages" />
  <ElementType name="book" model="closed">
    <element type="title" />
    <element type="author" />
    <element type="pages" />
  </ElementType>
</Schema>

Book.xml

<?xml version ="1.0"?>
<book xmlns="x-schema:bookschema.xml" >
  <title>Presenting XML</title>
  <author>Richard Light</author>
  <pages>334</pages>
</book>

Start by getting the root element (assuming the document has been parsed, and the object reference doc refers to the parsed document):

var bookNode =  doc.documentElement;

You can now use the definition property on bookNode to get the ElementType definition for the <book> element.

var bookDef = bookNode.definition;

bookDef is the returned schema declaration, or definition, of the <book> element:

<ElementType xmlns="urn:schemas-microsoft.com:xml-data" name="book" model="closed">
   <element type="title"/>
   <element type="author"/>
   <element type="pages"/>
</ElementType>

With bookDef, you can then access other schema information.

var title = bookDef.childNodes(0);
//returns <element xmlns="urn:schemas-microsoft.com:xmldata"
//type ="title"/>
var author = bookDef.childNodes(1).xml;
//returns <element xmlns="urn:schemas-microsoft.com:xmldata"
//type ="author">


XML Schemas and Namespaces

Msxml enables XML Schemas to be referenced directly from a namespace declaration. This allows the XML parser to validate the XML against the grammar defined in the schema. It also allows the object model to expose data types and to allow navigation to the schema document through the definition property (see XML Schemas and the DOM).

A namespace declaration can reference a schema by including the URL to the schema document prefaced by the "x-schema:" string. For example, to reference an XML Schema for books from an XML file conforming to this schema, you would write:

<book xmlns="x-schema:http://www.microsoft.com/xml/schema/book.xml">
  <title>Presenting XML</title>
  <author>Richard Light</author>
  <pages>334</pages>
</book>

Here the default namespace refers to the sample book grammar used in the Defining Elements and Attributes section. The namespace Universal Resource Identifier (URI) is prefixed with "x-schema:" to indicate to the XML parser that there is a schema corresponding to this namespace. The remainder of the URI is the schema's location. The path or URI to the schema file must be valid, or a parse error will occur.


Using In-line Schemas

Instead of using an external schema file, Msxml2 allows you to include schemas in-line.

For example, the following XML document contains an in-line schema against which the "Student" element is validated:

<Root>
<Schema name="mySchema" xmlns="urn:schemas-microsoft-com:xml-data"
             xmlns:dt="urn:schemas-microsoft-com:datatypes">
   <ElementType name="age" dt:type="int"/>
   <ElementType name="Student" model="closed" content="eltOnly">
     <element type="age"/>
   </ElementType>
</Schema>

<a:Student xmlns:a="x-schema:#mySchema">
   <age>10</age>
</a:Student>
</Root>

The ID of the schema is indicated through the "name" attribute on the "Schema" element. If there are two schemas with the same ID, the first schema to appear in the document will be the schema validated against.

An element is associated with an in-line schema just as it is with an external schema, through the "x-schema:" syntax when defining that element's namespace identifier. With an external schema, that which follows "x-schema:" indicates the location of the external schema file. With an in-line schema, that which follows "x-schema:" indicates the name of the schema within the XML instance document (this is indicated by the # sign followed by the name).

Note that the "#" syntax following "x-schema:" refers to schema within the local document only. This syntax cannot be used to reference schema within an external document (for example, "x-schema:URI#name" is not allowed).

The in-line schema associated with an element must precede that element within the document. When validating against the schema, if the name indicated through an element's namespace declaration does not correspond to an already parsed schema, an error will occur. This behavior is consistent with the case in which an element is associated with a schema that does not exist.

The DOMDocument's validateOnParse property behaves the same with in-line schemas as it does on external schemas: if set to false, validation doesn't occur, but the default attributes, IDs and IDREFs, and datatypes are all processed according to the schema. Multiple schemas can appear in the instance document as long as they conform to the ordering rules outlined above and their IDs do not conflict.

You can read from/write to in-line schemas through the DOM. The XMLDOMNode's definition property returns the type declaration that corresponds to the node just as it does with an external schema. If the type declaration is changed through the DOM, then the definition property reflects these changes. If the type declaration is removed or the value of the name attribute is changed, the definition property returns null. If a new type declaration is added to the schema and a corresponding node is added to the instance data, the definition property for the new node returns the new corresponding type declaration.

In-line Schemas Persisted from ADO

You can use Msxml2 to parse in-line schemas produced by ADO, but they won't be validated. So given the following ADO data:

<xml xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882'
   xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882'
   xmlns:rs='urn:schemas-microsoft-com:rowset'
   xmlns:z='#RowsetSchema'>
<s:Schema id='RowsetSchema'>
   <s:ElementType name='row'>
      <s:attribute type='CustomerID'/>
      <s:extends type='rs:rowbase'/>
   </s:ElementType>
<s:AttributeType name='CustomerID' rs:number='1'
   rs:nullable='true' rs:write='true'>
   <s:datatype dt:type='string' dt:maxLength='5'/>
</s:AttributeType>
</s:Schema>
<rs:data>
   <z:row CustomerID='…'/>
</rs:data>
</xml>

Msxml2 essentially ignores the following elements and attributes, and the remainder of the schema is parsed as if it were a valid schema:

Elements | Attributes
extends | dt:max
 | dt:maxExclusive
 | dt:maxLength
 | dt:min
 | dt:minExclusive

Validation against the remainder of the schema cannot occur because the schema does not contain a "name" attribute and because the reference to the schema is not prefaced by "x-schema:". Msxml2 views that reference as a URN with no special significance.


Representing the XML Schema as a DTD

The following is an example of representing the XML schema with a DTD. The representation is not exact, however; and there are a number of problems:

  • Schemas and DTDs use different content models. When the model is defined as open, the element can include ElementType elements, AttributeType elements, and mixed content not specified in the content model. When the model is defined as closed, the element cannot include elements and cannot include mixed content not specified in the content model. The DTD uses a closed model, and schemas use an open content model.
  • Possible datatypes for elements and attributes are different. The datatypes parameter entity below lists the datatypes for both attributes and elements so for the dt:type attribute of the datatype element. But in the Microsoft® Internet Explorer 5 version, not all of these datatypes are supported on attributes and elements. The attributes only support the first 10 types (which map to DTD attribute types) and elements only support from "string" through to uuid and therefore an element cannot behave like an id or idref, for example.
  • Placement of xmlns namespace declarations cannot be modeled very well using a DTD. In a real schema the xmlns:dt attributes can go anywhere. And for that matter any other xmlns declaration can also appear because schemas themselves follow an open content model. This way you can add extended information in your schemas for your own use.
  • The maxOccurs attribute can only have the values "1" or "*" and this cannot be modeled using a DTD enumeration because "*" is not a valid name token.
  • ElementTypes and AttributeTypes can only have one datatype child element. DTDs cannot model this sort of ordinality especially when other child elements are also allowed.
<!--
  The possible element datatypes according to
    http://msdn.microsoft.com/xml/schema/reference/datatypes.asp
-->
<!ENTITY % datatypes "(entity | entities | enumeration | id | idref | idrefs
| nmtoken | nmtokens | notation | string | bin.base64 | bin.hex | boolean |
char | date | dateTime | dateTime.tz | fixed.14.4 | float | int | number |
time | time.tz | i1 | i2 | i4 | r4 | r8 | ui1 | ui2 | ui4 | uri |
uuid)" >
<!--  *** datatype **** -->
<!ELEMENT datatype  (description)*>
<!ATTLIST datatype
    dt:type %datatypes;  #IMPLIED
    xmlns:dt CDATA #FIXED "urn:schemas-microsoft-com:datatypes"
>
<!--  ***** description  ***** -->
<!ELEMENT description (#PCDATA) >
<!-- ****  element  ***** -->
<!-- additional constraint on maxOccurs is that it must be 1 or "*" -->
<!ELEMENT element  (description)* >
<!ATTLIST element
   type IDREF        #REQUIRED
   minOccurs  CDATA  #IMPLIED
   maxOccurs  CDATA  #IMPLIED
>
<!-- ****  attribute   ***** -->
<!ELEMENT attribute  (description)* >
<!ATTLIST attribute
   type       IDREF    #REQUIRED
   default    CDATA        #IMPLIED
   required   (yes | no)   "no"
>
<!-- ****  AttributeType   ***** -->
<!ELEMENT AttributeType (datatype | description)* >
<!ATTLIST AttributeType
   name         ID              #REQUIRED
   default      CDATA           #IMPLIED
   dt:type      %datatypes;     #IMPLIED
   dt:values    CDATA           #IMPLIED
   required     (yes | no)      #IMPLIED
   xmlns:dt CDATA #FIXED "urn:schemas-microsoft-com:datatypes"
>
<!-- ****  ElementType   ***** -->
<!ELEMENT ElementType (datatype | description | AttributeType | attribute | element | group)* >
<!ATTLIST ElementType
   name  ID                       #REQUIRED
   model (open | closed)          #IMPLIED
   content (empty | textOnly | eltOnly | mixed) #IMPLIED
   order   (one | seq | many)     #IMPLIED
   dt:type %datatypes;            #IMPLIED
   dt:values  CDATA               #IMPLIED
   required  (yes | no)           #IMPLIED
   xmlns:dt CDATA #FIXED "urn:schemas-microsoft-com:datatypes"
>
<!-- ****  group   ***** -->
<!ELEMENT group  (group | element | description)*>
<!ATTLIST group
   minOccurs   CDATA              #IMPLIED
   maxOccurs   CDATA              #IMPLIED
   order      (one | seq | many)  #IMPLIED
>
<!-- ****  Schema   ***** -->
<!ELEMENT Schema  (AttributeType | ElementType | description )* >
<!ATTLIST Schema
   name  CDATA #IMPLIED
   xmlns:dt CDATA #FIXED "urn:schemas-microsoft-com:datatypes"
>


The XML Object Model in Internet Explorer 4.0

[The Internet Explorer 4.0 XML object model is obsolete, and is being replaced in favor of the new W3C-compliant XML DOM in the Internet Explorer 5 version of Msxml.dll.]

The XML object model in Microsoft® Internet Explorer 4.0 provides a means by which you can navigate and manipulate an XML document. An XML document is a tree-like structure, starting with the top-level element (its root) and branching out to its descendants. Being able to navigate this tree enables you to retrieve important information concerning the source document.

This topic explains how to access the XML object model from script in Internet Explorer 4.0. Information about the DOM interfaces can be found at XML DOM Reference. At the end of this section, you'll find an example of a JScript program that uses the XML object model to display an XML document in a Web page.

In accessing the XML object model, three objects are used: the XML Document, the XML Element, and the Element Collection.

The XML Document is an object representing an XML source document. That document consists of a root element (the top-level element) and its descendants (the other elements). An Element Collection is a group of sibling elements.

You create an XML document object by creating a new ActiveXObject:

var xml = new ActiveXObject("msxml");

The above code assigns the XML Document object to the variable xml.

Once you have created an XML Document object, you can access information concerning the object and manipulate the object by calling the following properties and methods. In the examples, xml is the document created above.

XML Document Properties and Methods

URL

Description: Returns the URL associated with the document.

Prototype: xml.URL

Example:

xml.URL = "http://Chein/_private/pdcxml.xml";

Note You can also use URL to load a new document based on another URL. The current document is destroyed and the new document is loaded in its place:

xml.URL = "http://Chein1/_private/newXML.xml";

root

Description: Returns the root element of the document.

Prototype: xml.root

Example:

var docroot = xml.root;

charset

Description: Returns a string that specifies the character set of the input document according to ISO standards.

Prototype: xml.charset

Note You can also assign a character set for the XML document.

version

Description: Returns the version of the XML specification being used.

Prototype: xml.version

doctype

Description: Returns the content specified in the !DOCTYPE element.

Prototype: xml.doctype

createElement()

Description: Creates a new element that can then be added as a child to another element in the tree. The first argument in the example below references the element type. A list of types and corresponding number references is provided in the explanation of the type method call.

Prototype: xml.createElement(elementType, newTagName)

Example:

var newElem = xml.createElement(0, "NEW-DESCRIPTION");

fileSize

Description: Returns the file size of the XML document.

Prototype: xml.fileSize

Note This property is documented in the C++ documentation, but has yet to be implemented.

fileModifiedDate

Description: Returns the date when the file was last modified.

Prototype: xml.fileModifiedDate

Note This property is documented in the C++ documentation, but has yet to be implemented.

fileUpdatedDate

Description: Returns the date when the file was last updated.

Prototype: xml.fileUpdatedDate

Note This property is documented in the C++ documentation, but has yet to be implemented.

mimeType

Description: Returns the MIME (Multipurpose Internet Mail Extension) type. MIME is a set of enhancements to SMTP allowing an Internet message to include a mixture of audio, image, video, and text components and to accommodate a variety of international character sets. MIME messages are identified by a MIME header in the top level of the STMP header. The MIME mechanisms are specified in the RFC 1341 document.

Prototype: xml.mimeType

Note This property is documented in the C++ documentation, but has yet to be implemented.

With access to these properties and methods, you can now specify the XML data file to be loaded by the XML Document object:

xml.URL = "http://Chein1/_private/pdcxml.xml7";

Likewise, using root you can now identify the document root of the XML document pointed to by the URL specified above:

var docRoot = xml.root

Having located the document root, it becomes easy to navigate the tree. The following properties and methods allow you access to the document root and all of its children. In the examples, elem represents an XML Element object.

XML Element Properties and Methods

type

Description: Returns the element type of the requested element:

0 - ELEMENT
1 - TEXT
2 - COMMENT
3 - DOCUMENT
4 - DTD

For Internet Explorer 4.0, the interesting element types are ELEMENT, TEXT, and COMMENT. In the JScript program at the end of this document, elem.type is used to differentiate between tag names and text.

Prototype: elem.type

Example:

if (elem.type == 0)
  return "ELEMENT";

tagName

Description: Returns the name of the tag as a string. The name is always in uppercase. The name of a comment element is returned as "!". The other META tags have names that correspond to the special character followed by the META tag name. For instance, the name of <?XML...> is "?XML".

Prototype: elem.tagName

Example:

if (elem.type == 0)  //if it is a tag
  return elem.tagName;  //return the tag name as a string

Note It is also possible to change a tag name by assigning a value to that property:

if (elem.tagName == "PAINTING")  //if a  element
  elem.tagName = "OIL-PAINTING";  //change its tag name to "OIL-PAINTING"

text

Description: Returns the content of text and comment elements. The content of a text element is the text between the tags. All markup between the tags is stripped. For instance, the contents of the following element, <TITLE>Review of <MOVIE>Titanic</MOVIE></TITLE>, would be "Review of Titanic". The content of a comment is the comment. For example, the following element, <! This is a comment/>, has the name "!" and the content "This is a comment".

Prototype: elem.text

Example:

if (elem.type == 1) //if the node is a text node
  return elem.text;

Note It is also possible to add to or change the text of an element by assigning a value to the text property:

if (elem.type == 1)
  elem.text = "This is an element";  //replaces the text of the element
                                     //with the string "This is an element"

addChild()

Description: Allows for the addition of children elements. The Element object passed to addChild is the element that is to become the new child. The index argument refers to the new element's relative position among its new siblings. "-1" must always be the final argument. All the children and descendants of this new child element remain attached as before. Any element can have only one parent element, so the previous parent will lose this child from its subtree. Note that it is possible to use this method to break off a subtree from one XML document and attach it to another XML document. However, there are some stringent restrictions on when this can be done:

There can be no outstanding references on collections on the child element or any of its descendants.

There can be no reference on any of the descendant elements of the child.

If these restrictions are not adhered to, an error will be returned. Finally, the implementation of breaking off a node that has many children and attaching it to a tree of another document is somewhat more expensive than breaking off a node and attaching it to another node of the same document.

Prototype: elem.addChild(elementObject, index, -1)

Example:

if (elem.children.item(0) != null)  //if the element has at least one child
  elem.addChild(elem.children.item(0).children.item(0),
      0, -1);  //add a descendant as a child

removeChild()

Description: Allows for the removal of children elements. The element remains in memory and can be reattached to a tree by using addChild. All the children and descendants of this child element remain attached as before. It is possible to use this method to break off a subtree from one XML document and attach it to another XML document. However, there are restrictions, as explained in addChild.

Prototype: elem.removeChild(childElement)

Example:

if (elem.children.item(0).length > 1)  //if the element has more than one child
  elem.removeChild(elem.children.item(1));  //remove the second child element

parent

Description: Returns the immediate ancestor of the requested element. Every element in the tree, except the document root, has a parent.

Prototype: elem.parent

Example:

if (elem != xml.root) //if the element is not the root element
  return elem.parent.tagName;

getAttribute()

Description: Returns the value of the specified attribute of the requested element. This value will always be returned as a string.

Prototype: elem.getAttribute(attributeName)

Example:

if (elem.getAttribute("font-style") == "italic")
  return "italic";

setAttribute()

Description: Allows for the setting of an attribute. The attribute's name and its new value are provided. The previous value of this attribute is lost. A private copy of the input value is made. The attribute value is always stored as a string. Therefore, the new attribute value should be passed as a string.

Prototype: elem.setAttribute(attributeName, attributeValue)

Example:

if (elem.getAttribute("font-style") == "italic")
  elem.setAttribute("font-style","normal");

removeAttribute()

Description: Allows for the removal of an attribute.

Prototype: elem.removeAttribute(attributeName)

Example:

if (elem.getAttribute("font-style") == "italic")
  elem.removeAttribute("font-style");

children

Description: Returns an enumeration of children elements of the requested element. The collection allows the application to make queries about the size of the collection as well as enumerate through the children or access any of them by index. If no children exist, elem.children returns null.

Prototype: elem.children

Example:

if (elem.children != null) // if the element has children
  return "yes";

The children of an element are manipulated as Element Collection objects. You can access the members of a collection by index and by tag name. If children are accessed by tag name and two or more children share that name, a specialized collection is created. The members of such a specialized collection can be accessed by index, just like the members of a general collection.

The item() method can be called to access and manipulate members of an Element Collection. The length property can be accessed to retrieve information concerning the length of an Element Collection.

Element Collection Properties and Methods

item()

Description: Returns the requested item or items from a collection. This method is fairly involved and can be used in different ways to retrieve members of a collection. If you specify solely an index, such as "0", the item at that index is returned. If you specify solely an element name, such as "TITLE", a collection of all elements with that specific name is returned. Note, if only one element of that name exists, it is returned as an XML element, not as a collection. If both an element name and an index are specified, the index number refers to the collection of elements of the specified name. In such a case, an element is returned.

Prototype: elem.children.item(index)
elem.children.item(elementName)
elem.children.item(elementName, index)

Example:

return elem.children.item(0).text;  //return the text of the first child element
return elem.children.item("TITLE")  //return a collection of <TITLE> elements
return elem.children.item("TITLE",3).text //return the text of the fourth <TITLE> element

length

Description: Returns the length of a collection.

Prototype: elem.children.length

Example:

for (i = 0; i < elem.children.length; i++)
  output_doc(elem.children.item(i),(indents + 1));

Sample JScript Program

Let's now take a look at a JScript program that uses the object model to reveal the contents of an XML document.

The script first creates an ActiveXObject representing an XML document and then passes the root of that document to output_doc(). This function then inserts the root element's tag name and contents into the HTML element with an ID value of "results," repeating the process for each element within the source document. The program results in the XML document being displayed in the HTML page.

Note The inability to enumerate attributes precludes the display of any attributes an element in the source document might have.

<SCRIPT LANGUAGE="JScript" FOR=window EVENT=onload>
  var indent_array = new String("                                                ");
  var str = "";
  var xml = new ActiveXObject("msxml");
  xml.URL = "http://Chein/_private/pdcxml.xml";
  var docroot = xml.root;
  output_doc(docroot,0);
  function output_doc(elem,indents)
  {
    var i;
if (elem.type == 0)  // 0 is a tagName
{
  document.all("results").insertAdjacentText("BeforeEnd",
  indent_array.substring(0,(4 * indents)) +
  "<" + elem.tagName + ">" + "\n");
  if (elem.children != null)
  {
  for (i = 0 ; i < elem.children.length ; i++)
output_doc(elem.children.item(i),(indents + 1));
  }
  document.all("results").insertAdjacentText("BeforeEnd",
  indent_array.substring(0,(4 * indents)) +
  "</" + elem.tagName + ">" + "\n");
    }
else if (elem.type == 1) // 1 is a text node
{
  document.all("results").insertAdjacentText("BeforeEnd",
    indent_array.substring(0,(4 * indents)) +
"\"" + elem.text + "\"\n");
}
else
  alert("unknown element type: " + elem.type);
  }
</script>

As you can see from the above script, the XML object model in Internet Explorer 4.0 makes it quite easy to write an application that can traverse an XML document.


Microsoft XML Parser in C++

[The Internet Explorer 4.0 XML object model is obsolete, and is being replaced in favor of the new W3C-compliant XML DOM in the Internet Explorer 5.0 version of Msxml.dll.]

About the Internet Explorer 4.0 C++ Parser

Internet Explorer 4.0 included an XML parser, written in C++. It was designed to be a high-performance, non-validating parser that supported most of the W3C XML specification.

The Internet Explorer 4.0 XML Object Model

The Internet Explorer 4.0 XML parser provides an object model for XML, giving developers programmatic access to structured data. The XML object model is based on the W3C standard Document Object Model (DOM). For more information about the XML object model supported by Internet Explorer 4.0, see The XML Object Model in Internet Explorer 4.0. For more information about the DOM, see http://www.w3.org/DOM/.


(Deprecated) Push Model XML Parser/Node Factory

[Deprecated. Do not use. Superseded by SAX2 API/MSXML 3.0]

This document defines an obsolete proprietary low-level interface for manipulating XML without having to build an in-memory tree representation of the entire XML document. It was intended to be a high performance XML parser, but has been completely superceded by the industry standard SAX2 API, which is available in MSXML v.3.0 and higher. It is highly recommended to use SAX2 API instead of NodeFactory interface. NodeFactory interface is deprecated and provided strictly "as is". No changes will be done to it, no support will be provided, and no future upgrades will be available. NodeFactory interface will be completely removed from the production version of MSXML version 3.0.


Introduction

[Deprecated. Do not use. Superseded by SAX2 API/MSXML 3.0]

This interface was designed in a lieu of an industry-standard API for customers who want to scan a chunk of XML to find a certain tag. For example, the CDF Viewer needs to scan every CDF file in a given folder for a LOGO tag that has an ICON attribute so it can display the appropriate icon.

The Push Model XML Parser consists of low-level interfaces that are designed for C programmers only. The Push Model XML Parser includes an IXMLParser interface, which is used to parse XML either from an Istream, or directly from an in-memory buffer or a given URL. The parser also includes a Node Factory callback interface, which the parser calls for each XML tag or attribute it finds in the source. The client implements its own Node Factory.

Alternatives

The Node Factory is not the best way for applications to deal with XML. The Node Factory has been replaced by the SAX2 API in MSXML version 3.0. Another supported industry standard API available in this version of MSXML is DOM—Document Object Model. The IDOMNode interface maintains an in-memory tree representation of an XML document and provides methods for navigating, updating, querying, and transforming the tree. The DOM also provides other features like DTD validation, XML Schema, and DataType support.


(Deprecated) IXMLParser Interface

[Deprecated. Do not use. Superseded by SAX2 API/MSXML 3.0]

The Parser takes XML input in a variety of ways (i.e. via a stream, via a URL to a document, or via text pushed to it), parses the XML and sends parse events to a NodeFactory. The parser is divided into two interfaces: an IXMLNodeSource, which defines the parse events and other information (such as position information for parse errors) that are sent to a NodeFactory. The IXMLParser interface inherits from IXMLNodeSource and adds methods to define the XML source (stream, URL, or pushed text), set NodeFactories, and deal with security and state reporting issues.

interface IXMLParser : IXMLNodeSource
{
    HRESULT SetURL(
        [in] const WCHAR* pszBaseUrl,
        [in] const WCHAR* pszRelativeUrl,
        [in] BOOL fAsync);

    HRESULT Load(
        [in] BOOL fFullyAvailable,
        [in] IMoniker *pimkName,
        [in] LPBC pibc,
        [in] DWORD grfMode);

    HRESULT SetInput(
        [in] IUnknown *pStm);

    HRESULT PushData(
        [in] const char* pData,
        [in] ULONG ulChars,
        [in] BOOL fLastBuffer);

    HRESULT LoadDTD(
        [in] const WCHAR* pszBaseUrl,
        [in] const WCHAR* pszRelativeUrl);

    HRESULT LoadEntity(
        [in] const WCHAR* pszBaseUrl,
        [in] const WCHAR* pszRelativeUrl,
        [in] BOOL fpe);

    HRESULT ParseEntity(
        [in] const WCHAR* pwcText,
        [in] ULONG ulLen,
        [in] BOOL fpe);

    HRESULT ExpandEntity(
        [in] const WCHAR* pwcText,
        [in] ULONG ulLen);

    HRESULT SetRoot(
        [in] PVOID pRoot);

    HRESULT GetRoot(
        [in] PVOID* ppRoot);

    HRESULT Run(
        [in] long lChars);

    HRESULT GetParserState();

    HRESULT Suspend();

    HRESULT Reset();

    HRESULT SetFlags(
        [in] ULONG iFlags);

    HRESULT SetSecureBaseURL(
        [in] const WCHAR* pszBaseUrl);

    HRESULT GetSecureBaseURL(
        [out] const WCHAR** ppwcBuf);
};

The parser is multithread safe. In other words, it is safe to call these methods from any thread. They are protected by a critical section.

SetURL

This is one of four different methods for providing input to the parser. The other methods are Load, SetInput and PushData. You pass a base url, and a relative url. For example, the base could be http://www.microsoft.com/xml/test.htm and the relative url could be test.xml and the resulting file that would be loaded is http://www.microsoft.com/xml/test.xml. The presence of test.htm in this example is completely meaningless. It is designed to show how this method is useful when you have the base url of an HTML page and you want to fetch an xml document relative to that without doing with any string manipulation. Other than that the "test.htm" is completely arbitrary and has nothing else to do with the function of the XML parser.

The pszBaseUrl is optional and is only there so that you don't have to resolve relative URLs yourself. Passing a full URL for the pszRelativeURL overrides the pszBaseUrl, and the pszBaseUrl can be NULL. But you cannot pass a NULL for the pszRelativeURL. You can pass a directory as the base so long as there is a trailing "/". For example: http://www.microsoft.com/xml/.

File URLs have some additional behavior. The base URL file://central/ where central is the name of a server is not valid. You must also specify the name of the "share" on that server, for example: file://central/public/. Relative file URLs without a base URL load the file from the "current directory" for that process.

The async flag controls whether an http request is handled synchronously or asynchronously. If it is async, you will get E_PENDING from Run and you will need to make sure you pump your message queue. When data becomes available, the parser will be notified by URLMON (via the message queue) and will parse the new data and your NodeFactory will continue to be called.

Load

This method corresponds to the Load method in IPersistMoniker. The parser will call BindToStorage to get an IStream and load the XML associated with the given moniker. You can also call GetURL to get the URL representation of the given Moniker. This method will not work on a moniker created via CreateFileMoniker. Use CreateURLMoniker instead.

SetInput

Use this method or if you already have an IStream containing XML. If the stream returns E_PENDING then the parser returns E_PENDING from Run(). The caller then must call Run again at a later time when more data is available.

PushData

The lowest level way to provide input is as a raw buffer of bytes. The buffer is not NULL terminated. You call PushData with each buffer. Run() will continue to return E_PENDING until call PushData with the lastBuffer argument set to TRUE. It is also ok for an XML token to span multiple buffers. For example, the first buffer might end with "<!-- foo" in which case the parser will return E_PENDING, then if the next buffer starts with "bar--->" then the parser will complete the token and call the NodeFactory with the combined COMMENT text "<!---foobar--->".

When you call PushData with lastBuffer set to TRUE, you should not push any more data unless you first Reset the parser. if you do the behavior is undefined.

LoadDTD

Once you have started parsing some XML you may want to then also go fetch an external DTD. For example, if the XML document contains: <!DOCTYPE foo SYSTEM "somedtd.dtd"> then you can call LoadDTD on the parser to load that DTD. Your factory will then be called to handle all the DTD nodes, just like it does for a DTD internal subset.

LoadEntity

DTDs can define external entities <!ENTITY bar SYSTEM "ent.xml">. This method is used to go fetch those entities. The fpe argument specified that this is a parameter entity.

ParseEntity

This method is used to parse the value of an internal entity reference <!ENTITY bar "<x>internal</x>">. CreateNode calls will be made for the contents of the entity.

ExpandEntity

This method is used to expand an entity reference. For example, when a parameter entity is used in a DTD, %foo; this method can be called to provide the entity replacement text that the parser should insert into the input stream before continuing to parse.

SetRoot, GetRoot

SetRoot provides the pNodeParent argument for all root level CreateNode calls. You need to manage any reference counting that may be required on this object, and therefore may need to use GetRoot after the parsing is finished.

Run

Run parses the specified amount of XML (in characters), then it returns E_PENDING if it isn't finished or it returns S_OK when it reaches the end of the input. An amount of -1 means parse as much as it can get from the input stream. If you are using PushData, the Run method returns E_PENDING until you push the last buffer and then after that it will return S_OK. The number given to Run is just a hint—if there are not the specified number of characters left in the input buffer then Run will still succeed.

If you started an asynchronous download Run(-1) will return E_PENDING, but then so long as your application has a message loop, the download and parsing will occur in the background as data becomes available from URLMON. Then you can check GetParserState from time to time and at some point the parser will no longer return XMLPARSER_BUSY. If you want to monitor the progress you can use the Load method and register your own IBindStatusCallback and watch the OnProgress calls.

GetParserState

GetParserState returns one of the following values indicating the state of the parser:

typedef enum
{
    XMLPARSER_IDLE,   // The parser is in the Reset state
    XMLPARSER_WAITING,   // The input stream returned E_PENDING
    XMLPARSER_BUSY,   // There is data available for parsing
    XMLPARSER_ERROR,   // The parser found an error
    XMLPARSER_STOPPED,   // Abort was called
    XMLPARSER_SUSPENDED,// Suspend was called
} XML_PARSER_STATE;

If the parser found an error, then this takes precedence over subsequent calls to Abort() or Suspend() so you will still get XMLPARSER_ERROR in this case. Also, if you aborted the parser this takes precedence over Suspend() so you will still get XMLPARSER_STOPPED even though the parser is also suspended.

Suspend

The parser can also be suspended at any time and resumed by calling Run again. This helps clients tweak their performance by doing just-in-time parsing.

Reset

The Reset method puts the parser back into the initial state so you can load another XML file. Reset also resets the root object and the root NodeFactory and all other Node Factories that may be in the current parser context.

SetFlags

For setting the flags defined in IXMLNodeSource::GetFlags.

SetSecureBaseURL,GetSecureBaseURL

The secure base URL is used to stop cross-domain data access. Once the secure base URL is set all XML loads must come from the same domain (unless the user has explicitly enabled cross-domain data access). For example:

pParser->SetSecureBaseURL(L"http://microsoft/");
pParser->SetURL(NULL, L"http://www.microsoft.com",FALSE);

will return an E_ACCESSDENIED error.


Encoding Support

[Deprecated. Do not use. Superseded by SAX2 API/MSXML 3.0]

For all the different methods of input, the same encoding support is provided. To provide Unicode data the input stream or buffer must start with a Unicode Byte Order mark 0xFFFE or 0xFEFF - depending on whether the Unicode is little or big endian. If it doesn't start with a byte order mark it is assumed to be UTF-8. To specify something other than UTF-8 use an xml declaration with an encoding attribute at the beginning of the input. For example, the following specifies a common encoding: for Japanese on Microsoft platforms:

<?xml version="1.0" encoding="shift_jis"?>

The XML Parser recognizes UTF-8 and UCS-2 directly. For all other character sets, MLang is used to perform conversions.

Example Parser Usage

An example usage if you have an IStream and a NodeFactory already is as follows:

HRESULT ParseXML{IStream* input, IXMLNodeFactory* f)
{
    IXMLParser* xp;
    HRESULT hr;
    checkhr(CoCreateInstance(CLSID_XMLParser, NULL, CLSCTX_INPROC_SERVER,
      IID_IXMLParser, (void**)&xp);
    checkhr(xp->SetInput(input));
    checkhr(xp->SetFactory(f));
    hr = xp->Run(-1);
    xp->Release();
    return hr;
}


(Deprecated) IXMLNodeSource Interface

[Deprecated. Do not use. Superseded by SAX2 API/MSXML 3.0]

The IXMLNodeSource interface is defined as a base class for the IXMLParser so that other components can drive a NodeFactory without having to implement the full IXMLParser interface. For example, you might want to drive a NodeFactory from a component that extracts the data out of a database.

If a NodeFactory needs methods not defined here but on IXMLParser, it will have to do a QueryInterface for IXMLParser to see if it is really being driven by a parser or not, and if not, fail gracefully.

interface IXMLNodeSource : IUnknown
{
    HRESULT SetFactory(
        [in] IXMLNodeFactory* pNodeFactory);

    HRESULT GetFactory(
        [out] IXMLNodeFactory** ppNodeFactory);

    HRESULT Abort(
        [in] BSTR bstrErrorInfo);

    ULONG GetLineNumber();

    ULONG GetLinePosition();

    ULONG GetAbsolutePosition();

    HRESULT GetLineBuffer(
        [out] const WCHAR** ppwcBuf,
        [out] ULONG* pulLen,
        [out] ULONG* pulStartPos);

    HRESULT GetLastError();

    HRESULT GetErrorInfo(
        [out] BSTR* pbstrErrorInfo);

    ULONG GetFlags();

    HRESULT GetURL(
        [out] const WCHAR** ppwcBuf);
};

SetFactory, GetFactory

Use SetFactory to plug in your customer NodeFactory. The NodeSource (or parser) delegates to your NodeFactory for each element it finds in the XML source. Use GetFactory to get back your NodeFactory later. See NodeFactory Interface for details.

Abort

The NodeSource (or parser) can be stopped by the caller or by the NodeFactory at anytime by calling Abort. If the NodeFactory calls abort, it can also provide a BSTR that contains more information about why the node factory is aborting. This will then be returned from GetErrorInfo.

GetLineNumber, GetLinePosition, GetAbsolutePosition, GetLastError, GetErrorInfo

The current line position information can be queried at any time. The IXMLNodeFactory is given a pointer to the IXMLParser so that it can use these functions also. GetAbsolutePosition returns the absolute offset from the beginning of the input stream (or buffer) (after UTF-8 decoding has been applied). Obviously, other types of NodeSources may not be able to provide this information. GetLastError returns the last HRESULT that was returned from other methods and GetErrorInfo returns a BSTR which contains more descriptive information about what went wrong (the same BSTR that was provided to the Abort method).

GetLineBuffer

Returns the current line of XML that is being parsed. This is useful when there is an error to see the original decoded XML that contained the error. ppwcBuf is a pointer into the decoded XML buffer. pulLen is the length of the buffer. pulStartPos is the position of the error inside the buffer. Obviously, other types of NodeSources may not be able to provide this information. This is not guaranteed to always return something. If no parse has been started or if there was an error fetching any XML data, it returns an empty buffer of length 0.

GetFlags

The following flags can combined, using OR, to control how the parser works. By default none of these flags are set.

typedef enum
{
    XMLFLAG_FLOATINGAMP       = 1,
    XMLFLAG_SHORTENDTAGS      = 2,
    XMLFLAG_CASEINSENSITIVE   = 4,
    XMLFLAG_NONAMESPACES      = 8,
    XMLFLAG_NOWHITESPACE      = 16,
    XMLFLAG_IE4QUIRKS         = 32,
    XMLFLAG_NODTDNODES        = 64,
    XMLFLAG_IE4COMPATIBILITY  = 255,
} XML_PARSER_FLAGS;
XMLFLAG_FLOATINGAMP
This is an IE4 compatibility flag that allows your PCDATA to contain unescaped ampersand characters.
XMLFLAG_SHORTENDTAGS
This is an IE4 compatibility flag that allows short end tags </>.
XMLFLAG_CASEINSENSITIVE
This makes the parser case insensitive but the text you get is still whatever was in the original file. It does NOT fold to upper case since this is inefficient and is not always needed.
XMLFLAG_NONAMESPACES
Whether the namespace declarations and tag name syntax are recognized.
XMLFLAG_NOWHITESPACE
Whether to return WHITESPACE nodes.
XMLFLAG_IE4QUIRKS
This is a miscellaneous flag for other IE4 compatibility things, as follows:
<x=> | This syntax error was allowed under IE4.
<!-- x -- y --> | Invalid "--" characters inside comments.
xml-space | Instead of xml:space.
<x y="1"z="2/> | Whitespace between attributes not needed.
<x a="1" a="2"/> | Duplicate attributes.
<!-- top of doc --> | Comments before the <?xml declaration.
<x y="<z>"/> | The '<' character inside an attribute.
<x>]]></x> | ']]>' sequence inside PCDATA.
XMLFLAG_NODTDNODES
This turns off parsing of any Internal DTD Subset so that NodeFactories who don't care about DTDs do not need to deal with them at all.
XMLFLAG_IE4COMPATIBILITY
This is an OR of all of the above, which results in a pretty good overall IE4 compatibility.

GetURL

For IXMLParser NodeSources, this returns the URL provided in the IXMLParser SetURL() or Load() methods. Other NodeSources may now return anything here.


(Deprecated) IXMLNodeFactory Interface

[Deprecated. Do not use. Superseded by SAX2 API/MSXML 3.0]

The NodeFactory interface is implemented by you and plugged into the NodeSource (or IXMLParser). In the case of the IXMLParser, calls to the NodeFactory are then made as it parses the XML document.

interface IXMLNodeFactory : IUnknown
{
    HRESULT NotifyEvent(
            [in] IXMLNodeSource* pSource,
            [in] XML_NODEFACTORY_EVENT iEvt);

    HRESULT BeginChildren(
            [in] IXMLNodeSource* pSource,
            [in] XML_NODE_INFO* pNodeInfo);

    HRESULT EndChildren(
            [in] IXMLNodeSource* pSource,
            [in] BOOL fEmpty,
            [in] XML_NODE_INFO* pNodeInfo);

    HRESULT Error(
            [in] IXMLNodeSource* pSource,
            [in] HRESULT hrErrorCode,
            [in] USHORT cNumRecs,
            [in] XML_NODE_INFO __RPC_FAR **aNodeInfo);

    HRESULT CreateNode(
            [in] IXMLNodeSource __RPC_FAR *pSource,
            [in] PVOID pNodeParent,
            [in] USHORT cNumRecs,
            [in] XML_NODE_INFO __RPC_FAR **aNodeInfo);
};

Node Info

And to help understand the above interface you really need to see the XML_NODE_INFO structure also:

typedef struct _XML_NODE_INFO
{
    DWORD           dwSize;
    DWORD           dwType;
    DWORD           dwSubType;
    BOOL            fTerminal;
    WCHAR*          pwcText;
    ULONG           ulLen;
    ULONG           ulNsPrefixLen;
    PVOID           pNode;
    PVOID           pReserved;
} XML_NODE_INFO;
DWORD dwSize
The size of this structure in bytes.
DWORD dwType
The node type.
DWORD dwSubType
The node sub type.
BOOL fTerminal
True if this node cannot have any children and so BeginChildren and EndChildren are guaranteed not to be called for this node.
const WCHAR* pwcText
This is either a tag name, or a PCDATA text value. The lifetime of this pointer is the lifetime of the CreateNode call. Note that Element/Attribute/PI tag names or certain attribute values (of type ID, NMTOKEN, ENTITY, or NOTATION), may have namespace prefixes.
ULONG ulLen
The length of the element or attribute name.
ULONG ulNsPrefixLen
The length of the namespace prefix, when present
PVOID pNode
This field can be used by the NodeFactory to RETURN an object representing the node. Since this is PVOID, the NodeFactory can build ANY tree representation it wants—even a raw struct hierarchy.
PVOID * pReserved
For private communication between factories.

This is essentially a callback interface so that custom node factories can build different kinds of object hierarchies that reflect the XML document. The NodeFactory doesn't have to create anything, though—it could just be searching for something in the XML document.

If a non-NULL pointer is returned in the pNode field of the NodeInfo struct then this will be passed back in by the parser as the pNodeParent in subsequent calls to CreateNode for the children of that node. In other words, the XML Parser maintains the parse context for you and passes in the appropriate parent pointer based on what it finds in the XML. The pNodeParent for root level nodes is equal to the argument provided in the SetRoot call on the Parser.

If your Node objects need to be reference counted, then you need to AddRef them before returning them, and then Release them in EndChildren. But you also need to Release all nodes provided in the Error method in case the parser stops on an XML syntax error. The parser guarantees to call Error in all cases, so you do not have to worry about leaking nodes.

NotifyEvent

Convenience method that tells the NodeFactory where in the XML document it is. Possible events are:

XMLNF_STARTDOCUMENT
This event is fired at the start of the document.
XMLNF_STARTDTD
The parser is about to start parsing a DTD file.
XMLNF_ENDDTD
The DTD is finished.
XMLNF_STARTDTDSUBSET
About to start the DTD internal subset.
XMLNF_ENDDTDSUBSET
The internal subset is finished.
XMLNF_ENDPROLOG
This event is fired just prior to the CreateNode call for the Root element for the document. In other words, all the initial XML declarations, PIs and Comments are done, and now we are about to get into the real data.
XMLNF_STARTENTITY
Called prior to ENDENTITY. This happens when you call ParseEntity on the IXMLParser.
XMLNF_ENDENTITY
Used in conjunction with ParseEntity. This indicates the entity is finished.
XMLNF_ENDDOCUMENT
At the end of the document.
XMLNF_DATAAVAILABLE
A regular event to tell the node factory more data has just arrived.

BeginChildren

This method is called when a node may contain children. For example the following node may contain children.

<foo name="123">

But nodes that are empty such as:

<foo name="123"/>

comment nodes, CDATA nodes, and text nodes will not have a BeginChildren call. You can tell if a node was empty at EndChildren time by checking the Boolean argument fEmpty.

EndChildren

This method is called when all the subelements of the given element are complete. In other words the matching end tag </FOO> has been reached. This is also called if the tag is an empty tag <FOO ... /> in which case the fEmpty argument is set to TRUE in case the NodeFactory needs to distinguish between this case and <FOO></FOO>. This method is not called for terminal nodes.

Error

This method is called when the parser runs into an error in the XML document. The parser will stop at this point and return the HRESULT error code to the caller. The NodeFactory can call back to the parser to get more information about the error.

CreateNode

CreateNode is the main method that gets called during parsing for every element.

IXMLNodeSource* pNodeSource
The node source is passed into each node factory call so that the node factory can call back and get important information—like the current line number, or to stop the parser.
PVOID* pNodeParent
This is the parent object of the node being created. This parent pointer was returned from a prior CreateNode call, or is the root object provided using the parser SetRoot method.
USHORT cNumRecs
Number of XML_NODE_INFO structs
XML_NODE_INFO **aNodeInfo
Pointer to an array of pointers to XML_NODE_INFO structures. The first member is the node information for the elements. The second through last members of the array contain attribute name nodes, followed by attribute value nodes. I.e., if we have <a y="2" x="foo&bar;blech">, then the array looks like:
Element "a"
Attribute "y"
#PCDATA "2"
Attribute "x"
#PCDATA "foo"
EntityRef "bar"
#PCDATA "more...."
HRESULT
So for example, given the following XML fragment:
<item id="FOO" ms:price="20" title="BAR &foo;" xmlns:price="URIXXX" >
  <value>The quick brown fox</value>
  <dsig id="1412231"/>
</item>

The following sequence of calls will be made on the node factory (indented for readability only—does not imply recursion):

CreateNode XML_ELEMENT: [item]
           XML_ATTRIBUTE: [id]
           XML_PCDATA: [FOO]
           XML_ATTRIBUTE: [ms][price]
           XML_PCDATA: [20]
           XML_ATTRIBUTE: [title]
           XML_PCDATA: [BAR ]
           XML_ENTITYREF: [foo]
           XML_ATTRIBUTE: [xmlns][price]
           XML_PCDATA: [URIXXX]
BeginChildren
  CreateNode XML_WHITESPACE: 0D0A09 (fTerminal=TRUE)
  CreateNode XML_ELEMENT: [value]
  BeginChildren
    CreateNode XML_PCDATA: [The quick brown fox](fTerminal=TRUE)
  EndChildren
  CreateNode XML_WHITESPACE: 0D0A09 (fTerminal=TRUE)
  CreateNode XML_ELEMENT: [dsig]
             XML_ATTRIBUTE: [id]
             XML_PCDATA: [1412231]
  EndChildren (fEmpty=TRUE)
  CreateNode XML_WHITESPACE: 0D0A (fTerminal=TRUE)
EndChildren

Note the following:

  • When fTerminal is true there is no BeginChildren or EndChildren. This happens when the node type implies that there can never be any child nodes according to the XML language spec.
  • Empty tags (like the dsig tag) do not have a BeginChildren, but they do have an EndChildren which specifies the fEmpty argument as TRUE.
  • XML_ATTRIBUTE nodes can have multiple child nodes as shown in the case where the title attribute contains both XML_PCDATA and an XML_ENTITYREF node.
  • XML_WHITESPACE nodes can usually be entirely ignored by your node factory (which is why they are different from XML_PCDATA) unless your node factory cares about preserving the original document format.


Node Types Reference

[Deprecated. Do not use. Superseded by SAX2 API/MSXML 3.0]

This is the complete set of node types and when they occur. There are two main types of nodes, those that can have children (containers) and those that cannot (terminal nodes). The second column in the tables below show the possible subtypes that can appear for that given node type. If the subtype is not one of those listed then it will be zero.


Containers

[Deprecated. Do not use. Superseded by SAX2 API/MSXML 3.0]
XML_ELEMENT
At the start of a new element <foo.….
XML_ATTRIBUTE
At the start of a new attribute FOO="…." Possible subtypes are:
XML_VERSION | Version attribute of xml declaration.
XML_ENCODING | Encoding attribute of xml declaration.
XML_STANDALONE | Standalone attribute of xml declaration.
XML_NS | The special xmlns:foo attribute. (Note: might be just xmlns=).
XML_NS | The special xmlns:foo attribute. (Note: might be just xmlns=).
XML_XMLSPACE | The special xml:space attribute.
XML_XMLLANG | The special xml:lang attribute.
XML_DTDATTRIBUTE
For attributes inside DTD elements including <!DOCTYPE:
XML_SYSTEM | SYSTEM literal
XML_PUBLIC | PUBLIC literal
XML_NDATA | The NDATA attribute on an <!ENTITY declaration.
XML_PI
The start of a <?foo with text equal to PI name.
XML_XMLDECL
This the special <?xml ... PI. In this case the parser parses the version, encoding, and standalone attributes, and passes these as special node types. See XML_VERSION, XML_ENCODING and XML_STANDALONE below.
XML_DOCTYPE
This is the special <!DOCTYPE declaration. The text is the doctype name, and this will be followed by an optional XML_PUBLIC or XML_SYSTEM node and an optional XML_DTDSUBSET. If there is a 'PUBLIC' keyword, you will get XML_PUBLIC with the text of the public id, followed by XML_SYSTEM with the text of the system literal.
XML_ENTITYDECL
<!ENTITY ... This will be followed by STRING and NAME nodes. If the subtype is XML_ELEMENTDECL, then this is a parameter entity declaration.
XML_ELEMENTDECL
<!ELEMENT ... This will be followed by content model nodes, XML_GROUP, XML_NAME, XML_CHOICE, XML_SEQUENCE, XML_STAR, XML_PLUS, XML_QUESTIONMARK, etc.
XML_ATTLISTDECL
<!ATTLIST ... This will be followed by STRING and NAME nodes.
XML_NOTATION
<!NOTATION ... This will be followed by STRING and NAME nodes.
XML_GROUP
The ( ... ) grouping in content models.
XML_INCLUDESECT
<!INCLUDE[.., conditional section in a DTD. The children of this will be the DTD declarations in this include section.


Terminal Nodes

[Deprecated. Do not use. Superseded by SAX2 API/MSXML 3.0]
XML_PCDATA
The text inside a node or an attribute.
XML_CDATA
The text inside <![CDATA[...]]>
XML_IGNORESECT
<!IGNORE[.., conditional section in a DTD. The text value of this node type is the entire contents of the section of the DTD that is being ignored.
XML_COMMENT
The text inside '<!--' and '-->'
XML_ENTITYREF
A named entity node, &foo;
XML_WHITESPACE
White space between elements.
XML_NAME
General name for certain attribute values based on DTD declarations.
XML_NMTOKEN
General name token for certain typed attribute values or DTD declarations.
XML_STRING
General quoted string literal in DTD declarations.
XML_PEREF
%foo;
XML_MODEL
The pieces of a content model in an element declaration. Possible subtypes are:
XML_EMPTY | keyword EMPTY
XML_ANY | keyword ANY
XML_MIXED | keyword #PCDATA
XML_EMPTY | keyword EMPTY
XML_SEQUENCE ,
XML_CHOICE |
XML_STAR +
XML_PLUS *
XML_QUESTIONMARK ?
XML_ATTDEF
Name of attribute being defined in an ATTLIST declaration.
XML_ATTTYPE
The type of an attribute being defined. Possible subtypes are:
XML_AT_CDATA | Attribute is just raw text. No particular type.
XML_AT_ID | Attribute is supposed to contain an ID that is unique to the document.
XML_AT_IDREF | Attribute will reference another attribute of type ID.
XML_AT_IDREFS | Attribute will contain multiple ID references separated by spaces.
XML_AT_ENTITY | Attribute is the name of an unparsed entity. An unparsed entity is an entity declared with the NDATA keyword.
XML_AT_ENTITIES | Attribute points to multiple unparsed entities.
XML_AT_NMTOKEN | Attribute value must comply with rules for parsing name tokens.
XML_AT_NMTOKENS | Attribute value contains multiple name tokens.
XML_AT_NOTATION | Attribute value points one or more declared notations.
XML_ATTPRESENCE
Defines the presence rules for the attribute. Possible subtypes are:
XML_AT_REQUIRED | Attribute is required to be present in the data.
XML_AT_IMPLIED | Attribute is optional.
XML_AT_FIXED | Attribute has a fixed value and is therefore typically not present in the instance data.
XML_DTDSUBSET
This marks the beginning of an internal subset containing DTD information.


Error Codes

[Deprecated. Do not use. Superseded by SAX2 API/MSXML 3.0]

Possible error codes include S_OK, E_PENDING, E_FAIL, E_OUTOFMEMORY and the following parser specific errors:

XML_E_FORMATINDEX_BADINDEX | 0xC00CE306 | The value passed in to formatIndex needs to be greater than 0.
XML_E_FORMATINDEX_BADFORMAT | 0xC00CE307 | Invalid format string.
XML_E_EXPECTED_TOKEN | 0xC00CE380 | Expected token %1 found %2.
XML_E_UNEXPECTED_TOKEN | 0xC00CE381 | Unexpected token %1.
XML_E_MISSINGEQUALS | 0xC00CE501 | Missing equals sign between attribute and attribute value.
XML_E_MISSINGQUOTE | 0xC00CE502 | A string literal was expected, but no opening quote character was found.
XML_E_COMMENTSYNTAX | 0xC00CE503 | Incorrect syntax was used in a comment.
XML_E_BADSTARTNAMECHAR | 0xC00CE504 | A name was started with an invalid character.
XML_E_BADNAMECHAR | 0xC00CE505 | A name contained an invalid character.
XML_E_BADCHARINSTRING | 0xC00CE506 | A string literal contained an invalid character.
XML_E_XMLDECLSYNTAX | 0xC00CE507 | Invalid syntax for an xml declaration.
XML_E_BADCHARDATA | 0xC00CE508 | An Invalid character was found in text content.
XML_E_MISSINGWHITESPACE | 0xC00CE509 | Required white space was missing.
XML_E_EXPECTINGTAGEND | 0xC00CE50A | The character '>' was expected.
XML_E_BADCHARINDTD | 0xC00CE50B | Invalid character found in DTD.
XML_E_BADCHARINDECL | 0xC00CE50C | An invalid character was found inside a DTD declaration.
XML_E_MISSINGSEMICOLON | 0xC00CE50D | A semicolon character was expected.
XML_E_BADCHARINENTREF | 0xC00CE50E | An invalid character was found inside an entity reference.
XML_E_UNBALANCEDPAREN | 0xC00CE50F | Unbalanced parentheses.
XML_E_EXPECTINGOPENBRACKET | 0xC00CE510 | An opening '[' character was expected.
XML_E_BADENDCONDSECT | 0xC00CE511 | Invalid syntax in a conditional section.
XML_E_INTERNALERROR | 0xC00CE512 | Internal error.
XML_E_UNEXPECTED_WHITESPACE | 0xC00CE513 | Whitespace is not allowed at this location.
XML_E_INCOMPLETE_ENCODING | 0xC00CE514 | End of file reached in invalid state for current encoding.
XML_E_BADCHARINMIXEDMODEL | 0xC00CE515 | Mixed content model cannot contain this character.
XML_E_MISSING_STAR | 0xC00CE516 | Mixed content model must be defined as zero or more('*').
XML_E_BADCHARINMODEL | 0xC00CE517 | Invalid character in content model.
XML_E_MISSING_PAREN | 0xC00CE518 | Missing parenthesis.
XML_E_BADCHARINENUMERATION | 0xC00CE519 | Invalid character found in ATTLIST enumeration.
XML_E_PIDECLSYNTAX | 0xC00CE51A | Invalid syntax in PI declaration.
XML_E_EXPECTINGCLOSEQUOTE | 0xC00CE51B | A single or double closing quote character (\' or \") is missing.
XML_E_MULTIPLE_COLONS | 0xC00CE51C | Multiple colons are not allowed in a name.
XML_E_INVALID_DECIMAL | 0xC00CE51D | Invalid character for decimal digit.
XML_E_INVALID_HEXIDECIMAL | 0xC00CE51E | Invalid character for hexadecimal digit.
XML_E_INVALID_UNICODE | 0xC00CE51F | Invalid Unicode character value for this platform.
XML_E_WHITESPACEORQUESTIONMARK | 0xC00CE520 | Expecting whitespace or '?'.
XML_E_SUSPENDED | 0xC00CE550 | The parser is suspended.
XML_E_STOPPED | 0xC00CE551 | The parser is stopped.
XML_E_UNEXPECTEDENDTAG | 0xC00CE552 | End tag was not expected at this location.
XML_E_UNCLOSEDTAG | 0xC00CE553 | The following tags were not closed: %1.
XML_E_DUPLICATEATTRIBUTE | 0xC00CE554 | Duplicate attribute.
XML_E_MULTIPLEROOTS | 0xC00CE555 | Only one top level element is allowed in an XML document.
XML_E_INVALIDATROOTLEVEL | 0xC00CE556 | Invalid at the top level of the document.
XML_E_BADXMLDECL | 0xC00CE557 | Invalid xml declaration.
XML_E_MISSINGROOT | 0xC00CE558 | XML document must have a top level element.
XML_E_UNEXPECTEDEOF | 0xC00CE559 | Unexpected end of file.
XML_E_BADPEREFINSUBSET | 0xC00CE55A | Parameter entities cannot be used inside markup declarations in an internal subset.
XML_E_PE_NESTING | 0xC00CE55B | The replacement text for a parameter entity must be properly nested with parenthesized groups.
XML_E_INVALID_CDATACLOSINGTAG | 0xC00CE55C | The literal string ']]>' is not allowed in element content.
XML_E_UNCLOSEDPI | 0xC00CE55D | Processing instruction was not closed.
XML_E_UNCLOSEDSTARTTAG | 0xC00CE55E | Element was not closed.
XML_E_UNCLOSEDENDTAG | 0xC00CE55F | End element was missing the character '>'.
XML_E_UNCLOSEDSTRING | 0xC00CE560 | A string literal was not closed.
XML_E_UNCLOSEDCOMMENT | 0xC00CE561 | A comment was not closed.
XML_E_UNCLOSEDDECL | 0xC00CE562 | A declaration was not closed.
XML_E_UNCLOSEDMARKUPDECL | 0xC00CE563 | A markup declaration was not closed.
XML_E_UNCLOSEDCDATA | 0xC00CE564 | A CDATA section was not closed.
XML_E_BADDECLNAME | 0xC00CE565 | Declaration has an invalid name.
XML_E_BADEXTERNALID | 0xC00CE566 | External ID is invalid.
XML_E_BADELEMENTINDTD | 0xC00CE567 | An XML element is not allowed inside a DTD.
XML_E_RESERVEDNAMESPACE | 0xC00CE568 | The namespace prefix is not allowed to start with the reserved string "xml".
XML_E_EXPECTING_VERSION | 0xC00CE569 | The 'version' attribute is required at this location.
XML_E_EXPECTING_ENCODING | 0xC00CE56A | The 'encoding' attribute is required at this location.
XML_E_EXPECTING_NAME | 0xC00CE56B | At least one name is required at this location.
XML_E_UNEXPECTED_ATTRIBUTE | 0xC00CE56C | The specified attribute was not expected at this location. The attribute may be case sensitive.
XML_E_ENDTAGMISMATCH | 0xC00CE56D | End tag '%2' does not match the start tag '%1'.
XML_E_INVALIDENCODING | 0xC00CE56E | System does not support the specified encoding.
XML_E_INVALIDSWITCH | 0xC00CE56F | Switch from current encoding to specified encoding not supported.
XML_E_EXPECTING_NDATA | 0xC00CE570 | NDATA keyword is missing.
XML_E_INVALID_MODEL | 0xC00CE571 | Content model is invalid.
XML_E_INVALID_TYPE | 0xC00CE572 | Invalid type defined in ATTLIST.
XML_E_INVALIDXMLSPACE | 0xC00CE573 | XML space attribute has invalid value. Must specify 'default' or 'preserve'.
XML_E_MULTI_ATTR_VALUE | 0xC00CE574 | Multiple names found in attribute value when only one was expected.
XML_E_INVALID_PRESENCE | 0xC00CE575 | Invalid ATTDEF declaration. Expected #REQUIRED, #IMPLIED or #FIXED.
XML_E_BADXMLCASE | 0xC00CE576 | The name 'xml' is reserved and must be lower case.
XML_E_CONDSECTINSUBSET | 0xC00CE577 | Conditional sections are not allowed in an internal subset.
XML_E_CDATAINVALID | 0xC00CE578 | CDATA is not allowed in a DTD.
XML_E_INVALID_STANDALONE | 0xC00CE579 | The standalone attribute must have the value 'yes' or 'no'.
XML_E_UNEXPECTED_STANDALONE | 0xC00CE57A | The standalone attribute cannot be used in external entities.
XML_E_DOCTYPE_IN_DTD | 0xC00CE57B | Cannot have a DOCTYPE declaration in a DTD.
XML_E_MISSING_ENTITY | 0xC00CE57C | Reference to undefined entity.
XML_E_ENTITYREF_INNAME | 0xC00CE57D | Entity reference is resolved to an invalid name character.
XML_E_DOCTYPE_OUTSIDE_PROLOG | 0xC00CE57E | Cannot have a DOCTYPE declaration outside of a prolog.
XML_E_INVALID_VERSION | 0xC00CE57F | Invalid version number.
XML_E_DTDELEMENT_OUTSIDE_DTD | 0xC00CE580 | Cannot have a DTD declaration outside of a DTD.
XML_E_DUPLICATEDOCTYPE | 0xC00CE581 | Cannot have multiple DOCTYPE declarations.
XML_E_RESOURCE | 0xC00CE582 | Error processing resource '%1'.


XML Glossary

annotated schema
An XML-Data schema that has been extended for use with the XML returning query features of Shiloh to specify XML-to-relational mapping. Includes mapping of elements and attributes in the XML-Data schema to databases, instances, relations (tables or views), and columns, as well as the relationships required to express hierarchical relationships in XML. Qualified by "urn:schemas-microsoft-com:xml-sql".

See also schema.

attribute
XML structural construct. A name-value pair, separated by an equals sign, included inside a tagged element that modifies certain features of the element. All attribute values, including things like size and width, are in fact text strings and not numbers. For XML, all values must be enclosed in quotation marks.

You can declare attributes for an XML element type using an attribute list declaration.

Cascading Style Sheets (CSS)
Formatting descriptions that provide augmented control over presentation and layout of HTML and XML elements. CSS can be used for describing the formatting behavior of simply structured XML documents, but does not provide a display structure that deviates from the structure of the source data. See also Extensible Stylesheet Language.
CDF
See Channel Definition Format.
Channel Definition Format (CDF)
An XML-based data format used in Microsoft® Internet Explorer 4.0 and later to describe Active Channel™ content and desktop components.

CDF permits a Web publisher to offer frequently updated collections of information, or channels, enabling automatic delivery to compatible Web clients. The user only needs to choose the channel once, and scheduled deliveries of the channel information will be delivered to the client without further intervention.

character data
All the text content of an element or attribute that is not markup. XML differentiates this plain text from binary data. In the XML OM, character data is stored in text nodes, which are implemented as DOMText objects.
CIP
See Commerce Interchange Pipeline.
Commerce Interchange Pipeline (CIP)
An infrastructure used by Microsoft® Site Server Commerce Edition to exchange data between applications in XML over HTTP. It is an underlying mechanism used for tying Web payment technologies to applications.
CSS
See Cascading Style Sheets.
data island
An XML document (<XML> or <SCRIPT language="XML">). that exists within an HTML page. It allows you to script against the XML document without having to load it through script or through the <OBJECT> tag. Almost anything that can be in a well-formed XML document can be inside a data island.

HTML is used as the primary document or display format, and XML is used to embed data within the document.

Data Source Object
Provides a way to bind HTML controls directly to an XML data island. It assists developers in connecting to structured XML data and supplying it to an HTML page by using the data-binding facility of dynamic HTML.

XML Data Source Object allows you to work with data one node at a time, but you can also work with multiple nodes at a time, without having to walk the document tree. It binds the data to specific controls on the page and the controls are automatically populated with data from the Data Source Object.

document element
The element in an XML document that contains all other elements. It is the top-level element of an XML document and must be the first element in the document. There is exactly one document element, no part of which appears in the content of any other element. The document element represents the document as a whole; every other element represents a component of the document.

The terms root element and document element are interchangeable.

document entity
The starting-point for an XML parser. Unlike other entities, the document entity has no name and cannot be referenced. It is the entity in which the XML declaration and document type declaration can occur.
Document Object Model (DOM)
A platform- and language-neutral interface that allows programs and scripts to dynamically access and update the content, structure and style of documents. The Document Object Model provides a standard set of objects for representing HTML and XML documents, a standard model of how these objects can be combined, and a standard interface for accessing and manipulating them. Vendors can support the DOM as an interface to their proprietary data structures and APIs, and content authors can write to the standard DOM interfaces rather than product-specific APIs, thus increasing interoperability on the Web.
document type declaration
XML structural construct. Consists of markup code that indicates the grammar rules, or Document Type Definition (DTD), for the particular class of document. The document type declaration can also point to an external file that contains all or part of the DTD. It must appear following the XML declaration and preceding the document element. The syntax of the document type declaration is <!DOCTYPE content>.
Document Type Definition (DTD)
Can accompany a document, essentially defining the rules of the document, such as which elements are present and the structural relationship between the elements. It defines what tags can go in your document, what tags can contain other tags, the number and sequence of the tags, the attributes your tags can have, and optionally, the values those attributes can have.

DTDs help to validate the data when the receiving application does not have a built-in description of the incoming data. The DTD is declared within the document type declaration production of the XML file. With XML, however, DTDs are optional.

See also schema.

DOM
See Document Object Model.
DTD
See Document Type Definition.
EDI
See Electronic Data Interchange.
Electronic Data Interchange (EDI)
An existing format used to exchange data and support transactions. EDI transactions can be conducted only between sites that have been specifically set up with compatible systems. Proprietary EDI formats are more difficult to write than XML, and cannot be transmitted over HTTP like XML can.
element
XML structural construct. An XML element consists of a start tag, an end tag, and the information between the tags, which is often referred to as the contents. Each element has a type, identified by name, sometimes called its "generic identifier" (GI), and may have a set of attribute specifications. Each attribute specification has a name and a value. An instance of an element is declared using <element> tags.

Elements used in an XML file are described by a DTD or schema, either of which can provide a description of the structure of the data.

entity
XML structural construct. A file, database record, or another item that contains data. The primary purpose of an entity is to hold content—not structure, rules, or grammar. Each entity is identified by a unique name and contains its own content, from a single character inside the document to a large file that exists outside the document. The function of an XML entity is similar to that of a macro definition.

The entity can be referred to by an entity reference to insert the entity's contents into the tree at that point. Entity declarations occur in the DTD.

entity reference
XML structural construct. Acts as a placeholder for the content author, and the XML parser places the actual content at each reference site. To include an entity reference, you first insert an ampersand (&) and then enter the entity name followed by a semicolon (;), as follows: &YourEntityName;. Then, when the line is processed, the entity will be replaced with the entity's content.

It is used in much the same way as a macro.

Extensible Linking Language (XLL)
An XML vocabulary that provides links in XML similar to those in HTML but with more functionality. In addition to offering URL-based hyperlinks and anchors, XLL also supports linking to an arbitrary position in a document and multidirectional links. These features make XLL suitable for many new uses, as well as many traditional uses that are problematic in pure HTML, such as cross-references, footnotes, endnotes, and interlinked data. Designed for use in XML documents, links can exist at the object level rather than just at a page level.
Extensible Markup Language (XML)
A subset of SGML that is optimized for delivery over the Web, XML provides a uniform method for describing and exchanging structured data that is independent of applications or vendors.

The key is that with XML, the information is in the document, while the rendering instructions are elsewhere. In other words, content and presentation are separate. XML is the Web's language for data interchange and HTML is the Web's language for rendering.

At the time of this writing, XML 1.0 is a Worldwide Web Consortium Recommendation, which means that it is in the final stage of the approval process.

Extensible Stylesheet Language (XSL)
A language used to transform XML-based data into HTML or other presentation formats, for display in a Web browser. The transformation of XML into formats, such as HTML, is done in a declarative way, making it often easier and more accessible than through scripting. In addition, XSL uses XML as its syntax, freeing XML authors from having to learn another markup language.

In contrast to CSS, which "decorates" the XML tree with formatting properties, XSL transforms the XML tree into a new tree (the HTML), allowing extensive reordering, generated text, and calculations—all without modification to the XML source. The source can be maintained from the perspective of "pure content" and can simultaneously be delivered to different channels or target audiences by just switching style sheets.

XSL consists of two parts, a vocabulary for transformation and the XSL Formatting Objects.

invalid document
Documents that don't follow the XML tag rules. If a document has a DTD or schema, and it doesn't follow the rules defined in its DTD or schema, that document is invalid as well.
mixed content
Element types with mixed content are allowed to hold either character data alone or character data interspersed with child elements. In this case, the types of the child elements can be constrained, but not their order or their number of occurrences.
namespace
A mechanism that allows developers to uniquely qualify the element names and relationships and to make these names recognizable. By doing so, they can avoid name collisions on elements that have the same name but are defined in different vocabularies. They allow tags from multiple namespaces to be mixed, which is essential if data is coming from multiple sources. Namespaces ensure that element names do not conflict, and clarify who defined which term.

A namespace identifies an XML vocabulary defined within a URN. An attribute on an element, attribute, or entity reference associates a short name with the URN that defines the namespace; that short name is then used as a prefix to the element, attribute, or entity reference name to uniquely identify the namespace. Namespace references have scope. All child nodes beneath the node that specifies the namespace inherit that namespace. This allows nonqualified names to use the default namespace. See also RDF namespace.

notation
Tells the parser what type of object is being referenced. Usually refers to a data format of non-XML data, such as BMP. A notation identifies by name the format of unparsed entities, the format of elements that bear a notation attribute, or the application to which a processing instruction is addressed.
notation declaration
Tells the parser how to deal with a specific binary file type, as well as provides a name and an external identifier for a notation.

The notation declaration gives an internal name to an existing notation so that it can be referred to in attribute list declarations, unparsed entity declarations, and processing instructions.

The external identifier is used for the notation, which can allow an XML parser or its client application to locate a helper application capable of processing data in the given notation.

parsed entity
An entity that has content that is parsed and replaced with actual literal values. The result is called the replacement text. Parsed entities can only contain character data or XML markup.
processing instruction
XML structural construct. A mechanism for embedding information in a file intended for proprietary applications rather than the XML parser or browser. The XML parser passes the instructions to the application.

A processing instruction is a string of text included almost anywhere in an XML document's character data between <? and ?> marks. It begins with the name of the application for which the PI is intended, followed by the data for the instruction.

An example is the XMLdeclaration that begins every valid XML file:

<?xml version="1.0" standalone="yes" ?>
reference node
The reference node for a search context is the node that is the immediate parent of all nodes in the search context. Every search context has an associated reference node.
replacement text
The content of parsed entities, after replacement of character references and parameter-entity references.
SAX
See Simple API for XML.
schema
A formal specification of element names that indicates which elements are allowed in an XML document, and in what combinations. It also defines the structure of the document: which elements are child elements of others, the sequence in which the child elements can appear, and the number of child elements. It defines whether an element is empty or can include text. The schema can also define default values for attributes.

A schema is functionally equivalent to a DTD, but is written in XML. A schema also provides for extended functionality such as data typing, inheritance, and presentation rules. Consequently, the new schema languages are far more powerful than DTDs.

SGML
See Standard Generalized Markup Language.
Simple API for XML (SAX)
An XML API that allows developers to take advantage of event-driven XML parsing. Unlike the DOM specification, SAX doesn't require the entire XML file to be loaded into memory. SAX notifies you when certain events happen as it parses your document. When you respond to an event, any data you don't specifically store is discarded. If your document is very large, using SAX will save significant amounts of memory when compared to using DOM. This is especially true if you only need a few elements in a large document.
Simple Object Access Protocol (SOAP)
Provides an open, extensible way for applications to communicate using XML-based messages over the Web, regardless of what operating system, object model, or language they use. SOAP provides a way to use the existing Internet infrastructure to enable applications to communicate directly with each other without being unintentionally blocked by firewalls.
SOAP
See Simple Object Access Protocol.
Standard Generalized Markup Language (SGML)
The international standard for defining descriptions of structure and content of electronic documents. Despite its name, SGML is not a language in itself, but a way of defining languages that are developed along its general principles. SGML defines the way that a markup language is built by specifying the syntax and definitions for the elements and attributes that compose it.

XML is a subset of SGML designed to deliver SGML-type information over the Web, while HTML is an application of SGML.

template
The basis of the XML style sheet is the template rule, which makes a template that allows a user agent to construct a styled Result node from a Source node. The template has two parts:

The matching part identifies the source (XML) node to which the processing action is to be applied. The matching information is contained in the match attribute.

The processing part defines how the children are to be processed and what styling is to be applied to them. The processing information is contained in the template's child elements.

tokenized attribute type
In a tokenized type, the parser will normalize all whitespace to a single space character and will eliminate leading and trailing whitespace altogether. It will also validate the contents based on the declared type.

Seven attribute types are characterized as tokenized types because each value represents either a single token (ID, IDREF, ENTITY, NMTOKEN) or a list of tokens (IDREFS, ENTITIES, and NMTOKENS).

Uniform Resource Identifier (URI)
A superclass that includes both URNs and URLs. Presently, URI means URL in nearly all cases when discussing XML, although it is expected that URNs will become more numerous in the future. The URI supplies a universally unique number or name that can identify an element or attribute in a universally unique way.

URIs are a slightly more general scheme for locating resources on the Internet that focuses a more on the resource and less on the location. In theory, a URI could find the closest copy of a mirrored document or locate a document moved from one site to another.

Uniform Resource Locator (URL)
The set of URI schemes that have explicit instructions on how to access the resource on the Internet.

URLs are uniform in that they have the same basic syntax no matter what specific type of resource (Web page, newsgroup) is being addressed or what mechanism is described to fetch it.

Uniform Resource Name (URN)
Identifies a persistent Internet resource. A URN can provide a mechanism for locating and retrieving a schema file that defines a particular namespace. While an ordinary URL could provide similar functionality, a URN is more robust and easier to manage for this purpose because a URN can refer to more than one URL.

URNs are not location-dependent as URLs are.

unparsed entity
Any block of non-XML data, sometimes referred to as a binary entity because its content is often a binary file (such as an image) that is not directly interpreted by the XML parser. An unparsed entity could contain plain text, so the term binary is a bit misleading.

Unlike a parsed entity, an unparsed entity requires a notation, which identifies the format or type of resource to which the entity is declared. Beyond a requirement that an XML parser make the identifiers for the entity and notation available to the application, XML places no constraints on the contents of unparsed entities.

URI
See Uniform Resource Identifier.
URL
See Uniform Resource Locater.
URN
See Uniform Resource Name.
valid XML
XML that conforms to the rules defined in the XML specification, as well as the rules defined in the DTD or schema.

The parser must understand the validity constraints of the XML specification and check the document for possible violations. If the parser finds any errors, it must report them to the XML application. The parser must also read the DTD, validate the document against it, and again report any violations to the XML application.

Because all of this parsing and checking can take time and because validation might not always be necessary, XML supports the notion of the well-formed document.

vocabulary
See XML vocabulary.
W3C
See Worldwide Web Consortium.
well-formed XML
XML that follows the XML tag rules listed in the W3C Recommendation for XML 1.0, but doesn't have a DTD or schema. A well-formed XML document contains one or more elements; it has a single document element, with any other elements properly nested under it; and each of the parsed entities referenced directly or indirectly within the document is well formed.

Well-formed XML documents are easy to create because they don't require the additional work of creating a DTD. Well-formed XML can save download time because the client does not need to download the DTD, and it can save processing time because the XML parser doesn't need to process the DTD.

Worldwide Web Consortium (W3C)
A standards body physically located at MIT and virtually at that sets standards for XML, HTML, XSL, and many other Web technologies.
XLL
See Extensible Linking Language.
XML
See Extensible Markup Language.
XML-Data
A language used to create a schema, which identifies the structure and constraints of a particular XML document. XML-Data carries out the same basic tasks as DTD, but with more power and flexibility. Unlike DTD, which requires its own language and syntax, XML-Data uses XML syntax for its language.
XML declaration
The first line of an XML file can optionally contain the "xml" processing instruction, which is known as the XML declaration. The XML declaration can contain pseudo-attributes to indicate the XML language version, the character set, and whether the document can be used as a standalone entity.

An example is the XMLdeclaration that begins every valid XML file:

<?xml version="1.0" standalone="yes" ?>
XML document
A document object that is well formed, according to the XML recommendation, and that might (or might not) be valid. The XML document has a logical structure (composed of declarations, elements, comments, character references, and processing instructions) and a physical structure (composed of entities, starting with the root, or document entity).
XML engine
Software that supports XML functionality on the client; Internet Explorer 4.0, and Internet Explorer 5 include XML engines. Its components include the XML parser, the XSL processor, and schema support.
XML Object Model
An API that defines a standard way in which developers can interact with the elements of the XML structured tree. The XML object model exposes properties, methods, and the actual content (data) contained in an object. It controls how users communicate with trees, and exposes all tree elements as objects, which can be accessed without any return trips to the server. The XML OM uses the W3C standard Document Object Model.
XML parser
A software module used to read XML documents and provide access to their content and structure. The XML parser generates a hierarchically structured tree, then hands off data to viewers and other applications for processing, and finally returns the results to the browser. A validating XML parser also checks the XML syntax and reports errors.
XPath
The result of an effort to provide a common syntax and semantics for functionality shared between XSL Transformations (XSLT) and XPointer. The primary purpose of XPath is to address parts of an XML document. It also provides basic facilities for manipulation of strings, numbers and booleans. XPath uses a compact, non-XML syntax to facilitate use of XPath within URIs and XML attribute values. XPath gets its name from its use of a path notation as used in URLs for navigating through the hierarchical structure of an XML document.
XML Pointer Language (XPointer)
A W3C initiative that specifies constructs for addressing the internal structures of XML documents. In particular, it provides for specific reference to elements, character strings, and other parts of XML documents, whether or not they bear an explicit ID attribute.

An XPointer consists of a series of location terms, each of which specifies a location, usually relative to the location specified by the prior location term. Each location term has a keyword (such as id, child, ancestor, and so on) and can have arguments, such as an instance number, element type, or attribute. For example, the XPointer:

child(2,precocious)

refers to the second child element whose type is precocious.

XML Query Language (XQL)
A set of extensions to XSL Patterns proposed to the W3C.

XQL is an extension to the capabilities of XSL that will provide for searching into, and data retrieval from, XML documents. It provides ways to manipulate XML in order to create new documents, to control the content of existing documents, and to manage the ordering and presentation of these documents along with XSL.

XML Schema
See schema.
XML vocabulary
A set of actual elements and the structure for a specific document type used in particular data formats. Vocabularies, along with the structural relationships between the elements, are defined in a DTD that serves as the rulebook for that vocabulary.

One of the first and probably most well-know vocabularies is the Channel Definition Format used to define Web pages that are designed to be sent automatically, or "pushed" to client users.

XPointer
See XML Pointer Language.
XQL
See XML Query Language.
XSL
See Extensible Stylesheet Language.
XSL Formatting Objects
A set of formatting semantics expressed as an XML vocabulary.

Conceptually, these objects form a tree. The formatting objects denote typographic elements such as page, paragraph, rule, and so forth. Finer control over the presentation of these elements is provided by a set of formatting properties, such as indents; word- and letter-spacing; and widow, orphan, and hyphenation control. The formatting objects and formatting properties provide the vocabulary for expressing presentation intent.

XSL Patterns
Provide a simple query language for identifying nodes in an XML document, based on their type, name, and values, as well as the relationship of the node to other nodes in the document.

Just like XSL, XSL Patterns is a declarative, not procedural, language. That is, its queries specify what should be found in an XML document, not how to find it. This provides the application with much more flexibility to determine the most efficient method to use to find a piece of data.

Internet Explorer 5 supports XSL Patterns with some of the extensions described in XML Query Language.

XSL Transformations (XSLT)
Makes use of the expression language defined by XPath for selecting elements for conditional processing and for generating text.

XSLT provides two "hooks" for extending the language, one hook for extending the set of instruction elements used in templates and one hook for extending the set of functions used in XPath expressions. These hooks are both based on XML namespaces.


XML Reference

This section of the XML SDK documentation contains the following references:

  • XML DOM Reference
  • XML Schema Reference
  • XML Data Types Reference
  • XML Error Messages


XML DOM Reference

Welcome to the XML Document Object Model (DOM) Reference documentation.

The DOM is a World Wide Web Consortium (W3C) recommendation available at http://www.w3.org/TR/REC-DOM-Level-1/.

This reference documentation supplements the W3C recommendation and offers details relating to the Microsoft implementation supplied with Microsoft® Internet Explorer 5.

This documentation is provided in different formats for two different audiences:

  • XML DOM Objects (ActiveX® programming objects, for developers using script languages)
  • XML DOM Interfaces (COM programming interfaces, for C/C++ and Microsoft® Visual Basic® developers)


XML DOM Objects

This part of the XML Document Object Model (DOM) reference describes the programming object syntax for script developers. The following tables list the XML DOM programming objects by category.

XML DOM Base Objects

The following objects represent implementations of the XML DOM base interfaces. The objects include Microsoft extensions to support namespaces, data types, XML schemas, XSLT operations, asynchronous loading, and saving documents.

DOMDocument | Represents the top node of the XML DOM tree.
XMLDOMDocument2 | Extension of DOMDocument that supports schema caching, runtime validation, and a way to switch on XPath support.
XMLDOMNamedNodeMap | Provides iteration and access by name to the collection of attributes. This interface includes support for namespaces.
XMLDOMNode | Represents a single node in the document tree; the base interface for accessing data in the XML object model. Valid node types are defined in the DOMNodeType enumeration. This interface includes support for data types, namespaces, DTDs, and XML schemas.
XMLDOMNodeList | Supports iteration and indexed access operations on the live collection of XMLDOMNode objects.
XMLDOMParseError | Returns detailed information about the last error, including the error number, line number, character position, and a text description.
XMLHttpRequest | Provides client-side protocol support for communication with HTTP servers.
XTLRuntime | Implements methods that can be called from XSLT style sheets.

XML DOM Objects

The following objects represent implementations of other XML DOM interfaces.

XMLDOMAttribute | Represents an attribute object.
XMLDOMCDATASection | Quotes or escapes blocks of text so that text is not interpreted as markup language.
XMLDOMCharacterData | Provides text manipulation methods used by several objects.
XMLDOMComment | Represents the content of an XML comment.
XMLDOMDocumentFragment | Represents a lightweight object that is useful for tree insert operations.
XMLDOMDocumentType | Contains information associated with the document type declaration.
XMLDOMElement | Represents the element object.
XMLDOMEntity | Represents a parsed or unparsed entity in the XML document.
XMLDOMEntityReference | Represents an entity reference node.
XMLDOMImplementation | Provides methods that are independent of any particular instance of the document object model.
XMLDOMNotation | Contains a notation declared in the DTD or schema.
XMLDOMProcessingInstruction | Represents a processing instruction, which XML defines to keep processor-specific information in the text of the document.
XMLSchemaCache | Represents a set of namespace URIs.
XMLDOMSelection | Represents a list of nodes that match an XSLPattern or XPath expression.
XMLDOMText | Represents the text content of an element or attribute.
XSLProcessor | Used for performing transformations with cached XSL templates.
XSLTemplate | Represents a cached XSL style sheet.

The DOMNodeType enumeration lists all valid node types.

See Also

XML DOM Interfaces


XMLDOMAttribute Object

Represents an attribute of the XMLDOMElement object. Valid and default values for the attribute are defined in a DTD or schema.

Remarks

XMLDOMAttribute objects inherit the XMLDOMNode object but are not actually child nodes of the element and are not considered part of the document tree. Attributes are considered properties of their associated elements rather than independent and separate. Thus the object's parentNode, previousSibling, and nextSibling properties have the value Null.

Attribute nodes cannot be the immediate children of the XMLDOMDocumentFragment node. However, they can be associated with element nodes that are children of the XMLDOMDocumentFragment node.

In this object, the relationship between the value and specified properties can be summarized as follows: If the attribute has an assigned value in the document and the specified property is True, the value is the assigned value. If the attribute has no assigned value in the document and has a default value in the DTD, the specified property is False and the value is the default value in the DTD. If the attribute has no assigned value in the document and has a value of #IMPLIED in the DTD, the attribute does not appear in the structure model of the document.

In XML, the value of an attribute is represented by the child nodes of the attribute node because the value can contain entity references. Thus attributes that contain entity references will have a child list containing both text nodes and entity reference nodes. In addition, because the attribute type might be unknown, there are no tokenized attribute values.

Example

The following example creates an XMLDOMAttribute object from the first attribute of the root and displays the object's value and xml properties:

Dim xmlDoc
Dim root
Dim objDOMatt
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
Set objDOMAtt = root.attributes.item(0)
MsgBox (objDOMatt.value)
MsgBox (objDOMatt.xml)

See Also

Properties, Methods, and Events

XMLDOMElement Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMAttribute Properties, Methods, and Events

Properties

attributes | Contains the list of attributes for this node. Read-only.
baseName* | Returns the base name for the name qualified with the namespace. Read-only.
childNodes | Contains a node list containing the children (for nodes that can have children). Read-only.
dataType* | Specifies the data type for this node. Read/write.
definition* | Returns the definition of the node in the DTD or schema. Read-only.
firstChild | Contains the first child of this node. Read-only.
lastChild | Returns the last child node. Read-only.
name | Contains the attribute name. Read-only.
namespaceURI* | Returns the URI for the namespace. Read-only.
nextSibling | Contains the next sibling of this node in the parent's child list. Read-only.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types. Read-only.
nodeType | Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes. Read-only.
nodeTypedValue* | Contains this node's value, expressed in its defined data type. Read/write.
nodeTypeString* | Returns the node type in string form. Read-only.
nodeValue | Contains the text associated with the node. Read/write.
ownerDocument | Returns the root of the document that contains this node. Read-only.
parentNode | Contains the parent node (for nodes that can have parents). Read-only.
parsed* | Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed. Read-only.
prefix* | Returns the namespace prefix. Read-only.
previousSibling | Contains the left sibling of this node. Read-only.
specified | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema. Read-only.
text | Contains the text content of the node and its subtrees. Read/write.
value | Contains the attribute value. Read/write.
xml | Contains the XML representation of the node and all its descendants. Read-only.

* denotes an extension to the W3C DOM.

Methods

appendChild | Appends newChild as the last child of this node.
cloneNode | Creates a new node that is an exact clone of this node.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
selectNodes | Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.
selectSingleNode | Applies the specified pattern-matching operation to this node's context and returns the first matching node.
transformNode | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.
transformNodeToObject | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in the supplied object.

Events

None.

See Also

XMLDOMAttribute Object


XMLDOMCDATASection Object

Used to quote or escape blocks of text to keep that text from being interpreted as markup language.

Remarks

CDATA sections let you include material such as XML fragments within XML documents without needing to escape all the delimiters. The only delimiter recognized in a CDATA section is the "]]>" string that ends the CDATA section.

CDATA sections cannot be nested.

The text contained by the CDATA section is stored in a text node. Note that this text can contain characters that need to be escaped outside of CDATA sections.

Although the CDATASection object inherits the Text object, note that unlike text nodes, the XMLDOMElement object's normalize method does not merge CDATASection nodes.

This object has no unique properties or methods of its own but exposes the same objects and properties as the XMLDOMText object.

See Also

Properties, Methods, and Events

XMLDOMText Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMCDATASection Properties, Methods, and Events

Properties

attributes | Contains the list of attributes for this node. Read-only.
baseName* | Returns the base name for the name qualified with the namespace. Read-only.
childNodes | Contains a node list containing the children (for nodes that can have children). Read-only.
data | Contains this node's data, which depends on the node type. Read/write.
dataType* | Specifies the data type for this node. Read/write.
definition* | Returns the definition of the node in the DTD or schema. Read-only.
firstChild | Contains the first child of this node. Read-only.
lastChild | Returns the last child node. Read-only.
length | Specifies the length, in characters, of the data. Read-only.
namespaceURI* | Returns the URI for the namespace. Read-only.
nextSibling | Contains the next sibling of this node in the parent's child list. Read-only.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types. Read-only.
nodeType | Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes. Read-only.
nodeTypedValue* | Contains this node's value, expressed in its defined data type. Read/write.
nodeTypeString* | Returns the node type in string form. Read-only.
nodeValue | Contains the text associated with the node. Read/write.
ownerDocument | Returns the root of the document that contains this node. Read-only.
parentNode | Contains the parent node (for nodes that can have parents). Read-only.
parsed* | Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed. Read-only.
prefix* | Returns the namespace prefix. Read-only.
previousSibling | Contains the left sibling of this node. Read-only.
specified | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema. Read-only.
text* | Contains the text content of the node and its subtrees. Read/write.
xml* | Contains the XML representation of the node and all its descendants. Read-only.

* denotes an extension to the W3C DOM.

Methods

appendChild | Appends newChild as the last child of this node.
appendData | Appends the supplied string to the existing string data.
cloneNode | Creates a new node that is an exact clone of this node.
deleteData | Deletes the specified substring within the string data.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
insertData | Inserts the supplied string at the specified offset.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
replaceData | Replaces the specified number of characters with the supplied string.
selectNodes* | Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.
selectSingleNode* | Applies the specified pattern-matching operation to this node's context and returns the first matching node.
splitText | Breaks this text node into two text nodes at the specified offset and inserts the new text node into the tree as a sibling that immediately follows this node.
substringData | Retrieves a substring of the full string from the specified range.
transformNode* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.
transformNodeToObject | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in the supplied object.

* denotes an extension to the W3C DOM.

Events

None.

See Also

XMLDOMCDATASection Object


XMLDOMCharacterData Object

Provides text-manipulation methods that are used by several objects.

Remarks

The XMLDOMCharacterData object does not directly correspond to any node type. Its methods are reused by the XMLDOMCDATASection, XMLDOMComment, and XMLDOMText objects. These methods handle large amounts of text, including sizes larger than can be manipulated natively using string constructs.

See Also

Properties, Methods, and Events

XMLDOMElement Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMCharacterData Properties, Methods, and Events

Properties

attributes | Contains the list of attributes for this node. Read-only.
baseName* | Returns the base name for the name qualified with the namespace. Read-only.
childNodes | Contains a node list containing the children (for nodes that can have children). Read-only.
data | Contains this node's data, which depends on the node type. Read/write.
dataType* | Specifies the data type for this node. Read/write.
definition* | Returns the definition of the node in the DTD or schema. Read-only.
firstChild | Contains the first child of this node. Read-only.
lastChild | Returns the last child node. Read-only.
length | Specifies the length, in characters, of the data. Read-only.
namespaceURI* | Returns the URI for the namespace. Read-only.
nextSibling | Contains the next sibling of this node in the parent's child list. Read-only.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types. Read-only.
nodeType | Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes. Read-only.
nodeTypedValue* | Contains this node's value, expressed in its defined data type. Read/write.
nodeTypeString* | Returns the node type in string form. Read-only.
nodeValue | Contains the text associated with the node. Read/write.
ownerDocument | Returns the root of the document that contains this node. Read-only.
parentNode | Contains the parent node (for nodes that can have parents). Read-only.
parsed* | Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed. Read-only.
prefix* | Returns the namespace prefix. Read-only.
previousSibling | Contains the left sibling of this node. Read-only.
specified* | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema. Read-only.
text* | Contains the text content of the node and its subtrees. Read/write.
xml* | Contains the XML representation of the node and all its descendants. Read-only.

* denotes an extension to the W3C DOM.

Methods

appendChild | Appends newChild as the last child of this node.
appendData | Appends the supplied string to the existing string data.
cloneNode | Creates a new node that is an exact clone of this node.
deleteData | Deletes the specified substring within the string data.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
insertData | Inserts the supplied string at the specified offset.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
replaceData | Replaces the specified number of characters with the supplied string.
selectNodes* | Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.
selectSingleNode* | Applies the specified pattern-matching operation to this node's context and returns the first matching node.
substringData | Retrieves a substring of the full string from the specified range.
transformNode* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.
transformNodeToObject* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in the supplied object.

* denotes an extension to the W3C DOM.

Events

None.

See Also

XMLDOMCharacterData Object


XMLDOMComment Object

Represents the content of an XML comment.

Remarks

The content refers to all characters between the starting <!-- and ending --> tags.

This object has no unique properties or methods of its own, but exposes the same objects and properties as the XMLDOMCharacterData object.

Example

The following VBScript example creates a new XMLDOMComment object (comment) and appends it as the last child node of the DOMDocument object:

Dim xmlDoc
Dim root
Dim comment
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
Set comment = xmlDoc.createComment("Hello World!")
root.appendChild comment
MsgBox (root.xml)

See Also

Properties, Methods, and Events

XMLDOMElement Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMComment Properties, Methods, and Events

Properties

attributes | Contains the list of attributes for this node. Read-only.
baseName* | Returns the base name for the name qualified with the namespace. Read-only.
childNodes | Contains a node list containing the children (for nodes that can have children). Read-only.
data | Contains this node's data, which depends on the node type. Read/write.
dataType* | Specifies the data type for this node. Read/write.
definition* | Returns the definition of the node in the DTD or schema. Read-only.
firstChild | Contains the first child of this node. Read-only.
lastChild | Returns the last child node. Read-only.
length | Specifies the length, in characters, of the data. Read-only.
namespaceURI* | Returns the URI for the namespace. Read-only.
nextSibling | Contains the next sibling of this node in the parent's child list. Read-only.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types. Read-only.
nodeType | Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes. Read-only.
nodeTypedValue* | Contains this node's value, expressed in its defined data type. Read/write.
nodeTypeString* | Returns the node type in string form. Read-only.
nodeValue | Contains the text associated with the node. Read/write.
ownerDocument | Returns the root of the document that contains this node. Read-only.
parentNode | Contains the parent node (for nodes that can have parents). Read-only.
parsed* | Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed. Read-only.
prefix* | Returns the namespace prefix. Read-only.
previousSibling | Contains the left sibling of this node. Read-only.
specified* | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema. Read-only.
text* | Contains the text content of the node and its subtrees. Read/write.
xml* | Contains the XML representation of the node and all its descendants. Read-only.

* denotes an extension to the W3C DOM.

Methods

appendChild | Appends newChild as the last child of this node.
appendData | Appends the supplied string to the existing string data.
cloneNode | Creates a new node that is an exact clone of this node.
deleteData | Deletes the specified substring within the string data.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
insertData | Inserts the supplied string at the specified offset.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
replaceData | Replaces the specified number of characters with the supplied string.
selectNodes* | Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.
selectSingleNode* | Applies the specified pattern-matching operation to this node's context and returns the first matching node.
substringData | Retrieves a substring of the full string from the specified range.
transformNode* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.
transformNodeToObject* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in the supplied object.

* denotes an extension to the W3C DOM.

Events

None.

See Also

XMLDOMComment Object


DOMDocument Object

Represents the top level of the XML source. Includes methods and properties used to obtain or create all other XML objects.

Remarks

This object represents the top node in the tree. It implements all of the base DOM document methods and provides additional methods and properties that support XSL and XML transformations.

Although there are many programming objects, only one object can be created: the document. All other objects are accessed or created from the document.

The document can be created using either a free-threaded or a rental-threaded model. The behavior of the two models is identical; rental-threaded documents exhibit better performance because the parser doesn't need to manage concurrent access among threads. Note that you cannot combine nodes or documents that are created using differing threading models. The document threading model is determined by the following settings:

Setting | Rental-threaded model | Free-threaded model
Version independent ProgID | Msxml2.DOMDocument | Microsoft.FreeThreadedXMLDOM
ProgID | Msxml2.DOMDocument.2.6 | Microsoft.FreeThreadedXMLDOM.2.6
ClassID | F6D90F11-9C73-11D3-B32E-00C04F990BB4 | F6D90F12-9C73-11D3-B32E-00C04F990BB4
VB Class Name | DOMDocument26 | FreeThreadedDOMDocument26

Example

The following JScript examples demonstrate how to create the two types of objects:

var objDoc = new ActiveXObject("Msxml2.DOMDocument");
var objFTDoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument");

The following VBScript examples demonstrate how to create the two types of objects:

Set objDoc = CreateObject("Msxml2.DOMDocument")
Set objFTDoc = CreateObject("Msxml2.FreeThreadedDOMDocument")

See Also

Properties, Methods, and Events

XMLDOMNode Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

DOMDocument Properties, Methods, and Events

Properties

async* | Indicates whether asynchronous download is permitted. Read/write.
attributes | Contains the list of attributes for this node. Read-only.
baseName* | Returns the base name for the name qualified with the namespace. Read-only.
childNodes | Contains a node list containing the children (for nodes that can have children). Read-only.
dataType* | Specifies the data type for this node. Read/write.
definition* | Returns the definition of the node in the DTD or schema. Read-only.
doctype | Contains the document type node that specifies the DTD for this document. Read-only.
documentElement | Contains the root element of the document. Read/write.
firstChild | Contains the first child of this node. Read-only.
implementation | Contains the XMLDOMImplementation object for this document. Read-only.
lastChild | Returns the last child node. Read-only.
namespaceURI* | Returns the URI for the namespace. Read-only.
nextSibling | Contains the next sibling of this node in the parent's child list. Read-only.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types. Read-only.
nodeType | Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes. Read-only.
nodeTypedValue* | Contains this node's value, expressed in its defined data type. Read/write.
nodeTypeString* | Returns the node type in string form. Read-only.
nodeValue | Contains the text associated with the node. Read/write.
ondataavailable* | Specifies the event handler for the ondataavailable event. Read/write.
onreadystatechange* | Specifies the event handler to be called when the readyState property changes. Read/write.
ontransformnode* | Specifies the event handler for the ontransformnode event. Read/write.
ownerDocument | Returns the root of the document that contains this node. Read-only.
parentNode | Contains the parent node (for nodes that can have parents). Read-only.
parsed* | Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed. Read-only.
parseError* | Returns an XMLDOMParseError object that contains information about the last parsing error. Read-only.
prefix* | Returns the namespace prefix. Read-only.
preserveWhiteSpace* | Contains True if default processing preserves white space; False otherwise. Read/write.
previousSibling | Contains the left sibling of this node. Read-only.
readyState* | Indicates the current state of the XML document. Read-only.
resolveExternals* | Indicates whether external definitions (resolvable namespaces, DTD external subsets, and external entity references) are to be resolved at parse time, independent of validation. Read/write.
specified* | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema. Read-only.
text* | Contains the text content of the node and its subtrees. Read/write.
url* | Returns the canonicalized URL for the last loaded XML document. Read-only.
validateOnParse* | Indicates whether the parser should validate this document. Read/write.
xml* | Contains the XML representation of the node and all its descendants. Read-only.

* denotes an extension to the W3C DOM.

Methods

abort* | Aborts an asynchronous download in progress.
appendChild | Appends newChild as the last child of this node.
cloneNode | Creates a new node that is an exact clone of this node.
createAttribute | Creates a new attribute with the specified name.
createCDATASection | Creates a CDATA section node that contains the supplied data.
createComment | Creates a comment node that contains the supplied data.
createDocumentFragment | Creates an empty DocumentFragment object.
createElement | Creates an element node using the specified name.
createEntityReference | Creates a new EntityReference object.
createNode* | Creates a node using the supplied type, name, and namespace.
createProcessingInstruction | Creates a processing instruction node that contains the supplied target and data.
createTextNode | Creates a text node that contains the supplied data.
getElementsByTagName | Returns a collection of elements that have the specified name.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
load* | Loads an XML document from the specified location.
loadXML* | Loads an XML document using the supplied string.
nodeFromID* | Returns the node whose ID attribute matches the supplied value.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
save* | Saves an XML document to the specified location.
selectNodes* | Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.
selectSingleNode* | Applies the specified pattern-matching operation to this node's context and returns the first matching node.
transformNode* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.
transformNodeToObject* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in the supplied object.

* denotes an extension to the W3C DOM.

Events

ondataavailable* | Indicates that XML document data is available.
onreadystatechange* | Indicates when the readyState property changes.
ontransformnode* | Fires before each node in the style sheet is applied to each node in the XML source.

* denotes an extension to the W3C DOM.

See Also

DOMDocument Object


XMLDOMDocument2 Object

An extension of DOMDocument that supports schema caching and validation features, as well as way to switch on XPath support.

See Also

Properties, Methods, and Events

DOMDocument Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMDocument2 Properties, Methods, and Events

Properties

namespaces | Returns the list of namespaces in the document.
schemas | Uses the XMLSchemaCache object to find schema documents.

Methods

getProperty | Identifies default values or values set by setProperty.
selectNodes | Changes select nodes to return an XMLDOMSelection object.
setProperty | Mechanism for setting properties.
validate | Performs run-time validation on the currently loaded XML document.

Events

None.

See Also

XMLDOMDocument2 Object


XMLDOMDocumentFragment Object

A lightweight object that is useful for tree insert operations.

Remarks

The DocumentFragment object can represent a fragment of a document or portion of a document's tree. This makes it useful when implementing end-user commands that allow users to rearrange a document, such as cutting and pasting.

The DocumentFragment node has special, defined behavior for XMLDOMNode insert operations that makes it especially convenient for developers. When an XMLDOMDocumentFragment is inserted into a DOMDocument node (or other node that can take children), the children of the DocumentFragment are inserted into the node rather than the DocumentFragment itself. This makes the DocumentFragment useful when the user wants to create nodes that are siblings; the DocumentFragment acts as the parent of these nodes so that the user can employ the standard methods from the IXMLDOMNode interface, such as insertBefore and appendChild.

The children of a DocumentFragment node make up zero or more nodes representing the tops of any subtrees defining the structure of the document. DocumentFragment nodes do not need to be well-formed XML documents (although they do need to follow the rules imposed upon well-formed XML parsed entities, which can have multiple top nodes). For example, a DocumentFragment might have only one child, and that child node could be a Text node. Such a structure model represents neither an HTML document nor a well-formed XML document.

This object has no unique properties or methods of its own, but exposes the same objects and properties as the XMLDOMNode object.

See Also

Properties, Methods, and Events

XMLDOMNode Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMDocumentFragment Properties, Methods, and Events

Properties

attributes | Contains the list of attributes for this node. Read-only.
baseName* | Returns the base name for the name qualified with the namespace. Read-only.
childNodes | Contains a node list containing the children (for nodes that can have children). Read-only.
dataType* | Specifies the data type for this node. Read/write.
definition* | Returns the definition of the node in the DTD or schema. Read-only.
firstChild | Contains the first child of this node. Read-only.
lastChild | Returns the last child node. Read-only.
namespaceURI* | Returns the URI for the namespace. Read-only.
nextSibling | Contains the next sibling of this node in the parent's child list. Read-only.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types. Read-only.
nodeType | Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes. Read-only.
nodeTypedValue* | Contains this node's value, expressed in its defined data type. Read/write.
nodeTypeString* | Returns the node type in string form. Read-only.
nodeValue | Contains the text associated with the node. Read/write.
ownerDocument | Returns the root of the document that contains this node. Read-only.
parentNode | Contains the parent node (for nodes that can have parents). Read-only.
parsed* | Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed. Read-only.
prefix* | Returns the namespace prefix. Read-only.
previousSibling | Contains the left sibling of this node. Read-only.
specified* | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema. Read-only.
text* | Contains the text content of the node and its subtrees. Read/write.
xml* | Contains the XML representation of the node and all its descendants. Read-only.

* denotes an extension to the W3C DOM.

Methods

appendChild | Appends newChild as the last child of this node.
cloneNode | Creates a new node that is an exact clone of this node.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
selectNodes* | Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.
selectSingleNode* | Applies the specified pattern-matching operation to this node's context and returns the first matching node.
transformNode* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.
transformNodeToObject* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in the supplied object.

* denotes an extension to the W3C DOM.

Events

None.

See Also

XMLDOMDocumentFragment Object


XMLDOMDocumentType Object

Contains information associated with the document type declaration.

Remarks

Each DOMDocument object includes a doctype property that identifies the document's XMLDOMDocumentType object. The XMLDOMDocumentType object provides access to the list of entities and notations defined for the document.

See Also

Properties, Methods, and Events


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMDocumentType Properties, Methods, and Events

Properties

attributes | Contains the list of attributes for this node. Read-only.
baseName* | Returns the base name for the name qualified with the namespace. Read-only.
childNodes | Contains a node list containing the children (for nodes that can have children). Read-only.
dataType* | Specifies the data type for this node. Read/write.
definition* | Returns the definition of the node in the DTD or schema. Read-only.
entities | Contains a list of the entities, both external and internal, that are declared in the document type declaration. Read-only.
firstChild | Contains the first child of this node. Read-only.
lastChild | Returns the last child node. Read-only.
name | Contains the name of the document type. Read-only.
namespaceURI* | Returns the URI for the namespace. Read-only.
nextSibling | Contains the next sibling of this node in the parent's child list. Read-only.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types. Read-only.
nodeType | Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes. Read-only.
nodeTypedValue* | Contains this node's value, expressed in its defined data type. Read/write.
nodeTypeString* | Returns the node type in string form. Read-only.
nodeValue | Contains the text associated with the node. Read/write.
notations | Contains a list of the XMLDOMNotation objects present in the document type declaration. Read-only.
ownerDocument | Returns the root of the document that contains this node. Read-only.
parentNode | Contains the parent node (for nodes that can have parents). Read-only.
parsed* | Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed. Read-only.
prefix* | Returns the namespace prefix. Read-only.
previousSibling | Contains the left sibling of this node. Read-only.
specified* | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema. Read-only.
text* | Contains the text content of the node and its subtrees. Read/write.
xml* | Contains the XML representation of the node and all its descendants. Read-only.

* denotes an extension to the W3C DOM.

Methods

appendChild | Appends newChild as the last child of this node.
cloneNode | Creates a new node that is an exact clone of this node.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
selectNodes* | Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.
selectSingleNode* | Applies the specified pattern-matching operation to this node's context and returns the first matching node.
transformNode* | Processes this node and its children using the supplied XSL style sheet, and returns the resulting transformation.
transformNodeToObject* | Processes this node and its children using the supplied XSL style sheet, and returns the resulting transformation in the supplied object.

* denotes an extension to the W3C DOM.

Events

None.

See Also

XMLDOMDocumentType Object


XMLDOMElement Object

Represents the element object.

Remarks

Element nodes are among the most common objects in the XML document tree. Element nodes can have attributes associated with them. By definition, attributes are not defined as child nodes of an element and are not considered to be part of the document tree. Accordingly, the XMLDOMElement object provides methods to make it easier to manage attributes, including methods to associate an attribute with an element and to retrieve an attribute object and the attribute value by name.

To retrieve the set of all attributes associated with an element, you can also call the getAttribute method, which returns an XMLDOMNamedNodeMap collection object that contains all the element's attributes.

See Also

Properties, Methods, and Events

XMLDOMText Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMElement Properties, Methods, and Events

Properties

attributes | Contains the list of attributes for this node. Read-only.
baseName* | Returns the base name for the name qualified with the namespace. Read-only.
childNodes | Contains a node list containing the children (for nodes that can have children). Read-only.
dataType* | Specifies the data type for this node. Read/write.
definition* | Returns the definition of the node in the DTD or schema.
firstChild | Contains the first child of this node. Read-only.
lastChild | Returns the last child node. Read-only.
namespaceURI* | Returns the URI for the namespace. Read-only.
nextSibling | Contains the next sibling of this node in the parent's child list. Read-only.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types. Read-only.
nodeType | Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes. Read-only.
nodeTypeString* | Returns the node type in string form. Read-only.
nodeValue | Contains the text associated with the node. Read/write.
ownerDocument | Returns the root of the document that contains this node. Read-only.
parentNode | Contains the parent node (for nodes that can have parents). Read-only.
parsed* | Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed. Read-only.
prefix* | Returns the namespace prefix. Read-only.
previousSibling | Contains the left sibling of this node. Read-only.
specified* | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema. Read-only.
tagName | Contains the element name (the name that appears within the tag). Read-only.
text* | Contains the text content of the node and its subtrees. Read/write.
xml* | Contains the XML representation of the node and all its descendants. Read-only.

* denotes an extension to the W3C DOM.

Methods

appendChild | Appends newChild as the last child of this node.
cloneNode | Creates a new node that is an exact clone of this node.
getAttribute | Gets the value of the named attribute.
getAttributeNode | Gets the named attribute node.
getElementsByTagName | Returns a list of all descendant elements that match the supplied name.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
normalize | Normalizes all descendent elements; combines two or more adjacent text nodes into one unified text node.
removeAttribute | Removes or replaces the named attribute.
removeAttributeNode | Removes the specified attribute from this element.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
selectNodes* | Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.
selectSingleNode* | Applies the specified pattern-matching operation to this node's context and returns the first matching node.
setAttribute | Sets the value of the named attribute.
setAttributeNode | Adds or changes the supplied attribute node on this element.
transformNode* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.
transformNodeToObject* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in the supplied object.

* denotes an extension to the W3C DOM.

Events

None.

See Also

XMLDOMElement Object


XMLDOMEntity Object

Represents a parsed or unparsed entity in the XML document.

Remarks

The XMLDOMEntity object represents the entity itself rather than the entity declaration. The W3C DOM does not currently define an object that models the entity declaration.

According to the W3C DOM specification, an XML parser can completely expand entity references into entities before the structure model is passed to the DOM. When these entity references are expanded, the document tree does not contain any entity references.

When the Microsoft XML parser validates the XML document, it expands external entities (except binary entities). The nodes representing the expanded entity are available as read-only children of the entity reference. The Microsoft implementation does not expand these entities when it is not validating.

The nodeName property contains the name of the entity.

The structure of the entity child list is exactly the same as the structure of the child list for the XMLDOMEntityReference object with the same nodeName value.

Level 1 of the W3C DOM API does not define a way to change entity nodes (all XMLDOMEntity object properties are read-only).

See Also

Properties, Methods, and Events

DOMDocument Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMEntity Properties, Methods, and Events

Properties

attributes | Contains the list of attributes for this node. Read-only.
baseName* | Returns the base name for the name qualified with the namespace. Read-only.
childNodes | Contains a node list containing the children (for nodes that can have children). Read-only.
dataType* | Specifies the data type for this node. Read/write.
definition* | Returns the definition of the node in the DTD or schema. Read-only.
firstChild | Contains the first child of this node. Read-only.
lastChild | Returns the last child node. Read-only.
namespaceURI* | Returns the URI for the namespace. Read-only.
nextSibling | Contains the next sibling of this node in the parent's child list. Read-only.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types. Read-only.
nodeType | Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes. Read-only.
nodeTypedValue* | Contains this node's value, expressed in its defined data type. Read/write.
nodeTypeString* | Returns the node type in string form. Read-only.
nodeValue | Contains the text associated with the node. Read/write.
notationName | Contains the notation name. Read-only.
ownerDocument | Returns the root of the document that contains this node. Read-only.
parentNode | Contains the parent node (for nodes that can have parents). Read-only.
parsed* | Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed. Read-only.
prefix* | Returns the namespace prefix. Read-only.
previousSibling | Contains the left sibling of this node. Read-only.
publicId | Contains the public identifier associated with the entity. Read-only.
specified* | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema. Read-only.
systemId | Contains the system identifier associated with the entity. Read-only.
text* | Contains the text content of the node and its subtrees. Read/write.
xml* | Contains the XML representation of the node and all its descendants. Read-only.

* denotes an extension to the W3C DOM.

Methods

appendChild | Appends newChild as the last child of this node.
cloneNode | Creates a new node that is an exact clone of this node.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
selectNodes* | Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.
selectSingleNode* | Applies the specified pattern-matching operation to this node's context and returns the first matching node.
transformNode* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.
transformNodeToObject* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in the supplied object.

* denotes an extension to the W3C DOM.

Events

None.

See Also

XMLDOMEntity Object


XMLDOMEntityReference Object

Represents an entity reference node.

Remarks

If the XML parser expands entity references while building the structure model, no EntityReference objects appear in the tree.

XML does not require nonvalidating processors to handle external entity declarations (those made in the external subset or declared in external parameter entities). This means that parsed entities declared in the external subset need not be expanded, and therefore the replacement value of the entity might not be available. If available, the replacement value of the referenced entity appears in the child list of the EntityReference object.

The resolution of the children of the EntityReference (the replacement value of the referenced entity) can be lazily evaluated. Actions such as calling the childNodes method are assumed to trigger the evaluation. Note that character entities are expanded by the XML parser and do not appear as entity references, but instead appear within the character text in Unicode.

This object has no unique properties or methods of its own but exposes the same objects and properties as the XMLDOMNode object.

See Also

Members


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMEntityReference Properties, Methods, and Events

Properties

attributes | Contains the list of attributes for this node. Read-only.
baseName* | Returns the base name for the name qualified with the namespace. Read-only.
childNodes | Contains a node list containing the children (for nodes that can have children). Read-only.
dataType* | Specifies the data type for this node. Read/write.
definition* | Returns the definition of the node in the DTD or schema. Read-only.
firstChild | Contains the first child of this node. Read-only.
lastChild | Returns the last child node. Read-only.
namespaceURI* | Returns the URI for the namespace. Read-only.
nextSibling | Contains the next sibling of this node in the parent's child list. Read-only.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types. Read-only.
nodeType | Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes. Read-only.
nodeTypedValue* | Contains this node's value, expressed in its defined data type. Read/write.
nodeTypeString* | Returns the node type in string form. Read-only.
nodeValue | Contains the text associated with the node. Read/write.
ownerDocument | Returns the root of the document that contains this node. Read-only.
parentNode | Contains the parent node (for nodes that can have parents). Read-only.
parsed* | Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed. Read-only.
prefix* | Returns the namespace prefix. Read-only.
previousSibling | Contains the left sibling of this node. Read-only.
specified* | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema. Read-only.
text* | Contains the text content of the node and its subtrees. Read/write.
xml* | Contains the XML representation of the node and all its descendants. Read-only.

* denotes an extension to the W3C DOM.

Methods

appendChild | Appends newChild as the last child of this node.
cloneNode | Creates a new node that is an exact clone of this node.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
selectNodes* | Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.
selectSingleNode* | Applies the specified pattern-matching operation to this node's context and returns the first matching node.
transformNode* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.
transformNodeToObject* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in the supplied object.

* denotes an extension to the W3C DOM.

Events

None.

See Also

XMLDOMEntityReference Object


XMLDOMImplementation Object

Provides methods that are independent of any particular instance of the document object model.

See Also

Properties, Methods, and Events

DOMDocument Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMImplementation Properties, Methods, and Events

Properties

None.

Methods

hasFeature Returns True if the specified version of the implementation supports the specified feature.

Events

None.

See Also

XMLDOMImplementation Object


XMLDOMNamedNodeMap Object

Adds support for namespaces and iteration through the collection of attribute nodes.

Remarks

XMLDOMNamedNodeMap is a node collection that allows access by name as well as by index. This collection is typically used for attributes.

Note that the World Wide Web Consortium (W3C) document object model (DOM) definition does not require NamedNodeMap objects to be maintained in any particular order. Objects contained in a NamedNodeMap can also be accessed by an ordinal index, but this is simply to allow convenient enumeration and does not imply that the DOM specifies an order for these nodes. DOM implementations are not required to preserve the node order.

The Microsoft implementation preserves the attributes in the order in which they appear in the source. Additional attributes are added to the end of the list. Note that a namespace attribute (xmlns) is inserted as the first in the list if the tag name requires the namespace, or immediately before the attribute that uses an undeclared namespace.

Like the node list, however, a named node map collection is live; that is, the addition and removal of nodes, and changes within nodes, are immediately reflected in the collection. This means that two successive requests for items using the same index can return two different items, depending on changes to the collection. This also means that changes to the node objects are immediately available in the nodes obtained from the list.

Note that when a default value is specified in the schema for an attribute and that attribute is removed, it is automatically regenerated with the default value, as specified in the W3C standard. The attribute reappears immediately because the collection is live, with a corresponding change to the count of items in the collection. When removing an attribute with such a default setting, you will need to again retrieve the number of items in the collection to obtain an accurate count. You should also reset any index into the collection.

See Also

Properties, Methods, and Events

XMLDOMNode Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMNamedNodeMap Properties, Methods, and Events

Properties

length | Indicates the number of items in the collection. Read-only.
item | Allows random access to individual nodes within the collection. Read-only.

Methods

getNamedItem | Retrieves the attribute with the specified name.
getQualifiedItem* | Returns the attribute with the specified namespace and attribute name.
nextNode | Returns the next node in the collection.
removeNamedItem | Removes an attribute from the collection.
removeQualifiedItem | Removes the attribute with the specified namespace and attribute name.
reset | Resets the iterator.
setNamedItem | Adds the supplied node to the collection.

* denotes an extension to the W3C DOM.

Events

None.

See Also

XMLDOMNamedNodeMap Object


XMLDOMNode Object

Extends the core Node object with support for data types, namespaces, DTDs, and schemas.

Example

The following VBScript example creates an XMLDOMNode object, currNode, and displays its XML:

Dim xmlDoc
Dim currNode
Set xmlDoc = CreateObject("
Msxml2.DOMDocument")
xmlDoc.async = false
xmlDoc.load("c:\books.xml")
Set currNode = xmlDoc.documentElement.childNodes.item(1)
MsgBox currNode.xml

See Also

Properties, Methods, and Events

XMLDOMNodeList Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMNode Properties, Methods, and Events

Properties

attributes | Contains the list of attributes for this node. Read-only.
baseName* | Returns the base name for the name qualified with the namespace. Read-only.
childNodes | Contains a node list containing the children (for nodes that can have children). Read-only.
dataType* | Specifies the data type for this node. Read/write.
definition* | Returns the definition of the node in the DTD or schema. Read-only.
firstChild | Contains the first child of this node. Read-only.
lastChild | Returns the last child node. Read-only.
namespaceURI* | Returns the URI for the namespace. Read-only.
nextSibling | Contains the next sibling of this node in the parent's child list. Read-only.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types. Read-only.
nodeType | Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes. Read-only.
nodeTypedValue* | Contains this node's value, expressed in its defined data type. Read/write.
nodeTypeString* | Returns the node type in string form. Read-only.
nodeValue | Contains the text associated with the node. Read/write.
ownerDocument | Returns the root of the document that contains this node. Read-only.
parentNode | Contains the parent node (for nodes that can have parents). Read-only.
parsed* | Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed. Read-only.
prefix* | Returns the namespace prefix. Read-only.
previousSibling | Contains the left sibling of this node. Read-only.
specified* | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema. Read-only.
text* | Contains the text content of the node and its subtrees. Read/write.
xml* | Contains the XML representation of the node and all its descendants. Read-only.

* denotes an extension to the W3C DOM.

Methods

appendChild | Appends newChild as the last child of this node.
cloneNode | Creates a new node that is an exact clone of this node.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
selectNodes* | Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.
selectSingleNode* | Applies the specified pattern-matching operation to this node's context and returns the first matching node.
transformNode* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.
transformNodeToObject* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in the supplied object.

* denotes an extension to the W3C DOM.

Events

None.

See Also

XMLDOMNode Object


XMLDOMNodeList Object

Supports iteration through the live collection, in addition to indexed access.

Remarks

A NodeList collection is live; that is, the addition and removal of nodes, and changes within nodes, are immediately reflected in the collection. This means that two successive requests for items using the same index can return two different items, depending on changes to the collection. This also means that changes to the node objects are immediately available in the nodes obtained from the list. The collection can also be accessed using the "for...next" construct.

Example

The following VBScript example creates an XMLDOMNodeList object by using the document's getElementsByTagName method:

Dim xmlDoc
Dim objNodeList
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.load("c:\xmlinact\books.xml")
Set objNodeList = xmlDoc.getElementsByTagName("AUTHOR")

See Also

Properties, Methods, and Events

XMLDOMNode Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMNodeList Properties, Methods, and Events

Properties

length Indicates the number of items in the collection. Read-only.

Methods

item | Allows random access to individual nodes within the collection.
nextNode* | Returns the next node in the collection.
reset* | Resets the iterator.

* denotes an extension to the W3C DOM.

Events

None.

See Also

XMLDOMNodeList Object


XMLDOMNotation Object

Contains a notation declared in the DTD or schema.

Remarks

Notations identify by name the format of unparsed entities, the format of elements that bear a notation attribute, or the application to which a processing instruction is addressed.

The nodeName property contains the notation name.

See Also

Properties, Methods, and Events

XMLDOMNode Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMNotation Properties, Methods, and Events

Properties

attributes | Contains the list of attributes for this node. Read-only.
baseName* | Returns the base name for the name qualified with the namespace. Read-only.
childNodes | Contains a node list containing the children (for nodes that can have children). Read-only.
dataType* | Specifies the data type for this node. Read/write.
definition* | Returns the definition of the node in the DTD or schema. Read-only.
firstChild | Contains the first child of this node. Read-only.
lastChild | Returns the last child node. Read-only.
namespaceURI* | Returns the URI for the namespace. Read-only.
nextSibling | Contains the next sibling of this node in the parent's child list. Read-only.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types. Read-only.
nodeType | Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes. Read-only.
nodeTypedValue* | Contains this node's value, expressed in its defined data type. Read/write.
nodeTypeString* | Returns the node type in string form. Read-only.
nodeValue | Contains the text associated with the node. Read/write.
ownerDocument | Returns the root of the document that contains this node. Read-only.
parentNode | Contains the parent node (for nodes that can have parents). Read-only.
parsed* | Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed. Read-only.
prefix* | Returns the namespace prefix. Read-only.
previousSibling | Contains the left sibling of this node. Read-only.
publicId | Contains the public identifier for the notation. Read-only.
specified* | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema. Read-only.
systemId | Contains the system identifier for the notation. Read-only.
transformNode* | Contains the text content of the node and its subtrees. Read/write.
xml* | Contains the XML representation of the node and all its descendants. Read-only.

* denotes an extension to the W3C DOM.

Methods

appendChild | Appends newChild as the last child of this node.
cloneNode | Creates a new node that is an exact clone of this node.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
selectNodes* | Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.
selectSingleNode* | Applies the specified pattern-matching operation to this node's context and returns the first matching node.
transformNodeToObject* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.
text* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in the supplied object.

* denotes an extension to the W3C DOM.

Events

None.

See Also

XMLDOMNotation Object


XMLDOMParseError Object

Returns detailed information about the last parse error, including the error number, line number, character position, and a text description.

Example

The following VBScript example attempts to load an XML document. It then tests the XMLDOMParseError object's errorCode property to see if an error has occurred.

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
If xmlDoc.parseError.errorCode <> 0 Then
  MsgBox ("A parse error occurred.")
Else
  MsgBox xmlDoc.documentElement.xml
End If

See Also

Properties, Methods, and Events


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMParseError Properties, Methods, and Events

Properties

errorCode | Contains the error code of the last parse error. Read-only.
filepos | Contains the absolute file position where the error occurred. Read-only.
line | Specifies the line number that contains the error. Read-only.
linepos | Contains the character position within the line where the error occurred. Read-only.
reason | Explains the reason for the error. Read-only.
srcText | Returns the full text of the line containing the error. Read-only.
url | Contains the URL of the XML document containing the last error. Read-only.

Methods

None.

Events

None.

See Also

XMLDOMParseError Object


XMLDOMProcessingInstruction Object

Represents a processing instruction, which XML defines to keep processor-specific information in the text of the document.

Remarks

The content of the ProcessingInstruction node is the entire content between the delimiters of the processing instruction (PI).

The content of this node is usually subdivided into the target (the application to which this processing instruction is directed) and the content of the processing instruction. The target consists of the first token following the start of the tag, while the content of the PI refers to the text that extends from the first nonwhite-space character after the target through the character immediately preceding the ?>, which signifies the end of the tag.

Example

The following example creates a new XMLDOMProcessingInstruction object and displays its XML representation:

Dim xmlDoc
Dim pi
Set xmlDoc = CreateObject("msxml2.DOMDocument")
Set pi = xmlDoc.createProcessingInstruction("xml", "version=""1.0""")
MsgBox pi.xml

Output ( in a message box )

<?xml version=”1.0”?>

See Also

Properties, Methods, and Events

DOMDocument Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMProcessingInstruction Properties, Methods, and Events

Properties

attributes | Contains the list of attributes for this node. Read-only.
baseName* | Returns the base name for the name qualified with the namespace. Read-only.
childNodes | Contains a node list containing the children (for nodes that can have children). Read-only.
data | Contains the content of the processing instruction, excluding the target. Read/write.
dataType* | Specifies the data type for this node. Read/write.
definition* | Returns the definition of the node in the DTD or schema. Read-only.
firstChild | Contains the first child of this node. Read-only.
lastChild | Returns the last child node. Read-only.
namespaceURI* | Returns the URI for the namespace. Read-only.
nextSibling | Contains the next sibling of this node in the parent's child list. Read-only.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types. Read-only.
nodeType | Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes. Read-only.
nodeTypedValue* | Contains this node's value, expressed in its defined data type. Read/write.
nodeTypeString* | Returns the node type in string form. Read-only.
nodeValue | Contains the text associated with the node. Read/write.
ownerDocument | Returns the root of the document that contains this node. Read-only.
parentNode | Contains the parent node (for nodes that can have parents). Read-only.
parsed* | Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed. Read-only.
prefix* | Returns the namespace prefix. Read-only.
previousSibling | Contains the left sibling of this node. Read-only.
specified* | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema. Read-only.
target | Specifies the target, the application to which this processing instruction is directed. Read-only.
text* | Contains the text content of the node and its subtrees. Read/write.
xml* | Contains the XML representation of the node and all its descendants. Read-only.

* denotes an extension to the W3C DOM.

Methods

appendChild | Appends newChild as the last child of this node.
cloneNode | Creates a new node that is an exact clone of this node.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
selectNodes* | Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.
selectSingleNode* | Applies the specified pattern-matching operation to this node's context and returns the first matching node.
transformNode* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.
transformNodeToObject* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in the supplied object.

* denotes an extension to the W3C DOM.

Events

None.

See Also

XMLDOMProcessingInstruction Object


XMLSchemaCache Object

Used by the schemas and namespaces properties on XMLDOMDocument2.

Remarks

The SchemaCache object is free-threaded and can be used in multiple documents at the same time. A single schema document can belong to multiple schema collections and will remain read-only until it is removed from all collections. Schema documents belonging to a collection must be free-threaded.

You can create a SchemaCache object in JScript as follows:

var cache = new ActiveXObject("MSXML2.XMLSchemaCache");

Or in VBScript:

Dim cache
Set cache = CreateObject("MSXML2.XMLSchemaCache");

Example

The following example shows how to build a reusable schema cache:

  1. First, load a document that references schemas via x-schema:
    var xmldoc = new ActiveXObject("MSXML2.DOMDocument");
xmldoc.async = false;
xmdoc.load("http://myserver/mydata.xml");
  2. Next, build a cache from the schemas loaded by your document:
    var cache = new ActiveXObject("MSXML2.XMLSchemaCache");
cache.addCollection(xmldoc.namespaces);
  3. Now you can use this cache in another document's load method:
    var xmldoc2 = new ActiveXObject("MSXML2.DOMDocument");
xmldoc2.async = false;
xmldoc2.schemas = cache;
xmldoc2.load("http://myserver/newdata.xml");

"Xmldoc2" will load much faster because the schemas are cached.

See Also

Properties, Methods, and Events


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLSchemaCache Properties, Methods, and Events

Properties

length Returns the number of namespaces currently in a collection.

Methods

add | Adds a new item to a schema collection.
addCollection | Adds all the schemas from another collection into the current collection.
get | Returns a read-only DOM node containing the <Schema> element.
namespaceURI | Returns the namespace at the specified index.
remove | Removes the specified namespaces from a collection.

Events

None.

See Also

XMLSchemaCache


XMLDOMSelection Object

Represents the list of nodes that match a given XSL Pattern or XPath expression.

Remarks

A new XMLDOMSelection object is created via the selectNodes method on XMLDOMDocument2. For example, in JScript you can simply call the XMLDOMSelection methods on the object returned from selectNodes as follows:

xmldoc.setProperty("SelectionLanguage", "XPath")
var selection = xmldoc.selectNodes("//Customer[Zip = 98072]");
var expr = selection.expr; //This returns "//Customer[Zip = 98072]"

This object is an extension of the W3C DOM.

See Also

Properties, Methods, and Events


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMSelection Properties, Methods, and Events

Properties

context | Specifies the node (subtree) to apply a selection.
expr | Gets or puts the XPath expression string.
length | Indicates the number of items in a collection.

Methods

clone | Produces an exact copy of the current XMLDOMSelection object.
getProperty | Looks up a property by name.
item | Allows random access to individual nodes within a collection. This method has no effect on the current position.
matches | Checks whether the passed-in node is contained in the current collection.
nextNode | Returns the next node in a collection.
peekNode | Gets the next node that nextNode will return without advancing the line position.
removeAll | Removes all the nodes from the current context that match the query.
removeNext | Removes the next node that would be returned by peekNode or nextNode.
reset | Resets the position of the list to its initial state.

Events

None.

See Also

XMLDOMSelection Object


XMLDOMText Object

Represents the text content of an element or attribute.

Remarks

XML refers to this text content as character data and distinguishes it from markup, the tags that modify that character data. If there is no markup inside an element, that element's text is contained in a single XMLDOMText object that is the child of the element. If there is markup inside an element, it is parsed into child elements that are siblings of the XMLDOMText object(s). (Note that the content of the markup elements also appears within text nodes, which are the children of the specific markup element.)

When a document is first made available to the XML DOM, all text is normalized: there is only one text node for each block of text. Users can create adjacent text nodes that represent the contents of a given element without any intervening markup but should be aware that there is no way to represent the separations between these nodes, so they will not persist between XML DOM sessions.

The normalize method on the XMLDOMElement object merges adjacent text nodes into a single node. You should normalize before starting any operations that depend on a particular document structure to ensure that subsequent sessions will operate on the same structure.

Example

The following VBScript example creates an XMLDOMText object (objText), and inserts it before the first child of the root. The insertBefore method returns another XMLDOMText object, objText1.

Dim xmlDoc
Dim root
Dim objText
Dim objNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
Set objText = xmlDoc.createTextNode ("Hello World")
Set objText1 = root.insertBefore (objText, root.firstChild)
MsgBox root.xml

See Also

Properties, Methods, and Events


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLDOMText Properties, Methods, and Events

Properties

attributes | Contains the list of attributes for this node. Read-only.
baseName* | Returns the base name for the name qualified with the namespace. Read-only.
childNodes | Contains a node list containing the children (for nodes that can have children). Read-only.
data | Contains this node's data, which depends on the node type. Read/write.
dataType* | Specifies the data type for this node. Read/write.
definition* | Returns the definition of the node in the DTD or schema. Read-only.
firstChild | Contains the first child of this node. Read-only.
lastChild | Returns the last child node. Read-only.
length | Specifies the length, in characters, of the data. Read-only.
namespaceURI* | Returns the URI for the namespace. Read-only.
nextSibling | Contains the next sibling of this node in the parent's child list. Read-only.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types. Read-only.
nodeType | Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes. Read-only.
nodeTypedValue* | Contains this node's value, expressed in its defined data type. Read/write.
nodeTypeString* | Returns the node type in string form. Read-only.
nodeValue | Contains the text associated with the node. Read/write.
ownerDocument | Returns the root of the document that contains this node. Read-only.
parentNode | Contains the parent node (for nodes that can have parents). Read-only.
parsed* | Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed. Read-only.
prefix* | Returns the namespace prefix. Read-only.
previousSibling | Contains the left sibling of this node. Read-only.
specified | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema. Read-only.
text* | Contains the text content of the node and its subtrees. Read/write.
xml* | Contains the XML representation of the node and all its descendants. Read-only.

* denotes an extension to the W3C DOM.

Methods

appendChild | Appends newChild as the last child of this node.
appendData | Appends the supplied string to the existing string data.
cloneNode | Creates a new node that is an exact clone of this node.
deleteData | Deletes the specified substring within the string data.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
insertData | Inserts the supplied string at the specified offset.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
selectNodes* | Replaces the specified number of characters with the supplied string.
selectSingleNode* | Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.
specified* | Applies the specified pattern-matching operation to this node's context and returns the first matching node.
splitText | Breaks this text node into two text nodes at the specified offset and inserts the new text node into the tree as a sibling that immediately follows this node.
substringData | Retrieves a substring of the full string from the specified range.
transformNode* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.
transformNodeToObject* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in the supplied object.

* denotes an extension to the W3C DOM.

Events

None.

See Also

XMLDOMText Object


XMLHttpRequest Object

Provides client-side protocol support for communication with HTTP servers.

Remarks

A client computer can use the XMLHttpRequest object (Msxml2.XMLHTTP) to send an arbitrary HTTP request, receive the response, and have the Microsoft XML DOM parse that response.

This object is integrated with the Microsoft XML Parser (MSXML) to support sending the request body directly from, and parsing the response directly into, the MSXML DOM objects. When combined with the support for XSL, the XMLHttpRequest component provides an easy way to send structured queries to HTTP servers and efficiently display the results with a variety of presentations.

The usual sequence is to call open, set any custom header information via setRequestHeader followed by send, and then to check one of the four different response properties.

Example

The following JScript example posts a DOMDocument containing order information to an .asp page on a server and returns the result as a new XML document.

<script language="JScript">
  function PostOrder(xmldoc)
  {
    var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    xmlhttp.Open("POST", "http://myserver/orders/processorder.asp", false);
    xmlhttp.Send(xmldoc);
    return xmlhttp.responseXML;
  }
</script>

The .asp page on the server loads the posted XML document, processes the order, and builds a resulting XML document.

<%@ language=javascript %>
<%
    Response.Expires = -1000;
    // Load the posted XML document.
    var doc = Server.CreateObject("Msxml2.DOMDocument");
    doc.load(Request);
    var result = Server.CreateObject("Msxml2.DOMDocument");
    // Now process the order and build the result document.
    Response.ContentType = "text/xml";
    result.save(Response);
%>

See Also

Properties, Methods, and Events

DOMDocument Object


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XMLHttpRequest Properties, Methods, and Events

Properties

onreadystatechange* | Specifies the event handler to be called when the readyState property changes. Read/write.
readyState | Represents the state of the request. Read-only.
responseBody | Represents the response entity body as an array of unsigned bytes. Read-only.
responseStream | Represents the response entity body as an IStream. Read-only.
responseText | Represents the response entity body as a string. Read-only.
responseXML | Represents the response entity body as parsed by the MSXML parser. Read-only.
status | Represents the HTTP status code returned by a request. Read-only.
statusText | Represents the HTTP response line status. Read-only.

* denotes an extension to the W3C DOM.

Methods

abort | Cancels the current HTTP request.
getAllResponseHeaders | Retrieves the values of all the HTTP headers.
getResponseHeader | Retrieves the value of an HTTP header from the response body.
open | Initializes a Msxml2.XMLHTTP request, and specifies the method, URL, and authentication information for the request.
send | Sends an HTTP request to the server and receives a response.
setRequestHeader | Specifies the name of an HTTP header.

Events

None.

See Also

XMLHttpRequest Object


XSLProcessor Object

Used for transformations with compiled style sheets.

See Also

Properties, Methods, and Events


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XSLProcessor Properties, Methods, and Events

Properties

input | Specifies which XML input tree to transform.
output | Used to provide a custom output object to write the result of the transformation to.
readyState | Returns the current state of the processor.
startMode | Returns the base name part of the start mode.
startModeURI | Returns the namespace URI part of the start mode.
stylesheet | Provides the XSL style sheet that is to be compiled into the XSL template.
ownerTemplate | Returns the style sheet template that was used to create the XSL processor object.

Methods

addObject | Allows you to pass objects to a style sheet.
addParameter | Allows you to pass variables to a style sheet.
reset | Resets the processor state to what it was before calling transform.
setStartMode | Allows you to perform subsets of a larger XSL transformation.
transform | Starts the transformation process or resumes a transform that previously returned VARIANT_FALSE.

Events

None.

See Also

XSLProcessor Object


XSLTemplate Object

Used for caching compiled XSLT templates.

Remarks

Before the Microsoft 2.6 XML Parser (MSXML), every call to transformNode or transformNodeToObject had to recompile the style sheet because the style sheet was simply passed as an XMLDOMNode. You can increase performance by caching the compiled style sheet and reusing it with the XSLTemplate object.

To cache a compiled XSLT style sheet, load an XSLT style sheet into an XSLTemplate object. This object is free-threaded and stateless, so it can be stored in shared .ASP application state. Then to transform a given document using this template, create an XSLProcessor object using the createProcessor method. The XSLProcessor object stores the state for one transform call and has a rental-threading model.

This object is an extension of the W3C DOM.

See Also

Properties, Methods, and Events

Examples


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XSLTemplate Properties, Methods, and Events

Properties

stylesheet Provides the XSL style sheet to compile into an XSL template.

Methods

createProcessor Creates a rental-model XSLProcessor that will use this template.

Events

None.

See Also

XSLTemplate Object


XTLRuntime Object

Implements the following methods, which can be called from within XSLT style sheets:

  • absoluteChildNumber
  • ancestorChildNumber
  • childNumber
  • depth
  • formatDate
  • formatIndex
  • formatNumber
  • formatTime
  • uniqueID

See Also

Properties, Methods, and Events

Microsoft XSLT Reference


Properties, Methods, and Events

Microsoft XML SDK 2.6 - XML Reference

XTLRuntime Properties, Methods, and Events

Properties

attributes | Contains the list of attributes for this node. Read-only.
baseName* | Returns the base name for the name qualified with the namespace. Read-only.
childNodes | Contains a node list containing the children (for nodes that can have children). Read-only.
dataType* | Specifies the data type for this node. Read/write.
definition* | Returns the definition of the node in the DTD or schema. Read-only.
firstChild | Contains the first child of this node. Read-only.
lastChild | Returns the last child node. Read-only.
namespaceURI* | Returns the URI for the namespace. Read-only.
nextSibling | Contains the next sibling of this node in the parent's child list. Read-only.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types. Read-only.
nodeType | Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes. Read-only.
nodeTypedValue* | Contains this node's value expressed in its defined data type. Read/write.
nodeTypeString* | Returns the node type in string form. Read-only.
nodeValue | Contains the text associated with the node. Read/write.
ownerDocument | Returns the root of the document that contains this node. Read-only.
parentNode | Contains the parent node (for nodes that can have parents). Read-only.
parsed* | Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed. Read-only.
prefix* | Returns the namespace prefix. Read-only.
previousSibling | Contains the left sibling of this node. Read-only.
specified* | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema. Read-only.
text* | Contains the text content of the node and its subtrees. Read/write.
xml* | Contains the XML representation of the node and all its descendants. Read-only.

* denotes an extension to the W3C DOM.

Methods

absoluteChildNumber | Returns the number of the node relative to all siblings.
ancestorChildNumber | Returns the number of the nearest ancestor of a node with the requested node name.
appendChild | Appends newChild as the last child of this node.
childNumber | Returns the number of the node relative to siblings of the same node name.
cloneNode | Creates a new node that is an exact clone of this node.
depth | Returns the depth within the document tree at which the specified node appears.
formatDate | Formats the supplied date using the specified formatting options.
formatIndex | Formats the supplied integer using the specified numerical system.
formatNumber | Formats the supplied number using the specified format.
formatTime | Formats the supplied time using the specified formatting options.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
selectNodes* | Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.
selectSingleNode* | Applies the specified pattern-matching operation to this node's context and returns the first matching node.
transformNode* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.
transformNodeToObject* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in the supplied object.
uniqueID | Returns the unique identifier for the supplied node.

* denotes an extension to the W3C DOM.

Events

None.

See Also

XTLRuntime Object


XML DOM Collections

This section includes the following topics:

  • childNodes Collection


childNodes Collection

Contains a node list containing the children (for nodes that can have children).

objXMLDOMNodeList = oXMLDOMNode.childNodes

Remarks

Object. The collection is read-only. Note that an XMLDOMNodeList object is returned even if there are no children of the node. In such a case, the length of the list will be set to 0. For information about valid child node types for each node, see the DOMNodeType enumeration.

This value depends on the value of the nodeType property.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_ELEMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE | Returns an XMLDOMNodeList that contains a list of all child nodes for the specified node.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns an XMLDOMNodeList with a length of 0. These node types cannot have children.
NODE_DOCUMENT_TYPE | Returns an XMLDOMNodeList that contains a list of all child nodes for the XMLDOMDocumentType node. The node list for the document type node can contain entities and notations.

Example

The following example uses the childNodes collection to return an XMLDOMNodeList object, and then iterates through the collection, displaying the value of each item's xml property:

Dim xmlDoc
Dim root
Dim oNodeList
Dim Item
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmldoc.documentElement
Set oNodeList = root.childNodes
For Each Item in oNodeList
  MsgBox Item.xml
Next

See Also

Using Collections

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


XML DOM Properties

This section includes the following topics:

  • async Property
  • attributes Property
  • baseName Property
  • context Property
  • data Property (CharacterData)
  • data Property (Processing Instruction)
  • dataType Property
  • definition Property
  • doctype Property
  • documentElement Property
  • entities Property
  • errorCode Property
  • expr Property
  • filepos Property
  • firstChild Property
  • implementation Property
  • input Property
  • lastChild Property
  • length Property (SchemaCache)
  • length Property (CharacterData)
  • length Property (NamedNodeMap)
  • length Property (NodeList)
  • line Property
  • linepos Property
  • name Property (Attribute)
  • name Property (DocumentType)
  • namespaces Property
  • namespaceURI Property (Node)
  • namespaceURI Property (SchemaCache)
  • nextSibling Property
  • nodeName Property
  • nodeType Property
  • nodeTypedValue Property
  • nodeTypeString Property
  • nodeValue Property
  • notationName Property
  • notations Property
  • ondataavailable Property
  • onreadystatechange Property (Document)
  • onreadystatechange Property (HttpRequest)
  • ontransformnode Property
  • output Property
  • ownerDocument Property
  • ownerTemplate Property
  • parentNode Property
  • parsed Property
  • parseError Property
  • prefix Property
  • preserveWhiteSpace Property
  • previousSibling Property
  • publicId Property (Entity)
  • publicId Property (Notation)
  • readyState Property (Document)
  • readyState Property (HttpRequest)
  • readyState Property (XSLProcessor)
  • reason Property
  • resolveExternals Property
  • responseBody Property
  • responseStream Property
  • responseText Property
  • responseXML Property
  • specified Property
  • srcText Property
  • startMode Property
  • startModeURI Property
  • status Property
  • statusText Property
  • stylesheet Property (XSLProcessor)
  • stylesheet Property (XSLTemplate
  • systemId Property (Entity)
  • systemId Property (Notation)
  • tagName Property
  • target Property
  • text Property
  • url Property (Document)
  • url Property (ParseError)
  • validateOnParse Property
  • value Property
  • xml Property


async Property

Indicates whether asynchronous download is permitted.

boolValue = oXMLDOMDocument.async
objXMLDOMDocument.async = boolValue

Remarks

Boolean. The property is read/write. Returns True if asynchronous download is permitted; False if not. Default is True.

When set to True, the load method returns control to the caller before the download is finished. You can then use the readyState property to check the status of the download. You can also attach an onreadystatechange handler or connect to the onreadystatechange event to be notified when the ready state changes so that you know when the download is complete.

This member is an extension of the W3C DOM.

Example

The following VBScript example sets the async property of a DOMDocument object to False before loading sample.xml:

Dim xmlDoc
xmlDoc.async = false
xmlDoc.load("sample.xml")

See Also

Applies To: DOMDocument Object


attributes Property

Contains the list of attributes for this node.

objXMLDOMNamedNodeMap = oXMLDOMNode.attributes

Remarks

Object. The property is read-only. Returns an XMLDOMNamedNodeMap object for nodes that can return attributes (Element, Entity, and Notation nodes). Returns Null for all other node types. Note that for the valid node types, the NamedNodeMap is always returned; when there are no attributes on the element, the list length is set to zero.

This value depends on the value of the nodeType property.

NODE_ATTRIBUTE, NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_DOCUMENT_TYPE, NODE_ENTITY_REFERENCE, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Always returns Null.
NODE_ELEMENT | Returns an XMLDOMNamedNodeMap object that contains a list of nodes corresponding to the attributes of the element.
NODE_ENTITY, NODE_NOTATION | Returns an XMLDOMNamedNodeMap object that exposes the PUBLIC ID, SYSTEM ID, and NDATA as attributes.

Example

The following VBScript example creates an XMLDOMNamedNodeMap object from a document's attributes property, and then displays the number of nodes in the object:

Dim xmlDoc
Dim oNamedNodeMap
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set oNamedNodeMap = xmlDoc.documentElement.attributes
MsgBox oNamedNodeMap.length

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


baseName Property

Returns the base name for the name qualified with the namespace.

strValue = oXMLDOMNode.baseName

Remarks

String. The property is read-only. It returns the right-hand side of a namespace qualified name. For example, it returns "yyy" for the element <xxx:yyy>. It always returns a non-empty string.

This member is an extension of the W3C DOM.

Example

The following VBScript example assigns the value of a node's baseName property to a string, then displays it:

Dim xmlDoc
Dim MyStr
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
MyStr = xmldoc.documentElement.childNodes.item(2).baseName
MsgBox MyStr

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


context Property

Gets the node (subtree) that is applied to the selection.

Set objXMLDOMNode = objXMLDOMSelection.context
objXMLDOMSelection.context = objXMLDOMNode

Remarks

Because the selectNodes method can be called on any node type, context can also be assigned any node type. A node from a different document than the one that originally created this XMLDOMSelection object can also be specified as long as it has the same threading model. Calling context also resets the state of the node list so that nextNode starts over.

See Also

Applies To: XMLDOMSelection Object


data Property (XMLDOMCharacterData)

Stores this node's data, which depends on the node type.

strValue = oXMLDOMCharacterData.data
objXMLDOMCharacterData.data = strValue

Remarks

String. The property is read/write. It contains the same value as the nodeValue for this node. The meaning of the value depends on the XMLDOMNode object's nodeType property, as follows:

NODE_CDATA_SECTION | A string representing the text stored in the CDATA section.
NODE_COMMENT | The content of the comment, exclusive of the comment start and end sequence.
NODE_TEXT | A string representing the text stored in the text node.

Example

The following VBScript example walks the document tree and checks for comment node types. If one is found, it displays its contents with the data property.

Dim xmlDoc
Dim comment
Dim root
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
  For i = 0 To (root.childNodes.length -1)
    If (root.childNodes.item(i).nodeType = 8) Then
      MsgBox (root.childNodes.item(i).data)
    End If
  Next

See Also

Applies To: XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | XMLDOMText Object


data Property (XMLDOMProcessingInstruction)

Stores the content of the processing instruction, excluding the target.

strValue = oXMLDOMProcessingInstruction.data
objXMLDOMProcessingInstruction.data = strValue

Remarks

String. This property is read/write. The data has the same value as the nodeValue property.

Example

The following VBScript example iterates through the document's child nodes. If it finds a node of type NODE_PROCESSING_INSTRUCTION (7), it displays the node's data.

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.load("c:\books.xml")
For i = 0 TO (xmlDoc.childNodes.length -1)
  If xmlDoc.childNodes.item(i).nodeType = 7 Then
    MsgBox xmlDoc.childNodes.item(i).data
  End If
Next

See Also

Applies To: XMLDOMProcessingInstruction Object


dataType Property

Specifies the data type for this node.

objValue = oXMLDOMNode.dataType
objXMLDOMNode.dataType = objValue

Remarks

Variant. The property is read/write. The VARIANT can be a BSTR that contains the data type name, or VT_NULL if no data type is defined.

This value depends on the value of the XMLDOMNode object's nodeType property.

This member is an extension of the W3C DOM.

NODE_ATTRIBUTE | Contains the string representation of the data type specifier included in the schema, or Null if no data type is specified.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_DOCUMENT_TYPE, NODE_ENTITY, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Contains the constant string value "string." These node types do not have data types, so the dataType property cannot be set for these node types. Attempts to set this property are ignored.
NODE_ELEMENT | Contains the string representation of the data type specifier included on the instance of the element or in the schema, or Null if no data type is specified.
NODE_ENTITY_REFERENCE | Contains the string representation of the data type specifier for the referenced entity. This is limited to entities with a single data type on the root level element of the subtree corresponding to the entity. Contains Null if no data type is specified. It is not possible to set the data type of an entity reference. The data type must be set directly on the referenced element. Attempts to set the value of the dataType property are ignored.

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | DOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


definition Property

Returns the definition of the node in the DTD or schema.

objXMLDOMNode = oXMLDOMNode.definition

Remarks

Object. The property is read-only. Returns the node that contains the definition for the entity referenced.

The definition property is not supported when using in-line schemas. This behavior is by design because the node being referenced may be deleted or modified after the document is loaded, which would yield unpredictable results.

This value depends on the value of the nodeType property.

NODE_ENTITY_REFERENCE | Returns the node for the entity referenced; that is, the ENTITY element that was defined for a given ENTITYREF. Given the ENTITYREF &x;, for example, the definition property returns the node in the DOCTYPE node's entity collection that defines the corresponding ENTITY: <!ENTITY x "y">.
NODE_ENTITY | For unparsed entities, returns the NOTATION definition from the DOCTYPE node's notation collection. For example, given the ENTITY <!ENTITY networth SYSTEM "networth.xls" NDATA XLS>, the definition property returns the node corresponding to the notation <!NOTATION XLS PUBLIC "http://www.microsoft.com/office/excel/">. For parsed entities, returns Null.
NODE_ATTRIBUTE | Returns the XML-Data Schema AttributeType for a given ATTRIBUTE node. For example, the definition property for the attribute myAttribute="123" returns the node corresponding to the element <AttributeType name= "myAttribute"> in the schema. Returns Null when a DTD is used or when no schema is present.
NODE_ELEMENT | Returns the XML-Data Schema ElementType for a given ELEMENT node. For example, given the element <myelement>, the definition property returns the node <ElementType name="myelement"> in the specified external schema. Returns Null when a DTD is used or when no schema is present.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_DOCUMENT_TYPE, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns Null.

This member is an extension of the W3C DOM.

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


doctype Property

Contains the document type node that specifies the DTD for this document.

objXMLDOMDocumentType = oXMLDOMDocument.doctype

Remarks

Object. The property is read-only. For XML, it points to the node of type _TYPE that specifies the DTD. It returns Null for XML documents without a DTD and for HTML documents.

An XML document can contain a document type declaration before the first element in the document. It starts with the tag <!DOCTYPE> and can specify an external DTD.

Example

The following VBScript example creates an XMLDOMDocumentType object, and then displays the name property of the object:

Dim xmlDoc
Dim MyDocType
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set MyDocType = xmlDoc.doctype
MsgBox (mydoctype.name)

See Also

XMLDOMDocumentType Object

Applies To: DOMDocument Object


documentElement Property

Contains the root element of the document.

objXMLDOMElement = oXMLDOMDocument.documentElement
objXMLDOMDocument.documentElement = objXMLDOMElement

Remarks

Object. The property is read/write. It returns the XMLDOMElement object that represents the single element that represents the root of the XML document tree. It returns Null if no root exists.

When setting the documentElement property, the specified element node is inserted into the child list of the document after any document type node. To precisely place the node within the children of the document, call the XMLDOMNode object's insertBefore method.

The parentNode property is reset to the document node as a result of this operation.

Example

The following VBScript example creates an XMLDOMElement object and sets it to the root element of the document with the documentElement property. It then walks the document tree.

Dim root
Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
For i = 0 To (root.childNodes.length -1)
  MsgBox (root.childNodes.item(i).childNodes.item(0).text)
Next

See Also

Applies To: DOMDocument Object


entities Property

Contains a list of the entities, both external and internal, that are declared in the document type declaration.

objXMLDOMNamedNodeMap = oXMLDOMDocumentType.entities

Remarks

Object. The property is read-only. In the following document type declaration, for example, the node list in this property contains the entities "bax" and "bay" but not "baz."

<!DOCTYPE ex SYSTEM "ex.dtd" [  <!ENTITY bax "bax">  <!ENTITY bay "bay">
  <!ENTITY % baz "baz">]>

See Also

Applies To: XMLDOMDocumentType Object


errorCode Property

Contains the error code of the last parse error.

lValue = oXMLDOMParseError.errorCode

Remarks

Long integer. The property is read-only. It returns the error code in decimal.

Example

The following VBScript example attempts to load an XML document. If it encounters a parse error, it displays the error code:

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
If xmlDoc.parseError.errorcode <> 0 Then
   MsgBox xmlDoc.parseError.errorCode
Else
  MsgBox xmlDoc.documentElement.xml
End If

See Also

Applies To: XMLDOMParseError Object


expr Property

Gets or sets the XPath expression string.

strExpression = objXMLDOMSelection.expr
objXMLDOMSelection.expr = strExpression

Remarks

Setting a new expression immediately resets the state of this node list to the beginning unless the expression is invalid and an error is returned, in which case it has no effect. It does not reset the context.

See Also

Applies To: XMLDOMSelection Object


filepos Property

Contains the absolute file position where the error occurred.

lValue = oXMLDOMParseError.filepos

Remarks

Long integer. The property is read-only.

Example

The following VBScript example attempts to load an XML document. If it encounters a parse error, it displays the value of the filepos property.

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
If xmlDoc.parseError.errorcode <> 0 Then
   MsgBox xmlDoc.parseError.filepos
Else
  MsgBox xmlDoc.documentElement.xml
End If

See Also

Applies To: XMLDOMParseError Object


firstChild Property

Contains the first child of this node.

objXMLDOMNode = oXMLDOMNode.firstChild

Remarks

Object. The property is read-only. Returns the first child node. If there are no such children, returns Null.

This value depends on the value of the nodeType property.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_DOCUMENT_TYPE, NODE_ELEMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE | Returns the first child node. If there are no children, returns Null.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns Null. These node types cannot have children.

Example

The following VBScript example sets currNode to the first child node of the top-level node:

Dim xmlDoc
Dim currNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set currNode = xmlDoc.documentElement.firstChild
MsgBox currNode.xml

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


implementation Property

Contains the XMLDOMImplementation object for this document.

objXMLDOMImplementation = oXMLDOMDocument.implementation

Remarks

Object. The property is read-only. An XML DOM application can use objects from multiple implementations. This property provides access to the XMLDOMImplementation object that handles this document.

Example

The following VBScript example creates an XMLDOMImplementation object:

Dim xmlDoc
Dim i
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set i = xmlDoc.implementation

See Also

Applies To: DOMDocument Object


input Property

Specifies which XML input tree to transform. Setting this property resets the state of the processor.

Set objXMLDOMNode = objXSLProcessor.input

See Also

Applies To: XSLProcessor Object


lastChild Property

Returns the last child node.

objXMLDOMNode = oXMLDOMNode.lastChild

Remarks

Object. The property is read-only. If there are no children, it returns Null.

This value depends on the value of the nodeType property.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_DOCUMENT_TYPE, NODE_ELEMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE | Returns the last child node. If there are no children, returns Null.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns Null. These node types cannot have children.

Example

The following VBScript example creates a new XMLDOMNode (element) object, and then inserts it before the last child of the top-level node:

Dim xmlDoc
Dim root
Dim newNode
Dim currNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
MsgBox root.xml
Set newNode = xmlDoc.createNode (1, "VIDEOS", "")
Set currNode = root.insertBefore(newNode, root.lastChild)
MsgBox root.xml

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


length Property (XMLSchemaCache)

Returns the number of namespaces currently in the collection.

lValue = oXMLDOMSchemaCache.length

Remarks

Long integer. The property is read-only.

See Also

Applies To: XMLSchemaCache


length Property (XMLDOMCharacterData)

Specifies the length, in characters, of the data.

lValue = oXMLDOMCharacterData.length

Remarks

Long integer. The property is read-only. It specifies the length, in Unicode characters, of the string present in the data property.

Example

The following VBScript example creates an XMLDOMComment object and then assigns the length property to a variable:

Dim xmlDoc
Dim comment
Dim lValue
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set comment = xmlDoc.createComment("Hello World!")
lValue = comment.length
MsgBox lValue

See Also

Applies To: XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | XMLDOMText Object


length Property (XMLDOMNamedNodeMap)

Indicates the number of items in the collection.

lValue = oXMLDOMNamedNodeMap.length

Remarks

Long integer. The property is read-only.

See Also

Applies To: XMLDOMNamedNodeMap Object


length Property (XMLDOMNodeList)

Indicates the number of items in the collection.

lValue = oXMLDOMNodeList.length

Remarks

Long integer. The property is read-only.

Example

The following VBScript example creates an XMLDOMNodeList object and then uses its length property to support iteration:

Dim xmlDoc
Dim objNodeList
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\xmlinact\books.xml")
Set objNodeList = xmlDoc.getElementsByTagName("AUTHOR")
For i=0 To (objNodeList.length -1)
  MsgBox (objNodeList.item(i).text)
Next

See Also

Applies To: XMLDOMNodeList Object | XMLDOMSelection Object


line Property

Specifies the line number that contains the error.

lValue = oXMLDOMParseError.line

Remarks

Long integer. The property is read-only.

Example

The following VBScript example attempts to load an XML document. If it encounters a parse error, it displays the line where the error occurred.

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
If xmlDoc.parseError.errorcode <> 0 Then
  MsgBox ("A parse error occurred on line " & xmlDoc.parseError.line)
Else
  MsgBox xmlDoc.documentElement.xml
End If

See Also

Applies To: XMLDOMParseError Object


linepos Property

Contains the character position within the line where the error occurred.

lValue = oXMLDOMParseError.linepos

Remarks

Long integer. The property is read-only.

Example

The following VBScript example attempts to load an XML document. If it encounters a parse error, it displays the line and position within that line where the error occurred.

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
If xmlDoc.parseError.errorcode <> 0 Then
  MsgBox ("A parse error occurred on line " & xmlDoc.parseError.line _
  & " at position " & xmlDoc.parseError.linepos)
Else
  MsgBox xmlDoc.documentElement.xml
End If

See Also

Applies To: XMLDOMParseError Object


name Property (XMLDOMAttribute)

Contains the attribute name.

strValue = oXMLDOMAttribute.name

Remarks

String. The property is read-only. It returns the name of the attribute. The value is the same as the XMLDOMNode object's nodeName property.

Example

The following VBScript example creates an XMLDOMAttribute object from the first attribute of the root, and then displays the value of its name:

Dim xmlDoc
Dim root
Dim objDOMatt
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
Set objDOMatt = root.attributes.item(0)
MsgBox (objDOMatt.name)

See Also

Applies To: XMLDOMAttribute Object


name Property (XMLDOMDocumentType)

Contains the name of the document type.

strValue = oXMLDOMDocumentType.name

Remarks

String. The property is read-only. When specified, this corresponds to the single root element of the document tree.

See Also

Applies To: XMLDOMDocumentType Object


namespaces Property

Returns the list of namespaces used in the document as an XMLSchemaCache object. If there are no schemas, it returns an empty collection object.

Set objXMLDOMSchemaCache = objXMLDOMDocument2.namespaces

Remarks

There is one item in this list for each unique namespace declaration in the document. The schema collection returned may have an associated schema document for each namespace listed. This list is read-only. Any schema documents returned in this collection are also read-only.

The order of the namespaces returned does not necessarily match the order in which they appear in the document. The collection returned will change as new namespaces are added or removed via the DOM and in that sense it is a "live" list like XMLDOMNodeList.

This collection is completely reset each time you call load or loadXML.

Example

// Load a document (and any schemas referenced via X-Schema).
xmldoc.load ("somedata.xml");
// Get the namespace of the root element.
var namespaceURI = xmldoc.documentElement.namespaceURI
// Find the schema associated with the namespace.
var schema = xmldoc.namespaces.get(namespaceURI);

See Also

Applies To: XMLDOMSchemaCache


namespaceURI Property (XMLDOMNode)

Returns the URI for the namespace.

strValue = oXMLDOMNode.namespaceURI

Remarks

String. The property is read-only. Returns the URI (universal resource identifier) for the namespace. This refers to the "uuu" portion of the namespace declaration xmlns:nnn="uuu".

This member is an extension of the W3C DOM.

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


namespaceURI Property (XMLSchemaCache)

Returns the namespace at the specified index.

strNameSpaceURI = objXMLDOMSchemaCache.namespaceURI(index)

Parameters

index
The index between 0 and (count-1).

Remarks

String. namespaceURI allows you to iterate through the collection to discover its contents. You can then use the get(namespace) method to see if there is an associated schema.

This is the default collection property, so you can do the following:

var uri = xmldoc.namespaces(1)

See Also

Applies To: XMLSchemaCache


nextSibling Property

Contains the next sibling of this node in the parent's child list.

Set objXMLDOMNode = oXMLDOMNode.nextSibling

Remarks

Object. The property is read-only. It returns the right sibling of this node.

This value depends on the value of the nodeType property.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT | Always returns Null; these node types do not appear as children of any other nodes.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT_TYPE, NODE_ELEMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns the node immediately following this node in the list of children of this node's parent. If no such node exists, returns Null.

Example

The following VBScript example creates an XMLDOMNode object and sets it to the next sibling of the current node:

Dim xmlDoc
Dim currNode
Dim nextNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set currNode = xmlDoc.documentElement.childNodes.item(0)
Set nextNode = currNode.nextSibling
MsgBox nextNode.xml

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


nodeName Property

Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types.

strValue = oXMLDOMNode.nodeName

Remarks

String. The property is read-only and always contains a non-empty string. The nodeName property contains the qualified name for the element, attribute, or entity reference. For example, it returns xxx:yyy for the element <xxx:yyy>.

The node name value varies, depending on the nodeType property.

NODE_ATTRIBUTE | Contains the name of the attribute.
NODE_CDATA_SECTION | Contains the literal string "#cdata-section".
NODE_COMMENT | Contains the literal string "#comment".
NODE_DOCUMENT | Contains the literal string "#document".
NODE_DOCUMENT_TYPE | Contains the name of the document type; for example, xxx in <!DOCTYPE xxx ...>.
NODE_DOCUMENT_FRAGMENT | Contains the literal string "#document-fragment".
NODE_ELEMENT | Contains the name of the XML tag, with any namespace prefix included if present.
NODE_ENTITY | Contains the name of the entity.
NODE_ENTITY_REFERENCE | Contains the name of the entity referenced. Note that the name does not include the leading ampersand or the trailing semicolon. The name includes the namespace if one is present.
NODE_NOTATION | Contains the name of the notation.
NODE_PROCESSING_INSTRUCTION | Contains the target; the first token following the <? characters.
NODE_TEXT | Contains the literal string "#text".

Example

The following VBScript example creates an XMLDOMNode object and displays its name:

Dim xmlDoc
Dim currNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set currNode = xmlDoc.documentElement.childNodes.item(0)
MsgBox currNode.nodeName

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


nodeType Property

Specifies the XML DOM node type, which determines valid values and whether the node can have child nodes.

lValue = oXMLDOMNode.nodeType

Remarks

Enumeration. The property is read-only. It indicates the type of the node. Use the nodeTypeString property to return the node type in string form.

The DOMNodeType enumeration defines the following valid values that can appear in the nodeType property:

NODE_ELEMENT (1) | The node represents an element. An element node can have the following child node types: Element, Text, Comment, ProcessingInstruction, CDATASection, and EntityReference. An element node can be the child of the Document, DocumentFragment, EntityReference, and Element nodes.
NODE_ATTRIBUTE (2) | The node represents an attribute of an element. An attribute node can have the following child node types: Text and EntityReference. An attribute does not appear as the child node of any other node type; note that it is not considered a child node of an element.
NODE_TEXT (3) | The node represents the text content of a tag. A text node cannot have any child nodes. A text node can appear as the child node of the Attribute, DocumentFragment, Element, and EntityReference nodes.
NODE_CDATA_SECTION (4) | The node represents a CDATA section in the XML source. CDATA sections are used to escape blocks of text that would otherwise be recognized as markup. A CDATA section node cannot have any child nodes. A CDATA section node can appear as the child of the DocumentFragment, EntityReference, and Element nodes.
NODE_ENTITY_REFERENCE (5) | The node represents a reference to an entity in the XML document. This applies to all entities, including character entity references. An entity reference node can have the following child node types: Element, ProcessingInstruction, Comment, Text, CDATASection, and EntityReference. An entity reference node can appear as the child of the Attribute, DocumentFragment, Element, and EntityReference nodes.
NODE_ENTITY (6) | The node represents an expanded entity. An entity node can have child nodes that represent the expanded entity (for example, Text and EntityReference nodes). An entity node can appear as the child of the DocumentType node.
NODE_PROCESSING_INSTRUCTION (7) | The node represents a processing instruction (PI) from the XML document. A PI node cannot have any child nodes. A PI node can appear as the child of the Document, DocumentFragment, Element, and EntityReference nodes.
NODE_COMMENT (8) | The node represents a comment in the XML document. A comment node cannot have any child nodes. A comment node can appear as the child of Document, DocumentFragment, Element, and EntityReference nodes.
NODE_DOCUMENT (9) | The node represents a document object, which, as the root of the document tree, provides access to the entire XML document. It is created using the ProgID "Msxml2.DOMDocument", or through a data island using <XML> or <SCRIPT LANGUAGE=XML>. A document node can have the following child node types: Element (maximum of one), ProcessingInstruction, Comment, and DocumentType. A document node cannot appear as the child of any node types.
NODE_DOCUMENT_TYPE (10) | The node represents the document type declaration, indicated by the <!DOCTYPE > tag. A document type node can have the following child node types: Notation and Entity. A document type node can appear as the child of the Document node.
NODE_DOCUMENT_FRAGMENT (11) | The node represents a document fragment. A document fragment node associates a node or subtree with a document without actually being contained within the document. A document fragment node can have the following child node types: Element, ProcessingInstruction, Comment, Text, CDATASection, and EntityReference. A DocumentFragment node cannot appear as the child of any node types.
NODE_NOTATION (12) | A node represents a notation in the document type declaration. A notation node cannot have any child nodes. A notation node can appear as the child of the DocumentType node.

Example

The following VBScript example creates an XMLDOMNode object and displays its type enumeration (in this case 1, for NODE_ELEMENT):

Dim xmlDoc
Dim currNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set currNode = xmlDoc.documentElement.childNodes.item(0)
MsgBox currNode.nodeType

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


nodeTypedValue Property

Contains this node's value expressed in its defined data type.

objValue = oXMLDOMNode.nodeTypedValue
objXMLDOMNode.nodeTypedValue = objValue

Remarks

Variant. The property is read/write. It returns the value of this node in its defined data type, if such a data type is defined. If the node has no content, this property returns Null.

This value depends on the value of the XMLDOMNode object's nodeType property.

This member is an extension of the W3C DOM.

NODE_ATTRIBUTE | Returns the value in the data representation of the type specified in the schema for the attribute. (Data types are specified using the dt:dt attribute, and because attributes cannot be applied to attributes, the attribute's data type can be specified only in the schema.) If no type was specified, returns a string identical to the nodeValue property. If the new value is not of the type specified in the schema or cannot be coerced to the type specified in the schema, then an error occurs.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_DOCUMENT_TYPE, NODE_ENTITY, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION | Returns a string that is equal to the nodeValue property. These node types do not have data types.
NODE_ELEMENT | Returns the value in the data representation of the type specified on the element instance (using the dt:dt attribute) or specified in the schema. If no type was specified on the instance or in the schema, returns a string identical to the nodeValue property. If the element has an attribute data type, returns null.
NODE_ENTITY_REFERENCE | Returns the value in the data representation of the type specified for the referenced entity. This is limited to entities with a single data type on the root level element of the subtree corresponding to the entity. If no data type was specified, returns a string identical to the nodeValue property.
NODE_TEXT | Returns the value in the data representation of the type of the containing element. If the containing element was not typed, returns a string identical to the nodeValue property.

Example

Dim xmlDoc As New MSXML2.FreeThreadedDOMDocument
Dim xmlDocTest As New MSXML2.FreeThreadedDOMDocument
Dim strDestination As String
Dim childNode

Set xmlDoc.documentElement = xmlDoc.createElement("Test")
strDestination = "c:\test.xml"
Set childNode = xmlDoc.createNode(3, "", "")
xmlDoc.documentElement.appendChild childNode
xmlDoc.documentElement.dataType = "bin.hex"
childNode.nodeTypedValue = "ffab123d"
xmlDoc.save strDestination
xmlDocTest.async = False
xmlDocTest.Load strDestination
MsgBox xmlDocTest.xml

Result (in a messagebox)

<Test xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="bin.hex">ffab123d</Test>

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


nodeTypeString Property

Returns the node type in string form.

strValue = oXMLDOMNode.nodeTypeString

Remarks

String. The property is read-only. It contains the string version of the node type. To return the enumeration value, use the nodeType property.

This value depends on the value of the nodeType property:

NODE_ATTRIBUTE | attribute
NODE_CDATA_SECTION | cdatasection
NODE_COMMENT | comment
NODE_DOCUMENT | document
NODE_DOCUMENT_FRAGMENT | documentfragment
NODE_DOCUMENT_TYPE | documenttype
NODE_ELEMENT | element
NODE_ENTITY | entity
NODE_ENTITY_REFERENCE | entityreference
NODE_NOTATION | notation
NODE_PROCESSING_INSTRUCTION | processinginstruction
NODE_TEXT | text

This member is an extension of the W3C DOM.

Example

The following VBScript example creates an XMLDOMNode object and displays its node type in string form (in this case, "element"):

Dim xmlDoc
Dim currNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set currNode = xmlDoc.documentElement.childNodes.item(0)
MsgBox currNode.nodeTypeString

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


nodeValue Property

Contains the text associated with the node.

objValue = oXMLDOMNode.nodeValue
objXMLDOMNode.nodeValue = objValue

Remarks

Variant. The property is read/write.

This value depends on the value of the nodeType property.

NODE_ATTRIBUTE | Contains a string representing the value of the attribute. For attributes with subnodes, this is the concatenated text of all subnodes with entities expanded. Setting this value deletes all children of the node and replaces them with a single text node containing the value written.
NODE_CDATA_SECTION | Contains a string representing the text stored in the CDATA section.
NODE_COMMENT | Contains the content of the comment, exclusive of the comment's begin and end sequence.
NODE_DOCUMENT, NODE_DOCUMENT_TYPE, NODE_DOCUMENT_FRAGMENT, NODE_ELEMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE, NODE_NOTATION | Contains NULL. Note that attempting to set the value of nodes of these types generates an error.
NODE_PROCESSING_INSTRUCTION | Contains the content of the processing instruction, excluding the target. (The target appears in the nodeName property.)
NODE_TEXT | Contains a string representing the text stored in the text node.

Example

The following VBScript example creates an XMLDOMNode object and tests if it is a comment node. If it is, it displays its value:

Dim xmlDoc
Dim currNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set currNode = xmlDoc.documentElement.childNodes.item(0)
If currNode.nodeTypeString = "comment" Then
  MsgBox currNode.nodeValue
End If

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


notationName Property

Contains the notation name.

strValue = oXMLDOMEntity.notationName

Remarks

String. The property is read-only. For unparsed entities, the property contains the name of the notation for the entity (the name following NDATA). For parsed entities, it contains the empty string. Note that the name of the entity is available using the nodeName property.

See Also

Applies To: XMLDOMEntity Object


notations Property

Contains a list of the XMLDOMNotation objects present in the document type declaration.

objXMLDOMNamedNodeMap = oXMLDOMDocumentType.notations

Remarks

Object. The property is read-only. Notations identify by name the format of unparsed entities, the format of elements that bear a notation attribute, or the application to which a processing instruction is addressed.

See Also

Applies To: DOMDocumentType Object


ondataavailable Property

Specifies the event handler for the ondataavailable event.

objXMLDOMDocument.ondataavailable = value

Remarks

The property is write-only. When handling asynchronous data, it is useful to be able to start processing in parallel with the download as soon as data becomes available. The ondataavailable event fires each time a new chunk of data arrives. The readyState property defines several states that specify the current status of the asynchronous download.

This member is an extension of the W3C DOM.

See Also

Applies To: DOMDocument Object


onreadystatechange Property (DOMDocument)

Specifies the event handler to be called when the readyState property changes.

objXMLDOMDocument.onreadystatechange = value

Remarks

The property is write-only. When using scripting languages, this property can be set in ways other than directly accessing the property through the DOMDocument object. It can also be set using the onreadystatechange attribute of the <XML> tag, and the SCRIPT FOR... construct.

This member is an extension of the W3C DOM.

Example

The following JScript/HTML example specifies the handler CheckState gets called when the readyState property changes:

<script>
var xmldoc;
function Load(){
  xmldoc = new ActiveXObject("Msxml2.DOMDocument");
  xmldoc.onreadystatechange = CheckState;
  xmldoc.load(URL.value);
}
function CheckState(){
  var state = xmldoc.readyState;
  RESULTS.innerHTML += "readyState = " + state + "<BR>"
  if (state == 4){
    var err = xmldoc.parseError;
    if (err.errorCode != 0)
      RESULTS.innerHTML += err.reason +  "<BR>"
    else RESULTS.innerHTML += "Success" +  "<BR>"
  }
}
</script>
URL: <input type=text size=60 id=URL><input type=button value=LOAD onclick=jscript:Load()><div id=RESULTS style="color:red; font-weight:bold;"></div></script>

In VBScript, you can get a function pointer using the syntax getRef("HandleStateChange").

See Also

Applies To: DOMDocument Object


onreadystatechange Property (XMLHttpRequest)

Specifies the event handler to be called when the readyState property changes.

objXMLHttpRequest.onreadystatechange = value

Remarks

The property is write-only. When using scripting languages, this property can be set in ways other than directly accessing the property through the XMLHttpRequest object. It can also be set using the onreadystatechange attribute of the <XML> tag, and the SCRIPT FOR... construct.

This member is an extension of the W3C DOM.

Example

The following JScript/HTML example specifies the the handler HandleStateChange gets called when an XMLHttpRequest object's readyState property changes. A button on a page is enabled when readyState indicates that all data has been received (readystate == 4).

<script>
var xmlhttp=null;
function PostOrder(xmldoc)
{
  var xmlhttp = new ActiveXObject("Msxml2.XMLHttp");
  xmlhttp.Open("POST", "http://myserver/orders/processorder.asp", false);
  xmlhttp.onreadystatechange= HandleStateChange;
  xmlhttp.Send(xmldoc);
  myButton.disabled = true;
}
function HandleStateChange()
{
  if (xmlhttp.readyState == 4)
  {
    myButton.disabled = false;
    alert("Result = " + xmlhttp.responseXML.xml);
  }
}
</script>

In VBScript, you can get a function pointer using the syntax getRef("HandleStateChange").

See Also

Applies To: XMLHttpRequest Object


ontransformnode Property

Specifies the event handler for the ontransformnode event.

objXMLDOMDocument.ontransformnode = value

Remarks

The property is write-only. A transformation involves two trees: the XML source and the style sheet that is applied to it. The ontransformnode event fires before each node in the style sheet is applied to each node in the XML source.

The ontransformnode property provides a way for developers to specify their own event handler that overrides the default handler.

This member is an extension of the W3C DOM.

Example

In this example, the XML data island named "target" contains the XML source data, and the XML data island named "ss" contains the XSLT style sheet.

<XML id=target>
    <PEOPLE>
        <PERSON name="Atkinson, Teresa"  />
<PERSON name="Debroux, Steve" />
<PERSON name="O'Hara, Robert" />
<PERSON name="Porzuczek, Peter" />
    </PEOPLE>
</XML>
<XML id=ss>
  <xsl:template match="/" xmlns:xsl="http://www.w3.org/TR/WD-xsl">
   <xsl:for-each select="/PEOPLE/PERSON/@name" >
<P><xsl:value-of /></P>
   </xsl:for-each>
  </xsl:template>
</XML>

This script contains the Verify function, which sets the ontransformnode property to the event handler, onxform. The event handler is called before the transformation operation on each node, so given the sample data provided in the data islands, this sample calls the alert several times.

<SCRIPT LANGUAGE="javascript" FOR="window" EVENT="onload">
document.expando = false;
Verify();
</SCRIPT>
<Script LANGUAGE="javascript">
function onxform ( code, doc ) {
    alert("Target document node being transformed:\n\n" + doc.xml +
       "\n\nStylesheet node used for the transform:\n\n" + code.xml);
   if (doc.value == "O'Hara, Robert")
return false;  // stop processing
   else
return true;
}
function Verify() {
   target.ontransformnode = onxform;
   var sOut = target.XMLDocument.transformNode(ss.XMLDocument);
   div.innerHTML += sOut;
}
</Script>
<DIV id=div></DIV>
</BODY>
</HTML>

The return value from the event handler indicates whether to continue processing. In this example, the event handler checks for a particular data value and returns the value "false" to stop transformation processing.

See Also

Applies To: DOMDocument Object | transformNode


output Property

Set this property when you want to provide a custom output object to write the result of the transformation to. This can be any object that supports IStream, IPersistStream, IXMLDOMDocument, ASP IResponse, or ADODB.Stream.

objXSLProcessor.output(objoutput)

Remarks

When a new transform is started the processor will QueryInterface this output object for a new IStream interface and when the transform is complete or reset is called this interface is released. The only method that is used on the IStream interface is Write. The bytes written to the stream will be encoded according to the encoding attribute on the <xsl:output> element.

If you do not provide a custom output object then you will get back a string when you read this property. The string contains the incrementally buffered transformation result.

Reading this property has the side effect of resetting that internal buffer so that each time you read the property you get the next chunk of output. In this case the output is always generated in the Unicode encoding, and the encoding attribute on the <xsl:output> element is ignored.

See Also

Applies To: XSLProcessor Object


ownerDocument Property

Returns the root of the document that contains this node.

objXMLDOMDocument = oXMLDOMNode.ownerDocument

Remarks

Object. The property is read-only. It returns the parent document object that represents the root of the document to which this node belongs.

All nodes are created in the context of a document, and the ownerDocument property is maintained until the node is added to another document. For a node removed from a document, this property indicates the document in which the node was last included.

Example

The following VBScript example uses the ownerDocument property to return the parent DOMDocument object, then displays that object's root element tag name:

Dim xmlDoc
Dim currNode
Dim owner
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set currNode = xmlDoc.documentElement.childNodes.item(0).childNodes.item(1)
Set owner =  currNode.ownerDocument
MsgBox owner.documentElement.tagName

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


ownerTemplate Property

Returns the style sheet template that was used to create this XSLProcessor object.

Set objXSLTemplate = objXSLProcessor.ownerTemplate

Remarks

The processor keeps the template alive, so even if the user releases the template object, this property can still be used to return the original template object.

Note The stylesheet property on the returned template may be different from the stylesheet property on this processor object.

See Also

Applies To: XSLProcessor Object


parentNode Property

Contains the parent node (for nodes that can have parents).

objXMLDOMNode = oXMLDOMNode.parentNode

Remarks

Object. The property is read-only. All nodes except Document, DocumentFragment, and Attribute nodes can have a parent. However, if a node has just been created and not yet added to the tree, or if it has been removed from the tree, the parent is Null.

This value depends on the value of the nodeType property.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT | Returns Null; these nodes do not have parents.
NODE_CDATA_SECTION | Returns the element or entity reference containing the CDATA section.
NODE_COMMENT | Returns the element, entity reference, document type, or document containing the comment.
NODE_DOCUMENT_TYPE | Returns the document node.
NODE_ELEMENT | Returns the parent node of the element. If the element is the root node in the tree, the parent is the document node. If the node is the document node, parentNode is Null.
NODE_ENTITY | Returns the document type node.
NODE_ENTITY_REFERENCE | Returns the element, attribute, or entity reference containing the entity reference.
NODE_NOTATION | Returns the document type node.
NODE_PROCESSING_INSTRUCTION | Returns the document, element, document type, or entity reference containing the processing instruction.
NODE_TEXT | Returns the parent element, attribute, or entity reference.

Example

The following VBScript example creates an XMLDOMNode object from another node object's parent and displays its XML:

Dim xmlDoc
Dim currNode
Dim newNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set currNode = xmlDoc.documentElement.childNodes.item(1).childNodes.item(0)
Set newNode = currNode.parentNode
MsgBox newNode.xml

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


parsed Property

Contains True if this node and all descendants have been parsed and instantiated; False if any nodes remain to be parsed.

boolValue = oXMLDOMNode.parsed

Remarks

Boolean. The property is read-only. During asynchronous access, not all of the document tree may be available. Before performing some operations, such as XSLT transformations or pattern-matching operations, it is useful to know whether the entire tree below this node is available for processing.

This member is an extension of the W3C DOM.

Example

The following VBScript example displays whether or not the top-level node (root) and all its descendants are parsed:

Dim xmlDoc
Dim root
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = True
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
MsgBox root.parsed

See Also

XSLT Reference

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


parseError Property

Returns an XMLDOMParseError object that contains information about the last parsing error.

objXMLDOMParseError = oXMLDOMDocument.parseError

Remarks

Object. The property is read-only. It returns an XMLDOMParseError object. The returned object is always a valid object.

This member is an extension of the W3C DOM.

Example

The following VBScript example uses the document's parseError property to create an XMLDOMParseError object. It then tests the error and displays a message if one occurs:

Dim xmlDoc
Dim myErr
Set xmlDoc =  CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set myErr =  xmlDoc.parseError
If (myErr.errorCode <>  0) Then
  MsgBox ("You have error " & myErr.reason)
End If

See Also

Applies To: DOMDocument Object


prefix Property

Returns the namespace prefix.

strValue = oXMLDOMNode.prefix

Remarks

String. The property is read-only. It returns the namespace prefix specified on the object. For example, for the element <xxx:yyy>, it returns "xxx". It returns an empty string, "", if no prefix is specified.

This member is an extension of the W3C DOM.

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


preserveWhiteSpace Property

Contains True if default processing preserves white space; False otherwise.

boolVal = objXMLDOMDocument.preserveWhiteSpace;
objXMLDOMDocument.preserveWhiteSpace = boolVal;

Remarks

Boolean. The property is read/write. This property is initialized to False.

The preserveWhiteSpace property specifies the default white space handling. When preserveWhiteSpace is True, all white space is preserved, regardless of any xml:space attributes specified in the DTD. It is the equivalent of having an xml:space= "preserve" attribute on every element.

When preserveWhiteSpace is False, the values of any xml:space attributes determine where white space is preserved.

The xml property does not preserve white space exactly as it appears in the original document. Instead, the object model replaces white space present in the original document with a single new line character in the representation returned by the xml property. Similarly, the text property contains a representation without the leading and trailing spaces and replaces multiple intervening white space characters between words with a single space character.

The text and xml properties preserve white space when the preserveWhiteSpace property is set to True and xml:space on the XML element has the value preserve.

This member is an extension of the W3C DOM.

Example

The following VBScript example first loads and displays a file with the preserveWhiteSpace property set to True and then reloads and displays the file with the preserveWhiteSpace property set to False:

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.preserveWhiteSpace = True
xmlDoc.load("c:\books.xml")
MsgBox xmldoc.xml
xmlDoc.async = False
xmlDoc.preserveWhiteSpace = False
xmlDoc.load("c:\books.xml")
MsgBox xmldoc.xml

See Also

Handling White Space

Applies To: DOMDocument Object


previousSibling Property

Contains the left sibling of this node.

objXMLDOMNode = oXMLDOMNode.previousSibling

Remarks

Object. The property is read-only.

This value depends on the value of the nodeType property.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT | Always returns Null; these node types do not appear as children of any other nodes.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT_TYPE, NODE_ELEMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns the node immediately preceding this node in its parent's child list. Returns Null if no such node exists.

Example

The following VBScript example creates an XMLDOMNode object and sets it to the previous sibling of the current node:

Dim xmlDoc
Dim currNode
Dim prevNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set currNode = xmlDoc.documentElement.childNodes.item(1)
Set prevNode = currNode.previousSibling
MsgBox prevNode.xml

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


publicId Property (XMLDOMEntity)

Contains the public identifier associated with the entity.

strValue = oXMLDOMEntity.publicId

Remarks

String. The property is read-only. If the public identifier is not specified, the property contains the empty string.

See Also

Applies To: XMLDOMEntity Object


publicId Property (XMLDOMNotation)

Contains the public identifier for the notation.

objValue = oXMLDOMNotation.publicId

Remarks

Variant. The property is read-only. If the public identifier is not specified, the property contains the empty string.

See Also

Applies To: XMLDOMNotation Object


readyState Property (XSLProcessor)

Returns the current state of the processor.

lReadyState = objXSLProcessor.readyState

Remarks

The readyState property returns the current state of the processor as follows:

State | Description
READYSTATE_UNINITIALIZED (0) | This is the initial state until all the required properties are set (in particular, the input property). Calling transform at this point returns an error.
READYSTATE_LOADING (1) | Not used by XSLProcessor.
READYSTATE_LOADED (2) | This is the state in which all required properties are set. You can now call transform.
READYSTATE_INTERACTIVE (3) | transform has been called but is not finished yet. Not used by XSLProcessor.
READYSTATE_COMPLETE (4) | The transformation is finished, but there may still be some output to read if you are reading the output as a string. When you call reset or set any property, it reverts to READYSTATE_LOADED. If a required property is cleared, it reverts to READYSTATE_UNINITIALIZED.

See Also

Applies To: XSLProcessor Object


readyState Property (DOMDocument)

Indicates the current state of the XML document.

lValue = oXMLDOMDocument.readyState

Remarks

Long integer. The property is read-only. It returns a value that indicates the instantiation and download state of the XML document object. The value can be one of the following:

State | Description
LOADING (1) | The load is in progress—reading persisted properties, but not yet parsing data. For readyState definitions, data should be considered equivalent to BLOB (binary large object) properties.
LOADED (2) | Reading of the persisted properties completed—reading and parsing data, but the object model is not yet available.
INTERACTIVE (3) | Some data has been read and parsed, and the object model is now available on the partially retrieved data set. Although the object model is available during this state, it is read-only.
COMPLETED (4) | The document has been completely loaded, successfully or unsuccessfully.

This member is an extension of the W3C DOM.

Example

The following JScript/HTML example demonstrates the sequence of ready states when asynchronously loading a document:

<script>
var xmldoc;
function Load()
{
xmldoc = new ActiveXObject("Msxml2.DOMDocument");
xmldoc.onreadystatechange = CheckState;
xmldoc.load(URL.value);
}
function CheckState()
{
  var state = xmldoc.readyState;
  RESULTS.innerHTML += "readyState = " + state + "<BR>"
  if (state == 4)
  {
    var err = xmldoc.parseError;
    if (err.errorCode != 0)
      RESULTS.innerHTML += err.reason + "<BR>"
    else RESULTS.innerHTML +="success" + "<BR>"
  }
}
<script>
URL: <input type=text size=60 id=URL>
<input type=button value=LOAD onclick="jscript:Load()">
<div id=RESULTS style= "color:red;font-weight:bold;"></div>

See Also

Applies To: DOMDocument Object


readyState Property (XMLHttpRequest)

Represents the state of the request.

objValue = oXMLHttpRequest.readyState

Remarks

Variant. The property is read-only. It represents the state of the request as an I4 (4-byte integer). The following values are defined:

0 (UNINITIALIZED) | The object has been created but has not been initialized (open has not been called).
(1) LOADING | The object has been created, but the send method has not been called.
(2) LOADED | The send method has been called and the status and headers are available, but the response is not yet available.
(3) INTERACTIVE | Some data has been received. You can call responseBody and responseText to get the current partial results.
(4) COMPLETED | All the data has been received, and the complete data is available in responseBody and responseText.

This property returns a 4-byte integer.

See Also

status Property | statusText Property

Applies To: XMLHttpRequest Object


reason Property

Explains the reason for the error.

strValue = oXMLDOMParseError.reason

Remarks

String. The property is read-only. It displays a description of the error. Validation errors also include the URL of the schema and the node within the schema that corresponds to the error.

Example

The following VBScript example attempts to load an XML document. If it encounters a parse error, it uses the ParseError object's reason property to display the reason the error occurred:

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
If xmlDoc.parseError.errorcode <> 0 Then
   MsgBox xmlDoc.parseError.reason
Else
  MsgBox xmlDoc.documentElement.xml
End If

See Also

Applies To: XMLDOMParseError Object


resolveExternals Property

Indicates whether external definitions (resolvable namespaces, DTD external subsets, and external entity references) are to be resolved at parse time, independent of validation.

boolVal = objXMLDOMDocument.resolveExternals;
objXMLDOMDocument.resolveExternals = boolVal;

Remarks

Boolean. The property is read/write.

When isResolving is True, external definitions are resolved at parse time. This allows default attributes and data types to be defined on elements from the schema and allows use of the DTD as a file inclusion mechanism.

This setting is independent of whether validation is to be performed, as indicated by the value of the validateOnParse property. If externals cannot be resolved during validation, a validation error occurs. When the value of isResolving is False, externals are not resolved and validation is not performed.

A resolvable namespace is indicated by a namespace URI that begins with "x-schema:" as its prefix.

The default setting is True.

This member is an extension of the W3C DOM.

Example

The following VBScript example sets the resolveExternals property to False before loading a document:

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.resolveExternals = False
xmlDoc.load("c:\xmlinact\books.xml")

See Also

Applies To: DOMDocument Object


responseBody Property

Represents the response entity body as an array of unsigned bytes, namely a SAFEARRAY of type VT_ARRAY | VT_UI1. This contains the raw undecoded bytes as received directly from the server. Therefore, depending on what the server sent, this may appear as binary-encoded data (UTF-8, UCS-2, UCS-4, shiftJis, and so on).

objValue = oXMLHttpRequest.responseBody

Remarks

Variant. The property is read-only. This property represents only one of several forms in which the HTTP response can be returned.

See Also

responseStream Property | responseText Property | responseXML Property

Applies To: XMLHttpRequest Object


responseStream Property

Represents the response entity body as an IStream. This stream returns the raw undecoded bytes as received directly from the server. Therefore, depending on what the server sent, this may appear as binary-encoded data (UTF-8, UCS-2, UCS-4, shiftJis, and so on).

objValue = oXMLHttpRequest.responseStream

Remarks

Variant. The property is read-only. This property represents only one of several forms in which the HTTP response can be returned.

See Also

responseBody Property | responseText Property | responseXML Property

Applies To: XMLHttpRequest Object


responseText Property

Represents the response entity body as a string.

objValue = oXMLHttpRequest.responseText

Remarks

Variant. The property is read-only. This property represents only one of several forms in which the HTTP response can be returned.

The XMLHttp object tries to decode the response into a Unicode string. It assumes the default encoding is UTF-8, but it can decode any type of UCS-2 (big or little endian) or UTC-4 encoding as long as the server sends the appropriate Unicode byte-order mark. It does not process the <? XML coding declaration. If you know the response is going to be XML, you should use the responseXML property for full XML encoding support.

See Also

responseBody Property | responseStream Property | responseXML Property

Applies To: XMLHttpRequest Object


responseXML Property

Represents the response entity body as parsed by the Microsoft XML Parser (MSXML).

objDispatch = oXMLHttpRequest.responseXML

Remarks

Object. The property is read-only. For security reasons, the parser validation features are always turned off to prevent MSXML from attempting to download a DTD or XML-Data definition. If the response entity body is not valid XML, this property returns the DOMDocument object that was parsed so that you can access the error object. This property does not return the XMLDOMParseError error object itself, but that object is accessible from the DOMDocument object.

If the response was generated by an ASP page and the MIME type was not correctly set to "text/xml" using the ASP method Response.ContentType, responseXML will be empty.

See Also

responseBody Property | responseStream Property | responseText Property

Applies To: XMLHttpRequest Object


schemas Property

Used for finding schema documents during load.

Set objXMLDOMDocument.schemas = objXMLDOMSchemaCache

Remarks

The schemas property provides a way of associating preloaded schemas with any namespace. It also provides a way to override the schemas that are used by the document you are about to load. Setting a new schema collection has no effect on the current document until the next load or loadXML call. The schemas loaded by the document during load do not automatically get added to this collection.

Setting any non-null schema collection automatically disables DTD processing because you cannot mix both DTD and XML Schema processing on the same document. This means DTDs will be ignored. Setting the schemas collection to Null re-enables DTD processing.

See Also

XMLSchemaCache Object

Applies To: XMLDOMDocument2 Object


specified Property

Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema.

boolValue = oXMLDOMNode.specified

Remarks

Boolean. The property is read-only. Returns True if the attribute is explicitly specified in the element. Returns False if the attribute value comes from the DTD or schema.

This value depends on the value of the nodeType property.

NODE_ATTRIBUTE | Returns True if the attribute was specified directly on the element. Returns False for default attributes.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_DOCUMENT_TYPE, NODE_ELEMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns True.

This member is an extension of the W3C DOM.

Example

The following VBScript example creates an XMLDOMNode object from the specified item in an XMLDOMNamedNodeMap. It then displays whether or not the attribute was specified in the element rather than in a DTD or schema:

Dim xmlDoc
Dim currNode
Dim objNamedNodeMap
Dim myNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set currNode = xmlDoc.documentElement.childNodes.item(0)
Set objNamedNodeMap = currNode.attributes
Set myNode = objNamedNodeMap.item(0)
MsgBox myNode.specified

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


srcText Property

Returns the full text of the line containing the error.

strValue = oXMLDOMParseError.srcText

Remarks

String. The property is read-only. It returns an empty string if an error is caused when the XML is not well-formed and the error cannot be assigned to a specific line.

Example

The following VBScript example attempts to load an XML document. If it encounters a parse error, it displays the text of the line containing the error:

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
If xmlDoc.parseError.errorcode <> 0 Then
  MsgBox xmlDoc.parseError.srcText
Else
  MsgBox xmlDoc.documentElement.xml
End If

See Also

Applies To: XMLDOMParseError Object


startMode Property

Returns the base name part of the start mode, which is a qualified name (for more information, see http://www.w3.org/TR/WD-xslt#modes). The default value of startMode is the empty string, "".

strMode = objXSLProcessor.startMode

See Also

Applies To: XSLProcessor Object


startModeURI Property

Returns the namespace URI part of the start mode, which is a qualified name (for more information, see http://www.w3.org/TR/WD-xslt#modes). The default value of this property is the empty string, "".

strNameSpace = objXSLProcessor.startModeURI

See Also

Applies To: XSLProcessor Object


status Property

Represents the HTTP status code returned by a request.

lValue = oXMLHttpRequest.status

Remarks

Long integer. The property is read-only. It represents the returned status code as a long integer. This value is valid only after the send method returns successfully.

See Also

statusText Property

Applies To: XMLHttpRequest Object


statusText Property

Represents the HTTP response line status.

strValue = oXMLHttpRequest.statusText

Remarks

String. The property is read-only. It represents the HTTP response as a BSTR value. This value is valid only after the send method returns successfully.

See Also

status Property

Applies To: XMLHttpRequest Object


stylesheet Property (XSLProcessor)

Provides the XSL style sheet that is to be compiled into the XSL template.

Set objXMLDOMNode = objXSLProcessor.stylesheet

Remarks

The style sheet document is made read-only while the style sheet belongs to the template or any associated processor. The read-only flag is removed when the style sheet is released from all templates and processing objects.

The processor objects created by this template take a "snapshot" of the current style sheet and always use that same style sheet, so they are actually isolated from changes to the stylesheet property. The only way to update the processor to the new style sheet is to create a new processor.

See Also

Applies To: XSLProcessor Object


stylesheet Property (XSLTemplate)

Returns the XSL style sheet to compile into an XSL template.

Set objXMLDOMNode = objXSLTemplate.stylesheet

Remarks

The style sheet document is made read-only while the style sheet belongs to the template or any associated processor. The read-only flag is removed when the style sheet is released from all templates and processor objects.

The processor objects created by this template take a "snapshot" of the current style sheet and always use the same style sheet, so they are isolated from changes to this stylesheet property. The only way to update the processor to the new style sheet is to create a new processor.

See Also

Applies To: XSLTemplate Object


systemId Property (XMLDOMEntity)

Contains the system identifier associated with the entity.

strValue = oXMLDOMEntity.systemId

Remarks

String. The property is read-only. If the system identifier is not specified, the property contains the empty string.

See Also

Applies To: XMLDOMEntity Object


systemId Property (XMLDOMNotation)

Contains the system identifier for the notation.

objValue = oXMLDOMNotation.systemId

Remarks

Variant. The property is read-only. If the system identifier is not specified, this contains the empty string.

See Also

Applies To: XMLDOMNotation Object


tagName Property

Contains the element name (the name that appears within the tag).

strValue = oXMLDOMElement.tagName

Remarks

String. The property is read-only. It contains the string that represents the element's name. For example, the tag name is "book" in the tag

<book ISBN="1572318546">

See Also

Applies To: XMLDOMElement Object


target Property

Specifies the target—the application to which this processing instruction is directed.

strValue = oXMLDOMProcessingInstruction.target

Remarks

String. The property is read-only. XML defines the target as the first token following the markup that begins the processing instruction. For example, the target has the value "xml" in the processing instruction <?xml version="1.0">.

The target property has the same value as the nodeName property.

Example

The following VBScript example iterates through the document's child nodes. If it finds a node of type NODE_PROCESSING_INSTRUCTION (7), it displays the node's target:

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.load("c:\books.xml")
For i = 0 TO (xmlDoc.childNodes.length -1)
  If xmlDoc.childNodes.item(i).nodeType = 7 Then
    msgBox xmlDoc.childNodes.item(i).target
  End If
Next

See Also

Applies To: XMLDOMProcessingInstruction Object


text Property

String. Contains the text content of the node or the concatenated text representing this node and its descendants.

strValue = oXMLDOMNode.text
objXMLDOMNode.text = strValue

Remarks

The property is read/write. When concatenated, the text represents the contents of text or CDATA nodes. All concatenated text nodes are normalized according to xml:space attributes and the value of the preserveWhiteSpace switch. Concatenated CDATA text is not normalized. (Child nodes that contain NODE_COMMENT and NODE_PROCESSING_INSTRUCTION nodes are not concatenated.)

If you need more precise control over text manipulation in an XML document, you may want to use the lower-level nodeValue property, which returns the raw text associated with a NODE_TEXT node.

Consider the "root" element in this example:

<root att="   123   a   <   ">
   <a>   a   a   </a>
   <!--   comment   b   -->
   <?pi   pi   c   ?>
   <![CDATA[   cdata   d   ]]>
   e   f
</root>

The text property for the root element returns the concatenated text:

"a a   cdata   d    e f"

Note that the white space within the CDATA node is preserved.

This value depends on the value of the nodeType property.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_ENTITY | Returns a string representing the value of the node. This is the concatenated text of all subnodes with entities expanded.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns the text contained in the node, which is the same as the nodeValue property.
NODE_DOCUMENT_TYPE, NODE_NOTATION | Returns the empty string (""). These node types do not have associated text.
NODE_DOCUMENT_FRAGMENT | Returns the text comprised of the concatenation of all descendant nodes.
NODE_ELEMENT Contains a string that represents the element content. Note that this will also include the text content from all child elements, concatenated in document order. For example, consider the following XML:
<count>
  <item>one</item>
  <item>two</item>
  <item>three</item>
  <item>four</item>
</count>

The text property for the <count> element contains the value "one two three four".
NODE_ENTITY_REFERENCE | Returns the string representation of the entity reference.

This member is an extension of the W3C DOM.

Example

The following VBScript example creates an XMLDOMNode object (of type NODE_ENTITY), and then displays the object's text value (including that of any of the object's child nodes):

Dim xmlDoc
Dim currNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set currNode = xmlDoc.documentElement.childNodes.item(0)
MsgBox currNode.text

See Also

Handling White Space

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


url Property (DOMDocument)

Returns the canonicalized URL for the last loaded XML document.

strValue = oXMLDOMDocument.url

Remarks

String. The property is read-only. It returns the URL from the last successful load. If the document is being built in memory, this property returns Null.

The url property is not updated after saving a document with the save method. To update the url property in this case, reload the document using the load method.

This member is an extension of the W3C DOM.

Example

The following VBScript example creates a DOMDocument object and displays the value of its url property:

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
MsgBox (xmlDoc.url)

See Also

Applies To: DOMDocument Object


url Property (XMLDOMParseError)

Contains the URL of the XML document containing the last error.

strValue = oXMLDOMParseError.url

Remarks

String. The property is read-only.

Example

The following VBScript example attempts to load an XML document. If it encounters a parse error, it displays the URL of the document containing the error.

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
If xmlDoc.parseError.errorcode <> 0 Then
   MsgBox xmlDoc.parseError.url
Else
  MsgBox xmlDoc.documentElement.xml
End If

See Also

Applies To: XMLDOMParseError Object


validateOnParse Property

Indicates whether the parser should validate this document.

boolValue = oXMLDOMDocument.validateOnParse
objXMLDOMDocument.validateOnParse = boolValue

Remarks

Boolean. The property is read/write. If True, it validates during parsing. If False, it parses only for well-formed XML. The default is True.

This member is an extension of the W3C DOM.

See Also

Applies To: DOMDocument Object


value Property

Contains the attribute value.

objValue = oXMLDOMAttribute.value
objXMLDOMAttribute.value = objValue

Remarks

Variant. The property is read/write. It returns the value of the attribute. For attributes with subnodes, the string is the concatenated text of all subnodes with character and general entity references expanded (replaced with their values).

The new attribute value is added as the value of a single text node that is a child of the attribute node. The string contents are unparsed.

Example

The following VBScript example gets the value of the first attribute of the document root and assigns it to the variable myVar:

Dim xmlDoc
Dim myVar
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
myVar = root.attributes.item(0).value
MsgBox myVar

See Also

Applies To: XMLDOMAttribute Object


xml Property

Contains the XML representation of the node and all its descendants.

strValue = oXMLDOMNode.xml

Remarks

The property is read-only.

Note that the xml property always returns a Unicode string. That is, the xml property for the DOMDocument object converts the document from its original encoding to Unicode. As a result, the original encoding attribute is removed. For example,

<?xml version="1.0" encoding="UTF-8"?>

appears in the xml property as:

<?xml version="1.0"?>

If the original encoding wasn't removed by the xml property, the following line would return an error indicating the parser cannot switch from Unicode to UTF-8 encoding:

xmlDoc.loadxml(xmlDoc.xml);

If you use the save method, the original encoding is preserved. So if you load a document in Windows-1252 encoding, it will be saved in that format, and so on.

This value depends on the value of the nodeType property.

NODE_ATTRIBUTE, NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_ELEMENT, NODE_ENTITY, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns the string representation of the node and all its descendants.
NODE_DOCUMENT_FRAGMENT | Returns the string representation of all descendant nodes of the document fragment.
NODE_DOCUMENT_TYPE | Returns the string representation of the <!DOCTYPE ...> declaration, including the internal subset if specified.
NODE_ENTITY_REFERENCE | Returns the string representation of the entity reference. Although the entity reference has children, they are not returned.

This member is an extension of the W3C DOM.

Example

The following VBScript example creates an XMLDOMNode object (of type NODE_ENTITY), and then displays the object's XML value (including that of any of the object's child nodes):

Dim xmlDoc
Dim currNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set currNode = xmlDoc.documentElement.childNodes.item(0)
MsgBox currNode.xml

See Also

Handling White Space | XML DOM Persistence

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


XML DOM Methods

This section includes the following topics:

  • abort Method (Document)
  • abort Method (HttpRequest)
  • absoluteChildNumber Method
  • add Method
  • addCollection Method
  • addObject Method
  • addParameter Method
  • ancestorChildNumber Method
  • appendChild Method
  • appendData Method
  • childNumber Method
  • clone Method
  • cloneNode Method
  • createAttribute Method
  • createCDATASection Method
  • createComment Method
  • createDocumentFragment Method
  • createElement Method
  • createEntityReference Method
  • createNode Method
  • createProcessingInstruction Method
  • createProcessor Method
  • createTextNode Method
  • deleteData Method
  • depth Method
  • formatDate Method
  • formatIndex Method
  • formatNumber Method
  • formatTime Method
  • get Method
  • getAllResponseHeaders Method
  • getAttribute Method
  • getAttributeNode Method
  • getElementsByTagName Method (Document)
  • getElementsByTagName Method (Element)
  • getNamedItem Method
  • getProperty Method (Document)
  • getProperty Method (Selection)
  • getQualifiedItem Method
  • getResponseHeader Method
  • hasChildNodes Method
  • hasFeature Method
  • insertBefore Method
  • insertData Method
  • item Method (NodeList)
  • item Method (NamedNodeMap)
  • load Method
  • loadXML Method
  • matches Method
  • nextNode Method (NodeList)
  • nextNode Method (NamedNodeMap)
  • nodeFromID Method
  • normalize Method
  • peekNode Method
  • open Method
  • remove Method
  • removeAll Method
  • removeAttribute Method
  • removeAttributeNode Method
  • removeChild Method
  • removeNamedItem Method
  • removeNext Method
  • removeQualifiedItem Method
  • replaceChild Method
  • replaceData Method
  • reset Method (NamedNodeMap)
  • reset Method (NodeList)
  • reset Method (XSLProcessor)
  • save Method
  • selectNodes Method (Node)
  • selectNodes Method (Document2)
  • selectSingleNode Method
  • send Method
  • setAttribute Method
  • setAttributeNode Method
  • setNamedItem Method
  • setProperty Method
  • setRequestHeader Method
  • setStartMode Method
  • splitText Method
  • substringData Method
  • transform Method
  • transformNode Method
  • transformNodeToObject Method
  • uniqueID Method
  • validate Method


abort Method (DOMDocument)

Aborts an asynchronous download in progress.

oXMLDOMDocument.abort()

Remarks

This method stops download and parsing and discards any portion of the XML tree already built. The XMLDOMParseError object indicates that the download was stopped.

If the readyState property has the value "COMPLETED", no action is taken and the current document is unchanged.

This member is an extension of the W3C DOM.

See Also

Applies To: DOMDocument Object


abort Method (XMLHttpRequest)

Cancels the current HTTP request.

oXMLHttpRequest.abort()

Remarks

The object will be returned to the UNINITIALIZED state, and open must be called next.

See Also

readyState Property

Applies To: XMLHttpRequest Object


absoluteChildNumber Method

Returns the number of the node relative to all siblings.

absoluteChildNumber(pNode)

Parameters

pNode
Object. Node for which the child number is to be returned.

Return Value

Long integer. Returns the index for the node in its parent's list of all child nodes. The first entry in the list is assigned the value "1".

See Also

Applies To: XTLRuntime Object


add Method

Adds a new schema to the schema collection, associating the given namespace URI with the specified schema.

objXMLDOMSchemaCache.add(namespaceURI, schema)

Parameters

namespaceURI
This is the namespace to associate with the specified schema. The empty string, "", will associate the schema with the empty namespace, xmlns="".

This may be any string that can be used in an xmlns attribute, but it cannot contain entity references. The same whitespace normalization that occurs on the xmlns attribute also occurs on namespaceURI (that is, leading and trailing white space is trimmed, new lines are converted to spaces, and multiple adjacent white space characters are collapsed into one space).

schema
Specifies the schema to load. It will load it synchronously and with resolveExternals=false and validateOnParse=false. The schema parameter can also be any DOMDocument.

This argument can be Null, which results in the removal of any schema for the specified namespaces. If the schema is an XMLDOMNode, the entire document the node belongs to will be preserved.

Example

The following Visual Basic sample attaches a schema to an xml document:

Dim xslDoc As New MSXML2.FreeThreadedDOMDocument
Dim SchemaCache
'Create the collection.
Set SchemaCache = CreateObject("Msxml2.XMLSchemaCache")
SchemaCache.Add "x-schema:books.xml", "d:\\inetpub\\wwwroot\\bookschema.xml"
'This Schema Cache can be attached to multiple documents.
Set xslDoc.schemas = SchemaCache
xslDoc.async = False
'The document will load only if a valid schema is attached to the xml
'file.
xslDoc.Load "http://sampleserver/samplexmldtd.xml"
MsgBox xslDoc.xml

http://sampleserver/samplexmldtd.xml

<?xml version='1.0'?>
<COLLECTION xmlns="x-schema:books.xml"
   xmlns:dt="urn:schemas-microsoft-com:datatypes">
   <DATE dt:dt="datetime">1998-10-13T15:56:00</DATE>
   <BOOK>
      <TITLE>Cosmos</TITLE>
      <AUTHOR>Carl Sagan</AUTHOR>
      <PUBLISHER>Ballantine Books</PUBLISHER>
   </BOOK>
   <BOOK>
      <TITLE>Catwings</TITLE>
      <AUTHOR>Ursula K. Le Guin</AUTHOR>
      <PUBLISHER>Scholastic</PUBLISHER>
   </BOOK>
</COLLECTION>

d:\inetpub\wwwroot\bookschema.xml

<?xml version="1.0"?>
<Schema xmlns="urn:schemas-microsoft-com:xml-data">
   <ElementType name="TITLE"/>
   <ElementType name="AUTHOR"/>
   <ElementType name="PUBLISHER"/>
   <ElementType name="DATE"/>
   <ElementType name="BOOK" model="closed">
      <element type="TITLE"/>
      <element type="AUTHOR"/>
      <element type="PUBLISHER"/>
   </ElementType>
   <ElementType name="COLLECTION" model="closed">
      <element type="BOOK"/>
   </ElementType>
</Schema>

Output (in a message box)

<?xml version='1.0'?>
<COLLECTION xmlns="x-schema:books.xml"
   xmlns:dt="urn:schemas-microsoft-com:datatypes">
   <DATE dt:dt="datetime">1998-10-13T15:56:00</DATE>
   <BOOK>
      <TITLE>Cosmos</TITLE>
      <AUTHOR>Carl Sagan</AUTHOR>
      <PUBLISHER>Ballantine Books</PUBLISHER>
   </BOOK>
   <BOOK>
      <TITLE>Catwings</TITLE>
      <AUTHOR>Ursula K. Le Guin</AUTHOR>
      <PUBLISHER>Scholastic</PUBLISHER>
   </BOOK>
</COLLECTION>

See Also

Applies To: XMLSchemaCache


addCollection Method

Adds all the schemas from another collection into this collection, replacing any schemas that collide on the same namespace URI.

objXMLDOMSchemaCache.addCollection(objXMLDOMSchemaCache)

Parameters

objXMLDOMSchemaCollection
The collection containing the schemas to add.

Remarks

There is no guarantee that the two collections will return exactly the same schema objects—the collection is free to clone them if necessary.

Adding a collection to itself has no effect.

Example

Dim xmldoc As New MSXML2.FreeThreadedDOMDocument
Dim xsldoc As New MSXML2.FreeThreadedDOMDocument
Dim SchemaCache As New MSXML2.XMLSchemaCache
Dim SchemaCache2 As New MSXML2.XMLSchemaCache

xmldoc.loadXML "<root><bar>bar</bar></root>"

xsldoc.async = False
xsldoc.validateOnParse = False
SchemaCache.Add "x-schema:books", "d:\inetpub\wwwroot\bookschema.xml"
SchemaCache2.addCollection SchemaCache
SchemaCache2.Add "x-schema:books", "d:\inetpub\wwwroot\newbookschema.xml"
Set xsldoc.schemas = SchemaCache2
' The document will load only if a valid schema is attached to the xml
' file.

xsldoc.Load "d:\inetpub\wwwroot\samplexmldtd.xml"

MsgBox xsldoc.xml

d:\inetpub\wwwroot\samplexmldtd.xml

<?xml version='1.0'?>
<COLLECTION xmlns="x-schema:books"
   xmlns:dt="urn:schemas-microsoft-com:datatypes">
   <DATE dt:dt="datetime">1998-10-13T15:56:00</DATE>
   <BOOK>
      <TITLE>Cosmos</TITLE>
      <AUTHOR>Carl Sagan</AUTHOR>
      <PUBLISHER>Ballantine Books</PUBLISHER>
   </BOOK>
</COLLECTION>

Output (in a message box)

<?xml version='1.0'?>
<COLLECTION xmlns="x-schema:books"
   xmlns:dt="urn:schemas-microsoft-com:datatypes">
   <DATE dt:dt="datetime">1998-10-13T15:56:00</DATE>
   <BOOK>
      <TITLE>Cosmos</TITLE>
      <AUTHOR>Carl Sagan</AUTHOR>
      <PUBLISHER>Ballantine Books</PUBLISHER>
   </BOOK>
</COLLECTION>

See Also

Applies To: XMLSchemaCache


addObject Method

Used to pass objects into a style sheet. Numbers are coerced to double, everything is coerced into a string, and objects return an error.

objXSLProcessor.addObject(obj, namespaceURI)

Parameters

obj
Object to pass in.
namespaceURI
The namespace to use inside the style sheet to identify the object.

Example

The following example passes an object to the stylesheet:

Dim xsldoc As New MSXML2.FreeThreadedDOMDocument
Dim xmldoc As New MSXML2.FreeThreadedDOMDocument
Dim xsltemp As New MSXML2.XSLTemplate
Dim xslproc As MSXML2.IXSLProcessor

xsldoc.Load "d:\inetpub\wwwroot\sampleXSLWithObject.xml"
Set xsltemp.stylesheet = xsldoc.documentElement
Set xslproc = xsltemp.createProcessor
xmldoc.loadXML "<foo>foo</foo>"
xslproc.input = xmldoc

xslproc.addObject xmldoc, "urn:my-object"
xslproc.Transform
MsgBox xslproc.output

d:\inetpub\wwwroot\sampleXSLWithObject.xml

<xsl:stylesheet xmlns:xsl=http://www.w3.org/1999/XSL/Transform version="1.0" xmlns:myObj="urn:my-object">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
   <xsl:element name="bar">
   <xsl:value-of select="myObj:get-text()"/>
   </xsl:element>
</xsl:template>
</xsl:stylesheet>

Output

<?xml versio="1.0" encoding="UTF-16"?>
<bar>
foo
</bar>

See Also

Applies To: XSLProcessor Object


addParameter Method

Allows you to pass variables into an XSLT style sheet that will be referenced by <xsl:param> within the style sheet.

objXSLProcessor.addParameter(baseName, parameter, namespaceURI)

Parameter

baseName
The name that will be used inside the style sheet to identify the parameter context.
parameter
A number, boolean, string, XMLDOMNodeList, or XMLDOMNode. Passing in a single node will produce a node list that contains one node (shortcut).
namespaceURI
Optional namespace.

Remarks

The addParameter method can be called in on TransformNode handlers and between transform calls (in async processing), and further processing will use the updated parameter.

See Also

Applies To: XSLProcessor Object


ancestorChildNumber Method

Returns the number of the nearest ancestor of a node with the requested node name.

ancestorChildNumber(bstrNodeName, pNode)

Parameters

bstrNodeName
String. Node name of the desired ancestor.
pNode
Object. Node from which to start the search.

Return Value

Long integer. Returns the number of the ancestor relative to its sibling nodes. The first entry in the list is assigned the value "1". Returns Null if there is no such ancestor.

See Also

Applies To: XTLRuntime Object


appendChild Method

Appends newChild as the last child of this node.

objXMLDOMNode = oXMLDOMNode.appendChild(newChild)

Parameters

newChild
Object. Address of the new child node to be appended at the end of the list of children belonging to this node.

Return Value

Object. Returns the new child node successfully appended to the list.

Remarks

Note that this is equivalent to calling insertBefore(newChild, null). For complete information, see the insertBefore method.

Example

The following VBScript example creates a new XMLDOMNode object, and then uses the appendChild method to append it to the document's list of children:

Dim xmlDoc
Dim root
Dim newNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
MsgBox root.xml
Set newNode = xmlDoc.createNode (1, "VIDEOS", "")
root.appendChild(newNode)
MsgBox root.xml

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


appendData Method

Appends the supplied string to the existing string data.

oXMLDOMCharacterData.appendData(data)

Parameters

data
String containing the data that is to be appended to the existing string.

Remarks

The length property is also updated by this operation.

Example

The following example creates an XMLDOMComment object and uses the appendData method to add text to the string:

Dim xmlDoc
Dim comment
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set comment = xmlDoc.createComment("Hello World!")
comment.appendData ("Ellohay Orldway!")
MsgBox (comment.data)

See Also

Applies To: XMLDOMCDATASection | XMLDOMCharacterData | XMLDOMComment | XMLDOMText


childNumber Method

Returns the number of the node relative to siblings of the same node name.

childNumber(pNode)

Parameters

pNode
Object. Node for which the child number is to be returned.

Return Value

Long integer. Returns the index for the node in its parent's list of child nodes with that name. The first entry in the list is assigned the value "1".

See Also

Applies To: XTLRuntime Object


clone Method

Produces an exact copy of the current XMLDOMSelection object, with the same position and context.

Set NewObjXMLDOMSelection = objXMLDOMSelection.clone

JScript Example

//Create a DOMDocument2 object and load some XML:
xmldoc = new ActiveXObject("Msxml2.DOMDocument");
xmldoc.loadXML("<root><elem1>foo</elem1><elem2>bar</elem2></root>");
//Create an XMLDOMSelection object from selected nodes:
xpath = xmldoc.selectNodes("root/elem1");
//Cache the XPath expression and context:
xpath.expr = "root/elem1";
xpath.context = xmldoc;

//Clone the XMLDOMSelection object
xpath2 = xpath.clone();
temp1 = xpath.peekNode();  // temp1 == <elem1/>
temp2 = xpath2.peekNode();  // temp2 == <elem1/> Note the position and
// context are maintained.

See Also

Applies To: XMLDOMSelection Object


cloneNode Method

Creates a new node that is an exact clone of this node.

objXMLDOMNode = oXMLDOMNode.cloneNode(deep)

Parameters

deep
Boolean. Flag that indicates whether to recursively clone all nodes that are descendants of this node. If True, create a clone of the complete tree below this node. If False, clone this node and its attributes only.

Return Value

Object. Returns the newly created clone node.

Remarks

The cloned node has the same property values as this node for the following properties: nodeName, nodeValue, nodeType, parentNode, ownerDocument, and (if it is an element) attributes. The value of the clone's childNodes collection depends on the setting of the deep flag parameter.

Example

The following example clones a node, and then appends it as a child of the top-level node:

Dim xmlDoc
Dim root
Dim currNode
Dim MyNewNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmldoc.documentElement
Set currNode = root.childNodes.item(2)
Set MyNewNode = currNode.cloneNode(true)
root.appendChild (MyNewNode)
MsgBox xmlDoc.xml

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


createAttribute Method

Creates a new attribute with the specified name.

objXMLDOMAttribute = oXMLDOMDocument.createAttribute(name)

Parameters

name
String specifying the name of the new attribute object. This name is subsequently available as the new node's nodeName property.

Return Value

Object. Returns the new XMLDOMAttribute object.

Remarks

Creating an attribute with this method is the same as using createNode where the type parameter value is NODE_ATTRIBUTE and no namespace is specified.

You cannot create a namespace-qualified attribute using the createAttribute method. Regardless of whether a namespace prefix is included in name, the namespaceURI property for the new attribute is set to an empty string, "". An attribute constructed as part of an XML document load operation will never have both a prefix and an empty namespace URI. You can only create a namespace-qualified attribute using the DOMDocument object's createNode method.

No data value is set for the attribute during the create operation. You can set the value by calling the element object's setAttribute method.

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null. To associate the attribute with an element, call the XMLDOMElement object's setAttribute method.

The nodeType property has the value NODE_ATTRIBUTE.

Example

The following VBScript example creates a new attribute called ID and adds it to the attributes of the DOMDocument object:

Dim xmlDoc
Dim root
Dim newAtt
Dim namedNodeMap
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
Set newAtt = xmlDoc.createAttribute("ID")
Set namedNodeMap = root.attributes
namedNodeMap.setNamedItem newAtt
For i = 0 to (namedNodeMap.length -1)
  MsgBox (namedNodeMap.item(i).xml)
Next

See Also

Applies To: DOMDocument Object


createCDATASection Method

Creates a CDATA section node that contains the supplied data.

objXMLDOMCDATASection = oXMLDOMDocument.createCDATASection(data)

Parameters

data
String specifying the value to be supplied to the new CDATASection object's nodeValue property.

Return Value

Object. Returns the new XMLDOMCDATASection object.

Remarks

Creating a CDATA section with this method is the same as using createNode where the type parameter value is NODE_CDATA_SECTION and no namespace is specified. You cannot specify a namespace with the createCDATASection method.

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null. You must explicitly call one of the node insert methods—insertBefore, replaceChild, or appendChild—to add the new object.

The new object's nodeType property has the value NODE_CDATA_SECTION.

Example

The following example creates a new CDATA section node and appends it as the last child node of the DOMDocument object:

Dim xmlDoc
Dim root
Dim CDATASection
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
Set CDATASection = xmlDoc.createCDATASection("Hello World!")
root.appendChild CDATASection
Msgbox (root.xml)

See Also

Applies To: DOMDocument Object


createComment Method

Creates a comment node that contains the supplied data.

objXMLDOMComment = oXMLDOMDocument.createComment(data)

Parameters

data
String specifying the value to be supplied to the new Comment object's nodeValue property.

Return Value

Object. Returns the new XMLDOMComment object.

Remarks

Creating a comment with this method is the same as using createNode where the type parameter value is NODE_COMMENT and no namespace is specified. You cannot specify a namespace with the createComment method.

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null. You must explicitly call one of the node insert methods—insertBefore, replaceChild, or appendChild—to add the new object.

The new object's nodeType property has the value NODE_COMMENT.

Example

The following example creates a new comment node and appends it as the last child node of the DOMDocument object:

Dim xmlDoc
Dim root
Dim comment
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
Set comment = xmlDoc.createComment("Hello World!")
root.appendChild comment
Msgbox (root.xml)

See Also

Applies To: DOMDocument Object


createDocumentFragment Method

Creates an empty DocumentFragment object.

objXMLDOMDocumentFragment = oXMLDOMDocument.createDocumentFragment()

Return Value

Object. Returns the empty XMLDOMDocumentFragment object.

Remarks

Creating a document fragment with this method is the same as using createNode where the type parameter value is NODE_DOCUMENT_FRAGMENT and no namespace is specified. You cannot specify a namespace with the createDocumentFragment method.

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null. You must explicitly call one of the node insert methods—insertBefore, replaceChild, or appendChild—to add the new object.

The nodeType property has the value NODE_DOCUMENT_FRAGMENT.

See Also

Applies To: DOMDocument Object


createElement Method

Creates an element node using the specified name.

objXMLDOMElement = oXMLDOMDocument.createElement(tagName)

Parameters

tagName
String specifying the name for the new element node. The name is case sensitive. This name is subsequently available as the element node's nodeName property.

Return Value

Object. Returns the XMLDOMElement object for the new element.

Remarks

Creating an element with this method is the same as using createNode where the type parameter value is NODE_ELEMENT and no namespace is specified.

You cannot create a namespace-qualified element using the createElement method. Regardless of whether a namespace prefix is included in tagName, the namespaceURI property for the new element node is set to an empty string, "". An element node constructed as part of an XML document load operation will never have both a prefix and an empty namespace URI. You can only create a namespace-qualified element using the DOMDocument object's createNode method

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null. You must explicitly call one of the node insert methods—insertBefore, replaceChild, or appendChild—to add the new object.

The nodeType property has the value NODE_ELEMENT.

Example

The following example creates an element called PAGES and appends it to an XMLDOMNode object. It then sets the text value of the element to 400:

Dim xmlDoc
Dim root
Dim newElem
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
Set newElem = xmlDoc.createElement("PAGES")
root.childNodes.item(1).appendChild newElem
root.childNodes.item(1).lastChild.text = "400"
MsgBox root.childNodes.item(1).xml

See Also

Applies To: DOMDocument Object


createEntityReference Method

Creates a new EntityReference object.

objXMLDOMEntityReference = oXMLDOMDocument.createEntityReference(name)

Parameters

name
String specifying the name of the entity referenced. This name is subsequently available as the new object's nodeName property.

Return Value

Object. Returns the new XMLDOMEntityReference object.

Remarks

Creating an entity reference with this method is the same as using createNode where the type parameter value is NODE_ENTITY_REFERENCE and no namespace is specified. You cannot specify a namespace within the name parameter.

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null. You must explicitly call one of the node insert methods—insertBefore, replaceChild, or appendChild—to add the new object.

The nodeType property has the value NODE_ENTITY_REFERENCE.

Example

The following example creates a new XMLDOMEntityReference object and appends it to an XMLDOMNode object:

Dim xmlDoc
Dim root
Dim MyEntity
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
Set MyEntity = xmlDoc.createEntityReference("newRef")
root.childNodes.item(0).appendChild MyEntity
MsgBox root.xml

See Also

Applies To: DOMDocument Object


createNode Method

Creates a node using the supplied type, name, and namespace.

objXMLDOMNode = oXMLDOMDocument.createNode(type, name, namespaceURI)

Parameters

type
Variant. Value that uniquely identifies the node type. This can be specified using either the integer value or the string value. For the complete list of values, see the XMLDOMNodeType enumeration or the description for the name parameter.
name
String containing the value for the new node's nodeName property. The relationship between the node name and node type is summarized below.
namespaceURI
String defining the namespace URI. If specified, the node is created in the context of the namespaceURI with the prefix specified on the node name. If the name parameter does not have a prefix, this is treated as the default namespace.

Return Value

Object. Returns the newly created node.

Remarks

The name parameter depends on the value of the type parameter:

NODE_ATTRIBUTE | The name of the attribute.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_TEXT | The nodeName property for these node types is a constant value; the name parameter is ignored.
NODE_DOCUMENT_TYPE | The name of the document type; for example, the xxx in <!DOCTYPE xxx ...>.
NODE_ELEMENT | The name of the XML tag, with any namespace prefix included if present.
NODE_ENTITY | The name of the entity.
NODE_ENTITY_REFERENCE | The name of the entity referenced. Note that the name does not include the leading ampersand or the trailing semicolon. The name includes the namespace if one is present.
NODE_NOTATION | The name of the notation.
NODE_PROCESSING_INSTRUCTION | The target—the first token following the <? characters.

You cannot create a node of type NODE_DOCUMENT, NODE_DOCUMENT_TYPE, NODE_ENTITY, or NODE_NOTATION.

When a node is created, it is created in the context of a namespace if one is supplied (if the namespaceURI parameter is supplied). If one is not supplied, the node is created in the namespace of the document. If namespaceURI is specified, the node is created in the context of the namespaceURI with the prefix specified on the node name.

For node types that don't have names, the empty string, "", should be passed as the name parameter.

For elements and entity references, when the namespaceURI parameter is anything other than "" and the name parameter does not contain a prefix (xxx in xxx:yyy), the namespaceURI is treated as the default namespace.

Attributes cannot be scoped to a default namespace, and other elements are not qualified to a particular namespace (they are treated as being from the namespace defined by the document itself).

When namespaceURI is the empty string, "", the node is created within the special namespace of the current document. Creating a qualified node without specifying a nonempty namespaceURI returns an error.

This member is an extension of the W3C DOM.

Example

The following example creates an attribute node called "Sci-Fi" and adds it to the attributes for the DOMDocument object:

Dim xmlDoc
Dim MyNode
Dim namedNodeMap
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set MyNode = xmlDoc.createNode(2, "Sci-Fi", "")
Set namedNodeMap = xmlDoc.childNodes.item(0).attributes
namedNodeMap.setNamedItem (MyNode)
MsgBox (xmlDoc.documentElement.xml)

See Also

Applies To: DOMDocument Object


createProcessingInstruction Method

Creates a processing instruction node that contains the supplied target and data.

objXMLDOMProcessingInstruction =
    oXMLDOMDocument.createProcessingInstruction(target, data)

Parameters

target
String specifying the target part of the processing instruction. This supplies the nodeName property of the new object.
data
String specifying the rest of the processing instruction preceding the closing ?> characters. This supplies the nodeValue property for the new object.

Return Value

Object. Returns the new XMLDOMProcessingInstruction object.

Remarks

Creating a processing instruction node with this method is the same as using createNode where the type parameter value is NODE_PROCESSING_INSTRUCTION and no namespace is specified. You cannot specify a namespace with the createProcessingInstruction method.

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null. You must explicitly call one of the node insert methods—insertBefore, replaceChild, or appendChild—to add the new object.

The new object's nodeType property has the value NODE_PROCESSING_INSTRUCTION.

Example

For example, to generate the processing instruction <?XML version="1.0"?>, specify the target string XML and the data string version="1.0":

Dim xmlDoc
Dim pi
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set pi = xmlDoc.createProcessingInstruction("xml", "version=""1.0""")
xmlDoc.insertBefore pi, xmlDoc.childNodes.item(0)
MsgBox (xmldoc.xml)

See Also

Applies To: DOMDocument Object


createProcessor Method

Creates a rental-model XSLProcessor object that will use this template. Multiple processors can be created from the same XSLTemplate object.

Set objXSLProcessor = objXSLTemplate.createProcessor

See Also

Applies To: XSLTemplate Object


createTextNode Method

Creates a text node that contains the supplied data.

objXMLDOMText = oXMLDOMDocument.createTextNode(data)

Parameters

data
String specifying the value to be supplied to the new text object's nodeValue property.

Return Value

Object. Returns the new XMLDOMText object.

Remarks

Creating a text node with this method is the same as using createNode where the type parameter value is NODE_TEXT and no namespace is specified. You cannot specify a namespace with the createTextNode method.

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. (In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null.) You must explicitly call one of the node insert methods—insertBefore, replaceChild, or appendChild—to add the new object.

The new object's nodeType property has the value NODE_TEXT.

Example

The following example creates an XMLDOMText object, and then uses the insertBefore method to place it before the first child node of the document:

Dim xmlDoc
Dim root
Dim MyText
Dim MyNewNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
Set MyText = xmlDoc.createTextNode ("Hello World")
Set MyNewNode = root.insertBefore (MyText, root.childNodes.item(0))

See Also

Applies To: DOMDocument Object


deleteData Method

Deletes the specified substring within the string data.

oXMLDOMCharacterData.deleteData(offset, count)

Parameters

offset
Long integer value specifying the offset, in characters, at which to start deleting string data.
count
Long integer value specifying the number of characters to delete.

Remarks

If the offset and count of characters specify a range beyond the end of the string, this method deletes only to the end of the string.

The length property is updated by this operation.

Example

The following VBScript example creates an XMLDOMComment object and uses the deleteData method to delete the last six characters:

Dim xmlDoc
Dim comment
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set comment = xmlDoc.createComment("Hello World!")
comment.deleteData 6, 6
MsgBox (comment.xml)

See Also

Applies To: XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | XMLDOMText Object


depth Method

Returns the depth within the document tree at which the specified node appears.

depth(pNode)

Parameters

pNode
Object. Node for which the depth is to be returned.

Return Value

Long integer. Returns the depth within the document tree at which the specified node appears. The document element, or root node, returns the value "0"; its children return the value "1", and so on.

See Also

Applies To: XTLRuntime Object


formatDate Method

Formats the supplied date using the specified formatting options.

formatDate(varDate, bstrFormat, varDestLocale)

Parameters

varDate
Variant. Date that is to be formatted.
bstrFormat
String. Specified formatting. The following codes are supported:
M | Months as 1–12
MM | Months as 01–12
MMM | Months as Jan–Dec
MMMM | Months as January–December
MMMMM | Months as the first letter of the month
d | Days as 1–31
dd | Days as 01–31
ddd | Days as Sun–Sat
dddd | Days as Sunday–Saturday
yy | Years as 00–99
yyyy | Years as 1900–9999
varDestLocale [optional]
Variant. Locale to use in determining the correct sequence to be employed in the date. If not supplied, the month-day-year sequence is used.

Note This parameter is not yet implemented.

Return Value

String. Returns the string containing the date in the requested format.

The formatDate method is derived from the Win32 API call, GetDateFormat. Information on this API can be found at http://msdn.microsoft.com/library/books/devintl/S24B4_a.HTM.

Example

<?xml version='1.0'?>
<?xml-stylesheet type="text/xsl" href="Sample.xsl"?>
<XMLSamples xml:space="preserve"
         xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <FileDetails>
   <Author>Amit S. Kripalani</Author>
   <Date dt:dt="datetime">2000-02-16T15:56:00</Date>
  </FileDetails>
</XMLSamples>

Sample.xsl

<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl"
      xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <xsl:template match="/">
  <HTML>
  <HEAD>
  </HEAD>
  <BODY>
   <xsl:for-each select="XMLSamples/FileDetails/Date">
   Date Edited :
<xsl:eval>
formatDate(this.nodeTypedValue, "MMM dd, yyyy");
</xsl:eval>
   </xsl:for-each>
  </BODY>
  </HTML>
</xsl:template>
</xsl:stylesheet>

Output

Date Edited : Feb 16, 2000

See Also

Applies To: XTLRuntime Object


formatIndex Method

Formats the supplied integer using the specified numerical system.

formatIndex(lIndex, bstrFormat)

Parameters

lIndex
Long integer. Integer value that is to be converted.
bstrFormat
String. Specified numbering system. The following formats are supported:
1 | Standard numbering system
01 | Standard numbering with leading zeroes
A | Uppercase sequence: A–Z, AA–ZZ
a | Lowercase sequence: a–z, aa–zz
I | Uppercase Roman numerals: I, II, III, IV, ...
i | Lowercase Roman numerals: i, ii, iii, iv, ...

Return Value

String. Returns the integer value formatted using the specified numbering system.

See Also

Applies To: XTLRuntime Object


formatNumber Method

Formats the supplied number using the specified format.

formatNumber(dblNumber, bstrFormat)

Parameters

dblNumber
Double. Number to be formatted.
bstrFormat
String. Specified formatting characteristics to be applied to the number. Zero or more of the following values can be present:
# Display only significant digits; does not display insignificant zeros.
0 (zero) | Display insignificant zeros if a number has fewer digits than there are zeros in the format.
? Add spaces for insignificant zeros on either side of the decimal point so that decimal points align with a fixed-point font. You can also use this symbol for fractions that have varying numbers of digits.
. Indicate the placement of the decimal point within the format.
, Display a comma as a thousands separator or scales a number by a multiple of one thousand.
% Display a number as a percentage of 100.
E- | (Also "e-") Display a number in scientific format (exponential notation). If a format contains a zero (0) or a number sign (#) to the right of an exponent code, display the number in scientific format and inserts an "E" or "e." The number of zeros or number signs to the right determines the number of digits in the exponent. Place a minus sign by negative exponents.
E+ | (Also "e+") Place a minus sign by negative exponents and a plus sign by positive exponents.

Return Value

String. Returns a string containing the formatted number.

See Also

Applies To: XTLRuntime Object


formatTime Method

Formats the supplied time using the specified formatting options.

formatTime(varTime, bstrFormat, varDestLocale)

Parameters

varTime
Variant. Time value that is to be formatted.
bstrFormat
String. Specified formatting to be applied to the time. The following codes are supported:
h | Hours as 0-23
hh | Hours as 00-23
m | Minutes as 0-59
mm | Minutes as 00-59
s | Seconds as 0-59
ss | Seconds as 00-59
AM/PM | Insert AM or PM, display hours as 12-hour clock
am/pm | Insert am or pm, display hours as 12-hour clock
A/P | Insert A or P, display hours as 12-hour clock
a/p | Insert a or p, display hours as 12-hour clock
[h]:mm | Elapsed time in hours; for example, 25.02
[mm]:ss | Elapsed time in minutes; for example, 63:46
[ss] | Elapsed time in seconds
ss.00 | Fractions of a second
varDestLocale [optional]
Variant. Locale to use in determining the correct sequence to be employed in the time.

Note This parameter is not yet implemented.

Return Value

String. Returns the string containing the time in the requested format.

Example

<?xml version='1.0'?>
<?xml-stylesheet type="text/xsl" href="Sample.xsl"?>
<XMLSamples xml:space="preserve"
         xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <FileDetails>
   <Author>Amit S. Kripalani</Author>
   <Date dt:dt="datetime">2000-02-16T15:56:00</Date>
  </FileDetails>
</XMLSamples>

Sample.xsl

<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl"
      xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <xsl:template match="/">
  <HTML>
  <HEAD>
  </HEAD>
  <BODY>
   <xsl:for-each select="XMLSamples/FileDetails/Date">
   Date Edited :
<xsl:eval>
formatTime(this.nodeTypedValue, "hh:mm:ss");
</xsl:eval>
   </xsl:for-each>
  </BODY>
  </HTML>
</xsl:template>
</xsl:stylesheet>

Output

Time Edited : 03:56:00

See Also

Applies To: XTLRuntime Object


get Method

Returns a read-only XML DOM node containing the <Schema> element.

Set objXMLDOMNode = objXMLDOMSchemaCache.get(namespaceURI)

Parameters

namespaceURI
The namespace URI associated with the schema to return.

This may be any string that can be used in an xmlns attribute, but it cannot contain entity references. The same white space normalization that occurs on the xmlns attribute also occurs on this argument (that is, leading and trailing white space is trimmed, new lines are converted to spaces, and multiple adjacent white space characters are collapsed into one space).

Remarks

This will not necessarily be the same document object provided by the add method, because the add method may have copied the schema. For inline schemas, this will apply directly to the <Schema> node embedded within the document.

See Also

Applies To: XMLSchemaCache


getAllResponseHeaders Method

Retrieves the values of all the HTTP headers.

strValue = oXMLHttpRequest.getAllResponseHeaders()

Return Value

String. Contains the resulting header information.

Remarks

Each header name/value pair is separated by a combination carriage return-linefeed character (vbCrLf in Microsoft® Visual Basic®).

The results of this method are valid only after the send method has been successfully completed.

See Also

getResponseHeader Method | setRequestHeader Method

Applies To: XMLHttpRequest Object


getAttribute Method

Gets the value of the named attribute.

objValue = oXMLDOMElement.getAttribute(name)

Parameters

name
String specifying the name of the attribute to return.

Return Value

Variant. Returns a string that contains the attribute value or returns the empty string if the named attribute does not have a specified or default value.

Remarks

Another way to retrieve attributes is to use the XMLDOMNamedNodeMap object's getNamedItem method.

See Also

Applies To: XMLDOMElement Object


getAttributeNode Method

Gets the named attribute node.

objXMLDOMAttribute = oXMLDOMElement.getAttributeNode(name)

Parameters

name
String specifying the name of the attribute to be retrieved.

Return Value

Object. Returns the XMLDOMAttribute object with the supplied name, or Null if the named attribute cannot be found on this element.

See Also

Applies To: XMLDOMElement Object


getElementsByTagName Method (DOMDocument)

Returns a collection of elements that have the specified name.

objXMLDOMNodeList =
    oXMLDOMDocument.getElementsByTagName(tagName)

Parameters

tagName
String specifying the element name to find. The tagName "*" returns all elements in the document.

Return Value

Object. Points to a collection of elements that match the specified name.

Remarks

The elements in the collection are returned in the order in which they would be encountered in a preorder traversal of the document tree. In a preorder traversal, the parent root node is visited first and each child node from left to right is then traversed.

The returned XMLDOMNodeList object is live and immediately reflects changes to the nodes that appear in the list.

More complex searches can be performed using the selectNodes method, which may also be faster in some cases.

Example

The following example creates an XMLDOMNodeList object using the DOMDocument object's getElementsByTagName method, and then displays all of the elements with the desired tag name:

Dim ElemList
Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set objNodeList = xmlDoc.getElementsByTagName("AUTHOR")
For i=0 To (objNodeList.length -1)
  MsgBox (objNodeList.item(i).xml)
Next

See Also

Using Collections

Applies To: DOMDocument Object


getElementsByTagName Method (XMLDOMElement)

Returns a list of all descendant elements that match the supplied name.

objXMLDOMNodeList = oXMLDOMElement.getElementsByTagName(tagName)

Parameters

tagName
String specifying the name of the element to find. The string "*" matches all descendant elements of this element.

Return Value

Object. Returns an XMLDOMNodeList object containing all elements that match the supplied name.

Remarks

Elements appear in the order encountered in a preorder traversal of this element's tree.

Note that the XMLDOMNodeList object is returned even if there are no matches. In such a case, the length of the list will be set to zero.

The XMLDOMNodeList object is live and immediately reflects changes to the nodes that appear in the list.

See Also

Applies To: XMLDOMElement Object


getNamedItem Method

Retrieves the attribute with the specified name.

objXMLDOMNode = oXMLDOMNamedNodeMap.getNamedItem(name)

Parameters

name
String specifying the name of the attribute.

Return Value

Object. Returns the XMLDOMNode object for the specified attribute. Returns Null if the attribute node is not in this collection.

See Also

Applies To: XMLDOMNamedNodeMap Object


getProperty Method (XMLDOMDocument2)

Gets the default values or values set by setProperty.

strPropValue = objXMLDOMDocument2.getProperty(name)

Parameters

name
String name of the property. This name is case-sensitive.

See Also

Applies To: XMLDOMDocument2 Object


getProperty Method (XMLDOMSelection)

Looks up a property by name.

strValue = objXMLDOMSelection.getProperty(name)

Parameters

name
String name of the property. This name is case-sensitive.

Remarks

The getProperty method returns the value for the SelectionLanguage that was set by calling the setProperty method on the document or the default value.

See Also

Applies To: XMLDOMSelection Object


getQualifiedItem Method

Returns the attribute with the specified namespace and attribute name.

objXMLDOMNode =
    oXMLDOMNamedNodeMap.getQualifiedItem(baseName, namespaceURI)

Parameters

baseName
String specifying the base name of the attribute, without namespace qualification.
namespaceURI
String specifying the namespace prefix that qualifies the attribute name.

Return Value

Object. Returns the attribute node specified by the baseName and namespaceURI parameters. Returns Null if the attribute is not in the collection or if the item is not an attribute.

Remarks

This member is an extension of the W3C DOM.

See Also

Applies To: XMLDOMNamedNodeMap Object


getResponseHeader Method

Retrieves the value of an HTTP header from the response body.

strValue = oXMLHttpRequest.getResponseHeader(bstrHeader)

Parameters

bstrHeader
String containing the case-insensitive header name.

Return Value

String. Contains the resulting header information.

Remarks

The results of this method are valid only after the send method has been successfully completed. The following line,

xmlhttp.getResponseHeader("Content-Type");

returns the string "text/xml", assuming the server set "text/xml" as the content type. The full list of header variables you can query can be accessed via the getAllResponseHeaders method.

See Also

getAllResponseHeaders Method | setRequestHeader Method

Applies To: XMLHttpRequest Object


hasChildNodes Method

Returns True if this node has children.

boolValue = oXMLDOMNode.hasChildNodes()

Return Value

Boolean. Returns True if this node has children.

Remarks

This method offers a fast way to determine whether a node has children. It always returns False for nodes that, by definition, cannot have children: the XMLDOMCDATASection, XMLDOMComment, XMLDOMNotation, XMLDOMProcessingInstruction, and XMLDOMText nodes.

Example

The following example checks a node to see if it has any child nodes. If it does, it displays the number of child nodes it contains:

Dim xmlDoc
Dim currNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set currNode = xmlDoc.documentElement.firstChild
If currNode.hasChildNodes Then
  MsgBox currNode.childNodes.length
Else
  MsgBox ("no child nodes")
End If

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


hasFeature Method

Returns True if the specified version of the implementation supports the specified feature.

boolValue = oXMLDOMImplementation.hasFeature(feature, version)

Parameters

feature
String that specifies the feature to test. In Level 1, valid feature values are "XML," "DOM," and "MS-DOM" (case insensitive).
version
String that specifies the version number to test, or if Null, tests for implementation of the feature in any version. In Level 1, "1.0" is the valid version value.

Return Value

Boolean. Returns True if the specified feature is implemented; False otherwise.

See Also

Applies To: XMLDOMImplementation Object


insertBefore Method

Inserts a child node to the left of the specified node or at the end of the list.

objXMLDOMNode = oXMLDOMNode.insertBefore(newChild, refChild)

Parameters

newChild
Object. Address of the new node to be inserted.
refChild
Variant. Address of the reference node; newChild is inserted to the left of refChild. If Null, newChild is inserted at the end of the child list.

Return Value

Object. On success, returns the child node that was inserted.

Remarks

This operation depends on the value of the nodeType property.

NODE_ATTRIBUTE

This operation depends on the value of the newChild parameter.

NODE_ATTRIBUTE, NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_TYPE, NODE_ELEMENT, NODE_ENTITY, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION | Returns an error. These node types cannot be children of an attribute.
NODE_DOCUMENT_FRAGMENT | Inserts the children of the document fragment (newChild) and returns newChild.
NODE_ENTITY_REFERENCE, NODE_TEXT | Inserts newChild and returns newChild.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_ENTITY, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns an error. These node types either cannot have children or their children are read-only.

NODE_DOCUMENT

This operation depends on the value of the newChild parameter.

NODE_ATTRIBUTE, NODE_CDATA_SECTION, NODE_DOCUMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE, NODE_NOTATION, NODE_TEXT | Returns an error. These nodes are not valid as children of a document node.
NODE_COMMENT, NODE_PROCESSING_INSTRUCTION | Inserts newChild and returns newChild.
NODE_ELEMENT | Inserts newChild and returns newChild. Note, however, that by definition an XML document (the Document node) can have only a single child. Therefore an error is returned if the Document node already has a child.
NODE_DOCUMENT_FRAGMENT | Inserts the children of the document fragment (in newChild) and returns newChild. Note that the insert operations are subject to the rules for child nodes and can fail if the document fragment children represent node types that cannot be inserted.
NODE_DOCUMENT_TYPE | Returns an error. The document type is read-only.

NODE_DOCUMENT_FRAGMENT

This operation depends on the value of the newChild parameter.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_TYPE | Returns an error. These nodes types are not valid as children of a document fragment.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_ELEMENT, NODE_ENTITY_REFERENCE, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Inserts newChild and returns newChild.
NODE_DOCUMENT_FRAGMENT | Inserts the children of the document fragment (newChild) and returns newChild.
NODE_ENTITY, NODE_NOTATION | Returns an error. Entities and notations are read-only and cannot be inserted into a document.

NODE_ELEMENT

This operation depends on the value of the newChild parameter.

NODE_CDATA_SECTION, NODE_COMMENT, NODE_ELEMENT, NODE_ENTITY_REFERENCE, NODE_TEXT, NODE_PROCESSING_INSTRUCTION | Inserts newChild and returns newChild.
NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_TYPE, NODE_ENTITY, NODE_NOTATION | Returns an error. These node types cannot be children of an element node.
NODE_DOCUMENT_FRAGMENT | Inserts the children of the document fragment (newChild) and returns newChild.
NODE_ENTITY_REFERENCE | Returns an error. Although the child nodes of an entity reference are the expanded entity, the children cannot be modified.

The node supplied in refChild must be a child node of this node or Null. The newChild is inserted before refChild (as the left sibling) in the child list. If refChild is Null, newChild is inserted at the end of the child list. If refChild is not a child of this node, an error is returned.

If newChild is a node of type DOCUMENT_FRAGMENT, all its children are inserted, in the same order, before refChild. If newChild is already in the tree, it is first removed.

These parameters can represent nodes in the same document or in different documents. When in the same document, the nodes retain their default attributes and data types. When these parameters represent nodes in different documents, the nodes either lose or alter their default attributes, depending on whether this node's document has a DTD (or other schema). The Microsoft implementation attempts to correctly merge the different DTDs.

If the newChild node's DTD or schema differs from the original document, the nodes will be treated with the definitions in the new DTD, including default attributes and data types. If there is no DTD, the nodes keep their data types by picking up an instance definition (attributes lose their data types), and default attributes are lost. Note also that cutting and pasting between documents with two different DTDs can result in an invalid document that can fail to parse after being saved.

When elements are added as children to other elements, they introduce their own namespace scoping. That is, if the element is from a default namespace, the default has scope beginning with the element being added.

If an entity reference is being added as a child to an element or document fragment, the namespace declaration of the entity reference is added to the containing element. Similarly, if the entity reference is being added to an attribute, the namespace and the prefix of the entity reference are added to the element containing the attribute.

If there is a conflict between prefixes on the containing element and the prefixes being added, the insertBefore operation fails and returns an error. For example, a conflict occurs when a new attribute referring to a different namespace is added to an element with the namespace, as in the following:

xmlns:myns="URN1"

The error could result from the new attribute, where "myns" refers to a different namespace, "URN2," such as would result from a call to createNode ("attribute","myns:myname","URN2"):

myns:myname="myattributevalue"

Note that namespace conflict errors occur only when setting attributes. Inserted child elements do not cause a namespace conflict.

When adding a document fragment, the containing element adds all namespaces and prefixes from the document fragment. If this causes a conflict with the containing element, insertBefore returns an error.

Example

The following VBScript example creates a new XMLDOMNode object and inserts it before the second child of the top-level node:

Dim xmlDoc
Dim root
Dim newNode
Dim currNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
MsgBox root.xml
Set newNode = xmlDoc.createNode (1, "VIDEOS", "")
Set currNode = root.insertBefore(newNode, root.childNodes.item(1))
MsgBox root.xml

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


insertData Method

Inserts the supplied string at the specified offset.

oXMLDOMCharacterData.insertData(offset, data)

Parameters

offset
Long integer specifying the offset, in characters, at which to insert the supplied string data.
data
String containing the data that is to be inserted into the existing string.

Remarks

The length property is updated by this operation.

Example

The following VBScript example creates an XMLDOMComment object and uses the insertData method to insert a string into it:

Dim xmlDoc
Dim comment
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set comment = xmlDoc.createComment("Hello World!")
comment.insertData 6, "Beautiful "
MsgBox (comment.xml)

See Also

Applies To: XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | XMLDOMText Object


item Method (XMLDOMNodeList)

Allows random access to individual nodes within the collection.

objXMLDOMNode = oXMLDOMNodeList.item(index)

Parameters

index
Long integer. Index of the item within the collection. The first item is zero.

Return Value

Object. Returns an XMLDOMNode object. Returns Null if the index is out of range.

Example

The following VBScript example creates an XMLDOMNodeList object with the document's getElementsByTagName method. It then iterates through the collection, displaying the text value of each item in the list:

Dim xmlDoc
Dim objNodeList
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set objNodeList = xmlDoc.getElementsByTagName("AUTHOR")
For i = 0 To (objNodeList.length -1)
  MsgBox (objNodeList.item(i).text)
Next

See Also

Applies To: XMLDOMNodeList Object | XMLDOMSelection Object


item Method (XMLDOMNamedNodeMap)

Allows random access to individual nodes within the collection.

objXMLDOMNode = oXMLDOMNamedNodeMap.item(index)

Parameters

index
Long integer. Index of the item within the collection. The first item is zero.

Return Value

Object. Returns an XMLDOMNode object. Returns Null if the index is out of range.

Example

The following example creates an XMLDOMNodeList object with the document's getElementsByTagName method. It then iterates through the collection, displaying the text value of each item in the list:

Dim xmlDoc
Dim objNodeList
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set objNodeList = xmlDoc.getElementsByTagName("AUTHOR")
For i = 0 To (objNodeList.length -1)
  MsgBox (objNodeList.item(i).text)
Next

See Also

Applies To: XMLDOMNamedNodeMap Object | XMLDOMSelection Object


load Method

Loads an XML document from the specified location.

boolValue = oXMLDOMDocument.load(url)

Parameters

url
String containing a URL that specifies the location of the XML file.

Return Value

Boolean. Returns True if the load succeeded; False if the load failed.

Remarks

If the URL cannot be resolved or accessed or does not reference an XML document, this method returns an error and sets the DOMDocument object's documentElement property to null.

The load method can also take any object that supports IStream and the IIS Request object.

Calling load or loadXML on an existing document immediately discards the content of the document.

Note that schemas are not applied when loading XML from a string.

This member is an extension of the W3C DOM.

Example

The following VBScript example creates a DOMDocument object and uses the load method to load a local XML file:

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")

See Also

Applies To: DOMDocument Object | XML DOM Persistence


loadXML Method

Loads an XML document using the supplied string.

boolValue = oXMLDOMDocument.loadXML(xmlString)

Parameters

xmlString
String containing the XML string to load into this XML document object. This string can contain an entire XML document or a well-formed fragment.

Return Value

Boolean. Returns True if the XML load succeeded. Returns False and sets the DOMDocument object's documentElement property to Null if the XML load failed.

Remarks

Calling load or loadXML on an existing document immediately discards the content of the document. The loadXML() will work only with UTF-16 or UCS-2 encodings.

This member is an extension of the W3C DOM.

Example

The following VBScript example creates a DOMDocument object, and then uses its loadXML method to load the specified XML before displaying it:

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.loadXML("<customer><first_name>Joe</first_name><last_name>Smith</last_name></customer>")
MsgBox (xmlDoc.xml)

See Also

Applies To: DOMDocument Object | XML DOM Persistence


matches Method

Checks whether the node that is passed in is contained in the current collection.

NewObjXMLDOMNode = objXMLDOMSelection.matches(objXMLDOMNode)

Remarks

When matches is called with node A, it returns node B such that if B was set as the context on a query, A is in the result set of the query. If no such B node is found, matches returns Null.

The matches method does not take into account the current context of the query.

See Also

Applies To: XMLDOMSelection Object


nextNode Method (XMLDOMNodeList)

Returns the next node in the collection.

objXMLDOMNode = oXMLDOMNodeList.nextNode()

Return Value

XMLDOMNode object, which refers to the next node in the collection. Returns Null if there is no next node.

Remarks

The iterator initially points before the first node in the list so that the first call to nextNode returns the first node in the list.

This method returns Null when the current node is the last node or there are no items in the list. When the current node is removed from the list, subsequent calls to nextNode return Null. The iterator must be reset by calling the reset method.

This member is an extension of the W3C DOM.

Example

The following VBScript example creates an XMLDOMNodeList object and uses its nextNode property to iterate the collection:

Dim xmlDoc
Dim objNodeList
Dim objNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set objNodeList = xmlDoc.getElementsByTagName("AUTHOR")
For i = 0 TO (objNodeList.length -1)
  Set objNode = objNodeList.nextNode
  MsgBox objNode.text
Next

See Also

Applies To: XMLDOMNamedNodeMap Object | XMLDOMNodeList Object | XMLDOMSelection Object


nextNode Method (XMLDOMNamedNodeMap)

Returns the next node in the collection.

objXMLDOMNode = oXMLDOMNamedNodeMap.nextNode()

Return Value

XMLDOMNode object, which refers to the next node in the collection. Returns Null if there is no next node.

Remarks

The iterator initially points before the first node in the list so that the first call to nextNode returns the first node in the list.

This method returns Null when the current node is the last node or there are no items in the list. When the current node is removed from the list, subsequent calls to nextNode return Null. The iterator must be reset by calling the reset method.

This member is an extension of the W3C DOM.

Example

The following example creates an XMLDOMNodeList object and uses its nextNode property to iterate the collection:

Dim xmlDoc
Dim objNodeList
Dim objNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set objNodeList = xmlDoc.getElementsByTagName("AUTHOR")
For i = 0 TO (objNodeList.length -1)
  Set objNode = objNodeList.nextNode
  MsgBox objNode.text
Next

See Also

Applies To: XMLDOMNamedNodeMap Object | XMLDOMNodeList Object | XMLDOMSelection Object


nodeFromID Method

Returns the node whose ID attribute matches the supplied value.

objXMLDOMNode = oXMLDOMDocument.nodeFromID(idString)

Parameters

idString
String containing the value of the ID to match.

Return Value

Object. Returns the node that matches the supplied ID. If no nodes match, returns Null.

Remarks

According to the XML 1.0 Recommendation (REC-xml-19980210), ID attribute values must be unique within their XML documents and no element can specify more than one ID attribute.

This method was designed to handle ID/IDREF relationships in XML but does not require an attribute of type IDREF. It can be used generically, similar to the all collection in DHTML.

To reference a node with nodefromID, the node must by typed as ID in the schema or DTD—simply naming an attribute "ID" doesn't set its data type.

This member is an extension of the W3C DOM.

See Also

Applies To: DOMDocument Object


normalize Method

Normalizes all descendant elements; combines two or more adjacent text nodes into one unified text node.

oXMLDOMElement.normalize()

Remarks

This method converts all text node descendants of this element (at any depth) into a "normal" form where no text nodes are adjacent. In normal form, text nodes can be separated only by markup (such as tags, comments, processing instructions, CDATA sections, and entity references). The normal form is useful for operations that require a particular document tree structure and ensures that the XML DOM view of a document is identical when saved and reloaded.

Collapsed node objects are automatically deleted.

See Also

XMLDOMText Object

Applies To: XMLDOMElement Object


open Method

Initializes an Msxml2.XMLHTTP request and specifies the method, URL, and authentication information for the request.

oXMLHttpRequest.open(method, url, async, user, password)

Parameters

method
String. HTTP method used to open the connection, such as GET, POST, PUT, or PROPFIND.
url
String. Requested URL. This must be an absolute URL, such as "http://Myserver/Mypath/".
async [optional]
Boolean. Indicator of whether the call is asynchronous. The default is True (the call returns immediately). If set to True, attach an onreadystatechange callback so that you can tell when the send call has completed.
user [optional]
Name of the user for authentication. If this parameter is Null ("") or missing and the site requires authentication, the component displays a logon window.
password [optional]
Password for authentication. This parameter is ignored if the user parameter is Null ("") or missing.

Remarks

The values of the request headers, request body, response headers, and response body must be cleared before calling this method.

After calling this method, you must call send to send the request and data (if any) to the server.

Example

The following JScript example creates an HttpRequest object, and then uses the open method to synchronously open an ASP page. It then posts an XML document to an ASP page, which uses the loadXML method to load it.

var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
xmlHTTP.open("http://myserver/save.asp", false);
xmlHTTP.send(xmlDoc)
<% xmlDoc.loadXML(Request); %>

See Also

abort Method

Applies To: XMLHttpRequest Object


peekNode Method

Gets the next node that nextNode will return without advancing the list position.

Set objXMLDOMNode = objXMLDOMSelection.peekNode

Remarks

Like nextNode, peekNode does not result in a snapshot of all matching nodes. Consecutive calls to peekNode will produce the same node over and over, independent of changes in the underlying tree.

See Also

Applies To: XMLDOMSelection Object


remove Method

Removes the specified namespace from a collection.

objXMLDOMSchemaCache.remove(namespaceURI)

Parameters

namespaceURI
The namespace to remove from the collection. This may be any string that can be used in an xmlns attribute, but it cannot contain entity references. The same white space normalization that occurs on the xmlns attribute occurs on this argument (that is, leading and trailing white space is trimmed, new lines are converted to spaces, and multiple adjacent white space characters are collapsed into one space).

See Also

Applies To: XMLSchemaCache Object


removeAll Method

Removes all the nodes from the collection described by the XMLDOMSelection object.

objXMLDOMSelection.removeAll

See Also

Applies To: XMLDOMSelection Object


removeAttribute Method

Removes or replaces the named attribute.

oXMLDOMElement.removeAttribute(name)

Parameters

name
String specifying the name of the attribute to be removed or replaced.

Remarks

If the specified attribute has a default value, this is equivalent to a replace operation: the current value is removed and a new attribute is created with the default value. This operation also resets the object's specified property.

See Also

Applies To: XMLDOMElement Object


removeAttributeNode Method

Removes the specified attribute from this element.

objXMLDOMAttribute = oXMLDOMElement.removeAttributeNode(XMLDOMAttribute)

Parameters

XMLDOMAttribute
Object that supplies the XMLDOMAttribute object to be removed from this element.

Return Value

Object. Returns the removed element.

Remarks

Note that if the attribute has a default value, this call also creates a new attribute object with the default value, associates the new attribute with this element, and resets the attribute's specified property.

See Also

Applies To: XMLDOMElement Object


removeChild Method

Removes the specified child node from the list of children and returns it.

objXMLDOMNode = oXMLDOMNode.removeChild(oldChild)

Parameters

oldChild
Object. Child node to be removed from the list of children of this node.

Return Value

Object. Returns the removed child node.

Example

The following VBScript example creates an XMLDOMNode object (currNode), removes a child node from it, and displays the text of the removed node:

Dim xmlDoc
Dim root
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
Set currNode = root.childNodes.item(1)
Set oldChild = currNode.removeChild(currNode.childNodes.item(1))
MsgBox oldChild.text

See Also

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


removeNamedItem Method

Removes an attribute from the collection.

objXMLDOMNode = oXMLDOMNamedNodeMap.removeNamedItem(name)

Parameters

name
String specifying the name of the attribute to remove from the collection.

Return Value

Object. Returns the node removed from the collection. Returns Null if the named node is not an attribute.

See Also

Applies To: XMLDOMNamedNodeMap Object


removeNext Method

Removes the next node that would be returned by peekNode or nextNode.

Set objXMLDOMNode = objXMLDOMSelection.removeNext

Remarks

The removeNext method is equivalent to the following (except that it also works for attributes):

var node = list.peeknode();
node.parentnode.removeChile(node);

The side effect is that the length of the collection is decremented and the nextNode and item methods will not return it because it has been removed.

See Also

Applies To: XMLDOMSelection Object


removeQualifiedItem Method

Removes the attribute with the specified namespace and attribute name.

objXMLDOMNode =
    oXMLDOMNamedNodeMap.removeQualifiedItem(baseName, namespaceURI)

Parameters

baseName
String specifying the base name of the attribute, without namespace qualification.
namespaceURI
String specifying the namespace prefix that qualifies the attribute name.

Return Value

Object. Returns the attribute node removed, or Null if no node was removed.

Remarks

This member is an extension of the W3C DOM.

See Also

Applies To: XMLDOMNamedNodeMap Object


replaceChild Method

Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.

objXMLDOMNode = oXMLDOMNode.replaceChild(newChild, oldChild)

Parameters

newChild
Object. Address of the new child that is to replace the old child. If Null, oldChild is removed without a replacement.
oldChild
Object. Address of the old child that is to be replaced by the new child.

Return Value

Object. Returns the old child that is replaced.

Remarks

This operation depends on the value of the nodeType property:

NODE_ATTRIBUTE

This operation depends on the value of the newChild parameter:

NODE_ATTRIBUTE, NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_TYPE, NODE_ELEMENT, NODE_ENTITY, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION | Returns an error. These node types cannot be children of an attribute.
NODE_DOCUMENT_FRAGMENT | Replaces oldChild with the children of the document fragment in newChild and returns oldChild.
NODE_ENTITY_REFERENCE, NODE_TEXT | Replaces the specified oldChild with the supplied newChild and returns oldChild.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_ENTITY, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns an error. These node types either cannot have children or their children are read-only.

NODE_DOCUMENT

This operation depends on the value of the newChild parameter:

NODE_ATTRIBUTE, NODE_CDATA_SECTION, NODE_DOCUMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE, NODE_NOTATION, NODE_TEXT | Returns an error. These nodes are not valid as children of a document node.
NODE_COMMENT, NODE_PROCESSING_INSTRUCTION | Replaces the specified oldChild with the supplied newChild and returns oldChild.
NODE_DOCUMENT_TYPE, NODE_ELEMENT | Replaces oldChild with newChild and returns oldChild. Note, however, that by definition an XML document (the document node) can have only a single child. Therefore an error is returned if the document node already has a child.
NODE_DOCUMENT_FRAGMENT | Replaces the specified oldChild with the children of the document fragment (newChild) and returns oldChild. Note that the insert operations are subject to the rules for child nodes and can fail if the document fragment children represent node types that cannot be inserted.
NODE_DOCUMENT_TYPE | Returns an error. The document type is read-only.

NODE_DOCUMENT_FRAGMENT

This operation depends on the value of the newChild parameter:

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_TYPE | Returns an error. These nodes types are not valid as children of a document fragment.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_ELEMENT, NODE_ENTITY_REFERENCE, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Replaces the specified oldChild with the supplied newChild and returns oldChild.
NODE_DOCUMENT_FRAGMENT | Replaces the specified oldChild with the children of the document fragment (newChild) and returns oldChild. Note that the insert operations are subject to the rules for child nodes and can fail if the document fragment children represent node types that cannot be inserted.
NODE_ENTITY, NODE_NOTATION | Returns an error. Entities and notations are read-only and cannot be inserted into a document.

NODE_ELEMENT

This operation depends on the value of the newChild parameter:

NODE_CDATA_SECTION, NODE_COMMENT, NODE_ELEMENT, NODE_ENTITY_REFERENCE, NODE_TEXT, NODE_PROCESSING_INSTRUCTION | Replaces the specified oldChild with newChild and returns oldChild.
NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_TYPE, NODE_ENTITY, NODE_NOTATION | Returns an error. These node types cannot be children of an element node.
NODE_DOCUMENT_FRAGMENT | Replaces the specified oldChild with the children of the document fragment (newChild) and returns oldChild. Note that the insert operations are subject to the rules for child nodes and can fail if the document fragment children represent node types that cannot be inserted.
NODE_ENTITY_REFERENCE | Returns an error. Although the child nodes of an entity reference are the expanded entity, the children cannot be modified.

Example

The following VBScript example creates a new XMLDOMNode object, newElem, and replaces the specified child node with newElem:

Dim xmlDoc
Dim root
Dim newElem
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set root = xmlDoc.documentElement
Set newElem = xmlDoc.createElement("PAGES")
root.childNodes.item(1).replaceChild newElem, root.childNodes.item(1).childNodes.item(0)
MsgBox root.childNodes.item(1).xml

Applies To

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


replaceData Method

Replaces the specified number of characters with the supplied string.

oXMLDOMCharacterData.replaceData(offset, count, data)

Parameters

offset
Long integer value specifying the offset, in characters, at which to start replacing string data.
count
Long integer value specifying the number of characters to replace.
data
String containing the new data that replaces the old string data.

Example

The following VBScript example creates a new XMLDOMComment object, and then replaces the first five characters with a new string:

Dim xmlDoc
Dim comment
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set comment = xmlDoc.createComment("Hello World!")
MsgBox comment.xml
comment.replaceData 0,5,"Goodbye, Cruel"
MsgBox comment.xml

See Also

Applies To: XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | XMLDOMText Object


reset Method (XMLDOMNamedNodeMap)

Resets the iterator.

oXMLDOMNamedNodeMap.reset()

Remarks

This member is an extension of the W3C DOM.

See Also

Applies To: XMLDOMNamedNodeMap Object


reset Method (XMLDOMNodeList)

Resets the iterator.

oXMLDOMNodeList.reset()

Remarks

This method reinitializes the iterator to point before the first node in the XMLDOMNodeList object so that the next call to nextNode returns the first item in the list.

This member is an extension of the W3C DOM.

Example

The following VBScript example creates an XMLDOMNodeList object and iterates the collection using the nextNode method. It then uses the reset method to reset the iterator to point before the first node in the list.

Dim xmlDoc
Dim objNodeList
Dim objNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set objNodeList = xmlDoc.getElementsByTagName("AUTHOR")
For i = 0 TO (objNodeList.length -1)
  Set objNode = objNodeList.nextNode
  MsgBox objNode.text
Next
objNodeList.reset
Set objNode = objNodeList.nextNode
MsgBox objNode.text

See Also

Applies To: XMLDOMNodeList Object | XMLDOMSelection Object


reset Method (XSLProcessor)

Resets the state of the processor to the state it was in just before calling transform. It does not reset any other properties (for example, stylesheet or startMode).

objXSLProcessor.reset

Remarks

The reset method will cause any internally buffered output to be thrown away (that is, it aborts the asynchronous transformation). Calling reset results in the readyState going from READYSTATE_INTERACTIVE back to READYSTATE_LOADED.

A re-entrant call to reset from inside the scope of a call to transform results in that transform call finishing as soon as it can, returning E_FAIL with the error message "User aborted transform."

See Also

Applies To: XSLProcessor Object


save Method

Saves an XML document to the specified location.

objXMLDOMDocument.save(objTarget);

Parameters

objTarget
Object. The object can represent a file name, an ASP Response object, a DOMDocument object, or a custom object that supports persistence. For more information, see the Remarks section below.

Remarks

The behavior differs based on the object specified by the objTarget parameter.

Object | Description
string Specifies the file name. Note that this must be a file name rather than a URL. The file is created if necessary and the contents are entirely replaced with the contents of the saved document. This mode is not intended for use from a secure client such as Internet Explorer. For example:
    dim xmldoc
    set xmldoc = Server.CreateObject("Msxml2.DOMDocument")
    xmldoc.load("c:\myfile.xml")
    xmldoc.save(Server.MapPath("sample.xml"))

The ASP Response object sends the document back to the client that invoked the ASP script. For example:

    dim xmldoc
    set xmldoc = Server.CreateObject("Msxml2.DOMDocument")
    xmldoc.load(Server.MapPath("sample.xml"))

xmldoc.save(Response)
XML Document Object Duplicates the original document. It is the equivalent of saving the document and reparsing it. The document goes through full persistence through XML markup, thereby testing the persistability of your XML document. For example:
    <script language="jscript">
        var xmldoc1 = CreateObject("Msxml2.DOMDocument");
        var xmldoc2 = CreateObject("Msxml2.DOMDocument");
        xmldoc1.load("sample.xml");
        xmldoc1.save(xmldoc2.XMLDocument);

</script>
Custom object supporting persistence | Any other custom COM object that supports QueryInterface for IStream, IPersistStream, or IPersistStreamInit can also be provided here, and the document will be saved accordingly. In the IStream case, the IStream Write method will be called as it saves the document, and in the IPersistStream case, IPersistStream Load will be called with an IStream that supports the Read, Seek, and Stat methods.

External entity references in DOCTYPE, ENTITY, NOTATION, and xml namespace declarations are not changed; they point to the original document. A saved XML document might not load if the URLs are not accessible from the location in which you saved the document.

Character encoding is based on the encoding attribute in the xml declaration, such as <?xml version="1.0" encoding="windows-1252"?>. When no encoding attribute is specified, the default setting is UTF-8.

Validation is not performed during save, which can result in an invalid document that does not load again because of a specified DTD.

This member is an extension of the W3C DOM.

See Also

Applies To: DOMDocument Object | XML DOM Persistence


selectNodes Method (XMLDOMDocument2)

Changes select nodes to return an XMLDOMSelection object. The returned object inherits the threading model of the document object.

Set objXMLDOMSelection = objXMLDOMDocument2.selectNodes(strExpr)

Parameters

strExpr
The XQL or XPath instruction. (For details, see XMLDOMSelection).

Example

Dim xmldoc As New MSXML2.DOMDocument
Dim selection As MSXML2.IXMLDOMSelection

xmldoc.loadXML ("<Customer><Name>Microsoft</Name></Customer>")
xmldoc.setProperty "SelectionLanguage", "XPath"
Set selection = xmldoc.selectNodes("Customer/Name")
MsgBox selection.expr + " -- " + selection.Item(0).xml

The above message box displays "Customer/Name -- <Name>Microsoft</Name>"

selection.expr = "/Customer"
MsgBox selection.expr + " -- " + selection.Item(0).xml

The above message box displays "/Customer –-<Customer><Name>Microsoft</Name></Customer>"

See Also

Applies To: XMLDOMDocument2 Object


selectNodes Method (XMLDOMNode)

Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes as an XMLDOMNodeList object.

objXMLDOMNodeList = oXMLDOMNode.selectNodes(patternString)

Parameters

patternString
String specifying the XSL or XPath query that is to be applied to the context defined by this node. Whether an XSL or XPath query is used is determined by the value of the SelectionLanguage property. By default, the expression is an XSL pattern query. The SelectionLanguage property can be set with the SetProperty method.

Return Value

Object. Returns a collection of nodes selected by an XSL or XPath query. If no nodes are selected, an empty collection is returned.

Remarks

For a complete description of XSL Syntax, see XSL Pattern Syntax.

For a description of XPath sytax, see XPath Syntax.

The selectSingleNode method is much like the selectNodes method but returns only the first matching node rather than the list of all matching nodes.

The XMLDOMNodeList object is live and immediately reflects changes to the nodes that appear in the list.

This member is an extension of the W3C DOM.

Example

The following VBScript example creates an XMLDOMNodeList object containing the nodes specified by the pattern string (i.e., all of the AUTHOR child nodes with a BOOK parent). It then displays the number of nodes contained in the node list:

Dim xmlDoc
Dim nodeList
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set objNodeList = xmlDoc.documentElement.selectNodes("BOOK/AUTHOR")
MsgBox objNodeList.length

See Also

Using the XSLT Processor | Using Collections

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


selectSingleNode Method

Applies the specified pattern-matching operation to this node's context and returns the first matching node.

objXMLDOMNode = oXMLDOMNode.selectSingleNode(patternString)

Parameters

patternString
String specifying the XSL or XPath query that is to be applied to the context defined by this node. Whether an XSL or XPath query is used is determined by the value of the SelectionLanguage property. By default, the expression is an XSL pattern query. The SelectionLanguage property can be set with the SetProperty method.

Return Value

Object. Returns the first node that matches the XSL or XPath query. If no nodes match the query, it returns Null.

Remarks

For a complete description of XSL Syntax, see XSL Pattern Syntax.

For a complete description of XPath syntax, see XPath Syntax.

The selectSingleNode method is much like the selectNodes method but returns only the first matching node rather than the list of all matching nodes.

This member is an extension of the W3C DOM.

Example

The following VBScript example creates an XMLDOMNode object and sets it to the first instance of an AUTHOR node with a BOOK parent. It then displays the text of the node:

Dim xmlDoc
Dim currNode
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set currNode = xmlDoc.documentElement.selectSingleNode("BOOK/AUTHOR")
MsgBox currNode.text

See Also

Using the XSLT Processor

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


send Method

Sends an HTTP request to the server and receives a response.

oXMLHttpRequest.send(varHeader)

Parameters

varHeader
Variant. Case-insensitive header name.

Remarks

This method is synchronous or asynchronous, depending on the value of the bAsync parameter in the open call. If open is called with bAsync == False, this call does not return until the entire response is received or the protocol stack times out. If open is called with bAsync == True, this call returns immediately.

This method takes one parameter, which is the requestBody to use. The acceptable VARIANT input types are BSTR, SAFEARRAY of UI1 (unsigned bytes), IDispatch to an XML DOM object, and IStream *. You can only use chunked encoding (for sending) when sending IStream * input types. The component automatically sets the Content-Length header for all but IStream * input types.

If the input type is a BSTR, the response is always encoded as UTF-8. The caller must set a Content-Type header with the appropriate content type and include a charset parameter.

If the input type is a SAFEARRAY of UI1, the response is sent as is without additional encoding. The caller must set a Content-Type header with the appropriate content type.

If the input type is an XML DOM object, the response is encoded according to the encoding attribute on the <? declaration in the document. If there is no XML declaration or encoding attribute, UTF-8 is assumed.

If the input type is an IStream *, the response is sent as is without additional encoding. The caller must set a Content-Type header with the appropriate content type.

See Also

Applies To: XMLHttpRequest Object


setAttribute Method

Sets the value of the named attribute.

oXMLDOMElement.setAttribute(name, value)

Note that the syntax for Visual Basic must omit the parentheses because this method does not return a value.

oXMLDOMElement.setAttribute name, value

Parameters

name
String specifying the name of the attribute. If the attribute with that name already exists, its value is changed. If the attribute with that name does not exist, it is created.
value
Variant that supplies the value for the named attribute.

Remarks

If an attribute with the supplied name already exists, this method changes its value to the supplied value parameter. The supplied string is not parsed, so any markup (such as syntax meant to be recognized as an entity reference) is treated as literal text and needs to be appropriately escaped by the implementation when it is written out.

To assign an attribute value that contains entity references, the user must create an XMLDOMAttribute object plus any XMLDOMText and XMLDOMEntityReference objects, build the appropriate subtree, and call the setAttributeNode method.

See Also

Applies To: XMLDOMElement Object


setAttributeNode Method

Adds or changes the supplied attribute node on this element.

objXMLDOMAttribute = oXMLDOMElement.setAttributeNode(XMLDOMAttribute)

Parameters

XMLDOMAttribute
Object that contains the attribute node to be associated with this element.

Return Value

Object. Returns Null unless the new attribute replaces an existing attribute with the same name, in which case this method returns the previous, replaced attribute node.

Applies To

Applies To: XMLDOMElement Object


setNamedItem Method

Adds the supplied node to the collection.

objXMLDOMNode = oXMLDOMNamedNodeMap.setNamedItem(newItem)

Parameters

newItem
Object containing the attribute to be added to the collection.

Return Value

Object. Returns the attribute successfully added to the collection.

Remarks

If an attribute already exists with the name in the supplied XMLDOMNode object, the supplied object replaces the existing attribute. The attribute name appears in its XMLDOMNode property.

If the newItem node type is not NODE_ATTRIBUTE, setNamedItem returns an error. For example, it is not possible to modify entities or notations, which are read-only.

See Also

Applies To: XMLDOMNamedNodeMap Object


setProperty Method

Used for setting the SelectionLanguage property, which can be either "XSLPattern" or "XPath." The SelectionLanguage property defaults to "XSLPattern".

objXMLDOMDocument2.setProperty(name, value)

Parameters

name
The "SelectionLanguage" value.
value
Either the "XPath" or "XSLPattern" value.

Remarks

The following table shows properties that you can set using this method. Existing properties from DOMDocument are not accessible through this method. White space is not stripped or normalized in property names or values.

Property | Value
SelectionLanguage | "XPath" or "XSLPattern" This property indicates the type of query the user plans to pass into selectNodes or selectSingleNode. "XSLPattern" is the default for backward compatibility. This flag applies to all nodes whose ownerDocument property points to this document object. Therefore, if a given node is moved to a different document, its selectNode behavior may change depending on the SelectionLanguage setting in the new document.

Example

Dim xmldoc As New MSXML2.DOMDocument
Dim selection As MSXML2.IXMLDOMSelection

xmldoc.loadXML ("<Customer><Name>Microsoft</Name></Customer>")
xmldoc.setProperty "SelectionLanguage", "XPath"
Set selection = xmldoc.selectNodes("Customer/Name")
MsgBox selection.expr + " -- " + selection.Item(0).xml

The above message box displays "Customer/Name -- <Name>Microsoft</Name>"

selection.expr = "/Customer"
MsgBox selection.expr + " -- " + selection.Item(0).xml

The above message box displays "/Customer –-<Customer><Name>Microsoft</Name></Customer>"

See Also

Applies To: XMLDOMDocument2 Object


setRequestHeader Method

Specifies the name of an HTTP header.

oXMLHttpRequest.setRequestHeader(bstrHeader, bstrValue)

Parameters

bstrHeader
String. Header name to set; for example, "depth". This parameter should not contain a colon and should be the actual text of the HTTP header.
bstrValue
String. Value of the header; for example, "infinity".

Remarks

If another header already exists with this name, it is replaced.

Example

The following sample demonstrates usage of the setRequestHeader method

The following script example posts a DOMDocument to an .ASP page on a server and returns the result as a new XML document.

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0" />
</SCRIPT>
</HEAD>
<BODY LANGUAGE=javascript onload="return onload()">
<TABLE border='2' align='center'>
<TR><TD width="150" align='center'>
   <LABEL>Name</LABEL>
   </TD><TD>
   <input name='Customername' type="edit"/>
</TD></TR>
<TR><TD width="150" align='center'>
   <LABEL>Telephone number</LABEL>
   </TD><TD>
   <input type="edit" name='telno'/>
</TD></TR>
</TABLE>
<TABLE align='center'>
   <TR><TD width="150" align='center'>
      <input type='button' value='send Information' align='center' onclick='sendinfo()'/>
   </TD></TR>
</TABLE>
<script language="vbscript">
function onload()
   Set pi = mydata.createProcessingInstruction("xml", "version='1.0' encoding='UTF-8'")
   mydata.insertBefore pi, mydata.firstChild
end function

function sendinfo()
   Dim myhttp

   ' Do validation of input data before sending it.
   if(not(Customername.value = ""))   then
      mydata.documentElement.getElementsByTagName("Name").item(0).text =Customername.value
      mydata.documentElement.getElementsByTagName("TelNo").item(0).text =telno.value
      Set myhttp=CreateObject("Msxml2.XMLHTTP")
      myhttp.open "GET", "http://SamplesServer/httpreqserver.asp", false
      ' Simulate message sent by a custom user agent.
      myhttp.setRequestHeader "User-Agent", "MyCustomUser"
      mydata.async=false
      myhttp.send mydata.XMLDocument
      alert myhttp.responseText
   else
      alert "invalid data"
   end if
end function
</script>
</BODY>
</HTML>
<XML id="mydata">
<MyStruct>
   <Name/>
   <TelNo/>
</MyStruct>
</XML>

http://SamplesServer/httpreqserver.asp

<%@ language=javascript %>
<%
   Response.Expires = -1000;
   // Load the posted XML document.
   var doc = Server.CreateObject("Msxml2.DOMDocument");
   doc.async=false;
   doc.load(Request);
   var result = Server.CreateObject("Msxml2.DOMDocument");
   // Now process the order and build the result document.
   var userAgent = Request.ServerVariables("HTTP_User_Agent");
   var OutputString="Data for "+    doc.documentElement.childNodes.item(0).text+" Added";
   Response.ContentType = "text/xml";
   if(userAgent == "MyCustomUser")
   {
      result.loadXML("<result>" + OutputString +" </result>");
      var pi = result.createProcessingInstruction("xml", "version='1.0'");
      result.insertBefore( pi, result.firstChild);
      result.save(Response);
   }
   else
   {
      Response.Write("<P><B>" + OutputString+" </B></P>");
   }
%>

Output

<?xml version="1.0"?>
<result>Data for [input name] Added</result>

See Also

getResponseHeader Method

Applies To: XMLHttpRequest Object


setStartMode Method

Performs subsets of a larger XSLT transformation by selecting the XSLT mode to start with. This makes it possible to minimize the amount of XSLT processing.

The default value of the start mode is the empty string, "".

objXSLProcessor.setStartMode(mode, namespace)

Parameters

mode
The desired mode as a string. It must be the base name part of the qualified name (For more information, see http://www.w3.org/TR/WD-xslt#qname and http://www.w3.org/TR/REC-xml-names/).
namespace (optional)
The full namespace URI to fully qualify the start mode name.

Remarks

Using setStartMode is essentially the same as your XSLT style sheet starting with the following rule:

<xsl:template match="/">
   <xsl:apply-templates select="*" mode="{mode}"/>
</xsl:template>

See Also

Applies To: XSLProcessor Object


splitText Method

Breaks this text node into two text nodes at the specified offset and inserts the new text node into the tree as a sibling that immediately follows this node.

objXMLDOMText = oXMLDOMText.splitText(offset)

Parameters

offset
Long integer. Number of characters at which to split this text node into two nodes, starting from zero.

Return Value

Object. Returns the new text node.

Remarks

If you specify an offset of zero, the first text node is empty and the right-hand text node contains the previous contents of the node. If you specify an offset beyond the end of the string, a new, empty right-hand text node is created.

See Also

Applies To: XMLDOMCDATASection Object | XMLDOMText Object


substringData Method

Retrieves a substring of the full string from the specified range.

strValue = oXMLDOMCharacterData.substringData(offset, count)

Parameters

offset
Long integer value indicating the offset, in characters, from the beginning of the string. An offset of zero indicates copying from the start of the data.
count
Long integer value indicating the number of characters to retrieve from the specified offset.

Return Value

String. Returns the substring.

Remarks

If the offset and count parameters indicate a range beyond the end of the string, the returned substring continues only until the end of the string data.

Example

The following VBScript example creates an XMLDOMComment object (comment), and then uses the substringData method to retrieve the first five characters of the object:

Dim xmlDoc
Dim comment
Dim MyStr
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.load("c:\books.xml")
Set comment = xmlDoc.createComment("Hello World!")
MyStr = comment.subStringData(0,5)
msgbox strValue

See Also

Applies To: XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | XMLDOMText Object


transform Method

Starts the transformation process or resumes a transformation that previously returned False. When the end of the transformation is reached successfully, True is returned.

boolValue = objXSLProcessor.transform

See Also

Applies To: XSLProcessor Object


transformNode Method

Processes this node and its children using the supplied XSLT style sheet and returns the resulting transformation.

strValue = oXMLDOMNode.transformNode(stylesheet)

Parameters

stylesheet
Object. Valid XML document or DOM node that consists of XSLT elements that direct the transformation of this node.

Return Value

String. Returns a string that contains the product of the transformation of this XML document based on the XSLT style sheet.

Remarks

The stylesheet parameter must be either a DOMDocument node, in which case the document is assumed to be an XSL style sheet, or a DOM node in the XSL style sheet, in which case this node is treated as a stand-alone style sheet fragment.

The source node defines a context for the style sheet to operate on, but navigation outside this scope is allowed. For instance, a style sheet could use the id function to access other parts of the document.

This method supports both stand-alone and embedded style sheets and also provides the ability to run a localized style sheet fragment against a particular source node.

This member is an extension of the W3C DOM.

For more information about XSLT style sheets, see the XSLT Reference.

See Also

Using the XSLT Processor

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


transformNodeToObject Method

Processes this node and its children using the supplied XSLT style sheet and returns the resulting transformation in the supplied object.

obj.transformNodeToObject(stylesheet, outputObject);

Parameters

stylesheet
Object. Valid XML document or DOM node that consists of XSLT elements that direct the transformation of this node.
outputObject
Object. On return, contains the product of the transformation of this XML document based on the XSLT style sheet. If the variant represents the DOMDocument object, the document is built according to its properties and its child nodes are replaced during this transformation process. The XML transformation can also be sent to a stream.

Remarks

The stylesheet parameter must be either a DOMDocument node, in which case the document is assumed to be an XSLT style sheet, or a DOM node in the XSLT style sheet, in which case this node is treated as a stand-alone style sheet fragment.

The source node defines a context for the style sheet to operate on, but navigation outside this scope is allowed. For instance, a style sheet could use the id function to access other parts of the document.

This method supports both stand-alone and embedded style sheets and also provides the ability to run a localized style sheet fragment against a particular source node.

The transformNodeToObject method always generates a Unicode byte-order mark, which means it cannot be used in conjunction with other ASP Response.Write or Response.BinaryWrite calls.

For more information about XSLT, see the XSLT Reference.

This member is an extension of the W3C DOM.

Example

The following JScript example sets up the new XML document object named "result" before making the call to transformNodeToObject.

<SCRIPT>
  // Load data.
  var source = new ActiveXObject("Msxml2.DOMDocument");
  source.async = false
  source.load("data.xml");

  // Load style sheet.
  var stylesheet = new ActiveXObject("Msxml2.DOMDocument");
  stylesheet.async = false
  stylesheet.load("style.xsl");

  // Set up the resulting document.
  var result = new ActiveXObject("Msxml2.DOMDocument");
  result.async = false
  result.validateOnParse = true;

  // Parse results into a result DOM Document.
  source.transformNodeToObject(stylesheet, result);
</SCRIPT>

Visual Basic Example

This sample demonstrates the application of multiple style sheets to an XML file in succession:

  Dim Source As New MSXML2.DOMDocument
  Dim stylesheet As New MSXML2.DOMDocument
  Dim stylesheet2 As New MSXML2.DOMDocument
  Dim result As New MSXML2.DOMDocument
  Dim result2 As New MSXML2.DOMDocument

  ' Load data.
  Source.async = False
  Source.Load "sample.xml"

  ' Load style sheet.
  stylesheet.async = False
  stylesheet.Load "stylesheet1.xsl"

  ' Set up the resulting document.
  result.async = False
  result.validateOnParse = True
  result2.async = False
  result2.validateOnParse = True

  ' Parse results into a result DOM Document.
  Source.transformNodeToObject stylesheet, result

  stylesheet2.async = False
  stylesheet2.Load "stylesheet2.xsl"

  result.transformNodeToObject stylesheet2, result2
  MsgBox result2.xml

Sample.xml

<?xml version="1.0"?>
<COLLECTION dateCreated="01-04-2000">
 <BOOK>
    <TITLE>Home Town</TITLE>
    <AUTHOR>Tracy Kidder</AUTHOR>
    <PUBLISHER>Random House</PUBLISHER>
    <PRICE>250</PRICE>
 </BOOK>
  <BOOK>
    <TITLE>Cosmos</TITLE>
    <AUTHOR>Carl Sagan</AUTHOR>
    <PUBLISHER>Ballantine Books</PUBLISHER>
    <PRICE>200</PRICE>
 </BOOK>
 <BOOK>
    <TITLE>Catwings</TITLE>
    <AUTHOR>Ursula K. Le Guin</AUTHOR>
    <PUBLISHER>Scholastic</PUBLISHER>
    <PRICE>100</PRICE>
 </BOOK>
</COLLECTION>

Stylesheet1.xsl

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
   <PriceList>
      <xsl:for-each select="COLLECTION/BOOK">
   <xsl:sort select="TITLE" data-type="text"/>
   <xsl:copy>
   <xsl:apply-templates select="*"/>
   </xsl:copy>
      </xsl:for-each>
   </PriceList>
  </xsl:template>

  <xsl:template match="*">
   <xsl:copy>
   <xsl:apply-templates />
   </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Stylesheet2.xsl

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
   <LowPriceBooks>
      <xsl:for-each select="*/BOOK[not(PRICE >'220')]">
   <xsl:copy>
   <xsl:apply-templates select="*"/>
   </xsl:copy>
      </xsl:for-each>
   </LowPriceBooks>
  </xsl:template>
  <xsl:template match="*">
   <xsl:copy>
   <xsl:apply-templates />
   </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Final Output

<?xml version="1.0"?>
<LowPriceBooks><BOOK><TITLE>Catwings</TITLE><AUTHOR>Ursula K. Le Guin</AUTHOR><PUBLISHER>Scholastic</PUBLISHER><PRICE>100</PRICE></BOOK><BOOK><TITLE>Cosmos</TITLE><AUTHOR>Carl Sagan</AUTHOR><PUBLISHER>Ballantine Books</PUBLISHER><PRICE>200</PRICE></BOOK></LowPriceBooks>

See Also

Using the XSLT Processor

Applies To: XMLDOMAttribute Object | XMLDOMCDATASection Object | XMLDOMCharacterData Object | XMLDOMComment Object | DOMDocument Object | XMLDOMDocumentFragment Object | XMLDOMDocumentType Object | XMLDOMElement Object | XMLDOMEntity Object | XMLDOMEntityReference Object | XMLDOMNode Object | XMLDOMNotation Object | XMLDOMProcessingInstruction Object | XMLDOMText Object | XTLRuntime Object


uniqueID Method

Returns the unique identifier for the supplied node.

uniqueID(pNode)

Parameters

pNode
Object. Node for which the unique identifier is to be returned.

Return Value

Long integer. Returns the unique identifier for the specified node.

See Also

Applies To: XTLRuntime Object


validate Method

Performs run-time validation on the currently loaded document using the currently loaded DTD, schema, or schema collection. This method only validates fully loaded documents (readyState == 4).

objXMLDOMDocument.validate

Return Value

A ParseError object indicating exactly what error occurred, if any.

Remarks

The validate method returns an IXMLDOMParseError object that is independent of the object returned by the parseError property on a document. Only the errorCode and reason properties of the returned object are set.

Unlike load, validate will fail if there is no DTD/schema applied to the document element. Therefore, validate will not be able tell you whether the document is just well-formed.

The validate method does not parse new schemas but may import a schema from a SchemaCache associated with the document through the schemas property. If there is no schema for a given namespace, the elements in that namespace will not be validated.

Example

The following JScript example shows how to validate at run time:

var xmldoc = new ActiveXObject("Msxml2.DOMDocument");
xmldoc.async = false
// This will validate on load because validateOnParse is set to true
// by default.
xmldoc.load("http://server/myData.xml");
// You make a change to an attribute:
xmldoc.documentElement.setAttribute("a", "123");
// Now you want to verify your document is still valid:
var err = xmldoc.validate();
if (err.errorCode == 0)
{
   alert("Document is valid");
}
else
{
   alert("Validation error:" + err.reason);
}

See Also

Applies To: XMLDOMDocument2 Object


XML DOM Events

This section includes the following topics:

  • ondataavailable Event
  • onreadystatechange Event
  • ontransformnode Event


ondataavailable Event

Indicates that XML document data is available.

Inline HTML
<ELEMENT ondataavailable = "handler" ... >
 All platforms
Event property
object.ondataavailable = handler
 JScript (compatible with ECMA 262 language specification) only
Named script
<SCRIPT FOR = object EVENT = ondataavailable>
 Internet Explorer only

Remarks

When handling asynchronous data, it is useful to be able to start processing in parallel with the download as soon as data becomes available. The ondataavailable event fires each time a new chunk of data arrives. The readyState property defines several states that specify the current status of the asynchronous download.

The event handler for this event can be explicitly set using the ondataavailable property.

This member is an extension of the W3C DOM.

See Also

Applies To: DOMDocument Object


onreadystatechange Event

Indicates when the readyState property changes.

Inline HTML
<ELEMENT onreadystatechange = "handler" ... >
 All platforms
Event property
object.onreadystatechange = handler
 JScript (compatible with ECMA 262 language specification) only
Named script
<SCRIPT FOR = object EVENT = onreadystatechange>
 Internet Explorer only

Remarks

This event can be explicitly set using the onreadystatechange property.

This member is an extension of the W3C DOM.

Example

The following JScript/HTML example demonstrates the sequence of ready states when asynchronously loading a document:

<script>
var xmldoc;
function Load()
{
  xmldoc = new ActiveXObject("Microsoft.XMLDOM");
  xmldoc.onreadystatechange = CheckState;
  xmldoc.load(URL.value);
}
function CheckState()
{
  var state = xmldoc.readyState;
  RESULTS.innerHTML += "readyState = " + state + "<BR>"
  if (state == 4)
  {
    var err = xmldoc.parseError;
    if (err.errorCode != 0)
      RESULTS.innerHTML += err.reason + "<BR>"
    else RESULTS.innerHTML +="success" + "<BR>"
  }
}
</script>
URL: <input type=text size=60 id=URL>
<input type=button value=LOAD onclick="jscript:Load()">
<div id=RESULTS style= "color:red;font-weight:bold;"></div>

The following Visual Basic example shows how to use the onreadystatechange event in an application:

Dim WithEvents xmldoc As DOMDocument

Private Sub Form_Load()
    Set xmldoc = New DOMDocument
    xmldoc.Load ("http://xmlweb/msxml/jscript/error.xml")
End Sub

Private Sub xmldoc_onreadystatechange()
    If (xmldoc.readyState = 4) Then
      msg = "Load  is finished" & Chr(13)
      If (xmldoc.parseError.errorCode <> 0) Then
        msg = msg + "Error: " & xmldoc.parseError.reason
      End If
      MsgBox msg
    End If
End Sub

See Also

Applies To: DOMDocument Object


ontransformnode Event

Fires before each node in the style sheet is applied to each node in the XML source.

boolVal = ontransformnode(nodeCode, nodeData)

Parameters

nodeCode
Indicates the current node in the style sheet.
nodeData
Indicates the current node in the XML source data.

Return Value

Returns True to indicate that transformation processing is to continue; returns False to indicate that transformation processing should stop.

Remarks

The event handler for this event can be explicitly set using the ontransformnode property.

This member is an extension of the W3C DOM.

Example

In this example, the XML data island named "target" contains the XML source data, and the XML data island named "ss" contains the XSLT style sheet.

<XML id="target">
  <PEOPLE>
    <PERSON name="Atkinson, Teresa"  />
    <PERSON name="Debroux, Steve" />
    <PERSON name="O'Hara, Robert" />
    <PERSON name="Porzuczek, Peter" />
  </PEOPLE>
</XML>
<XML id="ss">
  <xsl:template match="/" xmlns:xsl="http://www.w3.org/TR/WD-xsl">
    <xsl:for-each select="/PEOPLE/PERSON/@name" >
      <P><xsl:value-of /></P>
    </xsl:for-each>
  </xsl:template>
</XML>

This script contains the Verify function, which sets the ontransformnode property to the event handler, onxform. The event handler is called before the transformation operation on each node so that, given the sample data provided in the data islands, this sample calls the alert several times.

<SCRIPT LANGUAGE="javascript" FOR="window" EVENT="onload">
document.expando = false;
Verify();
</SCRIPT>
<Script LANGUAGE="javascript">
function onxform ( code, doc ) {
  alert("Target document node being transformed:\n\n" + doc.xml +
    "\n\nStylesheet node used for the transform:\n\n" + code.xml);
  if (doc.value == "O'Hara, Robert")
   return false;  // Stop processing.
  else
   return true;
}
function Verify() {
  target.ontransformnode = onxform;
  var sOut = target.XMLDocument.transformNode(ss.XMLDocument);
  div.innerHTML += sOut;
}
</Script>
<DIV id="div"></DIV>
</BODY>
</HTML>

The return value from the event handler indicates whether to continue processing. In this example, the event handler checks for a particular data value and returns the value "false" to stop transformation processing.

See Also

Applies To: DOMDocument Object


XML DOM Enumerated Constants

DOMNodeType enumerations specify valid settings for the DOM node type. The node type determines valid values and whether the node can have child nodes.

Values

The DOMNodeType enumeration defines the following valid values:

NODE_ELEMENT (1)
The node represents an element (its nodeTypeString property is "element"). An Element node can have the following child node types: Element, Text, Comment, ProcessingInstruction, CDATASection, and EntityReference. The Element node can be the child of the Document, DocumentFragment, EntityReference, and Element nodes.
NODE_ATTRIBUTE (2)
The node represents an attribute of an element (its nodeTypeString property is "attribute"). An Attribute node can have the following child node types: Text and EntityReference. The Attribute node does not appear as the child node of any other node type; note that it is not considered a child node of an Element.
NODE_TEXT (3)
The node represents the text content of a tag (its nodeTypeString property is "text"). A Text node cannot have any child nodes. The Text node can appear as the child node of the Attribute, DocumentFragment, Element, and EntityReference nodes.
NODE_CDATA_SECTION (4)
The node represents a CDATA section in the XML source (its nodeTypeString property is "cdatasection"). CDATA sections are used to escape blocks of text that would otherwise be recognized as markup. A CDATASection node cannot have any child nodes. The CDATASection node can appear as the child of the DocumentFragment, EntityReference, and Element nodes.
NODE_ENTITY_REFERENCE (5)
The node represents a reference to an entity in the XML document (its nodeTypeString property is "entityreference"). This applies to all entities, including character entity references. An EntityReference node can have the following child node types: Element, ProcessingInstruction, Comment, Text, CDATASection, and EntityReference. The EntityReference node can appear as the child of the Attribute, DocumentFragment, Element, and EntityReference nodes.
NODE_ENTITY (6)
The node represents an expanded entity (its nodeTypeString property is "entity"). An Entity node can have child nodes that represent the expanded entity (for example, Text and EntityReference nodes). The Entity node can appear as the child of the DocumentType node.
NODE_PROCESSING_INSTRUCTION (7)
The node represents a processing instruction (PI) from the XML document (its nodeTypeString property is "processinginstruction"). A PI node cannot have any child nodes. The PI node can appear as the child of the Document, DocumentFragment, Element, and EntityReference nodes.
NODE_COMMENT (8)
The node represents a comment in the XML document (its nodeTypeString property is "comment"). A Comment node cannot have any child nodes. The Comment node can appear as the child of the Document, DocumentFragment, Element, and EntityReference nodes.
NODE_DOCUMENT (9)
The node represents a document object, which, as the root of the document tree, provides access to the entire XML document (its nodeTypeString property is "document"). It is created using the ProgID "Microsoft.XMLDOM" or through a data island using <XML> or <SCRIPT LANGUAGE=XML>. A Document node can have the following child node types: Element (maximum of one), ProcessingInstruction, Comment, and DocumentType. The Document node cannot appear as the child of any node types.
NODE_DOCUMENT_TYPE (10)
The node represents the document type declaration, indicated by the <!DOCTYPE> tag (its nodeTypeString property is "documenttype"). A DocumentType node can have the following child node types: Notation and Entity. The DocumentType node can appear as the child of the Document node.
NODE_DOCUMENT_FRAGMENT (11)
The node represents a document fragment (its nodeTypeString property is "documentfragment"). The DocumentFragment node associates a node or subtree with a document without actually being contained within the document. A DocumentFragment node can have the following child node types: Element, ProcessingInstruction, Comment, Text, CDATASection, and EntityReference. The DocumentFragment node cannot appear as the child of any node types.
NODE_NOTATION (12)
The node represents a notation in the document type declaration (its nodeTypeString property is "notation"). A Notation node cannot have any child nodes. The Notation node can appear as the child of the DocumentType node.

Remarks

Note that built-in and numeric entities (such as &amp;) are exposed as Text nodes rather than as Entity nodes.

See Also

XMLDOMNode Object (for script developers) | IXMLDOMNode Interface (for C/C++ and Microsoft Visual Basic developers)


XML DOM Interfaces

This part of the XML Document Object Model (DOM) reference describes the programming interface syntax for C, C++, and Visual Basic developers. The following tables list the XML DOM programming interfaces by category.

XML DOM Base Interfaces

The following interfaces represent implementations of the XML DOM core objects. The interfaces include Microsoft extensions to support namespaces, data types, XML schemas, XSLT operations, asynchronous loading, and saving documents.

IXMLDOMDocument | Represents the top node of the XML DOM tree.
IXMLDOMDocument2 | Extension of IXMLDOMDocument that supports schema caching and validation features, as well as a way to turn on XPath support.
IXMLDOMNamedNodeMap | Provides iteration and access by name to the collection of attributes. This interface includes support for namespaces.
IXMLDOMNode | Represents a single node in the document tree; the base interface for accessing data in the XML object model. Valid node types are defined in the DOMNodeType enumeration. This interface includes support for data types, namespaces, DTDs, and XML schemas.
IXMLDOMNodeList | Supports iteration and indexed access operations on the live collection of XMLDOMNode objects.
IXMLDOMParseError | Returns detailed information about the last error, including the error number, line number, character position, and a text description.
IXMLHttpRequest | Provides a scriptable interface for sending and receiving XML via HTTP, including the ability to communicate with servers that support DAV.
IXTLRuntime | Implements methods that can be called from XSL style sheets.

XML DOM Interfaces

The following interfaces represent implementations of other XML DOM objects.

IXMLDOMAttribute | Represents an attribute object.
IXMLDOMCDATASection | Quotes or escapes blocks of text so that text is not interpreted as markup.
IXMLDOMCharacterData | Provides text manipulation methods that are used by several objects.
IXMLDOMComment | Represents the content of an XML comment.
IXMLDOMDocumentFragment | Represents a lightweight object that is useful for tree insert operations.
IXMLDOMDocumentType | Contains information associated with the document type declaration.
IXMLDOMElement | Represents the Element object.
IXMLDOMEntity | Represents a parsed or unparsed entity in the XML document.
IXMLDOMEntityReference | Represents an entity reference node.
IXMLDOMImplementation | Provides methods that are independent of any particular instance of the document object model.
IXMLDOMNotation | Contains a notation declared in the DTD or schema.
IXMLDOMProcessingInstruction | Represents a processing instruction, which XML defines to keep processor-specific information in the text of the document.
IXMLDOMSchemaCollection | Represents a SchemaCache object.
IXMLDOMSelection | Represents the list of nodes that match a given XSLPattern or XPath expression.
IXMLDOMText | Represents the text content of an element or attribute.
IXSLProcessor | Used for transformations with compiled stylesheets.
IXSLTemplate | Used for caching compiled XSL templates.

The DOMNodeType enumeration lists all valid node types.

Header Files

If you have Microsoft Internet Explorer 5 installed, you can download the interface header files from the Web & Internet Samples download area on MSDN.

To download the header files

  1. In the left pane, click Headers and Libraries.
  2. Download IE 5.01 Headers & Libraries or IE 5.5 Headers & Libraries, and Win32 Headers & Libraries for Windows 2000. Save them to C:\Workshop.
  3. Start Visual C++. From the Tools menu, select Options and then Directories. The choice lists should show Win32 for Platform and Include files for Show directories for.
  4. Add C:\Workshop\Include and C:\Workshop\Include\NT5 to the top of the list of directories.
  5. Change Show Directories for to Library files.
  6. Add C:\Workshop\Lib and C:\Workshop\Lib\NT5 to the top of the list of directories.

See Also

XML DOM Objects


IXMLDOMAttribute Interface

Represents an attribute of the XMLDOMElement object. Valid and default values for the attribute are defined in a DTD or schema.

This interface inherits from IXMLDOMNode.

Methods

name | Contains the attribute name.
value | Contains the attribute value.

Remarks

XMLDOMAttribute objects inherit the XMLDOMNode object but are not actually child nodes of the element and are not considered part of the document tree. Attributes are considered properties of their associated elements rather than independent and separate. Thus the object's parentNode, previousSibling, and nextSibling properties have the value Null.

Attribute nodes cannot be the immediate children of the XMLDOMDocumentFragment node. However, they can be associated with element nodes that are children of the XMLDOMDocumentFragment node.

In this object, the relationship between the value and specified properties can be summarized as follows: If the attribute has an assigned value in the document and the specified property is True, the value is the assigned value. If the attribute has no assigned value in the document and has a default value in the DTD, the specified property is False and the value is the default value in the DTD. If the attribute has no assigned value in the document and has a value of #IMPLIED in the DTD, the attribute does not appear in the structure model of the document.

In XML, the value of an attribute is represented by the child nodes of the attribute node because the value can contain entity references. Thus attributes that contain entity references will have a child list containing both text nodes and entity reference nodes. In addition, because the attribute type might be unknown, there are no tokenized attribute values.

Example

   _variant_t varValue;
   BSTR bstrAttributeName = ::SysAllocString(_T("dateCreated"));
   IXMLDOMElement *pIXMLDOMElement = NULL;
   IXMLDOMDocument *pIXMLDOMDocument = NULL;
   HRESULT hr;

   try
   {
      // Initialize pIXMLDOMDocument and load an
      // XML file into it.
      // Get the document root.
      hr = pIXMLDOMDocument->get_documentElement( &pIXMLDOMElement);
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMElement)
      {
         varValue = _T("year 2000");
         hr = pIXMLDOMElement->setAttribute(bstrAttributeName, varValue);
         SUCCEEDED(hr) ? 0 : throw hr;

         hr = pIXMLDOMElement->getAttribute(bstrAttributeName, &varValue);
         SUCCEEDED(hr) ? 0 : throw hr;

         if(varValue.vt != VT_NULL)
         {
            ::MessageBox(NULL, _bstr_t(varValue), bstrAttributeName,                   MB_OK);
            bResult = TRUE;
         }
         RELEASE(pIXMLDOMElement);
      }
      ::SysFreeString(bstrAttributeName);
      bstrAttributeName=NULL;
   }
   catch(...)
   {
      if(bstrAttributeName)
      {
         ::SysFreeString(bstrAttributeName);
         bstrAttributeName = NULL;
      }
      CHECK_AND_RELEASE(pIXMLDOMElement);
      DisplayErrorToUser();
   }
   // Release pIXMLDOMDocument when finished with it.

Output ( in a message box )
Year 2000

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

See Also

IXMLDOMElement Interface


name Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMAttribute::name Method

IXMLDOMAttribute::get_name Method

Contains the attribute name.

Visual Basic Syntax

strValue = oXMLDOMAttribute.name

C/C++ Syntax

HRESULT get_name(
    BSTR *attributeName);

Parameters

attributeName [out]
Name of the attribute. The value is the same as the XMLDOMNode object's nodeName property.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value when returning Null.
E_INVALIDARG
Value returned if attributeName is Null.

See Also

IXMLDOMAttribute Interface


value Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMAttribute::value Method

IXMLDOMAttribute::get_value Method

Contains the attribute value.

Visual Basic Syntax

strValue = oXMLDOMAttribute.value

C/C++ Syntax

HRESULT get_value(
    VARIANT *attributeValue);

Parameters

attributeValue [out]
Value of the attribute. For attributes with subnodes, the string is the concatenated text of all subnodes with character and general entity reference expanded (replaced with their values).

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if attributeValue is Null.

IXMLDOMAttribute::put_value Method

Contains the attribute value.

Visual Basic Syntax

objXMLDOMAttribute.value = strValue

C/C++ Syntax

HRESULT put_value(
    VARIANT *attributeValue);

Parameters

attributeValue [out]
Value of the attribute. For attributes with subnodes, the string is the concatenated text of all subnodes with character and general entity reference expanded (replaced with their values).

C/C++ Return Values

S_OK
Value returned if successful.
E_FAIL
Value returned if an error occurs.

Remarks

The new attribute value is added as the value of a single text node that is a child of the attribute node. The string contents are unparsed.

See Also

IXMLDOMAttribute Interface


IXMLDOMCDATASection Interface

Used to quote or escape blocks of text to keep that text from being interpreted as markup language.

This interface inherits from IXMLDOMText.

Methods

This interface has no unique methods of its own but exposes the inherited methods of the IXMLDOMText interface.

Remarks

CDATA sections let you include material such as XML fragments within XML documents without needing to escape all the delimiters. The only delimiter recognized in a CDATA section is the "]]>" string that ends the CDATA section.

CDATA sections cannot be nested.

The text contained by the CDATA section is stored in a text node. Note that this text can contain characters that need to be escaped outside of CDATA sections.

Although the CDATASection object inherits the Text object, note that unlike text nodes, the IXMLDOMElement interface's normalize method does not merge CDATASection nodes.

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

See Also

IXMLDOMText Interface


IXMLDOMCharacterData Interface

Provides text-manipulation methods that are used by several objects.

This interface inherits from IXMLDOMNode.

Methods

appendData | Appends the supplied string to the existing string data.
data | Contains this node's data, which depends on the node type.
deleteData | Deletes the specified substring within the string data.
insertData | Inserts the supplied string at the specified offset.
length | Specifies the length, in characters, of the data.
replaceData | Replaces the specified number of characters with the supplied string.
substringData | Retrieves a substring of the full string from the specified range.

Remarks

The XMLDOMCharacterData object does not directly correspond to any node type. Its methods are reused by the XMLDOMCDATASection, XMLDOMComment, and XMLDOMText objects. These methods handle large amounts of text, including sizes larger than can be manipulated natively using string constructs.

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

See Also

IXMLDOMElement Interface


appendData Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMCharacterData::appendData Method

Appends the supplied string to the existing string data.

Visual Basic Syntax

oXMLDOMCharacterData.appendData(data)

C/C++ Syntax

HRESULT appendData(
    BSTR data);

Parameters

data [in]
String data to be appended to the existing string.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if the string isn't appended.
E_FAIL
Value returned if an error occurs.

Remarks

This operation also updates the length method.

See Also

IXMLDOMCharacterData Interface


data Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMCharacterData::data Method

IXMLDOMCharacterData::get_data Method

Contains this node's data, which depends on the node type.

Visual Basic Syntax

strValue = oXMLDOMCharacterData.data

C/C++ Syntax

HRESULT get_data(
    BSTR *data);

Parameters

data [out]
Same value as the nodeValue for this node. See the following Remarks section.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if there is no character data node.
E_INVALIDARG
Value returned if data is Null.

IXMLDOMCharacterData::put_data Method

Assigns this node's data, which depends on the node type.

Visual Basic Syntax

objXMLDOMCharacterData.data = strValue

C/C++ Syntax

HRESULT put_data(
    BSTR data);

Parameters

data [in]
Same value as the nodeValue for this node. See the following Remarks section.

C/C++ Return Values

S_OK
Value returned if successful.
E_FAIL
Value returned if an error occurs.

Remarks

This value depends on the value of the XMLDOMNode object's nodeType property, as follows:

NODE_CDATA_SECTION
Contains a string representing the text stored in the CDATA section.
NODE_COMMENT
Contains the content of the comment, exclusive of the comment start and end sequence.
NODE_TEXT
Contains a string representing the text stored in the text node.

This method returns an error if the data size is larger than can be stored in a single string. In that case, the data must be obtained using multiple calls to substringData.

See Also

IXMLDOMCharacterData Interface


deleteData Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMCharacterData::deleteData Method

Deletes the specified substring within the string data.

Visual Basic Syntax

oXMLDOMCharacterData.deleteData(offset, count)

C/C++ Syntax

HRESULT deleteData(
    long offset,
    long count);

Parameters

offset [in]
Offset, in characters, at which to start deleting string data.
count [in]
Number of characters to delete.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value when returning Null.
E_FAIL
Value returned if an error occurs.

Remarks

If the offset and count of characters specify a range beyond the end of the string, this method deletes only to the end of the string.

This operation also updates the length method.

See Also

IXMLDOMCharacterData Interface


insertData Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMCharacterData::insertData Method

Inserts the supplied string at the specified offset.

Visual Basic Syntax

oXMLDOMCharacterData.insertData(offset, data)

C/C++ Syntax

HRESULT insertData(
    long offset,
    BSTR data);

Parameters

offset [in]
Offset, in characters, at which to insert the supplied string data.
data [in]
String data that is to be inserted into the existing string.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value when returning Null.
E_FAIL
Value returned if an error occurs.

Remarks

This operation also updates the length property.

See Also

IXMLDOMCharacterData Interface


length Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMCharacterData::length Method

IXMLDOMCharacterData::get_length Method

Specifies the length of the data, in characters.

Visual Basic Syntax

lValue = oXMLDOMCharacterData.length

C/C++ Syntax

HRESULT get_length(
    long *dataLength);

Parameters

dataLength [out]
Length, in Unicode characters, of the string present in the data property.

C/C++ Return Values

S_OK

Value returned if successful.

E_INVALIDARG

Value returned if dataLength is Null.

See Also

IXMLDOMCharacterData Interface


replaceData Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMCharacterData::replaceData Method

Replaces the specified number of characters with the supplied string.

Visual Basic Syntax

oXMLDOMCharacterData.replaceData(offset, count, data)

C/C++ Syntax

HRESULT replaceData(
    long offset,
    long count,
    BSTR data);

Parameters

offset [in]
Offset, in characters, at which to start replacing string data.
count [in]
Number of characters to replace.
data [in]
New data that replaces the old string data.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value when returning Null.
E_FAIL
Value returned if an error occurs.

See Also

IXMLDOMCharacterData Interface


substringData Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMCharacterData::substringData Method

Retrieves a substring of the full string from the specified range.

Visual Basic Syntax

strValue = oXMLDOMCharacterData.substringData(offset, count)

C/C++ Syntax

HRESULT substringData(
    long offset,
    long count,
    BSTR *data);

Parameters

offset [in]
Offset, in characters, from the beginning of the string. An offset of zero indicates copying from the start of the data.
count [in]
Number of characters to retrieve from the specified offset.
data [out]
Substring to return.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value when returning Null.

Remarks

If the offset and count parameters indicate a range beyond the end of the string, the returned substring continues only until the end of the string data.

See Also

IXMLDOMCharacterData Interface


IXMLDOMComment Interface

Represents the content of an XML comment.

This interface inherits from IXMLDOMCharacterData.

Methods

This interface has no unique methods of its own but exposes the inherited methods from the IXMLDOMCharacterData interface.

Remarks

The content refers to all characters between the starting <!-- and ending --> tags.

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

See Also

IXMLDOMElement Interface


IXMLDOMDocument Interface

Represents the top level of the XML source. Includes methods and properties used to obtain or create all other XML objects.

This interface inherits from IXMLDOMNode.

Methods

abort* | Aborts an asynchronous download in progress.
async* | Indicates whether asynchronous download is permitted.
createAttribute | Creates a new attribute with the specified name.
createCDATASection | Creates a CDATA section node that contains the supplied data.
createComment | Creates a comment node that contains the supplied data.
createDocumentFragment | Creates an empty DocumentFragment object.
createElement | Creates an element node using the specified name.
createEntityReference | Creates a new EntityReference object.
createNode* | Creates a node using the supplied type, name, and namespace.
createProcessingInstruction | Creates a processing instruction node that contains the supplied target and data.
createTextNode | Creates a text node that contains the supplied data.
doctype | Contains the document type node that specifies the DTD for this document.
documentElement | Contains the root element of the document.
getElementsByTagName | Returns a collection of elements that have the specified name.
implementation | Contains a pointer to the XMLDOMImplementation object for this document.
load* | Loads an XML document from the specified location.
loadXML* | Loads an XML document using the supplied string.
nodeFromID* | Returns the node whose ID attribute matches the supplied value.
ondataavailable* | Property that specifies the event handler for the ondataavailable event.
ondataavailable* | Event that fires when data is available for processing.
onreadystatechange* | Property that specifies the event handler to be run when the readyState property changes.
onreadystatechange* | Event handler run when the readyState property changes.
ontransformnode* | Property that specifies the event handler to be run when transformNode is called.
ontransformnode* | Event handler run before each node of the XSL style sheet is applied to each node of the XML data source.
parseError* | Returns an XMLDOMParseError object that contains information about the last parsing error.
preserveWhiteSpace* | Indicates whether to preserve all white space in the XML document.
readyState* | Indicates the current state of the XML document.
resolveExternals* | Indicates that external definitions (resolvable namespaces, DTD external subsets, and external entity references) are to be resolved at parse time.
save* | Saves the XML document to the specified location.
url* | Returns the canonicalized URL for the last loaded XML document.
validateOnParse* | Indicates whether the parser should validate this document.

* denotes an extension to the W3C DOM.

Remarks

This object represents the top node in the tree. It implements all of the base DOM document methods and provides additional methods and properties that support XSL and XML transformations.

You can create the DOMDocument object and query for the other interfaces as follows:

HRESULT hr;
IXMLDOMDocument * pXMLDoc;
IXMLDOMNode * pXDN;
//...
hr = CoInitialize(NULL); // Check the return value, hr...
hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
       IID_IXMLDOMDocument, (void**)&pXMLDoc); // Check the return value, hr...
hr = pXMLDoc->QueryInterface(IID_IXMLDOMNode, (void **)&pXDN); // Check the return value.

Although there are many interfaces, only one object is created: the document. All other interfaces are accessed or created from the document.

The document can be created using either a free-threaded or a rental-threaded model. The behavior of the two models is identical; rental-threaded documents exhibit better performance because the parser doesn't need to manage concurrent access among threads. Note that you cannot combine nodes or documents that are created using differing threading models. The document threading model is determined by the following settings:

Setting | Rental-threaded model | Free-threaded model
Version-independent ProgID | Msxml2.DOMDocument | Msxml2.FreeThreadedDOMDocument
Version-independent ClassID | f6d90f11-9c73-11d3-b32e-00c04f990bb4 | f6d90f12-9c73-11d3-b32e-00c04f990bb4
Version-dependent ProgID | Msxml2.DOMDocument.2.6 | Msxml2.FreeThreadedDOMDocument.2.6
Version-dependent ClassID | f5078f1b-c551-11d3-89b9-0000f81fe221 | f5078f1c-c551-11d3-89b9-0000f81fe221
VB Class Name | Msxml.DOMDocument26 | Msxml.FreeThreadedDOMDocument26

In addition to the DOM interfaces, IXMLDOMDocument implements a number of standard COM interfaces. You can call the QueryInterface method on IXMLDOMDocument to get the following interfaces:

Interface | Usage
IUnknown | DOMDocument is a wrapper object and each query for DOMDocument returns a new wrapper. You should only compare IUnknown interface pointers.
IconnectionPointContainer | Supports outgoing events ondataavailable and onreadystatechange through IPropertyNotifySink::OnChanged and IDispatch::Invoke.
IDispatch | Interface used by Visual Basic.
IDispatchEx | Interface used by dynamic late-bound scripting languages such as VBScript and JScript. This is not fully implemented. The following methods always return E_NOTIMPL: DeleteMemberByName or DeleteMemberByDispID, GetMemberProperties, GetMemberName, GetNextDispID, and GetNameSpaceParent.
IMarshal | Can be used to get a FreeThreadedMarshaler for a free-threaded DOM document. This allows the free-threaded DOM document to be used in ASP shared Session and Application states for sharing XML documents across multiple clients in memory.
IObjectSafety | When the SetInterfaceSafetyOptions method is called with nonzero safety options, the Microsoft XML Parser (MSXML) will apply security rules before fetching XML data.
IObjectWithSite | Enables a host application to provide extra contextual information, such the base URL.
IOleCommandTarget | Used by an COM container to send an OLECMDID_STOP command to stop an asynchronous download.
IPersistMoniker | Provides control over how to bind the XML document to persistent data. Both synchronous and asynchronous loading are supported using BindToStorage on the given IMoniker. Save is not called; therefore the BindToStorage, IsDirty, and SaveCompleted methods return E_NOTIMPL.
IPersistStream | Used to save and load the XML document to and from an IStream.
IPersistStreamInit | Updated version of IPersistStream.
IProvideClassInfo | Provides an easy way to get ITypeInfo for the DOMDocument.
IStream | You can read and write directly to the document via the IStream that is returned. You cannot Seek during a Write operation, and the following methods are not implemented on this stream: SetSize, CopyTo, Commit, Revert, LockRegion, UnlockRegion, and Clone. This allows you to build an XML document efficiently by providing chunks of XML and calling Write on the stream. You can also use this to test the persistence of your current DOM document by calling "xmldoc1.save(xmldoc2)." The Save method uses this IStream interface.

When the object-creation methods (such as createElement) are used on the document, nodes are created in the context of the document (the ownerDocument property of the node points to the document), but the node is not part of the document tree. The node is only part of the document tree when it is explicitly added to the tree by calling insertBefore, replaceChild, or appendChild (or for attributes, setAttributeNode).

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

See Also

IXMLDOMNode Interface | XML DOM Persistence


abort Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::abort Method

Aborts an asynchronous download in progress.

Visual Basic Syntax

oXMLDOMDocument.abort

C/C++ Syntax

HRESULT abort(
    void
);

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

This method stops download and parsing and discards any portion of the XML tree already built. The IXMLDOMParseError interface indicates that the download was stopped.

If the readyState method has the value COMPLETED, no action is taken and the current document is unchanged.

This member is an extension of the W3C DOM.

See Also

IXMLDOMDocument Interface


async Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::async Method

IXMLDOMDocument::get_async Method

Indicates whether synchronous or asynchronous download is set. When set to True (the default setting), the load method returns control to the caller before the download is finished. You can then use the readyState property to check the status of the download. You can also attach an onreadystatechange handler or connect to the onreadystatechange event to be notified when the ready state changes so that you know when the download is complete.

Visual Basic Syntax

boolValue = oXMLDOMDocument.async

C/C++ Syntax

HRESULT get_async(
    VARIANT_BOOL *isAsync);

Parameters

isAsync [out]
True if asynchronous download is permitted; False if not.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if isAsync is Null.

Remarks

This member is an extension of the W3C DOM.

IXMLDOMDocument::put_async Method

Use to select synchronous or asynchronous download. When set to True (the default setting), the load method returns control to the caller before the download is finished. You can then use the readyState property to check the status of the download. You can also attach an onreadystatechange handler or connect to the onreadystatechange event to be notified when the ready state changes so that you know when the download is complete.

Visual Basic Syntax

objXMLDOMDocument.async = boolValue

C/C++ Syntax

HRESULT put_async(
    VARIANT_BOOL isAsync);

Parameters

isAsync [in]
True if asynchronous download is permitted; False if not.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

This member is an extension of the W3C DOM.

See Also

IXMLDOMDocument Interface


createAttribute Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::createAttribute Method

Creates a new attribute with the specified name.

Visual Basic Syntax

Set objXMLDOMAttribute = oXMLDOMDocument.createAttribute(name)

C/C++ Syntax

HRESULT createAttribute(
    BSTR name,
    IXMLDOMAttribute **attribute);

Parameters

name [in]
Name of the new attribute object. This name is subsequently available as the new node's nodeName property.
attribute [out]
Address of the new XMLDOMAttribute object.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if attribute is Null.
E_FAIL
Value returned if an error occurs.

Remarks

Creating an attribute with this method is the same as using createNode, where the type parameter is NODE_ATTRIBUTE and no namespace is specified.

You cannot create a namespace-qualified attribute using the createAttribute method. Regardless of whether a namespace prefix is included in name, the namespaceURI property for the new attribute is set to an empty string, "". An attribute constructed as part of an XML document load operation will never have both a prefix and an empty namespace URI. You can only create a namespace-qualified attribute using the IXMLDOMDocument interface's createNode method.

No data value is set for the attribute during the create operation. You can set the value by calling the element object's setAttribute method.

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null. To associate the attribute with an element, call the XMLDOMElement object's setAttribute method.

The nodeType property has the value NODE_ATTRIBUTE.

See Also

IXMLDOMDocument Interface


createCDATASection Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::createCDATASection Method

Creates a CDATA section node that contains the supplied data.

Visual Basic Syntax

Set objXMLDOMCDATASection = oXMLDOMDocument.createCDATASection(data)

C/C++ Syntax

HRESULT createCDATASection(
    BSTR data,
    IXMLDOMCDATASection **cdata);

Parameters

data [in]
Value to be supplied to the new CDATA section object's nodeValue property.
cdata [out]
Address of the new XMLDOMCDATASection object.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if cdata is Null.
E_FAIL
Value returned if an error occurs.

Remarks

Creating a CDATA section with this method is the same as using createNode, where the type parameter value is NODE_CDATA_SECTION and no namespace is specified. You cannot specify a namespace within the data parameter.

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null. You must explicitly call one of the node insert methods—insertBefore, replaceChild, or appendChild—to add the new object.

The new object's nodeType property has the value NODE_CDATA_SECTION.

See Also

IXMLDOMDocument Interface


createComment Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::createComment Method

Creates a comment node that contains the supplied data.

Visual Basic Syntax

Set objXMLDOMComment = oXMLDOMDocument.createComment(data)

C/C++ Syntax

HRESULT createComment(
    BSTR data,
    IXMLDOMComment **comment);

Parameters

data [in]
Value to be supplied to the new comment object's nodeValue property.
comment [out]
Address of the new XMLDOMComment object.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if comment is Null.
E_FAIL
Value returned if an error occurs.

Remarks

Creating a comment with this method is the same as using createNode, where the type parameter value is NODE_COMMENT and no namespace is specified. You cannot specify a namespace within the data parameter.

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null. You must explicitly call one of the node insert methods—insertBefore, replaceChild, or appendChild—to add the new object.

The new object's nodeType property has the value NODE_COMMENT.

See Also

IXMLDOMDocument Interface


createDocumentFragment Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::createDocumentFragment Method

Creates an empty DocumentFragment object.

Visual Basic Syntax

Set objXMLDOMDocumentFragment = oXMLDOMDocument.createDocumentFragment

C/C++ Syntax

HRESULT createDocumentFragment(
    IXMLDOMDocumentFragment **docFrag);

Parameters

docFrag [out]
Address of the empty XMLDOMDocumentFragment object.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if docFrag is Null.
E_FAIL
Value returned if an error occurs.

Remarks

Creating a document fragment with this method is the same as using createNode, where the type parameter value is NODE_DOCUMENT_FRAGMENT and no namespace is specified. You cannot specify a namespace with the createDocumentFragment method.

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null. You must explicitly call one of the node insert methods—insertBefore, replaceChild, or appendChild—to add the new object.

The nodeType property has the value NODE_DOCUMENT_FRAGMENT.

See Also

IXMLDOMDocument Interface


createElement Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::createElement Method

Creates an element node using the specified name.

Visual Basic Syntax

Set objXMLDOMElement = oXMLDOMDocument.createElement(tagName)

C/C++ Syntax

HRESULT createElement(
    BSTR tagName,
    IXMLDOMElement **element);

Parameters

tagName [in]
Name for the new element node. It is case sensitive. This name is subsequently available as the element node's nodeName property.
element [out]
Address of the IXMLDOMElement interface for the new element.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if element is Null.
E_FAIL
Value returned if an error occurs.

Remarks

Creating an element with this method is the same as using createNode, where the type parameter value is NODE_ELEMENT and no namespace is specified.

You cannot create a namespace-qualified element using the createElement method. Regardless of whether a namespace prefix is included in tagName, the namespaceURI property for the new element node is set to an empty string, "". An element node constructed as part of an XML document load operation will never have both a prefix and an empty namespace URI. You can only create a namespace-qualified element using the IXMLDOMDocument interface's createNode method.

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null. You must explicitly call one of the node insert methods—insertBefore, replaceChild, or appendChild—to add the new object.

The nodeType has the value NODE_ELEMENT.

See Also

IXMLDOMDocument Interface


createEntityReference Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::createEntityReference Method

Creates a new EntityReference object.

Visual Basic Syntax

Set objXMLDOMEntityReference = oXMLDOMDocument.createEntityReference(name)

C/C++ Syntax

HRESULT createEntityReference(
    BSTR name,
    IXMLDOMEntityReference **entityRef);

Parameters

name [in]
Name of the entity referenced. This name is subsequently available as the new object's nodeName property.
entityRef [out]
Address of the new XMLDOMEntityReference object.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if entityRef is Null.
E_FAIL
Value returned if an error occurs.

Remarks

Creating an entity reference with this method is the same as using createNode, where the type parameter value is NODE_ENTITY_REFERENCE and no namespace is specified. You cannot specify a namespace within the name parameter.

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null. You must explicitly call one of the node insert methods—insertBefore, replaceChild, or appendChild—to add the new object.

The nodeType has the value NODE_ENTITY_REFERENCE.

See Also

IXMLDOMDocument Interface


createNode Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::createNode Method

Creates a node using the supplied type, name, and namespace.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMDocument.createNode(type, name, namespaceURI)

C/C++ Syntax

HRESULT createNode(
    VARIANT Type,
    BSTR name,
    BSTR namespaceURI,
    IXMLDOMNode **node);

Parameters

type [in]
Value that uniquely identifies the node type. This can be specified using either the integer value or the string value. For the complete list of values, see the DOMNodeType enumeration or the description given for the name parameter.
name [in]
Value for the new node's nodeName property. The relationship between the node name and node type is summarized in the table below.
namespaceURI [in]
Namespace unique resource indicator (URI). If specified, the node is created in the context of the namespaceURI parameter with the prefix specified on the node name. If the name parameter does not have a prefix, this is treated as the default namespace.
node [out]
Newly created node.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if node is Null.
E_FAIL
Value returned if an error occurs.

Remarks

The name parameter depends on the value of the type parameter, as follows:

NODE_ATTRIBUTE | The name of the attribute.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_TEXT | The nodeName property for these node types is a constant value; the name parameter is ignored.
NODE_DOCUMENT_TYPE | The name of the document type, for example, the xxx in <!DOCTYPE xxx ...>.
NODE_ELEMENT | The name of the XML tag, with any namespace prefix included if present.
NODE_ENTITY | The name of the entity.
NODE_ENTITY_REFERENCE | The name of the entity referenced. Note that the name does not include the leading ampersand or the trailing semicolon. The name includes the namespace if one is present.
NODE_NOTATION | The name of the notation.
NODE_PROCESSING_INSTRUCTION | The target; the first token following the <? characters.

You cannot create a node of type NODE_DOCUMENT, NODE_DOCUMENT_TYPE, NODE_ENTITY, or NODE_NOTATION.

When a node is created, it is created in the context of a namespace if one is supplied (if the namespaceURI parameter is supplied). If one is not supplied, the node is created in the namespace of the document. If namespaceURI is specified, the node is created in the context of the namespaceURI parameter with the prefix specified on the node name.

For node types that don't have names, the empty string, "", should be passed as the name parameter.

For elements and entity references, when the namespaceURI parameter is anything other than "" and the name parameter does not contain a prefix (xxx in xxx:yyy), the namespaceURI is treated as the default namespace.

Attributes cannot be scoped to a default namespace, and other elements are not qualified to a particular namespace (they are treated as being from the namespace defined by the document itself).

When namespaceURI is the empty string, the node is created within the special namespace of the current document.

Creating a qualified node without specifying a nonempty namespaceURI returns an error. That is, if the name parameter contains a colon character, which indicates that the node is being created in the context of a namespace, you must also supply the namespace prefix in the namespaceURI parameter.

This member is an extension of the W3C DOM.

See Also

IXMLDOMDocument Interface


createProcessingInstruction Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::createProcessingInstruction Method

Creates a processing instruction node that contains the supplied target and data.

Visual Basic Syntax

Set objXMLDOMProcessingInstruction = oXMLDOMDocument.createProcessingInstruction(target, data)

C/C++ Syntax

HRESULT createProcessingInstruction(
    BSTR target,
    BSTR data,
    IXMLDOMProcessingInstruction **pi);

Parameters

target [in]
Target part of the processing instruction. It supplies the nodeName property of the new object.
data [in]
Remainder of the processing instruction preceding the closing ?> characters. It supplies the nodeValue property for the new object.
pi [out]
Address of the new XMLDOMProcessingInstruction object.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if pi is Null.
E_FAIL
Value returned if an error occurs.

Remarks

Creating a processing instruction with this method is the same as using createNode, where the type parameter value is NODE_PROCESSING_INSTRUCTION. You cannot specify a namespace with the createProcessingInstruction method.

To generate the processing instruction <?XML version="1.0"?>, for example, specify the target string "XML" and the data string "version="1.0"".

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null. You must explicitly call one of the node insert methods—insertBefore, replaceChild, or appendChild—to add the new object.

The new object's nodeType property has the value NODE_PROCESSING_INSTRUCTION.

See Also

IXMLDOMDocument Interface


createTextNode Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::createTextNode Method

Creates a text node that contains the supplied data.

Visual Basic Syntax

Set objXMLDOMText = oXMLDOMDocument.createTextNode(data)

C/C++ Syntax

HRESULT createTextNode(
    BSTR data,
    IXMLDOMText **text);

Parameters

data [in]
Value to be supplied to the new text object's nodeValue property.
text [out]
Address of the new XMLDOMText object.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if text is Null.
E_FAIL
Value returned if an error occurs.

Remarks

Creating a text node with this method is the same as using createNode, where the type parameter value is NODE_TEXT. You cannot specify a namespace with the createTextNode method.

Although this method creates the new object in the context of this document, it does not automatically add the new object to the document tree. In other words, although the ownerDocument property of the new node points to this document object, the parentNode property is set to Null. You must explicitly call one of the node insert methods—insertBefore, replaceChild, or appendChild—to add the new object.

The new object's nodeType property has the value NODE_TEXT.

See Also

IXMLDOMDocument Interface


doctype Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::doctype Method

IXMLDOMDocument::get_doctype Method

Contains the document type node that specifies the DTD for this document.

Visual Basic Syntax

Set objXMLDOMDocumentType = oXMLDOMDocument.doctype

C/C++ Syntax

HRESULT get_doctype(
    IXMLDOMDocumentType **documentType);

Parameters

documentType [out]
For XML, the address of the node of type NODE_DOCUMENT_TYPE that specifies the DTD. Returns Null for XML documents without a DTD and for HTML documents.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when there is no doctype node.
E_INVALIDARG
Value returned if documentType is Null.

Remarks

An XML document can contain a document type declaration before the first element in the document. It starts with the tag <!DOCTYPE> and can specify an external DTD.

See Also

IXMLDOMDocument Interface | IXMLDOMDocumentType Interface


documentElement Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::documentElement Method

IXMLDOMDocument::get_documentElement Method

Returns the root element of the document.

Visual Basic Syntax

Set objXMLDOMElement = oXMLDOMDocument.documentElement

C/C++ Syntax

HRESULT get_documentElement(
    IXMLDOMElement** DOMElement);

Parameters

DOMElement [out]
DOMElement object that represents the single element representing the root of the XML document tree. Returns Null if no root exists.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if there is no document element.
E_INVALIDARG
Value returned if DOMElement is Null.

IXMLDOMDocument::putref_documentElement Method

Sets the root element of the document.

Visual Basic Syntax

Set objXMLDOMDocument.documentElement = objXMLDOMElement

C/C++ Syntax

HRESULT putref_documentElement(
    IXMLDOMElement* DOMElement);

Parameters

DOMElement [in]
XMLDOMElement object that represents the single element that represents the root of the XML document tree. Returns Null if no root exists.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

When setting the documentElement property, the specified element node is inserted into the child list of the document after any document type node. To precisely place the node within the children of the document, call the XMLDOMNode object's insertBefore method.

The parentNode property is reset to the document node as a result of this operation.

See Also

IXMLDOMDocument Interface


getElementsByTagName Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::getElementsByTagName Method

Returns a collection of elements that have the specified name.

Visual Basic Syntax

Set objXMLDOMNodeList = oXMLDOMDocument.XMLDOMDocument(tagName)

C/C++ Syntax

HRESULT getElementsByTagName(
    BSTR tagName,
    IXMLDOMNodeList **resultList);

Parameters

tagName [in]
Element name to find. The tagname "*" returns all elements in the document.
resultList [out]
Address of a collection of elements that match the specified name.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

The elements in the collection are returned in the order in which they would be encountered in a preorder traversal of the document tree. In a preorder traversal, the parent root node is visited first, and then each child node from left to right is traversed.

The returned DOMNodeList object is live and immediately reflects changes to the nodes that appear in the list.

More complex searches can be performed using the selectNodes method, which may also be faster in some cases.

C/C++ Example

IXMLDOMDocument *pIXMLDOMDocument = NULL;
wstring strFindText (_T("AUTHOR"));
IXMLDOMNodeList *pIDOMNodeList = NULL;
IXMLDOMNode *pIDOMNode = NULL;
long value;
BSTR bstrItemText;
HRESULT hr;

try
{
   // Initialize pIXMLDOMDocument (create a DOMDocument).
   // Load document.
   hr = pIXMLDOMDocument->getElementsByTagName(
            (TCHAR*)strFindText.data(), &pIDOMNodeList);
   SUCCEEDED(hr) ? 0 : throw hr;

   hr = pIDOMNodeList->get_length(&value);
   if(SUCCEEDED(hr))
   {
      pIDOMNodeList->reset();
      for(int ii = 0; ii < value; ii++)
      {
         pIDOMNodeList->get_item(ii, &pIDOMNode);
         if(pIDOMNode )
         {
            pIDOMNode->get_text(&bstrItemText);
            ::MessageBox(NULL, bstrItemText,strFindText.data(), MB_OK);
            pIDOMNode->Release();
            pIDOMNode = NULL;
         }
      }
   }
   pIDOMNodeList->Release();
   pIDOMNodeList = NULL;
}
catch(...)
{
   if(pIDOMNodeList)
      pIDOMNodeList->Release();
   if(pIDOMNode)
      pIDOMNode->Release();
   DisplayErrorToUser();
}
// Release pIXMLDOMDocument when finished with it.

See Also

IXMLDOMDocument Interface


implementation Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::implementation Method

IXMLDOMDocument::get_implementation Method

Contains a pointer to the XMLDOMImplementation object for this document.

Visual Basic Syntax

Set objXMLDOMImplementation = oXMLDOMDocument.implementation

C/C++ Syntax

HRESULT get_implementation(
    IXMLDOMImplementation **impl);

Parameters

impl [out]
XMLDOMImplementation object for this document.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if impl is Null.

Remarks

A DOM application can use objects from multiple implementations. This provides access to the XMLDOMImplementation object that handles this document.

Example

IXMLDOMDocument * pIXMLDOMDocument = NULL;
IXMLDOMImplementation *pIXMLDOMImplementation = NULL;
VARIANT_BOOL varbFlag ;
BSTR bstrOutput = NULL;
BSTR bstrFeature = ::SysAllocString(_T("MS-DOM"));

try
{
   // Initialize pIXMLDOMDocument (create a DOMDocument).
   // Load document.
   hr = pIXMLDOMDocument->get_implementation (&pIXMLDOMImplementation);

   if(SUCCEEDED(hr) && pIXMLDOMImplementation)
   {
      pIXMLDOMImplementation->hasFeature(bstrFeature, _T("1.0"),
                  &varbFlag);
      if(varbFlag == VARIANT_TRUE )
         bstrOutput = ::SysAllocString(_T("Feature Supported"));
      else
         bstrOutput = ::SysAllocString(_T("Feature not Supported"));

      ::MessageBox(NULL, bstrOutput, bstrFeature, MB_OK);
      pIXMLDOMImplementation->Release();
      pIXMLDOMImplementation = NULL;
      ::SysFreeString(bstrOutput);
      bstrOutput = NULL;
   }
   ::SysFreeString(bstrFeature);
   bstrFeature = NULL;
}
catch(...)
{
   if(pIXMLDOMImplementation)
      pIXMLDOMImplementation->Release();
   if(bstrOutput)
      ::SysFreeString(bstrOutput);
   if(bstrFeature)
      ::SysFreeString(bstrFeature);
   DisplayErrorToUser();
}
// Release pIXMLDOMDocument when finished with it.

See Also

IXMLDOMDocument Interface


load Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::load Method

Loads an XML document from the specified location.

Visual Basic Syntax

boolValue = oXMLDOMDocument.load(xmlSource)

C/C++ Syntax

HRESULT load(
    VARIANT xmlSource,
    VARIANT_BOOL *isSuccessful);

Parameters

xmlSource [in]
Indicator of the object that specifies the source for the XML document. The object can represent a URL, a Request object, an IStream, a SAFEARRAY, or a DOMDocument object. For details, see the Remarks section below.
isSuccessful [out]
True if the load succeeded; False if the load failed.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if the load fails.
E_INVALIDARG
Value returned if isSuccessful is Null.

Remarks

The load behavior differs based on the object specified by the xmlSource parameter.

Object | Description
string | Specifies the URL to be loaded. This URL must meet basic security constraints to be accessed. If the URL cannot be resolved or accessed or does not reference an XML document, this method returns an error and sets the IXMLDOMDocument interface's documentElement property to Null.
ASP Request object | Reads and parses the data from the Request object.

Calling load or loadXML on an existing document immediately discards the content of the document.

This member is an extension of the W3C DOM.

See Also

IXMLDOMDocument Interface | XML DOM Persistence


loadXML Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::loadXML Method

Loads an XML document using the supplied string.

Visual Basic Syntax

boolValue = oXMLDOMDocument.loadXML(bstrXML)

C/C++ Syntax

HRESULT loadXML(
    BSTR xmlString,
    VARIANT_BOOL *isSuccessful);

Parameters

xmlString [in]
XML string to load into this XML document object. This string can contain an entire XML document or a well-formed fragment.
isSuccessful [out]
True if the XML load succeeded. This method returns False and sets the DOMDocument object's documentElement property to Null if the XML load failed.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if the load fails.
E_INVALIDARG
Value returned if isSuccessful is Null.

Remarks

Calling load or loadXML on an existing document immediately discards the content of the document.

This member is an extension of the W3C DOM.

See Also

IXMLDOMDocument Interface | XML DOM Persistence


nodeFromID Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::nodeFromID Method

Returns the node whose ID attribute matches the supplied value.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMDocument.nodeFromID(idString)

C/C++ Syntax

HRESULT nodeFromID(
    BSTR idString,
    IXMLDOMNode **node);

Parameters

idString [in]
Value of the ID to match.
node [out]
Node that matches the supplied ID. If no nodes match, this method returns Null.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when there is no node with the given ID.
E_INVALIDARG
Value returned if node is Null.

Remarks

According to the XML 1.0 Recommendation (REC-xml-19980210), ID attribute values must be unique within their XML documents, and no element can specify more than one ID attribute.

This method was designed to handle ID/IDREF relationships in XML but does not require an attribute of type IDREF. It can be used generically in a way similar to the all collection in DHTML.

To reference a node with nodefromID, the node must by typed as ID in the schema or DTD—simply naming an attribute "ID" doesn't set its data type.

This member is an extension of the W3C DOM.

See Also

IXMLDOMDocument Interface


ondataavailable Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::ondataavailable Method

IXMLDOMDocument::put_ondataavailable Method

Specifies the event handler for the ondataavailable event.

Visual Basic Syntax

oXMLDOMDocument.ondataavailable = funcMyEventHandler

C/C++ Syntax

HRESULT put_ondataavailable(
    VARIANT ondataavailableSink);

Parameters

ondataavailableSink [in]
Name of the function that should be called when the readyState property value changes to the value INTERACTIVE, indicating that data is available.

C/C++ Return Value

Returns S_OK if successful, or an error code otherwise.

Remarks

When handling asynchronous data, it is useful to be able to start processing in parallel with the download as soon as data becomes available. The ondataavailable event fires as soon as data is available for such processing. The readyState property defines several states that more closely specify the current status of the asynchronous download.

You can call the DOMDocument object's QueryInterface method to obtain the IConnectionPointContainer interface, which is used in event management. The DISPID for this event is DISPID_XMLDOMEVENT_ONDATAAVAILABLE.

This member is an extension of the W3C DOM.

See Also

IXMLDOMDocument Interface


ondataavailable Event

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::ondataavailable Event

Provides the event handler for the ondataavailable event.

C/C++Syntax

HRESULT ondataavailable(void);

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

When handling asynchronous data, it is useful to be able to start processing in parallel with the download as soon as data becomes available. The ondataavailable event fires as soon as data is available for such processing. The readyState method defines several states that more closely specify the current status of the asynchronous download.

This member is an extension of the W3C DOM.

C/C++ Example

// Assumptions :
// The sink (an object of the class defined below) has been connected
// to the connection point (DIID_XMLDOMDocumentEvents).
// To see an example of how to connect the connection point,
// see the example for the IXMLDOMDocument::onreadystatechange Event.

BEGIN_DISPATCH_MAP(CXMLDOMSamples, CCmdTarget)
   DISP_FUNCTION_ID(CXMLDOMSamples, "Data Available Event Handler",
   DISPID_XMLDOMEVENT_ONDATAAVAILABLE, DOMDocOnDataAvailable, VT_EMPTY, 0)
END_DISPATCH_MAP()

class CXMLDOMSamples : public CCmdTarget
{
public:
   void DOMDocOnDataAvailable();
};
void CXMLDOMSamples::DOMDocOnDataAvailable()
{
   // Called whenever any data is available.
   try
   {
       ::MessageBox(NULL, _T("in ondataavailable()"), _T("Error"), MB_OK);
   }
   catch(...)
   {
      DisplayErrorToUser();
   }
}

See Also

IXMLDOMDocument Interface


onreadystatechange Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::onreadystatechange Method

IXMLDOMDocument::put_onreadystatechange Method

Specifies the event handler to be invoked when the readyState property changes.

Visual Basic Syntax

oXMLDOMDocument.onreadystatechange = funcMyEventHandler

C/C++ Syntax

HRESULT put_onreadystatechange(
    VARIANT readystatechangeSink);

Parameters

readystatechangeSink [in]
Name of the function that should be called when the readyState property value changes.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

You can call the DOMDocument object's QueryInterface method to obtain the IConnectionPointContainer interface, which is used in event management. The DISPID for this event is DISPID_XMLDOMEVENT_ONREADYSTATECHANGE.

This member is an extension of the W3C DOM.

Example

See the IXMLDOMDocument::onreadystatechange Event example to learn how to do this with connection points.

See Also

IXMLDOMDocument Interface


onreadystatechange Event

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::onreadystatechange Event

Runs when the readyState method changes.

C/C++ Syntax

HRESULT onreadystatechange(void);

C/C++ Return Value

Returns S_OK if successful, or an error code otherwise.

Remarks

The event handler can be set by explicitly setting the onreadystatechange method.

This member is an extension of the W3C DOM.

Visual Basic Example

The following Visual Basic example shows how to use the onreadystatechange event in an application:

Dim WithEvents xmldoc As DOMDocument

Private Sub Form_Load()
    Set xmldoc = New DOMDocument
    xmldoc.Load ("http://xmlweb/msxml/jscript/error.xml")
End Sub

Private Sub xmldoc_onreadystatechange()
    If (xmldoc.readyState = 4) Then
       msg = "Load is finished" & Chr(13)
       If (xmldoc.parseError.errorCode <> 0) Then
            msg = msg + "Error: " & xmldoc.parseError.reason
       End If
       MsgBox msg
    End If
End Sub

C/C++ Example

BEGIN_DISPATCH_MAP(CXMLDOMSamples, CCmdTarget)
   DISP_FUNCTION_ID(CXMLDOMSamples, "Ready State Event Handler",
   DISPID_XMLDOMEVENT_ONREADYSTATECHANGE, DOMDocCheckState, VT_EMPTY, 0)
END_DISPATCH_MAP()

class CXMLDOMSamples : public CCmdTarget
{
private:
   HRESULT m_hr;
   IXMLDOMDocument* m_pIXMLDOMDocument;
   BSTR m_bstrXMLDocName;
   DWORD m_pdwCookie;
public:
   BOOL AdviseConnectionPoint(BOOL bAdvise = TRUE);
   void Caller();
   BOOL DOMDocLoadLocation();
   BOOL DOMDocStateChangeSetup();
   void DOMDocCheckState();
};

// Assumptions
// m_pIXMLDOMDocument has already been initialized (a DOMDocument
// object has been created).
// Automation is enabled for the class to enable it to connect
// to the Connection point (DIID_XMLDOMDocumentEvents).

void CXMLDOMSamples::Caller()
{
   try
   {
   m_bstrXMLDocName = ::SysAllocString(_T("http://MyServer/sample.xml"));
   DOMDocStateChangeSetup();
   DOMDocLoadLocation();
   ::SysFreeString(m_bstrXMLDocName);
   m_bstrXMLDocName = NULL;
}
   catch(...)
   {
      if(m_bstrXMLDocName)
      ::SysFreeString(m_bstrXMLDocName);
      DisplayErrorToUser();
   }
}
BOOL CXMLDOMSamples::AdviseConnectionPoint(BOOL bAdvise)
{
   BOOL bResult = FALSE;
   try
   {
      IConnectionPointContainer *pIConnectionPointContainer = NULL;
      IConnectionPoint *pCP = NULL;

      m_hr = m_pIXMLDOMDocument->QueryInterface
      (IID_IConnectionPointContainer,(void**)&pIConnectionPointContainer);
      SUCCEEDED(m_hr) ? 0 : throw m_hr;

      If(pIConnectionPointContainer)
      {
         m_hr = pIConnectionPointContainer->FindConnectionPoint
               (DIID_XMLDOMDocumentEvents, &pCP);
         SUCCEEDED(m_hr) ? 0 : throw m_hr;
         if(SUCCEEDED(m_hr) && pCP)
         {
            if(bAdvise)
               m_hr = pCP->Advise(GetIDispatch(TRUE), &m_pdwCookie);
            else
               m_hr = pCP->Unadvise(m_pdwCookie);
            bResult = SUCCEEDED(m_hr) ? TRUE : FALSE;
            pCP->Release();
            pCP = NULL;
         }
         pIConnectionPointContainer->Release();
         pIConnectionPointContainer = NULL;
      }
   }
   catch(...)
   {
      if(pIConnectionPointContainer)
         pIConnectionPointContainer->Release();
      if(pCP)
         pCP->Release();
      DisplayErrorToUser();
   }
   return bResult;
}
BOOL CXMLDOMSamples::DOMDocStateChangeSetup()
{
   BOOL bResult = FALSE;
   try
   {
      m_hr = m_pIXMLDOMDocument->put_async(VARIANT_TRUE);
      SUCCEEDED(m_hr) ? 0 : throw m_hr;
      bResult = AdviseConnectionPoint();
   }
   catch(...)
   {
      DisplayErrorToUser();
   }
   return bResult;
}
BOOL CXMLDOMSamples::DOMDocLoadLocation()
{
   short sResult = FALSE;
   BOOL bResult = FALSE;
   try
   {
      _variant_t varString = m_bstrXMLDocName;
      m_hr = m_pIXMLDOMDocument->load(varString, &sResult);
      bResult = SUCCEEDED(m_hr) ? TRUE : FALSE;
   }
   catch(...)
   {
      DisplayErrorToUser();
   }
   return bResult;
}
void CXMLDOMSamples::DOMDocCheckState()
{
   IXMLDOMParseError *pIParseError = NULL;
   long value;

   try
   {
      m_hr = m_pIXMLDOMDocument->get_readyState(&value);
      SUCCEEDED(m_hr) ? 0 : throw m_hr;
      if(value == 4 )
      {
         m_hr = m_pIXMLDOMDocument->get_parseError(&pIParseError);
         SUCCEEDED(m_hr) ? 0 : throw m_hr;
         m_hr = pIParseError->get_errorCode(&value);
         pIParseError->Release();
         pIParseError=NULL;
         if(!SUCCEEDED(m_hr))
         {
            throw m_hr;
         }
         m_strOutput = (value != 0) ? _T("Load Error") : _T("Load
            Succeeded");
      }
   }
   catch(...)
   {
      if(pIParseError)
         pIParseError->Release();
      DisplayErrorToUser();
   }
}

See Also

IXMLDOMDocument Interface


ontransformnode Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::ontransformnode Method

IXMLDOMDocument::put_ontransformnode Method

Specifies the event handler for the ontransformnode event.

Visual Basic Syntax

oXMLDOMDocument.ontransformnode = funcMyEventHandler

C/C++ Syntax

HRESULT put_ontransformnode(
    VARIANT ontransformnodeSink);

Parameters

ontransformnodeSink [in]
Name of the function that should be called when a transformation is about to be performed.

Remarks

A transformation involves two trees: the XML source and the style sheet that is applied to it. The ontransformnode event fires before each node in the style sheet is applied to each node in the XML source.

The ontransformnode property provides a way for developers to specify their own event handler that overrides the default handler.

See Also

IXMLDOMDocument Interface | transformNode Method


ontransformnode Event

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::ontransformnode Event

Fires before each node in the style sheet is applied to each node in the XML source.

C/C++Syntax

HRESULT ontransformnode(
    IXMLDOMNode *pCode,
    IXMLDOMNode *pData,
    VARIANT vBool);

Parameters

pCode [in]
Address of the current node in the XSL style sheet.
pData [in]
Address of the current node in the XML source data.
vBool [out]
Boolean return value. Returns True if transformation processing is to continue; returns False to stop transformation processing.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

The event handler can be set by explicitly setting the ontransformnode property.

This member is an extension of the W3C DOM.

See Also

IXMLDOMDocument Interface


parseError Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::parseError Method

IXMLDOMDocument::get_parseError Method

Returns an XMLDOMParseError object that contains information about the last parsing error.

Visual Basic Syntax

Set objError = objXMLDOMDocument.parseError

C/C++ Syntax

HRESULT get_parseError(
    IXMLDOMParseError **errorObj);

Parameters

errorObj [out]
DOMParseError object. The errorObj parameter is always a valid object.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if errorObj is Null.

Example

IXMLDOMParseError *pIParseError = NULL;
HRESULT hr;
IXMLDOMDocument *pIXMLDOMDocument = NULL;
wstring strOutput = _T("Load Error");

   // Initialize pIXMLDOMDocument (create a DOMDocument).
   // Load document.
try
{
   hr = pIXMLDOMDocument->get_parseError(&pIParseError);
   SUCCEEDED(hr) ? 0 : throw hr;

   if(pIParseError )
   {
      hr = pIParseError->get_errorCode(&value);
      if(SUCCEEDED(hr))
      {
         strOutput = (value != 0) ? _T("Load Error") : _T("Load
      Succeeded");
      }
      pIParseError->Release();
      pIParseError = NULL;
   }
}
catch(...)
{
   if(pIParseError)
      pIParseError->Release();
   DisplayErrorToUser();
}
// Release the IXMLDOMDocument interface when finished with it.

See Also

IXMLDOMDocument Interface


preserveWhiteSpace Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::preserveWhiteSpace Method

IXMLDOMDocument::get_preserveWhiteSpace Method

Indicates whether to preserve all white space in the XML document.

Visual Basic Syntax

boolValue = oXMLDOMDocument.preserveWhiteSpace

C/C++ Syntax

HRESULT get_preserveWhiteSpace(
    VARIANT_BOOL *isPreserving);

Parameters

isPreserving [out]
Value that indicates whether default processing preserves white space.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if isPreserving is Null.

IXMLDOMDocument::put_preserveWhiteSpace Method

Sets the value of the preserveWhiteSpace property.

Visual Basic Syntax

objXMLDOMDocument.preserveWhiteSpace = boolValue

C/C++ Syntax

HRESULT put_preserveWhiteSpace(
    VARIANT_BOOL isPreserving);

Parameters

isPreserving [in]
Value that indicates whether default processing preserves white space.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

This property is initialized to False.

The preserveWhiteSpace property specifies the default white space handling when the xml:space attribute is set to "default." When preserveWhiteSpace is True, all white space is preserved, regardless of the xml:space settings in the document. When preserveWhiteSpace is False, the values of the xml:space attribute specified in the document determine whether white space is preserved or not.

The xml property does not preserve white space exactly as it appears in the original document. Instead, the object model replaces white space present in the original document with a single newline character in the representation returned by the xml property. Similarly, the text property contains a representation without the leading and trailing spaces and replaces multiple intervening white space characters between words with a single space character.

The text and xml properties preserve white space when the user has set the preserveWhiteSpace property to True and when xml:space on the XML element has the value "preserve".

See Also

IXMLDOMDocument Interface | Handling White Space


readyState Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::readyState Method

IXMLDOMDocument::get_readyState Method

Indicates the current state of the XML document.

Visual Basic Syntax

lValue = oXMLDOMDocument.readyState

C/C++ Syntax

HRESULT get_readyState(
    long *value);

Parameters

value [out]
Value that indicates the instantiation and download state of the XML document object. It can be one of the following:
LOADING (1) | The load is in progress—reading persisted properties but not yet parsing data. For readyState definitions, data should be considered equivalent to BLOB (binary large object) properties.
LOADED (2) | Reading of the persisted properties completed—reading and parsing data, but the object model is not yet available.
INTERACTIVE (3) | Some data has been read and parsed, and the object model is now available on the partially retrieved data set. Although the object model is available during this state, it is read-only.
COMPLETED (4) | The document has been completely loaded, successfully or unsuccessfully.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if value is Null.

Example

See the IXMLDOMDocument::onreadystatechange Event example.

See Also

IXMLDOMDocument Interface


resolveExternals Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::resolveExternals Method

IXMLDOMDocument::get_resolveExternals Method

Indicates that external definitions (resolvable namespaces, DTD external subsets, and external entity references) are to be resolved at parse time, independent of validation.

Visual Basic Syntax

boolValue = oXMLDOMDocument.resolveExternals

C/C++ Syntax

HRESULT get_resolveExternals(
    VARIANT_BOOL *isResolving);

Parameters

isResolving [out]
True if external definitions are to be resolved at parse time; False otherwise. The default is True.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if isResolving is Null.

IXMLDOMDocument::put_resolveExternals Method

Indicates that external definitions (resolvable namespaces, DTD external subsets, and external entity references) are to be resolved at parse time.

Visual Basic Syntax

objXMLDOMDocument.resolveExternals = boolValue

C/C++ Syntax

HRESULT put_resolveExternals(
    VARIANT_BOOL isResolving);

Parameters

isResolving [in]
True if external definitions are to be resolved at parse time; False otherwise. The default is True.

Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

When isResolving is True, external definitions are resolved at parse time. This allows default attributes and data types to be defined on elements from the schema and allows use of the DTD as a file inclusion mechanism.

This setting is independent of whether validation is to be performed, as indicated by the value of the validateOnParse property. If externals cannot be resolved during validation, a validation error occurs. When the value of isResolving is False, externals are not resolved and validation is not performed.

A resolvable namespace is indicated by a namespace URI that begins with "x-schema:" as its prefix.

See Also

IXMLDOMDocument Interface


save Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::save Method

Saves an XML document to the specified location.

Visual Basic Syntax

oXMLDOMDocument.save(destination)

C/C++ Syntax

HRESULT save(
    VARIANT varObject);

Parameters

varObject [in]
Type of object to save. This object can represent a file name, an ASP Response object, an XML document object, or a custom object that supports persistence. For more information, see the Remarks section below.

C/C++ Return Values

S_OK
Value returned if successful.
XML_BAD_ENCODING
The document contains a character that does not belong in the specified encoding. The character must use a numeric entity reference. For example, the Japanese Unicode character 20013 does not fit into the encoding windows-1250 (the Central European alphabet) and therefore must be represented in markup as the numeric entity reference &#20013; or &#x4E2D;. This version of save does not automatically convert characters to the numeric entity references.
E_INVALIDARG
A string was provided but it is not a valid file name.
E_ACCESSDENIED
Save operation is not permitted.
E_OUTOFMEMORY
Save needs to allocate buffers.
(Other values)
Any other file system error can be returned in the save(string) case.

Remarks

The behavior of the save method differs based on the object specified by the varObject parameter.

Object | Description
String Specifies the file name. Note that this must be a file name rather than a URL. The file is created if necessary and the contents are entirely replaced with the contents of the saved document. This mode is not intended for use from a secure client such as Internet Explorer because the method always returns E_ACCESSDENIED and cannot be enabled through user security settings. For example:
dim xmldoc
set xmldoc = Server.CreateObject("Msxml2.DOMDocument")
xmldoc.load(Request)

xmldoc.save(Server.MapPath("sample.xml"))
ASP Response object Sends the document back to the client that invoked the ASP script. For example:
dim xmldoc
set xmldoc = Server.CreateObject("Msxml2.DOMDocument")
xmldoc.load(Server.MapPath("sample.xml"))

xmldoc.save(Response)
XML document object Duplicates the original document. It is the equivalent of saving the document and reparsing it. The document goes through full persistence through XML markup, thereby testing the persistability of your XML document. For example:
<script language="jscript">
    var xmldoc1 = CreateObject("Msxml2.DOMDocument");
    var xmldoc2 = CreateObject("Msxml2.DOMDocument");
    xmldoc1.load("sample.xml");
    xmldoc1.save(xmldoc2.XMLDocument);

</script>
Custom object supporting persistence | Any other custom COM object that supports QueryInterface for IStream, IPersistStream, or IPersistStreamInit can also be provided here, and the document will be saved accordingly. In the IStream case, the IStream::Write method will be called as it saves the document; in the IPersistStream case, IPersistStream::Load will be called with an IStream that supports the Read, Seek, and Stat methods.

External entity references in DOCTYPE, ENTITY, NOTATION, and xml namespace declarations are not changed; they point to the original document. A saved XML document might not load if the URLs are not accessible from the location in which you saved the document.

Character encoding is based on the encoding attribute in the xml declaration, such as <?xml version="1.0" encoding="windows-1252"?>. When no encoding attribute is specified, the default setting is UTF-8.

Validation is not performed during save, which can result in an invalid document that does not load again because of a specified DTD.

When manipulating a document, it is possible to create a document that lacks the appropriate namespace declarations to be reparsed or to have declarations that would cause some prefixed names to be interpreted incorrectly. When saving an XML representation of DOM nodes, the Microsoft XML parser (MSXML) attempts to insert appropriate namespace declarations to preserve the original namespace URI for each element and its attributes.

C/C++ Example

BOOL DOMDocSaveLocation()
{
   BOOL bResult = FALSE;
   IXMLDOMDocument *pIXMLDOMDocument = NULL;
   HRESULT hr;

   try
   {
      _variant_t varString = _T("D:\\sample.xml");
      // Initialize pIXMLDOMDocument (create a DOMDocument).
      // Load document.
      hr = pIXMLDOMDocument->save(varString);
      if(SUCCEEDED(hr))
         bResult = TRUE;
   }
   catch(...)
   {
      DisplayErrorToUser();
   // Release the IXMLDOMDocument interface.
   }
   // Release the IXMLDOMDocument interface when finished with it.
   return bResult;
}

See Also

IXMLDOMDocument Interface | XML DOM Persistence


url Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::url Method

IXMLDOMDocument::get_url Method

Returns the canonicalized URL for the last loaded XML document.

Visual Basic Syntax

strValue = oXMLDOMDocument.url

C/C++ Syntax

HRESULT get_url(
    BSTR *urlString);

Parameters

urlString [out]
URL from the last successful load. If the document is being built in memory, this property returns Null.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if there is no URL.
E_INVALIDARG
Value returned if urlString is Null.

Remarks

The url property is not updated after saving a document with the save method. To update the url property in this case, reload the document using the load method.

This member is an extension of the W3C DOM.

See Also

IXMLDOMDocument Interface


validateOnParse Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument::validateOnParse Method

IXMLDOMDocument::get_validateOnParse Method

Indicates whether the parser should validate this document.

Visual Basic Syntax

boolValue = oXMLDOMDocument.validateOnParse

C/C++ Syntax

HRESULT get_validateOnParse(
    VARIANT_BOOL *isValidating);

Parameters

isValidating [out]
If True, validates during parsing. If False, parses only for well-formed XML.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if isValidating is Null.

IXMLDOMDocument::put_validateOnParse Method

Indicates whether the parser should validate this document.

Visual Basic Syntax

objXMLDOMDocument.validateOnParse = boolValue

C/C++ Syntax

HRESULT put_validateOnParse(
    VARIANT_BOOL isValidating);

Parameters

isValidating [in]
If True, validates during parsing. If False, parses only for well-formed XML.

C/C++ Return Value

Returns S_OK if successful, or an error code otherwise.

See Also

IXMLDOMDocument Interface


IXMLDOMDocument2 Interface

An extension of IXMLDOMDocument that supports schema caching and validation features as well as a way to switch on XPath support.

Methods

getProperty | Gets the default values or values set by setProperty.
namespaces | Returns the list of namespaces used in the document as an XMLSchemaCache object.
schemas | Identifies the XMLSchemaCache object to use during load.
selectNodes | Changes select nodes to return an object that supports the IXMLDOMSelection interface via QueryInterface.
setProperty | Generic mechanism for setting properties.
validate | Performs run-time validation on the currently loaded XML document.

Remarks

The following CLSIDs support IID_XMLDOMDocument2 in CoCreateInstance. The following ProgROGIDs return an object that can be cast into an IXMLDOMDocument2 interface in Visual Basic. IXMLDOMDocument2 methods and properties are transparently available in late-bound scripting languages that go through IDispatch.

The same is true for DOMDocument objects exposed via the IXMLHttpTTPRequest::get_responseXML property and the DOMXMLDocument and XSLDocument properties in the XML Mime Viewer.

CLSID | ProgID
CLSID_DOMDocument | MicrosoftMSXML2.XMLDOMDocument
CLSID_DOMFreeThreadedDocument | MicrosoftMSXML2.FreeThreadedXMLDOMDocument

Requirements

Implementation: Msxml2.dll

Header and IDL files: Msxml.h, Xmldom.idl

Version-Independent ProgID: Msxml2.DOMDocument, Msxml2.FreeThreadedDOMDocument

Version-Independent CLSID: f6d90f11-9c73-11d3-b32e-00c04f990bb4, f6d90f12-9c73-11d3-b32e-00c04f990bb4 (free-threaded)

Version-Dependent ProgID: Msxml2.DOMDocument.2.6, Msxml2FreeThreadedDOMDocument.2.6

Version-Dependent CSLID: f5078f1b-c551-11d3-89b9-0000f81fe221, f5078f1c-c551-11d3-89b9-0000f81fe221 (free-threaded)

See Also

IXMLDOMDocument Interface


getProperty Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument2::getProperty Method

Gets the default values or values set by setProperty.

Visual Basic Syntax

strPropValue = objXMLDOMDocument2.getProperty(name)

C/C++ Syntax

HRESULT getProperty(BSTR name, VARIANT* value);

Parameters

name [in]
String name of the property. This name is case-sensitive.
value [out]
Variant return value of the requested flag.

C/C++ Return Values

S_OK
Value returned if successful.
E_FAIL
Value returned if property name is invalid.

See Also

IXMLDOMDocument2 Interface


namespaces Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument2::namespaces Method

Returns the list of namespaces used in the document as an XMLSchemaCollection object.

Visual Basic Syntax

Set objXMLSchemaCache = objXMLDOMDocument2.namespaces

C/C++ Syntax

HRESULT get_namespaces(IXMLDOMSchemaCollection** ppSchemaCollection);

Parameters

ppSchemaCollection [out]
The collection of all namespaces for this document.

C/C++ Return Values

S_OK
Value returned if the method executes successfully, even if there are no schemas.
E_POINTER
Value returned if ppSchemaCollection = Null.

Note Never returns Null. If there are no schemas, it returns an empty collection object.

Remarks

Returns the list of namespaces used in the document as an XMLSchemaCollection object. There is one item in this list for each unique namespace declaration in the document. The schema collection returned may have an associated schema document for each namespace listed. This list is read-only. Any schema documents returned in this collection are also read-only.

The order of the namespaces returned does not necessarily match the order in which they appear in the document. The collection returned will change as new namespaces are added or removed via the DOM. In this sense it is a "live" list like IXMLDOMNodeList.

This collection is completely reset each time you call load or loadXML.

C/C++ Example

BOOL DOMDocument2nameSpaces()
{
   BOOL bResult = FALSE;
   short sResult = FALSE;
   IXMLDOMElement *pIXMLDOMElement=NULL;
   IXMLDOMSchemaCollection *pIXMLDOMSchemaCollection=NULL;
   IXMLDOMDocument2 *pIXMLDOMDocument2=NULL;
   HRESULT hr;
   BSTR bstrValue;

   try
   {
      hr=CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_SERVER,
                  IID_IXMLDOMDocument2, (LPVOID*)(&pIXMLDOMDocument2));
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMDocument2)
      {
         hr=pIXMLDOMDocument2->put_async(VARIANT_FALSE);
         if(SUCCEEDED(hr))
         {
            hr=pIXMLDOMDocument2->load(_variant_t(
               _T("d:\\inetpub\\wwwroot\\samplexmldtd.xml")), &sResult);
            if(SUCCEEDED(hr) && (sResult==VARIANT_TRUE))
            {
               hr=pIXMLDOMDocument2->get_namespaces(
                           &pIXMLDOMSchemaCollection);
               if(SUCCEEDED(hr))
               {
                  LONG uLength;

                  bResult=TRUE;
                  hr=pIXMLDOMSchemaCollection->get_length(&uLength);
                  if(SUCCEEDED(hr))
                  {
                     for(int iIndex=0; iIndex < uLength; iIndex++)
                     {
                        hr=pIXMLDOMSchemaCollection->get_namespaceURI(
                           iIndex, &bstrValue);
                        if(SUCCEEDED(hr))
                           ::MessageBox(NULL, bstrValue, _T("Namespace"),
                                 MB_OK);
                     }
                  }
               }
            }
         }
         RELEASE(pIXMLDOMDocument2);
      }
   }
   catch(...)
   {
      CHECK_AND_RELEASE(pIXMLDOMDocument2);
      DisplayErrorToUser();
   }
   return bResult;
}

d:\\inetpub\\wwwroot\\samplexmldtd.xml

<?xml version='1.0'?>
<COLLECTION xmlns:dt="urn:schemas-microsoft-com:datatypes">
   <DATE dt:dt="datetime">1998-10-13T15:56:00</DATE>
   <BOOK>
      <TITLE>Cosmos</TITLE>
      <AUTHOR>Carl Sagan</AUTHOR>
      <PUBLISHER>Ballantine Books</PUBLISHER>
   </BOOK>
</COLLECTION>

Output ( in a message box )

urn:schemas-microsoft-com:datatypes

Visual Basic Example

Dim xmlDoc As New MSXML2.DOMDocument
Dim DocNamespaces As MSXML2.XMLSchemaCache

xmlDoc.async = False
xmlDoc.Load " sample.xml"
Set DocNamespaces = xmlDoc.NameSpaces
For i = 0 To DocNamespaces.length - 1
   MsgBox DocNamespaces.namespaceURI(i)
Next I

sample.xml

<?xml version='1.0'?>
<COLLECTION xmlns:dt="urn:schemas-microsoft-com:datatypes">
   <DATE dt:dt="datetime">1998-10-13T15:56:00</DATE>
   <BOOK>
      <TITLE>Cosmos</TITLE>
      <AUTHOR>Carl Sagan</AUTHOR>
      <PUBLISHER>Ballantine Books</PUBLISHER>
   </BOOK>
</COLLECTION>

Output

urn:schemas-microsoft-com:datatypes

See Also

IXMLDOMDocument2 Interface


schemas Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument2::schemas Method

IXMLDOMDocument2::get_schemas Method

Identifies the XMLSchemaCache object to use during load.

Visual Basic Syntax

Set objXMLDOMSchemaCollection = objIXMLDOMDocument2.schemas

C/C++ Syntax

HRESULT get_schemas (VARIANT* pSchemaCollection);

Parameters

pSchemaCollection [out]
The schema collection that is returned. This is the same object that was previously set, or Null if none has been set.

C/C++ Return Values

S_OK
Value returned if the method executes successfully and a schema collection is set.
S_FALSE
Value returned if no schema collection is set.
E_POINTER
Value returned if pSchemaCollection = Null.

Null if no schema collection is currently set. You will always get back the same collection object you put in.

Remarks

This method provides a way of associating preloaded schemas with any namespace. It also provides a way to override the schemas that are used by the document you are about to load. Setting a new schema collection has no effect on the current document until the next load or loadXML call. See XMLSchemaCache for more information. The schemas loaded by the document during load do not automatically get added to this collection.

Setting any non-null schema collection automatically disables DTD processing because you cannot mix both DTD and XML Schema processing on the same document. This means DTDs will be ignored. Setting the schemas collection back to Null will re-enable DTD processing.

IXMLDOMDocument2::put_schemas Method

Used for finding schema documents during load.

Visual Basic Syntax

objXMLDOMDocument.schemas = objXMLDOMSchemaCollection

C/C++ Syntax

HRESULT putref_schemas (VARIANT schemaCollection);

Parameters

schemaCollection [in]
The schema collection to set. If this is Null, it removes any schema collection that is currently set.

C/C++ Return Values

S_OK
Value returned if method executes successfully.
E_FAIL
Value returned if an IXMLSchemaCollection interface cannot be obtained from SchemaCollection with formatted IErrorInfo.

Null if no schema collection is currently set. You will always get the same collection object you put in.

Remarks

This method provides a way of associating preloaded schemas with any namespace. It also provides a way to override the schemas that are used by the document you are about to load. Setting a new schema collection has no effect on the current document until the next load or loadXML call. See IXMLDOMSchemaCollection for more information. The schemas loaded by the document during load do not automatically get added to this collection.

Setting any non-null schema collection automatically disables DTD processing because you cannot mix both DTD and XML Schema processing on the same document. This means DTDs will be ignored. Setting the schemas collection back to Null will re-enable DTD processing.

Example

BOOL DOMDocument2schemas()
{
   BOOL bResult = FALSE;
   short sResult = FALSE;
   IXMLDOMNodeList *pIXMLDOMNodeList=NULL;
   IXMLDOMDocument2 *pIXMLDOMDocument2=NULL;
   IXMLDOMSchemaCollection *pIXMLDOMSchemaCollection=NULL;
   HRESULT hr;
   BSTR bstrValue;
   VARIANT varValue;

   try
   {
      hr=CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_SERVER,                   IID_IXMLDOMDocument2, (LPVOID*)(&pIXMLDOMDocument2));
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMDocument2)
      {
         hr=pIXMLDOMDocument2->put_async(VARIANT_FALSE);
         if(SUCCEEDED(hr))
         {
            hr=CoCreateInstance(CLSID_XMLSchemaCache, NULL, CLSCTX_SERVER,
               IID_IXMLDOMSchemaCollection,
               (LPVOID*)(&pIXMLDOMSchemaCollection));
            SUCCEEDED(hr) ? 0 : throw hr;

            if(SUCCEEDED(hr) && pIXMLDOMSchemaCollection)
            {
               hr = pIXMLDOMSchemaCollection->add(_T("x-schema:books"),
            _variant_t(_T("d:\\inetpub\\wwwroot\\bookschema.xml")));
               if(SUCCEEDED(hr))
               {
                  varValue.vt = VT_DISPATCH;
                  varValue.pdispVal = pIXMLDOMSchemaCollection;
                  hr=pIXMLDOMDocument2->putref_schemas(varValue);

                  // The document will load only if a valid schema is
                  // attached to the xml file.
                  hr=pIXMLDOMDocument2->load(_variant_t(
            _T("d:\\inetpub\\wwwroot\\samplexmldtd.xml")),
      &sResult);
                  if(SUCCEEDED(hr) && (sResult==VARIANT_TRUE))
                  {
                     pIXMLDOMDocument2->get_xml(&bstrValue);
                     ::MessageBox(NULL, bstrValue, _T("Loaded Document"),
                        MB_OK);
                     bResult=TRUE;
                  }
               }
               RELEASE(pIXMLDOMSchemaCollection);
            }
         }
         RELEASE(pIXMLDOMDocument2);
      }
   }
   catch(...)
   {
      CHECK_AND_RELEASE(pIXMLDOMDocument2);
      CHECK_AND_RELEASE(pIXMLDOMNodeList);
      CHECK_AND_RELEASE(pIXMLDOMSchemaCollection);
      DisplayErrorToUser();
   }
   return bResult;
}
 d:\\inetpub\\wwwroot\\samplexmldtd.xml

<?xml version='1.0'?>
<COLLECTION xmlns="x-schema:books"
   xmlns:dt="urn:schemas-microsoft-com:datatypes">
   <DATE dt:dt="datetime">1998-10-13T15:56:00</DATE>
   <BOOK>
      <TITLE>Cosmos</TITLE>
      <AUTHOR>Carl Sagan</AUTHOR>
      <PUBLISHER>Ballantine Books</PUBLISHER>
   </BOOK>
   <BOOK>
      <TITLE>Catwings</TITLE>
      <AUTHOR>Ursula K. Le Guin</AUTHOR>
      <PUBLISHER>Scholastic</PUBLISHER>
   </BOOK>
   <BOOK>
      <TITLE>Home Town</TITLE>
      <AUTHOR>Tracy Kidder</AUTHOR>
      <PUBLISHER>Random House</PUBLISHER>
   </BOOK>
</COLLECTION>

d:\\inetpub\\wwwroot\\bookschema.xml

<?xml version="1.0"?>
<Schema xmlns="urn:schemas-microsoft-com:xml-data">
   <ElementType name="TITLE" />
   <ElementType name="AUTHOR" />
   <ElementType name="PUBLISHER" />
   <ElementType name="DATE" />
   <ElementType name="BOOK" model="closed">
      <element type="TITLE" />
      <element type="AUTHOR" />
      <element type="PUBLISHER" />
   </ElementType>
   <ElementType name="COLLECTION" model="closed">
      <element type="BOOK" />
   </ElementType>
</Schema>

See Also

IXMLDOMDocument2 Interface


selectNodes Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument2::selectNodes Method

Changes select nodes to return an object that supports the IXMLDOMSelection interface via QueryInterface.

Visual Basic Syntax

Set objXMLDOMNodeList = objXLMDOMDocument2.selectNodes(expression)

C/C++ Syntax

HRESULT selectNodes(BSTR expression, IXMLDOMNodeList** ppNodeList);

Parameters

expression [in]
The XQL or XPath expression. (see IXMLDOMSelection for more information).
ppNodeList [out]
The new node list object that is returned. It inherits the threading model of this document object.

C/C++ Return Values

S_OK
Value returned if methid executes successfully.
E_INVALIDARG
Value returned if ppNodeList = NULL.
E_FAIL
Value returned if expression is invalid, with formatted IErrorInfo.

Remarks

In late-bound scripting languages, the IXMLDOMSelection methods are automatically available on this object. In Visual Basic, IXMLDOMNodeList has to be cast into IXMLDOMSelection. In C++ you have to make a QueryInterface call to get the IXMLDOMSelection interface pointer. The language used depends on the SelectionLanguage property defined with setProperty.

C/C++ Example

BOOL DOMDocument2selectNodes()
{
   BOOL bResult = FALSE;
   short sResult = FALSE;
   IXMLDOMSelection *pIXMLDOMSelection=NULL;
   IXMLDOMNodeList *pIXMLDOMNodeList=NULL;
   IXMLDOMNode *pIXMLDOMNode=NULL;
   IXMLDOMDocument2 *pIXMLDOMDocument2=NULL;
   BSTR bstrValue;
   HRESULT hr;

   try
   {
      hr=CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_SERVER,                IID_IXMLDOMDocument2, (LPVOID*)(&pIXMLDOMDocument2));
         SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMDocument2)
      {
         hr=pIXMLDOMDocument2->put_async(VARIANT_FALSE);
         if(SUCCEEDED(hr))
         {
            hr=pIXMLDOMDocument2->load(_variant_t(
            _T("d:\\inetpub\\wwwroot\\samplexmldtd.xml")), &sResult);
            if(SUCCEEDED(hr) && (sResult==VARIANT_TRUE))
            {
               m_hr=pIXMLDOMDocument2->setProperty(
                                    _T("SelectionLanguage"),
                                    _variant_t(_T("XPath")));
               if(SUCCEEDED(m_hr))
               {
                  m_hr=pIXMLDOMDocument2->selectNodes(
                              _T("*/BOOK[TITLE='Cosmos']"),
                              (IXMLDOMNodeList**)&pIXMLDOMSelection);
                  if(SUCCEEDED(m_hr) && pIXMLDOMSelection)
                  {
                     LONG uLength;

                     bResult=TRUE;
                     m_hr=pIXMLDOMSelection->get_length(&uLength);
                     if(SUCCEEDED(m_hr))
                     {
                        for(int iIndex=0; iIndex < uLength; iIndex++)
                        {
                     // Remove all the nodes from the list - display them
                     // as they are removed.
                           m_hr=pIXMLDOMSelection->removeNext(
                                          &m_pIXMLDOMNode);
                           if(SUCCEEDED(m_hr) && m_pIXMLDOMNode)
                           {
                              m_hr=m_pIXMLDOMNode->get_text(&m_bstrValue);
                              if(SUCCEEDED(m_hr))
                                 ::MessageBox(NULL, m_bstrValue, _T("Node
                                    Text"), MB_OK);
                              RELEASE(m_pIXMLDOMNode);
                           }
                        }
                     }
                     RELEASE(pIXMLDOMSelection);
                  }
               }
            }
         }
         RELEASE(pIXMLDOMDocument2);
      }
   }
   catch(...)
   {
      CHECK_AND_RELEASE(pIXMLDOMNode);
      CHECK_AND_RELEASE(pIXMLDOMDocument2);
      CHECK_AND_RELEASE(pIXMLDOMNodeList);
      CHECK_AND_RELEASE(pIXMLDOMSelection);
      DisplayErrorToUser();
   }
   return bResult;
}

d:\\inetpub\\wwwroot\\samplexmldtd.xml

<?xml version='1.0'?>
<COLLECTION xmlns:dt="urn:schemas-microsoft-com:datatypes">
   <DATE dt:dt="datetime">1998-10-13T15:56:00</DATE>
   <BOOK>
      <TITLE>Cosmos</TITLE>
      <AUTHOR>Carl Sagan</AUTHOR>
      <PUBLISHER>Ballantine Books</PUBLISHER>
   </BOOK>
   <BOOK>
      <TITLE>Catwings</TITLE>
      <AUTHOR>Ursula K. Le Guin</AUTHOR>
      <PUBLISHER>Scholastic</PUBLISHER>
   </BOOK>
   <BOOK>
      <TITLE>Home Town</TITLE>
      <AUTHOR>Tracy Kidder</AUTHOR>
      <PUBLISHER>Random House</PUBLISHER>
   </BOOK>
</COLLECTION>

output ( in a message box )

Cosmos Carl Sagan Ballantine Books

See Also

IXMLDOMDocument2 Interface


setProperty Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument2::setProperty Method

Generic mechanism for setting properties.

Visual Basic Syntax

objXMLDOMDocument2.setProperty(name, value)

C/C++ Syntax

HRESULT setProperty (BSTR name, VARIANT value);

Parameters

name [in]
The "SelectionLanguage" value.
value [in]
Either "XPath" or "XSLPattern".

C/C++ Return Values

S_OK
Value returned if successful.
E_FAIL
Value returned if name or value is invalid.

Remarks

Use this interface to set the SelectionLanguage property. Existing properties from IXMLDOMDocument are not accessible with this method. White space is not stripped or normalized in property names or values.

See Also

IXMLDOMDocument2 Interface


validate Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocument2::validate Method

Performs run-time validation on the currently loaded document using the currently loaded DTD, schema, or schema collection.

Visual Basic Syntax

objXMLDOMDocument2.validate

C/C++ Syntax

HRESULT validate(IXMLDOMParseError __RPC_FAR *__RPC_FAR * ppError);

C/C++ Return Values

IErrorInfo containing a formatted error message indicating exactly what went wrong and one of the following HRESULTs (also returned in the ErrorInfo.Number property).

Return value | Hexadecimal value | Description
E_PENDING | 0x8000000A | Document readyState is not 4 (indicating the document is not completely loaded).
S_OK | 0 | The document is valid according to DTD/Schemas.
S_FALSE | 1 | The document is invalid according to DTD/Schemas. See the returned IXMLDOMParseError object for error information.

Possible validation errors returned with S_FALSE are listed below, together with the errorCode value:

Message ID | Error Code (Hex Value) | Message
XML_E_NODTD | 0xC00CE224 | Validate failed because a DTD/Schema was not specified in the document.
XML_E_NOTWF | 0xC00CE223 | Validate failed because the document does not contain exactly one root node.
XML_ENTITY_UNDEFINED | 0xC00CE002 | Reference to undefined entity '%1'.
XML_INFINITE_ENTITY_LOOP | 0xC00CE003 | Entity '%1' contains an infinite entity reference loop.
XML_NDATA_INVALID_PE | 0xC00CE004 | Cannot use the NDATA keyword in a parameter entity declaration.
XML_REQUIRED_NDATA | 0xC00CE005 | Cannot use a general parsed entity '%1' as the value for attribute '%2'.
XML_NDATA_INVALID_REF | 0xC00CE006 | Cannot use unparsed entity '%1' in an entity reference.
XML_EXTENT_IN_ATTR | 0xC00CE007 | Cannot reference an external general parsed entity '%1' in an attribute value.
XML_ELEMENT_UNDECLARED | 0xC00CE00D | The element '%1' is used but not declared in the DTD/Schema.
XML_ELEMENT_ID_NOT_FOUND | 0xC00CE00E | The attribute '%1' references the ID '%2' which is not defined anywhere in the document.
XML_EMPTY_NOT_ALLOWED | 0xC00CE011 | Element cannot be empty according to the DTD/Schema.
XML_ELEMENT_NOT_COMPLETE | 0xC00CE012 | Element content is incomplete according to the DTD/Schema.
XML_ROOT_NAME_MISMATCH | 0xC00CE013 | The name of the top-most element must match the name of the DOCTYPE declaration.
XML_INVALID_CONTENT | 0xC00CE014 | Element content is invalid according to the DTD/Schema.
XML_ATTRIBUTE_NOT_DEFINED | 0xC00CE015 | The attribute '%1' on this element is not defined in the DTD/Schema.
XML_ATTRIBUTE_FIXED | 0xC00CE016 | Attribute '%1' has a value that does not match the fixed value defined in the DTD/Schema.
XML_ATTRIBUTE_VALUE | 0xC00CE017 | Attribute '%1' has an invalid value according to the DTD/Schema.
XML_ILLEGAL_TEXT | 0xC00CE018 | Text is not allowed in this element according to DTD/Schema.
XML_MULTI_FIXED_VALUES | 0xC00CE019 | An attribute declaration cannot contain multiple fixed values: '%1'.
XML_ELEMENT_UNDEFINED | 0xC00CE01C | Reference to undeclared element: '%1'.
XML_XMLNS_FIXED | 0xC00CE01E | Attribute '%1' must be a #FIXED attribute.
XML_REQUIRED_ATTRIBUTE_MISSING | 0xC00CE020 | Required attribute '%1' is missing.
XML_DTD_EXPECTING | 0xC00CE026 | Expecting: %1.
XML_E_NODTD | 0xC00CE224 | Validate failed because a DTD/Schema was not specified in the document.

See Also

IXMLDOMDocument2 Interface


IXMLDOMDocumentFragment Interface

A lightweight object that is useful for tree insert operations.

This interface inherits from IXMLDOMNode.

Methods

This interface has no unique methods of its own but exposes the inherited methods from the IXMLDOMNode interface.

Remarks

The DocumentFragment object can represent a fragment of a document or portion of a document's tree. This makes it useful when implementing end-user commands that allow users to rearrange a document, such as cutting and pasting.

The DocumentFragment node has special, defined behavior for XMLDOMNode insert operations that makes it especially convenient for developers. When an XMLDOMDocumentFragment is inserted into a DOMDocument node (or other node that can take children), the children of the DocumentFragment are inserted into the node rather than the DocumentFragment itself. This makes the DocumentFragment useful when the user wants to create nodes that are siblings; the DocumentFragment acts as the parent of these nodes so that the user can employ the standard methods from the IXMLDOMNode interface, such as insertBefore and appendChild.

The children of a DocumentFragment node make up zero or more nodes representing the tops of any subtrees defining the structure of the document. DocumentFragment nodes do not need to be well-formed XML documents (although they do need to follow the rules imposed upon well-formed XML parsed entities, which can have multiple top nodes). For example, a DocumentFragment might have only one child, and that child node could be a Text node. Such a structure model represents neither an HTML document nor a well-formed XML document.

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

See Also

IXMLDOMNode Interface


IXMLDOMDocumentType Interface

Contains information associated with the document type declaration.

This interface inherits from IXMLDOMNode.

Methods

entities | Contains a list of the entities declared in the document type declaration.
name | Contains the name of the document type.
notations | Contains a list of the notations present in the document type declaration.

Remarks

Each DOMDocument object includes a doctype property that identifies the document's XMLDOMDocumentType object. The XMLDOMDocumentType object provides access to the list of entities and notations defined for the document.

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

See Also

IXMLDOMNode Interface


entities Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocumentType::entities Method

IXMLDOMDocumentType::get_entities Method

Contains a list of the entities declared in the document type declaration.

Visual Basic Syntax

Set objXMLDOMNamedNodeMap = oXMLDOMDocumentType.entities

C/C++ Syntax

HRESULT get_entities(
    IXMLDOMNamedNodeMap **entityMap);

Parameters

entityMap [out]
List of the general entities, both external and internal, that are present in this document.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if entityMap is Null.

Remarks

In the following document type declaration example, the node list in this property contains the entities "bat" and "bar" but not "baz":

<!DOCTYPE ex SYSTEM "ex.dtd" [
  <!ENTITY bat "bat">
  <!ENTITY bar "bar">
  <!ENTITY % baz "baz">
]>

Example

IXMLDOMEntity *GetRootEntity()
{
   HRESULT hr;
   IXMLDOMNamedNodeMap *pIXMLDOMNamedNodeMapEntities = NULL;
   IXMLDOMEntity *pIXMLDOMEntity = NULL;
   IXMLDOMNode *pIXMLDOMNode = NULL;
   IXMLDOMDocument *pIXMLDOMDocument = NULL;
   IXMLDOMDocumentType *pIXMLDOMDocumentType = NULL;

   try
   {
      // Create an instance of DOMDocument and initialize
      // pIXMLDOMDocument.
      // Load/create an XML fragment.
      hr = pIXMLDOMDocument->get_doctype(&pIXMLDOMDocumentType);
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMDocumentType)
      {
         hr = pIXMLDOMDocumentType->get_entities
                        (&pIXMLDOMNamedNodeMapEntities);
         if(SUCCEEDED(hr) && pIXMLDOMNamedNodeMapEntities)
         {
            hr = pIXMLDOMNamedNodeMapEntities->nextNode(&pIXMLDOMNode);
            if(SUCCEEDED(hr) && pIXMLDOMNode)
            {
               hr = pIXMLDOMNode->QueryInterface(IID_IXMLDOMEntity,
                     (void**)&pIXMLDOMEntity );
               SUCCEEDED(hr) ? 0 : throw hr;
               pIXMLDOMNode->Release();
               pIXMLDOMNode = NULL;
            }
            pIXMLDOMNamedNodeMapEntities->Release();
         }
         pIXMLDOMDocumentType->Release();
      }
   }
   catch(...)
   {
      if(pIXMLDOMNode)
         pIXMLDOMNode->Release();
      DisplayErrorToUser();
   }

   // Release pIXMLDOMEntity when finished using it.
   return pIXMLDOMEntity;
}

See Also

IXMLDOMDocumentType Interface


name Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocumentType::name Method

IXMLDOMDocumentType::get_name Method

Contains the name of the document type.

Visual Basic Syntax

strValue = oXMLDOMDocumentType.name

C/C++ Syntax

HRESULT get_name(
    BSTR *rootName);

Parameters

rootName [out]
When specified, this corresponds to the single root element of the document tree.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value when returning Null.

Example

BOOL DOMDocGetDocType()
{
   BOOL bResult = FALSE;
   IXMLDOMDocumentType *pIXMLDOMDocumentType = NULL;
   IXMLDOMDocument *pIXMLDOMDocument = NULL;
   IXMLDOMDocumentType *pIXMLDOMDocumentType = NULL;
   BSTR bstrDTDName;
   HRESULT hr;

   try
   {
      // Create an instance of DOMDocument and initialize
      // pIXMLDOMDocument.
      // Load/create an XML fragment.
      hr = pIXMLDOMDocument->get_doctype(&pIXMLDOMDocumentType);
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMDocumentType)
      {
         pIXMLDOMDocumentType->get_name(&bstrDTDName);
         ::MessageBox(NULL, bstrDTDName, _T("Doc Type"), MB_OK);
         bResult = TRUE;
         pIXMLDOMDocumentType->Release();
      }
   }
   catch(...)
   {
      if(pIXMLDOMDocumentType)
         pIXMLDOMDocumentType->Release();
      DisplayErrorToUser();
   }
   return bResult;
}

See Also

IXMLDOMDocumentType Interface


notations Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMDocumentType::notations Method

IXMLDOMDocumentType::get_notations Method

Contains a list of the notations present in the document type declaration.

Visual Basic Syntax

Set objXMLDOMNamedNodeMap = oXMLDOMDocumentType.notations

C/C++ Syntax

HRESULT get_notations(
    IXMLDOMNamedNodeMap **notationMap);

Parameters

notationMap [out]
List of the XMLDOMNotation objects present in this document type declaration.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if notationMap is Null.

Remarks

Notations identify by name the format of unparsed entities, the format of elements that bear a notation attribute, or the application to which a processing instruction is addressed.

Example

BOOL DOMDocTypeprops()
{
   BOOL bResult = FALSE;
   IXMLDOMDocument *pIXMLDOMDocument = NULL;
   IXMLDOMDocumentType *pIXMLDOMDocumentType = NULL;
   IXMLDOMNamedNodeMap *pIXMLDOMNamedNodeMapNotations = NULL;
   HRESULT hr;

   try
   {
      // Create an instance of DOMDocument and initialize
      // pIXMLDOMDocument.
      // Load/create an XML fragment.
      hr = pIXMLDOMDocument->get_doctype(&pIXMLDOMDocumentType);
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMDocumentType)
      {
         hr = pIXMLDOMDocumentType->get_notations
            (&pIXMLDOMNamedNodeMapNotations);
         if(pIXMLDOMNamedNodeMapNotations)
         {
         // DisplayMapEntries(pIXMLDOMNamedNodeMapNotations)
            bResult = TRUE;
            pIXMLDOMNamedNodeMapNotations->Release();
         }
         pIXMLDOMDocumentType->Release();
      }
   }
   catch(...)
   {
      if(pIXMLDOMDocumentType)
         pIXMLDOMDocumentType->Release();
      if(pIXMLDOMNamedNodeMapNotations)
         pIXMLDOMNamedNodeMapNotations->Release();
      DisplayErrorToUser();
   }
   return bResult;
}

See Also

IXMLDOMDocumentType Interface


IXMLDOMElement Interface

Represents the element object.

This interface inherits from IXMLDOMNode.

Methods

getAttribute | Retrieves the value of the named attribute.
getAttributeNode | Retrieves the named attribute node.
getElementsByTagName | Returns a list of all descendant elements that match the supplied name.
normalize | Normalizes all descendant elements; combines two or more adjacent text nodes into one unified text node.
removeAttribute | Removes or replaces the named attribute.
removeAttributeNode | Removes the specified attribute from this element.
setAttribute | Sets the value of the named attribute.
setAttributeNode | Adds or changes the supplied attribute node on this element.
tagName | Contains the element name—the name that appears within the tag.

Remarks

Element nodes are among the most common objects in the XML document tree. Element nodes can have attributes associated with them. By definition, attributes are not defined as child nodes of an element and are not considered to be part of the document tree. Accordingly, the XMLDOMElement object provides methods to make it easier to manage attributes, including methods to associate an attribute with an element and to retrieve an attribute object and the attribute value by name.

To retrieve the set of all attributes associated with an element, you can also call the getAttributes method, which returns an XMLDOMNamedNodeMap collection object that contains all the element's attributes.

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

Version-Independent ProgID: MSXML2.DOMDocument

See Also

IXMLDOMNode Interface | IXMLDOMText Interface


getAttribute Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMElement::getAttribute Method

Retrieves the value of the named attribute.

Visual Basic Syntax

strValue = oXMLDOMElement.getAttribute(name)

C/C++ Syntax

HRESULT getAttribute(
    BSTR name,
    VARIANT *value);

Parameters

name [in]
Name of the attribute to return.
value [out]
String that contains the attribute value. The empty string is returned if the named attribute does not have a specified or default value.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value when no attribute with the given name is found.
E_INVALIDARG
Value returned if name is Null.

Remarks

Another way to retrieve attributes is to use the XMLDOMNamedNodeMap object's getNamedItem method.

C/C++ Example

BOOL DOMElementAttribute()
{
   BOOL bResult = FALSE;
   _variant_t varValue;
   BSTR bstrAttributeName = ::SysAllocString(_T("dateCreated"));
   IXMLDOMDocument *pIXMLDOMDocument = NULL;
   IXMLDOMElement *pIXMLDOMElement = NULL;
   HRESULT hr;

   try
   {
      // Create an instance of DOMDocument and initialize
      // pIXMLDOMDocument.
      // Load/create an XML fragment.
      hr = pIXMLDOMDocument->get_documentElement(&pIXMLDOMElement);
      SUCCEEDED(hr) ? 0 : throw hr;

      If(pIXMLDOMElement)
      {
         varValue = _T("year 2000");
         hr = pIXMLDOMElement->setAttribute(bstrAttributeName, varValue);
         SUCCEEDED(hr) ? 0 : throw hr;

         hr = pIXMLDOMElement->getAttribute(bstrAttributeName, &varValue);
         SUCCEEDED(hr) ? 0 : throw hr;
         if(varValue.vt != VT_NULL)
         {
            ::MessageBox(NULL, _bstr_t(varValue), bstrAttributeName,                   MB_OK);
            bResult = TRUE;
         }
         ::SysFreeString(bstrAttributeName);
         bstrAttributeName = NULL;
         pIXMLDOMElement->Release();
      }
   }
   catch(...)
   {
      if(bstrAttributeName)
         ::SysFreeString(bstrAttributeName);
      if(pIXMLDOMElement)
         pIXMLDOMElement->Release();
      DisplayErrorToUser();
   }
   return bResult;
}

See Also

IXMLDOMElement Interface


getAttributeNode Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMElement::getAttributeNode Method

Retrieves the named attribute node.

Visual Basic Syntax

Set objXMLDOMAttribute = oXMLDOMElement.getAttributeNode(name)

C/C++ Syntax

HRESULT getAttributeNode(
    BSTR name,
    IXMLDOMAttribute **attributeNode);

Parameters

name [in]
Name of the attribute to be retrieved.
attributeNode [out]
DOMAttribute object that is returned with the supplied name, or Null if the named attribute cannot be found on this element.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when no attribute with the given name is found.
E_INVALIDARG
Value returned if name is Null.

C/C++ Example

BOOL DOMElementAttributeNode()
{
   BOOL bResult = FALSE;
   BSTR bstrAttributeName = ::SysAllocString(_T("dateCreated"));
   IXMLDOMAttribute* pIXMLDOMAttribute = NULL;
   IXMLDOMElement *pIXMLDOMElement = NULL;
   IXMLDOMDocument *pIXMLDOMDocument = NULL;
   HRESULT hr;

   try
   {
      // Create an instance of DOMDocument and initialize
      // pIXMLDOMDocument.
      // Load/create an XML fragment.
      hr = pIXMLDOMDocument->get_documentElement(&pIXMLDOMElement);
      SUCCEEDED(m_hr) ? 0 : throw hr;

      If(pIXMLDOMElement)
      {
         hr = pIXMLDOMElement->getAttributeNode(bstrAttributeName,                   &pIXMLDOMAttribute);
         if(SUCCEEDED(hr) && pIXMLDOMAttribute)
         {
         // Read node value and display . . .
            bResult = TRUE;
            pIXMLDOMAttribute->Release();
            pIXMLDOMAttribute = NULL;
         }
         ::SysFreeString(bstrAttributeName);
         bstrAttributeName = NULL;
         pIXMLDOMElement->Release();
         pIXMLDOMElement = NULL;
      }
   }
   catch(...)
   {
      if(bstrAttributeName)
         ::SysFreeString(bstrAttributeName);
      if(pIXMLDOMAttribute)
         pIXMLDOMAttribute->Release();
      if(pIXMLDOMElement)
         pIXMLDOMElement->Release();
      DisplayErrorToUser();
   }
   return bResult;
}

See Also

IXMLDOMElement Interface


getElementsByTagName Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMElement::getElementsByTagName Method

Returns a list of all descendant elements that match the supplied name.

Visual Basic Syntax

Set objXMLDOMNodeList = oXMLDOMElement.getElementsByTagName(tagName)

C/C++ Syntax

HRESULT getElementsByTagName(
    BSTR tagName,
    IXMLDOMNodeList **resultList);

Parameters

tagName [in]
Name of the element to find. The string "*" matches all descendant elements of this element.
resultList [out]
DOMNodeList object containing all elements that match the supplied name.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

Elements appear in the order encountered in a preorder traversal of this element's tree.

Note that the XMLDOMNodeList object is returned even if there are no matches. In such a case, the length of the list will be set to zero.

The IXMLDOMNodeList interface is live and immediately reflects changes to the nodes that appear in the list.

C/C++ Example

// Find all Nodes with a particular tag
BOOL DOMDocFindNodesWithTag()
{
   BOOL bResult = FALSE;
   CString strFindText (_T("AUTHOR"));
   IXMLDOMNodeList *pIDOMNodeList = NULL;
   IXMLDOMNode *pIDOMNode = NULL;
   long value = 0;
   BSTR bstrItemText = NULL;
   HRESULT hr;

   try
   {
      // Create the DOMDocument and initialise m_pIXMLDOMDocument2
      // find all "AUTHOR" elements
      hr = m_pIXMLDOMDocument2->getElementsByTagName((TCHAR *)strFindText.GetBuffer(0), &pIDOMNodeList);
      SUCCEEDED(hr) ? 0 : throw hr;

      // get the length of the list returned by the previous find
      hr = pIDOMNodeList->get_length(&value);
      if(SUCCEEDED(hr))
      {
         pIDOMNodeList->reset();
      // loop through the elements found and display the contents
         for(int ii = 0; ii < value; ii++)
         {
            hr = pIDOMNodeList->get_item(ii, &pIDOMNode);
            SUCCEEDED(hr) ? 0 : throw hr;
            if(pIDOMNode)
            {
               hr = pIDOMNode->get_text(&bstrItemText);
               SUCCEEDED(hr) ? 0 : throw hr;
               if(bstrItemText)
               {
                     bResult = TRUE;
                     ::MessageBox(NULL, bstrItemText, strFindText, MB_OK);
                     ::SysFreeString(bstrItemText);
                     bstrItemText = NULL;
               }
               pIDOMNode->Release();
               pIDOMNode = NULL;
            }
         }
         pIDOMNodeList->Release();
         pIDOMNodeList = NULL;
      }
   }
   catch(...)
   {
      if(pIDOMNodeList)
         pIDOMNodeList->Release();
      if(pIDOMNode)
         pIDOMNode->Release();
      if(bstrItemText)
         ::SysFreeString(bstrItemText);
      bResult = FALSE;
      DisplayErrorToUser();
   }

   return bResult;
}

See Also

IXMLDOMElement Interface


normalize Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMElement::normalize Method

Normalizes all descendant elements; combines two or more adjacent text nodes into one unified text node.

Visual Basic Syntax

oXMLDOMElement.normalize

C/C++ Syntax

HRESULT normalize(void);

C/C++ Return Values

S_OK
Value returned if successful.
E_FAIL
Value returned if an error occurs.

Remarks

This method converts all text node descendants of this element (at any depth) into a "normal" form, in which no text nodes are adjacent. In normal form, text nodes can be separated only by markup (such as tags, comments, processing instructions, CDATA sections, and entity references). The normal form is useful for operations that require a particular document tree structure and ensures that the DOM view of a document is identical when saved and reloaded.

Collapsed node objects are automatically deleted.

C/C++ Example

BOOL DOMElementNormalize()
{
   BOOL bResult = FALSE;
   IXMLDOMElement *pIXMLDOMElement = NULL;
   IXMLDOMDocument *pIXMLDOMDocument = NULL;
   HRESULT hr;

   try
   {
      // Create an instance of DOMDocument and initialize
      // pIXMLDOMDocument.
      // Load/create an XML fragment.
      hr = pIXMLDOMDocument->get_documentElement(&pIXMLDOMElement);
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMElement )
      {
         hr = pIXMLDOMElement->normalize();
         if(SUCCEEDED(m_hr))
            bResult = TRUE;
         pIXMLDOMElement->Release();
      }
   }
   catch(...)
   {
      if(pIXMLDOMElement)
         pIXMLDOMElement->Release();
      DisplayErrorToUser();
   }
   return bResult;
}

See Also

IXMLDOMElement Interface


removeAttribute Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMElement::removeAttribute Method

Removes or replaces the named attribute.

Visual Basic Syntax

oXMLDOMElement.removeAttribute(name)

C/C++ Syntax

HRESULT removeAttribute(
    BSTR name);

Parameters

name [in]
Name of the attribute to be removed or replaced.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when no attribute with the given name is found.
E_FAIL
Value returned if an error occurs.

Remarks

If the specified attribute has a default value, this is equivalent to a replace operation: The current value is removed and a new attribute is created with the default value. This operation also resets the IXMLDOMNode interface's specified property.

C/C++ Example

IXMLDOMElement *pIXMLDOMElement = NULL;
_bstr_t bstrAttributeName = _T("dateCreated");
IXMLDOMDocument *pIXMLDOMDocument = NULL;
HRESULT hr;

try
{
   // Create an instance of DOMDocument and initialize pIXMLDOMDocument.
   // Load/create an XML fragment.
   hr = pIXMLDOMDocument->get_documentElement(&pIXMLDOMElement);
   SUCCEEDED(hr) ? 0 : throw hr;

   hr = pIXMLDOMElement->removeAttribute(bstrAttributeName);
   if(SUCCEEDED(hr))
   {
   // Attribute removed.
   }
   pIXMLDOMElement->Release();
   pIXMLDOMElement = NULL;
   // Release pIXMLDOMDocument when finished with it.
}
catch(...)
{
   // Release pIXMLDOMDocument if it exists.
   if(pIXMLDOMElement)
      pIXMLDOMElement->Release();
   DisplayErrorToUser();
}

See Also

IXMLDOMElement Interface


removeAttributeNode Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMElement::removeAttributeNode Method

Removes the specified attribute from this element.

Visual Basic Syntax

Set objXMLDOMAttribute = oXMLDOMElement.removeAttributeNode(DOMAttribute)

C/C++ Syntax

HRESULT removeAttributeNode(
    IXMLDOMAttribute *DOMAttribute,
    IXMLDOMAttribute **attributeNode);

Parameters

DOMAttribute [in]
DOMAttribute object that is to be removed from this element.
attributeNode [out]
Removed element.

C/C++ Return Values

S_OK
Value returned if successful.
E_FAIL
Value returned if an error occurs.

Remarks

Note that if the attribute has a default value, this call also creates a new attribute object with the default value, associates the new attribute with this element, and resets the attribute's specified method.

C/C++ Example

IXMLDOMElement *pIXMLDOMElement = NULL;
_bstr_t bstrAttributeName = _T("dateCreated");
IXMLDOMAttribute *pIXMLDOMAttribute = NULL;
IXMLDOMAttribute *pRemovedIXMLDOMAttribute = NULL;
IXMLDOMDocument *pIXMLDOMDocument = NULL;
HRESULT hr;

try
{
   // Create an instance of DOMDocument and initialize pIXMLDOMDocument.
   // Load/create an XML fragment.
      hr = pIXMLDOMDocument->get_documentElement(&pIXMLDOMElement);
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMElement)
      {
         hr = pIXMLDOMElement->getAttributeNode(bstrAttributeName,                   &pIXMLDOMAttribute);
         if(SUCCEEDED(hr) && pIXMLDOMAttribute)
         {
            hr = pIXMLDOMElement->removeAttributeNode(pIXMLDOMAttribute,                &pRemovedIXMLDOMAttribute);
            if(SUCCEEDED(hr))
               // Attribute node removed.
               bResult = TRUE;
            pIXMLDOMAttribute->Release();
         }
         pIXMLDOMElement->Release();
      }
   // Release pIXMLDOMDocument when finished with it.
}
catch(...)
{
   // Release pIXMLDOMDocument if it exists.
   if(pIXMLDOMAttribute)
      pIXMLDOMAttribute->Release();
   if(pIXMLDOMElement)
      pIXMLDOMElement->Release();
   DisplayErrorToUser();
}

See Also

IXMLDOMElement Interface


setAttribute Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMElement::setAttribute Method

Sets the value of the named attribute.

Visual Basic Syntax

oXMLDOMElement.setAttribute(name, value)

C/C++ Syntax

HRESULT setAttribute(
    BSTR name,
    VARIANT value);

Parameters

name [in]
Name of the attribute. If an attribute with that name already exists, its value is changed. If an attribute with that name does not exist, it is created.
value [in]
Value for the named attribute.

C/C++ Return Values

S_OK
Value returned if successful.
E_FAIL
Value returned if an error occurs.

Remarks

If an attribute with the supplied name already exists, this method changes its value to the supplied value parameter. The supplied string is not parsed, so any markup is treated as literal text (such as syntax to be recognized as an entity reference) and needs to be appropriately escaped by the implementation when it is written out.

To assign an attribute value that contains entity references, the user must create an XMLDOMAttribute object plus any XMLDOMText and XMLDOMEntityReference objects, build the appropriate subtree, and call the setAttributeNode method.

C/C++ Example

See the example given for the getAttribute method.

See Also

IXMLDOMElement Interface


setAttributeNode Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMElement::setAttributeNode Method

Adds or changes the supplied attribute node on this element.

Visual Basic Syntax

Set objXMLDOMAttribute = oXMLDOMElement.XMLDOMElement(DOMAttribute)

C/C++ Syntax

HRESULT setAttributeNode(
    IXMLDOMAttribute *DOMAttribute,
    IXMLDOMAttribute **attributeNode);

Parameters

DOMAttribute [in]
Attribute node that is to be associated with this element.
attributeNode [out]
Null unless the new attribute replaces an existing attribute with the same name, in which case this method returns the previous, replaced attribute node.

C/C++ Return Values

S_OK
Value returned if successful.
E_FAIL
Value returned if an error occurs.

Remarks

You cannot add an existing attribute to an element until you first remove it from its previous element. Also, you cannot add a namespace-qualified attribute when it uses the same prefix as another attribute with a different namespaceURI.

C/C++ Example

See the example given for the getAttributeNode method.

See Also

IXMLDOMElement Interface


tagName Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMElement::tagName Method

IXMLDOMElement::get_tagName Method

Contains the element name—the name that appears within the tag.

Visual Basic Syntax

strValue = oXMLDOMElement.tagName

C/C++ Syntax

HRESULT get_tagName(
    BSTR *tagName);

Parameters

tagName [out]
String that represents the element's name. For example, the tag name is "book" in the following tag:
<book ISBN="1572318546">

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value when returning Null.
E_INVALIDARG
Value returned if tagName is Null.

Example

IXMLDOMElement *pIXMLDOMElement = NULL;
BSTR bstrTagName = NULL;
IXMLDOMDocument *pIXMLDOMDocument = NULL;
HRESULT hr;

try
{
   // Create an instance of DOMDocument and initialize pIXMLDOMDocument.
   // Load/create an XML fragment.
   hr = pIXMLDOMDocument->get_documentElement(&pIXMLDOMElement);
   SUCCEEDED(hr) ? 0 : throw hr;

   if(pIXMLDOMElement)
   {
      hr = pIXMLDOMElement->get_tagName(&bstrTagName);
      if(SUCCEEDED(hr))
      {
         ::MessageBox(NULL, bstrTagName, _T("Tag Name"), MB_OK);
      }
      ::SysFreeString(bstrTagName);
      bstrTagName = NULL;
      pIXMLDOMElement->Release();
   }
}
catch(...)
{
   if(bstrTagName)
      ::SysFreeString(bstrTagName);
   if(pIXMLDOMElement)
      pIXMLDOMElement->Release();
   DisplayErrorToUser();
}

See Also

IXMLDOMElement Interface


IXMLDOMEntity Interface

Represents a parsed or unparsed entity in the XML document.

This interface inherits from IXMLDOMNode.

Methods

notationName | Contains the notation name.
publicId | Contains the public identifier.
systemId | Contains the system identifier.

Remarks

The XMLDOMEntity object represents the entity itself rather than the entity declaration. The W3C DOM does not currently define an object that models the entity declaration.

According to the W3C DOM specification, an XML parser can completely expand entity references into entities before the structure model is passed to the DOM. When these entity references are expanded, the document tree does not contain any entity references.

When the Microsoft XML parser validates the XML document, it expands external entities (except binary entities). The nodes representing the expanded entity are available as read-only children of the entity reference. The Microsoft implementation does not expand these entities when it is not validating.

The nodeName property contains the name of the entity.

The structure of the entity child list is exactly the same as the structure of the child list for the XMLDOMEntityReference object with the same nodeName value.

Level 1 of the W3C DOM API does not define a way to change entity nodes (all XMLDOMEntity object properties are read-only).

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

See Also

IXMLDOMDocument Interface


notationName Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMEntity::notationName Method

IXMLDOMEntity::get_notationName Method

Contains the notation name.

Visual Basic Syntax

strValue = oXMLDOMEntity.notationName

C/C++ Syntax

HRESULT get_notationName(
    BSTR *name);

Parameters

name [out]
For unparsed entities, the name of the notation for the entity (the name following NDATA). For parsed entities, contains the empty string.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when no notation is specified.
E_INVALIDARG
Value returned if name is Null.

Remarks

Note that the name of the entity is available using the nodeName method.

Example

BOOL DOMEntityNotationName()
{
   BOOL bResult = FALSE;
   IXMLDOMEntity *pIXMLDOMEntity = NULL;
   BSTR bstrName ;
   HRESULT hr;

   try
   {
      pIXMLDOMEntity = GetFirstEntity();

      if(pIXMLDOMEntity)
      {
         hr =   pIXMLDOMEntity->get_notationName(&bstrName);
         ::MessageBox(NULL, bstrName, _T("Notation name"), MB_OK);
         bResult = TRUE;
         ::SysFreeString(bstrName);
         pIXMLDOMEntity->Release();
      }
   }
   catch(...)
   {
      if(pIXMLDOMEntity)
         pIXMLDOMEntity->Release();
      DisplayErrorToUser();
   }
   return bResult;
}
IXMLDOMEntity* GetFirstEntity()
{
   IXMLDOMNamedNodeMap *pIXMLDOMNamedNodeMapEntities = NULL;
   IXMLDOMEntity *pIXMLDOMEntity = NULL;
   IXMLDOMNode *pIXMLDOMNode = NULL;
   IXMLDOMDocument *pIXMLDOMDocument = NULL;
   HRESULT hr;

   try
   {
      // Create an instance of DOMDocument and initialize
      // pIXMLDOMDocument.
      // Load/create an XML fragment.
      hr = pIXMLDOMDocument->get_doctype(&pIXMLDOMDocumentType);
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMDocumentType)
      {
         hr = pIXMLDOMDocumentType->get_entities
            (&pIXMLDOMNamedNodeMapEntities);
         if(SUCCEEDED(hr) && pIXMLDOMNamedNodeMapEntities)
         {
            hr = pIXMLDOMNamedNodeMapEntities->nextNode(&pIXMLDOMNode);
            if(SUCCEEDED(hr) && pIXMLDOMNode)
            {
               hr = pIXMLDOMNode->QueryInterface(IID_IXMLDOMEntity,
                  (void**)&pIXMLDOMEntity );
               SUCCEEDED(m_hr) ? 0 : throw m_hr;
               pIXMLDOMNode->Release();
            }
            pIXMLDOMNamedNodeMapEntities->Release();
         }
         pIXMLDOMDocumentType->Release();
      }
   }
   catch(...)
   {
      if(pIXMLDOMNode)
         pIXMLDOMNode->Release();
      if(m_pIXMLDOMNamedNodeMapEntities)
         m_pIXMLDOMNamedNodeMapEntities->Release();
      if(m_pIXMLDOMDocumentType)
         m_pIXMLDOMDocumentType->Release();
      DisplayErrorToUser();
   }
   // Release pIXMLDOMEntity when finished using it.
   // Release pIXMLDOMDocument when finished using it.
   return pIXMLDOMEntity;
}

See Also

IXMLDOMEntity Interface


publicId Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMEntity::publicId Method

IXMLDOMEntity::get_publicId Method

Contains the public identifier.

Visual Basic Syntax

strValue = oXMLDOMEntity.publicId

C/C++ Syntax

HRESULT get_publicId(
    BSTR *publicID);

Parameters

publicID [out]
Public identifier associated with the entity, if specified. If the public identifier is not specified, contains the empty string.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if no public ID is specified.
E_INVALIDARG
Value returned if publicID is Null.

Example

IXMLDOMEntity *pIXMLDOMEntity = NULL;
VARIANT varName ;
HRESULT hr;

try
{
   // See help on IXMLDOMEntity::get_notationName method for definition of
   // the GetFirstEntity() method.
   pIXMLDOMEntity = GetFirstEntity();

   if(pIXMLDOMEntity)
   {
      hr = pIXMLDOMEntity->get_publicId(&varName);
      if(SUCCEEDED(hr))
         ::MessageBox(NULL, _bstr_t(varName), _T("Public ID"), MB_OK);
      pIXMLDOMEntity->Release();
   }
}
catch(...)
{
   if(pIXMLDOMEntity)
      pIXMLDOMEntity->Release();
   DisplayErrorToUser();
}

See Also

IXMLDOMEntity Interface


systemId Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMEntity::systemId Method

IXMLDOMEntity::get_systemId Method

Contains the system identifier.

Visual Basic Syntax

strValue = oXMLDOMEntity.systemId

C/C++ Syntax

HRESULT get_systemId(
    BSTR *systemID);

Parameters

systemID [out]
System identifier associated with the entity, if specified. If the system identifier is not specified, contains the empty string.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when no system ID is specified.
E_INVALIDARG
Value returned if systemID is Null.

Example

IXMLDOMEntity *pIXMLDOMEntity = NULL;
VARIANT varName ;
HRESULT hr;

try
{
   // See help on IXMLDOMEntity::get_notationName method for definition of
   // the GetFirstEntity() method.
   pIXMLDOMEntity = GetFirstEntity();

   if(pIXMLDOMEntity)
   {
      hr = pIXMLDOMEntity->get_systemId(&varName);
      if(SUCCEEDED(hr))
         ::MessageBox(NULL, _bstr_t(varName), _T("Public ID"), MB_OK);
      pIXMLDOMEntity->Release();
   }
}
catch(...)
{
   if(pIXMLDOMEntity)
      pIXMLDOMEntity->Release();
   DisplayErrorToUser();
}

See Also

IXMLDOMEntity Interface


IXMLDOMEntityReference Interface

Represents an entity reference node.

Methods

This interface has no unique methods of its own but exposes the inherited methods from the IXMLDOMNode interface.

Remarks

If the XML parser expands entity references while building the structure model, no EntityReference objects appear in the tree.

XML does not require nonvalidating processors to handle external entity declarations (those made in the external subset or declared in external parameter entities). This means that parsed entities declared in the external subset need not be expanded, and therefore the replacement value of the entity might not be available. If available, the replacement value of the referenced entity appears in the child list of the EntityReference object.

The resolution of the children of the EntityReference (the replacement value of the referenced entity) can be lazily evaluated. Actions such as calling the childNodes method are assumed to trigger the evaluation. Note that character entities are expanded by the XML parser and do not appear as entity references, but instead appear within the character text in Unicode.

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

See Also

IXMLDOMDocument Interface | IXMLDOMEntity Interface


IXMLDOMImplementation Interface

The XMLDOMImplementation object provides methods that are independent of any particular instance of the document object model.

This interface inherits from IDispatch.

Methods

hasFeature Returns True if the specified version of the implementation supports the specified feature.

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

See Also

IXMLDOMDocument Interface


hasFeature Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMImplementation::hasFeature Method

Returns True if the specified version of the implementation supports the specified feature.

Visual Basic Syntax

boolVal = objXMLDOMImplementation.hasFeature(feature, version)

C++ Syntax

HRESULT hasFeature(
    BSTR feature,
    BSTR version,
    VARIANT_BOOL *hasFeature);

Parameters

feature [in]
Feature to test. In Level 1, valid feature values are "XML," "DOM," and "MS-DOM" (case insensitive).
version [in]
Version number to test, or, if Null, tests for implementation of the feature in any version. In Level 1, "1.0" is the only valid version value.
hasFeature [out]
True if the specified feature is implemented; False otherwise.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

C/C++ Example

IXMLDOMImplementation *pIXMLDOMImplementation = NULL;
VARIANT_BOOL varbFlag ;
BSTR bstrOutput = NULL;
BSTR bstrFeature = ::SysAllocString(_T("MS-DOM"));
HRESULT hr;
IXMLDOMDocument *pIXMLDOMDocument = NULL;

try
{
   // Create an instance of DOMDocument and initialize pIXMLDOMDocument.
   // Load/create an XML fragment.
   hr = pIXMLDOMDocument->get_implementation(&pIXMLDOMImplementation);

   if(SUCCEEDED(hr) && pIXMLDOMImplementation)
   {
      hr = pIXMLDOMImplementation->hasFeature(bstrFeature, _T("1.0"),                &varbFlag);
      if(varbFlag == VARIANT_TRUE )
         bstrOutput = ::SysAllocString(_T("Feature Supported"));
      else
         bstrOutput = ::SysAllocString(_T("Feature not Supported"));

      ::MessageBox(NULL, bstrOutput, bstrFeature, MB_OK);
      ::SysFreeString(bstrOutput);
      bstrOutput = NULL;
      ::SysFreeString(bstrFeature);
      bstrFeature = NULL;
      pIXMLDOMImplementation->Release();
   }
}
catch(...)
{
   if(bstrOutput)
      ::SysFreeString(bstrOutput);
   if(bstrFeature)
      ::SysFreeString(bstrFeature);
   if(pIXMLDOMImplementation)
      pIXMLDOMImplementation->Release();
   DisplayErrorToUser();
}
// Release pIXMLDOMDocument when finished using it.

See Also

IXMLDOMImplementation Interface


IXMLDOMNamedNodeMap Interface

Adds support for namespaces and iteration through the collection of attribute nodes.

This interface inherits from IDispatch.

Methods

getNamedItem | Retrieves the attribute with the specified name.
getQualifiedItem | Returns the attribute with the specified namespace and attribute name.
item | Allows random access to individual nodes within the collection.
length | Indicates the number of items in the collection.
nextNode | Returns the next node in the collection.
removeNamedItem | Removes an attribute from the collection.
removeQualifiedItem | Removes the attribute with the specified namespace and attribute name.
reset | Resets the iterator.
setNamedItem | Adds the supplied node to the collection.

Remarks

NamedNodeMap is a node collection that allows access by name as well as by index. This collection is typically used for attributes.

Note that the World Wide Web Consortium (W3C) document object model (DOM) definition does not require NamedNodeMap objects to be maintained in any particular order. Objects contained in a NamedNodeMap can also be accessed by an ordinal index, but this is simply to allow convenient enumeration and does not imply that the DOM specifies an order for these nodes. DOM implementations are not required to preserve the node order.

The Microsoft implementation preserves the attributes in the order in which they appear in the source. Additional attributes are added to the end of the list. Note that a namespace attribute (xmlns) is inserted as the first in the list if the tag name requires the namespace, or immediately before the attribute that uses an undeclared namespace.

Like the node list, however, a named node map collection is live; that is, the addition and removal of nodes, and changes within nodes, are immediately reflected in the collection. This means that two successive requests for items using the same index can return two different items, depending on changes to the collection. This also means that changes to the node objects are immediately available in the nodes obtained from the list.

Note that when a default value is specified in the schema for an attribute and that attribute is removed, it is automatically regenerated with the default value, as specified in the W3C standard. The attribute reappears immediately because the collection is live, with a corresponding change to the count of items in the collection. When removing an attribute with such a default setting, you will need to again retrieve the number of items in the collection to obtain an accurate count. You should also reset any index into the collection.

Requirements

Implementation: Msxml.dll, Msxml2.dll, Msxml3.dll

Header and IDL files: Msxml.h, Xmldom.idl

Version-Independent ProgID: MSXML2.DOMDocument, MSXML2FreeThreadedDOMDocument

Version-Dependent ProgID: MSXML2.DOMDocument.3.0, MSXML2FreeThreadedDOMDocument.3.0

See Also

IXMLDOMNode Interface


getNamedItem Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNamedNodeMap::getNamedItem Method

Retrieves the attribute with the specified name.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNamedNodeMap.getNamedItem(name)

C/C++ Syntax

HRESULT getNamedItem(
    BSTR name,
    IXMLDOMNode **namedItem);

Parameters

name [in]
Name of the attribute.
namedItem [out]
XMLDOMNode object for the specified attribute. Returns Null if the attribute node is not in this collection.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value when returning Null.
E_INVALIDARG
Value returned if namedItem is Null.

C/C++ Example

IXMLDOMNode *pIXMLDOMNode = NULL;
IXMLDOMNamedNodeMap *pIXMLDOMNamedNodeMap = NULL;
BSTR bstrAttributeName = ::SysAllocString(_T("dateModified"));
IXMLDOMElement *pIXMLDOMElement = NULL;
IXMLDOMDocument *pIXMLDOMDocument = NULL;
VARIANT varValue;

try
{
   // Create an instance of DOMDocument and initialize pIXMLDOMDocument.
   // Load/create an XML fragment.
   hr = pIXMLDOMDocument->get_documentElement(&pIXMLDOMElement);
   SUCCEEDED(hr) ? 0 : throw hr;

   if(pIXMLDOMElement)
   {
      hr = pIXMLDOMElement->get_attributes(&pIXMLDOMNamedNodeMap);
      if(SUCCEEDED(hr) && pIXMLDOMNamedNodeMap)
      {
         hr = pIXMLDOMNamedNodeMap->getNamedItem(bstrAttributeName,                      &pIXMLDOMNode);
         if(SUCCEEDED(hr) && pIXMLDOMNode)
         {
            pIXMLDOMNode->get_nodeValue(&varValue);
            ::MessageBox(NULL, _bstr_t(varValue), _T("Item Value"),                      MB_OK);
            pIXMLDOMNode->Release();
            pIXMLDOMNode = NULL;
         }
         pIXMLDOMNamedNodeMap->Release();
         pIXMLDOMNamedNodeMap = NULL;
      }
      pIXMLDOMElement->Release();
      pIXMLDOMElement = NULL;
   }
   ::SysFreeString(bstrAttributeName);
   bstrAttributeName = NULL;
}
catch(...)
{
   if(bstrAttributeName)
      ::SysFreeString(bstrAttributeName);
   if(pIXMLDOMElement)
      pIXMLDOMElement->Release();
   if(pIXMLDOMNamedNodeMap)
      pIXMLDOMNamedNodeMap->Release();
   if(pIXMLDOMNode)
      pIXMLDOMNode->Release();
   DisplayErrorToUser();
}
// Release pIXMLDOMDocument when finished with it.

See Also

IXMLDOMNamedNodeMap Interface


getQualifiedItem Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNamedNodeMap::getQualifiedItem Method

Returns the attribute with the specified namespace and attribute name.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNamedNodeMap.getQualifiedItem(baseName, namespaceURI)

C/C++ Syntax

HRESULT getQualifiedItem(
    BSTR baseName,
    BSTR namespaceURI,
    IXMLDOMNode **qualifiedItem);

Parameters

baseName [in]
Base name of the attribute, without namespace qualification.
namepaceURI [in]
Namespace prefix that qualifies the attribute name.
qualifiedItem [out]
Attribute node specified by the baseName and namespaceURI parameters. Returns Null if the attribute is not in the collection or if the item is not an attribute.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value when returning Null.
E_INVALIDARG
Value returned if qualifiedItem is Null.

C/C++ Example

IXMLDOMNode *pIXMLDOMNode = NULL;
IXMLDOMNamedNodeMap *pIXMLDOMNamedNodeMap = NULL;
BSTR bstrAttributeName = ::SysAllocString(_T("dateModified"));
IXMLDOMElement *pIXMLDOMElement = NULL;
VARIANT varValue;
IXMLDOMDocument *pIXMLDOMDocument = NULL;
HRESULT hr;

try
{
   // Create an instance of DOMDocument and initialize pIXMLDOMDocument.
   // Load/create an XML fragment.
   hr = pIXMLDOMDocument->get_documentElement(&pIXMLDOMElement);
   SUCCEEDED(hr) ? 0 : throw hr;

   if(pIXMLDOMElement)
   {
      hr = pIXMLDOMElement->get_attributes(&pIXMLDOMNamedNodeMap);
      if(SUCCEEDED(hr) && pIXMLDOMNamedNodeMap)
      {
         hr = pIXMLDOMNamedNodeMap->getQualifiedItem(bstrAttributeName,                NULL, &pIXMLDOMNode);
         if(SUCCEEDED(hr) && pIXMLDOMNode)
         {
            pIXMLDOMNode->get_nodeValue(&varValue);
            ::MessageBox(NULL, _bstr_t(varValue), _T("Item Value"),                      MB_OK);
            pIXMLDOMNode->Release();
            pIXMLDOMNode = NULL;
         }
         pIXMLDOMNamedNodeMap->Release();
         pIXMLDOMNamedNodeMap = NULL;
      }
      pIXMLDOMElement->Release();
      pIXMLDOMElement = NULL;
   }
   ::SysFreeString(bstrAttributeName);
   bstrAttributeName = NULL;
}
catch(...)
{
   if(bstrAttributeName)
      ::SysFreeString(bstrAttributeName);
   if(pIXMLDOMElement)
      pIXMLDOMElement->Release();
   if(pIXMLDOMNamedNodeMap)
      pIXMLDOMNamedNodeMap->Release();
   if(pIXMLDOMNode)
      pIXMLDOMNode->Release();
   DisplayErrorToUser();
}
// Release pIXMLDOMDocument when finished with it.

See Also

IXMLDOMNamedNodeMap Interface


item Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNamedNodeMap::item Method

Allows random access to individual nodes within the collection.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNamedNodeMap.item(index)

C/C++ Syntax

HRESULT item(
  long index,
  IXMLDOMNode **listItem);

Parameters

index [in]
Index of the item within the collection. The first item is zero.
listItem [out]
XMLDOMNode object. Returns Null if the index is out of range.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if listItem is Null.

See Also

IXMLDOMNamedNodeMap Interface


length Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNamedNodeMap::length Method

IXMLDOMNamedNodeMap::get_length Method

Indicates the number of items in the collection.

Visual Basic Syntax

lValue = oXMLDOMNamedNodeMap.length

C/C++ Syntax

HRESULT get_length(
    long *listLength);

Parameters

listLength [out]
Number of items in the collection.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if listLength is Null.

Example

IXMLDOMNode *pIXMLDOMNode = NULL;
IXMLDOMNamedNodeMap *pIXMLDOMNamedNodeMap = NULL;
IXMLDOMElement *pIXMLDOMElement = NULL;
long lLength;
TCHAR strTemp[10];
HRESULT hr;
IXMLDOMDocument *pIXMLDOMDocument = NULL;

try
{
   // Create an instance of DOMDocument and initialize pIXMLDOMDocument.
   // Load/create an XML fragment.
   hr = pIXMLDOMDocument->get_documentElement(&pIXMLDOMElement);
   SUCCEEDED(hr) ? 0 : throw hr;

   if(pIXMLDOMElement)
   {
      hr = pIXMLDOMElement->get_attributes(&pIXMLDOMNamedNodeMap);
      if(SUCCEEDED(hr) && pIXMLDOMNamedNodeMap)
      {
         hr = pIXMLDOMNamedNodeMap->get_length(&lLength);
         if(SUCCEEDED(hr))
         {
            _itot(lLength, strTemp, 10);
            ::MessageBox(NULL, strTemp, _T("Number of Attributes"),                      MB_OK);
         }
         pIXMLDOMNamedNodeMap->Release();
         pIXMLDOMNamedNodeMap = NULL;
      }
      pIXMLDOMElement->Release();
      pIXMLDOMElement = NULL;
   }
}
catch(...)
{
   if(pIXMLDOMElement)
      pIXMLDOMElement->Release();
   if(pIXMLDOMNamedNodeMap)
      pIXMLDOMNamedNodeMap->Release();
   DisplayErrorToUser();
}
// Release pIXMLDOMDocument when finished with it.

See Also

IXMLDOMNamedNodeMap Interface


nextNode Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNamedNodeMap::nextNode Method

Returns the next node in the collection.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNamedNodeMap.nextNode

C/C++ Syntax

HRESULT nextNode(
    IXMLDOMNode **nextItem);

Parameters

nextItem [out]
Next node in the collection, or Null if there is no next node.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value when returning Null.
E_INVALIDARG
Value returned if nextItem is Null.

Remarks

The iterator initially points before the first node in the list, so the first call to this method returns the first node in the list.

This method returns Null when the current node is the last node or there are no items in the list.

When the current node is removed from the list, subsequent calls to nextNode return Null. The iterator must be reset by calling the reset method.

See Also

IXMLDOMNamedNodeMap Interface


removeNamedItem Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNamedNodeMap::removeNamedItem Method

Removes an attribute from the collection.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNamedNodeMap.removeNamedItem(name)

C/C++ Syntax

HRESULT removeNamedItem(
    BSTR name,
    IXMLDOMNode **namedItem);

Parameters

name [in]
Name of the attribute to remove from the collection.
namedItem [out]
Node removed from the collection. Returns Null if the named node is not an attribute.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value when returning Null.
E_FAIL
Value returned if an error occurs.

C/C++ Example

IXMLDOMNode *pIXMLDOMNode = NULL;
IXMLDOMNamedNodeMap *pIXMLDOMNamedNodeMap = NULL;
BSTR bstrAttributeName = ::SysAllocString(_T("dateModified"));
IXMLDOMElement *pIXMLDOMElement = NULL;
VARIANT varValue;
HRESULT hr;
IXMLDOMDocument *pIXMLDOMDocument = NULL;

try
{
   // Create an instance of DOMDocument and initialize pIXMLDOMDocument.
   // Load/create an XML fragment.
   hr = pIXMLDOMDocument->get_documentElement(&pIXMLDOMElement);
   SUCCEEDED(hr) ? 0 : throw hr;

   if(pIXMLDOMElement)
   {
      hr = pIXMLDOMElement->get_attributes(&pIXMLDOMNamedNodeMap);
      if(SUCCEEDED(hr) && pIXMLDOMNamedNodeMap)
      {
         hr = pIXMLDOMNamedNodeMap->removeNamedItem(bstrAttributeName,                &pIXMLDOMNode);
         if(SUCCEEDED(hr) && pIXMLDOMNode)
         {
            pIXMLDOMNode->get_nodeValue(&varValue);
            ::MessageBox(NULL, _bstr_t(varValue), _T("Removed Item"),                   MB_OK);
            pIXMLDOMNode->Release();
            pIXMLDOMNode = NULL;
         }
         pIXMLDOMNamedNodeMap->Release();
         pIXMLDOMNamedNodeMap = NULL;
      }
      pIXMLDOMElement->Release();
      pIXMLDOMElement = NULL;
   }
   ::SysFreeString(bstrAttributeName);
   bstrAttributeName = NULL;
}
catch(...)
{
   if(bstrAttributeName)
      ::SysFreeString(bstrAttributeName);
   if(pIXMLDOMElement)
      pIXMLDOMElement->Release();
   if(pIXMLDOMNamedNodeMap)
      pIXMLDOMNamedNodeMap->Release();
   if(pIXMLDOMNode)
      pIXMLDOMNode->Release();
   DisplayErrorToUser();
}
// Release pIXMLDOMDocument when finished with it.

See Also

IXMLDOMNamedNodeMap Interface


removeQualifiedItem Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNamedNodeMap::removeQualifiedItem Method

Removes the attribute with the specified namespace and attribute name.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNamedNodeMap.removeQualifiedItem(baseName,
    namespaceURI)

C/C++ Syntax

HRESULT removeQualifiedItem(
    BSTR baseName,
    BSTR namespaceURI,
    IXMLDOMNode **qualifiedItem);

Parameters

baseName [in]
Base name of the attribute, without namespace qualification.
namespaceURI [in]
Namespace prefix that qualifies the attribute name.
qualifiedItem [out]
Attribute node removed, or Null if no node was removed.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value when returning Null.
E_FAIL
Value returned if an error occurs.

C/C++ Example

IXMLDOMNode *pIXMLDOMNode = NULL;
IXMLDOMNamedNodeMap *pIXMLDOMNamedNodeMap = NULL;
BSTR bstrAttributeName = ::SysAllocString(_T("dateModified"));
IXMLDOMElement *pIXMLDOMElement = NULL;
VARIANT varValue;
HRESULT hr;
IXMLDOMDocument *pIXMLDOMDocument = NULL;

try
{
   // Create an instance of DOMDocument and initialize pIXMLDOMDocument.
   // Load/create an XML fragment.
   hr = pIXMLDOMDocument->get_documentElement(&pIXMLDOMElement);
   SUCCEEDED(hr) ? 0 : throw hr;

   if(pIXMLDOMElement)
   {
      hr = pIXMLDOMElement->get_attributes(&pIXMLDOMNamedNodeMap);
      if(SUCCEEDED(hr) && pIXMLDOMNamedNodeMap)
      {
         hr = pIXMLDOMNamedNodeMap->removeQualifiedItem(bstrAttributeName,             NULL, &pIXMLDOMNode);
         if(SUCCEEDED(hr) && pIXMLDOMNode)
         {
            pIXMLDOMNode->get_nodeValue(&varValue);
            ::MessageBox(NULL, _bstr_t(varValue), _T("Removed Item"),                   MB_OK);
            pIXMLDOMNode->Release();
            pIXMLDOMNode = NULL;
         }
         pIXMLDOMNamedNodeMap->Release();
         pIXMLDOMNamedNodeMap = NULL;
      }
      pIXMLDOMElement->Release();
      pIXMLDOMElement = NULL;
   }
   ::SysFreeString(bstrAttributeName);
   bstrAttributeName = NULL;
}
catch(...)
{
   if(bstrAttributeName)
      ::SysFreeString(bstrAttributeName);
   if(pIXMLDOMElement)
      pIXMLDOMElement->Release();
   if(pIXMLDOMNamedNodeMap)
      pIXMLDOMNamedNodeMap->Release();
   if(pIXMLDOMNode)
      pIXMLDOMNode->Release();
   DisplayErrorToUser();
}
// Release pIXMLDOMDocument when finished with it.

See Also

IXMLDOMNamedNodeMap Interface


reset Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNamedNodeMap::reset Method

Resets the iterator.

Visual Basic Syntax

oXMLDOMNamedNodeMap.reset

C/C++ Syntax

HRESULT reset(void);

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

See Also

IXMLDOMNamedNodeMap Interface


setNamedItem Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNamedNodeMap::setNamedItem Method

Adds the supplied node to the collection.

Visual Basic Syntax

Set objXMLDOMNode = oIXMLDOMNamedNodeMap.setNamedItem(newItem)

C/C++ Syntax

HRESULT setNamedItem(
    IXMLDOMNode *newItem,
    IXMLDOMNode **nameItem);

Parameters

newItem [in]
Attribute to be added to the collection.
nameItem [out]
Attribute successfully added to the collection. If Null, no object is created.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if newItem is Null.
E_FAIL
Value returned if an error occurs.

Remarks

If an attribute already exists with the name in the supplied XMLDOMNode object, the supplied object replaces the existing attribute. The attribute name appears in its IXMLDOMNode property.

You cannot add an existing attribute to an element until you first remove it from its previous element. Also, you cannot add a namespace-qualified attribute when it uses the same prefix as another attribute with a different namespaceURI.

If the newItem node type is not NODE_ATTRIBUTE, setNamedItem returns an error. For example, it is not possible to modify entities and notations, which are read-only.

C/C++ Example

HRESULT hr;
IXMLDOMDocument *pIXMLDOMDocument = NULL;
IXMLDOMNode *pIXMLDOMNode = NULL;
IXMLDOMNamedNodeMap *pIXMLDOMNamedNodeMap = NULL;
BSTR bstrAttributeName = ::SysAllocString(_T("dateModified"));
IXMLDOMAttribute *pIXMLDOMAttribute = NULL;
IXMLDOMElement *pIXMLDOMElement = NULL;

try
{
   // Create an instance of DOMDocument and initialize pIXMLDOMDocument.
   // Load/create an XML fragment.
   hr = m_pIXMLDOMDocument->get_documentElement(&pIXMLDOMElement);
   SUCCEEDED(hr) ? 0 : throw hr;

   if(pIXMLDOMElement)
   {
      hr = pIXMLDOMElement->get_attributes(&pIXMLDOMNamedNodeMap);
      if(SUCCEEDED(hr) && pIXMLDOMNamedNodeMap)
      {
         hr = m_pIXMLDOMDocument->createAttribute(bstrAttributeName,                &pIXMLDOMAttribute);
         if(SUCCEEDED(hr) && pIXMLDOMAttribute)
         {
            hr = pIXMLDOMAttribute->put_nodeValue(_variant_t(_T("year                   2000")));
            hr = pIXMLDOMNamedNodeMap->setNamedItem(pIXMLDOMAttribute,                   &pIXMLDOMNode);
            if(SUCCEEDED(hr) && pIXMLDOMNode)
            {
               pIXMLDOMNode->Release();
               pIXMLDOMNode = NULL;
            }
            pIXMLDOMAttribute->Release();
            pIXMLDOMAttribute = NULL;
         }
         pIXMLDOMNamedNodeMap->Release();
         pIXMLDOMNamedNodeMap = NULL;
      }
      pIXMLDOMElement->Release();
      pIXMLDOMElement = NULL;
   }
   ::SysFreeString(bstrAttributeName);
   bstrAttributeName = NULL;
}
catch(...)
{
   if(bstrAttributeName)
      ::SysFreeString(bstrAttributeName);
   if(pIXMLDOMElement)
      pIXMLDOMElement->Release();
   if(pIXMLDOMNamedNodeMap)
      pIXMLDOMNamedNodeMap->Release();
   if(pIXMLDOMAttribute)
      pIXMLDOMAttribute->Release();
   if(pIXMLDOMNode)
      pIXMLDOMNode->Release();
   DisplayErrorToUser();
}
// Release pIXMLDOMDocument when finished with it.

See Also

IXMLDOMNamedNodeMap Interface


IXMLDOMNode Interface

Extends the core Node interface with support for data types, namespaces, DTDs, and schemas.

This interface inherits from IDispatch.

Methods

appendChild | Appends newChild as the last child of this node.
attributes | Contains the list of attributes for this node.
* | Returns the base name for the name qualified with the namespace.
childNodes | Contains a node list containing the children (for nodes that can have children).
cloneNode | Creates a new node that is an exact clone of this node.
dataType* | Specifies the data type for this node.
definition* | Returns the definition of the node in the DTD or schema.
firstChild | Contains the first child of this node.
hasChildNodes | Returns True if this node has children.
insertBefore | Inserts a child node to the left of the specified node or at the end of the list.
lastChild | Returns the last child node.
namespaceURI* | Returns the URI for the namespace.
nextSibling | Contains the next sibling of this node in the parent's child list.
nodeName | Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types.
nodeType | Specifies the DOM node type, which determines valid values and whether the node can have child nodes.
nodeTypeString* | Returns the node type in string form.
nodeTypedValue* | Contains this node's value, expressed in its defined data type.
nodeValue | Contains the text associated with the node; depends on the node type.
ownerDocument | Returns the root of the document that contains this node.
parentNode | Contains the parent node (for nodes that can have parents).
parsed* | Indicates whether this node and all descendants have been parsed and instantiated.
prefix* | Returns the namespace prefix.
previousSibling | Contains the left sibling of this node.
removeChild | Removes the specified child node from the list of children and returns it.
replaceChild | Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.
selectNodes* | Applies the specified pattern to this node's context and returns the list of matching nodes.
selectSingleNode* | Applies the specified pattern to this node's context and returns the first matching node.
specified* | Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema.
text* | Contains the text content of the node and its subtrees.
transformNode* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in a string.
transformNodeToObject* | Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation in the supplied object.
xml* | Contains the XML representation of the node and all its descendants.

* denotes an extension to the W3C DOM.

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

See Also

IXMLDOMNodeList Interface


appendChild Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::appendChild Method

Appends newChild as the last child of this node.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNode.appendChild(newChild)

C/C++ Syntax

HRESULT appendChild(
    IXMLDOMNode *newChild,
    IXMLDOMNode **outNewChild);

Parameters

newChild [in]
Address of the new child node to be appended to the end of the list of children of this node.
outNewChild [out]
New child node successfully appended to the list. If Null, no object is created.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if newChild is Null.
E_FAIL
Value returned if an error occurs.

Remarks

In the case in which newChild has an existing parent, the node is automatically removed from that parent before being inserted into its new location.

When moving a node, it retains its namespace information. Moving a node doesn't create a namespace declaration, but declarations are added when retrieving the XML source (via the IXMLDOMDocument::save or IXMLDOMNode::get_xml method) to ensure that all namespaces are properly declared.

When inserting a node tree under another node that has a different owner document, the ownerDocument method for each inserted node is changed to match the owner document of its new parent.

When moving a node tree to another document, the content of all entity reference nodes contained therein is updated to conform with the new document. So if the new document does not declare an entity that was moved into it, the entity reference will have no children, and the old content is removed. Existing references to nodes under the entity reference are still valid, but the node whose parent perviously was the entity reference now has a null parent.

Note that this is equivalent to calling insertBefore(newChild, null). For complete information, see the insertBefore method.

See Also

IXMLDOMNode Interface


attributes Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::attributes Method

IXMLDOMNode::get_attributes Method

Contains the list of attributes for this node.

Visual Basic Syntax

Set objXMLDOMNamedNodeMap = oXMLDOMNode.attributes

C/C++ Syntax

HRESULT get_attributes(
    IXMLDOMNamedNodeMap **attributeMap);

Parameters

attributeMap [out]
NamedNodeMap object returned for nodes that can return attributes (Element, Entity, and Notation nodes). Returns Null for all other node types. Note that for the valid node types, the NamedNodeMap is always returned; when there are no attributes on the element, the list length is set to zero.

C/C++ Return Values

S_OK
Success. Returns a non-null pointer value.
S_FALSE
Value returned if the node type does not allow attributes.
E_INVALIDARG
Value returned if attributeMap is Null.

Remarks

This value depends on the value of the nodeType property.

NODE_ATTRIBUTE, NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_DOCUMENT_TYPE, NODE_ENTITY_REFERENCE, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Always returns Null.
NODE_ELEMENT | Returns an NamedNodeMap object that contains a list of nodes corresponding to the attributes of the element.
NODE_ENTITY, NODE_NOTATION | Returns an NamedNodeMap object that exposes PUBLIC ID, SYSTEM ID, and NDATA as attributes.

See Also

IXMLDOMNode Interface


baseName Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::baseName Method

IXMLDOMNode::get_baseName Method

Returns the base name for the name qualified with the namespace.

Visual Basic Syntax

strValue = oXMLDOMNode.baseName

C/C++ Syntax

HRESULT get_baseName(
    BSTR *nameString);

Parameters

nameString [out]
Right-hand side of a namespace qualified name. For example, it returns yyy for the element <xxx:yyy>. It always returns a non-empty string.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when there is no base name.
E_INVALIDARG
Value returned if nameString is Null.

Remarks

This member is an extension of the W3C DOM.

See Also

IXMLDOMNode Interface


childNodes Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::childNodes Method

IXMLDOMNode::get_childNodes Method

Returns a node list containing the children (for nodes that can have children).

Visual Basic Syntax

objXMLDOMNodeList = oXMLDOMNode.childNodes

C/C++ Syntax

HRESULT get_childNodes(
    IXMLDOMNodeList **childList);

Parameters

childList [out]
A list of children in the current node.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if childList is Null.

Remarks

Object. The collection is read-only. Note that an XMLDOMNodeList object is returned even if there are no children of the node. In such a case, the length of the list will be set to 0. For information about valid child node types for each node, see the DOMNodeType enumeration.

This value depends on the value of the nodeType property.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_ELEMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE | Returns an XMLDOMNodeList that contains a list of all child nodes for the specified node.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns an XMLDOMNodeList with a length of 0. These node types cannot have children.
NODE_DOCUMENT_TYPE | Returns an XMLDOMNodeList that contains a list of all child nodes for the XMLDOMDocumentType node. The node list for the document type node can contain entities and notations.

See Also

IXMLDOMNode Interface


cloneNode Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::cloneNode Method

Creates a new node that is an exact clone of this node.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNode.cloneNode(deep)

C/C++ Syntax

HRESULT cloneNode(
    VARIANT_BOOL deep,
    IXMLDOMNode **cloneRoot);

Parameters

deep [in]
Flag that indicates whether to recursively clone all nodes that are descendants of this node. If True, create a clone of the complete tree below this node. If False, clone this node and its attributes only.
cloneRoot [out]
Newly created clone node.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if cloneRoot is Null.

Remarks

The cloned node has the same property values as this node for the following properties: nodeName, nodeValue, nodeType, parentNode, ownerDocument, and (if it is an element) attributes. The value of the clone's childNodes collection depends on the setting of the deep parameter flag.

Note If the node is the IXMLDOMDocument node, it is safer to clone the document using the save method, as follows:

doc.save(doc2);

See Also

IXMLDOMNode Interface


dataType Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::dataType Method

IXMLDOMNode::get_dataType Method

Specifies the data type for this node.

Visual Basic Syntax

strValue = oXMLDOMNode.dataType

C/C++ Syntax

HRESULT get_dataType(
    VARIANT *dataTypeName);

Parameters

dataTypeName [out]
VARIANT can be a BSTR that contains the data type name, or VT_NULL if no data type is defined.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if the node is not typed.
E_INVALIDARG
Value returned if dataTypeName is Null.

IXMLDOMNode::put_dataType Method

Specifies the data type for this node.

Visual Basic Syntax

objXMLDOMNode.dataType = strValue

C/C++ Syntax

HRESULT put_dataType(
    VARIANT *dataTypeName);

Parameters

dataTypeName [out]
VARIANT can be a BSTR that contains the data type name, or VT_NULL if no data type is defined.

C/C++ Return Values

S_OK
Value returned if successful.
E_FAIL
Value returned if an error occurs.

Remarks

This value depends on the value of the IXMLDOMNode interface's nodeType method.

NODE_ATTRIBUTE | Contains the string representation of the data type specifier included in the schema. Null if no data type is specified.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_DOCUMENT_TYPE, NODE_ENTITY, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Contains the constant string value "string". These node types do not have data types, so the dataType method cannot be set for these node types. Attempts to set this method are ignored.
NODE_ELEMENT | Contains the string representation of the data type specifier included on the instance of the element or in the schema. Null if no data type is specified.
NODE_ENTITY_REFERENCE | Contains the string representation of the data type specifier for the referenced entity. This is limited to entities with a single data type on the root level element of the subtree corresponding to the entity. Contains Null if no data type was specified. It is not possible to set the data type of an entity reference. The data type must be set directly on the referenced element. Attempts to set the value of the dataType method are ignored.

This method represents a convenient alternative way to obtain the data type specified in this node's dt:dt attribute.

Changes to the data type also cause updates to the nodeTypedValue method.

See Also

IXMLDOMNode Interface


definition Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::definition Method

IXMLDOMNode::get_definition Method

Returns the definition of the node in the DTD or schema.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNode.definition

C/C++ Syntax

HRESULT get_definition(
    IXMLDOMNode **definitionNode);

Parameters

definitionNode [out]
Node that contains the definition for the entity referenced.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when no definition is found.
E_INVALIDARG
Value returned if definitionNode is Null.

Remarks

This value depends on the value of the nodeType method.

The definition method is not supported when using in-line schemas. This behavior is by design because the node being referenced may be deleted or modified after the document is loaded, which would yield unpredictable results.

NODE_ENTITY_REFERENCE | Returns the node for the entity referenced; that is, returns the ENTITY element that was defined for a given ENTITYREF. Given the ENTITYREF &x;, for example, the definition method returns the node in the DOCTYPE node's entity collection that defines the corresponding ENTITY: <!ENTITY x "y">.
NODE_ENTITY | For unparsed entities, returns the NOTATION definition from the DOCTYPE node's notation collection. For example, given the ENTITY <!ENTITY networth SYSTEM "networth.xls" NDATA XLS>, the definition method returns the node corresponding to the notation <!NOTATION XLS PUBLIC "http://www.microsoft.com/office/excel/">. For parsed entities, returns Null.
NODE_ATTRIBUTE | Returns the XML-Data Schema AttributeType for a given ATTRIBUTE node. For example, the definition method for the attribute myAttribute="123" returns the node corresponding to the element <AttributeType name="myAttribute"> in the schema. Returns Null when a DTD is used or when no schema is present.
NODE_ELEMENT | Returns the XML-Data Schema ElementType for a given ELEMENT node. For example, given the element <myelement>, the definition method returns the node <ElementType name="myelement"> in the specified external schema. Returns Null when a DTD is used or when no schema is present.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_DOCUMENT_TYPE, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns Null.

This member is an extension of the W3C DOM.

Example

The following example demonstrates retrieval of the definition method from an XMLDOMElement:

BOOL DOMNodeGetDefinition()
{
   BOOL bResult = FALSE;
   BSTR bstrValue;
   IXMLDOMElement *pIXMLDOMElement = NULL;
   IXMLDOMNode *pIXMLDOMNode = NULL;
   IXMLDOMDocument2 *pIXMLDOMDocument = NULL;
   HRESULT hr;

   try
   {
      // Create the document and initialize pIXMLDOMDocument.
      // Load the document (sample.xml).
      // Get the documents root.
      hr = pIXMLDOMDocument->get_documentElement(&pIXMLDOMElement);
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMElement)
      {
         hr = pIXMLDOMElement->get_definition(&pIXMLDOMNode);
         if(SUCCEEDED(hr) && pIXMLDOMNode)
         {
            hr = pIXMLDOMNode->get_xml(&bstrValue);
            if(SUCCEEDED(hr) && bstrValue)
            {
               ::MessageBox(NULL, bstrValue, _T("Definition"), MB_OK);
               ::SysFreeString(bstrValue);
               bResult = TRUE;
            }
            RELEASE(pIXMLDOMNode);
         }
         RELEASE(pIXMLDOMElement);
      }
   }
   catch(...)
   {
      if(bstrValue)
         ::SysFreeString(bstrValue);
      CHECK_AND_RELEASE(pIXMLDOMNode);
      CHECK_AND_RELEASE(pIXMLDOMElement);
      // Release the DOMDocument.
      DisplayErrorToUser();
   }
   // Release the document when finished with it.
   return bResult;
}

Sample.xml

<?xml version="1.0"?>
<SomeDoc xmlns="x-schema:collschema.xml">
   <SomeElement Id="first1">
   </SomeElement>
</SomeDoc>

collschema.xml

<?xml version='1.0'?>
<Schema xmlns="urn:schemas-microsoft-com:xml-data"
       xmlns:dt="urn:schemas-microsoft-com:datatypes">
   <AttributeType name='Id' dt:type="id"/>
   <ElementType name='SomeElement' content="mixed" model="open">
      <attribute type='Id'/>
   </ElementType>

   <ElementType name='SomeDoc' content="mixed" model="open">
      <attribute type='Id'/>
      <element type='SomeElement' minOccurs='1' maxOccurs='*'/>
   </ElementType>
</Schema>

Output (in a message box)

<ElementType name='SomeDoc' content="mixed" model="open">
   <attribute type='Id'/>
   <element type='SomeElement' minOccurs='1' maxOccurs='*'/>
</ElementType>

See Also

IXMLDOMNode Interface


firstChild Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::firstChild Method

IXMLDOMNode::get_firstChild Method

Contains the first child of this node.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNode.firstChild

C/C++ Syntax

HRESULT get_firstChild(
    IXMLDOMNode **firstChild);

Parameters

firstChild [out]
First child node. If there are no such children, it returns Null.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when there are no children.
E_INVALIDARG
Value returned if firstChild is Null.

Remarks

This value depends on the value of the nodeType method.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_DOCUMENT_TYPE, NODE_ELEMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE | Returns the first child node. If there are no children, returns Null.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns Null. These node types cannot have children.

See Also

IXMLDOMNode Interface


hasChildNodes Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::hasChildNodes Method

Returns True if this node has children.

Visual Basic Syntax

boolValue = oXMLDOMNode.hasChildNodes

C/C++ Syntax

HRESULT hasChildNodes(
    VARIANT_BOOL *hasChild);

Parameters

hasChild [out]
True is returned if this node has children.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when there are no children.
E_INVALIDARG
Value returned if hasChild is Null.

Remarks

This method offers a fast way to determine whether a node has children. It always returns False for nodes that, by definition, cannot have children: the XMLDOMCDATASection, XMLDOMComment, XMLDOMNotation, XMLDOMProcessingInstruction, and XMLDOMText nodes.

See Also

IXMLDOMNode Interface


insertBefore Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::insertBefore Method

Inserts a child node to the left of the specified node or at the end of the list.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNode.insertBefore(newChild, refChild)

C/C++ Syntax

HRESULT insertBefore(
    IXMLDOMNode *newChild,
    VARIANT refChild,
    IXMLDOMNode **outNewChild);

Parameters

newChild [in]
Address of the new node to be inserted.
refChild [in]
Address of the reference node; newChild is inserted to the left of refChild. If Null, newChild is inserted at the end of the child list.
outNewChild [out]
On success, the child node that was inserted. If Null, no object is created.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if newChild is Null.
E_FAIL
Value returned if an error occurs.

Remarks

When moving a node, it retains its namespace information. Moving a node doesn't create a namespace declaration, but declarations are added when retrieving the XML source (via the IXMLDOMDocument::save or IXMLDOMNode::get_xml method) to ensure that all namespaces are properly declared.

When inserting a node tree under another node that has a different owner document, the ownerDocument method for each inserted node is changed to match the owner document of its new parent.

When moving a node tree to another document, the content of all entity reference nodes contained therein is updated to conform with the new document. So if the new document does not declare an entity that was moved into it, the entity reference will have no children, and the old content is removed. Existing references to nodes under the entity reference are still valid, but the node whose parent previously was the entity reference now has a Null parent.

This operation depends on the value of the nodeType property.

NODE_ATTRIBUTE

This operation depends on the value of the newChild parameter.

NODE_ATTRIBUTE, NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_TYPE, NODE_ELEMENT, NODE_ENTITY, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION | Returns an error. These node types cannot be children of an attribute.
NODE_DOCUMENT_FRAGMENT | Inserts the children of the document fragment (newChild) and returns newChild.
NODE_ENTITY_REFERENCE, NODE_TEXT | Inserts newChild and returns newChild.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_ENTITY, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns an error. These node types either cannot have children or their children are read-only.

NODE_DOCUMENT

This operation depends on the value of the newChild parameter.

NODE_ATTRIBUTE, NODE_CDATA_SECTION, NODE_DOCUMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE, NODE_NOTATION, NODE_TEXT | Returns an error. These nodes are not valid as children of a document node.
NODE_COMMENT, NODE_PROCESSING_INSTRUCTION | Inserts newChild and returns newChild.
NODE_ELEMENT | Inserts newChild and returns newChild. Note, however, that by definition an XML document (the document node) can have only a single child. Therefore an error is returned if the document node already has a child.
NODE_DOCUMENT_FRAGMENT | Inserts the children of the document fragment (in newChild) and returns newChild. Note that the insert operations are subject to the rules for child nodes and can fail if the document fragment children represent node types that cannot be inserted.
NODE_DOCUMENT_TYPE | Returns an error. The document type is read-only.

NODE_DOCUMENT_FRAGMENT

This operation depends on the value of the newChild parameter.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_TYPE | Returns an error. These nodes types are not valid as children of a document fragment.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_ELEMENT, NODE_ENTITY_REFERENCE, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Inserts newChild and returns newChild.
NODE_DOCUMENT_FRAGMENT | Inserts the children of the document fragment (newChild) and returns newChild.
NODE_ENTITY, NODE_NOTATION | Returns an error. Entities and notations are read-only and cannot be inserted into a document.

NODE_ELEMENT

This operation depends on the value of the newChild parameter.

NODE_CDATA_SECTION, NODE_COMMENT, NODE_ELEMENT, NODE_ENTITY_REFERENCE, NODE_TEXT, NODE_PROCESSING_INSTRUCTION | Inserts newChild and returns newChild.
NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_TYPE, NODE_ENTITY, NODE_NOTATION | Returns an error. These node types cannot be children of an element node.
NODE_DOCUMENT_FRAGMENT | Inserts the children of the document fragment (newChild) and returns newChild.
NODE_ENTITY_REFERENCE | Returns an error. Although the child nodes of an entity reference are the expanded entity, the children cannot be modified.

The node supplied in refChild must be a child node of this node or Null. The newChild is inserted before refChild (as the left sibling) in the child list. If refChild is Null, newChild is inserted at the end of the child list. If refChild is not a child of this node, an error is returned.

If newChild is a node of type DOCUMENT_FRAGMENT, all its children are inserted, in the same order, before refChild. If newChild is already in the tree, it is first removed.

These parameters can represent nodes in the same document or in different documents. When in the same document, the nodes retain their default attributes and data types. When the parameters represent nodes in different documents, the nodes either lose or alter their default attributes, depending on whether this node's document has a DTD or other schema. The Microsoft implementation attempts to correctly merge the different DTDs.

If the newChild node's DTD or schema differs from the original document, the nodes will be treated with the definitions in the new DTD, including default attributes and data types. If there is no DTD, the nodes keep their data types by picking up an instance definition (attributes lose their data type), and default attributes are lost. Note also that cutting and pasting between documents with two different DTDs can result in an invalid document that can fail to parse after being saved.

When elements are added as children to other elements, they introduce their own namespace scoping. That is, if the element is from a default namespace, the default has scope beginning with the element being added.

If an entity reference is being added as a child to an element or document fragment, the namespace declaration of the entity reference is added to the containing element. Similarly, if the entity reference is being added to an attribute, the namespace and the prefix of the entity reference are added to the element containing the attribute.

If there is a conflict between prefixes on the containing element and the attribute prefixes being added, the insertBefore operation fails and returns an error. For example, a conflict occurs when a new attribute referring to a different namespace is added to an element with the following namespace:

xmlns:myns="URN1"

The error could result from the new attribute, in which "myns" refers to a different namespace, "URN2", that would result from a call to createNode ("attribute","myns:myname","URN2").

myns:myname="myattributevalue"

Note that namespace conflict errors occur only when setting attributes. Inserted child elements do not cause a namespace conflict.

When adding a document fragment, the containing element adds all namespaces and prefixes from the document fragment. If this causes a conflict on the containing element, insertBefore returns an error.

See Also

IXMLDOMNode Interface


lastChild Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::lastChild Method

IXMLDOMNode::get_lastChild Method

Returns the last child node.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNode.lastChild

C/C++ Syntax

HRESULT get_lastChild(
    IXMLDOMNode **lastChild);

Parameters

lastChild [out]
Last child node. If there are no children, it returns Null.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when there are no children.
E_INVALIDARG
Value returned if lastChild is null.

Remarks

This value depends on the value of the nodeType method.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_DOCUMENT_TYPE, NODE_ELEMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE | Returns the last child node. If there are no children, returns Null.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns Null. These node types cannot have children.

See Also

IXMLDOMNode Interface


namespaceURI Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::namespaceURI Method

IXMLDOMNode::get_namespaceURI Method

Returns the URI (universal resource identifier) for the namespace.

Visual Basic Syntax

strValue = oXMLDOMNode.namespaceURI

C/C++ Syntax

HRESULT get_namespaceURI(
    BSTR *namespaceURI);

Parameters

namespaceURI [out]
URI for the namespace. This refers to the "uuu" portion of the namespace declaration xmlns:nnn="uuu".

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when there is no namespace.
E_INVALIDARG
Value returned if namespaceURI is Null.

Remarks

This member is an extension of the W3C DOM.

See Also

IXMLDOMNode Interface


nextSibling Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::nextSibling Method

IXMLDOMNode::get_nextSibling Method

Contains the next sibling of this node in the parent's child list.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNode.nextSibling

C/C++ Syntax

HRESULT get_nextSibling(
    IXMLDOMNode **nextSibling);

Parameters

nextSibling [out]
Right sibling of this node.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when there is no next sibling.
E_INVALIDARG
Value returned if nextSibling is Null.

Remarks

This value depends on the value of the nodeType method.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT | Always returns Null; these node types do not appear as children of any other nodes.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT_TYPE, NODE_ELEMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns the node immediately following this node in the list of children of this node's parent. If no such node exists, returns Null.

See Also

IXMLDOMNode Interface


nodeName Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::nodeName Method

IXMLDOMNode::get_nodeName Method

Contains the qualified name of the element, attribute, or entity reference, or a fixed string for other node types.

Visual Basic Syntax

strValue = oXMLDOMNode.nodeName

C/C++ Syntax

HRESULT get_nodeName(
    BSTR *name);

Parameters

name [out]
Node name, which varies depending on the node type.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if name is Null.

Remarks

This value depends on the value of the nodeType method.

NODE_ATTRIBUTE | The name of the attribute.
NODE_CDATA_SECTION | The literal string "#cdata-section".
NODE_COMMENT | The literal string "#comment".
NODE_DOCUMENT | The literal string "#document".
NODE_DOCUMENT_TYPE | The name of the document type. For example, xxx in <!DOCTYPE xxx ...>.
NODE_DOCUMENT_FRAGMENT | The literal string "#document-fragment".
NODE_ELEMENT | The name of the XML tag, with any namespace prefix included if present.
NODE_ENTITY | The name of the entity.
NODE_ENTITY_REFERENCE | The name of the entity referenced. Note that the name does not include the leading ampersand or the trailing semicolon. The name includes the namespace if one is present.
NODE_NOTATION | The name of the notation.
NODE_PROCESSING_INSTRUCTION | The target; the first token following the <? characters.
NODE_TEXT | The literal string "#text".

This method returns the qualified name for the element, attribute, or entity reference. For example, it returns xxx:yyy for the element <xxx:yyy>. The nodeName method always returns a non-empty string.

See Also

IXMLDOMNode Interface


nodeType Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::nodeType Method

IXMLDOMNode::get_nodeType Method

Specifies the DOM node type, which determines valid values and whether the node can have child nodes.

Visual Basic Syntax

lEnum = oXMLDOMNode.nodeType

C/C++ Syntax

HRESULT get_nodeType(
    DOMNodeType *nodeType);

Parameters

nodeType [out]
Type of the node.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if nodeType is Null.

Remarks

The DOMNodeType enumeration defines the following valid values for the nodeType method:

NODE_ELEMENT (1) | The node represents an element. An element node can have the following child node types: Element, Text, Comment, ProcessingInstruction, CDATASection, and EntityReference. An element node can be the child of the Document, DocumentFragment, EntityReference, and Element nodes.
NODE_ATTRIBUTE (2) | The node represents an attribute of an element. An attribute node can have the following child node types: Text and EntityReference. An attribute does not appear as the child node of any other node type; note that it is not considered a child node of an element.
NODE_TEXT (3) | The node represents the text content of a tag. A text node cannot have any child nodes. A text node can appear as the child node of the Attribute, DocumentFragment, Element, and EntityReference nodes.
NODE_CDATA_SECTION (4) | The node represents a CDATA section in the XML source. CDATA sections are used to escape blocks of text that would otherwise be recognized as markup. A CDATA section node cannot have any child nodes. A CDATA section node can appear as the child of the DocumentFragment, EntityReference, and Element nodes.
NODE_ENTITY_REFERENCE (5) | The node represents a reference to an entity in the XML document. This applies to all entities, including character entity references. An entity reference node can have the following child node types: Element, ProcessingInstruction, Comment, Text, CDATASection, and EntityReference. An entity reference node can appear as the child of the Attribute, DocumentFragment, Element, and EntityReference nodes.
NODE_ENTITY (6) | The node represents an expanded entity. An entity node can have child nodes that represent the expanded entity (for example, Text and EntityReference nodes). An entity node can appear as the child of the DocumentType node.
NODE_PROCESSING_INSTRUCTION (7) | The node represents a processing instruction (PI) from the XML document. A PI node cannot have any child nodes. A PI node can appear as the child of the Document, DocumentFragment, Element, and EntityReference nodes.
NODE_COMMENT (8) | The node represents a comment in the XML document. A comment node cannot have any child nodes. A comment node can appear as the child of Document, DocumentFragment, Element, and EntityReference nodes.
NODE_DOCUMENT (9) | The node represents a document object, which, as the root of the document tree, provides access to the entire XML document. It is created using the ProgID "Microsoft.XMLDOM", or through a data island using <XML> or <SCRIPT LANGUAGE=XML>. A document node can have the following child node types: Element (maximum of one), ProcessingInstruction, Comment, and DocumentType. A document node cannot appear as the child of any node types.
NODE_DOCUMENT_TYPE (10) | The node represents the document type declaration, indicated by the <!DOCTYPE > tag. A document type node can have the following child node types: Notation and Entity. A document type node can appear as the child of the Document node.
NODE_DOCUMENT_FRAGMENT (11) | The node represents a document fragment. A document fragment node associates a node or subtree with a document without actually being contained within the document. A document fragment node can have the following child node types: Element, ProcessingInstruction, Comment, Text, CDATASection, and EntityReference. A DocumentFragment node cannot appear as the child of any node types.
NODE_NOTATION (12) | A node represents a notation in the document type declaration. A notation node cannot have any child nodes. A notation node can appear as the child of the DocumentType node.

See Also

IXMLDOMNode Interface


nodeTypedValue Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::nodeTypedValue Method

IXMLDOMNode::get_nodeTypedValue Method

Contains this node's value, expressed in its defined data type.

Visual Basic Syntax

strValue = oXMLDOMNode.nodeTypedValue

C/C++ Syntax

HRESULT get_nodeTypedValue(
    VARIANT *typedValue);

Parameters

typedValue [out]
Value of this node in its defined data type, if such a data type is defined. If this node has no content, the value of typedValue is Null.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if typedValue is Null.

Data Type Mapping

VARIANT returned | Corresponding element data type
VT_BSTR | string
VT_BSTR | number
VT_I4 | int
VT_CY | fixed.14.4
VT_BOOL | boolean
VT_DATE | dateTime
VT_DATE | dateTime.tz
VT_DATE | date
VT_DATE | time
VT_DATE | time.tz
VT_I1 | i1 byte
VT_I2 | i2
VT_I4 | i4, int
VT_UI1 | ui1
VT_UI2 | ui2
VT_UI4 | ui4
VT_FLOAT | r4
VT_DOUBLE | r8, float
VT_BSTR | uuid
VT_ARRAY | bin.hex
VT_ARRAY | bin.base64

IXMLDOMNode::put_nodeTypedValue Method

Contains this node's value, expressed in its defined data type.

Visual Basic Syntax

objXMLDOMNode.nodeTypedValue = strValue

C/C++ Syntax

HRESULT put_nodeTypedValue(
    VARIANT typedValue);

Parameters

typedValue [out]
Value of this node in its defined data type, if such a data type is defined.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if typedValue is Null.
E_FAIL
Value returned if an error occurs.

Remarks

This value depends on the value of the IXMLDOMNode interface's nodeType method.

NODE_ATTRIBUTE | Returns the value in the data representation of the type specified in the schema for the attribute. (Data types are specified using the dt:dt attribute, and because attributes cannot be applied to attributes, the attribute's data type can be specified only in the schema.) If no type is specified, it returns a string identical to the nodeValue method. If the new value is not of the type specified in the schema or cannot be coerced to the type specified in the schema, an error occurs.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_DOCUMENT_TYPE, NODE_ENTITY, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION | Returns a string that is equal to the nodeValue method. These node types do not have data types.
NODE_ELEMENT | Returns the value in the data representation of the type specified on the element instance (using the dt:dt attribute) or specified in the schema. If no type is specified on the instance or in the schema, it returns a string identical to the nodeValue method. If the element has an attribute data type, it returns Null.
NODE_ENTITY_REFERENCE | Returns the value in the data representation of the type specified for the referenced entity. This is limited to entities with a single data type on the root level element of the subtree corresponding to the entity. If no data type is specified, it returns a string identical to the nodeValue method.
NODE_TEXT | Returns the value in the data representation of the type of the containing element. If the containing element is not typed, it returns a string identical to the nodeValue method.

The data type for this node is defined in its dataType method. This method also depends on the value of the node type, in the nodeType method.

Although you can explicitly set the nodeTypedValue method with a particular data type in the VARIANT, the set operation does not change the data type. The value in the VARIANT is coerced to the data type specified in the dataType method, and the set operation returns an error if the type coercion is not possible.

Changes to this method are also immediately available from the nodeValue method.

See Also

IXMLDOMNode Interface


nodeTypeString Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::nodeTypeString Method

IXMLDOMNode::get_nodeTypeString Method

Returns the node type in string form.

Visual Basic Syntax

strValue = oXMLDOMNode.nodeTypeString

C/C++ Syntax

HRESULT get_nodeTypeString(
    BSTR *nodeType);

Parameters

nodeType [out]
String version of the node type.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if nodeType is Null.

Remarks

This value depends on the value of the nodeType method.

NODE_ATTRIBUTE | attribute
NODE_CDATA_SECTION | cdatasection
NODE_COMMENT | comment
NODE_DOCUMENT | document
NODE_DOCUMENT_FRAGMENT | documentfragment
NODE_DOCUMENT_TYPE | documenttype
NODE_ELEMENT | element
NODE_ENTITY | entity
NODE_ENTITY_REFERENCE | entityreference
NODE_NOTATION | notation
NODE_PROCESSING_INSTRUCTION | processinginstruction
NODE_TEXT | text

This member is an extension of the W3C DOM.

See Also

IXMLDOMNode Interface


nodeValue Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::nodeValue Method

IXMLDOMNode::get_nodeValue Method

Contains the text associated with the node.

Visual Basic Syntax

strValue = oXMLDOMNode.nodeValue

C/C++ Syntax

HRESULT get_nodeValue(
    VARIANT *value);

Parameters

value [out]
Node value; depends on the nodeType method.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value when returning Null.
E_INVALIDARG
Value returned if value is Null.
E_FAIL
Value returned if an error occurs.

IXMLDOMNode::put_nodeValue Method

Contains the text associated with the node.

Visual Basic Syntax

objXMLDOMNode.nodeValue = strValue

C/C++ Syntax

HRESULT put_nodeValue(
    VARIANT value);

Parameters

value [in]
Node value; depends on the nodeType method.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

This value depends on the value of the nodeType method.

NODE_ATTRIBUTE | A string representing the value of the attribute. For attributes with subnodes, this is the concatenated text of all subnodes with entities expanded. Setting this value deletes all children of the node and replaces them with a single text node containing the value written.
NODE_CDATA_SECTION | A string representing the text stored in the CDATA section.
NODE_COMMENT | The content of the comment, exclusive of the comment start and end sequence.
NODE_DOCUMENT, NODE_DOCUMENT_TYPE, NODE_DOCUMENT_FRAGMENT, NODE_ELEMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE, NODE_NOTATION | Null. Note also that attempting to set the value of the node returns an error.
NODE_PROCESSING_INSTRUCTION | The content of the processing instruction, excluding the target. (The target appears in the nodeName method.)
NODE_TEXT | A string representing the text stored in the text node.

The new value is also available from the nodeType method.

See Also

IXMLDOMNode Interface


ownerDocument Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::ownerDocument Method

IXMLDOMNode::get_ownerDocument Method

Returns the root of the document that contains this node.

Visual Basic Syntax

Set objXMLDOMDocument = oXMLDOMNode.ownerDocument

C/C++ Syntax

HRESULT get_ownerDocument(
    IXMLDOMDocument **DOMDocument);

Parameters

DOMDocument [out]
Address of the parent document object that represents the root of the document.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if DOMDocument is Null.

Remarks

This method indicates the document to which this node belongs.

All nodes are created in the context of a document, and the ownerDocument method is maintained until the node is added to another document. For a node removed from a document, this method indicates the document in which the node was last included.

See Also

IXMLDOMNode Interface


parentNode Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::parentNode Method

IXMLDOMNode::get_parentNode Method

Contains the parent node (for nodes that can have parents).

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNode.parentNode

C/C++ Syntax

HRESULT get_parentNode(
    IXMLDOMNode **parent);

Parameters

parent [out]
Parent of the given node instance. All nodes except Document, DocumentFragment, and Attribute nodes can have a parent. However, if a node has just been created and not yet added to the tree, or if it has been removed from the tree, the parent is Null.

C/C++ Return Values

S_FALSE
Value returned if there is no parent.
S_OK
Value returned if successful.
E_INVALIDARG
Value returned if parent is Null.

Remarks

This value depends on the value of the nodeType method.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT | Null; they do not have parents.
NODE_CDATA_SECTION | The element or entity reference containing the CDATA section.
NODE_COMMENT | The element, entity reference, document type, or document containing the comment.
NODE_DOCUMENT_TYPE | The document node.
NODE_ELEMENT | The parent node of the element. If the element is the root node in the tree, the parent is the document node. If the node is the document node, parentNode is Null.
NODE_ENTITY | The document type node.
NODE_ENTITY_REFERENCE | The element, attribute, or entity reference containing the entity reference.
NODE_NOTATION | The document type node.
NODE_PROCESSING_INSTRUCTION | The document, element, document type, or entity reference containing the processing instruction.
NODE_TEXT | The parent element, attribute, or entity reference.

See Also

IXMLDOMNode Interface


parsed Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::parsed Method

Indicates whether this node and all descendants have been parsed and instantiated.

Visual Basic Syntax

boolValue = oXMLDOMNode.parsed

C/C++ Syntax

HRESULT parsed(
    VARIANT_BOOL *isParsed);

Parameters

isParsed [out]
True if this node and all descendants have been parsed; False if any descendants remain to be parsed.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if isParsed is Null.

Remarks

During asynchronous access, not all of the document tree may be available. Before performing some operations, such as XSL transformations and pattern-matching operations, it is useful to know whether the entire tree below this node is available for processing.

See Also

IXMLDOMNode Interface


prefix Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::prefix Method

IXMLDOMNode::get_prefix Method

Returns the namespace prefix.

Visual Basic Syntax

strValue = oXMLDOMNode.prefix

C/C++ Syntax

HRESULT get_prefix(
    BSTR *prefixString);

Parameters

prefixString [out]
Namespace prefix specified on the element, attribute, or entity reference. For example, for the element <xxx:yyy>, it returns xxx. It returns an empty string, "", if no prefix is specified.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when there is no prefix.
E_INVALIDARG
Value returned if prefixString is Null.

See Also

IXMLDOMNode Interface


previousSibling Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::previousSibling Method

IXMLDOMNode::get_previousSibling Method

Contains the left sibling of this node.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNode.previousSibling

C/C++ Syntax

HRESULT get_previousSibling(
    IXMLDOMNode **previousSibling);

Parameters

previousSibling [out]
Left sibling of this node.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when there is no previous sibling.
E_INVALIDARG
Value returned if previousSibling is Null.

Remarks

This value depends on the value of the nodeType method.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT | Always returns Null; these node types do not appear as children of any other nodes.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT_TYPE, NODE_ELEMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns the node immediately preceding this node in this node's parent's child list. Returns Null if no such node exists.

See Also

IXMLDOMNode Interface


removeChild Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::removeChild Method

Removes the specified child node from the list of children and returns it.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNode.removeChild(childNode)

C/C++ Syntax

HRESULT removeChild(
    IXMLDOMNode *oldChild,
    IXMLDOMNode **outOldChild);

Parameters

oldChild [in]
Child node to be removed from the list of children of this node.
outOldChild [out]
Removed child node. If Null, no object is created.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if oldChild is not a child of this node, when the specified oldChild is read-only and cannot be removed, or when oldChild is Null.
E_FAIL
Value returned if an error occurs.

See Also

IXMLDOMNode Interface


replaceChild Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::replaceChild Method

Replaces the specified old child node with the supplied new child node in the set of children of this node, and returns the old child node.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNode.replaceChild(newChild, oldChild)

C/C++ Syntax

HRESULT replaceChild(
    IXMLDOMNode *newChild,
    IXMLDOMNode *oldChild,
    IXMLDOMNode **outOldChild);

Parameters

newChild [in]
Address of the new child that is to replace the old child. If Null, oldChild is removed without a replacement.
oldChild [in]
Address of the old child that is to be replaced by the new child.
outOldChild [out]
Old child that is replaced. If Null, no object is created.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if the newChild node cannot be inserted as a child of this node, when the specified oldChild is not a child of this node, or if oldChild is null.
E_FAIL
Value returned if an error occurs.

Remarks

In the case where newChild has an existing parent, the node is automatically removed from that parent before being inserted into its new location.

When moving a node, it retains its namespace information. Moving a node doesn't create a namespace declaration, but declarations are added when retrieving the XML source (via the IXMLDOMDocument::save or IXMLDOMNode::get_xml method) to ensure that all namespaces are properly declared.

When inserting a node tree under another node that has a different owner document, the ownerDocument method for each inserted node is changed to match the owner document of its new parent.

When moving a node tree to another document, the content of all entity reference nodes contained therein is updated to conform with the new document. So if the new document does not declare an entity that was moved into it, the entity reference will have no children, and the old content is removed. Existing references to nodes under the entity reference are still valid, but the node whose parent previously was the entity reference now has a Null parent.

This operation depends on the value of the nodeType method.

NODE_ATTRIBUTE

This operation depends on the value of the newChild parameter.

NODE_ATTRIBUTE, NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_TYPE, NODE_ELEMENT, NODE_ENTITY, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION | Returns an error. These node types cannot be children of an attribute.
NODE_DOCUMENT_FRAGMENT | Replaces oldChild with the children of the document fragment in newChild and returns oldChild.
NODE_ENTITY_REFERENCE, NODE_TEXT | Replaces the specified oldChild with the supplied newChild and returns oldChild.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_ENTITY, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns an error. These node types either cannot have children or their children are read-only.

NODE_DOCUMENT

This operation depends on the value of the newChild parameter.

NODE_ATTRIBUTE, NODE_CDATA_SECTION, NODE_DOCUMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE, NODE_NOTATION, NODE_TEXT | Returns an error. These nodes are not valid as children of a document node.
NODE_COMMENT, NODE_PROCESSING_INSTRUCTION | Replaces the specified oldChild with the supplied newChild and returns oldChild.
NODE_DOCUMENT_TYPE, NODE_ELEMENT | Replaces oldChild with newChild and returns oldChild. Note, however, that by definition an XML document (the document node) can have only a single child. Therefore, an error is returned if the document node already has a child.
NODE_DOCUMENT_FRAGMENT | Replaces the specified oldChild with the children of the document fragment (newChild) and returns oldChild. Note that the insert operations are subject to the rules for child nodes and can fail if the document fragment children represent node types that cannot be inserted.
NODE_DOCUMENT_TYPE | Returns an error. The document type is read-only.

NODE_DOCUMENT_FRAGMENT

This operation depends on the value of the newChild parameter.

NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_TYPE | Returns an error. These node types are not valid as children of a document fragment.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_ELEMENT, NODE_ENTITY_REFERENCE, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Replaces the specified oldChild with the supplied newChild and returns oldChild.
NODE_DOCUMENT_FRAGMENT | Replaces the specified oldChild with the children of the document fragment (newChild) and returns oldChild. Note that the insert operations are subject to the rules for child nodes and can fail if the document fragment children represent node types that cannot be inserted.
NODE_ENTITY, NODE_NOTATION | Returns an error. Entities and notations are read-only and cannot be inserted into a document.

NODE_ELEMENT

This operation depends on the value of the newChild parameter.

NODE_CDATA_SECTION, NODE_COMMENT, NODE_ELEMENT, NODE_ENTITY_REFERENCE, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Replaces the specified oldChild with newChild and returns oldChild.
NODE_ATTRIBUTE, NODE_DOCUMENT, NODE_DOCUMENT_TYPE, NODE_ENTITY, NODE_NOTATION | Returns an error. These node types cannot be children of an element node.
NODE_DOCUMENT_FRAGMENT | Replaces the specified oldChild with the children of the document fragment (newChild) and returns oldChild. Note that the insert operations are subject to the rules for child nodes and can fail if the document fragment children represent node types that cannot be inserted.
NODE_ENTITY_REFERENCE | Returns an error. Although the child nodes of an entity reference are the expanded entity, the children cannot be modified.

See Also

IXMLDOMNode Interface


selectNodes Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::selectNodes Method

Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes.

Visual Basic Syntax

Set objXMLDOMNodeList = oXMLDOMNode.selectNodes(patternString)

C/C++ Syntax

HRESULT selectNodes(
    BSTR patternString,
    IXMLDOMNodeList **resultList);

Parameters

patternString [in]
XSL or XPath expression that is to be applied to the context defined by this node. Whether an XSL or XPath query is used is determined by the value of the SelectionLanguage property. By default, the expression is an XSL pattern query. The SelectionLanguage property can be set with the SetProperty method.
resultList [out]
List of nodes selected by the XSL or XPath query. If no nodes are selected by the query, an empty node list is returned.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if resultList is Null.

Remarks

For a complete description of XSL Syntax, see XSL Pattern Syntax.

For a complete description of XPath syntax, see the XPath Syntax reference.

The selectSingleNode method is much like the selectNodes method but returns only the first matching node rather than the list of all matching nodes.

The IXMLDOMNodeList is live and immediately reflects changes to the nodes that appear in the list.

See Also

IXMLDOMNode Interface | Using the XSLT Processor | Using Collections


selectSingleNode Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::selectSingleNode Method

Applies the specified pattern to this node's context and returns the first matching node.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNode.selectSingleNode(patternString)

C/C++ Syntax

HRESULT selectSingleNode(
    BSTR patternString,
    IXMLDOMNode **resultNode);

Parameters

patternString [in]
String specifying the XSL or XPath query that is to be applied to the context defined by this node. Whether an XSL or XPath query is used is determined by the value of the SelectionLanguage property. By default, the expression is an XSL pattern query. The SelectionLanguage property can be set with the SetProperty method.
resultNode [out]
First node that is selected by the XPath query. If no nodes result from the query, it returns Null.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if there is no match.
E_INVALIDARG
Value returned if resultNode is Null.

Remarks

For a complete description of XPath, see the XPath Syntax reference.

The selectSingleNode method is much like the selectNodes method but returns only the first matching node rather than the list of all matching nodes.

See Also

IXMLDOMNode Interface | Using the XSLT Processor


specified Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::specified Method

IXMLDOMNode::get_specified Method

Indicates whether the node (usually an attribute) is explicitly specified or derived from a default value in the DTD or schema.

Visual Basic Syntax

boolValue = oXMLDOMNode.specified

C/C++ Syntax

HRESULT get_specified(
    VARIANT_BOOL *isSpecified);

Parameters

isSpecified [out]
True if the attribute is explicitly specified in the element; False if the attribute value comes from the DTD or schema.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if isSpecified is Null.

Remarks

This value depends on the value of the nodeType method.

NODE_ATTRIBUTE | Returns True if the attribute is specified directly on the element. Returns False for default attributes.
NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_DOCUMENT_FRAGMENT, NODE_DOCUMENT_TYPE, NODE_ELEMENT, NODE_ENTITY, NODE_ENTITY_REFERENCE, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns True.

See Also

IXMLDOMNode Interface


text Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::text Method

IXMLDOMNode::get_text Method

Returns the string representing the text contents of this node or the concatenated text representing this node and its descendants.

Visual Basic Syntax

strValue = oXMLDOMNode.text

C/C++ Syntax

HRESULT get_text(
    BSTR *text);

Parameters

text
String representing the text content of this node and its descendants. This value varies depending on the value of the nodeType method (see below).

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned when there is no text.
E_INVALIDARG
Value returned if text is Null.

Remarks

When concatenated, the text represents the contents of text or CDATA nodes. All concatenated text nodes are normalized according to xml:space attributes and the value of the preserveWhiteSpace switch. Concatenated CDATA text is not normalized. (Child nodes that contain NODE_COMMENT and NODE_PROCESSING_INSTRUCTION nodes are not concatenated.)

Note that if you need more precise control over text manipulation in an XML document, you may want to use the lower-level nodeValue method, which returns the raw text associated with a NODE_TEXT node.

IXMLDOMNode::put_text Method

Sets the text contents of this node or the concatenated text representing this node and its descendants.

Visual Basic Syntax

objXMLDOMNode.text = strValue

C/C++ Syntax

HRESULT put_text(
    BSTR *text);

Parameters

text
String representing the text content of this node and its descendants. This value varies depending on the value of the nodeType method (see below).

C/C++ Return Values

S_OK
Value returned if successful.
E_FAIL
Value returned if an error occurs.

Remarks

When concatenated, the text represents the contents of text or CDATA nodes. All concatenated text nodes are normalized according to xml:space attributes and the value of the preserveWhitespace switch. Concatenated CDATA text is not normalized. (Child nodes that contain NODE_COMMENT and NODE_PROCESSING_INSTRUCTION nodes are not concatenated.)

Note that if you need more precise control over text manipulation in an XML document, you may want to use the lower-level nodeValue method, which returns the raw text associated with a NODE_TEXT node.

Consider the "root" element in this example:

<root att="   123   a   <   ">
   <a>   a   a   </a>
   <!--   comment   b   -->
   <?pi   pi   c   ?>
   <![CDATA[   cdata   d   ]]>
   e   f
</root>

The text method for the root element returns the concatenated text:

"a a   cdata   d    e f"

Note that the white space within the CDATA node is preserved.

This value depends on the value of the nodeType method.

NODE_ATTRIBUTE,
NODE_DOCUMENT,
NODE_ENTITY 		Returns a string representing the value of the node. This is the concatenated text of all subnodes with entities expanded.
NODE_CDATA_SECTION,
NODE_COMMENT,
NODE_PROCESSING_INSTRUCTION,
NODE_TEXT 		Returns the text contained in the node, which is the same as the nodeValue method.
NODE_DOCUMENT_TYPE,
NODE_NOTATION 		Returns the empty string, "". These node types do not have associated text.
NODE_DOCUMENT_FRAGMENT | Returns the text comprised of the concatenation of all descendant nodes.
NODE_ELEMENT Contains a string that represents the element content. Note that this will also include the text content from all child elements, concatenated in document order. For example, consider the following XML:
<count>
  <item>one</item>
  <item>two</item>
  <item>three</item>
  <item>four</item>
</count>

The text method for the "count" element contains the value "onetwothreefour".
NODE_ENTITY_REFERENCE | Returns the string representation of the entity reference.

Setting the text method is allowed only on nodes that allow write operations. It is not allowed on read-only nodes, such as NODE_DOCUMENT_FRAGMENT, NODE_DOCUMENT_TYPE, NODE_ENTITY, NODE_ENTITY_REFERENCE, and NODE_NOTATION.

Note that when writing this method, the supplied value applies to all nodes beneath it in the tree. Setting the text method has the result of deleting the nodes containing substructure and inserting a single new child text node with the supplied text. For example, consider the following XML:

<count>
  <item>one</item>
  <item>two</item>
  <item>three</item>
  <item>four</item>
</count>

Immediately after setting the text method of the count element node to zero, the XML method for that node contains the new value:

<count>zero</count>
</pre>

See Also

IXMLDOMNode Interface | Handling White Space


transformNode Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::transformNode Method

Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.

Visual Basic Syntax

strValue = oXMLDOMNode.transformNode(objStylesheet)

C/C++ Syntax

HRESULT transformNode(
    IXMLDOMNode *stylesheet,
    BSTR *xmlString);

Parameters

stylesheet [in]
Valid XML document or DOM node that consists of XSLT elements that direct the transformation of this node.
xmlString [out]
String that contains the product of the transformation of this XML document based on the XSLT style sheet.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if stylesheet or xmlString is Null.

Remarks

The stylesheet parameter must be either an IXMLDOMDocument node, in which case the document is assumed to be an XSL style sheet, or a DOM node in the XSL style sheet, in which case this node is treated as a stand-alone style sheet fragment.

The source node defines a context for the style sheet to operate on, but navigation outside this scope is allowed. For instance, a style sheet could use the id function to access other parts of the document.

This method supports both stand-alone and embedded style sheets and also provides the ability to run a localized style sheet fragment against a particular source node.

For more information about XSLT style sheets, see the XSLT Reference.

See Also

IXMLDOMNode Interface | Using the XSLT Processor


transformNodeToObject Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::transformNodeToObject Method

Processes this node and its children using the supplied XSL style sheet and returns the resulting transformation.

Visual Basic Syntax

oXMLDOMNode.transformNodeToObject(stylesheet, outputObject)

C/C++ Syntax

HRESULT transformNodeToObject(
    IXMLDOMNode *stylesheet,
    VARIANT outputObject);

Parameters

stylesheet [in]
Valid XML document or DOM node that consists of XSL elements that direct the transformation of this node.
outputObject [in]
Object that contains the product of the transformation of this XML document based on the XSLT style sheet. If the variant represents the DOMDocument object, the document is built according to its properties and its child nodes are replaced during this transformation process. If the variant contains an IStream interface, the XML transformation is sent to this stream.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if stylesheet or outputObject is Null.

Remarks

The stylesheet parameter must be either an IXMLDOMDocument node, in which case the document is assumed to be an XSL style sheet, or a DOM node in the XSL style sheet, in which case this node is treated as a stand-alone style sheet fragment.

The source node defines a context for the stylesheet to operate on, but navigation outside this scope is allowed. For instance, a stylesheet could use the id Function to access other parts of the document.

This method supports both stand-alone and embedded style sheets and also provides the ability to run a localized style sheet fragment against a particular source node.

The transformNodeToObject method always generates a Unicode byte-order mark, which means it cannot be used in conjunction with other ASP Response.Write or Response.BinaryWrite calls.

For more information about XSLT, see the XSLT Reference.

Example

This example sets up the new DOMDocument object and puts it into a VARIANT before making the call to transformNodeToObject.

// p is the XML source that is to be transformed, pXSL is the style sheet.
// Create an empty DOM document for the result. (Error checking omitted
// for brevity.)
hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
    IID_IXMLDOMDocument, (void**)&pDoc);
hr = pDoc->QueryInterface(IID_IDispatch, (void **)&pDisp);
vObject.vt = VT_DISPATCH;   // the new object
vObject.pdispVal = pDisp;
hr = p->transformNodeToObject(pXSL, vObject);   // Transformation is
                                                // present in pDoc.

See Also

IXMLDOMNode Interface | Using the XSLT Processor


xml Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNode::xml Method

IXMLDOMNode::get_xml Method

Contains the XML representation of the node and all its descendants.

Visual Basic Syntax

strValue = oXMLDOMNode.xml

C/C++ Syntax

HRESULT get_xml(
    BSTR *xmlString);

Parameters

xmlString [in, out]
XML representation of this node and all its descendants.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if xmlString is Null.

Remarks

Note that the xml method always returns a Unicode string (BSTR). That is, the xml method for the document object converts the document from its original encoding into Unicode. As a result, the original encoding attribute is removed. For example,

<?xml version="1.0" encoding="UTF-8"?>

appears in the xml method as

<?xml version="1.0"?>

If the original encoding wasn't removed by the xml method, the following line would return an error indicating the parser cannot switch from Unicode to UTF-8 encoding:

xmlDoc.loadxml(xmlDoc.xml);

If you use IXMLDOMDocument::save(), the original encoding will be preserved. So if you load a document in Windows-1252 encoding, it will be saved in that format, and so on.

This value depends on the value of the nodeType method.

NODE_ATTRIBUTE, NODE_CDATA_SECTION, NODE_COMMENT, NODE_DOCUMENT, NODE_ELEMENT, NODE_ENTITY, NODE_NOTATION, NODE_PROCESSING_INSTRUCTION, NODE_TEXT | Returns the string representation of the node and all its descendants.
NODE_DOCUMENT_FRAGMENT | Returns the string representation of all descendant nodes of the document fragment.
NODE_DOCUMENT_TYPE | Returns the string representation of the <!DOCTYPE ...> declaration, including the internal subset if specified.
NODE_ENTITY_REFERENCE | Returns the string representation of the entity reference. Although the entity reference has children, they are not returned.

When manipulating a document, it is possible to create a document that lacks the appropriate namespace declarations to be reparsed, or to have declarations that would cause some prefixed names to be interpreted incorrectly. When saving an XML representation of DOM nodes, the Microsoft XML Parser (MSXML) attempts to insert appropriate namespace declarations to preserve the original namespace URI for each element and its attributes.

See Also

IXMLDOMNode Interface | Handling White Space | XML DOM Persistence


IXMLDOMNodeList Interface

Supports iteration through the live collection, in addition to indexed access.

This interface inherits from IDispatch.

Methods

item | Allows random access to individual nodes within the collection.
length | Indicates the number of items in the collection.
nextNode | Returns the next node in the collection.
reset | Resets the iterator.

Remarks

A NodeList collection is live; that is, the addition and removal of nodes, and changes within nodes, are immediately reflected in the collection. This means that two successive requests for items using the same index can return two different items, depending on changes to the collection. This also means that changes to the node objects are immediately available in the nodes obtained from the list. The collection can also be accessed using the "for...next" construct.

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

See Also

IXMLDOMNode Interface


item Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNodeList::item Method

Allows random access to individual nodes within the collection.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNodeList.item(index)

C/C++ Syntax

HRESULT item(
  long index,
  IXMLDOMNode **listItem);

Parameters

index [in]
Index of the item within the collection. The first item is number zero.
listItem [out]
XMLDOMNode object. Returns Null if the index is out of range.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if listItem is Null.

See Also

IXMLDOMNodeList Interface


length Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNodeList::length Method

IXMLDOMNodeList::get_length Method

Indicates the number of items in the collection.

Visual Basic Syntax

lValue = oXMLDOMNodeList.length

C/C++ Syntax

HRESULT get_length(
    long *listLength);

Parameters

listLength [out]
Number of items in the collection.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if listLength is Null.

See Also

IXMLDOMNodeList Interface | IXMLDOMSelection Interface


nextNode Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNodeList::nextNode Method

Returns the next node in the collection.

Visual Basic Syntax

Set objXMLDOMNode = oXMLDOMNodeList.nextNode

C/C++ Syntax

HRESULT nextNode(
    IXMLDOMNode **nextItem);

Parameters

nextItem [out]
Next node in the collection, or Null if there is no next node.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if nextItem is Null.

Remarks

The iterator initially points before the first node in the list, so the first call to nextNode returns the first node in the list.

This method returns Null when the current node is the last node or there are no items in the list.

When the current node is removed from the list, subsequent calls to nextNode return Null. The iterator must be reset by calling the reset method.

See Also

IXMLDOMNodeList Interface


reset Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNodeList::reset Method

Resets the iterator.

Visual Basic Syntax

oXMLDOMNodeList.reset

C/C++ Syntax

HRESULT reset(void);

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

This method reinitializes the iterator to point before the first node in the IXMLDOMNodeList, so that the next call to nextNode returns the first item in the list.

See Also

IXMLDOMNodeList Interface


IXMLDOMNotation Interface

Contains a notation declared in the DTD or schema.

This interface inherits from IXMLDOMNode.

Methods

publicId | Contains the public identifier for the notation.
systemId | Contains the system identifier for the notation.

Remarks

Notations identify by name the format of unparsed entities, the format of elements that bear a notation attribute, or the application to which a processing instruction is addressed.

The nodeName method contains the notation name.

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

See Also

IXMLDOMNode Interface


publicId Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNotation::publicId Method

IXMLDOMNotation::get_publicId Method

Contains the public identifier for the notation.

Visual Basic Syntax

strValue = oXMLDOMNotation.publicId

C/C++ Syntax

HRESULT get_publicId(
    VARIANT *publicID);

Parameters

publicID [out]
Public identifier for the notation. If the public identifier is not specified, this contains an empty string.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if publicID is Null.

See Also

IXMLDOMNotation Interface


systemId Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMNotation::systemId Method

IXMLDOMNotation::get_systemId Method

Contains the system identifier for the notation.

Visual Basic Syntax

strValue = oXMLDOMNotation.systemId

C/C++ Syntax

HRESULT get_systemId(
    VARIANT *systemID);

Parameters

systemID [out]
System identifier for the notation. If the system identifier is not specified, this contains an empty string.

C/C++ Return Values

S_OK
Value returned if successful.
E_INVALIDARG
Value returned if systemID is Null.

See Also

IXMLDOMNotation Interface


IXMLDOMParseError Interface

Returns detailed information about the last parse error, including the error number, line number, character position, and a text description.

This interface inherits from IDispatch.

Methods

errorCode | Returns the error code of the last parse error.
filepos | Contains the absolute file position where the error occurred.
line | Specifies the line number that contains the error.
linepos | Contains the character position within the line where the error occurred.
reason | Explains the reason for the error.
srcText | Returns the text of the line containing the error.
url | Contains the URL of the XML document containing the last error.

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl


errorCode Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMParseError::errorCode Method

IXMLDOMParseError::get_errorCode Method

Returns the error code of the last parse error.

Visual Basic Syntax

lValue = oXMLDOMParseError.errorCode

C/C++ Syntax

HRESULT get_errorCode(
    long *errorCode);

Parameters

errorCode [out]
Returns the error code in a decimal value.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if no error occurred.

See Also

IXMLDOMParseError Interface


filepos Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMParseError::filepos Method

IXMLDOMParseError::get_filepos Method

Contains the absolute file position where the error occurred.

Visual Basic Syntax

lValue = oXMLDOMParseError.filepos

C/C++ Syntax

HRESULT get_filepos(
    long *filePosition);

Parameters

filePosition [out]
Absolute file position where the error occurred.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if no error occurred.

See Also

IXMLDOMParseError Interface


line Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMParseError::line Method

IXMLDOMParseError::get_line Method

Specifies the line number that contains the error.

Visual Basic Syntax

lValue = oXMLDOMParseError.line

C/C++ Syntax

HRESULT get_line(
    long *lineNumber);

Parameters

lineNumber [out]
Line number where the error occurred.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if no error occurred.

See Also

IXMLDOMParseError Interface


linepos Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMParseError::linepos Method

IXMLDOMParseError::get_linepos Method

Contains the character position within the line where the error occurred.

Visual Basic Syntax

lValue = oXMLDOMParseError.linepos

C/C++ Syntax

HRESULT get_linepos(
    long *linePosition);

Parameters

linePosition [out]
Character position within the line where the error occurred.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if no error occurred.

See Also

IXMLDOMParseError Interface


reason Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMParseError::reason Method

IXMLDOMParseError::get_reason Method

Explains the reason for the error.

Visual Basic Syntax

strValue = oXMLDOMParseError.reason

C/C++ Syntax

HRESULT get_reason(
    BSTR *reasonString);

Parameters

reasonString [out]
Reason for the error. This displays a description of the error, the line number, character position, and source text of the error. Validation errors also include the URL of the schema and the node within the schema that corresponds to the error.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if no error occurred.

See Also

IXMLDOMParseError Interface


srcText Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMParseError::srcText Method

IXMLDOMParseError::get_srcText Method

Returns the text of the line containing the error.

Visual Basic Syntax

strValue = oXMLDOMParseError.srcText

C/C++ Syntax

HRESULT get_srcText(
    BSTR *sourceString);

Parameters

sourceString [out]
Full text of the line containing the error. This returns an empty string if the error is caused when the XML is not well-formed and the error cannot be assigned to a specific line.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if no error occurred.

See Also

IXMLDOMParseError Interface


url Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMParseError::url Method

IXMLDOMParseError::get_url Method

Contains the URL of the XML document containing the last error.

Visual Basic Syntax

strValue = oXMLDOMParseError.url

C/C++ Syntax

HRESULT get_url(
    BSTR *urlString);

Parameters

urlString [out]
URL of the XML file containing the error.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value returned if no error occurred.

See Also

IXMLDOMParseError Interface


IXMLDOMProcessingInstruction Interface

Represents a processing instruction, which XML defines to keep processor-specific information in the text of the document.

This interface inherits from IXMLDOMNode.

Methods

data | Stores the content of the processing instruction, excluding the target.
target | Specifies the target—the application to which this processing instruction is directed.

Remarks

The content of the ProcessingInstruction node is the entire content between the delimiters of the processing instruction (PI).

The content of this node is usually subdivided into the target (the application to which this processing instruction is directed) and the content of the processing instruction. The target consists of the first token following the start of the tag, while the content of the PI refers to the text that extends from the first nonwhite-space character after the target through the character immediately preceding the ?>, which signifies the end of the tag.

Example

BOOL BuildDynamicXMLwithProcessingInstruction()
{
   BOOL bResult = FALSE;
   IXMLDOMDocument *pIXMLDOMDocument=NULL;
   IXMLDOMElement *pIXMLDOMElement=NULL;
   IXMLDOMProcessingInstruction *pIXMLDOMProcessingInstruction=NULL;
   IXMLDOMNode *pIXMLDOMNode = NULL;
   HRESULT hr ;
   BSTR bstrValue ;

   try
   {
      hr=CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_SERVER,                   IID_IXMLDOMDocument2, (LPVOID*)(&pIXMLDOMDocument));
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMDocument)
      {
         hr=pIXMLDOMDocument->createElement(_T("Node1"), &pIXMLDOMElement);
         if(SUCCEEDED(hr) && pIXMLDOMElement)
         {
            hr=pIXMLDOMElement->put_text(_T("test"));
            if(SUCCEEDED(hr))
            {
               hr=pIXMLDOMDocument->createProcessingInstruction(_T("xml"),                   _T("version='1.0'"), &pIXMLDOMProcessingInstruction);
               if(SUCCEEDED(hr) && pIXMLDOMProcessingInstruction)
               {
                  pIXMLDOMDocument->appendChild(
                              pIXMLDOMProcessingInstruction, &pIXMLDOMNode);
               pIXMLDOMDocument->putref_documentElement(pIXMLDOMElement);
               hr=pIXMLDOMDocument->get_xml(&bstrValue);
               if(SUCCEEDED(hr))
               {
               ::MessageBox(NULL, bstrValue, _T("Loaded Doc"), MB_OK);
                  bResult=TRUE;
                  }
                  CHECK_AND_RELEASE(pIXMLDOMNode);
                  RELEASE(pIXMLDOMProcessingInstruction);
               }
            }
            RELEASE(pIXMLDOMElement);
         }
         RELEASE(pIXMLDOMDocument);
      }
   }
   catch(...)
   {
      CHECK_AND_RELEASE(pIXMLDOMElement);
      CHECK_AND_RELEASE(pIXMLDOMDocument);
      CHECK_AND_RELEASE(pIXMLDOMNode);
      CHECK_AND_RELEASE(pIXMLDOMProcessingInstruction);
      DisplayErrorToUser();
   }
   return bResult;
}

Output (in a messagebox)

<?xml version="1.0"?>
<Node1>test</Node1>

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

See Also

IXMLDOMDocument Interface


data Method

Microsoft XML SDK 2.6 - XML Reference

IXSLProcessingInstruction::data Method

IXMLDOMProcessingInstruction::get_data Method

Gets the content of the processing instruction, excluding the target.

Visual Basic Syntax

strValue = oXMLDOMProcessingInstruction.data

C/C++ Syntax

HRESULT get_data(
    BSTR *value);

Parameters

value [out]
Content of the processing instruction for this target. The data has the same value as the nodeValue property.

IXMLDOMProcessingInstruction::put_data Method

Sets the content of the processing instruction, excluding the target.

Visual Basic Syntax

objXMLDOMProcessingInstruction.data = strValue

C/C++ Syntax

HRESULT put_data(
    BSTR value);

Parameters

value [in]
Content of the processing instruction for this target. The data has the same value as the nodeValue property.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

See Also

IXMLDOMProcessingInstruction Interface


target Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMProcessingInstruction::target Method

Specifies the target—the application to which this processing instruction is directed.

strValue = oXMLDOMProcessingInstruction.target

Remarks

String. The property is read-only. XML defines the target as the first token following the markup that begins the processing instruction. For example, the target has the value "xml" in the processing instruction <?xml version="1.0">.

The target method has the same value as the nodeName method.

Example

The following VBScript example iterates through the document's child nodes. If it finds a node of type NODE_PROCESSING_INSTRUCTION (7), it displays the node's target:

Dim xmlDoc
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.load("c:\books.xml")
For i = 0 TO (xmlDoc.childNodes.length -1)
  If xmlDoc.childNodes.item(i).nodeType = 7 Then
    msgBox xmlDoc.childNodes.item(i).target
  End If
Next

See Also

IXMLDOMProcessingInstruction Interface

IXMLDOMProcessingInstruction::get_target Method

Specifies the target—the application to which this processing instruction is directed.

Visual Basic Syntax

strValue = oXMLDOMProcessingInstruction.target

C/C++ Syntax

HRESULT get_target(
    BSTR *name);

Parameters

name [out]
Application to which this processing instruction is directed (the target).

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

XML defines the target as the first token following the markup that begins the processing instruction. For example, the target has the value "xml" in the processing instruction <?xml version="1.0">.

The target method has the same value as the nodeName method.

See Also

IXMLDOMProcessingInstruction Interface


IXMLDOMSchemaCollection Interface

Used by the schemas and namespaces properties on IXMLDOMDocument2.

Methods

add | Adds a new item to a schema collection.
addCollection | Adds all the schemas from another collection into the current collection.
get | Returns a read-only DOM node containing the <Schema> element.
length | Returns the number of namespaces currently in a collection.
namespaceURI | Returns the namespace at the specified index.
remove | Removes the specified namespaces from a collection.

Remarks

The SchemaCollection interface is free-threaded and can be used in multiple documents at the same time. A single schema document can belong to multiple schema collections and will remain read-only until it is removed from all collections. Schema documents belonging to a collection must be free-threaded.

The IXMLDOMSchemaCollection implementation in Msxml 2.6 and 3.0 also supports the following interface via QueryInterface:

Interface | Usage
IID_EnumVARIANT | Returns an IEnumVARIANT implementation so the collection can be used in Visual Basic For…Each statements. Enumerates the namespace URIs.

Return Values

E_FAIL
Attempted to modify a read-only object; argument is not a valid schema; invalid namespace URI; or document is not ready.
E_INVALIDARG
The parameter is incorrect.
E_OUTOFMEMORY
Out of memory.
E_POINTER
Invalid pointer.

Example

BOOL DOMDocument2nameSpaces()
{
   BOOL bResult = FALSE;
   short sResult = FALSE;
   IXMLDOMElement *pIXMLDOMElement=NULL;
   IXMLDOMSchemaCollection *pIXMLDOMSchemaCollection=NULL;
   IXMLDOMDocument2 *pIXMLDOMDocument2=NULL;
   HRESULT hr;
   BSTR bstrValue;

   try
   {
      hr=CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_SERVER,
         IID_IXMLDOMDocument2, (LPVOID*)(&pIXMLDOMDocument2));
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMDocument2)
      {
         hr=pIXMLDOMDocument2->put_async(VARIANT_FALSE);
         if(SUCCEEDED(hr))
         {
            hr=pIXMLDOMDocument2->load(_variant_t(
               _T("d:\\inetpub\\wwwroot\\samplexmldtd.xml")), &sResult);
            if(SUCCEEDED(hr) && (sResult==VARIANT_TRUE))
            {
               hr=pIXMLDOMDocument2->get_namespaces(
                  &pIXMLDOMSchemaCollection);
               if(SUCCEEDED(hr))
               {
                  LONG uLength;

                  bResult=TRUE;
                  hr=pIXMLDOMSchemaCollection->get_length(&uLength);
                  if(SUCCEEDED(hr))
                  {
                     for(int iIndex=0; iIndex < uLength; iIndex++)
                     {
                        hr=pIXMLDOMSchemaCollection->get_namespaceURI(
                           iIndex, &bstrValue);
                        if(SUCCEEDED(hr))
                           ::MessageBox(NULL, bstrValue, _T("Namespace"),
                           MB_OK);
                     }
                  }
               }
            }
         }
         RELEASE(pIXMLDOMDocument2);
      }
   }
   catch(...)
   {
      CHECK_AND_RELEASE(pIXMLDOMDocument2);
      DisplayErrorToUser();
   }
   return bResult;
}

d:\\inetpub\\wwwroot\\samplexmldtd.xml

<?xml version='1.0'?>
<COLLECTION xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <DATE dt:dt="datetime">1998-10-13T15:56:00</DATE>
  <BOOK>
    <TITLE>Cosmos</TITLE>
    <AUTHOR>Carl Sagan</AUTHOR>
    <PUBLISHER>Ballantine Books</PUBLISHER>
  </BOOK>
</COLLECTION>

Output (in a message box)

urn:schemas-microsoft-com:datatypes

Requirements

Implementation: Msxml2.dll

Header and IDL files: Msxml.h, Xmldom.idl

Version-Independent ProgID: Msxml2.XMLSchemaCache

Version-Independent CLSID: 373984C9-B845-449B-91E7-45AC83036ADE

Version-Dependent ProgID: Msxml2.XMLSchemaCache.2.6

Version-Dependent CLSID: f5078f1d-c551-11d3-89b9-0000f81fe221


add Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMSchemaCollection::add Method

Adds a new schema to the schema collection, associating the given namespace URI with the specified schema.

Visual Basic Syntax

objXMLDOMSchemaCol.add(namespaceURI, var)

C/C++ Syntax

HRESULT add(BSTR namespaceURI, VARIANT var);

Parameters

namespaceURI [in]
This is the namespace to associate with the specified schema.

The empty string, "", will associate the schema with the empty namespace, xmlns="". This may be any string that can be used in an xmlns attribute, but it cannot contain entity references. The same white space normalization that occurs on the xmlns attribute also occurs on this parameter (that is, leading and trailing white space is trimmed, new lines are converted to spaces, and multiple adjacent white space characters are collapsed into one space).

var [in]
This specifies the schema. It can be a BSTR, in which case it points to the URL to load. It will load it synchronously and with resolveExternals=false and validateOnParse=false. var can also be any IXMLDOMDocument.

This argument can be Null, which results in the removal of any schema for the specified namespaces. If the schema is an IXMLDOMNode, the entire document the node belongs to will be preserved.

C/C++ Return Values

If this call fails, the collection remains unchanged. E_FAIL is returned if:

  • the collection is read-only
  • the document is not a recognized schema
  • an error occurs when compiling the schema
  • the ready state of the document is not 4.

If it was loading a schema and encountered a parse error, then the parse error reason is returned in the IErrorInfo. E_INVALIDARG is returned if the VARIANT argument contains an invalid value.

Remarks

Schemas referenced by this schema are not added to the collection. The contents of the schema are added to the internal collection (the schema may be copied). If a schema is already in the collection with this namespace, it will be replaced with the new one being added.

Example

BOOL DOMDocument2schemas()
{
   BOOL bResult = FALSE;
   short sResult = FALSE;
   IXMLDOMNodeList *pIXMLDOMNodeList=NULL;
   IXMLDOMDocument2 *pIXMLDOMDocument2=NULL;
   IXMLDOMSchemaCollection *pIXMLDOMSchemaCollection=NULL;
   HRESULT hr;
   BSTR bstrValue;
   VARIANT varValue;

   try
   {
      hr=CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_SERVER,
         IID_IXMLDOMDocument2, (LPVOID*)(&pIXMLDOMDocument2));
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMDocument2)
      {
         hr=pIXMLDOMDocument2->put_async(VARIANT_FALSE);
         if(SUCCEEDED(hr))
         {
            hr=CoCreateInstance(CLSID_XMLSchemaCache, NULL, CLSCTX_SERVER,
               IID_IXMLDOMSchemaCollection,
               (LPVOID*)(&pIXMLDOMSchemaCollection));
            SUCCEEDED(hr) ? 0 : throw hr;

            if(SUCCEEDED(hr) && pIXMLDOMSchemaCollection)
            {
               hr = pIXMLDOMSchemaCollection->add(_T("x-schema:books"),
                  _variant_t(_T("d:\\inetpub\\wwwroot\\bookschema.xml")));
               if(SUCCEEDED(hr))
               {
                  varValue.vt = VT_DISPATCH;
                  varValue.pdispVal = pIXMLDOMSchemaCollection;
                  hr=pIXMLDOMDocument2->putref_schemas(varValue);

               // The document will load only if a valid schema is
               // attached to the xml file.
                  hr=pIXMLDOMDocument2->load(_variant_t(
                  _T("d:\\inetpub\\wwwroot\\samplexmldtd.xml")),
                  &sResult);
                  if(SUCCEEDED(hr) && (sResult==VARIANT_TRUE))
                  {
                     pIXMLDOMDocument2->get_xml(&bstrValue);
                     ::MessageBox(NULL, bstrValue, _T("Loaded Document"),
                        MB_OK);
                     bResult=TRUE;
                  }
               }
               RELEASE(pIXMLDOMSchemaCollection);
            }
         }
         RELEASE(pIXMLDOMDocument2);
      }
   }
   catch(...)
   {
      CHECK_AND_RELEASE(pIXMLDOMDocument2);
      CHECK_AND_RELEASE(pIXMLDOMNodeList);
      CHECK_AND_RELEASE(pIXMLDOMSchemaCollection);
      DisplayErrorToUser();
   }
   return bResult;
}

d:\\inetpub\\wwwroot\\samplexmldtd.xml

<?xml version='1.0'?>
<COLLECTION xmlns="x-schema:books"
   xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <DATE dt:dt="datetime">1998-10-13T15:56:00</DATE>
  <BOOK>
    <TITLE>Cosmos</TITLE>
    <AUTHOR>Carl Sagan</AUTHOR>
    <PUBLISHER>Ballantine Books</PUBLISHER>
  </BOOK>
  <BOOK>
    <TITLE>Catwings</TITLE>
    <AUTHOR>Ursula K. Le Guin</AUTHOR>
    <PUBLISHER>Scholastic</PUBLISHER>
  </BOOK>
  <BOOK>
    <TITLE>Home Town</TITLE>
    <AUTHOR>Tracy Kidder</AUTHOR>
    <PUBLISHER>Random House</PUBLISHER>
  </BOOK>
</COLLECTION>

d:\\inetpub\\wwwroot\\bookschema.xml

<?xml version="1.0"?>
<Schema xmlns="urn:schemas-microsoft-com:xml-data">
   <ElementType name="TITLE" />
   <ElementType name="AUTHOR" />
   <ElementType name="PUBLISHER" />
   <ElementType name="DATE" />
   <ElementType name="BOOK" model="closed">
      <element type="TITLE" />
      <element type="AUTHOR" />
      <element type="PUBLISHER" />
   </ElementType>
   <ElementType name="COLLECTION" model="closed">
      <element type="BOOK" />
   </ElementType>
</Schema>

See Also

IXMLDOMSchemaCollection Interface


addCollection Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMSchemaCollection::addCollection Method

Adds all the schemas from another collection into this collection, replacing any schemas that collide on the same namespace URI.

Visual Basic Syntax

objXMLDOMSchemaCol.addCollection(objXMLDOMSchemaCollection)

C/C++ Syntax

HRESULT addCollection(IXMLDOMSchemaCollection* pCollection);

Parameters

pCollection [in]
The collection containing the schemas to add.

C/C++ Return Values

S_OK
Value returned if the collection is added successfully.
E_FAIL
  • Value returned if:
    the collection is read-only.
    the document is not a recognized schema.
    an error occurs when compiling the schema.
    the ready state of the document is not 4.
E_POINTER
Value returned if input pointer is invalid.

Remarks

There is no guarantee that the two collections will return exactly the same schema objects—the collection is free to clone them if necessary.

Adding a collection to itself has no effect; S_OK is returned.

See Also

IXMLDOMSchemaCollection Interface


get Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMSchemaCollection::get Method

Returns a read-only DOM node containing the <Schema> element.

Visual Basic Syntax

Set objXMLDOMNode = objXMLDOMSchemaCol.get(namespaceURI)

C/C++ Syntax

HRESULT get(BSTR namespaceURI, IXMLDOMNode ** pNode);

Parameters

namespaceURI [in]
The namespace URI associated with the schema to return.

This may be any string that can be used in an xmlns attribute, but it cannot contain entity references. The same white space normalization that occurs on the xmlns attribute also occurs on this argument (that is, leading and trailing white space is trimmed, new lines are converted to spaces, and multiple adjacent white space characters are collapsed into one space).

pNode [out]
Read-only IXMLDOMNode that represents the schema that is returned.

Remarks

This will not necessarily be the same document object provided by the add method, because the add method may have copied the schema. For inline schemas, this will apply directly to the <Schema> node embedded within the document.

C/C++ Return Values

S_OK

See Also

IXMLDOMSchemaCollection Interface


length Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMSchemaCollection::length Method

Returns the number of namespaces currently in the schema collection.

Visual Basic Syntax

lValue = objXMLDOMSchemaCollection.length

C/C++ Syntax

HRESULT length(ULONG *pCount);

Parameters

pCount [out]
The number of namespaces in the collection.

C/C++ Return Values

S_OK
Value returned if successful.

See Also

IXMLDOMSchemaCollection Interface


namespaceURI Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMSchemaCollection::namespaceURI Method

Returns the namespace at the specified index.

Visual Basic Syntax

strNamespaceURI = objXMLDOMSchemaCollection.namespaceURI(index)

C/C++ Syntax

HRESULT namespaceURI(ULONG index, BSTR* pNamespaceURI);

Parameters

index [in]
The index between 0 and count-1.
pNamespaceURI [out]
The namespace URI at the given index. This string is a normalized version of the one provided in the add method (that is, leading and trailing white space is trimmed, new lines are converted to spaces, and multiple adjacent white space characters are collapsed into one space).

C/C++ Return Values

E_INVALIDARG
Value returned if the index is out of range.

Remarks

The namespaceURI parameter allows you to iterate through the collection to discover its contents. You can then use the get (namespace) method to see if there is an associated schema.

This is the default collection property, so you can use the following:

var uri = xmldoc.namespaces(1)

See Also

IXMLDOMSchemaCollection Interface


remove Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMSchemaCollection::remove Method

Removes the specified namespace from a collection.

Visual Basic Syntax

objXMLDOMSchemaCol.remove(namespaceURI)

C/C++ Syntax

HRESULT remove(BSTR namespaceURI);

Parameters

namespaceURI [in]
The namespace to remove from the collection.

This may be any string that can be used in an xmlns attribute, but it cannot contain entity references. The same white space normalization that occurs on the xmlns attribute occurs on this argument (that is, leading and trailing white space is trimmed, new lines are converted to spaces, and multiple adjacent white space characters are collapsed into one space).

C/++Return Values

E_INVALIDARG
Value returned if the namespace does not exist in the collection.
E_FAIL
Value returned if the collection is read-only.

See Also

IXMLDOMSchemaCollection Interface


IXMLDOMSelection Interface

Represents the list of nodes that match a given XSL Pattern or XPath expression.

This interface inherits from IXMLDOMNodeList.

Methods

clone | Produces an exact copy of the current XMLDOMSelection object.
matches | Checks whether the passed-in node is contained in the current collection.
context | Specifies the node (subtree) to apply a selection.
expr | Gets or puts the XPath or XSLPattern expression string.
getProperty | Looks up a property by name.
peekNode | Gets the next node that nextNode will return without advancing the line position.
removeAll | Removes all the nodes from the current context that match the query.
removeNext | Removes the next node that would be returned by peekNode or nextNode.

Remarks

A new XMLDOMSelection object is created via selectNodes on XMLDOMDocument2. The selectNodes method still returns an XMLDOMNodeList pointer, so in Visual Basic (and C++) you have to cast the result of selectNodes into an IXMLDOMSelection, as follows:

Dim Selection As IXMLDOMSelection
Set Selection = xmldoc.selectNodes("//Customer[Zip =98072]")

The XMLDOMSelection object inherits the threading model of the document that created it.

This interface is an extension of the W3C DOM.

Example

BOOL DOMSelectionDemo()
{
   BOOL bResult = FALSE;
   short sResult = FALSE;
   IXMLDOMSelection *pIXMLDOMSelection=NULL;
   IXMLDOMNodeList *pIXMLDOMNodeList=NULL;
   IXMLDOMNode *pIXMLDOMNode=NULL;
   IXMLDOMDocument2 *pIXMLDOMDocument2=NULL;
   BSTR bstrValue;
   HRESULT hr;

   try
   {
      hr=CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_SERVER,
               IID_IXMLDOMDocument2, (LPVOID*)(&pIXMLDOMDocument2));
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMDocument2)
      {
         hr=pIXMLDOMDocument2->put_async(VARIANT_FALSE);
         if(SUCCEEDED(hr))
         {
            hr=pIXMLDOMDocument2->load(_variant_t(
      _T("d:\\inetpub\\wwwroot\\samplexmldtd.xml")), &sResult);
            if(SUCCEEDED(hr) && (sResult==VARIANT_TRUE))
            {
               hr=pIXMLDOMDocument2->selectNodes(
               _T("*/BOOK[TITLE='Cosmos']"), &pIXMLDOMNodeList);
               if(SUCCEEDED(hr))
               {
                  hr=pIXMLDOMNodeList->QueryInterface(IID_IXMLDOMSelection
                     ,(void**)&pIXMLDOMSelection );
                  if(SUCCEEDED(hr) && pIXMLDOMSelection)
                  {
                     LONG uLength;

                     bResult=TRUE;
                     hr=pIXMLDOMSelection->get_length(&uLength);
                     if(SUCCEEDED(hr))
                     {
                        for(int iIndex=0; iIndex < uLength; iIndex++)
                        {
                           // remove all the nodes from the list-display
                           // them as they are removed.
                           hr=pIXMLDOMSelection->removeNext(
                              &pIXMLDOMNode);
                           if(SUCCEEDED(hr) && pIXMLDOMNode)
                           {
                              hr=pIXMLDOMNode->get_text(&bstrValue);
                              if(SUCCEEDED(hr))
                                 ::MessageBox(NULL, bstrValue, _T("Node
                                  Text"), MB_OK);
                              RELEASE(pIXMLDOMNode);
                           }
                        }
                     }
                     RELEASE(pIXMLDOMSelection);
                  }
                  RELEASE(pIXMLDOMNodeList);
               }
            }
         }
         RELEASE(pIXMLDOMDocument2);
      }
   }
   catch(...)
   {
      CHECK_AND_RELEASE(pIXMLDOMNode);
      CHECK_AND_RELEASE(pIXMLDOMDocument2);
      CHECK_AND_RELEASE(pIXMLDOMNodeList);
      CHECK_AND_RELEASE(pIXMLDOMSelection);
      DisplayErrorToUser();
   }
   return bResult;
}

d:\\inetpub\\wwwroot\\samplexmldtd.xml

<?xml version='1.0'?>
<COLLECTION xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <DATE dt:dt="datetime">1998-10-13T15:56:00</DATE>
  <BOOK>
    <TITLE>Cosmos</TITLE>
    <AUTHOR>Carl Sagan</AUTHOR>
    <PUBLISHER>Ballantine Books</PUBLISHER>
 </BOOK>
 <BOOK>
    <TITLE>Catwings</TITLE>
    <AUTHOR>Ursula K. Le Guin</AUTHOR>
    <PUBLISHER>Scholastic</PUBLISHER>
 </BOOK>
 <BOOK>
    <TITLE>Home Town</TITLE>
    <AUTHOR>Tracy Kidder</AUTHOR>
    <PUBLISHER>Random House</PUBLISHER>
 </BOOK>
</COLLECTION>

Output (in a message box)

Cosmos Carl Sagan Ballantine Books

Requirements

Implementation: Msxml2.dll

Header and IDL Files: Msxml.h, Xmldom.idl

Version-Independent ProgID: Msxml2.DOMDocument, Msxml2.FreeThreadedDOMDocument

Version-Independent CLSID: f6d90f11-9c73-11d3-b32e-00c04f990bb4

Version-Dependent ProgID: Msxml2.DOMDocument.2.6, Msxml2.FreeThreadedDOMDocument.2.6

Version-Dependent CLSID: f5078f1b-c551-11d3-89b9-0000f81fe221

See Also

Examples


clone Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMSelection::clone Method

Produces an exact copy of the current XMLDOMSelection object, with the same position and context.

Visual Basic Syntax

Set NewObjXMLDOMSelection = objXMLDOMSelection.clone

C/C++ Syntax

HRESULT clone(IXMLDOMSelection ** ppSelection);

Parameters

ppSelection [out]
The returned copy of the XMLDOMSelection object.

C/C++ Return Values

S_OK
Value returned if the cloning is successful.
E_INVALIDARG
Value returned if the input argument is NULL.

See Also

IXMLDOMSelection Interface


context Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMSelection::context Method

IXMLDOMSelection::get_context Method

Gets the node (subtree) that is applied to the selection.

Visual Basic Syntax

Set objXMLDOMNode = objXMLDOMSelection.context

C/C++ Syntax

HRESULT get_context(IXMLDOMNode ** ppNode);

Parameters

ppNode [out]
The subtree to apply the pattern to.

C/C++ Return Values

S_OK
Value returned if the method is successful.
S_FALSE
Value returned if the context is invalid.
E_INVALIDARG
Value returned if the input argument is Null.

Remarks

Because selectNodes can be called on any node type, context can also be assigned any node type. A node from a different document than the one that originally created this XMLDOMSelection object can also be specified as long as it has the same threading model. Calling context also resets the state of the node list so that nextNode starts over.

Example

The following Visual Basic example shows what the context of a selection contains when a query is executed and also that the selected set is reset when this property is changed:

Dim xmlDoc As New MSXML2.DOMDocument
Dim selection As MSXML2.IXMLDOMSelection

xmlDoc.loadXML ("<Customer>Microsoft</Customer>")
xmlDoc.setProperty "SelectionLanguage", "XPath"
Set selection = xmlDoc.selectNodes("//Customer")
MsgBox selection.context.xml

Selection.context.xml contains "<Customer>Amit</Customer>", selection.length = 1

xmlDoc.loadXML ("<new>none</new>")
MsgBox selection.context.xml

Selection.context.xml contains "<new>no-one</new>", selection.length = 0

IXMLDOMSelection::put_context Method

Specifies the node (subtree) to apply to the selection.

Visual Basic Syntax

objXMLDOMSelection.context = objXMLDOMNode

C/C++ Syntax

HRESULT putref_context(IXMLDOMNode * pNode);

Parameters

pNode [in]
The subtree to apply the pattern to.

C/C++ Return Values

S_OK
Value returned if the context is set successfully.
E_FAIL
Value returned if a node with a different threading model is provided.

Remarks

Because selectNodes can be called on any node type, context can also be assigned any node type. A node from a different document than the one that originally created this XMLDOMSelection object can also be specified as long as it has the same threading model. Calling context also resets the state of the node list so that nextNode starts over.

Example

The following C++ code example shows what the context property contains after selectNodes is executed.

BOOL DOMSelectionContextDemo()
{
   BOOL bResult = FALSE;
   short sResult = FALSE;
   IXMLDOMSelection *pIXMLDOMSelection=NULL;
   IXMLDOMNode *pIXMLDOMNode=NULL;
   IXMLDOMDocument2 *pIXMLDOMDocument2=NULL;
   HRESULT hr;
   BSTR bstrValue;

   try
   {
      hr=CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_SERVER,
         IID_IXMLDOMDocument2, (LPVOID*)(&pIXMLDOMDocument2));
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMDocument2)
      {
         hr=pIXMLDOMDocument2->put_async(VARIANT_FALSE);
         if(SUCCEEDED(hr))
         {
            hr=pIXMLDOMDocument2->load(_variant_t(
               _T("d:\\inetpub\\wwwroot\\samplexml.xml")), &sResult);
            if(SUCCEEDED(hr) && (sResult==VARIANT_TRUE))
            {
               hr=pIXMLDOMDocument2->selectNodes(
               _T("*/BOOK[TITLE='Cosmos']"),
               (IXMLDOMNodeList**)&pIXMLDOMSelection);
               if(SUCCEEDED(hr))
               {
                  if(SUCCEEDED(hr) && pIXMLDOMSelection)
                  {
                     hr=pIXMLDOMSelection->get_context(&pIXMLDOMNode);
                     if(SUCCEEDED(hr) && pIXMLDOMNode)
                     {
                        hr=pIXMLDOMNode->get_xml(&bstrValue);
                        if(SUCCEEDED(hr))
                           ::MessageBox(NULL, bstrValue, _T("Current
                              Selection Context"), MB_OK);
                        bResult=TRUE;
                        RELEASE(pIXMLDOMNode);
                     }
                     RELEASE(pIXMLDOMSelection);
                  }
               }
            }
         }
         RELEASE(pIXMLDOMDocument2);
      }
   }
   catch(...)
   {
      CHECK_AND_RELEASE(pIXMLDOMNode);
      CHECK_AND_RELEASE(pIXMLDOMSelection);
      CHECK_AND_RELEASE(pIXMLDOMDocument2);
      DisplayErrorToUser();
   }
   return bResult;
}

d:\\inetpub\\wwwroot\\samplexml.xml

<?xml version='1.0'?>
<COLLECTION xmlns:dt="urn:schemas-microsoft-com:datatypes">
   <DATE dt:dt="datetime">1998-10-13T15:56:00</DATE>
   <BOOK>
      <TITLE>Cosmos</TITLE>
      <AUTHOR>Carl Sagan</AUTHOR>
      <PUBLISHER>Ballantine Books</PUBLISHER>
   </BOOK>
   <BOOK>
      <TITLE>Catwings</TITLE>
      <AUTHOR>Ursula K. Le Guin</AUTHOR>
      <PUBLISHER>Scholastic</PUBLISHER>
   </BOOK>
</COLLECTION>

Output (in a message box)

<?xml version='1.0'?>
<COLLECTION xmlns:dt="urn:schemas-microsoft-com:datatypes">
   <DATE dt:dt="datetime">1998-10-13T15:56:00</DATE>
   <BOOK>
      <TITLE>Cosmos</TITLE>
      <AUTHOR>Carl Sagan</AUTHOR>
      <PUBLISHER>Ballantine Books</PUBLISHER>
   </BOOK>
   <BOOK>
      <TITLE>Catwings</TITLE>
      <AUTHOR>Ursula K. Le Guin</AUTHOR>
      <PUBLISHER>Scholastic</PUBLISHER>
   </BOOK>
</COLLECTION>

See Also

IXMLDOMSelection Interface


expr Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMSelection::expr Method

IXMLDOMSelection::get_expr Method

Gets the XPath or XSLPattern expression string.

Visual Basic Syntax

strExpression = objXMLDOMSelection.expr

C/C++ Syntax

HRESULT get_expr(BSTR* expression);

Parameters

expr [out]
The IXMLDOMSelection expression string is either XSLPattern or XPath syntax, depending on the setting of the SelectionLanguage property on the IXMLDOMDocument2 interface that created this Selection object. (For details, see IXMLDOMDocument2::setProperty.) Once created, the XMLDOMSelection object continues to use the expression language it was created with.

C/C++ Return Values

S_OK
Value returned if method is successful.

E_FAIL and formatted error message via IErrorInfo if expression is invalid.

Remarks

Setting a new expression immediately resets the state of this node list to the beginning unless the expression is invalid and an error is returned, in which case it has no effect. It does not reset the context.

Example

Dim xmlDoc As New MSXML2.DOMDocument
Dim selection As MSXML2.IXMLDOMSelection

xmlDoc.loadXML ("<Customer><Name>Microsoft</Name></Customer>")
xmlDoc.setProperty "SelectionLanguage", "XPath"
Set selection = xmlDoc.selectNodes("Customer/Name")
MsgBox selection.expr + " -- " + selection.Item(0).xml

Displays "Customer/Name -- <Name> Microsoft</Name>"

selection.expr = "/Customer"

The selection is immediately refreshed with a list corresponding to the new expression

MsgBox selection.expr + " -- " + selection.Item(0).xml

Displays "/Customer -- <Customer><Name> Microsoft</Name></Customer>"

IXMLDOMSelection::put_expr Method

Puts the XPath or XSLPattern expression string.

Visual Basic Syntax

objXMLDOMSelection.expr = strExpression

C/C++ Syntax

HRESULT put_expr(BSTR expression);

Parameters

expr [in]
The IXMLDOMSelection expression string is either XSLPattern or XPath syntax, depending on the setting of the SelectionLanguage property on the IXMLDOMDocument2 interface that created this Selection object. (For details, see IXMLDOMDocument2::setProperty). Once created, the XMLDOMSelection object continues to use the expression language it was created with.

C/C++ Return Values

S_OK if method is successful.

E_FAIL and formatted error message via IErrorInfo if expression is invalid.

Remarks

Setting a new expression immediately resets the state of this node list to the beginning unless the expression is invalid and an error is returned, in which case it has no effect. It does not reset the context.

Example

The following code example demonstrates the resetting of the selected nodes list if the expr property is changed:

BOOL DOMSelectionExprDemo()
{
   BOOL bResult = FALSE;
   short sResult = FALSE;
   long lvalue;
   IXMLDOMSelection *pIXMLDOMSelection=NULL;
   IXMLDOMDocument2 *pIXMLDOMDocument2=NULL;
   HRESULT hr;
   BSTR bstrValue;

   try
   {
      hr=CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_SERVER,
         IID_IXMLDOMDocument2, (LPVOID*)(&pIXMLDOMDocument2));
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXMLDOMDocument2)
      {
         hr=pIXMLDOMDocument2->put_async(VARIANT_FALSE);
         if(SUCCEEDED(hr))
         {
            hr=pIXMLDOMDocument2->load(_variant_t(
               _T("d:\\inetpub\\wwwroot\\sample.xml")), &sResult);
            if(SUCCEEDED(hr) && (sResult==VARIANT_TRUE))
            {
               hr=pIXMLDOMDocument2->selectNodes(
                  _T("*/BOOK[TITLE='Cosmos']"),
                  (IXMLDOMNodeList**)&pIXMLDOMSelection);
               if(SUCCEEDED(hr))
               {
                  if(SUCCEEDED(hr) && pIXMLDOMSelection)
                  {
                     hr = pIXMLDOMSelection->get_expr(&bstrValue);
                     if(SUCCEEDED(hr))
                     {
                        ::MessageBox(NULL, bstrValue, _T("Current
                           Expression"), MB_OK);
                        bResult=TRUE;
                        hr = pIXMLDOMSelection->get_length(&lvalue);
                        hr = pIXMLDOMSelection->put_expr(_T("*/BOOK"));
                        hr = pIXMLDOMSelection->get_length(&lvalue);
                     }
                     RELEASE(pIXMLDOMSelection);
                  }
               }
            }
         }
         RELEASE(pIXMLDOMDocument2);
      }
   }
   catch(...)
   {
      CHECK_AND_RELEASE(pIXMLDOMSelection);
      CHECK_AND_RELEASE(pIXMLDOMDocument2);
      DisplayErrorToUser();
   }
   return bResult;
}

d:\\inetpub\\wwwroot\\sample.xml

<?xml version='1.0'?>
<COLLECTION xmlns:dt="urn:schemas-microsoft-com:datatypes">
   <DATE dt:dt="datetime">1998-10-13T15:56:00</DATE>
   <BOOK>
      <TITLE>Cosmos</TITLE>
      <AUTHOR>Carl Sagan</AUTHOR>
      <PUBLISHER>Ballantine Books</PUBLISHER>
   </BOOK>
   <BOOK>
      <TITLE>Catwings</TITLE>
      <AUTHOR>Ursula K. Le Guin</AUTHOR>
      <PUBLISHER>Scholastic</PUBLISHER>
   </BOOK>
</COLLECTION>

Output

The first messagebox would display "1".

The second would display "2". When the expression changes, the nodes in the selection are recomputed using the new expression. No changes are made to the context property.

See Also

IXMLDOMSelection Interface


getProperty Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMSelection::getProperty Method

Looks up a property by name.

Visual Basic Syntax

strValue = objXMLDOMSelection.getProperty(name)

C/C++ Syntax

HRESULT getProperty(BSTR name, VARIANT* value);

Parameters

name [in]
String name of the property. This name is case-sensitive.
value [out]
Variant return value of the requested property.

C/C++ Return Values

S_OK
Value returned if method successful.
E_INVALIDARG
Value returned if named property doesn't exist.

Remarks

The getProperty method returns the value for the SelectionLanguage that was set by calling the setProperty method on the document or the default value.

See Also

IXMLDOMSelection Interface


matches Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMSelection::matches Method

Checks whether the node that is passed in is contained in the current collection.

Visual Basic Syntax

objXMLDOMNode = objXMLDOMSelection.matches(objXMLDOMNode)

C/C++ Syntax

HRESULT matches(IXMLDOMNode* pNode, IXMLDOMNode** ppNode);

Parameters

pNode [in]
The node that is passed in.
ppNode [out]
Boolean result.

C/C++ Return Values

S_OK
Value returned if method successful.

E_INVALIDARG

Value returned if pNode=NULL.

An error for cases that are not allowed, as outlined in the XSL specification (applied to ID, IDREF, and Ancestor).

Remarks

When matches is called with node A, it returns node B such that if B was set as the context on a query, A is in the result set of the query. If no such B node is found, matches returns Null.

The matches method does not take into account the current context of the query.

See Also

IXMLDOMSelection Interface


peekNode Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMSelection::peekNode Method

Gets the next node that nextNode will return without advancing the list position.

Visual Basic Syntax

Set objXMLDOMNode = objXMLDOMSelection.peekNode

C/C++ Syntax

HRESULT peekNode (IXMLDOMNode** ppNode);

Parameters

ppNode [out]
The returned node, or Null if there are no more nodes or if E_PENDING is returned.

C/C++ Return Values

S_OK
Value returned if the method is successful.
E_PENDING
Value returned if the context document is still being built and the selection object has hit the end of the available nodes to match.

Remarks

Like nextNode, peekNode does not result in a snapshot of all matching nodes. Consecutive calls to peekNode will produce the same node over and over, independent of changes in the underlying tree.

See Also

IXMLDOMSelection Interface


removeAll Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMSelection::removeAll Method

Removes all the nodes from the collection described by the XMLDOMSelection object.

Visual Basic Syntax

objXMLDOMSelection.removeAll

C/C++ Syntax

HRESULT removeAll();

C/C++ Return Values

S_OK
Value returned if the method is successful.
E_PENDING
Value returned if all nodes cannot be found at this time (in which case no nodes are removed).

See Also

IXMLDOMSelection Interface


removeNext Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMSelection::removeNext Method

Removes the next node that would be returned by peekNode or nextNode.

Visual Basic Syntax

Set objXMLDOMNode = objXMLDOMSelection.removeNext

C/C++ Syntax

HRESULT removeNext(IXMLDOMNode ** ppNode);

Parameters

ppNode [out]
The node that was removed, or Null if there is no nextNode to remove. If the parameter is Null, the removed node is not returned, but it is still removed.

C/C++ Return Values

S_OK
Value returned if the method is successful.
S_FALSE
Value returned if no nodes left in the selection.
E_PENDING
Value returned if all nodes cannot be found at this time (in which case no nodes are removed).

Remarks

The removeNext method is equivalent to the following (except that it also works for attributes):

var node = list.peeknode();
node.parentnode.removeChild(node);

The side effect is that the length of the collection is decremented and the nextNode and item methods will not return it because it has been removed.

See Also

IXMLDOMSelection Interface


IXMLDOMText Interface

Represents the text content of an element or attribute.

This interface inherits from IXMLDOMCharacterData.

Methods

splitText Breaks this text node into two text nodes at the specified offset and inserts the new text node into the tree as a sibling that immediately follows this node.

Remarks

XML refers to this text content as character data and distinguishes it from markup, the tags that modify that character data. If there is no markup inside an element, that element's text is contained in a single XMLDOMText object that is the child of the element. If there is markup inside an element, it is parsed into child elements that are siblings of the XMLDOMText object(s). (Note that the content of the markup elements also appears within text nodes, which are the children of the specific markup element.)

When a document is first made available to the XML DOM, all text is normalized: there is only one text node for each block of text. Users can create adjacent text nodes that represent the contents of a given element without any intervening markup but should be aware that there is no way to represent the separations between these nodes, so they will not persist between XML DOM sessions.

The normalize method on the XMLDOMElement object merges adjacent text nodes into a single node. You should normalize before starting any operations that depend on a particular document structure to ensure that subsequent sessions will operate on the same structure.

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl


splitText Method

Microsoft XML SDK 2.6 - XML Reference

IXMLDOMText::splitText Method

Breaks this text node into two text nodes at the specified offset and inserts the new text node into the tree as a sibling that immediately follows this node.

Visual Basic Syntax

Set objXMLDOMText = oXMLDOMText.splitText(offset)

C/C++ Syntax

HRESULT splitText(
    long offset,
    IXMLDOMText **rightHandTextNode);

Parameters

offset [in]
Number of characters at which to split this text node into two nodes, starting from zero.
rightHandTextNode [out]
New text node.

C/C++ Return Values

S_OK
Value returned if successful.
S_FALSE
Value when returning Null.
E_FAIL
Value returned if an error occurs.

Remarks

If you specify an offset of zero, the first text node is empty and the right-hand text node contains the previous contents of the node.

If you specify an offset beyond the end of the string, a new, empty right-hand text node is created.

C/C++ Example

IXMLDOMText *pIXMLDOMTextTemp = NULL;
IXMLDOMText *pIXMLDOMText = NULL;
IXMLDOMNode *pIXMLDOMNodeTemp = NULL;
IXMLDOMElement *pIXMLDOMElement = NULL;
IXMLDOMNode *pIXMLDOMNode = NULL;
DOMNodeType DOMNodeTyp;
VARIANT varValue;
HRESULT hr;

try
{
   // Create an instance of DOMDocument and initialize pIXMLDOMDocument.
   // Load/create an XML fragment.
   hr = pIXMLDOMDocument->get_documentElement(&pIXMLDOMElement);
   SUCCEEDED(hr) ? 0 : throw hr;

   if(pIXMLDOMElement)
   {
      hr = pIXMLDOMElement->QueryInterface(IID_IXMLDOMNode, (void
            **)&pIXMLDOMNode);
      SUCCEEDED(hr) ? 0 : throw hr;

      pIXMLDOMElement->Release();
      pIXMLDOMElement = NULL;

      while(pIXMLDOMNode)
      {
         hr = pIXMLDOMNode->get_nodeType(&DOMNodeTyp);
         if(SUCCEEDED ( hr) && (DOMNodeTyp == NODE_TEXT))
         {
            hr = pIXMLDOMNode->QueryInterface(IID_IXMLDOMText,
                     (void**)&pIXMLDOMText);
            SUCCEEDED(hr) ? 0 : throw hr;

            if(pIXMLDOMText)
            {
            hr = pIXMLDOMText->splitText(2, &pIXMLDOMTextTemp);
            if(SUCCEEDED(hr) && pIXMLDOMTextTemp)
            {
               hr = pIXMLDOMTextTemp->get_nodeValue(&varValue);
               if(SUCCEEDED(hr))
                  ::MessageBox(NULL, _bstr_t(varValue), _T("Notation Public                      ID"), MB_OK);
               pIXMLDOMTextTemp->Release();
               pIXMLDOMTextTemp = NULL;
            }
            pIXMLDOMText->Release();
            pIXMLDOMText = NULL;
         }
            pIXMLDOMNode->Release();
            pIXMLDOMNode = NULL;
            break;
         }
         pIXMLDOMNode->get_firstChild(&pIXMLDOMNodeTemp);
         pIXMLDOMNode->Release();
         pIXMLDOMNode = pIXMLDOMNodeTemp;
         pIXMLDOMNodeTemp = NULL;
      }
   }
}
catch(...)
{
   if(pIXMLDOMNodeTemp)
      pIXMLDOMNodeTemp->Release();
   if(pIXMLDOMNode)
      pIXMLDOMNode->Release();
   if(pIXMLDOMTextTemp)
      pIXMLDOMTextTemp->Release();
   if(pIXMLDOMText)
      pIXMLDOMText->Release();
   if(pIXMLDOMElement)
      pIXMLDOMElement->Release();
   DisplayErrorToUser();
}
// Release pIXMLDOMDocument when finished with it.

See Also

IXMLDOMText Interface


IXMLHttpRequest Interface

Provides client-side protocol support for communication with HTTP servers.

This interface inherits from IDispatch.

Methods

abort | Cancels the current HTTP request.
getAllResponseHeaders | Retrieves the values of all the HTTP headers.
getResponseHeader | Retrieves the value of an HTTP header from the response body.
onreadystatechange | Specifies the event handler to be invoked when the readyState method changes.
open | Initializes an Msxml2.XMLHTTP request and specifies the method, URL, and authentication information for the request.
readyState | Represents the state of the request.
responseBody | Represents the response entity body as an array of unsigned bytes.
responseStream | Represents the response entity body as an IStream.
responseText | Represents the response entity body as a string.
responseXML | Represents the response entity body as parsed by the Microsoft XML Parser (MSXML) parser.
send | Sends an HTTP request to the server and receives a response.
setRequestHeader | Specifies the name of an HTTP header.
status | Represents the HTTP status code returned by a request.
statusText | Represents the HTTP response line status.

Remarks

A client computer can use the XMLHttpRequest object (Msxml2.XMLHTTP) to send an arbitrary HTTP request, receive the response, and have the Microsoft XML DOM parse that response.

This object is integrated with the Microsoft XML Parser (MSXML) to support sending the request body directly from, and parsing the response directly into, the MSXML DOM objects. When combined with the support for XSL, the XMLHttpRequest component provides an easy way to send structured queries to HTTP servers and efficiently display the results with a variety of presentations.

The usual sequence is to call open, set any custom header information via setRequestHeader followed by send, and then to check one of the four different response properties.

Requirements

Implementation: Msxml.dll, Msxml2.dll

Header and IDL files: Msxml.h, Xmldom.idl

Version-Independent ProgID: Msxml2.XMLHTTP

Version-Independent CLSID: f6d90f16-9c73-11d3-b32e-00c04f990bb4

Version-Dependent ProgID: Msxml2.XMLHTTP.2.6

Version-Dependent CLSID: f5078f1e-c551-11d3-89b9-0000f81fe221

See Also

IXMLDOMDocument Interface


abort Method

Microsoft XML SDK 2.6 - XML Reference

IXMLHttpRequest::abort Method

Cancels the current HTTP request.

Visual Basic Syntax

oXMLHttpRequest.abort

C/C++ Syntax

HRESULT abort(void);

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

The object will be returned to the UNINITIALIZED state, and open must be called next.

See Also

IXMLHttpRequest Interface | readyState Method


getAllResponseHeaders Method

Microsoft XML SDK 2.6 - XML Reference

IXMLHttpRequest::getAllResponseHeaders Method

Retrieves the values of all the HTTP headers.

Visual Basic Syntax

strValue = oXMLHttpRequest.getAllResponseHeaders

C/C++ Syntax

HRESULT getAllResponseHeaders(BSTR *pbstrHeaders);

Parameters

pbstrHeaders [out]
Resulting header information.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

Each header name/value pair is separated by a combination carriage return–linefeed character (vbCrLf in Visual Basic).

The results of this method are valid only after the send method has been successfully completed.

C/C++ Example

HRESULT hr;
BSTR bstrValue = NULL;
IXMLHttpRequest *pIXMLHttpRequest = NULL;

try
{
   // Create XMLHttpRequest object and initialize pIXMLHttpRequest.
   hr = pIXMLHttpRequest->getAllResponseHeaders(&bstrValue);
   if(SUCCEEDED(hr))
      ::MessageBox(NULL, bstrValue, _T("All Response Headers"), MB_OK);
}
catch(...)
{
   DisplayErrorToUser();
}
// Release pIXMLHttpRequest when finished with it.

See Also

IXMLHttpRequest Interface | getResponseHeader Method | setRequestHeader Method


getResponseHeader Method

Microsoft XML SDK 2.6 - XML Reference

IXMLHttpRequest::getResponseHeader Method

Retrieves the value of an HTTP header from the response body.

Visual Basic Syntax

strValue = oXMLHttpRequest.getResponseHeader(bstrHeader)

C/C++ Syntax

HRESULT getResponseHeader(BSTR bstrHeader, BSTR* pbstrValue);

Parameters

bstrHeader [in]
Case-insensitive header name.
pbstrValue [out]
Resulting header information.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

The results of this method are valid only after the send method has been successfully completed. The following line,

xmlhttp.getResponseHeader("Content-Type");

returns the string "text/xml", assuming the server set "text/xml" as the content type. The full list of header variables you can query can be accessed via the getAllResponseHeaders method.

C/C++ Example

HRESULT hr;
BSTR bstrValue = NULL;
IXMLHttpRequest *pIXMLHttpRequest = NULL;

try
{
   // Create XMLHttpRequest object and initialize pIXMLHttpRequest.
   hr = pIXMLHttpRequest->getResponseHeader(_T("Server"), &bstrValue);
   if(SUCCEEDED(hr))
   {
      ::MessageBox(NULL, m_bstrValue, _T("Response Header-Server"), MB_OK);
      ::SysFreeString(bstrValue);
      bstrValue = NULL;
   }
}
catch(...)
{
   if(bstrValue)
      ::SysFreeString(bstrValue);
   DisplayErrorToUser();
}
// Release pIXMLHttpRequest when finished with it.

See Also

IXMLHttpRequest Interface | getAllResponseHeaders Method | setRequestHeader Method


onreadystatechange Method

Microsoft XML SDK 2.6 - XML Reference

IXMLHttpRequest::onreadystatechange Method

IXMLHttpRequest::put_onreadystatechange Method

Specifies the event handler to be invoked when the readyState property changes.

Visual Basic Syntax

oXMLHttpRequest.onreadystatechange = funcMyHandler

C/C++ Syntax

HRESULT put_onreadystatechange(VARIANT* readystatechangeSink);

Parameters

readystatechangeSink [in]
Name of the function that should be called when the readyState property value changes.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

This event has the same syntax as the IXMLDOMDocument::onreadystatechange event.

You can call the XMLHttpRequest object's QueryInterface method to obtain the IConnectionPointContainer interface, which is used in event management.

Example

For a C/C++ example, see IXMLDOMDocument::onreadystatechange Event.

See Also

IXMLHttpRequest Interface


open Method

Microsoft XML SDK 2.6 - XML Reference

IXMLHttpRequest::open Method

Initializes an Msxml2.XMLHTTP request and specifies the method, URL, and authentication information for the request.

Visual Basic Syntax

oXMLHttpRequest.open(bstrMethod, bstrUrl, bAsync, bstrUser, bstrPassword)

C/C++ Syntax

HRESULT open(BSTR bstrMethod, BSTR bstrUrl, VARIANT bAsync, VARIANT bstrUser, VARIANT bstrPassword);

Parameters

bstrMethod [in]
HTTP method used to open the connection, such as PUT or PROPFIND.
bstrUrl [in]
Requested URL. This must be an absolute URL, such as "http://Myserver/Mypath/".
bAsync [in, optional]
Boolean. Indicator as to whether the call is asynchronous. The default is True (the call returns immediately). If set to True, attach an onreadystatechange callback so that you can tell when the send call has completed.
bstrUser [in, optional]
Name of the user for authentication. If this parameter is Null ("") or missing and the site requires authentication, the component displays a logon window.
bstrPassword [in, optional]
Password for authentication. This parameter is ignored if the user parameter is Null or missing.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

The values of the request headers, request body, response headers, and response body must be cleared before calling this method.

After calling this method you must call send to send the request and data (if any) to the server.

C/C++ Example

HRESULT hr;
IXMLHttpRequest *pIXMLHttpRequest = NULL;

try
{
   // Create XMLHttpRequest object and initialize pIXMLHttpRequest.
   hr = pIXMLHttpRequest->open(_bstr_t(_T("PUT")),
      _bstr_t(_T("http://MyServer/Sample.xml")),
      _variant_t(VARIANT_FALSE), _variant_t(""), _variant_t(""));
   if(SUCCEEDED(hr))
      ::MessageBox(NULL, _T("Success !"), _T(""), MB_OK);
}
catch(...)
{
   DisplayErrorToUser();
}
// Release pIXMLHttpRequest when finished with it.

See Also

IXMLHttpRequest Interface | abort Method


readyState Method

Microsoft XML SDK 2.6 - XML Reference

IXMLHttpRequest::readyState Method

IXMLHttpRequest::get_readyState Method

Represents the state of the request.

Visual Basic Syntax

lValue = oXMLHttpRequest.readyState

C/C++ Syntax

HRESULT get_readyState(long* pvarVal);

Parameters

pvarVal [out]
State of the request, as an I4 (4-byte integer). The following values are defined:
(0) UNINITIALIZED | The object has been created but has not been initialized (open has not been called).
(1) LOADING | The object has been created but send has not been called.
(2) LOADED | send has been called and the status and headers are available, but the response is not yet available.
(3) INTERACTIVE | Some data has been received. You can call responseBody and responseText to get the current partial results.
(4) COMPLETED | All the data has been received, and the complete data is available in responseBody and responseText.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

This property returns a 4-byte integer.

See Also

IXMLHttpRequest Interface | status Method | statusText Method


responseBody Method

Microsoft XML SDK 2.6 - XML Reference

IXMLHttpRequest::responseBody Method

IXMLHttpRequest::get_responseBody Method

Represents the response entity body as an array of unsigned bytes, namely a SAFEARRAY of type VT_ARRAY | VT_UI1. This contains the raw undecoded bytes as received directly from the server. So depending on what the server sent, this may appear as binary-encoded data (UTF-8, UCS-2, UCS-4, shiftJis, and so on).

Visual Basic Syntax

strValue = oXMLHttpRequest.responseBody

C/C++ Syntax

HRESULT get_responseBody(VARIANT* pvarVal);

Parameters

pvarVal [out]
Response entity body as an array of unsigned bytes.

C/C++ Return Values

S_OK
Value returned if successful.
E_PENDING
Value returned if the data is unavailable.

Remarks

This property represents only one of several forms in which the HTTP response can be returned.

See Also

IXMLHttpRequest Interface | responseStream Method | responseText Method | responseXML Method


responseStream Method

Microsoft XML SDK 2.6 - XML Reference

IXMLHttpRequest::responseStream Method

IXMLHttpRequest::get_responseStream Method

Represents the response entity body as an IStream. This stream returns the raw un-decoded bytes as received directly from the server. So depending on what the server sent, this may appear as binary-encoded data (UTF-8, UCS-2, UCS-4, shiftJis, and so on).

Visual Basic Syntax

strValue = oXMLHttpRequest.responseStream

C/C++ Syntax

HRESULT get_responseStream(VARIANT* pvarVal);

Parameters

pvarVal [out]
Response entity body as an IStream.

C/C++ Return Values

S_OK
Value returned if successful.
E_PENDING
Value returned if the data is unavailable.

Remarks

This property represents only one of several forms in which the HTTP response can be returned.

Example

HRESULT hr;
IXMLHttpRequest *pIXMLHttpRequest = NULL;
IStream *pIStream = NULL;
VARIANT varValue;

try
{
   // Create XMLHttpRequest object and initialize pIXMLHttpRequest.
   hr = pIXMLHttpRequest->get_responseStream(&varValue);
   if(SUCCEEDED(hr))
   {
      pIStream = (IStream*)varValue.punkVal;
      if(pIStream)
      {
         pIStream->Release();
         pIStream = NULL;
      }
   }
}
catch(...)
{
   if(pIStream)
      pIStream->Release();
   DisplayErrorToUser();
}
// Release pIXMLHttpRequest when finished with it.

See Also

IXMLHttpRequest Interface | responseBody Method | responseText Method | responseXML Method


responseText Method

Microsoft XML SDK 2.6 - XML Reference

IXMLHttpRequest::responseText Method

IXMLHttpRequest::get_responseText Method

Represents the response entity body as a string.

Visual Basic Syntax

strValue = oXMLHttpRequest.responseText

C/C++ Syntax

HRESULT get_responseText(VARIANT* pvarVal);

Parameters

pvarVal [out]
Response entity body as a string.

C/C++ Return Values

S_OK
Value returned if successful.
E_PENDING
Value returned if the data is unavailable.

Remarks

The XMLHttpRequest object tries to decode the response into a Unicode string. It assumes the default encoding is UTF-8, but it can decode any type of UCS-2 (big or little endian) or UTC-4 encoding as long as the server sends the appropriate Unicode byte-order mark. It does not process the <? XML coding declaration. If you know the response is going to be XML, you should use the responseXML property for full XML encoding support.

This property represents only one of several forms in which the HTTP response can be returned.

See Also

IXMLHttpRequest Interface | responseBody Method | responseStream Method | responseXML Method


responseXML Method

Microsoft XML SDK 2.6 - XML Reference

IXMLHttpRequest::responseXML Method

IXMLHttpRequest::get_responseXML Method

Represents the response entity body as parsed by the Microsoft XML Parser (MSXML).

Visual Basic Syntax

Set objDispatch = oXMLHttpRequest.responseXML

C/C++ Syntax

HRESULT get_responseXML(IDispatch** ppXmlDom);

Parameters

ppXmlDom [out]
Response entity body as an XML document.

C/C++ Return Values

S_OK
Value returned if successful.
E_PENDING
Value returned if the data is unavailable.

Remarks

For security reasons, the parser validation features are always turned off to prevent MSXML from attempting to download a DTD or XML-Data definition.

If the response entity body is not valid XML, this method returns the DOMDocument object that was parsed so that you can access the error object. This property does not return the error object XMLDOMParseError itself, but that object is accessible from the DOMDocument object.

If the response was generated by an ASP page and the MIME type was not correctly set to "text/xml" or "application/xml" using the ASP method Response.ContentType, responseXML will be empty.

See Also

IXMLHttpRequest Interface | responseBody Method | responseStream Method | responseText Method


send Method

Microsoft XML SDK 2.6 - XML Reference

IXMLHttpRequest::send Method

Sends an HTTP request to the server and receives a response.

Visual Basic Syntax

oXMLHttpRequest.send(varBody)

C/C++ Syntax

HRESULT send(VARIANT varBody);

Parameters

varBody [in, optional]
The body of the message being sent with the request.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

This method is synchronous or asynchronous, depending on the value of the bAsync parameter in the open call. If open is called with bAsync == False, this call does not return until the entire response is received or the protocol stack times out. If open is called with bAsync == True, this call returns immediately.

This method takes one parameter, which is the requestBody to use. The acceptable VARIANT input types are BSTR, SAFEARRAY of UI1 (unsigned bytes), IDispatch to an XML DOM object, and IStream *. You can only use chunked encoding (for sending) when sending IStream *input types. The component automatically sets the Content-Length header for all but IStream *input types.

If the input type is a BSTR, the response is always encoded as UTF-8. The caller must set a Content-Type header with the appropriate content type and include a charset parameter.

If the input type is a SAFEARRAY of UI1, the response is sent as is without additional encoding. The caller must set a Content-Type header with the appropriate content type.

If the input type is an XMLDOM object, the response is encoded according to the encoding attribute on the <? XML declaration in the document. If there is no XML declaration or encoding attribute, UTF-8 is assumed.

If the input type is an IStream *, the response is sent as is without additional encoding. The caller must set a Content-Type header with the appropriate content type.

See Also

IXMLHttpRequest Interface


setRequestHeader Method

Microsoft XML SDK 2.6 - XML Reference

IXMLHttpRequest::setRequestHeader Method

Specifies the name of an HTTP header.

Visual Basic Syntax

oXMLHttpRequest.setRequestHeader(bstrHeader, bstrValue)

C/C++ Syntax

HRESULT setRequestHeader(BSTR bstrHeader, BSTR bstrValue);

Parameters

bstrHeader [in]
Header name to set; for example, "depth". This parameter should not contain a colon and should be the actual text of the HTTP header.
bstrValue [in]
Value of the header; for example, "infinity".

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

If another header already exists with this name, it is replaced.

See Also

IXMLHttpRequest Interface | getResponseHeader Method


status Method

Microsoft XML SDK 2.6 - XML Reference

IXMLHttpRequest::status Method

IXMLHttpRequest::get_status Method

Represents the HTTP status code returned by a request.

Visual Basic Syntax

lValue = oXMLHttpRequest.status

C/C++ Syntax

HRESULT get_status(long* plStatus);

Parameters

plStatus [out]
Returned status code, as a long integer.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

The results of this method are valid only after the send method has been successfully completed.

See Also

IXMLHttpRequest Interface | statusText Method


statusText Method

Microsoft XML SDK 2.6 - XML Reference

IXMLHttpRequest::statusText Method

IXMLHttpRequest::get_statusText Method

Represents the HTTP response line status.

Visual Basic Syntax

strValue = oXMLHttpRequest.statusText

C/C++ Syntax

HRESULT get_statusText(BSTR* pbstrStatus);

Parameters

pbstrStatus [out]
HTTP response as a "BSTR" value.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

Remarks

The results of this method are valid only after the send method has been successfully completed.

See Also

IXMLHttpRequest Interface | status Method


IXSLProcessor Interface

Used for transformations with compiled style sheets.

Methods

addObject | Allows you to pass objects to a style sheet.
addParameter | Allows you to pass variables to a style sheet.
input | Specifies which XML input tree to transform.
output | Used to provide a custom output object to write the result of a transformation to.
readyState | Returns the current state of the processor.
reset | Resets the processor state to what it was before calling transform.
setStartMode | Allows you to perform subsets of a larger XSLT transformation.
startMode | Returns the base name part of the start mode (a qualified name).
startModeURI | Returns the namespace URI of the start mode (a qualified name).
stylesheet | Returns the style sheet that was defined on the IXSLTemplate at the time this processor object was created.
ownerTemplate | Returns the style sheet template used to create the XSL processor object.
transform | Starts the transformation process or resumes a transform that previously returned VARIANT_FALSE.

Remarks

The createProcessor method returns an XSLProcessor object. The processor object has a transform method that takes the input data, applies the XSLT style sheet defined in the XSLTemplate, and writes the result to the specified output stream.

This XSL processor is completely independent of the transformNode and transformNodeToObject methods on IXMLDOMDocument. In particular, when you transform XML using this interface, the ontransformnode event is not fired on the DOMDocument object.

The IXSLProcessor interface is designed to handle asynchronous transformations. In this case, multiple calls to transform need to be made, in which each call will transform as much as it can based on what is available from the input. In this scenario, the processor remains in READYSTATE_INTERACTIVE until the transform is complete.

Example

BOOL XSLProcessorDemo()
{
   BOOL bResult = FALSE;
   short sResult = FALSE;
   HRESULT hr;
   IXMLDOMDocument2 *pStyleSheet=NULL;
   IXMLDOMDocument2 *pDOMObject=NULL;
   IXSLTemplate *pIXSLTemplate=NULL;
   IXSLProcessor *pIXSLProcessor=NULL;
   VARIANT varValue;

   try
   {
      hr = CoCreateInstance(CLSID_XSLTemplate, NULL, CLSCTX_SERVER,
            IID_IXSLTemplate, (LPVOID*)(&pIXSLTemplate));
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXSLTemplate)
      {
         hr=CoCreateInstance(CLSID_FreeThreadedDOMDocument, NULL,
               CLSCTX_SERVER, IID_IXMLDOMDocument2,
               (LPVOID*)(&pStyleSheet));
         SUCCEEDED(hr) ? 0 : throw hr;

         if(pStyleSheet)
         {
            hr=pStyleSheet->put_async(VARIANT_FALSE);
            if(SUCCEEDED(hr))
            {
               hr=pStyleSheet->load(_variant_t
                  (_T("d:\\inetpub\\wwwroot\\samplexsl.xml")), &sResult);
               if(SUCCEEDED(hr) && (sResult==VARIANT_TRUE))
               {
                  hr=pIXSLTemplate->putref_stylesheet(pStyleSheet);
                  if(SUCCEEDED(hr))
                  {
                     hr=pIXSLTemplate->createProcessor(&pIXSLProcessor);
                     SUCCEEDED(hr) ? 0 : throw hr;
                     if(pIXSLProcessor)
                     {
                        hr=CoCreateInstance(CLSID_DOMDocument, NULL,
                           CLSCTX_SERVER, IID_IXMLDOMDocument2,
                           (LPVOID*)(&pIXMLDOMDocument));
                        SUCCEEDED(hr) ? 0 : throw hr;

                        if(pIXMLDOMDocument)
                        {
                           hr=pIXMLDOMDocument->put_async(VARIANT_FALSE);
                           if(SUCCEEDED(hr))
                           {
                              hr=pIXMLDOMDocument->load(_variant_t(
                  _T("d:\\inetpub\\wwwroot\\sampleXSLWithObject.xml")),                                  &sResult);
                              if(SUCCEEDED(hr) && (sResult==VARIANT_TRUE))
                              {
                                 hr=pIXSLProcessor->put_input(_variant_t
                                    (pIXMLDOMDocument));
                                 if(SUCCEEDED(hr))
                                 {
                                    hr=CoCreateInstance(
                                       CLSID_FreeThreadedDOMDocument,
                                       NULL, CLSCTX_SERVER,
                                       IID_IXMLDOMDocument2,
                                       (LPVOID*)(&pDOMObject));
                                    if(SUCCEEDED(hr) && pDOMObject)
                                    {
                                       hr=pDOMObject->put_async(
                                          VARIANT_FALSE);
                                       if(SUCCEEDED(hr))
                                       {
                                          hr=pDOMObject->loadXML(
                                          _T("<foo>foo</foo>"), &sResult);
                                          if(SUCCEEDED(hr))
                                          {
                                             hr=pIXSLProcessor->addObject(
                                                pDOMObject, _T("urn:my-
                                                object"));
                                             if(SUCCEEDED(hr))
                                                bResult=TRUE;
                                          }
                                       }
                                    }

                                    hr=pIXSLProcessor->transform(
                                       &sResult);
                                    if(SUCCEEDED(hr)&&(sResult==
                                       VARIANT_TRUE))
                                    {
                                       pIXSLProcessor->get_output(
                                          &varValue);
                                       ::MessageBox(NULL,
                                          _bstr_t(varValue),
                                          _T("Transformed Output"),
                                          MB_OK);
                                    }
                                    RELEASE(pDOMObject);
                                 }
                              }
                           }
                           RELEASE(pIXMLDOMDocument);
                        }
                     }
                  }
               }
            }
            RELEASE(pStyleSheet);
         }
         RELEASE(pIXSLTemplate);
      }
   }
   catch(...)
   {
      CHECK_AND_RELEASE(pIXSLTemplate);
      CHECK_AND_RELEASE(pStyleSheet);
      CHECK_AND_RELEASE(pIXMLDOMDocument);
      CHECK_AND_RELEASE(pDOMObject);
      DisplayErrorToUser();
   }
   return bResult;
}

The stylesheet – "d:\\inetpub\\wwwroot\\sampleXSLWithObject.xml"

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
       version="1.0" xmlns:myObj="urn:my-object">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:element name="bar">
<xsl:value-of select="myObj:get-text()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

Output (in a message box)

<?xml version="1.0" encoding="UTF-16"?>
<bar>
foo
</bar>

Requirements

Implementation: Msxml2.dll

Header and IDL files: Msxml.h, Xmldom.idl

Version-Independent ProgID: Msxml2.XSLTemplate

Version-Independent CLSID: 2933BF94-7B36-11d2-B20E-00C04F983E60

Version-Dependent ProgID: Msxml2.XSLTemplate.2.6

Version-Dependent CLSID: f5078f21-c551-11d3-89b9-0000f81fe221


addObject Method

Microsoft XML SDK 2.6 - XML Reference

IXSLProcessor::addObject Method

Used to pass objects into a style sheet. Numbers are coerced to double, everything is coerced into a string, and objects return an error.

Visual Basic Syntax

objXSLProcessor.addObject(obj, namespaceURI)

C/C++ Syntax

HRESULT addObject (IDispatch* obj, BSTR namespaceURI);

Parameters

obj [in]
Object to pass in.
namespaceURI [in]
The namespace that will be used inside the style sheet to identify the object.

C/C++ Return Values

E_FAIL if readyState is READYSTATE_INTERACTIVE.

See Also

IXSLProcessor Interface


addParameter Method

Microsoft XML SDK 2.6 - XML Reference

IXSLProcessor::addParameter Method

Allows you to pass variables into a style sheet that will be referenced by <xsl:param> within the style sheet.

Visual Basic Syntax

objXSLProcessor.addParameter(baseName, parameter, namespaceURI)

C/C++ Syntax

HRESULT addParameter (BSTR baseName, VARIANT parameter, BSTR
namespaceURI);

Parameters

baseName [in]
The name that will be used inside the style sheet to identify the parameter context.
parameter [in]
A number, boolean, string, node list, or node. Passing in a single node will produce a nodelist that contains one node (shortcut).
namespaceURI [in, optional]
Optional namespace.

C/C++ Return Values

E_FAIL if readyState is READYSTATE_INTERACTIVE.

Remarks

The addParameter method can be called in on TransformNode handlers and between transform calls (in async processing), and further processing will use the updated parameter.

C/C++ Example

BOOL XSLProcessorAddParameterDemo ()
{
   BOOL bResult = FALSE;
   short sResult = FALSE;
   HRESULT hr;
   IXMLDOMDocument2 *pStyleSheet=NULL;
   IXSLTemplate *pIXSLTemplate=NULL;
   IXSLProcessor *pIXSLProcessor=NULL;
   VARIANT varValue;

   try
   {
      hr = CoCreateInstance(CLSID_XSLTemplate, NULL, CLSCTX_SERVER,
         IID_IXSLTemplate, (LPVOID*)(&pIXSLTemplate));
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXSLTemplate)
      {
         hr=CoCreateInstance(CLSID_FreeThreadedDOMDocument, NULL,
            CLSCTX_SERVER, IID_IXMLDOMDocument2, (LPVOID*)(&pStyleSheet));
         SUCCEEDED(hr) ? 0 : throw hr;

         if(pStyleSheet)
         {
            hr=pStyleSheet->put_async(VARIANT_FALSE);
            if(SUCCEEDED(hr))
            {
               hr=pStyleSheet->load(_variant_t
                  (_T("d:\\inetpub\\wwwroot\\samplexsl.xml")), &sResult);
               if(SUCCEEDED(hr) && (sResult==VARIANT_TRUE))
               {
                  hr=pIXSLTemplate->putref_stylesheet(pStyleSheet);
                  if(SUCCEEDED(hr))
                  {
                     hr=pIXSLTemplate->createProcessor(&pIXSLProcessor);
                     SUCCEEDED(hr) ? 0 : throw hr;
                     if(pIXSLProcessor)
                     {
                        hr=CoCreateInstance(CLSID_DOMDocument, NULL,
                           CLSCTX_SERVER, IID_IXMLDOMDocument2,
                           (LPVOID*)(&pIXMLDOMDocument));
                        SUCCEEDED(hr) ? 0 : throw hr;

                        if(pIXMLDOMDocument)
                        {
                           hr=pIXMLDOMDocument->put_async(VARIANT_FALSE);
                           if(SUCCEEDED(hr))
                           {
                              hr=pIXMLDOMDocument->load(_variant_t(
                     _T("d:\\inetpub\\wwwroot\\sampleXSLWithParam.xml")),                                  &sResult);
                              if(SUCCEEDED(hr) && (sResult==VARIANT_TRUE))
                              {
                                 hr=pIXSLProcessor->put_input(_variant_t
                                     (pIXMLDOMDocument));
                                 if(SUCCEEDED(hr))
                                 {
                                    hr=pIXSLProcessor->addParameter(
                                       _T("fooText"), _variant_t(_T("Add                                        Parameter Test")), _T(""));
                                    if(SUCCEEDED(hr))
                                       bResult=TRUE;
                                       hr=pIXSLProcessor->transform(
                                          &sResult);
                                    if(SUCCEEDED(hr)&& (sResult ==
                                       VARIANT_TRUE))
                                    {
                                       pIXSLProcessor->get_output(
                                          &varValue);
                                       ::MessageBox(NULL,
                                       _bstr_t(varValue),
                                       _T("Transformed Output"), MB_OK);
                                    }
                                 }
                              }
                           }
                           RELEASE(pIXMLDOMDocument);
                        }
                     }
                  }
               }
            }
            RELEASE(pStyleSheet);
         }
         RELEASE(pIXSLTemplate);
      }
   }
   catch(...)
   {
      CHECK_AND_RELEASE(pIXSLTemplate);
      CHECK_AND_RELEASE(pStyleSheet);
      CHECK_AND_RELEASE(pIXMLDOMDocument);
      DisplayErrorToUser();
   }
   return bResult;
}

The stylesheet – "d:\\inetpub\\wwwroot\\sampleXSLWithParam.xml"

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0" xmlns:myObj="urn:my-object">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="fooText"/>

<xsl:template match="/">
<xsl:element name="bar">
<xsl:value-of select="$fooText"/>
</xsl:element>
</xsl:template>

</xsl:stylesheet>

Output (in a message box)

<?xml version=”1.0” encoding=”UTF-16”?>
<bar>
Add Parameter Test
</bar>

See Also

XSLTemplate JScript Examples

IXSLProcessor Interface


input Method

Microsoft XML SDK 2.6 - XML Reference

IXSLProcessor::input Method

IXSLProcessor::get_input Method

Specifies which XML input tree to transform. Setting this property resets the state of the processor.

Visual Basic Syntax

Set objXMLDOMNode = objXSLProcessor.input

C/C++ Syntax

HRESULT get_input (VARIANT * pVar);

Parameters

pVar [out]
The input XML to apply the transformation to. This can be any object that supports ISequentialStream, IStream, or an IXMLDOMNode. If applying the transformation to an IXMLDOMNode, any node type and any threading model can be specified. If this argument is Null, the existing input will be cleared.

C/C++ Return Values

E_FAIL
Value returned if readyState is READYSTATE_INTERACTIVE.
E_INVALIDARG
Value returned if the variant is not VT_DISPATCH or VT_UNKNOWN.
E_NOINTERFACE
Value returned if the argument does not support IStream or IXMLDOMNode.

IXSLProcessor::put_input Method

Specifies which XML input tree to transform. Setting this property resets the state of the processor.

Visual Basic Syntax

objXMLDOMXSLProcessor.input = objXMLDOMNode

C/C++ Syntax

HRESULT put_input (VARIANT pVar);

Parameters

pVar [in]
The XML input tree to transform.

C/C++ Return Values

E_FAIL
Value returned if readyState is READYSTATE_INTERACTIVE.
E_INVALIDARG
Value returned if the variant is not VT_DISPATCH or VT_UNKNOWN.
E_NOINTERFACE
Value returned if the argument does not support IStream or IXMLDOMNode.

See Also

XSLTemplate JScript Examples

IXSLProcessor Interface


output Method

Microsoft XML SDK 2.6 - XML Reference

IXSLProcessor::output Method

IXSLProcessor::get_output Method

Used to get a custom output object to which to write the result of the transformation.

Visual Basic Syntax

objXSLProcessor.output(objoutput)

C/C++ Syntax

HRESULT get_output (VARIANT* pOutput);

Parameters

pOutput [out]
The custom output object or a BSTR containing the transformation result.

C/C++ Return Values

E_NOINTERFACE
Value returned if the given output object doesn’t support a supported interface.
E_FAIL
Value returned if readyState is READYSTATE_INTERACTIVE.

Remarks

When a new transformation is started, the processor will QueryInterface this output object for a new IStream interface. When the transformation is complete or when you call reset, this interface is released. The only method that is used on the IStream interface is Write. The bytes written to the stream are encoded according to the encoding attribute on the <xsl:output> element.

If you don’t provide a custom output object, a BSTR is returned when you read the output property that contains the incrementally buffered transformation result. Reading this property has the side effect of resetting that internal buffer, so that each time you read the property you get the next chunk of output. In this case, the output is always generated in Unicode encoding, and the encoding attribute on the <xsl:output> element is ignored.

You cannot change the output object midway through a transformation.

IXSLProcessor::put_output Method

Used to provide a custom output object to write the result of the transformation to. This can be any object that supports IStream, IPersistStream, IXMLDOMDocument, ASP IResponse, or ADODB.Stream.

Visual Basic Syntax

objXSLProcessor.output(output)

C/C++ Syntax

HRESULT put_output (VARIANT pOutput);

Parameters

pOutput [in]
The object to write the output of the transformation to.

C/C++ Return Values

E_NOINTERFACE
Value returned if the given output object doesn’t support a supported interface.
E_FAIL
Value returned if readyState is READYSTATE_INTERACTIVE.

Remarks

When a new transformation is started, the processor will QueryInterface this output object for a new IStream interface. When the transformation is complete or when you call reset, this interface is released. The only method that is used on the IStream interface is Write. The bytes written to the stream are encoded according to the encoding attribute on the <xsl:output> element.

If you don’t provide a custom output object, a BSTR is returned when you read the output property that contains the incrementally buffered transformation result. Reading this property has the side effect of resetting that internal buffer, so that each time you read the property you get the next chunk of output. In this case, the output is always generated in Unicode encoding, and the encoding attribute on the <xsl:output> element is ignored.

You cannot change the output object midway through a transformation.

See Also

Examples

IXSLProcessor Interface


ownerTemplate Method

Microsoft XML SDK 2.6 - XML Reference

IXSLProcessor::ownerTemplate Method

Returns the style sheet template that was used to create this XSLProcessor object.

Visual Basic Syntax

Set objXSLTemplate = objXSLProcessor.ownerTemplate

C/C++ Syntax

HRESULT get_ownerTemplate (IXSLTemplate** ppTemplate);

Parameters

ppTemplate [out]
The returned style sheet template property.

Remarks

The processor keeps the template alive, so even if the user releases the template object this property can still be used to return the original template object.

Note The stylesheet property on the returned template may be different from the stylesheet property on this processor object.

See Also

IXSLProcessor Interface


readyState Method

Microsoft XML SDK 2.6 - XML Reference

IXSLProcessor::readyState Method

Returns the current state of the processor, as follows:

State | Description
READYSTATE_UNINITIALIZED (0) | This is the initial state until all the required properties are set (in particular, the input property). Calling transform at this point returns an error.
READYSTATE_LOADED (1) | State when all the required properties are set. You can now call transform.
READYSTATE_INTERACTIVE (2) | transform has been called but is not finished yet.
READYSTATE_COMPLETE (3) | The transformation is finished, but there may still be some output to read if you are reading the output as a BSTR. When you call reset or set any property, the state reverts to READYSTATE_LOADED. If a required property is cleared, it reverts to READYSTATE_UNINITIALIZED.

Visual Basic Syntax

lReadyState = objXSLProcessor.readyState

C/C++ Syntax

HRESULT readyState (long* pReadyState);

Parameters

pReadyState [out]
The state of the current transformation.

See Also

IXSLProcessor Interface


reset Method

Microsoft XML SDK 2.6 - XML Reference

IXSLProcessor::reset Method

Resets the state of the processor to the state it was in just before calling transform. It does not reset any other properties (for example, stylesheet or startMode).

Visual Basic Syntax

objXSLProcessor.reset

C/C++ Syntax

HRESULT reset();

Remarks

Using reset will cause any internally buffered output to be thrown away (that is, it aborts the asynchronous transformation). Calling reset results in the readyState reverting from READYSTATE_INTERACTIVE back to READYSTATE_LOADED.

A re-entrant call to reset from inside the scope of a call to transform results in that transform call finishing as soon as it can, returning E_FAIL with the error message "User aborted transform."

See Also

IXSLProcessor Interface


setStartMode Method

Microsoft XML SDK 2.6 - XML Reference

IXSLProcessor::setStartMode Method

Performs subsets of a larger XSLT transformation by selecting the XSLT mode to start with. This makes it possible to minimize the amount of XSLT processing.

The default value of the start mode is the empty string, "".

Visual Basic Syntax

objXSLProcessor.setStartMode(mode, namespace)

C/C++ Syntax

HRESULT setStartMode(BSTR mode, BSTR namespace);

Parameters

mode [in]
The desired mode as a string. It must be the base name part of the qualified name. (For more information, see http://www.w3.org/TR/WD-xslt#qname and http://www.w3.org/TR/REC-xml-names/.)
namespace [in, optional]
The full namespace URI to fully qualify the start mode name.

C/C++ Return Values

E_FAIL
Value returned if readyState is READYSTATE_INTERACTIVE.
E_INVALIDARG
Value returned if the mode base name contains a colon character or is an invalid name.

Remarks

Using setStartMode is essentially the same as your XSLT style sheet starting with the following rule:

<xsl:template match="/">
   <xsl:apply-templates select="*" mode="{mode}"/>
</xsl:template>

See Also

IXSLProcessor Interface


startMode Method

Microsoft XML SDK 2.6 - XML Reference

IXSLProcessor::setStartMode Method

Performs subsets of a larger XSL transformation by selecting the XSL mode to start with. This makes it possible to minimize the amount of XSL processing.

The default value of the start mode is the empty string, "".

Visual Basic Syntax

objXSLProcessor.setStartMode(mode, namespace)

C/C++ Syntax

HRESULT setStartMode(BSTR mode, BSTR namespace);

Parameters

mode [in]
The desired mode as a string. It must be the base name part of the qualified name. (For more information, see http://www.w3.org/TR/WD-xslt#qname and http://www.w3.org/TR/REC-xml-names/.)
namespace [in, optional]
The full namespace URI to fully qualify the start mode name.

C/C++ Return Values

E_FAIL
Value returned if readyState is READYSTATE_INTERACTIVE.
E_INVALIDARG
Value returned if the mode base name contains a colon character or is an invalid name.

Remarks

Using setStartMode is essentially the same as your XSL style sheet starting with the following rule:

<xsl:template match="/">
  <xsl:apply-templates select="*" mode="{mode}"/>
</xsl:template>

See Also

IXSLProcessor Interface


startModeURI Method

Microsoft XML SDK 2.6 - XML Reference

IXSLProcessor::startModeURI Method

Returns the namespace URI part of the start mode, which is a qualified name. (For more information, see http://www.w3.org/TR/WD-xslt#modes.) The default value of this property is the empty string, "".

Visual Basic Syntax

strNamespace = objXSLProcessor.startModeURI

C/C++ Syntax

HRESULT get_startModeURI(BSTR * namespaceURI);

Parameters

namespaceURI [out]
The namespace URI of the starting XSL mode.

See Also

IXSLProcessor Interface


stylesheet Method

Microsoft XML SDK 2.6 - XML Reference

IXSLProcessor::stylesheet Method

Provides the XSL style sheet that is to be compiled into the XSL template.

Visual Basic Syntax

Set objXMLDOMNode = objXSLProcessor.stylesheet

C/C++ Syntax

HRESULT get_stylesheet (IXMLDOMNode** stylesheet);

Parameters

stylesheet [out]
The node that represents an XSL style sheet. This must be a free-threaded document object.

C/C++ Return Values

E_FAIL
Value returned if the document is not a valid style sheet or if the document is free-threaded.

Remarks

The style sheet document is made read-only, while the style sheet belongs to the template or any associated processor. The read-only flag is removed when the style sheet is released from all templates and/or processing objects.

The processor objects created by this template take a "snapshot" of the current style sheet and always use that same style sheet, so they are actually isolated from changes to the stylesheet property. The only way to update the processor to the new style sheet is to create a new processor.

See Also

IXSLProcessor Interface


transform Method

Microsoft XML SDK 2.6 - XML Reference

IXSLProcessor::transform Method

Starts the transformation process or resumes a transform that previously returned VARIANT_FALSE. When the end of the transformation is reached successfully, VARIANT_TRUE is returned.

Visual Basic Syntax

boolValue = objXSLProcessor.transform

C/C++ Syntax

HRESULT transform (VARIANT_BOOL* pDone);

Parameters

pDone [out]
The returned success message.

C/C++ Return Values

E_FAIL
Value returned if readyState is not READYSTATE_LOADED or READYSTATE_COMPLETE, or if a transformation error is encountered (for example, a script engine error).
VARIANT_FALSE
Value returned if the end of the input tree is reached and more input is pending.
VARIANT_TRUE
Value returned if the entire transformation is completed successfully.

See Also

IXSLProcessor Interface


IXSLTemplate Interface

Used for caching compiled XSLT templates.

Methods

createProcessor | Creates a rental-model IXSLProcessor that will use this template.
stylesheet | Provides the XSL style sheet to compile into an XSL template.

Remarks

Before the Microsoft 2.6 XML Parser (MSXML), every call to transformNode or transformNodeToObject had to recompile the style sheet because the style sheet was simply passed as an XMLDOMNode. You can increase performance by caching the compiled style sheet and reusing it with the XSLTemplate object.

To cache a compiled XSLT style sheet, load an XSLT style sheet into an XSLTemplate object. This object is free-threaded and stateless, so it can be stored in shared .ASP application state. Then to transform a given document using this template, create an XSLProcessor object using the createProcessor method. The XSLProcessor object stores the state for one transform call and has a rental-threading model.

Example

The following C++ example demonstrates the use of a single style sheet with multiple XML files. The style sheet is loaded once and cached into the XSLTemplate object.

BOOL XSLTemplateDemo()
{
   BOOL bResult = FALSE;
   short sResult = FALSE;
   IXMLDOMDocument2 *pStyleSheet=NULL;
   IXMLDOMDocument2 *pIXMLDOMDocument2 =NULL;
   IXSLTemplate *pIXSLTemplate=NULL;
   IXSLProcessor *pIXSLProcessor=NULL;
   IXMLDOMDocument2 *pIXMLDOMDocument=NULL;
   HRESULT hr;
   VARIANT varValue;

   try
   {
      hr = CoCreateInstance(CLSID_XSLTemplate, NULL, CLSCTX_SERVER,
         IID_IXSLTemplate, (LPVOID*)(&pIXSLTemplate));
      SUCCEEDED(hr) ? 0 : throw hr;

      if(pIXSLTemplate)
      {
         hr=CoCreateInstance(CLSID_FreeThreadedDOMDocument, NULL,
                  CLSCTX_SERVER, IID_IXMLDOMDocument2,
                  (LPVOID*)(&pStyleSheet));
         SUCCEEDED(hr) ? 0 : throw hr;

         if(pStyleSheet)
         {
            hr=pStyleSheet->put_async(VARIANT_FALSE);
            if(SUCCEEDED(hr))
            {
               hr=pStyleSheet->load(_variant_t(
               _T("d:\\inetpub\\wwwroot\\samplexsl.xml")), &sResult);
               if(SUCCEEDED(hr) && (sResult==VARIANT_TRUE))
               {
                  hr=pIXSLTemplate->putref_stylesheet(pStyleSheet);
                  if(SUCCEEDED(hr))
                  {
                     hr=pIXSLTemplate->createProcessor(&pIXSLProcessor);
                     SUCCEEDED(hr) ? 0 : throw hr;
                     if(pIXSLProcessor)
                     {
                        hr=CoCreateInstance(CLSID_DOMDocument, NULL,
                              CLSCTX_SERVER, IID_IXMLDOMDocument2,
                                    (LPVOID*)(&pIXMLDOMDocument));
                        SUCCEEDED(hr) ? 0 : throw hr;

                        if(pIXMLDOMDocument)
                        {
                           hr=pIXMLDOMDocument->put_async(VARIANT_FALSE);
                           if(SUCCEEDED(hr))
                           {
                              hr=pIXMLDOMDocument->load(_variant_t(
                           _T("d:\\inetpub\\wwwroot\\samplexmldtd.xml")),
                                 &sResult);
                              if(SUCCEEDED(hr) && (sResult==VARIANT_TRUE))
                              {
                                 hr=pIXSLProcessor->put_input(_variant_t(
                                    pIXMLDOMDocument));
                                 if(SUCCEEDED(hr))
                                 {
                                    hr=pIXSLProcessor->transform(
                                             &sResult);
                                    if(SUCCEEDED(hr)&&(sResult==
                                                VARIANT_TRUE))
                                    {
                                       pIXSLProcessor->get_output(
                                                &varValue);
                                       ::MessageBox(NULL,
                                          _bstr_t(varValue),
                                          _T("Transformed
                                             Output"), MB_OK);
                                    }
                                 }
                              }
                           }
                           RELEASE(pIXMLDOMDocument);
                        }
                           // Load another document.
                        hr=CoCreateInstance(CLSID_DOMDocument, NULL,
                              CLSCTX_SERVER, IID_IXMLDOMDocument2,
                                 (LPVOID*)(&pIXMLDOMDocument2));
                        SUCCEEDED(hr) ? 0 : throw hr;

                        if(pIXMLDOMDocument2)
                        {
                           hr=pIXMLDOMDocument2->put_async(VARIANT_FALSE);
                           if(SUCCEEDED(hr))
                           {
                              // Load some XML into the document.
                              hr=pIXMLDOMDocument2->loadXML(
                                 _T("<COLLECTION\ xmlns:dt=\"urn:schemas-
                                 microsoft-\ com:datatypes\">\
                                 <DATE dt:dt=\"datetime\">1998-10-
                                       13T15:56:00\
                                          </DATE><BOOK><TITLE>Cosmos</TITLE> \
                                 <AUTHOR>Carl Sagan</AUTHOR> \
                                 <PUBLISHER>Ballantine Books</PUBLISHER> \
                                 </BOOK> \
                                 </COLLECTION>"), &sResult);
                              if(SUCCEEDED(hr) && (sResult==VARIANT_TRUE))
                              {
                                 // Use the same processor.
                                 hr=pIXSLProcessor->put_input(_variant_t(
                                    pIXMLDOMDocument2));
                                 if(SUCCEEDED(hr))
                                 {
                                    hr=pIXSLProcessor->transform(
                                             &sResult);
                                    if(SUCCEEDED(hr)&&(sResult==
                                                VARIANT_TRUE))
                                    {
                                       pIXSLProcessor->get_output(
                                                &varValue);
                                       ::MessageBox(NULL,
                                          _bstr_t(varValue),
                                          _T("Transformed
                                    Output"), MB_OK);
                                    }
                                 }
                              }
                           }
                           RELEASE(pIXMLDOMDocument2);
                        }
                        RELEASE(pIXSLProcessor);
                     }
                  }
               }
            }
            RELEASE(pStyleSheet);
         }
         RELEASE(pIXSLTemplate);
      }
   }
   catch(...)
   {
      CHECK_AND_RELEASE(pIXSLProcessor);
      CHECK_AND_RELEASE(pIXSLTemplate);
      CHECK_AND_RELEASE(pStyleSheet);
      CHECK_AND_RELEASE(pIXMLDOMDocument);
       CHECK_AND_RELEASE(pIXMLDOMDocument2);
      DisplayErrorToUser();
   }
   return bResult;
}

Requirements

Implementation: Msxml2.dll, Msxml3.dll

Header and IDL files: Msxml.h, Xmldom.idl

Version-Independent ProgID: Msxml2.XSLTemplate

Version-Independent CLSID: 2933BF94-7B36-11d2-B20E-00C04F983E60

Version-Dependent ProgID: Msxml2.XSLTemplate.2.6

Version-Dependent CLSID: F5078F21-C551-11D3-89B9-0000F81FE221

See Also

Examples


createProcessor Method

Microsoft XML SDK 2.6 - XML Reference

IXSLTemplate::createProcessor Method

Creates a rental-model IXSLProcessor that will use this template. Multiple processors can be created from the same XSLTemplate object.

Visual Basic Syntax

Set objXSLProcessor = objXSLTemplate.createProcessor

C/C++ Syntax

HRESULT createProcessor(IXSLProcessor** ppProcessor);

Parameters

ppProcessor [out]
The returned processor associated with this template.

C/C++ Return Values

E_OUTOFMEMORY

E_FAIL
Value returned if the template has no style sheet.

See Also

IXSLTemplate Interface


stylesheet Method

Microsoft XML SDK 2.6 - XML Reference

IXSLTemplate::stylesheet Method

IXSLTemplate::get_stylesheet Method

Returns the XSL style sheet to compile into an XSL template.

Visual Basic Syntax

Set objXMLDOMNode = objXSLTemplate.stylesheet

C/C++ Syntax

HRESULT get_stylesheet(IXMLDOMNode** stylesheet);

Parameters

stylesheet [out]
The node that represents an XSLT style sheet. This must be a free-threaded document object.

C/C++ Return Values

E_FAIL
Value returned if the document is not a valid style sheet or if the document is not free-threaded.

Remarks

The style sheet document is made read-only, while the style sheet belongs to the template or any associated processor. The read-only flag is removed when the style sheet is released from all templates and/or processor objects.

The processor objects created by this template take a "snapshot" of the current style sheet and always use the same style sheet, so they are isolated from changes to this stylesheet property. The only way to update the processor to the new style sheet is to create a new processor (this satisfies the ASP scenarios shown in the IXSLTemplate example).

IXSLTemplate::put_stylesheet Method

Provides the XSL style sheet to compile into an XSL template. The style sheet document is made read-only, while the style sheet belongs to the template or any associated processor. The read-only flag is removed when the style sheet is released from all templates and/or processor objects.

The processor objects created by this template take a "snapshot" of the current style sheet and always use the same style sheet, so they are isolated from changes to this stylesheet property. The only way to update the processor to the new style sheet is to create a new processor (this satisfies the ASP scenarios shown in the XSLTemplate example).

Visual Basic Syntax

objXSLTemplate.stylesheet = objXMLDOMNode

C/C++ Syntax

HRESULT putref_stylesheet(IXMLDOMNode* stylesheet);

Parameters

stylesheet [in]
The node that represents an XSLT style sheet. This must be a free-threaded document object.

C/C++ Return Value

E_FAIL
Value returned if the document is not a valid style sheet or if the document is not free-threaded.

See Also

IXSLTemplate Interface


IXTLRuntime Interface

Implements methods that can be called from within XSL style sheets.

This interface inherits from IXMLDOMNode.

Methods

absoluteChildNumber | Returns the number of the node relative to all siblings.
ancestorChildNumber | Returns the number of the nearest ancestor of the node with the requested node name.
childNumber | Returns the number of the node relative to siblings of the same node name.
depth | Returns the depth within the document tree at which the specified node appears.
formatDate | Formats the supplied date using the specified formatting options.
formatIndex | Formats the supplied integer using the specified numerical system.
formatNumber | Formats the supplied number using the specified format.
formatTime | Formats the supplied time using the specified formatting options.
uniqueID | Returns the unique identifier for the supplied node.

Requirements

Implementation: Msxml.dll

Header and IDL files: Msxml.h, Xmldom.idl

See Also

Microsoft XSLT Reference


absoluteChildNumber Method

Microsoft XML SDK 2.6 - XML Reference

IXTLRuntime::absoluteChildNumber Method

Returns the number of the node relative to all siblings.

Visual Basic Syntax

lValue = oXTLRuntime.absoluteChildNumber(objNode)

C/C++ Syntax

HRESULT absoluteChildNumber(IXMLDOMNode* pNode, long* pNumber);

Parameters

pNode [in]
Node for which the child number is to be returned.
pNumber [out]
Index for the node in its parent's list of all child nodes. The first entry in the list is assigned the value "1".

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

See Also

IXTLRuntime Interface


ancestorChildNumber Method

Microsoft XML SDK 2.6 - XML Reference

IXTLRuntime::ancestorChildNumber Method

Returns the number of the nearest ancestor of the node with the requested node name.

Visual Basic Syntax

lValue = oXTLRuntime.ancestorChildNumber(bstrNodeName, objNode)

C/C++ Syntax

HRESULT ancestorChildNumber(
    BSTR bstrNodeName,
    IXMLDOMNode* pNode,
    long* pNumber);

Parameters

bstrNodeName [in]
Node name of the desired ancestor.
pNode [in]
Node from which to start the search.
pNumber [out]
Number of the ancestor relative to its sibling nodes. The first entry in the list is assigned the value "1". Returns Null if there is no such ancestor.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

See Also

IXTLRuntime Interface


childNumber Method

Microsoft XML SDK 2.6 - XML Reference

IXTLRuntime::childNumber Method

Returns the number of the node relative to siblings of the same node name.

Visual Basic Syntax

lValue = oXTLRuntime.childNumber(objNode)

C/C++ Syntax

HRESULT childNumber(IXMLDOMNode* pNode, long* pNumber);

Parameters

pNode [in]
Node for which the child number is to be returned.
pNumber [out]
Index for the node in its parent's list of child nodes with that name. The first entry in the list is assigned the value "1".

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

See Also

IXTLRuntime Interface


depth Method

Microsoft XML SDK 2.6 - XML Reference

IXTLRuntime::depth Method

Returns the depth within the document tree at which the specified node appears.

Visual Basic Syntax

lValue = oXTLRuntime.depth(objNode)

C/C++ Syntax

HRESULT depth(IXMLDOMNode* pNode, long* pDepth);

Parameters

pNode [in]
Node for which the depth is to be returned.
pDepth [out]
Depth within the document tree at which the specified node appears. The documentElement, or root node, returns the value "0", its children return the value "1", and so on.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

See Also

IXTLRuntime Interface


formatDate Method

Microsoft XML SDK 2.6 - XML Reference

IXTLRuntime::formatDate Method

Formats the supplied date using the specified formatting options.

Visual Basic Syntax

strValue = oXTLRuntime.formatDate(varDate, bstrFormat, varDestLocale)

C/C++ Syntax

HRESULT formatDate(
    VARIANT varDate,
    BSTR bstrFormat,
    VARIANT varDestLocale,
    BSTR *pbstrFormattedString);

Parameters

varDate [in]
Date that is to be formatted.
bstrFormat [in]
Formatting specifics. The following codes are supported:
M | Months as 1–12
MM | Months as 01–12
MMM | Months as Jan–Dec
MMMM | Months as January–December
MMMMM | Months as the first letter of the month
d | Days as 1–31
dd | Days as 01–31
ddd | Days as Sun–Sat
dddd | Days as Sunday–Saturday
yy | Years as 00–99
yyyy | Years as 1900–9999
varDestLocale [optional]
Variant. Locale to use in determining the correct sequence to be employed in the date. If not supplied, the month-day-year sequence is used.

Note This parameter is not yet implemented.

pbstrFormattedString [out]
String containing the date in the requested format.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

See Also

IXTLRuntime Interface


formatIndex Method

Microsoft XML SDK 2.6 - XML Reference

IXTLRuntime::formatIndex Method

Formats the supplied integer using the specified numerical system.

Visual Basic Syntax

strValue = oXTLRuntime.formatIndex(lIndex, bstrFormat)

C/C++ Syntax

HRESULT formatIndex(
    long lIndex,
    BSTR bstrFormat,
    BSTR *pbstrFormattedString);

Parameters

lIndex [in]
Integer value that is to be converted.
bstrFormat [in]
Numbering system. The following codes are supported:
1 | Standard numbering system
01 | Standard numbering with leading zeroes
A | Uppercase sequence: A–Z, AA–ZZ
a | Lowercase sequence: a–z, aa–zz
I | Uppercase Roman numerals: I, II, III, IV, ...
i | Lowercase Roman numerals: i, ii, iii, iv, ...
pbstrFormattedString [out]
Integer value formatted using the specified numbering system.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

See Also

IXTLRuntime Interface


formatNumber Method

Microsoft XML SDK 2.6 - XML Reference

IXTLRuntime::formatNumber Method

Formats the supplied number using the specified format.

Visual Basic Syntax

strValue = oXTLRuntime.formatNumber(dblNumber, bstrFormat)

C/C++ Syntax

HRESULT formatNumber(
    double dblNumber,
    BSTR bstrFormat,
    BSTR *pbstrFormattedString);

Parameters

dblNumber [in]
Number to be formatted.
bstrFormat [in]
Formatting characteristics to be applied to the number. Zero or more of the following values can be present:
# Display only significant digits; do not display insignificant zeros.
0 (zero) | Display insignificant zeros if a number has fewer digits than there are zeros in the format.
? Add spaces for insignificant zeros on either side of the decimal point so that decimal points align with a fixed-point font. You can also use this symbol for fractions that have varying numbers of digits.
. Indicate the placement of the decimal point within the format.
, Display a comma as a thousands separator, or scale a number by a multiple of one thousand.
% Display a number as a percentage of 100.
E- | (Also "e-") Display a number in scientific format (exponential notation). If a format contains a zero (0) or a number sign (#) to the right of an exponent code, display the number in scientific format and insert an "E" or "e." The number of zeros or number signs to the right determines the number of digits in the exponent. Place a minus sign by negative exponents.
E+ | (Also "e+") Place a minus sign by negative exponents and a plus sign by positive exponents.
pbstrFormattedString [out]
String containing the formatted number.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

See Also

IXTLRuntime Interface


formatTime Method

Microsoft XML SDK 2.6 - XML Reference

IXTLRuntime::formatTime Method

Formats the supplied time using the specified formatting options.

Visual Basic Syntax

strValue = oXTLRuntime.formatTime(varTime, bstrFormat, varDestLocale)

C/C++ Syntax

HRESULT formatTime(
    VARIANT varTime,
    BSTR bstrFormat,
    VARIANT varDestLocale,
    BSTR *pbstrFormattedString);

Parameters

varTime [in]
Time value that is to be formatted.
bstrFormat [in]
Formatting to be applied to the time. The following codes are supported:
h | Hours as 0–23
hh | Hours as 00–23
m | Minutes as 0–59
mm | Minutes as 00–59
s | Seconds as 0–59
ss | Seconds as 00–59
AM/PM | Insert AM or PM, display hours as 12-hour clock
am/pm | Insert am or pm, display hours as 12-hour clock
A/P | Insert A or P, display hours as 12-hour clock
a/p | Insert a or p, display hours as 12-hour clock
[h]:mm | Elapsed time in hours; for example, 25.02
[mm]:ss | Elapsed time in minutes; for example, 63:46
[ss] | Elapsed time in seconds
ss.00 | Fractions of a second
varDestLocale [optional]
Variant. Locale to use in determining the correct sequence to be employed in the time.

Note This parameter is not yet implemented.

pbstrFormattedString [out]
String containing the time in the requested format.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

See Also

IXTLRuntime Interface


uniqueID Method

Microsoft XML SDK 2.6 - XML Reference

IXTLRuntime::uniqueID Method

Returns the unique identifier for the supplied node.

Visual Basic Syntax

lValue = oXTLRuntime.uniqueID(objNode)

C/C++ Syntax

HRESULT uniqueID(IXMLDOMNode* pNode, long* pID);

Parameters

pNode [in]
Node for which the unique identifier is to be returned.
pID [out]
Unique identifier for the specified node.

C/C++ Return Values

Returns S_OK if successful, or an error code otherwise.

See Also

IXTLRuntime Interface


XML Schemas, reference; schemas, reference

Microsoft XML SDK 2.6 - XML Reference

XML Schema Reference

Microsoft XML Parser (MSXML) 2.0 and subsequent versions include support for XML Schema.

The W3C XML Activity Page states: "While XML 1.0 supplies a mechanism, the Document Type Definition (DTD) for declaring constraints on the use of markup and automated processing of XML documents requires more rigorous and comprehensive facilities in this area. Requirements are for constraints on how the component parts of an application fit together, the document structure, attributes, datatyping, and so on. The W3C XML Schema Working Group is addressing means for defining the structure, content, and semantics of XML documents."

The XML Schema language is based on the XML-Data note submitted to the W3C. XML Schema as implemented in this release can be thought of as the subset of the XML-Data submission that corresponds to the feature set proposed for Document Content Description (DCD).

Microsoft is providing this release of XML Schema with data type support that might be useful for developers interested in building prototypes and gaining experience with schema and rich data types. Microsoft is actively involved in defining the emerging W3C XML schema standard and will track this effort. Developers should note that this version of XML Schema is subject to change.

Unlike the unique DTD syntax, XML Schema allows you to define the rules governing the relations between elements and attributes using a standard XML instance syntax, which can be parsed and managed using XML applications. XML Schema also adds support for namespaces, data types, and such useful features as range constraints.

The definitions in an XML schema are contained within the top-level Schema document element. The Schema document element definition must come from the namespace xmlns="urn:schemas-microsoft-com:xml-data." The namespace declaration is an XML processing instruction (PI) as defined in the W3C XML Namespace Working Draft. It is convenient to make the "Schema" namespace the default namespace to avoid having to prefix all definitions within a schema. This documentation uses the convention in which the "Schema" namespace is the default namespace and omits a prefix from all XML Schema element and attribute names.

The Schema document element in an XML Schema document should also contain namespace declarations for any other schemas, such as the namespace that defines the built-in data types for XML Schema.

<Schema name="myschema" xmlns="urn:schemas-microsoft-com:xml-data"
                        xmlns:dt="urn:schemas-microsoft-com:datatypes">
 <!-- ... -->
</Schema>

The Schema document element can contain any number of top-level declarations. The principal XML Schema elements include ElementType (which declares element types) and AttributeType (which declares attribute types). These elements must have an explicit name attribute that is unique within the schema. The element and attribute elements can appear within the ElementType element to constrain the content and attributes that can appear in elements of the named type. The ElementType attribute order and the group element allow you to further establish fine-grained definitions. The minOccurs and maxOccurs attributes allow you to define the number of occurrences of an element or group.

The content for an element of a given element type can be defined as one of four categories: empty (no content), text only, subelements only, or a mix of text and subelements. This choice is expressed in the ElementType attribute named content, which can take the values "empty," "textOnly," "eltOnly," and "mixed," respectively.

Element types and attribute types can constrain their values and contents to be instances of a particular data type, referenced from the datatype element namespace. To use the datatype namespace in a schema, it must be declared within the Schema element. The dt:type attribute can then be used to reference data types; in the Internet Explorer 5 implementation, element and attribute types can take "rich" data types. For a complete description of data types, see XML Data Types Reference.

Finally, note that in XML, schema files are optional and not required. Some XML applications do not require validation and do not specify a schema file. For a detailed description and examples of these elements, see the reference topic for each XML Schema element.

See Also

XML Schema Developer's Guide

XML DOM Reference


XML Schema Elements

The following table describes the XML Schema elements:

attribute | Refers to a declared attribute type that can appear within the scope of the named ElementType element.
AttributeType | Defines an attribute type for use within the Schema element.
datatype | Specifies the data type for the ElementType or AttributeType element.
description | Provides documentation about an ElementType or AttributeType element.
element | Refers to a declared element type that can appear within the scope of the named ElementType element.
ElementType | Defines an element type for use within the Schema element.
group | Organizes content into a group to specify a sequence.
Schema | Identifies the start of a schema definition.

XML Data Types Support

Support for data types is integrated into this release. For more information, see XML Data Types Reference.


attribute Element

Refers to a declared attribute type that can appear within the scope of the named ElementType element.

<attribute
  default="default-value"
  type="attribute-type"
  [required="{yes | no}"]>

Attributes

default
Default value for the attribute. This specified default takes precedence over any default provided on the AttributeType element it refers to.
type
Name of an AttributeType element defined in this schema (or another schema indicated by the specified namespace). The supplied value must correspond to the name attribute on the AttributeType element. Note that the type can include a namespace prefix.
required
Indicator of whether the attribute must be present on the element. Optional if the required attribute is present on the referenced AttributeType.

Element Information

Number of occurrences | Number of attribute types specified in the schema.
Parent elements | ElementType
Child elements | (No child elements)
Requires closing tag | Yes. XML Schema is an XML grammar, and like all XML grammars, all tags must have closing tags to satisfy the definition of well-formed.

Remarks

When the required attribute is set to "yes" and the default attribute specifies a default value, the supplied default value must always be the value, and documents containing other attribute values are invalid.

When the required attribute is set to "yes" and no default is specified, each element whose type is declared to have the attribute must supply its value.


AttributeType Element

Defines an attribute type for use within the Schema element.

<AttributeType
    default="default-value"
    dt:type="primitive-type"
    dt:values="enumerated-values"
    name="idref"
    required="{yes | no}">

Attributes

default
Default value for the attribute. The default value must be legal for that attribute instance. For example, when the attribute is an enumerated type, the default value must appear in the values list.
dt:type
Specifies the data type for this attribute type. In the implementation provided with Internet Explorer 5, an attribute can take one of the following types: entity, entities, enumeration, id, idref, idrefs, nmtoken, nmtokens, notation, or string. When the type "enumeration" is selected, the dt:values attribute should also be supplied, listing the allowed values. For more information about data types, see XML Data Types.
dt:values
When dt:type is set to "enumeration," this attribute lists the possible values.
name
Name of the attribute type. This attribute is required. References to this attribute type within an ElementType definition are made in the schema with the attribute element. The name supplied here corresponds to the type attribute of the attribute element.
required
Indicator of whether the attribute must be present on the element.

Element Information

Number of occurrences | Unlimited
Parent elements | ElementType, Schema
Child elements | datatype, description
Requires closing tag | Yes. XML Schema is an XML grammar and like all XML grammars, all tags must have closing tags to satisfy the definition of well-formed.

Remarks

An AttributeType declaration contained within an ElementType element defines that attribute type within the scope of the ElementType that contains it.

In XML Schema, as in XML 1.0, the order of appearance of attributes in an element is not constrained and a given attribute can appear no more than once in an element.

Example

The following example shows an enumeration and its DTD equivalent:

<AttributeType name="colors" dt:type="enumeration"
  dt:values="red green blue">
<!ATTLIST x colors (red | green | blue)>

The following example declares an attribute type called "myattr." This declaration constrains "myattr" attributes to contain only a name token.

<AttributeType name="myattr" dt:type="nmtoken"/>


datatype Element

Specifies the data type for the ElementType or AttributeType element.

<datatype
  dt:type="datatype">

Attributes

dt:type
Data type. For a list of valid values, see XML Data Types Preview.
<ElementType name="colors">
  <datatype dt:type="char"/>
</ElementType>

Element Information

Number of occurrences | 1
Parent elements | AttributeType, ElementType
Child elements | (No child elements)
Requires closing tag | Yes. XML Schema is an XML grammar, and like all XML grammars, all tags must have closing tags to satisfy the definition of well-formed.

Remarks

The datatype functionality is an extension offered by XML Schema that cannot be specified using the DTD syntax.


description Element

Provides documentation about an ElementType or AttributeType element.

<description>

Element Information

Number of occurrences | 1
Parent elements | AttributeType, ElementType, Schema, group, element, attribute
Child elements | Any well-formed XML content.
Requires closing tag | Yes. XML Schema is an XML grammar, and like all XML grammars, all tags must have closing tags to satisfy the definition of well-formed.

Remarks

Information supplied within the description element is not used in validation, but provides a standardized way to supply information that can be retrieved by tools.

Example

<ElementType name="Book">
  <description>
  This is how we describe the books we sell. Be sure to specify
  ageGroup!
  </description>
  <element type="ageGroup">
    <default>ADULT</default>
  </element>
</ElementType>


element Element

Refers to a declared element type that can appear within the scope of the named ElementType element.

<element
  type="element-type"
  [minOccurs="{0 | 1}"]
  [maxOccurs="{1 | *}"]
>

Attributes

type
Name of an ElementType element defined in this schema (or another schema indicated by the specified namespace). The supplied value must correspond to the name attribute on the ElementType element. Note that the type can include a namespace prefix.
minOccurs
Optional. Minimum number of times the reference element type can occur on the element. The following values can be assigned to this attribute:
0 | When the minimum value is zero, the specified element is not required; the element is optional.
1 | The specified element must occur at least once.
maxOccurs
Optional. Maximum number of times the element can occur on the element. The following values can be assigned to this attribute:
1 Can occur at most once.
* Unlimited number of occurrences.

Element Information

Number of occurrences | Number of element types defined in the schema.
Parent elements | ElementType, group
Child elements | (No child elements)
Requires closing tag | Yes. XML Schema is an XML grammar, and like all XML grammars, all tags must have closing tags to satisfy the definition of well-formed.

Remarks

ElementType declarations can constrain the content and attributes that appear in elements of the named type by referring to other element type or attribute type declarations.

The minOccurs and maxOccurs attributes have the default value "1." An element with neither attribute specified uses these default values and therefore must appear once and only once in a content model.


ElementType Element

Defines an element type for use within the Schema element.

<ElementType
  content="{empty | textOnly | eltOnly | mixed}"
  dt:type="datatype"
  model="{open | closed}"
  name="idref"
  order="{one | seq | many}">

Attributes

content
Indicator of whether the content must be empty or can contain text, elements, or both. The following values can be assigned to this attribute. The default value for the content attribute is "mixed." Note that when the content attribute is set to "mixed", the order attribute requires a "many" value.
empty | The element cannot contain content.
textOnly | The element can contain only text, not elements. Note that if the model attribute is set to "open," the element can contain text and other unnamed elements.
eltOnly | The element can contain only the specified elements. It cannot contain any free text.
mixed | The element can contain a mix of named elements and text. Mixed is the default value. Note that if the value of the content attribute is mixed, minOccurs and maxOccurs attributes will not trigger a validation error if the number of child elements is outside the specified boundary.

Example

<ElementType name="x" content="empty"/>
<ElementType name="x" content="textOnly"/>
<ElementType name="x" content="eltOnly">
  <element type="y"/>
</ElementType>
<ElementType name="x" content="mixed">
  <element type="q"/>
  <element type="r"/>
</ElementType>

The DTD equivalent is as follows:

empty: <!ELEMENT x EMPTY>
textOnly: <!ELEMENT x (#PCDATA)>
eltOnly: <!ELEMENT x y>
mixed: <!ELEMENT x (#PCDATA | q | r)*>
dt:type
Data type of the element. The valid data types are defined in the XML Data Types Preview.
model
Indicator of whether the content can include only what is defined in the content model or can contain content not specified in the model. Default is "open."
<ElementType name="x" model="open"/>

When the model is defined as open, the element can include additional elements or attributes not declared explicitly in the content model. These additional tags can come from the same namespace or a different namespace. If they are in the same namespace, there must be a corresponding ElementType or AttributeType definition for them in the schema.

When the model is defined as closed, the element cannot include elements and cannot include mixed content not specified in the content model. The DTD uses a closed model.

name
Name of the element. This attribute is required. If this element type is declared as a valid child of another element type, this name is specified within an element element.
<ElementType name="x">
</ElementType>

The DTD equivalent is as follows:

<!ELEMENT x EMPTY>
order
The following values can be assigned to this attribute.
one | Permits only one of a set of elements. Note that for a document to correctly validate when the one attribute is specified, the model attribute for the ElementType must be specified as "closed."
seq | Requires the elements to appear in the specified sequence.
many | Permits the elements to appear (or not appear) in any order. If you specify "many" for the order attribute, maxOccurs values are no longer relevant during validation.

The "seq" setting is needed to specify valid sequences. For example, it can be used to specify when a particular sequence is valid—such as "x1,y1" or "x2,y2"—but no other possible combinations are valid. The "seq" value serves the same role as parentheses in a DTD. The default value is "many."

The following example demonstrates the "one" setting. Note that the value of the model attribute must be "closed" when "one" is specified for the order attribute.

<ElementType name="z" content="eltOnly" order="one" model="closed">
<element type="x" minOccurs="1" maxOccurs="1"/>
<element type="y" minOccurs="1" maxOccurs="1"/>
</ElementType>

The following example demonstrates the "seq" setting:

<ElementType name="x" content="eltOnly" order="one" model="closed">
  <group order="seq" content="eltOnly" model="closed">
    <element type="x1"/>
    <element type="y1"/>
  </group>
  <group order="seq" content="eltOnly" model="closed">
    <element type="x2"/>
    <element type="y2"/>
  </group>
</ElementType>

The following example demonstrates the "many" setting:

<ElementType name="x" content="eltOnly" order="many" model="closed">
  <element type="q"/>
  <element type="r"/>
</ElementType>

It's important to note that the "many" setting of the order attribute makes the minOccurs and maxOccurs attributes irrelevant during validation. Rather than using the "many" setting for the order attribute in conjunction with minOccurs and maxOccurs, use the "seq" setting instead. For example, consider the following schema. Notice at the bottom of the sample that order="seq" is set.

<?xml version="1.0"?>
<Schema name="SampleSchema" xmlns="urn:schemas-microsoft-com:xml-data"
    xmlns:dt="urn:schemas-microsoft-com:datatypes">
    <ElementType name="PublisherID" model="closed" dt:type="ID"
      content="textOnly">
    </ElementType>
    <ElementType name="PublisherName" model="closed" dt:type="string"
      content="textOnly">
    </ElementType>

    <ElementType name="Publisher" model="closed" content="eltOnly"
      order="many">
      <element type="PublisherID"/>
      <element type="PublisherName"/>
    </ElementType>

    <ElementType name="Book" model="closed" content="eltOnly"
      order="seq">
    <element type="Publisher" minOccurs="1" maxOccurs="1"/>
    </ElementType>

</Schema>

Now if the following XML document is validated against the above schema, the validation fails because the number of <Publisher> elements exceeds the specified maxOccurs value of "1". However, if the order value is set to "many," the validation would incorrectly succeed because the maxOccurs="1" setting would be ignored.

<?xml version='1.0'?>
<Book xmlns="x-schema:orderschema.xml" >

<Publisher>
    <PublisherID>P1</PublisherID>
    <PublisherName>GG&G</PublisherName>
</Publisher>
<Publisher>
    <PublisherID>P2</PublisherID>
    <PublisherName>North</PublisherName>
</Publisher>

</Book>

Element Information

Number of occurrences | Unlimited
Parent elements | Schema
Child elements | Attribute, AttributeType, datatype, description, element, group
Requires closing tag | Yes. XML Schema is an XML grammar, and like all XML grammars, all tags must have closing tags to satisfy the definition of well-formed.

Remarks

The term "element type" refers to the element type of which all elements sharing a name are instances. Element types are declared in schemas; elements occur in documents. Element types are declared with the ElementType element type.

Example

<ElementType name="x">
  <!-- element content declarations go here -->
</ElementType>


group Element

Organizes content into a group to specify a sequence.

<group
  maxOccurs="{1 | *}"
  minOccurs="{0 | 1}"
  order="{one | seq | many}" >

Attributes

maxOccurs
Maximum number of times the group can occur. The following values can be assigned to this attribute:
1 Occurs at most once.
* Unlimited number of occurrences.
minOccurs
Minimum number of times the group can occur. The following values can be assigned to this attribute:
0 | Not required; the group is optional.
1 | Must occur at least once.
order
Constraints on the sequence of elements (and other contained groups) within this group. The following values can be assigned to this attribute:
one | Permits only one instance of each element contained in the group. (This corresponds to the | symbol in the DTD.)
seq | Requires the elements in the group to appear in the specified sequence.
many | Permits the elements in the group to appear (or not appear) in any order.

The "seq" setting is needed to specify valid sequences. For example, it can be used to specify when a particular sequence such as "x1,y1" or "x2,y2" is valid but no other possible combinations are valid. The "seq" value serves the same role as parentheses in a DTD.

Element Information

Number of occurrences | Unlimited
Parent elements | ElementType
Child elements | element, description
Requires closing tag | Yes. XML Schema is an XML grammar, and like all XML grammars, all tags must have closing tags to satisfy the definition of well-formed.

Remarks

The order attribute takes the same values as the order attribute of the ElementType element.

The minOccurs and maxOccurs attributes have the default value "1." A group with neither attribute must appear once and only once in a content model.

Examples

The following example demonstrates the "one" setting:

<ElementType name="z" order="one">
<element type="x">
<element type="y">
</ElementType>

The following represents a legal instance of the schema:

<z>
  <x/>
  <y></y>
</z>

The following example demonstrates the "seq" setting:

<ElementType name="x" order="one">
  <group order="seq">
    <element type="x1">
    <element type="y1">
  </group>
  <group order="seq">
    <element type="x2">
    <element type="y2">
  </group>
</ElementType>

The following two examples represent legal instances of this schema:

<x>
  <x1/>
  <y1/>
</x>

and:

<x>
  <x2/>
  <y2/>
</x>

The following example demonstrates the "many" setting:

<ElementType name="x" content="eltOnly" order="many">
  <element type="q">
  <element type="r">
</ElementType>

The following five examples represent all legal instances for this schema:

<x> </x>
<x> <q> </x>
<x> <r> </x>
<x> <q> <r> </x>
<x> <r> <q> </x>


Schema Element

Identifies the start of a schema definition.

<Schema
  name="schema-name"
  xmlns="namespace">

Attributes

name
Name of the schema.
xmlns
One or more namespaces for use with the schema.

Element Information

Number of occurrences | 1
Parent elements | (No parent elements)
Child elements | AttributeType, ElementType, description
Requires closing tag | Yes. XML Schema is an XML grammar, and like all XML grammars, all tags must have closing tags to satisfy the definition of well-formed.

Remarks

The Schema element is the document element (the highest-level element) in a schema definition.

The namespaces specified using the xmlns attribute must include the namespace for the schema-defining elements (Schema, ElementType, and so on). Any prefix can be used, but it is convenient to make the "Schema" namespace the default namespace to avoid having to use a prefix on every XML Schema element.

The Schema document element in an XML Schema should also contain namespace declarations for any other schemas, such as the namespace that defines the built-in data types for XML Schema.

Example

<Schema name="fooSchema" xmlns="urn:schemas-microsoft-com:xml-data"
                    xmlns:dt="urn:schemas-microsoft-com:datatypes">

</Schema>


XML Data Types Reference

In addition to the string and other primitive types defined by the XML 1.0 recommendation, the Microsoft XML Parser supports a rich set of additional data types. It also supports conversions between the primitive types and these rich data types.

Data types are referenced from the data type namespace "urn:schemas-microsoft-com:datatypes". To use data types within an XML Schema, declare the data type namespace within the schema document. This documentation assumes that the data type namespace has been assigned the "dt" prefix. For example:

<Schema name="myschema"
        xmlns="urn:schemas-microsoft-com:xml-data"
        xmlns:dt="urn:schemas-microsoft-com:datatypes">
 <!-- ... -->
</Schema>

See Also

XML Data Types | Primitive XML Data Types | Supported Data Type Conversions | XML Schema Developer's Guide | XML Schema Reference


XML Data Types

bin.base64 | MIME-style Base64 encoded binary BLOB.
bin.hex | Hexadecimal digits representing octets.
boolean | 0 or 1, where 0 == "false" and 1 == "true".
char | String, one character long.
date | Date in a subset ISO 8601 format, without the time data. For example: "1994-11-05". The date itself is not validated (for example, 2-31-99 will pass validation).
dateTime | Date in a subset of ISO 8601 format, with optional time and no optional zone. Fractional seconds can be as precise as nanoseconds. For example, "1988-04-07T18:39:09".
dateTime.tz | Date in a subset ISO 8601 format, with optional time and optional zone. Fractional seconds can be as precise as nanoseconds. For example: "1988-04-07T18:39:09-08:00".
fixed.14.4 | Same as "number" but no more than 14 digits to the left of the decimal point, and no more than 4 to the right.
float | Real number with no limit on digits; can potentially have a leading sign, fractional digits, and, optionally, an exponent. Punctuation as in U.S. English. Values range from 1.7976931348623157E+308 to 2.2250738585072014E-308.
int | Number with optional sign, no fractions, and no exponent.
number | Number with no limit on digits; can potentially have a leading sign, fractional digits, and, optionally, an exponent. Punctuation as in U.S. English. (Values have the same range as the most significant number, R8, 1.7976931348623157E+308 to 2.2250738585072014E-308.)
time | Time in a subset ISO 8601 format, with no date and no time zone. For example: "08:15:27".
time.tz | Time in a subset ISO 8601 format, with no date but optional time zone. For example: "08:1527-05:00".
i1 | Integer represented in one byte. A number with optional sign, no fractions, no exponent. For example: "1, 127, -128".
i2 | Integer represented in one word. A number with optional sign, no fractions, no exponent. For example: "1, 703, -32768".
i4 | Integer represented in four bytes. A number with optional sign, no fractions, no exponent. For example: "1, 703, -32768, 148343, -1000000000".
i8 | Integer represented in eight bytes. A number with optional sign, no fractions, no exponent, and 19-digit precision. Range is from –9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
r4 | Real number with seven-digit precision; can potentially have a leading sign, fractional digits, and, optionally, an exponent. Punctuation as in U.S. English. Values range from 3.40282347E+38F to 1.17549435E-38F.
r8 | Same as "float." Real number with 15-digit precision; can potentially have a leading sign, fractional digits, and, optionally, an exponent. Punctuation as in U.S. English. Values range from 1.7976931348623157E+308 to 2.2250738585072014E-308.
ui1 | Unsigned integer. A number, unsigned, no fractions, no exponent. For example: "1, 255".
ui2 | Unsigned integer, two bytes. A number, unsigned, no fractions, no exponent. For example: "1, 255, 65535".
ui4 | Unsigned integer, four bytes. A number, unsigned, no fractions, no exponent. For example: "1, 703, 3000000000".
ui8 | Unsigned integer, eight bytes. A number, unsigned, no fractions, no exponent. Range is 0 to 18,446,744,073,709,551,615.
uri | Universal Resource Identifier (URI). For example: "urn:schemas-microsoft-com:Office9".
uuid | Hexadecimal digits representing octets, optional embedded hyphens that are ignored. For example: "333C7BC4-460F-11D0-BC04-0080C7055A83".

See Also

XML Data Types Reference | Primitive XML Data Types | Supported Data Type Conversions | XML Schema Developer's Guide | XML Schema Reference


Primitive XML Data Types

The World Wide Web Consortium (W3C) XML 1.0 Recommendation also defines enumerated types (notations and enumerations) and a set of tokenized types. These types are referred to as "primitive types" within this Microsoft XML documentation.

The primitive types include the following, defined in Section 3.3.1 of the W3C XML 1.0 Recommendation:

entity | Represents the XML ENTITY type.
entities | Represents the XML ENTITIES type.
enumeration | Represents an enumerated type (supported on attributes only).
id | Represents the XML ID type.
idref | Represents the XML IDREF type.
idrefs | Represents the XML IDREFS type.
nmtoken | Represents the XML NMTOKEN type.
nmtokens | Represents the XML NMTOKENS type.
notation | Represents a NOTATION type.
string | Represents a string type.

See Also

XML Data Types Reference | XML Data Types | Supported Data Type Conversions | XML Schema Developer's Guide | XML Schema Reference


Supported Data Type Conversions

The following table lists the data type conversions supported by the Microsoft XML Parser in Microsoft® Internet Explorer 5:

Data type | Variant type
string | VT_BSTR, VT_BOOL, VT_CY, VT_DATE, VT_VARIANT, VT_DECIMAL, VT_UII, VT_I2, VT_I4, VT_R4, VT_R8, VT_EMPTY, VT_NULL, VT_ARRAY
number | VT_BSTR, VT_BOOL, VT_CY, VT_VARIANT, VT_DECIMAL, VT_UII, VT_I2, VT_I4, VT_R4, VT_R8, VT_EMPTY, VT_NULL
int | VT_BSTR, VT_BOOL, VT_VARIANT, VT_UII, VT_I2, VT_I4, VT_EMPTY, VT_NULL
float | VT_BSTR, VT_CY, VT_VARIANT, VT_DECIMAL, VT_UII, VT_I2, VT_I4, VT_R4, VT_R8, VT_EMPTY, VT_NULL
fixed.14.4 (currency) | VT_BSTR, VT_CY, VT_VARIANT, VT_DECIMAL, VT_UII, VT_I2, VT_I4, VT_R4, VT_EMPTY, VT_NULL
boolean | VT_BSTR, VT_BOOL, VT_VARIANT, VT_DECIMAL, VT_UII, VT_I2, VT_I4, VT_EMPTY, VT_NULL
dateTime | VT_BSTR, VT_DATE, VT_VARIANT
i1 | VT_BSTR, VT_BOOL, VT_VARIANT, VT_DECIMAL, VT_EMPTY, VT_NULL
i2 | VT_BSTR, VT_BOOL, VT_VARIANT, VT_DECIMAL, VT_I2, VT_EMPTY, VT_NULL
i4 | VT_BSTR, VT_BOOL, VT_VARIANT, VT_DECIMAL, VT_I2, VT_I4, VT_EMPTY, VT_NULL
i8 | VT_BSTR, VT_BOOL, VT_CY, VT_VARIANT, VT_DECIMAL, VT_I2, VT_I4
ui1 | VT_BSTR, VT_BOOL, VT_VARIANT, VT_DECIMAL, VT_UI1, VT_EMPTY, VT_NULL
ui2 | VT_BSTR, VT_BOOL, VT_VARIANT, VT_UI1, VT_I2, VT_EMPTY, VT_NULL
ui4 | VT_BSTR, VT_BOOL, VT_VARIANT, VT_UI1, VT_I2, VT_I4, VT_EMPTY, VT_NULL
ui8 | VT_BSTR, VT_BOOL, VT_CY, VT_VARIANT, VT_DECIMAL, VT_UI1, VT_I2, VT_I4, VT_EMPTY, VT_NULL
r4 | VT_BSTR, VT_CY, VT_VARIANT, VT_DECIMAL, VT_I2, VT_I4, VT_R4, VT_EMPTY, VT_NULL
r8 | VT_BSTR, VT_CY, VT_VARIANT, VT_DECIMAL, VT_I2, VT_I4, VT_R4, VT_R8, VT_EMPTY, VT_NULL
uuid | VT_BSTR, VT_VARIANT
uri | VT_BSTR, VT_VARIANT
bin.hex | VT_BSTR, VT_VARIANT, VT_ARRAY
char | VT_BSTR, VT_VARIANT, VT_I2, VT_EMPTY, VT_NULL

See Also

XML Data Types Reference | XML Data Types | Primitive XML Data Types | XML Schema Developer's Guide | XML Schema Reference


XML Error Messages

Msxml.dll errors are generated by the parser, the Document Object Model (DOM), the XSL or XQL processors, or the MIME viewer.

The following topics provide a list of error messages returned by Msxml.dll:

  • IXMLParseError Error Messages
  • XML DOM Error Messages
  • XSL Error Messages
  • XQL Error Messages
  • MIMETYPE Viewer Error Messages


IXMLParseError Error Messages

The following errors are obtained from the IXMLDOMParseError object's errorCode property, which is specifically for parse errors.

Parse Errors

MSG_E_FORMATINDEX_BADINDEX | The value passed in to formatIndex needs to be greater than zero.
MSG_E_FORMATINDEX_BADFORMAT | Invalid format string.
MSG_E_SYSTEM_ERROR | System error: %1.
MSG_E_MISSINGEQUALS | Missing equals sign between attribute and attribute value.
MSG_E_EXPECTED_TOKEN | Expected token %1 found %2.
MSG_E_UNEXPECTED_TOKEN | Unexpected token %1.
MSG_E_MISSINGQUOTE | A string literal was expected, but no opening quote character was found.
MSG_E_COMMENTSYNTAX | Incorrect syntax was used in a comment.
MSG_E_BADSTARTNAMECHAR | A name was started with an invalid character.
MSG_E_BADNAMECHAR | A name contained an invalid character.
MSG_E_BADCHARINSTRING | The character "<" cannot be used in an attribute value.
MSG_E_XMLDECLSYNTAX | Invalid syntax for an xml declaration.
MSG_E_BADCHARDATA | An invalid character was found in text content.
MSG_E_MISSINGWHITESPACE | Required white space was missing.
MSG_E_EXPECTINGTAGEND | The character ">" was expected.
MSG_E_BADCHARINDTD | Invalid character found in DTD.
MSG_E_BADCHARINDECL | An invalid character was found inside a DTD declaration.
MSG_E_MISSINGSEMICOLON | A semicolon character was expected.
MSG_E_BADCHARINENTREF | An invalid character was found inside an entity reference.
MSG_E_UNBALANCEDPAREN | Unbalanced parentheses.
MSG_E_EXPECTINGOPENBRACKET | An opening "[" character was expected.
MSG_E_BADENDCONDSECT | Invalid syntax in a conditional section.
MSG_E_INTERNALERROR | Internal error.
MSG_E_UNEXPECTED_WHITESPACE | White space is not allowed at this location.
MSG_E_INCOMPLETE_ENCODING | End of file reached in invalid state for current encoding.
MSG_E_BADCHARINMIXEDMODEL | Mixed content model cannot contain this character.
MSG_E_MISSING_STAR | Mixed content model must be defined as zero or more("*").
MSG_E_BADCHARINMODEL | Invalid character in content model.
MSG_E_MISSING_PAREN | Missing parenthesis.
MSG_E_BADCHARINENUMERATION | Invalid character found in ATTLIST enumeration.
MSG_E_PIDECLSYNTAX | Invalid syntax in PI declaration.
MSG_E_EXPECTINGCLOSEQUOTE | A single or double closing quote character (\' or \") is missing.
MSG_E_MULTIPLE_COLONS | Multiple colons are not allowed in a name.
MSG_E_INVALID_DECIMAL | Invalid character for decimal digit.
MSG_E_INVALID_HEXADECIMAL | Invalid character for hexadecimal digit.
MSG_E_INVALID_UNICODE | Invalid Unicode character value for this platform.
MSG_E_WHITESPACEORQUESTIONMARK | Expecting white space or "?".
MSG_E_SUSPENDED | The parser is suspended.
MSG_E_STOPPED | The parser is stopped.
MSG_E_UNEXPECTEDENDTAG | End tag was not expected at this location.
MSG_E_UNCLOSEDTAG | The following tags were not closed: %1.
MSG_E_DUPLICATEATTRIBUTE | Duplicate attribute.
MSG_E_MULTIPLEROOTS | Only one top level element is allowed in an XML document.
MSG_E_INVALIDATROOTLEVEL | Invalid at the top level of the document.
MSG_E_BADXMLDECL | Invalid XML declaration.
MSG_E_MISSINGROOT | XML document must have a top level element.
MSG_E_UNEXPECTEDEOF | Unexpected end of file.
MSG_E_BADPEREFINSUBSET | Parameter entities cannot be used inside markup declarations in an internal subset.
MSG_E_PE_NESTING | The replacement text for a parameter entity must be properly nested with parenthesized groups.
MSG_E_INVALID_CDATACLOSINGTAG | The literal string "]]>" is not allowed in element content.
MSG_E_UNCLOSEDPI | Processing instruction was not closed.
MSG_E_UNCLOSEDSTARTTAG | Element was not closed.
MSG_E_UNCLOSEDENDTAG | End element was missing the character ">".
MSG_E_UNCLOSEDSTRING | A string literal was not closed.
MSG_E_UNCLOSEDCOMMENT | A comment was not closed.
MSG_E_UNCLOSEDDECL | A declaration was not closed.
MSG_E_UNCLOSEDMARKUPDECL | A markup declaration was not closed.
MSG_E_UNCLOSEDCDATA | A CDATA section was not closed.
MSG_E_BADDECLNAME | Declaration has an invalid name.
MSG_E_BADEXTERNALID | External ID is invalid.
MSG_E_BADELEMENTINDTD | An XML element is not allowed inside a DTD.
MSG_E_RESERVEDNAMESPACE | The namespace prefix is not allowed to start with the reserved string "xml".
MSG_E_EXPECTING_VERSION | The version attribute is required at this location.
MSG_E_EXPECTING_ENCODING | The encoding attribute is required at this location.
MSG_E_EXPECTING_NAME | At least one name is required at this location.
MSG_E_UNEXPECTED_ATTRIBUTE | The specified attribute was not expected at this location. The attribute may be case sensitive.
MSG_E_ENDTAGMISMATCH | End tag %2 does not match the start tag %1.
MSG_E_INVALIDENCODING | System does not support the specified encoding.
MSG_E_INVALIDSWITCH | Switch from current encoding to specified encoding not supported.
MSG_E_EXPECTING_NDATA | NDATA keyword is missing.
MSG_E_INVALID_MODEL | Content model is invalid.
MSG_E_INVALID_TYPE | Invalid type defined in ATTLIST.
MSG_E_INVALIDXMLSPACE | XML space attribute has invalid value. Must specify "default" or "preserve".
MSG_E_MULTI_ATTR_VALUE | Multiple names found in attribute value when only one was expected.
MSG_E_INVALID_PRESENCE | Invalid ATTDEF declaration. Expected #REQUIRED, #IMPLIED, or #FIXED.
MSG_E_BADXMLCASE | The name "xml" is reserved and must be lowercase.
MSG_E_CONDSECTINSUBSET | Conditional sections are not allowed in an internal subset.
MSG_E_CDATAINVALID | CDATA is not allowed in a DTD.
MSG_E_INVALID_STANDALONE | The standalone attribute must have the value "yes" or "no".
MSG_E_UNEXPECTED_STANDALONE | The standalone attribute cannot be used in external entities.
MSG_E_DOCTYPE_IN_DTD | Cannot have a DOCTYPE declaration in a DTD.
MSG_E_MISSING_ENTITY | Reference to an undefined entity.
MSG_E_ENTITYREF_INNAME | Entity reference is resolved to an invalid name character.
MSG_E_DOCTYPE_OUTSIDE_PROLOG | Cannot have a DOCTYPE declaration outside of a prolog.
MSG_E_INVALID_VERSION | Invalid version number.
MSG_E_DTDELEMENT_OUTSIDE_DTD | Cannot have a DTD declaration outside of a DTD.
MSG_E_DUPLICATEDOCTYPE | Cannot have multiple DOCTYPE declarations.
MSG_E_RESOURCE | Error processing resource %1.
XML_IOERROR | Error opening input file: %1.
XML_ENTITY_UNDEFINED | Reference to undefined entity %1.
XML_INFINITE_ENTITY_LOOP | Entity %1 contains an infinite entity reference loop.
XML_NDATA_INVALID_PE | Cannot use the NDATA keyword in a parameter entity declaration.
XML_REQUIRED_NDATA | Cannot use a general parsed entity '%1' as the value for attribute '%2'.
XML_NDATA_INVALID_REF | Cannot use unparsed entity %1 in an entity reference.
XML_EXTENT_IN_ATTR | Cannot reference an external general parsed entity %1 in an attribute value.
XML_STOPPED_BY_USER | XML parser stopped by user.
XML_PARSING_ENTITY | Error while parsing entity %1. %2.
XML_E_MISSING_PE_ENTITY | Parameter entity must be defined before it is used.
XML_E_MIXEDCONTENT_DUP_NAME | The same name must not appear more than once in a single mixed-content declaration: %1.
XML_NAME_COLON | Entity, EntityRef, PI, Notation names, or NMToken cannot contain a colon.
XML_ELEMENT_UNDECLARED | The element %1 is used but not declared in the DTD/Schema.
XML_ELEMENT_ID_NOT_FOUND | The attribute %1 references the ID %2, which is not defined anywhere in the document.
XML_DEFAULT_ATTRIBUTE | Error in the default attribute value defined in DTD/Schema.
XML_XMLNS_RESERVED | Reserved namespace "%1" cannot be redeclared.
XML_EMPTY_NOT_ALLOWED | Element cannot be empty according to the DTD/Schema.
XML_ELEMENT_NOT_COMPLETE | Element content is incomplete according to the DTD/Schema.
XML_ROOT_NAME_MISMATCH | The name of the top-most element must match the name of the DOCTYPE declaration.
XML_INVALID_CONTENT | Element content is invalid according to the DTD/Schema.
XML_ATTRIBUTE_NOT_DEFINED | The attribute %1 on this element is not defined in the DTD/Schema.
XML_ATTRIBUTE_FIXED | Attribute %1 has a value which does not match the fixed value defined in the DTD/Schema.
XML_ATTRIBUTE_VALUE | Attribute %1 has an invalid value according to the DTD/Schema.
XML_ILLEGAL_TEXT | Text is not allowed in this element according to the DTD/Schema.
XML_MULTI_FIXED_VALUES | An attribute declaration cannot contain multiple fixed values: "%1".
XML_NOTATION_DEFINED | The notation %1 is already declared.
XML_ELEMENT_DEFINED | The element %1 is already declared.
XML_ELEMENT_UNDEFINED | Reference to undeclared element: %1.
XML_XMLNS_UNDEFINED | Reference to undeclared namespace prefix: %1.
XML_XMLNS_FIXED | Attribute %1 must be a #FIXED attribute.
XML_E_UNKNOWNERROR | Unknown error: %1.
XML_REQUIRED_ATTRIBUTE_MISSING | Required attribute %1 is missing.
XML_MISSING_NOTATION | Declaration %1 contains a reference to undefined notation %2.
XML_ATTLIST_DUPLICATED_ID | Cannot define multiple ID attributes on the same element.
XML_ATTLIST_ID_PRESENCE | An attribute of type ID must have a declared default of #IMPLIED or #REQUIRED.
XML_XMLLANG_INVALIDID | The language ID %1 is invalid.
XML_PUBLICID_INVALID | The public ID %1 is invalid.
XML_DTD_EXPECTING | Expecting: %1.
XML_NAMESPACE_URI_EMPTY | Only a default namespace can have an empty URI.
XML_LOAD_EXTERNALENTITY | Could not load %1.
XML_BAD_ENCODING | Unable to save character to %1 encoding.
SCHEMA_ATTRIBUTEVALUE_NOSUPPORT | A namespace was found but not supported at the current location.
SCHEMA_SCHEMAROOT_EXPECTED | Incorrect definition for the root element in the schema.
SCHEMA_ELEMENT_NOSUPPORT | Element %1 is not allowed in this context.
SCHEMA_ETNAME_MISSING | An ElementType declaration must contain a name attribute.
SCHEMA_ETYPE_MISSING | An element declaration must contain a type attribute.
SCHEMA_ETORDER_UNKNOWN | Schema only supports order types "seq", "one", and "many".
SCHEMA_ELEMENTDT_NOSUPPORT | Content must be "textOnly" when using datatype on an ElementType.
SCHEMA_ETORDER_DISABLED | Order must be "many" when content is "mixed".
SCHEMA_ETCONTENT_UNKNOWN | Content must be of type "empty","eltOnly","textOnly", or "mixed".
SCHEMA_ETMODEL_UNKNOWN | The value of the model must be either "open" or "closed".
SCHEMA_ELEMENT_DISABLED | Cannot contain child elements because content is set to "textOnly".
SCHEMA_ELEMENT_MISSING | Must provide at least one "element" in a group.
SCHEMA_ATTRIBUTE_NOTSUPPORT | The attribute %1 on an %2 is not supported.
SCHEMA_ATNAME_MISSING | AttributeType declaration must contain a name attribute.
SCHEMA_ATNAME_DUPLICATED | Duplicated attribute declaration.
SCHEMA_ATREQUIRED_INVALID | Invalid value for required attribute.
SCHEMA_DTTYPE_UNKNOWN | Unknown attribute datatype.
SCHEMA_DTTYPE_DUPLICATED | Duplicated datatype declaration.
SCHEMA_ENUMERATION_MISSING | An element with a values attribute must contain a type attribute of the value "enumeration".
SCHEMA_DTVALUES_MISSING | Must provide a values attribute on an element that contains a type attribute of the value "enumeration".
SCHEMA_ATYPE_MISSING | Attribute declaration must contain a type attribute.
SCHEMA_ATYPE_UNDECLARED | The specified attribute must first be declared using an AttributeType declaration.
SCHEMA_GROUP_DISABLED | A "group" is not allowed within an ElementType that has a "textOnly" content model.
SCHEMA_GMATTRIBUTE_NOTSUPPORT | The attribute %1 on a group is not supported.
SCHEMA_DTVALUES_VALUES_MISSING | The values for enumeration type are missing.
SCHEMA_ATTRIBUTE_DEFAULTVALUE | The default value "%1" is invalid.
SCHEMA_DTTYPE_DISABLED | Datatype is not allowed when content model is not "textOnly".
SCHEMA_ELEMENT_EMPTY | Child element is not allowed when content model is "empty".
SCHEMA_ELEMENT_DATATYPE | Child element is not allowed when datatype is set.
SCHEMA_DTTYPE_MISSING | Type is missing on the datatype element.
SCHEMA_MINOCCURS_INVALIDVALUE | The value of attribute minOccurs should be "0" or "1".
SCHEMA_MAXOCCURS_INVALIDVALUE | The value of attribute maxOccurs should be "1" or "*".
SCHEMA_MAXOCCURS_MUSTBESTAR | The value of attribute maxOccurs must be "*" when attribute "order" is set to "many".
SCHEMA_ELEMENTDT_EMPTY | The value of the datatype attribute cannot be empty.
SCHEMA_DOCTYPE_INVALID | DOCTYPE is not allowed in Schema.


XML DOM Error Messages

The following errors are generated by the XML document object model:

DOM Errors

XMLOM_DUPLICATE_ID | The ID %1 is duplicated.
XMLOM_DATATYPE_PARSE_ERROR | Error parsing %1 as %2 datatype.
XMLOM_NAMESPACE_CONFLICT | There was a namespace conflict for the "%1" namespace.
XMLOM_OBJECT_EXPAND_NOTIMPL | Unable to expand an attribute with "object" value.
XMLOM_DTDT_DUP | Cannot have two datatype attributes on one element.
XMLOM_INSERTPOS_NOTFOUND | Insert position node not found.
XMLOM_NODE_NOTFOUND | Node not found.
XMLOM_INVALIDTYPE | This operation cannot be performed with a node of type %1.
XMLOM_INVALID_XMLDECL_ATTR | %1 is not a valid attribute on the XML declaration. Only version, encoding, or standalone attributes are allowed.
XMLOM_INVALID_INSERT_PARENT | Inserting a node or its ancestor under itself is not allowed.
XMLOM_INVALID_INSERT_POS | Insert position node must be a child of the node to insert under.
XMLOM_NO_ATTRIBUTES | Attributes are not allowed on nodes of type %1.
XMLOM_NOTCHILD | The parameter node is not a child of this ode.
XMLOM_CREATENODE_NEEDNAME | createNode requires a name for given nodeType.
XMLOM_UNEXPECTED_NS | Unexpected namespace parameter.
XMLOM_MISSING_PARAM | Required parameter is missing (or null/empty).
XMLOM_INVALID_NAMESPACENODE | Namespace node is invalid.
XMLOM_READONLY | Attempt to modify a read-only node.
XMLOM_ACCESSDENIED | Access denied.
XMLOM_ATTRMOVE | Attributes must be removed before adding them to a different node.
XMLOM_BADVALUE | Invalid data for a node of type %1.
XMLOM_USERABORT | Operation aborted by caller.
XMLOM_NEXTNODEABORT | Unable to recover node list iterator position.
XMLOM_INVALID_INDEX | The offset must be zero or a positive number that is not greater than the number of characters in the data.
XMLOM_INVALID_ATTR | The provided node is not a specified attribute on this node.
XMLOM_INVALID_ONDOCTYPE | This operation cannot be performed on a DOCTYPE node.
XMLOM_INVALID_MODEL | Cannot mix different threading models in the document.
XMLOM_INVALID_DATATYPE | Datatype %1 is not supported.


XSL Error Messages

Microsoft XML SDK 2.6 - XML Reference

XSLT Error Messages

The following error messages are returned by the XSLT processor:

XSLT Errors

XSL_PROCESSOR_STACKOVERFLOW | The XSLT processor stack has overflowed—probable cause is infinite template recursion.
XSL_PROCESSOR_UNEXPECTEDKEYWORD | Keyword %1 may not be used here.
XSL_PROCESSOR_BADROOTELEMENT | The root element of XSLT stylesheet must be <xsl:document> or <xsl:template>.
XSL_PROCESSOR_KEYWORDMAYNOTFOLLOW | Keyword %1 may not follow %2.
XSL_PROCESSOR_INVALIDSCRIPTENGINE | %1 is not a scripting language.
XSL_PROCESSOR_SCRIPTERROR_LINE | line = %1, col = %2 (line is offset from the <xsl:script> tag).
XSL_PROCESSOR_METHODERROR | Error returned from property or method call.
XSL_KEYWORD_MAYNOTCONTAIN | Keyword %1 may not contain %2.


XQL Error Messages

The following error messages are returned by the XQL processor.

XQL Errors

XQL_EXPR_NOT_DOM_NODE | Expression does not return a DOM node.
XQL_EXPR_NOT_QUERY_OR_INTEGER | Expression must be a query or an integer constant.
XQL_INCOMPLETE_QUERY | Incomplete query expression.
XQL_UNKNOWN_METHOD | Unknown method.
XQL_UNEXPECTED_CHAR | Unexpected character in query string.
XQL_QUERY_INVALID_HERE | %1 may not appear to the right of / or // or be used with |.
XQL_EXPR_NOT_STRING | Expression must be a string constant.
XQL_METHOD_NOT_SUPPORTED | Object does not support this method.
XQL_INVALID_CAST | Expression can't be cast to this data type.
XMLISLANDS_NOSCRIPTLETS | The XML script engine does not support script fragments. This error was probably caused by having a script tag with the language attribute set to "XML" or the text attribute set to "text/xml" before any other script tag on the page.
XQL_EXPR_NOT_QUERY_OR_STRING | Parameter must be a query or a string constant.
XQL_EXPR_NOT_INTEGER | Parameter must be an integer constant.


MIMETYPE Viewer Error Messages

The following errors are returned by the Internet Explorer 5 XML MIME viewer:

MIMETYPE Viewer Errors

XMLMIME_ERROR | <%1TABLE width=400> <%1P style="font:13pt/15pt verdana"> The XML page cannot be displayed <%1P style="font:8pt/11pt verdana">Cannot view XML input using %3 style sheet. Please correct the error and then click the <%1a href="javascript:location.reload()" target="_self"> Refresh</%1a> button, or try again later. <%1HR><%1P style="font:bold 8pt/11pt verdana">%2</%1P><%1/TABLE> <%1pre style="font:10pt/12pt"><%1font color="blue">%4</%1font></%1pre>
XMLMIME_LINEPOS | Line %1, Position %2
XMLMIME_SOURCENA | The XML source file is unavailable for viewing.


XML Tutorial

List of Lessons

This tutorial consists of a set of interactive lessons that walk you through typical XML authoring and development tasks.

You will be able to read the lessons in Microsoft® Internet Explorer 3.0, 4.0, and other browsers, but the interactive elements and demos require Internet Explorer 5.

  • Lesson 1: Authoring XML Elements
  • Lesson 2: Authoring XML Documents
  • Lesson 3: Authoring XML Data Islands
  • Lesson 4: Using the XML Object Model
  • Lesson 5: Using XML Namespaces
  • Lesson 6: Authoring XML Schemas
  • Lesson 7: Using Data Types Within XML Documents
  • Lesson 8: Accessing Typed XML Values
  • Lesson 9: Using the C++ XML DSO
  • Lesson 10: Using the Master/Detail Feature with the C++ XML DSO


Lesson 1: Authoring XML Elements

What is an XML element?

XML is a meta-markup language, a set of rules for creating semantic tags used to describe data. An XML element is made up of a start tag, an end tag, and data in between. The start and end tags describe the data within the tags, which is considered the value of the element. For example, the following XML element is a <director> element with the value "Ed Wood."

<director>Ed Wood</director>

The element name "director" allows you to mark up the value "Ed Wood" semantically, so you can differentiate that particular bit of data from another, similar bit of data. For example, there might be another element with the value "Ed Wood."

<actor>Ed Wood</actor>

Because each element has a different tag name, you can easily tell that one element refers to Ed Wood, the director of Jail Bait, while the other refers to Ed Wood, the lead actor in Glen or Glenda. If there were no way to mark up the data semantically, having two elements with the same value might cause some confusion.

In addition, XML tags are case-sensitive, so the following are each a different element.

<City> <CITY> <city>

Attributes

An element can optionally contain one or more attributes. An attribute is a name-value pair separated by an equal sign (=).

<CITY ZIP="01085">Westfield</CITY>

In this example above, ZIP="01085" is an attribute of the <CITY> element. Attributes are used to attach additional, secondary information to an element, usually meta-information. Attributes can also accept default values, while elements cannot. Each attribute of an element can be specified only once, but in any order.

Try it!

In the text box below, type the title of a favorite movie and then click Continue.

In the box below, enter a single-word description of the word (movie, film, screenplay, title, and so on) and then click the Continue button.

You have just created the following XML element:

Check the syntax

Because XML is a highly structured language, it is important that all XML be well-formed. That is, the XML must have both a start tag and an end tag, and must be authored using the proper syntax. In the box below, create an XML element with a start tag, an end tag, and a value on a single line. Click the Well-formed? button to see if your XML is correct.


Lesson 2: Authoring XML Documents

What is an XML document?

A basic XML document is simply an XML element that can—but might not—include nested XML elements.

For example, the XML <books> element is a valid XML document:

<books>
  <book isbn="0345374827">
    <title>The Great Shark Hunt</title>
    <author>Hunter S. Thompson</author>
  </book>
</books>

Authoring guidelines

There are several key things to remember when constructing a basic XML document:

  • All elements must have an end tag.
  • All elements must be cleanly nested (overlapping elements are not allowed).
  • All attribute values must be enclosed in quotation marks.
  • Each document must have a unique first element, the root node.

Try it!

In the text box below, create an XML document that contains both <element> and <attribute> nodes. Click the Well-formed? button to see whether your XML document conforms to the XML specification.


Lesson 3: Authoring XML Data Islands

What is an XML data island?

A data island is an XML document that exists within an HTML page. It allows you to script against the XML document without having to load it through script or through the <OBJECT> tag. Almost anything that can be in a well-formed XML document can be inside a data island.

The <XML> element marks the beginning of the data island, and its ID attribute provides a name that you can use to reference the data island.

The XML for a data island can be either inline:

<XML ID="XMLID">
  <customer>
    <name>Mark Hanson</name>
    <custID>81422</custID>
  </customer>
</XML>

or referenced through a SRC attribute on the <XML> tag:

<XML ID="XMLID" SRC="customer.xml"></XML>

You can also use the <SCRIPT> tag to create a data island:

<SCRIPT LANGUAGE="xml" ID="XMLID">
  <customer>
    <name>Mark Hanson</name>
    <custID>81422</custID>
  </customer>
</SCRIPT>

Authoring guidelines

Simply author an XML document, place that XML document within an <XML> element, and give that <XML> element an ID attribute.

Try it!

In the text box below, type a well-formed XML document.

Type an ID for the data island.

Click the Insert Data Island button to display an HTML page with your data island inserted.


Lesson 4: Using the XML Object Model

What is the XML object model?

The XML object model is a collection of objects that you use to access and manipulate the data stored in an XML document. The XML document is modeled after a tree, in which each element in the tree is considered a node. Objects with various properties and methods represent the tree and its nodes. Each node contains the actual data in the document.

How do I access the nodes in the tree?

You access nodes in the tree by scripting against their objects. These objects are created by the XML parser when it loads and parses the XML document. You reference the tree, or document object, by its ID value. In the following example, MyXMLDocument is the document object's ID value. The document object's properties and methods give you access to the root and child node objects of the tree. The root, or document element, is the top-level node from which its child nodes branch out to form the XML tree. The root node can appear in the document only once.

Run the mouse over the following data island to reveal the code needed to access each node. The root node is <class>, and its child node is <student>, which has child nodes of <name> and <GPA>.

<XML ID="MyXMLDocument">
  <class>
    <student studentID="13429">
      <name>Jane Smith</name>
      <GPA>3.8</GPA>
    </student>
  </class>
</XML>

The following list is a sample of the properties and methods that you use to access nodes in an XML document.

Property/Method | Description
XMLDocument | Returns a reference to the XML Document Object Model (DOM) exposed by the object.
DocumentElement | Returns the document root of the XML document.
ChildNodes | Returns a node list containing the children of a node (if any).
Item | Accesses individual nodes within the list through an index. Index values are zero based, so item(0) returns the first child node.
Text | Returns the text content of the node.

The following code shows an HTML page containing an XML data island. The data island is contained within the <XML> element.

<HTML>
  <HEAD>
    <TITLE>HTML with XML Data Island</TITLE>
  </HEAD>
  <BODY>
    <P>Within this document is an XML data island.</P>
    <XML ID="resortXML">
      <resorts>
        <resort>Calinda Cabo Baja</resort>
        <resort>Na Balam Resort</resort>
      </resorts>
    </XML>
  </BODY>
</HTML>
Calinda Cabo Baja Na Balam Resort

You access the data island through the ID value, "resortXML", which becomes the name of the document object. In the example above, the root node is <resorts>, and the child nodes are <resort>.

The following code accesses the second child node of <resorts> and returns its text, "Na Balam Resort."

resortXML.XMLDocument.documentElement.childNodes.item(1).text

How do I persist XML DOM tree information?

Several methods and interfaces are available for persisting DOM information.

If you are using a script language, the DOMDocument object exposes the load, loadXML, and save methods, and the xml property.

For Visual Basic and C or C++ programmers, the IXMLDOMDocument interface exposes the same members as the DOMDocument object. IXMLDOMDocument also implements standard COM interfaces such as IPersistStreamInit, IPersistMoniker, and IStream.

Try it!

In the text box below, enter code to access a part of either of the above documents (assume that the first document object is "MyXMLDocument" and the second is "resortXML"). Then click the Access XML button to reveal the node you have referenced.

Jane Smith 3.8


Lesson 5: Using XML Namespaces

What is an XML namespace?

An XML namespace is a collection of names that can be used as element or attribute names in an XML document. The namespace qualifies element names uniquely on the Web in order to avoid conflicts between elements with the same name. The namespace is identified by some URI (Universal Resource Identifier), either a URL (Uniform Resource Locator), or a URN (Uniform Resource Number), but it doesn't matter what, if anything, it points to. URIs are used simply because they are globally unique across the Internet.

Namespaces can be declared either explicitly or by default. With an explicit declaration, you define a shorthand, or prefix, to substitute for the full name of the namespace. You use this prefix to qualify elements belonging to that namespace. Explicit declarations are useful when a node contains elements from different namespaces. A default declaration declares a namespace to be used for all elements within its scope, and a prefix is not used.

How do I declare an explicit namespace?

The following explicit declaration declares "bk" and "money" to be shorthand for the full names of their respective namespaces. The xmlns attribute is an XML keyword for a namespace declaration.

<BOOKS>
  <bk:BOOK xmlns:bk="urn:BookLovers.org:BookInfo"
           xmlns:money="urn:Finance:Money">
    <bk:TITLE>A Suitable Boy</bk:TITLE>
    <bk:PRICE money:currency="US Dollar">22.95</bk:PRICE>
  </bk:BOOK>
</BOOKS>

All elements beginning with "bk:" or "money:" are considered to be from the namespace "urn:BookLovers.org:BookInfo" or "urn:Finance:Money," respectively. Run the mouse over the XML to reveal information about the different elements.

How do I declare a default namespace?

A namespace declared without a prefix becomes the default namespace for the document. All elements and attributes in the document that do not have a prefix will then belong to the default namespace. The following example declares that the <BOOK> element and all elements and attributes within it (<TITLE>, <PRICE>, currency) are from the namespace "urn:BookLovers.org:BookInfo."

<BOOK xmlns="urn:BookLovers.org:BookInfo">
  <TITLE>A Suitable Boy</TITLE>
  <PRICE currency="US Dollar">22.95</PRICE>

Try it!

In the text box below, add an explicit namespace declaration to the <BOOK> element of the following XML document for the namespace "urn:BookLovers.org:AuthorInfo" with a prefix of "author". Click the Well-formed? button to see whether your XML document conforms to the XML specification.

<BOOKS>
  <bk:BOOK xmlns:bk="urn:BookLovers.org:BookInfo"
           xmlns:money="urn:Finance:Money">
    <bk:TITLE>A Suitable Boy</bk:TITLE>
    <bk:PRICE money:currency="US Dollar">22.95</bk:PRICE>
  </bk:BOOK>
</BOOKS>

Now, add an element to the XML above called <AUTHOR>, qualified with the namespace prefix "author", containing the book's author, "Vikram Seth". Again, click the Well-formed? button to see whether your XML document still conforms to the XML specification.

<BOOKS>
  <bk:BOOK xmlns:bk="urn:BookLovers.org:BookInfo"
           xmlns:money="urn:Finance:Money"
           xmlns:author="urn:BookLovers.org:AuthorInfo">
    <bk:TITLE>A Suitable Boy</bk:TITLE>
    <bk:PRICE money:currency="US Dollar">22.95</bk:PRICE>
    <author:AUTHOR>Vikram Seth</author:AUTHOR>
  </bk:BOOK>
</BOOKS>


Lesson 6: Authoring XML Schemas

What is an XML Schema?

An XML Schema is an XML-based syntax, or schema, for defining how an XML document is marked up. XML Schema is a schema specification recommended by Microsoft and it has many advantages over Document Type Definition (DTD), the initial schema specification for defining an XML model. DTDs have many drawbacks, including the use of non-XML syntax, no support for data-typing, and non-extensibility. For example, DTDs don't allow you to define element content as anything other than another element, or a string. To find out more about DTDs, see the W3C XML Recommendation. XML Schema improves upon DTDs in several ways, including the use of XML syntax, and support for data-typing and namespaces. For example, an XML Schema allows you to specify an element as an integer, a float, a Boolean, a URL, and so on.

The XML parser in Internet Explorer 5 can validate an XML document with both a DTD and an XML Schema.

How can I create an XML Schema?

Run the mouse over the following XML document to reveal the schema declarations for each node.

  <class xmlns="x-schema:classSchema.xml">
    <student studentID="13429">
      <name>James Smith</name>
      <GPA>3.8</GPA>
    </student>
  </class>

You'll notice in the above document that the default namespace is "x-schema:classSchema.xml." This tells the parser to validate the entire document against the schema (x-schema) at the following URL ("classSchema.xml").

Below is the entire schema for the above document. The schema begins with the <Schema> element containing the declaration of the schema namespace and, in this case, the declaration of the "datatypes" namespace as well. The first, "xmlns="urn:schemas-microsoft-com:xml-data"," indicates that this XML document is an XML Schema. The second, "xmlns:dt="urn:schemas-microsoft-com:datatypes"," allows you to type element and attribute content by using the dt prefix on the type attribute within their ElementType and AttributeType declarations.

<Schema xmlns="urn:schemas-microsoft-com:xml-data"
  xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <AttributeType name='studentID' dt:type='string' required='yes'/>
  <ElementType name='name' content='textOnly'/>
  <ElementType name='GPA' content='textOnly' dt:type='float'/>
  <ElementType name='student' content='mixed'>
    <attribute type='studentID'/>
    <element type='name'/>
    <element type='GPA'/>
  </ElementType>
  <ElementType name='class' content='eltOnly'>
    <element type='student'/>
  </ElementType>
</Schema>

The declaration elements that you use to define elements and attributes are described as follows:

Element | Description
ElementType | Assigns a type and conditions to an element, and what, if any, child elements it can contain.
AttributeType | Assigns a type and conditions to an attribute.
attribute | Declares that a previously defined attribute type can appear within the scope of the named ElementType element.
element | Declares that a previously defined element type can appear within the scope of the named ElementType element.

The content of the schema begins with the AttributeType and ElementType declarations of the innermost elements.

<AttributeType name='studentID' dt:type='string' required='yes'/>
<ElementType name='name' content='textOnly'/>
<ElementType name='GPA' content='textOnly' dt:type='float'/>

The next ElementType declaration is followed by its attribute and child elements. When an element has attributes or child elements they must be included this way in its ElementType declaration. They must also be previously declared in their own ElementType or AttributeType declaration.

<ElementType name='student' content='mixed'>
  <attribute type='studentID'/>
  <element type='name'/>
  <element type='GPA'/>
</ElementType>

This process is continued throughout the rest of the schema, until every element and attribute has been declared.

Unlike DTDs, XML Schemas allow you to have an open content model, allowing you to do such things as type elements and apply default values without necessarily restricting content.

In the following schema, the <GPA> element is typed and has an attribute with a default value, but no other nodes are declared within the <student> element.

<Schema xmlns="urn:schemas-microsoft-com:xml-data"
  xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <AttributeType name="scale" default="4.0"/>
  <ElementType name="GPA" content="textOnly" dt:type="float">
    <attribute type="scale"/>
  </ElementType>
  <AttributeType name="studentID"/>
  <ElementType name="student" content="eltOnly" model="open" order="many">
    <attribute type="studentID"/>
    <element type="GPA"/>
  </ElementType>
</Schema>

The above schema allows you to validate only the area with which you are concerned. This gives you more control over the level of validation for your document and allows you to use some of the features provided by the schema without having to employ strict validation.

Try it!

Try authoring a schema for the following XML document.

<order>
  <customer>
    <name>Fidelma McGinn</name>
    <phone_number>425-655-3393</phone_number>
  </customer>
  <item>
    <number>5523918</number>
    <description>shovel</description>
    <price>39.99</price>
  </item>
  <date_of_purchase>1998-10-23</date_of_purchase>
  <date_of_delivery>1998-11-03</date_of_delivery>
</order>

Once you have completed the schema, run it through the XML Validator.

The Downloads area of MSDN Online provides a set of XML sample files, including an XML document with an accompanying schema. Download these samples to work with the XML document and the schema. To test the validity of your XML against a schema, you can load the document through the XML Validator or simply view the XML file in the Mime Type Viewer.

A few considerations:

  1. ElementType and AttributeType declarations must precede attribute and element content declarations that refer to these types. For example, in the above schema, the ElementType declaration for the <GPA> element must precede the ElementType declaration for the <student> element.
  2. The default value of the order attribute depends on the value of the content attribute. When the content is set to "eltOnly," the order defaults to seq. When the content is set to "mixed", the order defaults to many. For information about these default values, see the XML Schema Reference.


Lesson 7: Using Data Types Within XML Documents

What is a data type within an XML document?

A data type within an XML document is a type that has been assigned to an element on the instance using the dt:dt attribute, or through an XML Schema, a formal definition of an XML document. In addition, data types can be declared as elements. The XML parser uses the data type information to validate the document.

Previously, XML element values were limited to a single type String, so developers who wanted to process XML documents had to spend time converting all values to a type within their own applications. Typing your XML data will let the parser take care of data type conversions. In addition, because values are assigned a particular data type, changes made to that value will have to conform to that data type. This provides you with a method of validating user input.

In addition to having a string value, each XML element can also have a typed value. For example, the following XML element:

<date>1970-09-30</date>

can have both a value of "1970-09-30" and a typed value of "Wed Sep 30 00:00:00 PDT 1970."

How do I type XML element values?

Consider the following XML document:

<?xml version="1.0"?>
<weather xmlns="x-schema:weatherSchema.xml">
  <date>1970-09-30</date>
  <degrees>67.5</degrees>
</weather>

where "weatherSchema.xml" is the following file:

<Schema xmlns="urn:schemas-microsoft-com:xml-data" xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <ElementType name="date" content="textOnly" dt:type="date"/>
  <ElementType name="degrees" content="textOnly" dt:type="float"/>
  <ElementType name="weather" content="eltOnly">
    <element type="date"/>
    <element type="degrees"/>
  </ElementType>
</Schema>

The schema above is a formal definition assigning data types to the elements of the preceding XML document. The attribute xmlns is the XML keyword for a namespace declaration. The declaration 'xmlns:dt="urn:schemas-microsoft-com:datatypes"' assigns dt to the namespace "urn:schemas-microsoft-com:datatypes." Any type qualified with dt will be from the namespace "urn:schemas-microsoft-com:datatypes."

The schema and data type namespaces are declared at the beginning of the XML Schema so that the dt prefix can then be used to denote which type attributes hold data type designations.

<Schema xmlns="urn:schemas-microsoft-com:xml-data" xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <ElementType name="NUMBER" content="textOnly" dt:type="number"/>

Typing an element within the XML document

An element can also be typed through the dt attribute on the instance of the element. To do this, declare the data type namespace at the beginning of the XML document.

<NUMBERS xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <NUMBER dt:dt="number">44533</NUMBER>
</NUMBERS>

The dt prefix can now be used on the dt attribute to assign a data type to an instance of an element.

Datatypes as elements within the XML document

In addition to declaring data types on the instance of an element and in a schema, they can also be used as elements. Compare the following examples. The first declares data types on the instance of an element:

<shoes xmlns:dt="urn:schemas-microsoft-com:datatypes" id="f1121" sizes="mens"/>
<sizes id="mens">
  <size dt:dt="int">8</size>
  <size dt:dt="int">10</size>
  <size dt:dt="int">12</size>
</sizes>

The second example declares the types as elements:

<shoes xmlns:dt="urn:schemas-microsoft-com:datatypes" id="f1121" sizes="array1"/>
<array id="array1">
  <dt:int>8</dt:int>
  <dt:int>10</dt:int>
  <dt:int>12</dt:int>
</array>

It is not possible to type such elements through schema.

For a list of data types that you can use within your XML documents, see XML Data Types Reference.

Try it!

In the box below, write an ElementType declaration for a <PRICE> element that has a data type of "fixed.14.4." Note that XML is case sensitive.


Lesson 8: Accessing Typed XML Values

What is a typed XML value?

A typed XML value is an XML element value that has been assigned a data type in an XML Schema, a formal definition of an XML document. The XML parser uses the schema to validate the document. The XML Schema information resides either in a schema file or within the XML document itself.

How do I access typed XML values?

It is possible to access typed data through the XML object model. Just as you can retrieve the value of an element by calling the nodeValue property on that element's node, you can retrieve the typed value of an element by calling the nodeTypedValue property on the element itself.

For example, consider the following XML document:

<?xml version="1.0"?>
<weather xmlns="x-schema:weatherSchema.xml">
  <date>1970-09-30</date>
  <degrees>67.5</degrees>
</weather>

where "weatherSchema.xml" is the following file:

<Schema xmlns="urn:schemas-microsoft-com:xml-data" xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <ElementType name="date" content="textOnly" dt:type="date"/>
  <ElementType name="degrees" content="textOnly" dt:type="float"/>
  <ElementType name="weather" content="eltOnly"/>
    <element type="date"/>
    <element type="degrees"/>
  </ElementType>
</Schema>

If you navigate to the <degrees> element (xmlDocument.documentElement.childNodes.item(1)), you can access its typed value by calling nodeTypedValue as follows: xmlDocument.documentElement.childNodes.item(1).nodeTypedValue.

Try it!

In the text box below, enter the code that will access the typed value of the <date> element (assume that "xmlDocument" is the document object), and then click the Check Code button.

To experience the validation capability of data types, see what happens when you enter a date that is not valid. In the text box below, enter an invalid date using the yyyy-mm-dd format. For instance, enter "1998-13-02", and then click the Check Date Validation button.


Lesson 9: Using the C++ XML DSO

What is the C++ XML DSO?

Microsoft® Internet Explorer 5 ships with a C++ Data Source Object (DSO) that can be used to bind XML to HTML. Microsoft® Internet Explorer 4 shipped with a Java XML DSO that is still supported in Internet Explorer 5; however, the new C++ DSO gives you better performance and the ability to bind directly to an XML data island.

The C++ DSO provides you with the ability to create XML-driven Web applications in a completely declarative fashion, although it is still possible to write scripts against the XML document object. With the C++ DSO, both the ActiveX® Data Objects (ADO) and XML object models are available to you.

How do I bind XML to HTML?

Consider the following XML file:

<universities>
  <university>
    <name>UCSB</name>
    <location>Santa Barbara, CA</location>
  </university>
  <university>
    <name>University of Texas at Arlington</name>
    <location>Arlington, TX</location>
  </university>
  <university>
    <name>USM</name>
    <location>Hattiesburg, MS</location>
  </university>
  <university>
    <name>Baylor</name>
    <location>Waco, TX</location>
  </university>
</universities>

You can bind this XML to a repeating table with the following HTML:

<XML ID=xmlDoc src="universities.xml"></XML>
<TABLE DATASRC="#xmlDoc" BORDER=1>
  <THEAD><TH>NAME</TH><TH>LOCATION</TH></THEAD>
  <TR>
    <TD><SPAN DATAFLD="name"></SPAN></TD>
    <TD><SPAN DATAFLD="location"></SPAN></TD>
  </TR>
</TABLE>

Click the button below to view the table created.

UCSB Santa Barbara, CA University of Texas at Arlington Arlington, TX USM Hattiesburg, MS Baylor Waco, TX
NAME | LOCATION

There's no need for an <APPLET> or <OBJECT> tag; the XML DSO does all the work for you.

Try it!

See the XML DSO demo in the XML Downloads area. Download the sources for that demo and see if you can change the display format from a form to a repeated table.


Lesson 10: Using the Master/Detail Feature with the C++ XML DSO

What is the master/detail feature?

The new master/detail feature allows you to bind to the current record of a hierarchical record set. This means that you can now bind the child elements of the current record to a distinct table. For example, consider the following XML:

<orders>
  <order order_number="2233">
    <customer>
      <name>John Smith</name>
      <custID>192883</custID>
    </customer>
    <item>
      <name>Fly Swatter</name>
      <price>9.99</price>
    </item>
  </order>
  <order order_number="2234">
    <customer>
      <name>Marea Angela Castaneda</name>
      <custID>827145</custID>
    </customer>
    <item>
      <name>Fly Paper</name>
      <price>15.99</price>
    </item>
  </order>
  <order order_number="2235">
    <customer>
      <name>Amy Jones</name>
      <custID>998022</custID>
    </customer>
    <item>
      <name>Mosquito Netting</name>
      <price>38.99</price>
    </item>
  </order>
</orders>

You could allow your user to navigate through the orders by ID, displaying only the customer and item information for the current order. Your user would not have to view the information for all orders—only for the one in which he or she is interested.

How do I bind to the details?

The key to binding to lower levels in the hierarchy (the details) is to understand the structure of your data. The above XML has three elements within the root (the <orders>) element. Based on the heuristic employed by the XML DSO, each order will be mapped to a rowset containing an "order_number", "customer", and "item" field. The "order_number" column will contain the value of the "order_number" attribute. The "customer" and "item" columns will contain pointers to respective "customer" and "item" recordsets. The "customer" recordset will then contain a "name" and "custID" field with the values of those elements within. The "item" recordset will contain a "name" and "price" field with the values of those elements within.

So, with this in mind, note that within the top-level (the "orders") recordset, you can get to the value of the "order_number." You will then allow your user to navigate through the orders by "order_number."

<P>ORDER NUMBER: <SPAN DATASRC="#xmlDoc" DATAFLD="order_number"></SPAN></P>

Now add some buttons to help users move throughout the "orders" recordset.

<INPUT TYPE=BUTTON VALUE="Previous Order" onclick="xmlDoc.recordset.movePrevious()">
<INPUT TYPE=BUTTON VALUE="Next Order" onclick="xmlDoc.recordset.moveNext()">

To retrieve the values within the child elements of the current record, create a table and set that table's DATASRC attribute to "#xmlDoc" exactly as was done above. However, this time also set its DATAFLD attribute to "customer." This tells the table to bind to data within the "customer" recordset pointed at within the "customer" field of the "orders" recordset.

<TABLE DATASRC="#xmlDoc" DATAFLD="customer" BORDER>
  <THEAD><TH>NAME</TH><TH>ID</TH></THEAD>
  <TR>
    <TD><SPAN DATAFLD="name"></SPAN></TD>
    <TD><SPAN DATAFLD="custID"></SPAN></TD>
  </TR>
</TABLE>

Then do the same for the data within the "item" element.

<TABLE DATASRC="#xmlDoc" DATAFLD="item" BORDER=1>
  <THEAD><TR><TH>ITEM</TH><TH>PRICE</TH></TR></THEAD>
  <TR>
    <TD><SPAN DATAFLD="name"></SPAN></TD>
    <TD><SPAN DATAFLD="price"></SPAN></TD>
  </TR>
</TABLE>

Now, as the user clicks the buttons and moves to the next and previous records in the recordset, the data in the tables will change to correspond to the current record.

Click the Show Example button to view the page created above.

John Smith 192883 Fly Swatter 9.99 Candice Calloway 827145 Fly Paper 15.99 Mandy Jones 998022 Mosquito Netting 38.99

ORDER NUMBER:

NAME | ID
ITEM | PRICE

Try it!

See if you can take the above XML and create a Web page that will allow a user to navigate through the orders by customer name.


XSLT Developer's Guide

The MSXML Parser supports a subset of the XSL Transformations (XSLT) and XPath Version 1.0 World Wide Web Consortium (W3C) Recommendations. In addition, MSXML maintains backward compatibility with the MSXML 2.0 and 2.5 implementations of Section 2 of the December 1998 XSL Working Draft of XSL. There is no support for XSL Formatting Objects. XSLT and XPath Conformance Notes detail unimplemented features and known conformance bugs.

Information about support for XSL can be obtained from the XML 2.5 SDK documentation. See the XML Developer Center for more information.

For information about the differences between XSL and XSLT, see XSL to XSLT Converter.

XSLT transformations address some common needs in XML:

  • Enabling display: The XSLT transformation language enables display of XML by transforming XML into grammar and structure suitable for display—for instance, into HTML.
  • Direct browsing of XML files: Internet Explorer 5 can apply style sheets that produce HTML, allowing direct browsing of the XML files.
  • Content delivery to down-level browsers: XSLT transformations can be executed on the server to provide HTML documents for down-level browsers.
  • Schema Translation: The transformation process is independent of any particular output grammar and can be used for translating XML data from one schema to another.
  • Converting XML through querying, sorting, and filtering: The transformation can be used for general-purpose transformations within a single grammar, including filtering, sorting, and summarizing data.

The XSLT Developer's Guide covers these topics:

  • XSLT and XPath Conformance Notes
  • Getting Started with XSLT
  • Browsing XML Documents in Internet Explorer 5
  • Using the XSLT Processor
  • XPath
  • Advanced XSLT Features

See Also

XML and XSL Samples | XSLT Elements


XSLT and XPath Conformance Notes

The MSXML Parser implementation is based on the XSL Transformations (XSLT) Version 1.0 W3C Recommendation 16 November 1999, found at http://www.w3.org/TR/1999/REC-xslt-19991116 and the XML Path Language (XPath) Version 1.0 W3C Recommendation 16 November 1999, found at http://www.w3.org/TR/1999/REC-xpath-19991116.

The XML Parser contains an implementation of a subset of the XSLT and XPath specifications. Because of the time necessary to develop the parser, it was impossible to implement all specifications for this release. Microsoft is, however, committed to full compliance and will deliver the remaining features in a future release.

In error cases where the XSLT recommendation provides a fallback behavior (for example, two matching templates with the same priority), MSXML performs the fallback behavior.

MSXML provides two mechanisms for implementing extension functions, embedded script implementations (<msxsl:script>) and external objects (addObject). MSXML does not provide a mechanism for implementing extension elements.

The <xsl:output> element supports only the xml method.

Unimplemented features and known conformance bugs are listed below.

Unimplemented Features

There are a number of features that are not implemented in this release. Partial workarounds are noted where they exist.

XSLT Features

  • Recognition of future namespaces (section 2.5)
  • <xsl:import> element (section 2.6.2); <xsl:include> element is supported
  • <xsl:strip-space> and <xsl:preserve-space> elements (section 3.4)
  • <xsl:apply-imports> element (section 5.6)
  • Named templates, <xsl:call-template> element (section 6)
  • <xsl:namespace-alias> element (section 7.1.1)
  • Named attribute sets (section 7.1.4)
  • <xsl:number> element (section 7.7)
  • case-order and lang attributes (section 10)
  • <xsl:key> element (section 12.2)
  • <xsl:message> element (section 13)
  • <xsl:fallback> element (section 15)

XPath Features

  • Axes (section 2.2)
    • following
    • following-sibling
    • preceding
    • preceding-sibling
    • namespace
  • Boolean Functions (section 4.3)
    • lang()
  • Number Functions (section 4.4)
    • sum()
    • floor()
    • ceiling()
    • round()
  • XSLT Additional Functions (XSLT, section 12)
    • document()
    • key()
    • unparsed-entity-uri()
    • generate-id()


Getting Started with XSLT

An XSLT transformation results in the creation of a new XML tree. This clear separation between the source document and the result trees fulfills the goal of XML to separate content from presentation by allowing both the grammar and the structure of the XML source to be independent of the presentation language and structure.

XSLT at its most basic provides a capability similar to a "mail merge." The style sheet contains a template of the desired result structure, and identifies data in the source document to insert into this template. This model for merging data and templates is referred to as the template-driven model and works well on regular and repetitive data. This model will be familiar to users of Active Server Pages, which embed bits of script within an HTML template to control processing and to insert data. The Microsoft® Internet Explorer data binding features also use a template-driven model—elements in the HTML page provide templates for displaying bound data.

XSLT also provides capabilities for handling highly irregular and recursive data such as is typical in documents. Template fragments are defined, and the XSLT processor combines the results of these fragments into a final result tree based on the shape of the source data. Each template fragment declares the type and context of source nodes it is appropriate for, allowing the XSLT processor to match source nodes with template fragments. This model is known as the data-driven model since the shape of the data drives the shape of the final output.

Template-driven and data-driven transformation mechanisms can be combined in a single style sheet, making XSLT appropriate to a wide variety of XML applications.

These topics will introduce you to the basic XSLT syntax:

  • Creating and Populating an HTML Template
  • Authoring Well-Formed HTML
  • Accessing and Outputting Attributes
  • Sorting XML
  • Conditional Templates

You can continue to learn more about the XSLT language by exploring these topics:

  • XPath
  • Advanced XSLT Features


Creating and Populating an HTML Template

You can use XSLT to merge simple regular XML data with an HTML template for display. Consider the following example:

<?xml version="1.0"?>
<portfolio xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <stock exchange="nyse">
    <name>zacx corp</name>
    <symbol>ZCXM</symbol>
    <price dt:dt="number">28.875</price>
  </stock>
  <stock exchange="nasdaq">
    <name>zaffymat inc</name>
    <symbol>ZFFX</symbol>
    <price dt:dt="number">92.250</price>
  </stock>
  <stock exchange="nasdaq">
    <name>zysmergy inc</name>
    <symbol>ZYSZ</symbol>
    <price dt:dt="number">20.313</price>
  </stock>
</portfolio>

The structure of this sample data is repetitive and regular—the stock element structure is repeated several times with the same subelements. For this example the stock information will be displayed in a table with a stock in each row, and cells containing the name, symbol, and price. First create a template of HTML elements to display such a table.

<HTML>
  <BODY>
    <TABLE BORDER="2">
      <TR>
        <TD>Symbol</TD>
        <TD>Name</TD>
        <TD>Price</TD>
      </TR>
      <!-- repeat the following row for each stock -->
      <TR>
        <TD><!-- symbol goes here --></TD>
        <TD><!-- name goes here --></TD>
        <TD><!-- price goes here --></TD>
      </TR>
    </TABLE>
  </BODY>
</HTML>

To populate this template with data from the XSLT file, you could manually replace the comments with data from the XML file. This is essentially the process that the XSLT performs. Elements from the XSLT namespace are used to locate data in the XML file, and insert it into the HTML template.

<HTML>
  <BODY>
    <TABLE BORDER="2">
      <TR>
        <TD>Symbol</TD>
        <TD>Name</TD>
        <TD>Price</TD>
      </TR>
      <xsl:for-each select="portfolio/stock">
        <TR>
          <TD><xsl:value-of select="symbol"/></TD>
          <TD><xsl:value-of select="name"/></TD>
          <TD><xsl:value-of select="price"/></TD>
        </TR>
      </xsl:for-each>
    </TABLE>
  </BODY>
</HTML>

The <xsl:for-each> element locates a set of elements in the XML data ("stock" elements inside the "portfolio" element) and repeats a portion of the template for each one. Because this sample contains three stock elements, three rows will be created.

The select attribute describes how to find a set of elements in the source document. The syntax for this attribute is called a pattern, and works much like navigating a file system hierarchy where a forward slash (/) selects subdirectories relative to the current directory. In an XSLT style sheet, the navigation starts at the current node and drills down into the XML data hierarchy, selecting all the nodes that match the pattern. In this case the pattern "portfolio/stock" starts at the document root and drills down through the "portfolio" element to select the three "stock" children.

For many style sheets, the use of element names and the / operator is sufficiently powerful to perform the desired transformation. Details of other XSLT operations are described in Introduction to the Syntax of XPath.

Within the <xsl:for-each> element, you can further drill down to select children of each "stock" element. The <xsl:value-of> element selects a specific child and then inserts the text content of that child into the template. The patterns inside the <xsl:value-of> element's select attribute do not require starting at the document root again but are relative to the element selected in the <xsl:for-each> element.

The preceding template can be made into a complete XSLT style sheet by placing it in an XML file and enclosing it within the <xsl:stylesheet> element.

<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
    <HTML>
      <BODY>
        <TABLE BORDER="2">
          <TR>
            <TD>Symbol</TD>
            <TD>Name</TD>
            <TD>Price</TD>
          </TR>
          <xsl:for-each select="portfolio/stock">
            <TR>
              <TD><xsl:value-of select="symbol"/></TD>
              <TD><xsl:value-of select="name"/></TD>
              <TD><xsl:value-of select="price"/></TD>
            </TR>
          </xsl:for-each>
        </TABLE>
      </BODY>
    </HTML>
  </xsl:template>
</xsl:stylesheet>

Because a style sheet is an XML file itself, the file begins with the recommended xml declaration. The <xsl:stylesheet> element indicates that this document is a style sheet file, and provides a location for declaring the XSLT namespace. Microsoft® Internet Explorer 5 supports the XSL namespace URL http://www.w3.org/TR/WD-xsl. The current MSXML Parser supports both the XSL namespace URL http://www.w3.org/TR/WD-xsl and the XSLT namespace URL http://www.w3.org/1999/XSL/Transform.

You also must wrap the template with <xsl:template match="/"> to indicate that this template corresponds to the root (/) of the XML source document. For more information, see Advanced XSLT Features.

The entire file must be well-formed to comply with XML rules, including the HTML that comprises the template. For assistance in authoring or converting to well-formed HTML, see Authoring Well-Formed HTML.

You must add a processing instruction to the XML file to indicate that a style sheet is to be used. For instance, in the above example, if the name of the style sheet file is "stock.xsl", you would then add <?xml-stylesheet type="text/xsl" href="stock.xsl"?> to the XML document, immediately following the <?xml version="1.0"?> processing instruction.

The end result of running the portfolio document through the style sheet is this:

<HTML>
  <BODY>
    <TABLE BORDER="2">
      <TR>
        <TD>Symbol</TD>
        <TD>Name</TD>
        <TD>Price</TD>
      </TR>
      <TR>
        <TD>ZCXM</TD>
        <TD>zacx corp</TD>
        <TD>28.875</TD>
      </TR>
      <TR>
        <TD>ZFFX</TD>
        <TD>zaffymat inc</TD>
        <TD>92.250</TD>
      </TR>
      <TR>
        <TD>ZYSZ</TD>
        <TD>zysmergy inc</TD>
        <TD>20.313</TD>
      </TR>
    </TABLE>
  </BODY>
</HTML>

This result illustrates how the basic transformation mechanism combines a template (defined in the style sheet) with data from the source document to create a final result.

Try it! See the above example at work in the Portfolio Sample at XSLT Developer's Guide Samples. See the XSL version of this sample at XSL Developer's Guide Samples.

Note To browse directly to this XML file, the sample file also contains a style sheet processing instruction pointing to the style sheet. For details, see Browsing XML Documents in Internet Explorer 5.


Authoring Well-Formed HTML

Well-formed HTML simply means HTML that conforms to the rules of XML. This means that the same HTML tags are available, but the stricter XML syntax is required. An XSLT style sheet is itself XML and thus any HTML within it must be well-formed.

In addition to HTML within an XSLT style sheet, you should consider authoring well-formed HTML for its own sake as well. Much of the industry is moving toward well-formedness as a way to increase the robustness of the Web, while simplifying and accelerating the processing of well-formed documents and data. Well-formedness has great advantages for authoring tools and can benefit hand authoring by ensuring that the markup is unambiguous. The industry expectation is that a future HTML standard will be an XML application.

The price for these benefits is that a less-forgiving syntax must be used.

Writing well-formed HTML is simple. Here are the main points you should watch for as you author or convert to well-formed HTML.

All Tags Must Be Closed

HTML allows certain end tags to be optional, the most common being <P>, <LI>, <TR>, and <TD>. XML requires all tags to be closed explicitly.

HTML Well-formed HTML
<P> This is an HTML paragraph.

<P>or two.

<P>This is an HTML paragraph.</P>

<P>or two.</P>

Leaf nodes must also be closed by placing a forward slash (/) within the tag. The most common examples are <BR>, <HR>, <INPUT>, and <IMG>.

HTML Well-formed HTML
<IMG src="sample.gif"

     width="10" height="20">

<IMG src="sample.gif"

     width="10" height="20" />

No Overlapping Tags Are Allowed

XML does not allow start and end tags to overlap, but enforces a strict hierarchy within the document.

HTML Well-formed HTML
<B>Well <I>Hello</B> Dolly!</I>

<B>Well</B> <I><B>Hello</B> Dolly!</I>

Case Matters

Choose a consistent case for open and close tags. At this point, which case to use isn't specified, as long as it is consistent between start and end tags. The examples generally use uppercase for HTML elements.

HTML Well-formed HTML
<B><i>Hello Dolly!</I></b>

<B><I>Hello Dolly!</I></B>

Quote Your Attributes

All attributes must be surrounded by quotation marks, either single or double.

HTML Well-formed HTML
<IMG src=sample.gif

     width=10 height=20 >

<IMG src='sample.gif'

     width="10" height="20" />

Use a Single Root

Shortcuts that eliminate the <HTML> element as the single top-level element are not allowed.

HTML Well-formed HTML
<TITLE>Funky markup</TITLE>

<BODY>
  <P>Amazing that this HTML works.</P>
</BODY>

<HTML>

  <HEAD>
    <TITLE>Clean markup</TITLE>
  </HEAD>
  <BODY>
    <P>Not nearly so amazing that
    this well-formed HTML works.</P>
  </BODY>
</HTML>

Fewer Built-in Entities

XML defines only a minimal set of built-in character entities:

  • &lt; — (<)
  • &gt; — (>)
  • &amp; — (&)
  • &quot; — (")
  • &apos; — (')

Numeric character entities are supported. The list of numeric values for entities can be found at HTML Character Sets.

Escape Script Blocks

Script blocks in HTML can contain unparseable characters, namely < and &. These need to be escaped in well-formed HTML by using character entities, or by enclosing the script block in a CDATA section.

In addition, JScript® (compatible with ECMA 262 language specification) comments terminate at the end of the line, so preserving the white space within script blocks containing comments is important. The xml:space attribute value by default normalizes white space by compressing adjacent white space characters into a single space. This destroys the new line that terminates the JScript comment. Any JScript following the comment is treated as part of the comment and ignored, often resulting in script errors. The CDATA notation also ensures that the white space is preserved.

The following HTML script block contains both an unparseable character (<) and JScript comments. The well-formed script block uses CDATA to encapsulate the script.

HTML Well-formed HTML
<SCRIPT>

  // checks a number against 7
  function less-than-seven(n) {
    return n < 7;
  }
</SCRIPT>

<SCRIPT><![CDATA[

  // checks a number against 7
  function less-than-seven(n) {
    return n < 7;
  }
]]></SCRIPT>

While not all scripts will fail if not escaped in this way, it is highly recommended that you do it as a matter of habit. This ensures not only that the script will work if it contains escaped characters or comments now, but will continue to work if these characters are added in the future.


Accessing and Outputting Attributes

XSLT provides mechanisms for accessing attributes in the source document, and for generating attributes in the result tree. XSLT attempts to be agnostic about whether data should be encoded as attribute values or subelements, and strives to process either with equal ease.

Attributes in the source document can be accessed in a pattern by preceding the attribute name with the @ symbol. Thus the following example extracts the value of the exchange attribute on stock elements and inserts it into the output. For more details about selecting attributes, see XPath.

<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
    <HTML>
      <BODY>
        <TABLE BORDER="2">
          <TR>
            <TD>Symbol</TD>
            <TD>Name</TD>
            <TD>Price</TD>
          </TR>
          <xsl:for-each select="portfolio/stock">
            <TR>
              <xsl:attribute name="TITLE"><xsl:value-of select="symbol"/>
                is listed on the <xsl:value-of select="@exchange"/>
                stock exchange.</xsl:attribute>
              <TD><xsl:value-of select="symbol"/></TD>
              <TD><xsl:value-of select="name"/></TD>
              <TD><xsl:value-of select="price"/></TD>
            </TR>
          </xsl:for-each>
        </TABLE>
      </BODY>
    </HTML>
  </xsl:template>
</xsl:stylesheet>

Attributes can be created in two ways—by placing them on an output element, such as the BORDER='2' attribute above, or by adding them to an element with the <xsl:attribute> element. <xsl:attribute> allows the output attribute's value to be generated from the source data.

The name attribute specifies the name for the output attribute, and the contents of the tag are evaluated to determine its value. In the previous example, a TITLE attribute is added to the <TR> element to display a ToolTip generated by mixing text with element and attribute values from the source document.

Attributes can be added to an element that already has attributes directly specified on it, so you can mix direct specifications and <xsl:attribute> freely. Watch out for these limitations, though:

  • You cannot add an attribute to an element that already has an attribute of that name.
  • Attributes added with <xsl:attribute> must appear before children are added to the element.

Try it! View the sample above by clicking Portfolio at XSLT Developer's Guide Samples. Move the mouse over each row to see the generated ToolTip.

See the XSL version of this sample at XSL Developer's Guide Samples.


Sorting XML

Sorting is specified by adding <xsl:sort> elements as children to an <xsl:apply-templates> or an <xsl:for-each> element. The first <xsl:sort> child specifies the primary sort key, the second <xsl:sort> child specifies the secondary sort key, and so on. When an <xsl:apply-templates> or <xsl:for-each> element has one or more <xsl:sort> children, instead of processing the selected nodes in document order, it sorts the nodes according to the specified sort keys and then processes them in sorted order. When used in <xsl:for-each>, <xsl:sort> elements must occur first. When a template is instantiated by <xsl:apply-templates> and <xsl:for-each>, the current node list consists of the complete list of nodes being processed in sorted order.

<xsl:sort> has a select attribute whose value is an Expression. For each node to be processed, the expression is evaluated with that node as the current node and with the complete list of nodes being processed in unsorted order as the current node list. The resulting object is converted to a string; this string is used as the sort key for that node. The default value of the select attribute is ".", which will cause the string-value of the current node to be used as the sort key.

This string serves as a sort key for the node. The following optional attributes on <xsl:sort> control how the list of sort keys are sorted:

  • order specifies whether the strings should be sorted in ascending or descending order. Ascending specifies ascending order; descending specifies descending order. The default is ascending.
  • data-type specifies the data type of the strings. The following values are allowed:
    • text specifies that the sort keys should be sorted alphabetically. This is the default.
    • number specifies that the sort keys should be converted to numbers and then sorted according to the numeric value. The sort key is converted to a number as if by a call to the function.
    • a Qualified Name with a prefix is expanded into an expanded name. The expanded name identifies the data type.

The sort must be stable: In the sorted list of nodes, any sub list that has sort keys that all compare equal must be in document order.

For example, suppose an employee database has the following form:

<employees>
  <employee>
    <name>
      <given>James</given>
      <family>Clark</family>
    </name>
    ...
  </employee>
</employees>

Then a list of employees sorted by name could be generated using:

<xsl:template match="employees">
  <ul>
    <xsl:apply-templates select="employee">
      <xsl:sort select="name/family"/>
      <xsl:sort select="name/given"/>
    </xsl:apply-templates>
  </ul>
</xsl:template>

<xsl:template match="employee">
  <li>
    <xsl:value-of select="name/given"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="name/family"/>
  </li>
</xsl:template>


Conditional Templates

Conditional templates are output only if certain conditions exist within the source document. Conditional templates are defined with the <xsl:if> and <xsl:choose> elements.

Notice in the following portfolio data that the "stock" element has an attribute named "exchange". You might want to generate some output only when this attribute has a certain value.

<?xml version="1.0"?>
<portfolio xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <stock exchange="nyse">
    <name>zacx corp</name>
    <symbol>ZCXM</symbol>
    <price dt:dt="number">28.875</price>
  </stock>
  <stock exchange="nasdaq">
    <name>zaffymat inc</name>
    <symbol>ZFFX</symbol>
    <price dt:dt="number">92.250</price>
  </stock>
  <stock exchange="nasdaq">
    <name>zysmergy inc</name>
    <symbol>ZYSZ</symbol>
    <price dt:dt="number">20.313</price>
  </stock>
</portfolio>

You could easily create another row in the table in which to place the attribute value. But suppose you want to indicate stocks from a particular exchange not by a completely new table row, but by noting them with an asterisk (*). The <xsl:if> element provides a mechanism for conditionally inserting structure into the output tree.

In the following example, the <xsl:if> element inserts an "*" after the symbol for those stocks listed on the NASDAQ stock exchange. The <xsl:if> contents can be simple text, as in this example, or any structure allowed by XSLT, such as elements or attributes.

<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
    <HTML>
      <BODY>
        <TABLE BORDER="2">
          <TR>
            <TD>Symbol</TD>
            <TD>Name</TD>
            <TD>Price</TD>
          </TR>
          <xsl:for-each select="portfolio/stock">
            <TR>
              <TD>
                <xsl:value-of select="symbol"/>
                <xsl:if test="@exchange[.='nasdaq']">*</xsl:if>
              </TD>
              <TD><xsl:value-of select="name"/></TD>
              <TD><xsl:value-of select="price"/></TD>
            </TR>
          </xsl:for-each>
        </TABLE>
        <P>* Listed on Nasdaq stock exchange</P>
      </BODY>
    </HTML>
  </xsl:template>
</xsl:stylesheet>

The test attribute takes a pattern. If the query described by the pattern selects one or more nodes, the <xsl:if> template is inserted. If the selection is empty, the conditional is skipped. In this case, the query checks to see if the <stock> element has an exchange attribute, and further checks that the value of the exchange attribute is equal to "nasdaq". For more information about qualifying a pattern using brackets ([]) and constructing patterns that compare values, see Introduction to the Syntax of XPath.

Try it! This style sheet is used in the Portfolio Sample (Show Exchange) at XSL Developer's Guide Samples. See the XSLT version of this sample at XSLT Developer's Guide Samples.

Choosing Alternatives

The <xsl:choose> element provides a mechanism for "either/or" processing. <xsl:choose> contains a series of <xsl:when> elements that are tested in order from top to bottom until a match is found. An <xsl:otherwise> element can be used to insert a template if no match is found.

The following code could be added to the above example to color-code the rows by price—0-25 are displayed in green, 25-50 are displayed in blue, and 50+ are displayed in red. The color is changed by conditionally generating a portion of the value of the STYLE attribute on the table row.

<TR>
  <xsl:attribute name="STYLE">color:
    <xsl:choose>
      <xsl:when test="price[. &lt; 25]">green</xsl:when>
      <xsl:when test="price[. &lt; 50]">blue</xsl:when>
      <xsl:otherwise>red</xsl:otherwise>
    </xsl:choose>
  </xsl:attribute>
  <TD>
    ...

Try it! View this sample by clicking Portfolio Sample (Show Price Range) at XSL Developer's Guide Samples. See the XSLT version of this sample at XSLT Developer's Guide Samples.


Browsing XML Documents in Internet Explorer 5

You can use Microsoft® Internet Explorer 5 to view XML documents in the browser just as you would view HTML pages. There are several ways to navigate to an XML document on a Web site. You can follow a link to that document, type the URL of the XML document into the address bar, select an XML document from your history or favorites, double-click an XML document from the desktop, and so on. To view the XML source, select View Source from the File menu.

This section contains the following topics:

  • XML and Style Sheets
  • Choosing Between CSS and XSLT
  • How to Write a CSS Style Sheet for Browsing XML
  • Browsing XML Using XSLT Style Sheets
  • Serving Up XML

Other mechanisms for delivering XML over the Web include:

  • Using XSLT in an Active Server Page
  • Lesson 4: Using the XML Object Model
  • XML Data Islands


XML and Style Sheets

Unlike HTML, XML does not predefine display properties for specific elements. Thus it requires a separate style sheet to contain the descriptions of how the XML should be displayed. This separation of the XML content from its presentation allows the content to be easily repurposed. Microsoft® Internet Explorer supports style sheets written in either CSS or XSLT.

You can enable an XML document for browsing by indicating the type and location of a style sheet with the style sheet processing instruction (style sheet PI). The World Wide Web Consortium (W3C) Recommendation for style sheet PI syntax can be found at http://www.w3.org/TR/xml-stylesheet. The basic form of the style sheet PI looks like this:

<?xml-stylesheet type="text/xsl" href="mystyle.xsl"?>

When Internet Explorer browses to the XML document, it looks for this PI and downloads the style sheet and uses it to display the XML. Note that the style sheet PI is used automatically only when displaying the XML directly in the browser. Other uses of XML, such as data islands, ignore the PI.

Each style sheet PI must have the type attribute. The values of this attribute describe the type of style sheet to apply: "text/css" indicates a CSS style sheet, and "text/xsl" indicates an XSLT style sheet.

The href attribute is a URL to the style sheet. Relative URLs are evaluated relative to the URL of the XML document.

An XML document can include multiple style sheet PIs. Internet Explorer 5 looks first for a PI of type "text/xsl" and uses the first one it finds. Otherwise it searches for "text/css" PIs and cascades them together using the same process as the HTML <LINK> element. Other attributes that are allowed on the <LINK> element can also be specified in the style sheet PI. These are either ignored in the case of XSLT or treated the same as corresponding elements on the HTML <LINK> element.

Using the Default Style Sheet as a Debugging Aid

If you browse to an XML document that doesn't contain a valid style sheet PI, Internet Explorer 5 will display the XML as an expanding and collapsing tree. This provides a convenient way for XML authors to check that their XML is well-formed. You can also test XSLT style sheets and XML Schema documents for well-formedness by opening them in the browser.

To test a document, simply double-click the XML document or type its URL into the address bar of the browser. Well-formedness errors will be reported with line numbers to help you find and correct the error. At times you might want to comment out any style sheet PIs in the source document to isolate source errors from style sheet errors.

Internet Explorer displays XML documents asynchronously. This means that it begins displaying the XML document before the document has been completely downloaded. When an error occurs toward the end of a long document, Internet Explorer 5 might have already displayed much of the document. In this case the error message is inserted directly into the document, and you can view it by scrolling to the bottom of the page.

XSL Developer's Guide Samples contains the following examples, which use the default style sheet to check for well-formedness:

  • Review Error Sample (Mismatched Case). This document contains tags of mismatched case. The corrected document is called Review Sample (No Style Sheet).
  • Style Sheet for Review Sample (XSL)—A well-formed XSL document.
  • Schema for Portfolio Sample—A well-formed schema document.

When directly browsing XML documents, Internet Explorer 5 loads the specified Document Type Definition (DTD) or XML Schema, but does not report validation errors.


Choosing Between CSS and XSLT

There are two style sheet languages available for use with XML in Microsoft® Internet Explorer 5—CSS and XSLT. An important point to consider in choosing a style sheet language for a particular document is whether the structure of the XML document is suitable for display. With CSS, the structure of the XML content must be virtually identical to the structure of the presentation. Because one of the goals of XML is a complete separation of content from display, many XML documents are difficult to display as desired using CSS.

The following sample document representing a restaurant review illustrates some of the tradeoffs to consider when choosing between CSS and XSLT.

<?xml version='1.0'?>
<story>
  <restaurant>
    <name>Red Apple Inn</name>
    <address>
      <street>3100 Longview Lane</street>
      <city>Palabro</city>
      <state>Wyoming</state>
    </address>
    <phone>123-555-1212</phone>
  </restaurant>

  <review>
    <rating stars="4">****</rating>
    <date>December 29, 1998</date>
    <reviewer><person>Louis Framptenheimer</person></reviewer>
  </review>

  <body>
    <p><summary>The small town of <city>Palabro</city> seems like
    an unlikely place for a high-class culinary establishment, but
    the <self>Red Apple Inn's</self> Chef <person>Alex
    Choperoni</person> is earning a national reputation for innovative
    dishes.</summary>  And rightly so, if this reviewer's recent
    culinary experience is any example.</p>

    <p>Within striking distance from the ski slopes at <city>Jackson
    Hole</city>, the <self>Red Apple Inn</self> is a completely
    restored Victorian hotel, and offers a warm and elegant atmosphere and
    gracious service. The upscale clientele includes regular
    appearances by movie stars <person>Drew Barrymore</person> and
    <person>Matthew Broderick</person>. Reservations are a must, and
    during the ski season it is not uncommon for the dining room to
    be booked solid six weeks in advance.</p>

    <p>It is hard to go wrong with a menu this packed with interesting
    choices. The crab cakes are especially fine with a thin crisp
    crust that opens with a gentle prod of a fork to reveal a light,
    moist, almost mousse-like interior with a rich nutty flavor.</p>

    <p>The <self>Red Apple Inn</self> boasts one of the finest wine
    cellars in the region. None of the dishes we sampled were less than
    outstanding. Here are a few of the selections from the daily menu.</p>
  </body>

  <menu>
    <appetizer>
      <description>Crab cakes with a creamy dill sauce served with crispy
        herbed daikon fries</description>
      <price>$23</price>
    </appetizer>
    <entree>
      <description>Braised jumbo sea scallops on a tomato beurre
        blanc</description>
      <price>$39</price>
    </entree>
    <entree>
      <description>Free-range chicken breast stuffed with morels in a
        brandy/green-peppercorn sauce</description>
      <price>$31</price>
    </entree>
    <entree>
      <description>Tarragon roast pork loin on a bed of ginger apple slaw
        and caramelized onions</description>
      <price>$34</price>
    </entree>
  </menu>

</story>

The document is structured as four sections: data about the restaurant, information about this review, the text of the review, and an excerpt from the menu. When applying CSS to this document, the overall structure must be used for the display as well. For example, the four sections will be displayed in the order they appear in the document. If later on it became necessary that the reviewer's name should be displayed after the menu excerpts, the XML document itself would have to be modified, not just the style sheet. XSLT transcends this limitation by allowing sophisticated transformations.

Often the simplicity of the CSS language is offset by additional complications in the design of the XML structure. For instance, in this sample document, the design of the <rating> element contains a compromise. Instead of just using a straight number, this element contains a string of "*" characters, which can be easily displayed by CSS (as long as no half stars are awarded). The numerical value is kept as an attribute to support a more flexible and program-accessible representation. XSLT style sheets can make use of the number to influence the display in a variety of ways.

In contrast to CSS, which "decorates" the XML tree with formatting properties, XSLT transforms the XML tree into a new tree (the HTML), allowing extensive reordering, generated text, and calculations—all without modification to the XML source. The source can be maintained from the perspective of "pure content" and can simultaneously be delivered to different channels or target audiences by just switching style sheets.

Some of the extra features of the XSLT version include:

  • Generated text labels for many of the data items, such as "Rating", "Appetizers", and simple things such as the comma in "Palabro, Wyoming".
  • More sophisticated layout using HTML tables.
  • Data items appearing more than once in the style sheet—the "Red Apple Inn" appears as the title of the page, as the headline, and in "Menu selections from the Red Apple Inn".
  • Access to information stored in attribute values—the "stars" attribute is used to generate a URL pointing to an image representing the number of stars.
  • Reordering of items—the reviewer's name appears after the article, even though it appears before it in the source document.
  • Dynamic display behaviors not easily possible through CSS or modification of the source.


How to Write a CSS Style Sheet for Browsing XML

This section illustrates how you can create a CSS style sheet for a simple XML document.

The following sample XML document represents a restaurant review. It contains several distinct sections representing different types of information important to the review. The first section is contained in an element named "restaurant" and provides data about the restaurant—name, address, and phone number. The next section is the "review" element, which contains data about this review—the reviewer's name, the date of the review, and the number of stars it received. The third section is the "body" of the review, and the final section contains an excerpt of the restaurant's menu.

<?xml version='1.0'?>
<story>
  <restaurant>
    <name>Red Apple Inn</name>
    <address>
      <street>3100 Longview Lane</street>
      <city>Palabro</city>
      <state>Wyoming</state>
    </address>
    <phone>123-555-1212</phone>
  </restaurant>

  <review>
    <rating stars="4">****</rating>
    <date>December 29, 1998</date>
    <reviewer><person>Louis Framptenheimer</person></reviewer>
  </review>

  <body>
    <p><summary>The small town of <city>Palabro</city> seems like
    an unlikely place for a high-class culinary establishment, but
    the <self>Red Apple Inn's</self> Chef <person>Alex
    Choperoni</person> is earning a national reputation for innovative
    dishes.</summary>  And rightly so, if this reviewer's recent
    culinary experience is any example.</p>

    <p>Within striking distance from the ski slopes at <city>Jackson
    Hole</city>, the <self>Red Apple Inn</self> is a completely
    restored Victorian hotel, and offers a warm and elegant atmosphere and
    gracious service. The upscale clientele includes regular
    appearances by movie stars <person>Drew Barrymore</person> and
    <person>Matthew Broderick</person>. Reservations are a must, and
    during the ski season it is not uncommon for the dining room to
    be booked solid six weeks in advance.</p>

    <p>It is hard to go wrong with a menu this packed with interesting
    choices. The crab cakes are especially fine with a thin crisp
    crust that opens with a gentle prod of a fork to reveal a light,
    moist, almost mousse-like interior with a rich nutty flavor.</p>

    <p>The <self>Red Apple Inn</self> boasts one of the finest wine
    cellars in the region. None of the dishes we sampled were less than
    outstanding. Here are a few of the selections from the daily menu.</p>
  </body>

  <menu>
    <appetizer>
      <description>Crab cakes with a creamy dill sauce served with crispy
        herbed daikon fries</description>
      <price>$23</price>
    </appetizer>
    <entree>
      <description>Braised jumbo sea scallops on a tomato beurre
        blanc</description>
      <price>$39</price>
    </entree>
    <entree>
      <description>Free-range chicken breast stuffed with morels in a
        brandy/green-peppercorn sauce</description>
      <price>$31</price>
    </entree>
    <entree>
      <description>Tarragon roast pork loin on a bed of ginger apple slaw
        and caramelized onions</description>
      <price>$34</price>
    </entree>
  </menu>

</story>

Notice that the XML tags used in this article have very little to do with presentation. There are no assumed layout, fonts, or colors. This information will be conveyed in the style sheet.

CSS style sheets augment or override the default formatting behavior built into HTML. For XML, the default formatting behavior is "display:inline". One of the first tasks in writing a CSS style sheet for XML is setting the display property to "block" or "none" for many of the elements.

Here's how you can begin to create a CSS style sheet for the restaurant review example. This style sheet assigns the display property the value "block" for the main sections.

story      { display: block; }
restaurant { display: block; }
review     { display: block; }
body       { display: block; }
menu       { display: block; }

Save this document as review.css. Now add the style sheet processing instruction (PI) to the beginning of the XML document (before the "story" element) so that it can locate the newly created style sheet.

<?xml-stylesheet type="text/css" href="review.css" ?>

At this point, opening the XML document in the browser will provide a rudimentary display of the XML document. Additional CSS formatting properties can be added to augment these rules, and new rules can be created for the remaining XML elements. Here is an augmented set of style rules for the <menu> element and its children.

menu
{
  display: block;
  border: 2px solid black;
  padding: 1em;
  background-color: #888833;
  color: #FFFFDD;
  font-family: Times, serif;
  font-style: italic;
  text-align: center;
}
appetizer
{
  display: block;
  margin-bottom: .4em;
}
entree
{
  display: block;
  margin-bottom: .4em;
}
price
{
  display: inline;
  font-weight: bold;
  font-style: normal;
}
description
{
  display: inline;
}

To complete this style sheet, additional rules must be added to handle the remaining elements in the source document.

Using Namespaces with CSS

Namespaces must be declared on the <document> element when using XML with a CSS. Locally scoped namespaces will parse correctly, but CSS does not currently describe a mechanism for resolving namespace prefixes; thus, CSS rules might not apply correctly to elements in the locally scoped namespace. Placing all namespace declarations on the <document> element ensures that they are all "global" and thus there will be no collision of prefixes. The default namespace cannot be used—all namespaced elements must have an explicit prefix. In addition, CSS selectors can specify a namespaced element only by its prefix, so ensure that the prefix is consistent between the XML document and the style sheet.

Here is an example of a style sheet for elements with the "HTML" prefix.

HTML\:IMG {
  border: 2px solid black;
}
HTML\:A:visited HTML\:IMG {
  border-color: grey;
}
HTML\:A:active HTML\:IMG {
  border-color: red;
}
HTML\:A:link HTML\:IMG {
  border-color: blue;
}

Notice that the ":" namespace character must be escaped by a leading backslash to differentiate it from a pseudoclass.

Using the HTML Namespace

The HTML namespace is treated specially when browsing XML with CSS. Elements from the HTML namespace are displayed as they would appear in HTML. This allows access to capabilities not yet provided by CSS. Some examples of useful HTML elements to embed are <TABLE>, <A>, <IMG>, <SCRIPT>, and <STYLE>.

For example, you can add a link and logo to the restaurant review sample. First, you must declare the HTML namespace at the top of the document and then use the "HTML" prefix on the embedded HTML elements. Note that HTML embedded this way must be well-formed XML, so the <IMG> element needs a minimized end tag.

<story xmlns:HTML="http://www.w3.org/Profiles/XHTML-transitional">
  ...
  <restaurant>
    <name>Red Apple Inn</name>
    <logo>
      <HTML:A href="javascript:alert('Visit the Red Apple Inn!')">
        <HTML:IMG src="red-apple.gif" height="50" width="200"/>
      </HTML:A>
    </logo>
    ...

Note that in Microsoft® Internet Explorer 5 the prefix must remain "HTML" or "html" for the elements to be interpreted as HTML elements.

An <HTML:STYLE> block can be used to embed a CSS style sheet within an XML document. This block will augment any style sheets pointed to by style sheet PIs. When there is no external style sheet, there still must be a style sheet PI present to indicate that the XML document should be displayed using the CSS style sheet language, even though no "href" attribute is specified.

The following example shows how the review.css style sheet could be embedded into the XML document using the HTML namespace, the <HTML:STYLE> element, and the style sheet PI without an "href" attribute.

<?xml version="1.0"?>
<?xml-stylesheet type="text/css"?>
<story xmlns:HTML="http://www.w3.org/Profiles/XHTML-transitional">
  <HTML:STYLE>
    story
    {
      display: block;
      font-family: Arial, Helvetica, sans-serif;
      font-size: small;
      width: 30em;
    }
    restaurant
    {
      display: block;
      padding: 1.2em;
      font-size: x-small;
      margin-bottom: 1em;
    }
    ...
  </HTML:STYLE>
  <restaurant>
    ...

Try it! Here are the final versions of the restaurant review sample. These two versions display identically, but reference the style sheet in different ways:

  • External CSS style sheet: Review Sample (CSS)
  • Embedded CSS style sheet: Review Sample (Embedded CSS)

Both can be found at XSL Developer's Guide Samples. See the XSLT versions of the same samples at XSLT Developer's Guide Samples.


Browsing XML Using XSLT Style Sheets

XSLT style sheets used for browsing XML files have stricter requirements than those used for general transformation. For more information about authoring an XSLT style sheet, see Getting Started with XSLT. When authoring a style sheet for use in direct browsing, keep the following limitations in mind:

  • The style sheet must target HTML as its output.
    • The style sheet applies to the root of the source document. Style sheets written to process against the document element instead will generally not display correctly. Some notes on the difference between the document root and the document element can be found at How the DOM Defines the Context for XPath Expressions.
    • The XSLT style sheet must come from the same URL scheme (example: http) and host name (example: www.microsoft.com) as the XML source. The style sheet download follows the same security policies as those for fetching an external entity. Attempting to read a style sheet from a different domain results in an access violation error unless the security option "Access data across domains" is selected from the Internet Options dialog box.

Microsoft® Internet Explorer 5 uses a style sheet to display an XML document when that document contains a style sheet processing instruction (PI) with type "text/xsl".

<?xml-stylesheet type="text/xsl" href="review.xsl" ?>

Accessing the XML and XSLT Documents from Script

The HTML that results from browsing XML with an XSLT style sheet is fully scriptable using the Introduction to the Dynamic HTML Object Model. In addition, there are two properties available on the DHTML document object that provide script access to the XML and XSLT documents:

  • The document.XMLDocument property returns the root node of the XML source document.
  • The document.XSLDocument property returns the root node of the XSLT style sheet document.

Modifications to these two documents via the DOM do not automatically cause updates to the resulting HTML tree, but rather offer a hook that you can use to wire up the specific updates you need.

Consider the following example, which shows how the menu is dynamically sorted in the Review Sample (XSLT). First, add the links that trigger sorting to the style sheet.

<P class="tagline">
  <A href="javascript:sort('price')">Sort menu by price</A>
  <A href="javascript:sort('description')">Sort menu by description</A>
</P>

Next, write the "sort" function to apply the sort to the menu data and update the display. The script accesses the DOM for the XSLT style sheet with the XSLDocument property and uses DOM calls to change attributes representing sort keys.

The XMLDocument is used to locate the XML source fragment that will be used in the update. Calling transformNode on the fragment will result in a string of HTML that can be pasted back into the HTML document.

function sort(key) {
  // Find the "xsl:sort select" attributes in the style sheet.
  var s = document.XSLDocument.selectNodes(
  "*/xsl:template[@match='menu']//xsl:apply-templates/xsl:sort/@select");

  // Replace the values with the new sort key.
  for (var i = s.nextNode(); i != null; i = s.nextNode())
  {
    i.value = key;
  }

  // Find the subset of the document we need to update.
  var d = document.XMLDocument.selectSingleNode("story/menu");

  // Apply the style sheet to the subset, and update the display.
  menu.innerHTML = d.transformNode(document.XSLDocument);
}

In this example, the style sheet is modified to generate a different view of the XML. The same mechanisms can be used to change the XML source data and to generate updated views of this data.

Try it! The above code is part of the Review Sample at XSL Developer's Guide Samples. See the XSLT versions of the same sample at XSLT Developer's Guide Samples.


Serving Up XML

XML documents can be delivered through a standard HTTP server by registering the "text/xml" MIME type on the server. Installing Microsoft® Internet Explorer 5 on the server registers the XML MIME type and associates it with the .xml and .xsl file extensions. This association is used by Microsoft Internet Information Services (IIS) and the Microsoft Personal Web Server (PWS) to determine the MIME type.

When using either of these servers on systems that do not have Internet Explorer 5 installed, the XML MIME type can be registered by running the register-xml.regregister-xml.reg file, which inserts the appropriate keys into the registry. The changes will not take effect until the computer has been restarted.

Alternatively, for IIS on systems that do not have Internet Explorer 5 installed, the XML MIME type can be registered using the Internet Service Manager console. Select Default Web Site and open the Properties dialog box. Select the HTTP Headers tab, click the File Types tab, select New Type, type .xml for the extension and text/xml for the Content Type, and then click OK. The .xsl extension can be registered in a similar fashion.

The .xml and .xsl extensions can also be registered to the XML MIME type manually from the File Types dialog box under Folder Options. Extensions indicating other types of XML files for purposes of browsing need to be manually registered as MIME type "text/xml".

Declaring the XML MIME Type in an Active Server Page

When generating XML dynamically using Active Server Pages, the content type must be declared in the .asp file itself. The ContentType property of the Response object indicates that the result is XML. Include this line in ASPs that are delivering XML:

<% Response.ContentType = "text/xml" %>

Important If the XML MIME type is not registered on the server, the MIME type sent to Internet Explorer in the HTTP header will be incorrect—often "text/plain". Although Internet Explorer 5 often can detect and process these documents, conflicts can arise between the various detection algorithms. For instance, part of CDF file detection involves looking for <CHANNEL> tags—an XML document delivered with an incorrect MIME type and that uses elements named "CHANNEL" could mistakenly be handed off to the CDF processor instead of displayed as an XML document. Avoid these tricky and hard-to-debug problems by making sure your server correctly sends the XML MIME type.

Internet Explorer 5 also recognizes documents of MIME type "application/xml" as XML; see IETF RFC2376IETF_RFC2376.


Using the XSLT Processor

The Microsoft XML Parser makes XSLT services available from the DOM. The tight integration of XSLT with the DOM makes building XML applications simpler and more powerful. XSLT becomes a general service for locating and manipulating XML data in Web pages, server-side applications, and stand-alone applications.

XSLT functionality is surfaced through two pairs of DOM methods:

  • selectNodes and selectSingleNode perform XPath queries over a DOM document.
  • transformNode and transformNodeToObject perform complete XSLT transformations on a DOM node.

The following topics show how to use XSLT services in your application:

  • Transforming the Contents of XML Data Islands Using XSLT
  • Using XSLT in an Active Server Page
  • Getting the Results of a Transformation as a New DOM Document
  • Detecting and Handling XSLT Errors


Transforming the Contents of XML Data Islands Using XSLT

Data islands provide a convenient way to access XML data from an HTML page. Because XSLT is an XML grammar itself, data islands also provide a convenient way to load style sheets. The following example uses two data islands to load both the source XML data and the XSLT style sheet. The transformNode method is used on these two data islands to generate HTML that can then be inserted into the page.

<HTML>
  <HEAD>
    <TITLE>Simple demo of Microsoft XSL Processor</TITLE>
  </HEAD>
  <XML id="source" src="simple.xml"></XML>
  <XML id="style" src="simple.xsl"></XML>

  <SCRIPT FOR="window" EVENT="onload">
    xslTarget.innerHTML = source.transformNode(style.XMLDocument);
  </SCRIPT>
  <BODY>
    <P STYLE="font-size:10pt; font-family:Verdana; color:gray">
      <B>This demo shows the use of data islands for loading XML source and
      XSL style sheets and inserting the transformed result into the Web page.</B>
    </P>
    <DIV id="xslTarget"></DIV>
  </BODY>
</HTML>

When this page is completely loaded, including the data islands, the onload event fires and the transformNode method is executed upon the XML source data. transformNode takes a single parameter, which is a DOM node representing a style sheet. The result of this call is XML text. Because the simple.xsl style sheet used in this example generates well-formed HTML, you can feed this into the xslTarget <DIV> element for display using the innerHTML property.

Note that the XMLDocument property is used on the "style" data island. Without this property, the ID of a data island (for example, "style") is treated as identifying an HTML element, in this case the <XML> element. XMLDocument can be used to explicitly obtain the XML DOM Document node from the <XML> element. Writing style.XMLDocument makes it clear that you are operating on the contents of the data island and not the <XML> element representing the data island.

So why don't you need to also write source.XMLDocument.transformNode(style.XMLDocument)? There is a little helpful magic going on here. If a method from the XML DOM is applied to the data island element, the script engine interface reasons that you want this method applied to the XML DOM Document node instead, and thus the XMLDocument property can be omitted if another DOM property or method is being applied. In the case of the "source" data island, you are calling a DOM method (transformNode), thus the XMLDocument property is not required.

You could also write source.XMLDocument.transformNode(style.documentElement) and apply the style sheet to the document element instead of the root, but the style sheet would need to reflect this—for example, the document element would need to be handled by a rule other than the root template xsl:template match="/", which will not be invoked unless the style sheet is executed against the document root. It is recommended that you process your style sheets off the document root so they will also work for direct browsing of XML. Some notes on the difference between the document root and the document element can be found at How the DOM Defines the Context for XPath Expressions.

Try it! This example is part of the Breakfast Menu sample, which can be found at XSLT Developer's Guide Samples. See the XSL version of this sample at XSL Developer's Guide Samples.

See Also

XML Data Islands | Detecting and Handling XSLT Errors | Getting the Results of a Transformation as a New DOM Document


Using XSLT in an Active Server Page

XSLT transformations can be performed at the server. Besides the ability to use XSLT in your server-side logic, such as filtering your XML, customizing it, or changing its schema, this enables you to deploy content in XML and transform it to HTML on demand for down-level clients.

The code for performing XSLT transformations in an Active Server Page is similar to that used on the client (see Transforming the Contents of XML Data Islands Using XSLT). The XML source document and the style sheet are loaded, and transformNode is called to invoke the XSLT processor.

<%@ LANGUAGE = JScript %>
<%
  // Set the source and style sheet locations here
  var sourceFile = Server.MapPath("simple.xml");
  var styleFile = Server.MapPath("simple.xsl");

  // Load the XML
  var source = Server.CreateObject("Microsoft.XMLDOM");
  source.async = false;
  source.load(sourceFile);
  // Load the XSLT
  var style = Server.CreateObject("Microsoft.XMLDOM");
  style.async = false;
  style.load(styleFile);
  Response.Write(source.transformNode(style));
%>

The Server.MapPath method resolves a relative URL to a full path. The Server.CreateObject instantiates a new XML DOM Document object. The results of the transformation are sent to the client using the Response.Write method.

The transformNodeToObject method offers the ability to write a transformation directly to the IStream interface of the response object. To use this feature, the last line in the previous example would instead become:

source.transformNodeToObject(style, Response);

This method performs better on the server in most situations, especially for long documents, which would otherwise require a significant memory allocation to hold the complete transformation results.

Try it! The Breakfast Menu Demo found at XSLT Developer's Guide Samples shows the transformNodeToObject call in action.

See the XSL version of this sample at XSL Developer's Guide Samples.


Getting the Results of a Transformation as a New DOM Document

The transformNodeToObject method provides a way to generate a new DOM document instead of XML text. In this example, data islands are used to load the XML source and the style sheet, and an empty data island is created that will hold the results of the transformation.

<HTML>
  <SCRIPT FOR="window" EVENT="onload">
    data.transformNodeToObject(ss.XMLDocument, resultTree.XMLDocument);
  </SCRIPT>

  <XML id="data" src="portfolio.xml"></XML>
  <XML id="ss" src="portfolio.xsl"></XML>
  <XML id="resultTree"></XML>
</HTML>

Like the transformNode method, the transformNodeToObject method is applied to a node in the source tree, and accepts a style sheet in the form of a DOM tree. A second parameter contains another DOM Document node that will hold the results of the transformation.

Properties set on the output XML DOM Document will be honored during the transformation, such as validateOnParse and async. Any previous children will be deleted during the transformation.

Try it! The above code is a simplification of the Transformation Viewer sample found at XSLT Developer's Guide Samples.

See the XSL version of this sample at XSL Developer's Guide Samples.


Detecting and Handling XSLT Errors

An XSLT style sheet can generate two types of errors: parse errors and run-time errors. There are ways to check for these errors and report them.

Here is a simple HTML page that uses XML data islands to load an XML file and an XSLT style sheet and display the results.

<HTML>
  <XML id="source" src="simple.xml"></XML>
  <XML id="style" src="simple.xsl"></XML>
  <SCRIPT event="onload" for="window">
    showResult.innerHTML = source.transformNode(style.XMLDocument);
  </SCRIPT>
  <BODY>
    <DIV id="showResult"></DIV>
  </BODY>
</HTML>

The call to transformNode invokes the XSLT processor, and the resulting HTML is inserted into the page. But what if nothing appears? An error occurred somewhere, so you need to check to see where it happened.

First check the parseError object to see whether there was a parsing error in the XML file or in the style sheet. Parsing errors on data islands do not generate error messages in the browser. If an error occurred, you can retrieve the cause and try to recover gracefully, or put an error message into the page so you can see what went wrong. In the following example, parsing errors are checked for; if they occur, a function is called to format the error information in a fashion appropriate for inserting into the HTML document.

<HTML>
  <HEAD>
    <TITLE>Simple Error Message Demo</TITLE>
  </HEAD>

  <XML id="source" src="review.xml"></XML>
  <XML id="style" src="review.xsl"></XML>

  <SCRIPT src="reportErrors.js"></SCRIPT>
  <SCRIPT event="onload" for="window">
    if (source.parseError.errorCode != 0)
      result = reportParseError(source.parseError);
    else
    {
      if (style.parseError.errorCode != 0)
        result = reportParseError(style.parseError);
      else
      {
        try {
          result = source.transformNode(style.XMLDocument);
        }
        catch (exception) {
          result = reportRuntimeError(exception);
        }
      }
    }
    // Insert the results into the page.
    showResult.innerHTML = result;
  </SCRIPT>
  <BODY>
    <DIV id="showResult"></DIV>
  </BODY>
</HTML>

Besides parsing errors, the above example also detects XSLT run-time errors (those occurring inside calls to transformNode). Run-time errors will normally be reported to the user through the standard Microsoft® Internet Explorer 5 error dialog box. However, it is often useful to suppress this dialog box and do your own handling of the error message. The JScript® (compatible with ECMA 262 language specification) 5.0 try and catch functions allow the error to be suppressed and handled consistently with parsing errors.

The actual formatting of the error information in the previous example is provided by two functions: reportParseError and reportRuntimeError, which reside in a separate script file, reportErrors.js. The main HTML page references this script file.

// Parse error formatting function
function reportParseError(error)
{
  var s = "";
  for (var i=1; i<error.linepos; i++) {
    s += " ";
  }
  r = "<font face=Verdana size=2><font size=4>XML Error loading '" +
      error.url + "'</font>" +
      "<P><B>" + error.reason +
      "</B></P></font>";
  if (error.line > 0)
    r += "<font size=3><XMP>" +
    "at line " + error.line + ", character " + error.linepos +
    "\n" + error.srcText +
    "\n" + s + "^" +
    "</XMP></font>";
  return r;
}
// Runtime error formatting function.
function reportRuntimeError(exception)
{
  return "<font face=Verdana size=2><font size=4>XSL Runtime Error</font>" +
      "<P><B>" + exception.description + "</B></P></font>";
}

These two functions return HTML fragments that can be inserted into the HTML page in place of the correct transformation result, alerting you of a problem and assisting you in correcting it.

Try it! The following samples, found in the Review demo at XSLT Developer's Guide Samples, demonstrate the above code in various error situations:

  • Error Message Demo (XML Source Parsing Error)
  • Error Message Demo (XSLT Style Sheet Parsing Error)
  • Error Message Demo (XSLT Run-time Error)
  • Error Message Demo (XSLT Script Error)
  • Error Message Demo (No Errors)

See the XSL version of these samples at XSL Developer's Guide Samples.

Reporting an error using this mechanism is also useful in an ASP page. Here is the equivalent code for processing an XML file on the server.

<%@ LANGUAGE = JScript %>
<%
  // Parse error formatting function.
  function reportParseError(error)
  {
    var s = "";
    for (var i=1; i<error.linepos; i++) {
      s += " ";
    }
    r = "<font face=Verdana size=2><font size=4>XML Error loading '" +
        error.url + "'</font>" +
        "<P><B>" + error.reason +
        "</B></P></font>";
    if (error.line > 0)
      r += "<font size=3><XMP>" +
      "at line " + error.line + ", character " + error.linepos +
      "\n" + error.srcText +
      "\n" + s + "^" +
      "</XMP></font>";
    return r;
  }
  // Run-time error formatting function.
  function reportRuntimeError(exception)
  {
    return "<font face=Verdana size=2><font size=4>XSL Runtime Error</font>" +
        "<P><B>" + exception.description + "</B></P></font>";
  }
  // Set the source and style sheet locations here.
  var sourceFile = Server.MapPath("review.xml");
  var styleFile = Server.MapPath("review-simple.xsl");

  // Load the XML.
  var source = Server.CreateObject("Microsoft.XMLDOM");
  source.async = false;
  source.load(sourceFile);
  // Load the XSL.
  var style = Server.CreateObject("Microsoft.XMLDOM");
  style.async = false;
  style.load(styleFile);
  if (source.parseError.errorCode != 0)
    result = reportParseError(source.parseError);
  else
  {
    if (style.parseError.errorCode != 0)
      result = reportParseError(style.parseError);
    else
    {
      try {
        result = source.transformNode(style);
      }
      catch (exception) {
        result = reportRuntimeError(exception);
      }
    }
  }
  Response.Write(result);
%>

The code here is essentially the same as that for the client side, with the same error formatting functions and processing. The Server.CreateObject and Response.Write methods are used in place of the client-side functions provided by data islands and the innerHTML method.

Try it! The following samples, found in the Review demo at XSLT Developer's Guide Samples, demonstrate the above code in various error situations:

  • ASP Error Message Demo (XML Source Parsing Error)
  • ASP Error Message Demo (XSLT Style Sheet Parsing Error)
  • ASP Error Message Demo (XSLT Run-time Error)
  • ASP Error Message Demo (XSLT Script Error)
  • ASP Error Message Demo (No Errors)

See the XSL version of these samples at XSL Developer's Guide Samples.


XPath

XPath is a language used to create expressions that can address parts of an XML document, manipulate strings, numbers, and Booleans, and can match a set of nodes in the document. XPath is used by both XSLT and XPointer, and models an XML Document as a tree of nodes of different types, including element, attribute and text. XPath expressions can identify these nodes in the XML document, based on their type, name, and values, as well as the relationship of a node to other nodes in the document. An XPath expression yields one of the following four basic objects:

  • node set
  • Boolean
  • number
  • string

For instance, the query "find 'author' elements that have a 'period' attribute with the value 'classical', and that are contained in the 'authors' element at the document root" can be expressed as an XPath expression of the form "/authors/author[@period='classical']".

These simple expressions are a core part of XSLT transformations, which associate the expression results with templates to create a new XML document. Expressions using the XPath syntax can also be performed directly against the XML Document Object Model (DOM).

An important kind of XPath expression is a location path: an expression that selects a set of nodes relative to the context node. The result of evaluating location path is a node-set containing the selected nodes.

The MSXML Parser implements XPath as described in the XML Path Language (XPath) Version 1.0 W3C Recommendation, November 16 1999, found at http://www.w3.org/TR/xpath.

This section contains the following topics:

  • Introduction to the Syntax of XPath
  • How XSLT Defines the Context for XPath Expressions
  • How the DOM Defines the Context for XPath Expressions
  • Placing a List of Items into a Grid
  • Creating a Comma-Separated List of Items

See Also

XPath Syntax | Getting Started with XSLT | Authoring Match Patterns


Introduction to the Syntax of XPath

XML documents represent a hierarchy or tree of nodes, similar to the hierarchy of directories and files found in a file system. This similarity is the basis for the similarity of syntax between URLs and XPath. Some analogous features are compared in the following table.

File System (URLs) | XPath
Hierarchy comprised of directories and files in a file system. | Hierarchy comprised of elements and other nodes in an XML document.
Files at each level have unique names—URLs always identify a single file. | Element names at each level might not be unique—XPath patterns identify a set of all the matching elements.
URLs are evaluated relative to a particular directory—called the "current directory." | XPath patterns are evaluated relative to a particular node—called the "context" for the query.

The following XML data represents a simple hierarchy, which will be used to demonstrate some of the basic query facilities of XPath.

<authors>
  <author>
    <name>Victor Hugo</name>
    <nationality>French</nationality>
  </author>
  <author period="classical">
    <name>Sophocles</name>
    <nationality>Greek</nationality>
  </author>
  <author>
    <name>Leo Tolstoy</name>
    <nationality>Russian</nationality>
  </author>
  <author>
    <name>Alexander Pushkin</name>
    <nationality>Russian</nationality>
  </author>
  <author period="classical">
    <name>Plato</name>
    <nationality>Greek</nationality>
  </author>
</authors>

XPath can use any node in the XML document as the context for a query. In an XSLT style sheet, the context for a query is the source node being currently processed by an <xsl:template> or <xsl:for-each> element. When using XPath directly from the DOM, you define the context by performing the query from a particular node. For more information about how a context is defined, see How the DOM Defines the Context for XPath Expressions and How XSLT Defines the Context for XPath Expressions. The examples in this topic use the context of the root of the XML document.

A basic XPath pattern describes a path through the XML hierarchy with a slash-separated list of child element names. Starting from the document root, the following Pattern traverses down through the hierarchy of the sample XML document above to the <name> elements.

authors/author/name

The XPath pattern identifies all elements that match the path. Because XSLT always has the potential for identifying a set of nodes in a tree, it can be used as a simple query mechanism.

In addition to describing a path down a known hierarchy, XPath can include wildcards for describing unknown elements. An element of any name is represented by "*".

authors/*/name

The above query also identifies all the name elements, but does not require them to be children of an <author> element. Here is another example that finds both <name> and <nationality> elements.

authors/author/*

Branches on the path can also be specified, using square brackets. The following query describes a branch on the <author> element, indicating that only <author> elements with nationality children should be considered.

authors/author[nationality]/name

This becomes even more useful for the sample data when comparisons are added. The following query returns the names of Russian authors. Note that comparisons can be used only within brackets.

authors/author[nationality='Russian']/name

Attributes are indicated in a query by preceding the name of the attribute with "@". The attribute can be tested as a branch off the main path, or the query can identify attribute nodes. The following examples return authors from the classical period, and just the two period attributes, respectively.

authors/author[@period="classical"]
authors/author/@period

There are many more features available in XPath. The complete list supported by the MSXML Parser is described in XPath Syntax.

Try it! See these queries and test your own in the Simple XPath Query Demo found at XSLT Developer's Guide Samples.

See the XSL version of this sample at XSL Developer's Guide Samples.


How XSLT Defines the Context for XPath Expressions

At run time, templates in a style sheet are associated with specific source nodes, starting with the document root node that is associated with the root template (match="/"). Within the template, the associated source node is referred to as the current node. The current node defines a context for evaluating XPath expressions within the template.

XPath expressions are accepted as attribute values for <xsl:value-of>, <xsl:for-each>, <xsl:apply-templates>, <xsl:if>, <xsl:when>, <xsl:copy-of>, <xsl:param>, <xsl:sort>, <xsl:variable>, <xsl:with-param>, and <xsl:template>. The various attribute names define how the context for the query is determined and whether or not it results in a new context.

Select Expressions

The <xsl:value-of>, <xsl:for-each>, <xsl:copy-of>, <xsl:param>, <xsl:sort>, <xsl:variable>, <xsl:with-param>, and <xsl:apply-templates> elements have a select attribute. This expression is evaluated relative to the template's current node. In <xsl:for-each> or <xsl:apply-templates>, the select returns a node-set, and each node in this set becomes the current node for further queries within it.

<TABLE BORDER="1">
  <xsl:for-each select="invoices/invoice">
    <TR>
      <TD>Invoice #<xsl:value-of select="@id"/></TD>
    </TR>
    <xsl:for-each select="items/item">
      <TR>
        <TD><xsl:value-of select="qty"/></TD>
        <TD><xsl:value-of select="description"/></TD>
        <TD>$<xsl:value-of select="price"/></TD>
      </TR>
    </xsl:for-each>
  </xsl:for-each>
</TABLE>

The first <xsl:for-each> selects a set of "invoice" elements, each of which becomes the context for the "items/item" query. Each "item" in turn becomes the context for the various queries in the <xsl:value-of> elements.

Each node selected by <xsl:apply-templates> is associated with a template and thus becomes the current node for the template.

Because each expression defines a new context, a set of nested queries as in the above example produces a set or "path" of context nodes active at one time. xmconPlacingItemsintoaGridxmconCreatingCommaSepList

Test Boolean Expressions

The conditional elements <xsl:if> and <xsl:when> do not define a new context for queries within themselves because they do not actually select new nodes. They just test to make sure they are there.

<TABLE>
  <xsl:for-each select="items/item">
    <TR>
      <TD>
        <xsl:value-of select="qty"/>
      </TD>
      <TD>
        <xsl:value-of select="description"/>
      </TD>
      <TD>
        $<xsl:value-of select="price"/>
      </TD>
      <TD> <!-- 10% volume discount -->
        <xsl:if test="qty[.>=10]">
          <xsl:for-each select="price">
            <xsl:value-of select="formatNumber(.*.10, '$#,##0.00')"/>
          </xsl:for-each>
        </xsl:if>
      </TD>
      <TD STYLE="text-align:right"> <!-- line total -->
        <xsl:value-of select="formatNumber(user:lineTotal(.), '$#,##0.00')"/>
      </TD>
    </TR>
  </xsl:for-each>
</TABLE>

In this example you can see that the <xsl:for-each select="price"> query is relative to "item" elements, despite the intervening conditional that tests the "qty" value.

Match Patterns

The <xsl:template> element has a match attribute that accepts a pattern for the purposes of matching templates to specific elements, and thus there is no fixed context for a query in this case. See Authoring Match Patterns.

Try it! The above example can be found within the Invoice sample at XSLT Developer's Guide Samples.

For the XSL version of this sample, see XSL Developer's Guide Samples.


How the DOM Defines the Context for XPath Expressions

The MSXML Parser implementation contains methods that accept an XPath string to conveniently identify nodes within the XML tree. The selectNodes method returns a node list pointing to each of the selected nodes, as in the following example.

items = xmldoc.selectNodes("invoices/invoice/items/item");

Retrieval of the "item" nodes by walking the tree would take a substantial amount of code. With the selectNodes method and XPath expressions, nodes can be retrieved through a single line of code.

In most cases the returned set of nodes will be iterated over to perform further manipulations. The following example illustrates how you can calculate a total price for an "invoice" element.

function invoiceTotal(invoice)
  {
    invoice = invoice.nextNode()
    items = invoice.selectNodes("items/item");
    var sum = 0;
    for (var item = items.nextNode(); item; item = items.nextNode())
    {
      var price = item.selectSingleNode("price").nodeTypedValue;
      var qty = item.selectSingleNode("qty").nodeTypedValue;
      if (qty >= 10)
        price = 0.9*price;
      sum += price * qty;
    }
    return sum;

This example contains two query contexts. The first query is performed from the context of the "invoice" element (invoice), and the query string reflects this by beginning with the document element "items." The for... loop iterates through the returned nodes and uses each as a context for further queries. These query strings are relative to "item" elements, and look within them for the "price" and "qty" children. This illustrates that the node upon which the selectNodes is performed defines the context for the query.

A common source of confusion involves performing queries from the document root versus the document element. These two nodes are different in the XML tree and thus require different query strings. The following three statements will yield identical results for the sample document, although only the last two are logically equivalent.

items = xmldoc.selectNodes("invoices/invoice");
items = xmldoc.selectNodes("*/invoice");
items = xmldoc.documentElement.selectNodes("invoice");

The selectSingleNode method returns the first node that matches the query, providing a convenient way to return a node directly without extracting it from a NodeList. It is equivalent to "selectNodes(pattern).item(0)". The previous example could take advantage of selectSingleNode to simplify the code a bit.

function invoiceTotal(invoice)
{
  invoice = invoice.nextNode()
  items = invoice.selectNodes("items/item");
  var sum = 0;
  for (var item = items.nextNode(); item; item = items.nextNode())
  {
    var price = item.selectSingleNode("price").nodeTypedValue;
    var qty = item.selectSingleNode("qty").nodeTypedValue;
    sum += price * qty;
  }
  return sum;
}

In this example, selectNodes is still used to select a set of "item" elements. When only a single node is expected, such as the "price" and "qty" elements, selectSingleNode can be used.

Try it! This sample code is part of the Invoice Sample found at XSLT Developer's Guide Samples.

See the XSL version of this sample at XSL Developer's Guide Samples.


Placing a List of Items into a Grid

A frequently requested transform is placing a list of items into a grid or table. For instance, given a list of product names, you might want to present them two across in a table, like this:

product 1 | product 2
product 3 | product 4

Items need to be grouped in pairs into table rows. XSLT currently does not have a built-in mechanism to handle arbitrary grouping. A combination of the <xsl:if> element with the test attribute and the <xsl:variable> element can be used to approximate this behavior. The following template turns a list of "product" elements into a two-column table.

<TABLE BORDER="1">
  <xsl:for-each select="products/product">
    <xsl:variable name="pos" select="position()"/>
    <xsl:if test="position() mod 2 = 1">
      <TR>
        <TD><xsl:value-of select="."/></TD>
        <TD><xsl:value-of select="../product[$pos+1]"/></TD>
      </TR>
    </xsl:if>
  </xsl:for-each>
</TABLE>

How does this work? First, you iterate through each product element but use an <xsl:if> with the expression "position() mod 2 = 1" to isolate only those products that should start a new row (every other product) and its position saved in the variable "pos". The name of this product also is placed in the first cell in the row. Then you make another cell and place within it the next child—the one filtered out using the <xsl:if>. The query to select the next element navigates to the parent and selects the product with a position one greater than the position of the current node.

Three-row tables can be obtained by adding another cell to the row to obtain another sibling element, and adjusting the expression to allow only every third element to create a new row. You can extrapolate this mechanism to create tables with even more columns.

<TABLE BORDER="1">
  <xsl:for-each select="products/product">
    <xsl:variable name="pos" select="position()"/>
    <xsl:if test="(position() mod 3) = 1">
      <TR>
        <TD><xsl:value-of select="."/></TD>
        <TD><xsl:value-of select="../product[$pos+1]"/></TD>
        <TD><xsl:value-of select="../product[$pos+2]"/></TD>
      </TR>
    </xsl:if>
  </xsl:for-each>
</TABLE>

Try it! View a list of products in a two- or three-column table in the Product List sample found at XSLT Developer's Guide Samples.

See the XSL version of this sample at XSL Developer's Guide Samples.


Creating a Comma-Separated List of Items

The last() function can be used to determine whether the element is the last one in the query.

<DIV>
  <xsl:for-each select="products/product">
    <xsl:value-of select="."/> <xsl:if test="position()!=last()">, </xsl:if>
    </xsl:for-each>
</DIV>

Because the comma is being inserted based on the position in the query and not based on the position in the source document, the list can be sorted without creating errors in the results. The following template shows how to add commas to a sorted product list.

<DIV>
  <xsl:for-each select="products/product">
    <xsl:sort select="product" order="descending"/>
      <xsl:value-of select="."/><xsl:if test="position()!=last()">, </xsl:if>
  </xsl:for-each>
</DIV>

The <xsl:sort> order attribute is given the value "descending" to indicate a descending sort by product name.

In the following example, the names in a group of names are formatted as a comma-separated list:

<xsl:template match="namelist/name">
  <xsl:apply-templates/>
  <xsl:if test="position()!=last()">, </xsl:if>
</xsl:template>

Another way to accomplish this is by reversing the logic to verify whether this name is the first. In some circumstances this performs better than the above example because last() requires that the entire set of names be found and counted, while this one does not.

<xsl:template match="namelist/name">
  <xsl:if test="position()!=1">, </xsl:if>
  <xsl:apply-templates/>
</xsl:template>

Try it! View the result of the above templates in the Product List (Commas) Sample found at XSLT Developer's Guide Samples.

See the XSL version of this sample at XSL Developer's Guide Samples.


Advanced XSLT Features

The mechanisms described in Getting Started with XSLT provide template-driven transformations. This indicates that the output template drives the transformation process, pulling in data from the XML tree and inserting it into the template. This works well when the data has a fairly simple and homogeneous structure. If you know what the output structure should be, use the template-driven model.

XSLT also provides mechanisms for data-driven transformations, where the output is assembled from template fragments based on the structure of the input document. This is especially useful for XML that represents complex data structures or documents, as shown in Handling Documents and Irregular Data. If you only know what types of elements you might be encountering in the XML source, but not their order, parentage, or relationship to other elements, use the data-driven model.

This section contains the following topics:

  • Handling Documents and Irregular Data
  • Authoring Match Patterns
  • Debugging a Style Sheet
  • Built-in Templates
  • Creating a Table of Contents
  • Translating Between Schemas
  • Generating More Sophisticated XML Output


Handling Documents and Irregular Data

One of the benefits of using XML is the ability to model irregular data hierarchies, including data with these characteristics:

  • Collections of heterogeneous elements
  • Structures with many optional elements
  • Structures where the order is important
  • Recursive structures
  • Structures with complex containment requirements

This sounds complex, but most of these conditions are present in XML that represents documents. The following Pole example exhibits many of these characteristics.

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="pole.xsl"?>
<document>
  <title>To the Pole and Back</title>
  <section>
    <title>The First Day</title>
    <p>It was the <emph>best</emph> of days, it was the
      <emph>worst</emph> of days.</p>
    <list>
      <item><emph>best</emph> in that the sun was out.</item>
      <item><emph>worst</emph> in that it was 39 degrees below zero.</item>
    </list>
    <section>
      <title>Lunch Menu</title>
      <list>
        <item>ice cream</item>
        <item>popsicles</item>
      </list>
    </section>
  </section>
  <section>
    <title>The Second Day</title>
    <p>Ditto the first day.</p>
  </section>
</document>

Comparing this sample with the characteristics of irregular data, you can see that it contains heterogeneous collections of elements—a section can contain an arbitrary collection of <title> elements, <p> elements, <list> elements, and so on. Many elements are indeed optional—a section need not contain <p> or <list> elements, or other "section" elements. The order of most elements is important to preserve in the output—the first section comes before the second section. The structure is recursive because a "section" element can contain another "section". The <emph> element is probably allowed anywhere—indicating a complex set of containment requirements.

XSLT's ability to handle such irregular and recursive data makes it useful for transforming documents into a display language such as HTML—hence the name and origins of the Extensible Stylesheet Language.

The mechanism for handling data-driven transformations is similar to subroutines in programming languages. Template fragments (subroutines) can be defined and called. Instead of calling the templates by name, however, the most appropriate fragment is chosen based on the type of element for which the template is designed.

To manage this data, start by writing an output template for the HTML "wrapper," which inserts the document title into the output in two places, and then asks XSLT to find the appropriate template (call the appropriate subroutine) for "section" elements. For example:

<HTML>
  <HEAD>
    <TITLE><xsl:value-of select="document/title"/></TITLE>
  </HEAD>
  <BODY>
    <H1><xsl:value-of select="document/title"/></H1>
    <xsl:apply-templates select="document/section"/>
  </BODY>
</HTML>

The <xsl:apply-templates> element selects the "section" children of the document (not all of them, just the top level) and asks XSLT to find and apply an appropriate template. Now it is necessary to write a template that is appropriate for "section" elements.

<xsl:template match="section">
  <DIV>
    <xsl:apply-templates />
  </DIV>
</xsl:template>

The XSLT processor will output this template fragment for each of the "section" elements selected by the <xsl:apply-templates> element. The value of the match attribute indicates the kinds of nodes for which this template is appropriate. In this case it indicates that this template is appropriate for "section" elements. The nodes selected by <xsl:apply-templates> are matched up with the correct template.

Note that the template for sections itself contains an <xsl:apply-templates> element. Without a select attribute, all the children will be selected, and the XSLT processor will take each one in order (title, p, list, section) and look for an appropriate template. There already is a section template—this one—and the XSLT processor will recursively apply it, resulting in a nested structure of <DIV> elements that mirrors the nested structure of "section" elements in the source document.

Now define some more templates to handle other element types.

<xsl:template match="title">
  <H2><xsl:apply-templates /></H2>
</xsl:template>
<xsl:template match="p">
  <P><xsl:apply-templates /></P>
</xsl:template>
<xsl:template match="list">
  <UL>
    <xsl:for-each select="item">
      <LI><xsl:apply-templates /></LI>
    </xsl:for-each>
  </UL>
</xsl:template>
<xsl:template match="emph">
  <I><xsl:apply-templates /></I>
</xsl:template>

In each case you can include <xsl:apply-templates> to continue selecting the children (whatever they may be) and finding the appropriate template.

The <xsl:apply-templates> element is not limited to selecting element children, but can select other child nodes as well, including text. You can add a template to copy text children to the output.

<xsl:template match="text()"><xsl:value-of /></xsl:template>

When run against the Pole sample document, the templates above produce the following output:

<HTML>
  <HEAD>
    <TITLE>To the Pole and Back</TITLE>
  </HEAD>
  <BODY>
    <H1>To the Pole and Back</H1>
    <DIV>
      <H2>The First Day</H2>
      <P>It was the <I>best</I> of days, it was the
        <I>worst</I> of days.</P>
      <UL>
        <LI><I>best</I> in that the sun was out.</LI>
        <LI><I>worst</I> in that it was 39 degrees below zero.</LI>
      </UL>
      <DIV>
        <H2>Lunch Menu</H2>
        <UL>
          <LI>ice cream</LI>
          <LI>popsicles</LI>
        </list>
      </DIV>
    </DIV>
  </BODY>
</HTML>

By recursively processing the source document with <xsl:apply-templates>, this style sheet essentially converts the element types in the source XML to HTML element types. Even though this example is fairly trivial, you can already see some additional structural modifications occurring, notably the creation of the <HEAD> element and the duplication of the document title in both the <H1> element and the <TITLE> element.

This collection of templates can be packaged into a style sheet file by placing them within an <xsl:stylesheet> element. The XSLT namespace must be declared here.

The top-level template (or root template) needs to be marked as such by placing it within a template and giving it the special pattern / to indicate that this is the template for the document root. Here is the final complete style sheet.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
    <HTML>
      <HEAD>
        <TITLE><xsl:value-of select="document/title"/></TITLE>
      </HEAD>
      <BODY>
        <H1><xsl:value-of select="document/title"/></H1>
        <xsl:apply-templates select="document/section"/>
      </BODY>
    </HTML>
  </xsl:template>
  <xsl:template match="title">
  </xsl:template>
  <xsl:template match="section">
    <DIV>
      <H2><xsl:value-of select="title"/></H2>
      <xsl:apply-templates />
    </DIV>
  </xsl:template>
  <xsl:template match="p">
    <P><xsl:apply-templates /></P>
  </xsl:template>
  <xsl:template match="list">
    <UL>
      <xsl:for-each select="item">
        <LI><xsl:apply-templates /></LI>
      </xsl:for-each>
    </UL>
  </xsl:template>
  <xsl:template match="emph">
    <I><xsl:apply-templates /></I>
  </xsl:template>

</xsl:stylesheet>

This example illustrates the data-driven model of XSLT processing. For most of the structure of a document, you won't know what could be coming next. Instead you can create isolated templates for the types of nodes you expect to see in the output without too much consideration of their structure. In places where the structure is locally known, you can use <xsl:for-each> and <xsl:value-of> to populate the template. For instance, "list" and "item" elements appear in a regular and predictable structure. The ability to switch smoothly between data-driven and template-driven transformation is an important feature of XSLT.

Try it! This style sheet and data can be found in the To The Pole sample at XSLT Developer's Guide Samples.

See the XSL version of this sample at XSL Developer's Guide Samples.


Authoring Match Patterns

The match attribute on the <xsl:template> element contains a Pattern expression. The syntax is the same as that used to select nodes with <xsl:for-each>, <xsl:value-of>, and <xsl:apply-templates> elements. However, the pattern is used in quite a different way.

In a "select" pattern, the pattern describes a query down from a particular node to locate a new set of nodes. A "match" pattern is quite different. The match pattern doesn't locate anything new, but rather compares a specific node against a pattern to see if the node matches that pattern, and thus whether or not to use a particular template.

Here are some of the differences between the use of match patterns and select patterns.

Select patterns | Match patterns
Starts with a node (the context), and identifies more nodes. | Starts with a node (the target), and yields a true/false match/didn't match result.
Starts at the context and drills down into the data. | Starts by testing the node against the node target, and then checks the context, including ancestry and descendants.
Represents a query into an XML document. | Represents a pattern of contextual information against which a particular node can be tested. Match patterns are quite similar to selectors in CSS.

The simplest match pattern is just a single element name (such as "section") or node type (such as "text()"). It is simple to see whether a node matches or not. A text node will match the "text()" pattern and the associated template will be used; a "section" element will match the "section" pattern and that template will be used.

Matches become a bit more complex when they are used to not only test a node, but also the context in which the node appears in the source document. The pattern "section/title" will match "title" elements that are children of "section" elements. "title" elements not within a section (for example, the document title) do not match this pattern, and a separate template must be defined (such as "document/title"). Note that the target of the pattern (the rightmost term) represents the node to be tested, similar to a "select" pattern in which the target represents the nodes to select.

The following table lists some match patterns and their meanings.

Pattern | Meaning when used as a match pattern
title | Matches "title" elements.
section/title | Matches "title" elements that are children of "section" elements.
section//title | Matches "title" elements contained within "section" elements.
section/title[@short-name] | Matches "title" elements that are children of "section" elements, and that have a "short-name" attribute.
appendix//section[@type='reference']/title | Matches "title" elements that are children of "section" elements. The section must also have a "type" attribute with the value "reference", and have an "appendix" ancestor.
appendix[.//section[@type='reference']/title] | Matches "appendix" elements that contain "section" descendants, which in turn have both a "type" attribute with the value "reference" and a "title" child.

For more information, see Introduction to the Syntax of XPath and XPath Syntax.

You can add another template to the Pole sample, to provide different formatting (H3 instead of H2) for sections when they appear in the context of another section. For example:

<xsl:template match="section">
  <DIV>
    <H2><xsl:value-of select="title"/></H2>
    <xsl:apply-templates />
  </DIV>
</xsl:template>
<xsl:template match="section/section">
  <DIV>
    <H3><xsl:value-of select="title"/></H3>
    <xsl:apply-templates />
  </DIV>
</xsl:template>

Notice that a section within a section will match the pattern "section" as well as the "section/section" pattern. Which template will be selected? The rule is: Templates later in the style sheet override earlier templates. To ensure that the XSLT processor checks the "section/section" pattern first, place it farther down in the style sheet. Most of the templates in this sample are mutually exclusive, so their order is unimportant.

Try it! View the To The Pole sample at XSLT Developer's Guide Samples.

See the XSL version of this sample at XSL Developer's Guide Samples.


Debugging a Style Sheet

The interaction of a style sheet with a data file can be a complex process, and style sheet errors might not always be obvious. This section provides troubleshooting advice for the style sheet author.

Problem:

No Output At All

Solution:

When browsing to an XML file with an XSLT style sheet, well-formedness errors in the source file or the style sheet are reported, along with XSLT syntax errors and run-time errors. These errors are not automatically reported when using XSLT from script or upon data islands. For sample code to detect, format, and report these errors, see Detecting and Handling XSLT Errors. Alternatively, use direct browsing as a development tool for reporting errors, as described in Using the Default Style Sheet as a Debugging Aid.

If no errors are being reported, the problem is most likely an error in the style sheet design, which can be diagnosed and corrected by the style sheet author. Begin by testing that the root template of the style sheet is being executed, using the method outlined in the section that follows, "Expected Template Is Not Being Used."

Problem:

Expected Template Is Not Being Used

Solution:

If the contents of a template (including the root template) do not seem to be appearing in the output, first check that the template is really being called and not just producing output that doesn't cause display by adding an explicit trace message.

<xsl:template match="address">
  !! inside address template !!
  <xsl:apply-templates select="zipcode"/>
</xsl:template>

If the trace message appears in the output, the template is being executed, and the problem is likely that this <xsl:apply-templates> is not returning anything. For hints on diagnosing why the select pattern returns no results, see the section that follows, "No Results from Select Pattern."

If the trace message does not appear, the problem could be that the match pattern is in error. Check the spelling and case of element and attribute names, and check that the full context for the match is actually present in the source data.

Also determine which <xsl:apply-templates> element should invoke the template and ensure that it is selecting the correct nodes—see "No Results from Select Pattern".

If the template uses the root pattern (/) and is not being called, check that no template later in the style sheet uses the root pattern or omits the match attribute (matching all nodes including the document root). If the style sheet is being executed against a node other than the document root, a template matching that element is used instead of the root template.

Problem:

No Results from Select Pattern

Solution:

One of the most frequent errors preventing output is select patterns that don't match the input data. Fortunately these are also fairly easy to fix. The debug line added to the example below uses <xsl:value-of> to output the node and its children as preformatted XML source.

<xsl:template match="address">
  <PRE><xsl:value-of select="."/></PRE>
  <xsl:apply-templates select="zipcode"/>
</xsl:template>

Comparing the actual XML data to the select pattern, spelling mistakes in element and attribute names can be found; for example, the data could show "zip-code" or "zipCode" as the correct element name. The context of the query can be checked—is "zipcode" a child of "address"? And the source data can also be checked for accuracy—does this "address" have a "zipcode"?

For complex patterns it is often helpful to progressively simplify the pattern to isolate the problem.

Problem:

Elements with the XSLT Namespace Appear in Output

Solution:

Elements from the XSLT namespace appearing in the output indicate that the XSLT namespace declaration is using an incorrect URL. The XSLT processor doesn't recognize these elements as XSLT elements and treats them as output elements.

The correct namespace identifier for XSLT is "http://www.w3.org/1999/XSL/Transform".

Problem:

Highlighting Elements Not Handled by a Template

Solution:

Often it is helpful to author a style sheet incrementally, adding templates for elements one at a time. Adding this handy template to your style sheet will show you visually which elements do not have templates associated with them yet.

<xsl:template match="*">
  <SPAN STYLE="background-color:yellow">
    <xsl:attribute name="title">&lt;<xsl:value-of select="."/>&gt;</xsl:attribute>
    <xsl:apply-templates/>
  </SPAN>
</xsl:template>

Add this template near the top of your style sheet so that it doesn't override templates that handle specific elements. This template will display a yellow background behind the content of any element without a specific template in existence. Running the mouse over the yellow areas will display a ToolTip showing which elements still need to be handled by templates.

Writing style sheets in this fashion almost becomes a game—create a template and more yellow disappears. By the time the yellow is gone, your style sheet is finished!

See Also

Using the Default Style Sheet as a Debugging Aid


Built-in Templates

The November 1999 XSLT Recommendation includes two built-in templates, which are included in the Microsoft XSLT processor. These templates make it easier to create style sheets for irregular data by passing text from the source document to the output automatically.

The built-in template included in the Microsoft XSLT processor for element nodes and the root node looks like this:

<xsl:template match="/|*"><xsl:apply-templates/></xsl:template>

The built-in template for text and attribute nodes is as follows:

<xsl:template match="text()"><xsl:value-of select="."/></xsl:template>

The built-in template for processing instructions and comments is to do nothing.

<xsl:template match="processing-instruction()|comment()"/>

On their own, these templates provide for a complete (albeit simple) transformation of the source document. Elements and the document root are traversed, although they do not generate any output themselves. The values of text nodes and CDATA sections are passed through. Comments and processing instructions are not processed and thus do not appear in the output. The end result is that these templates by themselves strip all markup from the source document but preserve the text.

To override these built-in templates, specify your own templates in the style sheet so that every possible node is handled.


Creating a Table of Contents

Often an element from the source XML document appears multiple times in the output, with different formatting in each case. The classic example is the table of contents, in which section titles appear at the start of the document, and each one is also displayed at the start of the section. XSLT makes it easy to place something in multiple places—just select it multiple times using the <xsl:for-each> or <xsl:apply-templates> elements.

The <xsl:for-each> element provides a simple way to apply different formatting for each location. The <xsl:for-each> element contains its own template, so different output for various occurrences of an element is implied. In the following example, the "title" elements are pulled from the top-level section and used in a simple table of contents.

<xsl:template match="/">
  <H4>Table of Contents</H4>
  <xsl:for-each select="document/section">
    <DIV STYLE="margin-left:1em">
      <xsl:value-of select="title"/>
    </DIV>
  </xsl:for-each>
  <xsl:apply-templates select="document/section"/>
</xsl:template>
<xsl:template match="section">
  <DIV>
    <H2><xsl:value-of select="title"/></H2>
    <xsl:apply-templates />
  </DIV>
</xsl:template>

This example is a simplification of that found in the To The Pole Sample (with TOC).

Often, however, a hierarchical table of contents is needed, typically when the document itself defines sections recursively—sections containing subsections ad infinitum. <xsl:apply-templates> is specially suited for recursive structure, and it allows for templates to be defined locally. A template scoped in this way will not participate in the selection of an appropriate template at the global level. The template will only apply for items placed in the table of contents.

A document grammar with recursive structure is used by the W3C for XML versions of Recommendations (such as the XSLT Transformation). This grammar contains nested sections named "div1", "div2", and so on. While not strictly recursive, it is easy enough to treat it as if it were. The following example shows a hierarchical table of contents for this grammar. It uses an <xsl:template> element contained in (scoped to) the <xsl:apply-templates> element, which selects the top-level sections ("div1" in the document body).

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="text()"/>

<xsl:template match="/">
  <html>
    <body>
      <H3>Table of Contents</H3>
      <xsl:apply-templates select="spec/body/div1 | spec/back/*" mode="toc"/>
    </body>
  </html>
</xsl:template>

<xsl:template match="div1|div2|div3|div4|div5|div6" mode="toc">
  <DIV STYLE="margin-left:1em"><xsl:number level="multiple" format="1.1.1.1.1. " count="div1|div2|div3|div4|div5|div6"/>
  <xsl:value-of select="head"/><br/>
  <xsl:apply-templates select="div2|div3|div4|div5|div6" mode="toc"/>
  </DIV>
</xsl:template>

<xsl:template match="inform-div1" mode="toc">
  <DIV STYLE="margin-left:1em"><xsl:number level="multiple" format="1.1.1.1.1. "/>
    <xsl:value-of select="head"/> (Non-Normative)
    <xsl:apply-templates select="div2" mode="toc"/>
  </DIV>
</xsl:template>
</xsl:stylesheet>

The local templates are tested for a match before looking for a global template, and build nested <DIV> elements to indent each level in relation to its parent. Because <xsl:apply-templates> is careful to select only elements that can be matched by local templates, there is no possibility of interfering with the operation of other templates in the style sheet.

Try it! The above example is shown in XSLT Recommendation Table of Contents Sample at XSLT Developer's Guide Samples.

See the XSL version of this sample at XSL Developer's Guide Samples.


Translating Between Schemas

An XSLT style sheet can translate data from one XML grammar to another. The mechanisms in XSLT can be applied directly to any XML grammar—templates can be created using elements from the output grammar and populated using <xsl:for-each>, <xsl:value-of>, and so on.

Imagine that you receive stock quotes in the following format:

<?xml version="1.0"?>
<investments>
  <item type="stock" exch="nyse"   symbol="ZCXM" company="zacx corp"
        price="28.875"/>
  <item type="stock" exch="nasdaq" symbol="ZFFX" company="zaffymat inc"
        price="92.250"/>
  <item type="stock" exch="nasdaq" symbol="ZYSZ" company="zysmergy inc"
        price="20.313"/>
</investments>

You want to add this information to the rest of your stock information, which uses a different schema. You would like this information to look like this:

<?xml version="1.0"?>
<portfolio xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <stock exchange="nyse">
    <name>zacx corp</name>
    <symbol>ZCXM</symbol>
    <price dt:dt="number">28.875</price>
  </stock>
  <stock exchange="nasdaq">
    <name>zaffymat inc</name>
    <symbol>ZFFX</symbol>
    <price dt:dt="number">92.250</price>
  </stock>
  <stock exchange="nasdaq">
    <name>zysmergy inc</name>
    <symbol>ZYSZ</symbol>
    <price dt:dt="number">20.313</price>
  </stock>
</portfolio>

To transform the "investments" data into the "portfolio" grammar, you can use familiar XSLT techniques. Your templates will contain output elements such as "stock" instead of HTML elements. Note that attribute values are extracted using <xsl:value-of> and either generate new attributes using <xsl:attribute> or are converted to elements.

<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:template match="/">

<portfolio xmlns:dt="urn:schemas-microsoft-com:datatypes">

<xsl:for-each select="investments/item[@type='stock']">

<stock>

<xsl:attribute name="exchange"><xsl:value-of select="@exch"/></xsl:attribute>

<name><xsl:value-of select="@company"/></name>

<symbol><xsl:value-of select="@symbol"/></symbol>

<price dt:dt="number"><xsl:value-of select="@price"/></price>

</stock>

</xsl:for-each>

</portfolio>

</xsl:template>

</xsl:stylesheet>

This example also shows XSLT generating a document that declares a namespace. The data types namespace declaration on the <portfolio> element is treated by XSLT as it would any other attribute appearing on an output element and passed through. In this case the XSLT namespace declaration is passed through as well.

The translation can be performed in reverse with a similar style sheet, converting "portfolio" data to the "investments" grammar.

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
    <investments>
      <xsl:for-each select="portfolio/stock">
        <item type="stock">
          <xsl:attribute name="exch"><xsl:value-of select="@exchange"/></xsl:attribute>
          <xsl:attribute name="symbol"><xsl:value-of select="symbol"/></xsl:attribute>
          <xsl:attribute name="company"><xsl:value-of select="name"/></xsl:attribute>
          <xsl:attribute name="price"><xsl:value-of select="price"/></xsl:attribute>
        </item>
      </xsl:for-each>
    </investments>
  </xsl:template>
</xsl:stylesheet>

Note the use of <xsl:attribute> to not only copy an attribute value to a new attribute, but also to convert element content to attribute values.

Try it! You can see these transformations in the Portfolio sample at XSLT Developer's Guide Samples.

See the XSL version of this sample at XSL Developer's Guide Samples.


Generating More Sophisticated XML Output

When generating XML, it often is useful to generate comments, processing instructions, and so on. XSLT provides a set of commands for creating specific kinds of nodes in the output. Just as the <xsl:attribute> element can insert an attribute into the tree, XSLT provides a similar set of elements for inserting other types of nodes. The <xsl:attribute> element is described in Accessing and Outputting Attributes.

The <xsl:comment> element inserts a comment into the output—comments within the style sheet are not passed through to the output, but are treated as comments upon the style sheet itself. When a comment is needed in the output, place the comment text within an <xsl:comment> element.

The <xsl:processing-instruction> element allows processing instructions (PIs) to be inserted into the output. The name attribute specifies the name of the attribute, and the content of the element becomes the text of the PI.

The <xsl:element> element provides an alternate mechanism for creating elements in the output. The name attribute specifies the name of the element. The following two ways of creating a <DIV> element produce identical results.

1) <DIV class="menuItem">
     Choose me
   </DIV>
2) <xsl:element name="DIV">
     <xsl:attribute name="class">menuItem</xsl:attribute>
     Choose me
   </xsl:element>

Note that attributes must be added to elements created using the <xsl:element> element with the <xsl:attribute> element instead of being placed directly on the element.

Because XSLT allows output elements to be specified directly, there are only a few rare situations where <xsl:element> is useful. One use is as an escaping mechanism for creating XSLT elements in the output, allowing style sheets to generate style sheets.

The following sample converts style sheets to use the <xsl:element> syntax. In the process, it uses the <xsl:element> to generate elements in the XSLT namespace and inserts PIs and a comment at the start of the file.

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="showxsl.xsl"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
    <xsl:processing-instruction name="xml">version="1.0"</xsl:processing-instruction>
    <xsl:processing-instruction name="xml-stylesheet">type="text/xsl" href="style.xsl"</xsl:processing-instruction>
    <xsl:comment>Style sheet converted automatically to &lt;xsl:element&gt; syntax</xsl:comment>
    <xsl:apply-templates select="comment()"/>
    <xsl:apply-templates select="*"/>
  </xsl:template>
  <!-- Copy text, comments and pis -->
  <xsl:template match="comment() | processing-instruction() | text()">
    <xsl:copy>
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>
  <!-- Convert non-XSL elements to <xsl:element> syntax -->
  <xsl:template match="*">
    <xsl:element name="xsl:element">
      <xsl:attribute name="name"><xsl:node-name/></xsl:attribute>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:element>
  </xsl:template>
  <!-- Convert non-XSL attribute to <xsl:attribute> syntax -->
  <xsl:template match="@*">
    <xsl:element name="xsl:attribute">
      <xsl:attribute name="name"><xsl:node-name/></xsl:attribute>
      <xsl:value-of/>
    </xsl:element>
  </xsl:template>
  <!-- Copy XSL elements and their attributes -->
  <xsl:template match="xsl:*">
    <xsl:copy>
      <xsl:for-each select="@*">
        <xsl:copy><xsl:value-of/></xsl:copy>
      </xsl:for-each>
      <xsl:apply-templates select="node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

The above style sheet has five templates. The first adds a boilerplate to the root of the output document, and selects all comments and the document element for copying to the output. The second template copies comments, PIs, and text nodes to the output. The third template converts elements to the <xsl:element> syntax. The fourth converts attributes to the <xsl:attribute> syntax. And the final one ensures that any elements from the XSLT namespace are copied to the output with their attributes and not converted by the third template.

Try it! You can see the Style Sheet Converter in the Portfolio sample at XSLT Developer's Guide Samples.

See the XSL version of this sample at XSL Developer's Guide Samples.

Using XSL as the Default Namespace

Another use for <xsl:element> is to generate elements when the default namespace of the style sheet is set to XSL. Some style sheets, such as the following one, consist of nearly all elements from the XSL namespace and contain virtually no output elements. It might be convenient to set the default namespace of the document to the XSL namespace so that prefixes can be omitted. <xsl:element> can be used for the rare output elements. Style sheets written in this way can be more readable and compact, and are more amenable to validation with a DTD or XML Schema.

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <!-- Identity transformation template -->
  <template>
    <copy>
      <apply-templates select="@* | * | comment() | processing-instruction() | text()"/>
    </copy>
  </template>

  <!-- Rename "stock" elements to "security" -->
  <template match="stock">
    <element name="security">
      <apply-templates select="@* | * | comment() | processing-instruction() | text()"/>
    </element>
  </template>
</stylesheet>

This sample uses so few named output elements (just one, the "security" element) that it is easier to use the XSL namespace as the default, and use the <xsl:element> element for this one output element. Notice, however, that consistently using the "xsl" prefix helps make your style sheets look familiar to other authors.

Try it! You can see the XML Element Renaming demo in the Portfolio sample at XSLT Developer's Guide Samples.

See the XSL version of this sample at XSL Developer's Guide Samples.


XSLT Reference

The World Wide Web Consortium (W3C) Recommendation for XSLT specifies a transformation language for XML documents. The Microsoft® XML Parser (MSXML) supports a subset of this transformation language, XSL Transformations (XSLT) Version 1.0 (W3C Recommendation 16 November 1999). Microsoft plans to update this technology to full compliance with this recommendation.

You can use XSLT from the XML DOM. The transformNode and transformNodeToObject methods support the use of XSLT elements to perform transformations. The DOM selectNodes and selectSingleNode methods support the XPath expression syntax that enables sophisticated queries for nodes within a particular context of the overall tree structure.

Visual Basic and C++ developers should refer to the following versions of the above methods: transformNode, transformNodeToObject, selectNodes, and selectSingleNode.

This documentation is divided into the following sections:

  • XSLT Elements
  • XPath Syntax
  • Qualified Names
  • NaN Values
  • Expressions
  • Patterns

See Also

Getting Started with XSLT | XSLT and XPath Conformance Notes


XSLT Elements

XSLT can be used to manipulate, sort, and filter XML data. Many transformations will target well-formed HTML. Well-formed means that any HTML tag can be used, subject to the stricter syntax rules of XML—all start tags are paired with end tags and are nested correctly. Well-formed HTML can be displayed by the browser, or further manipulated by XML tools. The W3C currently has a recommendation for making the next generation of HTML an XML application. The W3C XHTML™ 1.0: The Extensible HyperText Markup Language describes some of the benefits of defining well-formed HTML. XSLT is a valuable tool for generating well-formed HTML or other XML files.

XSLT enables you to define templates for your output, into which data from the XML source is delivered. Each template defines a pattern that identifies elements in the source tree and defines the resulting output sub-tree to be generated. The XSLT transformation processor merges data from the XML source document with the template. By combining a set of template fragments into a style sheet, XSLT can be used to perform data-driven transforms useful for highly irregular XML data and XML documents.

XSLT templates are defined using a small set of XML elements, listed below and described in this reference documentation.

XSLT Elements

xsl:apply-templates | Directs the XSLT processor to find the appropriate template to apply, based on the type and context of each selected node.
xsl:attribute | Creates an attribute node and attaches it to an output element.
xsl:choose | Provides multiple conditional testing in conjunction with the <xsl:otherwise> and <xsl:when> elements.
xsl:comment | Generates a comment in the output.
xsl:copy | Copies the current node from the source to the output.
xsl:copy-of | Inserts sub-trees and result tree fragments into the result tree.
xsl:element | Creates an element with the specified name in the output.
xsl:for-each | Applies a template repeatedly, applying it in turn to each node in a set.
xsl:if | Allows simple conditional template fragments.
xsl:include | Specifies another XSLT style sheet to include.
xsl:number | Inserts a formatted number into the result tree.
xsl:otherwise | Provides multiple conditional testing in conjunction with the <xsl:choose> and <xsl:when> elements.
xsl:output | Specifies options for use in serializing the result tree.
xsl:param | Declares a named parameter for use within an xsl:stylesheet or xsl:template. Allows specification of a default value.
xsl:preserve-space | Preserves white space in a document.
xsl:processing-instruction | Generates a processing instruction in the output.
msxsl:script * | Defines global variables and functions for script extensions.
xsl:sort | Specifies sort criteria for node lists selected by xsl:for-each or xsl:apply-templates.
xsl:strip-space | Strips white space from a document.
xsl:stylesheet | The document element of a style sheet, containing all other stylesheet elements.
xsl:template | Defines a reusable template for generating the desired output for nodes of a particular type and context.
xsl:text | Generates text in the output.
xsl:transform | Synonym for xsl:stylesheet
xsl:value-of | Inserts the value of the selected node as text.
xsl:variable | Specifies a value bound in an expression.
xsl:when | Provides multiple conditional testing in conjunction with the <xsl:choose> and <xsl:otherwise> elements.
xsl:with-param | Passes a parameter to a template.

* Microsoft proprietary extensions to support scripting

See Also

Browsing XML Documents in Internet Explorer 5


xsl:apply-templates Element

Directs the XSLT processor to find the appropriate template to apply, based on the type and context of each selected node.

<xsl:apply-templates
  select = expression
  mode = QName>
</xsl:apply-templates>

Attributes

select
Can be used to process nodes selected by an expression instead of processing all children. The value of the select attribute is an expression. The expression must evaluate to a node-set. The selected set of nodes is processed in document order unless a sorting specification is present.
mode
The mode attribute allows an element to be processed multiple times, each time producing a different result. If <xsl:template> does not have a match attribute, it must not have a mode attribute. If an <xsl:apply-templates> element has a mode attribute, it applies only to those template rules from <xsl:template> elements that have a mode attribute with the same value; if an <xsl:apply-templates> element does not have a mode attribute, it applies only to those template rules from <xsl:template> elements that do not have a mode attribute.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:attribute, xsl:comment, xsl:copy, xsl:element, xsl:for-each, xsl:if, xsl:otherwise, xsl:param, xsl:processing-instruction, xsl:template, xsl:variable, xsl:when, xsl:with-param, output elements
Child elements | xsl:sort, xsl:with-param

Remarks

The <xsl:apply-templates> element first selects a set of nodes using the query specified in the select attribute. If this attribute is left unspecified, all children of the current node are selected. For each of the selected nodes, <xsl:apply-templates> directs the XSLT processor to find an appropriate <xsl:template> to apply. Templates are tested for applicability by comparing the node to the XPath expression specified in the template's match attribute. If more than one template satisfies the match pattern, the one appearing with the highest priority is chosen. If several templates have the same priority, the last in the style sheet is chosen.

Example

The following style sheet formats customer data in XML into an HTML <TABLE> element, where each row represents a customer and the columns represent the customer's name, address, and phone number. The <xsl:sort> element sorts the customers by state, with all customers from a single state sorted by name.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
    <HTML>
      <BODY>
        <TABLE>
          <xsl:apply-templates select="customers/customer">
            <xsl:sort select="state"/>
            <xsl:sort select="name"/>
          </xsl:apply-templates>
        </TABLE>
      </BODY>
    </HTML>
  </xsl:template>
  <xsl:template match="customer">
     <TR>
        <xsl:apply-templates select="name" />
        <xsl:apply-templates select="address" />
        <xsl:apply-templates select="phone" />
        <xsl:apply-templates select="phone" mode="accountNumber"/>
      </TR>
  </xsl:template>
  <xsl:template match="name">
    <TD STYLE="font-size:14pt font-family:serif">
      <xsl:apply-templates />
    </TD>
  </xsl:template>
  <xsl:template match="address">
    <TD> <xsl:apply-templates /> </TD>
  </xsl:template>
  <xsl:template match="phone">
    <TD> <xsl:apply-templates /> </TD>
  </xsl:template>
  <xsl:template match="phone" mode="accountNumber">
    <TD STYLE="font-style:italic">
      1-<xsl:value-of select="."/>-001
    </TD>
  </xsl:template>
</xsl:stylesheet>

See Also

Handling Documents and Irregular Data


xsl:attribute Element

Creates an attribute node and attaches it to an output element.

<xsl:attribute
  name = "attribute-name"
  namespace = "uri-reference">
</xsl:attribute>

Attributes

name
Required. Name of the attribute to create. If this value is a Qname, the attribute node is created in the namespace currently bound to the prefix unless otherwise overridden by the presence of a namespace attribute. The value of the name attribute is interpreted as an attribute value template (expressions in curly braces are evaluated and converted to strings as in <xsl:value-of>). This allows the name of the attribute to be calculated or obtained from the source XML.
namespace
The namespace URI of the created attribute. If the name attribute contains a Qname, the prefix specified there will be bound to the namespace specified in the namespace attribute, possibly resulting in the addition of additional namespace declarations when serializing. This value is interpreted as an attribute value template.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:copy, xsl:element, xsl:for-each, xsl:if, xsl:otherwise, xsl:param, xsl:template, xsl:variable, xsl:when, xsl:with-param
Child elements | xsl:apply-templates, xsl:choose, xsl:copy, xsl:copy-of, xsl:for-each, xsl:if, xsl:if, xsl:text, xsl:variable

Remarks

The contents of this element specify the value of the attribute.

Attributes can be added or modified during transformation by placing the <xsl:attribute> element within elements that generate output, such as the <xsl:copy> element. Note that <xsl:attribute> can be used directly on output elements and not only in conjunction with <xsl:element>.

All attributes must be applied before children are added to the element.

Example

This example generates an attribute that obtains its value from the XML source. It generates output in the form <IMG src="value-from-XML-source"/>, using an XPath expression that retrieves the appropriate data from the XML source—here, "imagenames/imagename".

<IMG>
  <xsl:attribute name="src">
    <xsl:value-of select="imagenames/imagename" />
  </xsl:attribute>
</IMG>

This example copies the "myElement" element and adds a "copied" attribute with the value "true".

<xsl:template match="myElement">
  <xsl:copy>
    <xsl:attribute name="copied">true</xsl:attribute>
    <xsl:apply-templates />
  </xsl:copy>
</xsl:template>

The following samples demonstrate how to use attribute value templates to use content in the XML source document as the name of an attribute:

Attribute.xml

<?xml version="1.0"?>
<investment>
  <type>stock</type>
  <name>Microsoft</name>
  <price type="high">100</price>
  <price type="low">94</price>
</investment>

attribute.xsl

<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <xsl:output method="xml" indent="yes"/>
   <xsl:template match="investment">
      <xsl:element name="{type}">
        <xsl:attribute name="name" ><xsl:value-of sselect="name"/></xsl:attribute>
        <xsl:for-each select="price">
          <xsl:attribute name="{@type}" ><xsl:value-of sselect="."/></xsl:attribute>
        </xsl:for-each>
      </xsl:element>
   </xsl:template>
</xsl:stylesheet>

XSLT Output (viewed with the XSLT viewer):

<?xml version="1.0" encoding="UTF-16"?>
<stock name="Microsoft" high="100" low="94"/>

See Also

Accessing and Outputting Attributes | Generating More Sophisticated Output


xsl:choose Element

Provides multiple conditional testing in conjunction with the <xsl:otherwise> and <xsl:when> elements.

<xsl:choose>
</xsl:choose>

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:attribute, xsl:comment, xsl:copy, xsl:element, xsl:for-each, xsl:if, xsl:otherwise, xsl:param, xsl:processing-instruction, xsl:template, xsl:variable, xsl:when, xsl:with-param, output elements
Child elements | xsl:otherwise, xsl:when

Remarks

The <xsl:when> children of the <xsl:choose> element are tested in order from top to bottom until a test attribute on one of these elements accurately describes conditions present in the source data or an <xsl:otherwise> element is reached. Once an <xsl:when> or <xsl:otherwise> element is chosen, the <xsl:choose> block is exited. No explicit break or exit statement is required.

For simple conditional testing, use the <xsl:if> element.

Example

This example shows a template for "order" elements and inserts an <HR> or <BR> before the order's contents, based on the order's "total" element value. If the total is less than 10, a red <HR> will be generated; if the total is less than 20, a pink <HR> will be generated; otherwise a <BR> element will be created.

<xsl:template match="order">
   <xsl:choose>
      <xsl:when test="total &lt; 10">
         <HR STYLE="color:red"/>
      </xsl:when>
      <xsl:when test="total &lt; 20">
         <HR STYLE="color:pink"/>
      </xsl:when>
      <xsl:otherwise>
         <BR/>
      </xsl:otherwise>
   </xsl:choose>
   <xsl:apply-templates />
</xsl:template>

See Also

Conditional Templates


xsl:comment Element

Generates a comment in the output.

<xsl:comment>
</xsl:comment>

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:copy, xsl:element, xsl:for-each, xsl:if, xsl:otherwise, xsl:param, xsl:template, xsl:variable, xsl:when, xsl:with-param, output elements
Child elements | xsl:apply-templates, xsl:choose, xsl:copy, xsl:copy-of, xsl:for-each, xsl:if, xsl:text, xsl:value-of, xsl:variable

Remarks

The text generated by the children of <xsl:comment> appears between the starting characters <!-- and the closing characters -->.

Example

The following example transforms a source element, "notes", into a comment:

<xsl:template match="notes">
  <xsl:comment>
    <xsl:value-of />
  </xsl:comment>
</xsl:template>

See Also

Generating More Sophisticated Output


xsl:copy Element

Copies the current node from the source to the output.

<xsl:copy
  use-attribute-sets = QName>
</xsl:copy>

Attributes

use-attribute-sets
A whitespace-separated list of attribute sets specified as a qualified name. Specifying a use-attribute-sets attribute is equivalent to adding <xsl:attribute> elements for each of the attributes in each of the named attribute sets to the beginning of the content of the element with the use-attribute-sets attribute, in the same order in which the names of the attribute sets are specified in the use-attribute-sets attribute. It is an error if use of use-attribute-sets attributes on <xsl:attribute-set> elements causes an attribute set to directly or indirectly use itself.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:attribute, xsl:comment, xsl:copy, xsl:element, xsl:for-each, xsl:if, xsl:otherwise, xsl:param, xsl:processing-instruction, xsl:template, xsl:variable, xsl:when, xsl:with-param, output elements
Child elements | xsl:apply-templates, xsl:attribute, xsl:choose, xsl:comment, xsl:copy, xsl:copy-of, xsl:element, xsl:for-each, xsl:if, xsl:processing-instruction, xsl:text, xsl:value-of, xsl:variable, output elements

Remarks

The <xsl:copy> element creates a node in the output with the same name, namespace, and type as the current node. Attributes and children are not copied automatically. This element makes identity transformation possible.

Example

The following example performs an identity transform on the entire document. The identity transform copies each node in the source to the output to provide a logically equivalent tree. It does not yield character-by-character equivalence—entities will be expanded and white space not marked as significant may be removed.

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/ | @* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Any XML file can be used with the above transform:

See Also

Generating More Sophisticated Output


xsl:copy-of Element

Inserts sub-trees and result tree fragments into the result tree.

<xsl:copy-of
  select = expression/>

Attributes

select
Required. XPath expression identifying nodes to be copied. The subtree below each of these nodes is copied to the result tree in its entirety.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:attribute, xsl:comment, xsl:copy, xsl:element, xsl:for-each, xsl:if, xsl:otherwise, xsl:param, xsl:processing-instruction, xsl:template, xsl:variable, xsl:when, xsl:with-param, output elements
Child elements | (No child elements)

Remarks

When the result of evaluating the expression is a result tree fragment, the complete fragment is copied into the result tree. When the result is a node-set, all the nodes in the set are copied in document order into the result tree; copying an element node copies the attribute nodes, namespace nodes, and children of the element node as well as the element node itself. A root node is copied by copying its children. When the result is neither a node-set nor a result tree fragment, the result is converted to a string and then inserted into the result tree, as with <xsl:value-of>.

Example

Given this XML,

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="family.xsl"?>
<family>
  <person>
    <given-name age="10">
      <name>Fred</name>
      <nick-name>Freddy</nick-name>
    </given-name>
    <family-name>Smith</family-name>
  </person>
  …
</family>

the following transform finds a person element with "given-name" and "family-name" children elements. The paragraph will contain the first "given-name" child element of the current node, including any attributes and child elements, followed by a space and the first "family-name" child element, including any attributes and child elements, of the current node.

<xsl:template match="person">
  <p>
    <xsl:copy-of select="given-name"/>
    <xsl:text> </xsl:text>
    <xsl:copy-of select="family-name"/>
  </p>
</xsl:template>

The generated HTML is as follows:

<?xml version="1.0" encoding="UTF-16"?>
<p>
  <given-name age="10">
    <name>Fred</name>
    <nick-name>Freddy</nick-name>
  </given-name>
  <family-name>Smith</family-name>
</p>

See Also

xsl:value-of Element


xsl:element Element

Creates an element with the specified name in the output.

<xsl:element
  name = "element-name"
  namespace = "uri-reference"
  use-attribute-sets = QName
</xsl:element>

Attributes

name
Required. Name of the element to create. If this value is a Qname, the element node is created in the namespace currently bound to the prefix unless otherwise overridden by the presence of a namespace attribute. The value of the name attribute is interpreted as an attribute value template (expressions in curly braces are evaluated and converted to strings as in <xsl:value-of>). This allows the name of the element to be calculated or obtained from the source XML.
namespace
The namespace URI of the created element. If the name element contains a Qname, the prefix specified there will be bound to the namespace specified in the namespace attribute, possibly resulting in the addition of additional namespace declarations when serializing. This value is interpreted as an attribute value template.
use-attribute-sets
A whitespace-separated list of attribute sets specified as a qualified name. Specifying a use-attribute-sets attribute is equivalent to adding <xsl:attribute> elements for each of the attributes in each of the named attribute sets to the beginning of the content of the element with the use-attribute-sets attribute, in the same order in which the names of the attribute sets are specified in the use-attribute-sets attribute. It is an error if use of use-attribute-sets attributes on <xsl:attribute-set> elements causes an attribute set to directly or indirectly use itself.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:copy, xsl:element, xsl:for-each, xsl:if, xsl:otherwise, xsl:param, xsl:template, xsl:variable, xsl:when, xsl:with-param, output elements
Child elements | xsl:apply-templates, xsl:attribute, xsl:choose, xsl:comment, xsl:copy, xsl:copy-of, xsl:element, xsl:for-each, xsl:if, xsl:processing-instruction, xsl:text, xsl:value-of, xsl:variable, output elements

Remarks

The <xsl:element> element allows an element to be created with a computed name. The name of the element to be created is specified by a required name attribute and an optional namespace attribute. The content of the <xsl:element> element is a template for the attributes and children of the created element.

This element provides an escaping mechanism for creating elements with namespace clashes, such as XSLT itself.

Examples

This example shows a template that generates this element in the output:

<xsl:template match="cost" xml:space="preserve">

The <xsl:element> element is necessary because XSLT elements cannot be used as output elements.

<xsl:template match="item">
  <xsl:element name="xsl:template">
    <xsl:attribute name="match">cost</xsl:attribute>
    <xsl:attribute name="xml:space">preserve</xsl:attribute>
    <xsl:apply-templates/>
  </xsl:element>

</xsl:template>

See Also

Generating More Sophisticated Output


xsl:for-each Element

Applies a template repeatedly, applying it in turn to each node in a set.

<xsl:for-each
  select = expression>
</xsl:for-each>

Attributes

select
Required. Expression evaluated on the current context to determine the set of nodes to iterate over.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:attribute, xsl:comment, xsl:copy, xsl:element, xsl:for-each, xsl:if, xsl:otherwise, xsl:param, xsl:processing-instruction, xsl:template, xsl:variable, xsl:when, xsl:with-param, output elements
Child elements | xsl:apply-templates, xsl:attribute, xsl:choose, xsl:comment, xsl:copy, xsl:copy-of, xsl:element, xsl:for-each, xsl:if, xsl:processing-instruction, xsl:sort, xsl:text, xsl:value-of, xsl:variable, output elements

Remarks

The <xsl:for-each> element establishes the context for iteration; the XSLT transformation instructions within this loop are to be applied to the selected nodes. Each source element selected by <xsl:for-each> becomes a new context against which any pattern matching within the <xsl:for-each> occurs.

Example

This example specifies a template that defines what the structure of the overall output document should be (a top-level HTML element containing <BODY> and <TABLE> elements with repeated rows for each customer) and uses templates to create <TD> elements for the name, address, and phone source elements.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
    <HTML>
      <BODY>
        <TABLE>
          <xsl:for-each select="customers/customer">
            <xsl:sort select="state" order="descending"/>
            <xsl:sort select="name"/>
            <TR>
              <TD><xsl:value-of select="name" /></TD>
              <TD><xsl:value-of select="address" /></TD>
              <TD><xsl:value-of select="phone" /></TD>
            </TR>
          </xsl:for-each>
        </TABLE>
      </BODY>
    </HTML>
  </xsl:template>
</xsl:stylesheet>

See Also

Creating and Populating an HTML Template


xsl:if Element

Allows simple conditional template fragments.

<xsl:if
  test = boolean expression>
</xsl:if>

Attributes

test
Required. The condition in the source data to test. If the expression in this attribute evaluates to true when cast to a boolean, the contents of <xsl:if> are placed in the output. Node sets are cast to a boolean true if they contain at least one node.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:attribute, xsl:comment, xsl:copy, xsl:element, xsl:for-each, xsl:if, xsl:otherwise, xsl:param, xsl:processing-instruction, xsl:template, xsl:variable, xsl:when, xsl:with-param, output elements
Child elements | xsl:apply-templates, xsl:attribute, xsl:choose, xsl:comment, xsl:copy, xsl:copy-of, xsl:element, xsl:for-each, xsl:if, xsl:processing-instruction, xsl:text, xsl:value-of, xsl:variable, output elements

Remarks

The content is a template. The expression is evaluated, and the resulting object is converted to a Boolean. If the result is True, the content template is instantiated; otherwise, nothing is created.

Examples

In the following example, the names in a group of names are formatted as a comma-separated list:

<xsl:template match="namelist/name">
  <xsl:apply-templates/>
  <xsl:if test="position()!=last()">, </xsl:if>
</xsl:template>

Another way to accomplish this is by reversing the logic to do a check if this name is the first. In some circumstances this performs better than the above example since last() requires that the entire set of names be found and counted, while this one does not.

<xsl:template match="namelist/name">
  <xsl:if test="position()!=1">, </xsl:if>
  <xsl:apply-templates/>
</xsl:template>

The following colors every other table row yellow:

<xsl:template match="item">
  <tr>
    <xsl:if test="position() mod 2 = 0">
       <xsl:attribute name="bgcolor">yellow</xsl:attribute>
    </xsl:if>
    <xsl:apply-templates/>
  </tr>
</xsl:template>

This example inserts the text "International Stock" when the "stock" element has an "international" attribute.

<xsl:template match="stock">
  <xsl:if test="@international">International Stock</xsl:if>
  <xsl:apply-templates />
</xsl:template>

See Also

Conditional Templates


xsl:include Element

Specifies an XSLT style sheet to include.

<xsl:include
  href = "URI reference"/>

Attributes

href
Required. A URI reference identifying the style sheet to be included.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:stylesheet, xsl:transform
Child elements | (No child elements)

Remarks

An XSLT style sheet can include another XSLT style sheet using the <xsl:include> element. The href attribute value is a URI reference identifying the style sheet to be included. The relative URI is resolved with relation to the base URI of the <xsl:include> element.

The <xsl:include> element is only allowed as the child of the <xsl:stylesheet> element.

Inclusions are processed in MSXML as if they occurred at the tree level, Note that this is not the same as the DOM view of the XML tree. The resource located by the href attribute value is parsed as an XML document, and the children of the <xsl:stylesheet> element in this document replace the <xsl:include> element in the including document. The fact that template rules or definitions are included does not affect the way they are processed.

An error occurs if a style sheet directly or indirectly includes itself.

Including a style sheet multiple times can cause errors because of duplicate definitions. Such multiple inclusions are less obvious when they are indirect. For example, if style sheet B includes style sheet A, style sheet C includes style sheet A, and style sheet D includes both style sheet B and style sheet C, then A will be included indirectly by D twice. If all of B, C, and D is used as independent style sheets, the error can be avoided by separating everything in B other than the inclusion of A into a separate style sheet B1 and changing B to contain just inclusions of B1 and A, doing the same for C, and then changing D to include A, B1, C1.

Example

The following sample demonstrates usage of the <xsl:include> element:

The XML file

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="xslinclude.xsl"?>
<COLLECTION>
   <BOOK>
      <TITLE>Cosmos</TITLE>
      <AUTHOR>Carl Sagan</AUTHOR>
      <PUBLISHER>Ballantine Books</PUBLISHER>
   </BOOK>
   <BOOK>
      <TITLE>Catwings</TITLE>
      <AUTHOR>Ursula K. Le Guin</AUTHOR>
      <PUBLISHER>Scholastic</PUBLISHER>
   </BOOK>
   <BOOK>
      <TITLE>Home Town</TITLE>
      <AUTHOR>Tracy Kidder</AUTHOR>
      <PUBLISHER>Random House</PUBLISHER>
   </BOOK>
</COLLECTION>

xslinclude.xsl

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xsl:space="preserve">
   <xsl:output method="xml" indent="yes"/>
   <xsl:template match="/">
      <xsl:for-each select="COLLECTION/BOOK">
         <xsl:apply-templates select="TITLE"/>
         <xsl:apply-templates select="AUTHOR"/>
         <xsl:apply-templates select="PUBLISHER"/>
         <BR/>  <!-- add this -->
      </xsl:for-each>
   </xsl:template>
   <xsl:include href="xslincludefile.xsl" />
</xsl:stylesheet>

xslincludefile.xsl

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xsl:space="preserve">
   <xsl:template match="TITLE">
      Title - <xsl:value-of /><BR/>
   </xsl:template>
   <xsl:template match="AUTHOR">
      Author - <xsl:value-of /><BR/>
   </xsl:template>
   <xsl:template match="PUBLISHER">
      Publisher - <xsl:value-of /><BR/><!-- removed second <BR/> -->
   </xsl:template>
</xsl:stylesheet>

Output

Title - Cosmos
Author - Carl Sagan
Publisher - Ballantine Books

Title - Catwings
Author - Ursula K. Le Guin
Publisher - Scholastic

Title - Home Town
Author - Tracy Kidder
Publisher - Random House


xsl:number Element

Inserts a formatted number into the result tree.

<xsl:number
  level = "single" | "multiple" | "any"
  count = pattern
  from = pattern
  value = number-expression
  format = { string }
  lang = { nmtoken }
  letter-value = { "alphabetic" | "traditional" }
  grouping-separator = { char }
  grouping-size = { number } />

Attributes

level
Specifies what levels of the source tree should be considered; it has the values single, multiple or any. The default is single.
count
A pattern that specifies what nodes should be counted at those levels. If the count attribute is not specified, then it defaults to the pattern that matches any node with the same node type as the current node and, if the current node has an expanded-name, with the same expanded-name as the current node.
from
A pattern that specifies where counting starts.
value
Specifies the expression to be converted to a number and output to the result tree. If no value attribute is specified, then the <xsl:number> element inserts a number based on the position of the current node in the source tree.
format
A sequence of tokens that specifies the format to be used for each number in the list. If there are no format tokens, then the default value of 1 is used which generates a sequence 1 2 … 10 11 12…. Each number after the first is separated from the preceding number by the separator token preceding the format token used to format that number. If there are no separator tokens, a period character is used ".".
Format token | Sequence generated
1 | 1 2 3 4 5 … 10 11 12 …
01 | 01 02 03 … 19 10 11 … 99 100 101…
A | A B C … Z AA AB AC…
i | i ii iii iv v vi vii viii ix x…
I | I II III IV V VI VII VIII IX X…
lang
Specifies which language's alphabet is used. If no lang value is specified, the language is determined from the system environment.
letter-value
Disambiguates between numbering sequences that use letters. One numbering sequence assigns numeric values to letters in alphabetic sequence, and the other assigns numeric values to each letter in some other manner traditional in that language. In English, these correspond to the numbering sequences specified by the format tokens "a" and "i". In some languages, the first member of each sequence is the same, and so the format token alone would be ambiguous. A value of "alphabetic" specifies the alphabetic sequence; a value of "traditional" specifies the other sequence. Default is "alphabetic."
grouping-separator
Gives the separator used as a grouping (e.g. thousands) separator in decimal numbering sequences. For example, grouping-separator="," and grouping-size="3" would produce numbers of the form 1,000,000. If only one of the grouping-separator and grouping-size attributes is specified, then it is ignored.
grouping-size
Specifies the size (normally 3) of the grouping. For example, grouping-separator="," and grouping-size="3" would produce numbers of the form 1,000,000. If only one of the grouping-separator and grouping-size attributes is specified, then it is ignored.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:copy, xsl:element, xsl:for-each, xsl:if, xsl:otherwise, xsl:param, xsl:template, xsl:variable, xsl:when, xsl:with-param, output elements
Child elements | (No child elements)

Remarks

The number to be inserted is specified by an expression contained in the value attribute. The expression is evaluated and the resulting object is converted to a number. The number is rounded to an integer and then converted to a string and inserted in the result tree.

The <xsl:number> element first constructs a list of positive integers using the level, count and from attributes:

  • When level="single", it goes up to the first node in the ancestor-or-self axis that matches the count pattern, and constructs a list of length one containing one plus the number of preceding siblings of that ancestor that match the count pattern. If there is no such ancestor, it constructs an empty list. If the from attribute is specified, then the only ancestors that are searched are those that are descendants of the nearest ancestor that matches the from pattern. Preceding-siblings has the same meaning here as with the preceding-sibling axis.
  • When level="multiple", it constructs a list of all ancestors of the current node in document order followed by the element itself; it then selects from the list those nodes that match the count pattern; it then maps each node in the list to one plus the number of preceding siblings of that node that match the count pattern. If the from attribute is specified, then the only ancestors that are searched are those that are descendants of the nearest ancestor that matches the from pattern. Preceding siblings has the same meaning here as with the preceding-sibling axis.
  • When level="any", it constructs a list of length one containing the number of nodes that match the count pattern and belong to the set containing the current node and all nodes at any level of the document that are before the current node in document order, excluding any namespace and attribute nodes (in other words the union of the members of the preceding and ancestor-or-self axes). If the from attribute is specified, then only nodes after the first node before the current node that match the from pattern are considered.

The following languages/numbering schemes are supported. "Format Token" corresponds to the format attribute, "Language" corresponds to the "lang" attribute, and "Letter-Value" corresponds to the letter-value attribute.

Description | Format Token | Language | Letter-Value
Western | 0x0031 (1) | n/a | n/a
Upper-case letter | 0x0041 (A) | n/a | n/a
Lower-case letter | 0x0061 (a) | n/a | n/a
Upper-case Roman | 0x0049 (I) | n/a | n/a
Lower-case Roman | 0x0069 (i) | n/a | n/a
Upper-case Russian (Cyrillic) | 0x0410 | n/a | n/a
Lower-case Russian (Cyrillic) | 0x0430 | n/a | n/a
Hebrew Alphabetic | 0x05d0 | n/a | Alphabetic
Hebrew Traditional | 0x05d0 | n/a | Traditional
Arabic | 0x0623 | n/a | n/a
Hindi Consonants | 0x0905 | n/a | n/a
Hindi Vowels | 0x0915 | n/a | n/a
Hindi Numbers | 0x0967 | n/a | n/a
Thai Letters | 0x0e01 | n/a | n/a
Thai Numbers | 0x0e51 | n/a | n/a
Japanese Aiueo (double-byte) | 0x30a2 | n/a | n/a
Japanese Iroha (double-byte) | 0x30a4 | n/a | n/a
Korean Chosung | 0x3131 | n/a | n/a
Taiwanese Decimal | 0x4e01 | "zh-tw" | n/a
Korean Decimal | 0x4e01 | "ko" | n/a
Far East Decimal | 0x4e01 | any other lang | n/a
Far East Kanji | 0x58f1 | n/a | n/a
Traditional Taiwanese | 0x58f9 | "zh-tw" | n/a
Traditional Chinese | 0x58f9 | any other lang | n/a
Chinese "Zodiac" 12 | 0x5b50 | n/a | n/a
Chinese "Zodiac" 10 | 0x7532 | n/a | n/a
Chinese "Zodiac" 60 | 0x7532, 0x5b50 | n/a | n/a
Korean Ganada | 0xac00 | n/a | n/a
Korean Decimal | 0xc77c | n/a | n/a
Korean 99 | 0xd558 | n/a | n/a
Western (double-byte) | 0xff11 | n/a | n/a
Japanese Aiueo (single-byte) | 0xff71 | n/a | n/a
Japanese Iroha (single-byte) | 0xff72 | n/a | n/a

If the format token alone is enough to disambiguate a particular numbering scheme, there is no need to specify the language or letter-value.

Example

The following sample demonstrates usage of the <xsl:number> element:

<xsl:template match="items">

<xsl:for-each select="item">

<xsl:sort select="."/>

<p>

<xsl:number value="position()" format="1. "/>

<xsl:value-of select="."/>

</p>

</xsl:for-each>

</xsl:template>

Here are some examples of conversion specifications:

format="&#x30A2;" specifies Katakana numbering

format="&#x30A4;" specifies Katakana numbering in the "iroha" order

format="&#x0E51;" specifies numbering with Thai digits

format="&#x05D0;" letter-value="traditional" specifies "traditional" Hebrew numbering

format="&#x10D0;" letter-value="traditional" specifies Georgian numbering

format="&#x03B1;" letter-value="traditional" specifies "classical" Greek numbering

format="&#x0430;" letter-value="traditional" specifies Old Slavic numbering


xsl:otherwise Element

Provides multiple conditional testing in conjunction with the <xsl:choose> and <xsl:when> elements.

<xsl:otherwise>
</xsl:otherwise>

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:choose
Child elements | xsl:apply-templates, xsl:attribute, xsl:choose, xsl:comment, xsl:copy, xsl:copy-of, xsl:element, xsl:for-each, xsl:if, xsl:processing-instruction, xsl:text, xsl:value-of, xsl:variable, output elements

Remarks

Provides a default condition for <xsl:choose>. Other alternatives are indicated by <xsl:when> elements.

For simple conditional testing, use the <xsl:if> element.

Example

This example shows a template for "order" elements and inserts an <HR> or <BR> before the order's contents, based on the order's "total" element value. If the total is less than 10, a red <HR> will be generated; if the total is less than 20, a pink <HR> will be generated; otherwise a <BR> element will be created.

<xsl:template match="order">
   <xsl:choose>
      <xsl:when test="total &lt; 10">
         <HR STYLE="color:red"/>
      </xsl:when>
      <xsl:when test="total &lt; 20">
         <HR STYLE="color:pink"/>
      </xsl:when>
      <xsl:otherwise>
         <BR/>
      </xsl:otherwise>
   </xsl:choose>
   <xsl:apply-templates />
</xsl:template>

See Also

Conditional Templates


xsl:output Element

Specifies options for use in serializing the result tree.

<xsl:output
  method = "xml"
  version = nmtoken
  encoding = string
  omit-xml-declaration = "yes" | "no"
  standalone = "yes" | "no"
  doctype-public = string
  doctype-system = string
  cdata-section-elements = qnames
  indent = "yes" | "no"
  media-type = string
  />

Attributes

method
Identifies the overall method used for outputting the result tree.
version
Specifies version "1.0" in relation to the "xml" output method; this appears in the output XML declaration as <?xml version="1.0"?>.
encoding
Specifies the preferred character encoding that the parser should use to encode sequences of characters as sequences of bytes. The value of the attribute is treated case-insensitively; it must contain only printable ASCII characters and be a registered character set, or begin with x-.
omit-xml-declaration
Specifies whether the XSLT processor should output an XML declaration; the value must be yes or no.
standalone
Specifies whether the XSLT processor should output a standalone document declaration; the value must be yes or no.
doctype-public
Specifies the public identifier to be used in the DTD.
doctype-system
Specifies the system identifier to be used in the DTD.
cdata-section-elements
Specifies a list of the names of elements whose text node children should be output using CDATA sections.
indent
Specifies additional whitespace to add when outputting the result tree; the value must be yes or no.
media-type
Specifies the media type (MIME content type) of the data that results from outputting the result tree.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:stylesheet, xsl:transform
Child elements | (No child elements)

Remarks

A style sheet may contain multiple <xsl:output> elements and may include style sheets that also contain <xsl:output> elements. All the <xsl:output> elements occurring in a style sheet are merged into a single effective <xsl:output> element. It is an error if there is more than one such value for an attribute. The values of attributes are defaulted after the <xsl:output> elements have been merged; different output methods may have different default values for an attribute.


xsl:param Element

Declares a named parameter for use within an xsl:stylesheet or xsl:template. Allows specification of a default value.

<xsl:param
  name = Qname
  select = expression>
</xsl:param>

Attributes

name
Required. Specifies the name of the parameter.
select
The value of the attribute is an expression, and the value of the variable is the object that results from evaluating the expression. When this attribute is specified, the content of <xsl:param> must be empty.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:stylesheet, xsl:template, xsl:transform
Child elements | xsl:apply-templates, xsl:attribute, xsl:choose, xsl:comment, xsl:copy, xsl:copy-of, xsl:element, xsl:for-each, xsl:if, xsl:processing-instruction, xsl:text, xsl:value-of, xsl:variable, output elements

Remarks

The value specified on the <xsl:param> element is a default value for binding. When the template or style sheet containing <xsl:param> is invoked, parameters are passed that are used in place of the default values.

The value of the parameter can be an object of any type that can be returned by an expression. The <xsl:param> element can specify the value of the variable in three alternative ways:

  • If the element has a select attribute, the value of the attribute must be an expression and the value of the parameter is the object that results from evaluating the expression. In this case, the content of the element must be empty.
  • If the element does not have a select attribute and has non-empty content such as one or more child nodes, the content specifies the value. The content is a template that is instantiated to give the value of the parameter. The value is a result tree fragment equivalent to a node-set containing just a single root node having as children the sequence of nodes produced by instantiating the template. The base URI of the nodes in the result tree fragment is the base URI of the element.

    An error occurs if a member of the sequence of nodes created by instantiating the template is an attribute node or a namespace node, because a root node cannot have an attribute node or a namespace node as a child.

  • If the content is empty and does not have a select attribute, the value of the parameter is an empty string. Thus
    <xsl:param name="x"/>

    is equivalent to

    <xsl:param name="x" select="''"/>

    Note When a parameter is used to select nodes by position, be careful not to do the following:

    <xsl:param name="n">2</xsl:param>
...
<xsl:value-of select="item[$n]"/>

    This will output the value of the first item element, because the variable "n" will be bound to a result tree fragment, not a number. Instead, do either

    <xsl:param name="n" select="2"/>
...
<xsl:value-of select="item[$n]"/>

    or

    <xsl:param name="n">2</xsl:param>
...
<xsl:value-of select="item[number($n)]"/>

    Note One convenient way to specify the empty node-set as the default value of a parameter is:

    <xsl:param name="x" select="/.."/>

See Also

xsl:variable


xsl:preserve-space Element

Preverves whitespace in a document.

<xsl:strip-space
  elements = tokens />

Attributes

elements
Required. White-space separated list of name tests.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:stylesheet, xsl:transform
Child elements | (No child elements)

Remarks

<xsl:preserve-space> is a whitespace preserving element and has an elements attribute whose value is a whitespace-separated list of name tests. Initially, the set of whitespace-preserving element names contains all element names. If an element name matches a name test in an <xsl:strip-space> element, then it is removed from the set of whitespace-preserving element names. If an element name matches a name test in an <xsl:preserve-space> element, then it is added to the set of whitespace-preserving element names. An element matches a name test if and only if the name test would be true for the element as an XPath node test. The applicable match for a particular element name is determined as follows:

  • First, any match with lower import precedence than another match is ignored.
  • Next, any match with a name test that has a lower default priority than the default priority of the name test of another match is ignored.
  • It is an error if this leaves more than one match.

See Also

xsl:strip-space


xsl:processing-instruction Element

Generates a processing instruction in the output.

<xsl:processing-instruction
  name = "pi-name">
</xsl: processing-instruction>

Attributes

name
Required. Name of the processing instruction.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:attribute, xsl:comment, xsl:copy, xsl:element, xsl:for-each, xsl:if, xsl:otherwise, xsl:param, xsl:processing-instruction, xsl:template, xsl:variable, xsl:when, xsl:with-param, output elements
Child elements | xsl:apply-templates, xsl:choose, xsl:copy, xsl:copy-of, xsl:for-each, xsl:if, xsl:text, xsl:value-of, xsl:variable, output elements

Remarks

The <xsl:processing-instruction> element generates a processing instruction (PI) in the output. The name is indicated by the name attribute. The content of the element provides the rest of the PI.

Example

This example shows a template that generates the XML declaration and a style sheet processing instruction in the output.

<xsl:template match="/">
  <xsl:text disable-output-escaping="yes">&#60;&#63;xml version="1.0"&#63;&#62;</xsl:text>
  <xsl:apply-templates />
</xsl:template>

See Also

Generating More Sophisticated Output


msxsl:script Element

Appears at the top level of the style sheet and contains script blocks that define global variables and functions for script extensions.

<msxsl:script
  language = "language-name"
  implements-prefix = "prefix of user's namespace">
</msxsl:script>

Attributes

language
Active Scripting language used for the functions defined within this element. If left unspecified, the current scripting language is used, as specified by a language attribute on an ancestor. If no such attribute exists, JScript® (compatible with ECMA 262 language specification) is used. This attribute accepts the same values as the language attribute on the HTML <SCRIPT> element.
implements-prefix
Required. Declares a namespace and associates it with the script block. The value of this attribute is the prefix which represents the namespace.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:stylesheet, xsl:transform
Child elements | (No child elements)

Remarks

Belongs to the namespace urn:schemas-microsoft-com:xslt. You can declare variables and define functions within the <msxsl:script> element. This can appear within the <xsl:stylesheet> element. A script block thus nested is treated as a global script block.

You can also instantiate COM objects in the <msxsl:script> element. However, a user's security settings may prevent your script from instantiating a client-side object.

Example

This example creates a script block with a namepace prefix of "user" that contains a function called "xml" that takes a node-list as an argument. Later, this function is called from the select attribute of <xsl:value-of>.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                xmlns:user="http://mycompany.com/mynamespace"
                version="1.0">
  <msxsl:script language="JScript" implements-prefix="user">
    function xml(nodelist) {
      return nodelist.nextNode().xml;
    }
  </msxsl:script>

  <xsl:template match="/">
    <xsl:value-of select="user:xml(.)"/>
  </xsl:template>
</xsl:stylesheet>

See Also

xsl:value-of Element


xsl:sort Element

Specifies sort criteria for node lists selected by xsl:for-each or xsl:apply-templates.

<xsl:sort
  select = string-expression
  data-type = { "text" | "number" | Qname }
  order = { "ascending" | "descending" }
  />

Attributes

select
An expression that is evaluated with that node as the current node and with the complete list of nodes being processed in unsorted order as the current node-list. The resulting object is converted to a string that is used as the sort key for that node. The default value of the select attribute is ".", which will cause the string-value of the current node to be used as the sort key.
data-type
Specifies the data type of the strings; the following values are allowed:
"text" | Specifies that the sort keys should be sorted alphabetically.
number | Specifies that the sort keys should be converted to numbers and then sorted according to the numeric value. The sort key is converted to a number.
QName | Expanded into a dt-expanded-name. The expanded-name identifies the data type.

If no data type is specified, the type of the expression will be used as the default.

order
Specifies whether the strings will be sorted in ascending or descending order. The default is ascending.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:apply-templates, xsl:for-each
Child elements | (No child elements)

Remarks

Inherent data types (from XML Schema) are supported in the following manner:

  • If there is a data-type attribute on the <xsl:sort> element, this data type will be used in performing the comparison.
  • Otherwise, if one of the items being compared has a data type, this data type will be used in performing the comparison.
  • Otherwise, the items will be compared as strings.

Example

For example, suppose an employee database has the form

<employees>
  <employee>
    <name>
      <given>James</given>
      <family>Clark</family>
    </name>
    ...
  </employee>
</employees>

Then a list of employees sorted by name could be generated using:

<xsl:template match="employees">
  <ul>
    <xsl:apply-templates select="employee">
      <xsl:sort select="name/family"/>
      <xsl:sort select="name/given"/>
    </xsl:apply-templates>
  </ul>
</xsl:template>
<xsl:template match="employee">
  <li>
    <xsl:value-of select="name/given"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="name/family"/>
  </li>
</xsl:template>

See Also

Sorting XML


xsl:strip-space Element

Strips whitespace in a document.

<xsl:strip-space
  elements = tokens />

Attributes

elements
Required. White-space separated list of name tests.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:stylesheet, xsl:transform
Child elements | (No child elements)

Remarks

<xsl:strip-space> is a whitespace preserving element and has an elements attribute whose value is a whitespace-separated list of name tests. Initially, the set of whitespace-preserving element names contains all element names. If an element name matches a name test in an <xsl:strip-space> element, then it is removed from the set of whitespace-preserving element names. If an element name matches a name test in an <xsl:preserve-space> element, then it is added to the set of whitespace-preserving element names. An element matches a name test if and only if the name test would be true for the element as an XPath node test. The applicable match for a particular element name is determined as follows:

  • First, any match with lower import precedence than another match is ignored.
  • Next, any match with a name test that has a lower default priority than the default priority of the name test of another match is ignored.
  • It is an error if this leaves more than one match.

See Also

xsl:preserve-space


xsl:stylesheet Element

The document element of a style sheet, containing all other stylesheet elements.

<xsl:stylesheet
  id = "id"
  extension-element-prefixes = "prefixes"
  exclude-result-prefixes = "prefixes"
  version = "number">
</xsl:stylesheet>

Attributes

version
Required. Indicates the version of XSLT the style sheet requires. Value should be set to 1.0 for this version of XSLT.
id
Specifies a unique identifier to facilitate embedding stylesheets.
extension-element-prefixes
Designates a namespace as an extension namespace. The value is a white-space separated list of namespace prefixes. The namespace bound to each of the prefixes is designated as an extension namespace. The default namespace (as declared by xmlns) may be designated as an extension namespace by including #default in the list of namespace prefixes. The designation of a namespace as an extension namespace is effective within the subtree of the stylesheet rooted at the element bearing the extension-element-prefixes; a subtree rooted at an xsl:stylesheet element does not include any stylesheets imported or included by children of that <xsl:stylesheet> element.
exclude-result-prefixes
Designates a namespace URI as an excluded namespace. The value is a white-space separated list of namespace prefixes. The namespace bound to each of the prefixes is designated as an excluded namespace. The default namespace (as declared by xmlns) may be designated as an excluded namespace by including #default in the list of namespace prefixes. The designation of a namespace as an excluded namespace is effective within the subtree of the stylesheet rooted at the element bearing the exclude-result-prefixes; a subtree rooted at an xsl:stylesheet element does not include any stylesheets imported or included by children of that <xsl:stylesheet> element.

Element Information

Number of occurrences | One
Parent elements | (No parent elements)
Child elements | xsl:include, xsl:output, xsl:param, xsl:template, xsl:variable,msxsl:script

Remarks

A style sheet contains the <xsl:stylesheet> element. This element can have a set of <xsl:template> elements representing different output templates. Processing begins by processing the root template, indicated by the pattern "/".

Example

This example shows a complete XSLT style sheet that contains a set of templates. The root template (match="/") defines the structure of the overall output document, and the other templates define the structure of the name, address, and phone elements.

<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
    <HTML>
      <BODY>
        <TABLE>
          <xsl:for-each select="customers/customer">
            <TR>
              <xsl:apply-templates select="name" />
              <xsl:apply-templates select="address" />
              <xsl:apply-templates select="phone" />
            </TR>
          </xsl:for-each>
        </TABLE>
      </BODY>
    </HTML>
  </xsl:template>
  <xsl:template match="name">
    <TD STYLE="font-size:14pt font-family:serif">
      <xsl:apply-templates />
    </TD>
  </xsl:template>
  <xsl:template match="address">
    <TD> <xsl:apply-templates /> </TD>
  </xsl:template>
  <xsl:template match="phone">
    <TD> <xsl:apply-templates /> </TD>
  </xsl:template>
  <xsl:template match="text()"><xsl:value-of /></xsl:template>
</xsl:stylesheet>


xsl:template Element

Defines a reuseable template for generating the desired output for nodes of a particular type and context.

<xsl:template
  name= Qname
  match = pattern
  priority = "number"
  mode = Qname
</xsl:template>

Attributes

name
The value of the name attribute is a Qname that is expanded. If it has a prefix, the prefix is expanded into a URI reference using the namespace declarations in effect on the attribute in which the name occurs. The expanded-name, consisting of the local part of the name and the possibly null URI reference is used as the name of the template. The default namespace is not used for unprefixed names. If an <xsl:template> element has a name attribute, it may, but need not, also have a match attribute.
match
A pattern that identifies the source node or nodes to which the rule applies. The match attribute is required unless the <xsl:template> element has a name attribute. The content of the <xsl:template> element is the template that is instantiated when the template rule is applied.
priority
All matching template rules that have lower priority than the matching template rule or rules with the highest priority are eliminated from consideration. The value of this must be a real number from 0–9, positive or negative, matching the production number with an optional leading minus sign (-). The default priority is computed as follows:
  • If the pattern contains multiple alternatives separated by |, it is treated equivalently to a set of template rules, one for each alternative.
  • If the pattern has the form of a Qname preceded by a child or attribute axis specifier or has the form processing-instruction literal preceded by a child or attribute axis specifier, the priority is 0.
  • If the pattern is a name preceded by a child or attribute axis specifier, the priority is -0.25.
  • Otherwise, if the pattern consists of just a node test preceded by a child or attribute axis specifier, the priority is -0.5.
  • Otherwise, the priority is 0.5.

Thus the most common kind of pattern (a pattern that tests for a node with a particular type and a particular expanded-name) has priority 0. The next less specific kind of pattern (a pattern that tests for a node with a particular type and an expanded-name with a particular namespace URI) has priority -0.25. Patterns less specific than this (patterns that just test for nodes with particular types) have priority -0.5. Patterns more specific than the most common kind of pattern have priority 0.5.

mode
The mode attribute allows an element to be processed multiple times, each time producing a different result. If <xsl:template> does not have a match attribute, it must not have a mode attribute. If an <xsl:apply-templates> element has a mode attribute, it applies only to those template rules from <xsl:template> elements that have a mode attribute with the same value; if an <xsl:apply-templates> element does not have a mode attribute, it applies only to those template rules from <xsl:template> elements that do not have a mode attribute.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:stylesheet, xsl:transform
Child elements | xsl:apply-templates, xsl:attribute, xsl:choose, xsl:comment, xsl:copy, xsl:copy-of, xsl:element, xsl:for-each, xsl:if, xsl:param, xsl:processing-instruction, xsl:text, xsl:value-of, xsl:variable, output elements

Remarks

Note that the template need not generate a complete XML document (even the root template, unless using transformNodeToObject), but only a fragment of XML. It is possible to include unenclosed text or multiple document elements defined by the template. This facilitates the generation of raw text and XML fragments that can be further processed by an application (for example, HTML fragments inserted into an HTML page).

The value of the name attribute is a Qname that is expanded. If it has a prefix, it is expanded into a URI reference using the namespace declarations in effect on the attribute in which the name occurs. The expanded-name consisting of the local part of the name and the possibly null URI reference is used as the name of the template. The default namespace is not used for unprefixed names.

An error occurs if a style sheet contains more than one template with the same name.

Example

The following template (in this case defined by the <xsl:template> element) has a pattern that identifies elements of type "stock" and produces an output <DIV> element with the attribute STYLE="font-weight:bold":

<xsl:template match="stock">
  <DIV STYLE="font-weight:bold">
    Symbol: <xsl:value-of match="symbol" />, Price: <xsl:value-of match="price" />
  </DIV>
</xsl:template>


xsl:text Element

Generates text in the output.

<xsl:text
  disable-output-escaping = "yes" | "no">
</xsl:text>

Attributes

disable-output-escaping
Default is "no". If the value is "yes", a text node generated by instantiating the <xsl:text> element will be output without any escaping. For example,
<xsl:text disable-output-escaping="yes">&lt;</xsl:text>

generates the single character "<".

Note disable-output-escaping="yes" can be used to generate non-well-formed documents, and thus should be used with caution, since non-well-formed output may generate errors in certain circumstances. For instance, transformNodeToObject to an XML Document requires that the result be well-formed and thus may not complete if disable-output-escaping has affected the well-formedness of the document. We recommend considering disable-output-escaping="yes" as an advanced feature to be used only when the potential dangers are understood.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:attribute, xsl:comment, xsl:copy, xsl:element, xsl:for-each, xsl:if, xsl:otherwise, xsl:param, xsl:processing-instruction, xsl:template, xsl:variable, xsl:when, xsl:with-param, output elements
Child elements | (No child elements)

Remarks

A text node is created for each <xsl:text> element appearing in a template. White space is stripped, and adjacent text nodes are automatically merged.

See Also

xsl:comment


xsl:transform Element

The document element of a style sheet, containing <xsl:template> and <msxsl:script> elements. Synonym for xsl:stylesheet.

<xsl:transform
  id = id
  extension-element-prefixes = tokens
  exclude-result-prefixes = tokens
  version = number>
  <!-- Content: (xsl:import*, top-level-elements) -->
</xsl:transform>

Attributes

version
Required. Indicates the version of XSLT the style sheet requires. Value should be set to 1.0 for this version of XSLT.
id
Specifies a unique identifier to facilitate embedding stylesheets.
extension-element-prefixes
Designates a namespace as an extension namespace. The value is a white-space separated list of namespace prefixes. The namespace bound to each of the prefixes is designated as an extension namespace. The default namespace (as declared by xmlns) may be designated as an extension namespace by including #default in the list of namespace prefixes. The designation of a namespace as an extension namespace is effective within the subtree of the stylesheet rooted at the element bearing the extension-element-prefixes; a subtree rooted at an xsl:stylesheet element does not include any stylesheets imported or included by children of that <xsl:stylesheet> element.
exclude-result-prefixes
Designates a namespace URI as an excluded namespace. The value is a white-space separated list of namespace prefixes. The namespace bound to each of the prefixes is designated as an excluded namespace. The default namespace (as declared by xmlns) may be designated as an excluded namespace by including #default in the list of namespace prefixes. The designation of a namespace as an excluded namespace is effective within the subtree of the stylesheet rooted at the element bearing the exclude-result-prefixes; a subtree rooted at an xsl:stylesheet element does not include any stylesheets imported or included by children of that <xsl:stylesheet> element.

Element Information

Number of occurrences | One
Parent elements | (No parent elements)
Child elements | xsl:include, xsl:output, xsl:param, xsl:template, xsl:variable,msxsl:script

Remarks

A synonym for the <xsl:stylesheet> element. This element can have a set of <xsl:template> elements representing different output templates. Processing begins by processing the root template, indicated by the pattern "/".

See Also

xsl:stylesheet


xsl:value-of Element

Inserts the value of the selected node as text.

<xsl:value-of
  select = expression
  disable-output-escaping = "yes" | "no"
</xsl:value-of>

Attributes

select
Required. Expression to be evaluated against the current context. The results are converted to a string as by a call to the string() function. A nodeset is converted to a string by inserting the string value of the first node in the set.
disable-output-escaping
Default is "no". If the value is "yes", a text node generated by instantiating the <xsl:text> element will be output without any escaping. For example,
<xsl:text disable-output-escaping="yes">&lt;</xsl:text>

generates the single character "<".

Note disable-output-escaping="yes" can be used to generate non-well-formed documents, and thus should be used with caution, since non-well-formed output may generate errors in certain circumstances. For instance, transformNodeToObject to an XML Document requires that the result be well-formed and thus may not complete if disable-output-escaping has affected the well-formedness of the document. We recommend considering disable-output-escaping="yes" as an advanced feature to be used only when the potential dangers are understood.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:attribute, xsl:comment, xsl:copy, xsl:element, xsl:for-each, xsl:if, xsl:otherwise, xsl:param, xsl:processing-instruction, xsl:template, xsl:variable, xsl:when, xsl:with-param, output elements
Child elements | (No child elements)

Remarks

The <xsl:value-of> element inserts a text string representing the value of the first element (in document order) specified by the select attribute.

If the XPath expression returns more than a single node, the <xsl:value-of> element returns the text of the first node returned (equivalent to the XMLDOMNode object's selectSingleNode method). If the node returned is an element with substructure, <xsl:value-of> returns the concatenated text nodes of that element's subtree with the markup removed.

Example

Given this XML,

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="family.xsl"?>
<family>
  <person>
    <given-name age="10">Fred</given-name>
    <family-name>Smith</family-name>
  </person>
  …
</family>

the following creates an HTML paragraph from a "person" element with "given-name" and "family-name" child elements. The paragraph will contain the string-value of the first "given-name" child element of the current node, followed by a space and the string-value of the first "family-name" child element of the current node.

<xsl:template match="person">
  <p>
    <xsl:value-of select="given-name"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="family-name"/>
  </p>
</xsl:template>

See Also

Creating and Populating an HTML Template


xsl:variable Element

Specifies a value bound in an expression.

<xsl:variable
  name = Qname
  select = expression>
</xsl:variable>

Attributes

name
Required. Specifies the name of the variable.
select
The value of the attribute is an expression, and the value of the variable is the object that results from evaluating the expression. When this attribute is specified, the content of <xsl:variable> must be empty.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:attribute, xsl:comment, xsl:copy, xsl:element, xsl:for-each, xsl:if, xsl:otherwise, xsl:param, xsl:processing-instruction, xsl:stylesheet xsl:template, xsl:variable, xsl:when, xsl:with-param, output elements
Child elements | xsl:apply-templates, xsl:attribute, xsl:choose, xsl:comment, xsl:copy, xsl:copy-of, xsl:element, xsl:for-each, xsl:if, xsl:processing-instruction, xsl:text, xsl:value-of, xsl:variable, output elements

Remarks

The value of the variable may be an object of any type that can be returned by an expression. The <xsl:variable> element can specify the value of the variable in three alternative ways:

  • If the element has a select attribute, the value of the attribute must be an expression and the value of the variable is the object that results from evaluating the expression. In this case, the content of the element must be empty.
  • If the element does not have a select attribute and has non-empty content such as one or more child nodes, the content specifies the value. The content is a template that is instantiated to give the value of the variable. The value is a result tree fragment equivalent to a node-set containing just a single root node having as children the sequence of nodes produced by instantiating the template. The base URI of the nodes in the result tree fragment is the base URI of the variable-binding element.

    An error occurs if a member of the sequence of nodes created by instantiating the template is an attribute node or a namespace node, because a root node cannot have an attribute node or a namespace node as a child.

  • If the content is empty and does not have a select attribute, the value of the variable is an empty string. Thus
    <xsl:variable name="x"/>

    is equivalent to

    <xsl:variable name="x" select="''"/>

    Note When a variable is used to select nodes by position, be careful not to do the following:

    <xsl:variable name="n">2</xsl:variable>
...
<xsl:value-of select="item[$n]"/>

    This will output the value of the first item element, because the variable "n" will be bound to a result tree fragment, not a number. Instead, do either

    <xsl:variable name="n" select="2"/>
...
<xsl:value-of select="item[$n]"/>

    or

    <xsl:variable name="n">2</xsl:variable>
...
<xsl:value-of select="item[number($n)]"/>

    Note One convenient way to specify the empty node-set as the default value of a parameter is:

    <xsl:param name="x" select="/.."/>

See Also

xsl:param


xsl:when Element

Provides multiple conditional testing in conjunction with the <xsl:choose> and <xsl:otherwise> elements.

<xsl:when
  test = boolean expression>
</xsl:when>

Attributes

test
Required. The condition in the source data to test. If the expression in this attribute evaluates to true when cast to a boolean, the contents of <xsl:when> are placed in the output. Node sets are cast to a boolean true if they contain at least one node.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:choose
Child elements | xsl:apply-templates, xsl:attribute, xsl:choose, xsl:comment, xsl:copy, xsl:copy-of, xsl:element, xsl:for-each, xsl:if, xsl:processing-instruction, xsl:value-of, xsl:variable, output elements

Remarks

Describes one of the alternatives to be chosen by the <xsl:choose> element. The default alternative is described by the <xsl:otherwise> element.

For simple conditional testing, use the <xsl:if> element.

Example

This example shows a template for "order" elements and inserts an <HR> or <BR> before the order's contents, based on the order's "total" element value. If the total is less than 10, a red <HR> will be generated; if the total is less than 20, a pink <HR> will be generated; otherwise a <BR> element will be created.

<xsl:template match="order">
   <xsl:choose>
      <xsl:when test="total &lt; 10">
         <HR STYLE="color:red"/>
      </xsl:when>
      <xsl:when test="total &lt; 20">
         <HR STYLE="color:pink"/>
      </xsl:when>
      <xsl:otherwise>
         <BR/>
      </xsl:otherwise>
   </xsl:choose>
   <xsl:apply-templates />
</xsl:template>

See Also

Conditional Templates


xsl:with-param Element

Passes a parameter to a template.

<xsl:with-param
  name = Qname
  select = expression>
</xsl:with-param>

Attributes

name
Required. Specifies the name of the parameter.
select
An expression to be matched against the current context. There is no default value, and empty string is generated if there is no content.

Element Information

Number of occurrences | Unlimited
Parent elements | xsl:apply-templates
Child elements | xsl:apply-templates, xsl:attribute, xsl:choose, xsl:comment, xsl:copy, xsl:copy-of, xsl:element, xsl:for-each, xsl:if, xsl:processing-instruction, xsl:text, xsl:value-of, xsl:variable

Remarks

The required name attribute specifies the name of the parameter (the variable the value of whose binding is to be replaced). The <xsl:with-param> element is allowed within <xsl:apply-templates>. The value of the parameter is specified in the same way as for <xsl:variable> and <xsl:param>. The current node and current node-list used for computing the value specified by the <xsl:with-param> element is the same as that used for the <xsl:apply-templates> element within which it occurs. It is not an error to pass a parameter x to a template that does not have an <xsl:param> element for x; the parameter is simply ignored.

See Also

xsl:variable | xsl:param | xsl:template


XPath Syntax

XPath can be used as a general-purpose query notation for addressing and filtering the elements and text of XML documents. XPath is supported in MSXML within XSLT, and through the Document Object Model (DOM) extensions selectNodes and selectSingleNode.

The Microsoft XML Parser (MSXML) supports the World Wide Web Consortium (W3C) recommendation for XML Path Language (XPath) Version 1.0 (W3C Recommendation 16 November 1999). For details of the parser's conformance, see the XSLT and XPath Conformance Notes.

This and the following topics introduce the syntax of XPath:

  • Operators and Special Characters
  • Collections
  • Filters and Filter Patterns
  • Boolean, Comparison, and Set Expressions
  • Comparisons
  • Set Operations
  • Location Paths
  • XPath Functions
  • Sample Data
  • XPath Examples

The XPath notation is declarative, rather than procedural. Each pattern describes the types of nodes to match using a notation that indicates the hierarchical relationship between the nodes. For example, the pattern "book/author" means find "author" elements contained in "book" elements.

The notation enables you to query the XML document tree, which represents the contents of the XML document as a hierarchy of nodes. The syntax mimics the URI (universal resource identifier) directory navigation syntax, but instead of specifying navigation through a logical hierarchy of directories, the navigation is through a hierarchy of nodes in the XML tree.

For example, the following URI means find the background.jpg file within the images directory:

images/background.jpg

Similarly, in an XPath pattern, the following means find the collection of book elements within bookstore elements:

bookstore/book

Context

All XPath queries occur within a particular context, that is, at the level of a particular node within the tree representation.

A "context" is the single node against which the pattern matching operates. To understand the concept of context, consider a tree containing nodes. Asking for all nodes named X from the root of the tree returns one set of results, while asking for those nodes from a branch in the tree returns a different set of results. The result of a query dependson the context against which it is executed.

XPath queries can match specific patterns at one particular context, retrieve the results, and perform additional operations relative to the context of the returned nodes. This notation gives XPath queries extraordinary flexibility in searching throughout the document tree.

When using patterns with the Microsoft XML DOM programming interfaces, the context is the Node object whose selectNodes or selectSingleNode method is called.

A pattern prefixed with a period and forward slash (./) explicitly uses the current context as the context for the query.

A pattern prefixed with a forward slash (/) uses the root of the document tree as the context for the query.

A pattern can use the // operator to indicate a search that can include zero or more levels of hierarchy. When this operator appears at the beginning of the pattern, the context is relative to the root of the document. The .// prefix indicates that the context starts at the level in the hierarchy indicated by the current context.

Note that element names can include the period character (.), and these can be used in patterns just as any other name.

Examples

Find all author elements within the current context:

./author

Note that this is equivalent to:

author

Find all first.name elements:

first.name

Find the bookstore element at the root of this document:

/bookstore

Find the root element of this document:

/*

Find all author elements anywhere within the current document:

//author

Find all bookstores where the value of the specialty attribute is equal to "textbooks":

/bookstore[@specialty = "textbooks"]

Find all books where the value of the style attribute on the book is equal to the value of the specialty attribute of the bookstore element at the root of the document:

book[@style = /bookstore/@specialty]

In addition to this notation for representing hierarchical relationships among nodes, the implementation also supports Boolean, Comparison, and Set Expressions, Set Operations, Filters and Filter Patterns, Collections, and XPath Functions.

XPath also support namespaces and data types. Namespace prefixes can be included in patterns so that the matching operations can check for specific namespace prefixes.

Samples throughout this documentation are summarized in XPath Examples and refer to the data shown in Sample Data.


Operators and Special Characters

XPath expressions are constructed using the operators and special characters shown in the following table:

/ | Child operator; selects immediate children of the left-side collection. When this path operator appears at the start of the pattern, it indicates that children should be selected from the root node.
// | Recursive descent; searches for the specified element at any depth. When this path operator appears at the start of the pattern, it indicates recursive descent from the root node.
. | Indicates the current context.
* | Wildcard; selects all elements regardless of the element name.
@ | Attribute; prefix for an attribute name.
@ | Attribute wildcard; selects all attributes regardless of name.
: | Namespace separator; separates the namespace prefix from the element or attribute name.
( ) | Groups operations to explicitly establish precedence.
[ ] | Applies a filter pattern.
[ ] | Subscript operator; used for indexing within a collection.
+ | Performs addition.
- | Performs subtraction.
div | Performs floating-point division according to IEEE 754.
* | Performs multiplication.
mod | Returns the remainder from a truncating division.

(Note that this table does not list Boolean, Comparison, and Set Expressions or Set Operations. These are treated in subsequent topics.)

Precedence order (from highest precedence to lowest) is defined as indicated in the following table:

1 | ( ) | Grouping
2 | [ ] | Filters
3 | / // | Path operations

Note that the filter pattern operators (the brackets) have a higher precedence than the path operators (the slash characters). For example, the expression "//comment()[3]" is interpreted as "//(comment()[3])", and selects all comments with an index equal to 3 relative to the comment's parent anywhere in the document. This differs from the expression "(//comment())[3]", which selects the third comment from the set of all comments relative to the parent. The first expression can return more than one comment, while the latter can return only one comment.

These operators and special characters are described in detail throughout this reference. Subsequent topics also discuss Set Operations.

Path Operators

The collection of elements of a certain type can be determined using the path operators (/ and //). These operators take as their arguments a "left side" collection on which to perform the selection and a "right side" collection indicating which elements to select. The child operator (/) selects from immediate children of the left-side collection, while the descendant operator (//) selects from arbitrary descendants of the left-side collection. In effect, // can be considered a substitute for one or more levels of hierarchy.

Note that the path operators change the context as the query is performed. By stringing path operators together, users can traverse the document tree.

Examples

Find all first-name elements within an author element. Note that the author children of the current context are found, and then first-name children are found, relative to the context of the author elements.

author/first-name

Find all title elements one or more levels deep in the bookstore (arbitrary descendants):

bookstore//title

Note that this is different from the following pattern, which finds all title elements that are grandchildren of bookstore elements:

bookstore/*/title

Find emph elements anywhere inside book excerpts, anywhere inside the bookstore:

bookstore//book/excerpt//emph

Find all titles one or more levels deep in the current context. Note that this situation is essentially the only one in which the period notation is required.

.//title

Wildcard Character

An element can be referenced without using its name by substituting the wildcard (*) collection. The * collection returns all elements that are children of the current context, regardless of the tag name.

Examples

Find all element children of author elements:

author/*

Find all last names that are grandchildren of books:

book/*/last-name

Find the grandchildren elements of the current context:

*/*

Find the book element from the "my" namespace:

my:book

Find all elements from the "my" namespace:

my:*

Note that the pattern "*:book" is not supported.

Attributes

XPath denotes attribute names with the @ symbol. Attributes and child elements are treated impartially, and capabilities are equivalent between the two types wherever possible.

Note Attributes cannot contain child elements, so syntax errors occur when path operators are applied to attributes. In addition, you cannot apply an index to attributes because, by definition, no order is defined for attributes.

Examples

Find the style attribute of the current element context:

@style

Find the exchange attribute on price elements within the current context:

price/@exchange

The following example is not valid:

price/@exchange/total

Find the style attribute for all book elements:

book/@style

Finding Multiple Attributes

All attributes of an element can be returned using @*. This is potentially useful for applications that treat attributes as fields in a record.

Note that the pattern "@*:title" is not supported.

Examples

Find all attributes of the current element context:

@*

Find all attributes from the "my" namespace. This does not include unqualified attributes on elements from the "my" namespace:

@my:*

See Also

Set Operations | Sample Data | XPath Examples


Collections

Collections returned by XPath queries preserve document order, hierarchy, and identity, to the extent that these are defined. That is, a collection of elements is returned in document order without repeated elements. Because by definition attributes are unordered, there is no implicit order to attributes returned for a specific element.

The collection of all elements with a certain tag name is expressed using the tag name itself. This can be qualified by showing that the elements are selected from the current context by using a period and forward slash (./), but the current context is used by default and need not be noted explicitly.

Examples

Find all first-name elements. These examples are equivalent:

./first-name
first-name

Find all unqualified book elements:

book

Indexing into a Collection

XPath expressions make it easy to find a specific node within a set of nodes. Simply enclose the index ordinal within square brackets. The ordinal is zero-based (the first element is number zero).

The bracket characters [] have higher precedence than the slash characters / and //. The expression "//comment()[3]" is interpreted as "//(comment()[3])", and selects all comments with an index equal to 3 relative to the comment's parent anywhere in the document. This differs from the expression "(//comment())[3]", which selects the third comment from the set of all comments relative to the parent. The first expressions can return more than one comment, while the other expression can return only one comment.

Examples

The following finds the first author element:

author[0]

The following finds the third author element that has a first name:

author[first-name][2]

Note that indexes are relative to the parent. Consider the following data:

<x>
  <y/>
  <y/>
</x>
<x>
  <y/>
  <y/>
</x>

Find the first y from each x:

x/y[0]

Find the first y from the entire set of y elements within x elements:

(x/y)[0]

Find the first y from the first x:

x[0]/y[0]

Finding the Last Element in a Collection

The last method returns True for the last element in a collection. Note that last is relative to the parent node.

Examples

Find the last book:

book[last()]

Find the last author for each book:

book/author[last()]

Find the last author from the entire set of authors of books:

(book/author)[last()]

Grouping

Parentheses can be used to group collection operators for clarity or where the normal precedence is inadequate to express an operation.

See Also

Sample Data | XPath Examples


Filters and Filter Patterns

Constraints and branching can be applied to any collection by adding a filter clause, [pattern], to the collection. The filter is analogous to the SQL WHERE clause. The filter contains a pattern within it called the filter pattern. The filter pattern evaluates to a Boolean value and is tested for each element in the collection. Any elements in the collection failing the filter pattern test are omitted from the result collection.

For convenience, if a collection is placed within the filter, a Boolean TRUE is generated if the collection contains any members and a FALSE is generated if the collection is empty. An expression such as "author/degree" implies a collection-to-Boolean conversion function that evaluates to TRUE if there exists an author with a child element degree.

Note that any number of filters can appear at a given level of an expression. Empty filters are not allowed.

Filters are always evaluated with respect to a context. In other words, the expression "book[author]" means that for every book element that is found, test whether it has an author subelement. Likewise, "book[author = 'Bob']" means that for every book element that is found, test whether it has an author child element with the value "Bob". One can examine the value of the context as well by using the period (.) character. For example, "book[. = 'Trenton']" means that for every book that is found in the current context, test whether its value is "Trenton".

If a filter compares values of elements and the filter is to be applied to more than one child element, you might want to use the any and all keywords. When these keywords are not specified, only the first matching child is used for the comparison.

This pattern looks for the first author child of each book element. If the value of the author child is "Bob", the corresponding book element will be returned.

book[author="Bob"]

Examples

Find all books that contain at least one excerpt element:

book[excerpt]

Find all titles of books that contain at least one excerpt element:

book[excerpt]/title

Find all authors of books where the book contains at least one excerpt and the author has at least one degree:

book[excerpt]/author[degree]

Find all books that have authors with at least one degree:

book[author/degree]

Find all books that have an excerpt and a title:

book[excerpt][title]

See Also

Sample Data | XPath Examples


Boolean, Comparison, and Set Expressions

Filter patterns can contain Boolean expressions, comparison expressions, and set expressions. Shortcuts listed in the following table represent alternative symbols that are provided in this XSLT implementation. This documentation discusses these expression operators.

Operator | Description
and | Logical-and
or | Logical-or
not() | Negation
= | Equality
!= * | Not equal
< * | Less than
<= * | Less than or equal
> * | Greater than
>= * | Greater than or equal
| | Set operation; returns the union of two sets of nodes

* extended XPath method

The W3C syntax for operator keywords uses white space and other separators rather than the dollar sign character ($) used in version 2.5. In the W3C syntax, a binary keyword of the form $xxx$ can be expressed as wsxxxws, where ws refers to a token terminator that can be white space, single quote characters ('), or double quote characters ("). Unary operators such as not() use functional notation. Although the Microsoft implementation supports both syntaxes, it is recommended that the W3C syntax be used where possible in the interests of future compatibility.

Precedence order (from highest to lowest) for comparison operators and Boolean operators is shown in the following table:

1 | ( ) | Grouping
2 | [ ] | Filters
3 | / // | Path operations
4 | < <= > >= | Comparisons
5 | = != | Comparisons
6 | | | Union
7 | not() | Boolean not
8 | and | Boolean and
9 | or | Boolean or

Boolean expressions can match all nodes of a particular value or all nodes with nodes in particular ranges. Boolean expressions are in the form (left-side value operator right-side value) and return a Boolean result.

Operators are case sensitive.

Boolean AND and OR

The Boolean operators and and or perform logical-and and logical-or operations, respectively. These operators, in conjunction with grouping parentheses, can be used to build very sophisticated logical expressions.

Examples

Find all author elements that contain at least one degree and one award:

author[degree and award]

Find all author elements that contain at least one degree or award and at least one publication:

author[(degree or award) and publication]

Boolean NOT

The Boolean not operator negates the value of an expression within a filter pattern.

Examples

Find all author elements that contain at least one degree element and contain no publication elements:

author[degree and not(publication)]

Find all author elements that contain publication elements but do not contain either degree elements or award elements:

author[not(degree or award) and publication]

See Also

Sample Data | XPath Examples


Comparisons

The = sign is used for equality; != is used for inequality.

Two operands are supplied for a comparison operation: the vector or scalar that is to be compared (the left-side value, or lvalue) and the scalar value (the right-side value, or rvalue) against which to compare. The rvalue must either be a scalar or a value that can be cast at run time to a scalar.

If the lvalue of a comparison is a set, "any" semantics are used for the comparison operators. That is, the result of a comparison is True if any item in the set meets the condition.

The lvalue of an expression cannot be a literal, i.e., "3" = a is not allowed. If the rvalue is an attribute, the text(lvalue) is compared to the text(rvalue).

All elements and attributes are strings but are automatically cast as integer values for numeric comparisons. Literal numeric values are cast to long or double types during comparison operations, as shown in the following table:

Literal type | Comparison | Example
String | text(lvalue) op text(rvalue) | a < foo
Integer | (long) lvalue op (long) rvalue | a < 3
Real | (double) lvalue op (double) rvalue | a < 3.1

Single or double quotation marks can be used for string delimiters in expressions. This makes it easier to construct and pass patterns from within scripting languages.

Examples

Find all author elements whose last name is Bob:

author[last-name = "Bob"]

Find all author elements where the first last-name is Bob:

author[last-name[0] = "Bob"]

Find all authors where the from attribute is not equal to "Harvard":

degree[@from != "Harvard"]

Find all authors where the last name is the same as the /guest/last-name element:

author[last-name = /guest/last-name]

Find all authors whose text is "Matthew Bob":

author[. = "Matthew Bob"]

Order of Precedence for Comparisons

Comparisons with regard to data types obey the order of precedence.

  • If at least one operand is a Boolean, each operand is first converted to a Boolean.
  • Otherwise, if at least one operand is a number, each operand is first converted to a number.
  • Otherwise, if at least one operand is a date, each operand is first converted to a date.
  • Otherwise, both operands are first converted to strings.

Comparing Dates

If one operand is a node-set and the other is a date, the comparison will be True if and only if there is a node in the node-set operand such that the result of performing the comparison on the date operand and on the result of converting the value of that node to a date using the msxslt:date() function is True.

Two dates are equal if and only if an unambiguous canonical format for the date (e.g., GMT representation as a number) indicates that they are so.

A date is less than another date if it indicates a date/time earlier than the second date/time, and so on.

Binary Comparison Operators

A set of binary comparison operators compares numbers and strings and returns Boolean results. <, <=, >, and >= are used for less than, less than or equal, greater than, and greater than or equal. Single or double quotation marks can be used for string delimiters in expressions. This makes it easier to construct and pass patterns within scripting languages.

Examples

Find all author elements whose last name is "Bob" and whose price is > 50:

author[last-name = "Bob" and price > 50]

Find all authors where the from attribute is not equal to "Harvard":

degree[@from != "Harvard"]

Find all authors whose last name begins with "M" or greater:

author[last-name >= "M"]

Find the first three books (0, 1, 2):

book[index() <= 2]

See Also

Sample Data | XPath Examples


Set Operations

XPath supports the set operation |.

"|" Operator

The "|", or union, operator returns the union of two queries; that is, it returns the set of nodes returned for one query combined with the set of nodes that satisfy the other query. Multiple union operators can be combined to union together the results of multiple queries. The union operator preserves document order and does not return duplicates.

The Microsoft implementation extends the union operator to be available anywhere in a query, not just at the top level. To support this behavior, the following constraint is applied to unions: All queries combined with the union operator must be from the same subtree and have the same root.

The following table demonstrates examples of valid and invalid queries based on these constraints:

Valid use of | (same subtree) | Invalid use of | (different subtrees)
a | b | c | a | /b | c
/a | /b | /c | a | /b | c
a | . | a | id('C1')
a | b | a | ../b
a | b | a | current()/b
id(x | y) | id(x) | id(y)

Examples

Find all first-names and last-names:

first-name | last-name

Find all books and magazines from a bookstore:

bookstore/(book | magazine)

Find all books and all authors:

book | book/author

Find the first-names, last-names, or degrees from authors within either books or magazines:

(book | magazine)/author/(first-name | last-name | degree)

Find all books with author/first-name equal to "Bob" and all magazines with price less than 10:

book[author/first-name = "Bob"] | magazine[price < 10]

The following will return the first node that matches, not multiple nodes:

id(x | y)

Precedence

Precedence order (from highest precedence to lowest) between Boolean and comparison operators is shown in the following table:

1 | ( ) | Grouping
2 | [ ] | Filters
3 | / // | Path operations
4 | < <= > >= | Comparisons
5 | = != | Comparisons
6 | | | Union
7 | not() | Boolean not
8 | and | Boolean and
9 | or | Boolean or

See Also

Sample Data | XPath Examples


Location Paths

A location path is an important kind of expression that selects a set of nodes relative to the context node. The result of evaluating the expression is the node-set containing the nodes selected by the location path. Location paths can recursively contain expressions that are used to filter sets of nodes.

This and the following topics include the syntax and pieces of location paths:

  • Location Steps
  • Axes
  • Node Tests
  • Predicates
  • Location Path Examples

Here are some examples of location paths:

To select the para element of the context node, you could use the following location path:

child::para

To select all element children of the context node, use

child::*

And to select all text node children of the context node, use

child::text()

Location paths are composed of one or more location steps, each separated by a "/". Each step in turn selects a set of nodes relative to the context node. The location steps are strung together from left to right. The left-most location step selects a set of nodes relative to the context node, which are then used as the context for the following step, and so on, until all steps have been processed.

For example, the following selects the para element children of the div element children of the context node:

child::div/child::para


Location Steps

A location step selects a set of nodes relative to the context node. It is composed of three parts:

  • An axis, which specifies the tree relationship between the nodes selected by the location step and the context node.
  • A node test, which specifies the node type and expanded-name of the nodes selected by the location step.
  • Zero or more predicates, which use expressions to further refine the set of nodes selected by the location step.

The syntax for a location step is the axis name and node test separated by a double colon, followed by zero or more expressions, each in square brackets.

For example, in the following, child is the name of the axis, para is the node test, and [position()=1] is a predicate:

child::para[position()=1]

The node-set selected by the location step is the node-set that results from generating an initial node-set from the axis and node test, and then filtering that node-set by each of the predicates in turn.

The initial node-set consists of the nodes having the relationship to the context node specified by the axis and having the node type and expanded-name specified by the node test. For example, a location step descendant::para selects the para element descendants of the context node: descendant specifies that each node in the initial node-set must be a descendant of the context; para specifies that each node in the initial node-set must be an element named "para". The available axes are described in Axes. The available node tests are described in Node Tests. The meaning of some node tests is dependent on the axis.

The initial node-set is filtered by the first predicate to generate a new node-set; this new node-set is then filtered using the second predicate, and so on. The final node-set is the node-set selected by the location step. The axis affects how the expression in each predicate is evaluated, so the semantics of a predicate is defined with respect to an axis.


Axes

An axis is used in a location path to specify the tree relationship between the nodes selected by the location step and the context node.

Axes | Description
ancestor | Contains the ancestors of the context node; the ancestors of the context node consist of the parent of the context node and the parent's parent and so on; thus the ancestor axis will always include the root node unless the context node is the root node.
ancestor-or-self | Contains the context node and the ancestors of the context node; thus the ancestor axis will always include the root node.
attribute | Contains the attributes of the context node; the axis will be empty unless the context node is an element.
child | Contains the children of the context node.
descendant | Contains the descendants of the context node; a descendant is a child or a child of a child and so on; thus the descendant axis never contains attribute or namespace nodes.
descendant-or-self | Contains the context node and the descendants of the context node.
parent | Contains the parent of the context node, if there is one.
self | Contains just the context node itself.


Node Tests

A node test specifies the node type and expanded name of the nodes selected by the location step. Every axis has a principal node type. If an axis can contain elements, the principal node type is element; otherwise it is the type of the nodes that the axis can contain. Therefore,

  • for the attribute axis, the principal node type is attribute.
  • for the namespace axis, the principal node type is namespace.
  • for other axes, the principal node type is element.

A node test that is a QName is True if and only if the type of the node is the principal node type and has an expanded-name equal to the expanded-name specified by the QName. For example, child::para selects the para element children of the context node; if the context node has no para children, it will select an empty set of nodes.

A QName in the node test is expanded into an expanded-name using the namespace declarations from the expression context. This is the same way expansion is done for element type names in start and end tags except that the default namespace declared with xmlns is not used: if the QName does not have a prefix, the namespace URI is Null (this is the same way attribute names are expanded). It is an error if the QName has a prefix for which there is no namespace declaration in the expression context.

A node test * is True for any node of the principal node type. For example, child::* will select all element children of the context node, and attribute::* will select all attributes of the context node.

A node test can have the form NCName:*. In this case, the prefix is expanded in the same way as with a QName, using the context namespace declarations. It is an error if there is no namespace declaration for the prefix in the expression context. The node test will be True for any node of the principal type whose expanded-name has the namespace URI to which the prefix expands, regardless of the local part of the name.

Available node tests are listed in the following table:

comment() | Returns True for a comment node.
node() | Returns True for a node of any type.
processing-instruction() | Returns True for a processing instruction node.
text() | Returns True for a text node.

The node test text() is true for any text node. For example, child::text() selects the text node children of the context node. Similarly, the node test comment() is True for any comment node, and the node test processing-instruction() is True for any processing instruction. The processing-instruction() test may have an argument that is literal; in this case, it is True for any processing instruction that has a name equal to the value of the literal.

A node test node() is True for any node of any type whatsoever.


Predicates

A predicate filters a node-set with respect to an axis to produce a new node-set. For each node in the node-set to be filtered, the expression is evaluated with that node as the context node, with the number of nodes in the node-set as the context size, and with the "proximity position" of the node in the node-set with respect to the axis as the context position; if the expression evaluates to True for that node, the node is included in the new node-set; otherwise it is not included.

An axis is either a forward axis or a reverse axis. An axis that only ever contains the context node or nodes that are after the context node in document order is a forward axis. An axis that only ever contains the context node or nodes that are before the context node in document order is a reverse axis. Thus ancestor and ancestor-or-self are reverse axes; all other axes are forward axes. Since the self axis always contains at most one node, it makes no difference whether it is a forward or reverse axis. The proximity position of a member of a node-set with respect to an axis is defined as the position of the node in the node-set ordered in document order if the axis is a forward axis and ordered in reverse document order if the axis is a reverse axis. The first position is 1.

A predicate expression is evaluated by evaluating the expression and converting the result to a Boolean. If the result is a number, it will be converted to True if the number is equal to the context position or converted to False otherwise; if the result is not a number, it will be converted to a Boolean. Thus a location path para[3] is equivalent to para[position()=3].


Examples

Microsoft XML SDK 2.6 - XSLT Reference

Location Path Examples

The following examples illustrate the use of location paths.

Select all the children of the context node, whatever their node type:

child::node()

Select the name attribute of the context node:

attribute::name

Select all the attributes of the context node:

attribute::*

Select the para element descendants of the context node:

descendant::para

Select all div ancestors of the context node:

ancestor::div

Select the div ancestors of the context node and, if the context node is a div element, the context node as well:

ancestor-or-self::div

Select the para element descendants of the context node and, if the context node is a para element, the context node as well:

descendant-or-self::para

Select the context node if it is a para element, and otherwise select nothing:

self::para

Select the para element descendants of the chapter element children of the context node:

child::chapter/descendant::para

Select all para grandchildren of the context node:

child::*/child::para

Select the document root (which is always the parent of the document element):

/

Select all the para elements in the same document as the context node:

/descendant::para

Select all the item elements that have an olist parent and that are in the same document as the context node:

/descendant::olist/child::item

Select the first para child of the context node:

child::para[position()=1]

Select the last para child of the context node:

child::para[position()=last()]

Select the next-to-last para child of the context node:

child::para[position()=last()-1]

Select all the para children of the context node other than the first para child of the context node.

child::para[position()>1]

Select the forty-second figure element in the document:

/descendant::figure[position()=42]

Select the second section of the fifth chapter of the doc document element:

/child::doc/child::chapter[position()=5]/child::section[position()=2]

Select all para children of the context node that have a type attribute with value "warning":

child::para[attribute::type="warning"]

Select the fifth para child of the context node that has a type attribute with value "warning":

child::para[attribute::type="warning"][position()=5]

Select the fifth para child of the context node if that child has a type attribute with value warning":

child::para[position()=5][attribute::type="warning"]

Select the chapter children of the context node that have one or more title children with string value equal to "Introduction":

child::chapter[child::title=Introduction"]

Select the chapter children of the context node that have one or more title children:

child::chapter[child::title]

Select the chapter and appendix children of the context node:

child::*[self::chapter or self::appendix]

Select the last chapter or appendix child of the context node:

child::*[self::chapter or self::appendix][position()=last()]


XPath Functions

XPath functions are used to evaluate expressions.

Each function in the function library is specified using a function prototype, which gives the return type, the name of the function, and the type of the arguments. If an argument type is followed by a question mark, the argument is optional; otherwise, the argument is required. Function names are case sensitive.

The functions are divided into several groups:

Node-Set | Either take a node-set argument, return a node-set, or return/provide information about a particular node within a node-set.
String | Perform evaluations, formatting, and manipulation on string arguments.
Boolean | Evaluate the argument expressions to obtain a Boolean result.
Number | Evaluate the argument expressions to obtain a numeric result.
XSLT | Additions to the core XPath function library.

Examples

Find all text children of author elements:

author/text()

Find all elements that have text node or CDATA node children:

*[text()]

Find the last author for each book:

book/author[last()]


Node Set Functions

Node-set functions either take a node-set argument, return a node-set, or return/provide information about a particular node within a node-set.

Node Set Functions

last | Returns a number equal to context size of the expression evaluation context.
position | Returns the index number of the node within the parent.
count | Returns the number of nodes in the argument node-set.
id | Selects elements by their unique ID.
local-name | Returns the local part of the expanded name of the node in the argument node-set that is first in document order.
namespace-uri | Returns the namespace URI of the expanded name of the node in the argument node-set that is first in document order.
name | Returns a string containing a QName representing the expanded name of the node in the argument node-set that is first in document order.


last Function

Returns a number equal to context size of the expression evaluation context.

number last()

Remarks

The following finds the last author for each book:

book/author[last()]

See Also

XML Data Types Reference


position Function

Returns the index number of the node within the parent.

number position()

Remarks

The first node returns a position of 1.

See Also

XML Data Types Reference


count Function

Returns the number of nodes in the argument node-set.

number count(node-set)

See Also

XML Data Types Reference


id Function

Selects elements by their unique ID.

node-set id(object)

Remarks

When the argument is of type node-set, the result is the union of the result of applying id() to the string value of each of the nodes in the argument node-set.

When the argument is of any other type, it is converted to a string and then split into a white space–separated list of tokens (white space is any sequence of characters matching the production); the result is a node-set containing the elements in the same document as the context node that have a unique ID equal to any of the tokens in the list.

The following selects the element with unique ID foo,

id("foo")

The following selects the fifth para child of the element with unique ID foo,

id("foo")/child::para[position()=5]

See Also

XML Data Types Reference


local-name Function

Returns the local part of the expanded name of the node in the argument node-set that is first in document order.

string local-name(node-set?)

Remarks

If the argument node-set is empty or the first node has no expanded name, an empty string is returned. If the argument is omitted, it defaults to a node-set with the context node as its only member.

See Also

XML Data Types Reference


namespace-uri Function

Returns the namespace URI of the expanded name of the node in the argument node-set that is first in document order.

string namespace-uri(node-set?)

Remarks

If the argument node-set is empty, the first node has no expanded name or the namespace URI of the expanded name is Null and an empty string is returned. If the argument is omitted, it defaults to a node-set with the context node as its only member.

Note The string returned will be empty except for element nodes and attribute nodes.

See Also

XML Data Types Reference


name Function

Returns a string containing a QName representing the expanded name of the node in the argument node-set that is first in document order.

string name(node-set?)

Remarks

The QName must represent the expanded name with respect to the namespace in effect on the node. This need not be the case if there are namespace declarations in effect on the node that associate multiple prefixes with the same namespace. If the argument node-set is empty or the first node has no expanded name, an empty string is returned. If the argument it omitted, it defaults to a node-set with the context node as its only member.

See Also

XML Data Types Reference


String Functions

String functions perform evaluations, formatting, and manipulation of string arguments, or convert an object to a string.

String Functions

concat | Returns the concatenation of the arguments.
contains | Returns True if the first argument string contains the second argument string; otherwise returns False.
string | Converts an object to a string.
normalize-space | Returns the argument string with the white space stripped.
starts-with | Returns True if the first argument string starts with the second argument string; otherwise returns False.
substring-before | Returns the substring of the first argument string that precedes the first occurrence of the second argument string in the first argument string.
substring-after | Returns the substring of the first argument string that follows the first occurrence of the second argument string in the first argument string.
substring | Returns the substring of the first argument starting at the position specified in the second argument and the length specified in the third argument.
string-length | Returns the number of characters in the string.
translate | Returns the first argument string with occurrences of characters in the second argument string replaced by the character at the corresponding position in the third argument string.

Remarks


string Function

Converts an object to a string.

string string(object?)

Remarks

An object is converted to a string as follows:

A node-set is converted to a string by returning the string value of the node in the node-set that is first in document order. If the node-set is empty, an empty string is returned.

A number is converted to a string as follows:

  • NaN is converted to the string NaN.
  • positive zero is converted to the string "0".
  • negative zero is converted to the string "0".
  • positive infinity is converted to the string "infinity".
  • negative infinity is converted to the string "-infinity".
  • if the number is an integer, the number is represented in decimal form as a number with no decimal point and no leading zeros, preceded by a minus sign (-) if the number is negative.
  • otherwise, the number is represented in decimal form as a number with a decimal point and at least one digit before the decimal point and at least one digit after the decimal point, preceded by a minus sign (-) if the number is negative; there must be no leading zeros before the decimal point, apart possibly from the one required digit immediately before the decimal point; beyond the one required digit after the decimal point there must be as many, but only as many, more digits as are needed to uniquely distinguish the number from all other IEEE 754 numeric values.

The Boolean "false" value is converted to the string "false". The Boolean "true" value is converted to the string "true".

An object of a type other than the four basic types is converted to a string in a way that is dependent on that type.

If the argument is omitted, it defaults to a node-set with the context node as its only member.

Note The string function is not intended for converting numbers into strings for presentation to users. The format-number function and <xsl:number> element in XSLT provide this functionality.

See Also

XML Data Types Reference


concat Function

Returns the concatenation of the arguments.

string concat(string, string, string*)

Remarks

If an argument is not of type string, it is first converted to a string and then evaluated.

Example

The following function call returns "abcdefg":

concat("abc", "d", "ef", "g")

The following function call returns "abc3efg":

concat("abc", 3, "ef", "g")

See Also

XML Data Types Reference


starts-with Function

Returns True if the first argument string starts with the second argument string; otherwise returns False.

boolean starts-with(string, string)

Remarks

If an argument is not of type string, it is first converted to a string and then evaluated.

The following function call returns True:

starts-with("foobar", "foo")

See Also

XML Data Types Reference


contains Function

Returns True if the first argument string contains the second argument string; otherwise returns False.

boolean contains(string, string)

Remarks

If an argument is not of type string, it is first converted to a string and then evaluated.

The following function call returns True:

contains("foobar", "oba")

See Also

XML Data Types Reference


substring-before Function

Returns the substring of the first argument string that precedes the first occurrence of the second argument string in the first argument string, or the empty string if the first argument string does not contain the second argument string.

string substring-before(string, string)

Remarks

If an argument is not of type string, it is first converted to a string and then evaluated.

The following function call returns "1999":

substring-before("1999/04/01","/")

See Also

XML Data Types Reference


substring-after Function

Returns the substring of the first argument string that follows the first occurrence of the second argument string in the first argument string, or the empty string if the first argument string does not contain the second argument string.

string substring-after(string, string)

Remarks

If an argument is not of type string, it is first converted to a string and then evaluated.

The following function call returns "04/01":

substring-after("1999/04/01","/")

The following function call returns "99/04/01":

substring-after("1999/04/01","19")

See Also

XML Data Types Reference


substring Function

Returns the substring of the first argument starting at the position specified in the second argument and the length specified in the third argument.

string substring(string, number, number?)

Remarks

Each character in the string is considered to have a numeric position: the position of the first character is 1, the position of the second character is 2, and so on.

If the third argument is not specified, it returns the substring starting at the position specified in the second argument and continuing to the end of the string.

The following function call returns "234":

substring("12345",2,3)

The following function call returns "2345":

substring("12345",2)

The returned substring contains those characters for which the position of the character is greater than or equal to the rounded value of the second argument, and if the third argument is specified, less than the sum of the rounded value of the second argument and the rounded value of the third argument. The comparisons and addition used for the above follow the standard IEEE 754 rules; rounding is done as if by a call to the round function.

The following examples illustrate various unusual cases:

substring("12345", 1.5, 2.6) returns "234"

substring("12345", 0, 3) returns "12"

substring("12345", 0 div 0, 3) returns ""

substring("12345", 1, 0 div 0) returns ""

substring("12345", -42, 1 div 0) returns "12345"

substring("12345", -1 div 0, 1 div 0) returns ""

See Also

XML Data Types Reference


string-length Function

Returns the number of characters in the string.

number string-length(string)

Remarks

If the argument is omitted, it returns the number of characters in the string-value of the context node.

The following function call returns 4:

string-length("abcd")

If an argument is not of type string, it is first converted to a string and then evaluated.

See Also

XML Data Types Reference


normalize-space Function

Returns the argument string with the white space stripped.

string normalize-space(string?)

Remarks

White space is normalized by stripping leading and trailing white space and replacing sequences of white space characters with a single space. If the argument is omitted, the string-value of the context node is normalized and returned.

The following function call returns "abc":

normalize-space("  abc  ")

If an argument is not of type string, it is first converted to a string and then evaluated.

See Also

XML Data Types Reference


translate Function

Returns the first argument string with occurrences of characters in the second argument string replaced by the character at the corresponding position in the third argument string.

string translate(string, string, string)

Remarks

The following function call returns "BAr":

translate("bar","abc","ABC")

If there is a character in the second argument string with no character at a corresponding position in the third argument string (because the second argument string is longer than the third argument string), occurrences of that character in the first argument string are removed.

The following function call returns "AAA":

translate("--aaa--","abc-","ABC")

If a character occurs more than once in the second argument string, the first occurrence determines the replacement character. If the third argument string is longer than the second argument string, excess characters are ignored.

If an argument is not of type string, it is first converted to a string and then evaluated.

See Also

XML Data Types Reference


Boolean Functions

The XPath syntax supports Boolean functions which return strings or numbers, and can be used with comparison operators in filter patterns.

Boolean Functions

boolean | Converts the argument to a Boolean.
true | Returns True.
false | Returns False.


boolean Function

Converts the argument to a Boolean.

boolean boolean(object)

Remarks

Converts its argument to a Boolean as follows:

  • A number is True if and only if it is neither positive or negative zero or NaN.
  • A node-set is True if and only if it is non-empty.
  • A string is True if and only if its length is non-zero.
  • An object of a type other than the four basic types is converted to a Boolean in a way that is dependent on that type.

See Also

XML Data Types Reference


true Function

Returns True.

boolean true()

See Also

XML Data Types Reference


false Function

Returns False.

boolean false()

See Also

XML Data Types Reference


Number Functions

The XPath syntax supports Number functions that return strings or numbers and can be used with comparison operators in filter patterns.

Number Functions

number Converts the argument to a number.


number Function

Converts the argument to a number.

number number(object?)

Remarks

Converts its argument to a number as follows:

  • A string that consists of optional white space followed by an optional minus sign followed by a number followed by white space is converted to the IEEE 754 number that is nearest (according to the IEEE 754 round-to-nearest rule) to the mathematical value represented by the string; any other string is converted to NaN.
  • Boolean True is converted to 1; Boolean False is converted to 0.
  • A node-set is first converted to a string and then converted in the same way as a string argument.
  • An object of a type other than the four basic types (node set, Boolean, number, or string), is converted to a number in a way that is dependent on that type.

If the argument is omitted, it defaults to a node-set with the context node as its only member.

Note The function should not be used for conversion of numeric data occurring in an element in an XML document unless the element is of a type that represents numeric data in a language-neutral format (which would typically be transformed into a language-specific format for presentation to a user). In addition, the function cannot be used unless the language-neutral format used by the element is consistent with the XPath syntax for a number.

See Also

XML Data Types Reference


XSLT Functions

The XPath syntax supports functions to provide information about nodes in a collection. These functions return strings or numbers and can be used with comparison operators in filter patterns. These functions are not available from the DOM's selectNodes.

XSLT Functions

current | Returns a node-set that has the current node as its only member.
function-available | Returns True if the function is available in the function library.
element-available | Returns True if and only if the expanded-name is the name of an instruction.
format-number | Converts the first argument to a string using the format pattern string specified by the second argument.
generate-id | Returns a string that uniquely identifies the node in the argument node-set that is first in document order.
system-property | Returns an object representing the value of the system property identified by the name.


system-property Function

Returns an object representing the value of the system property identified by the name.

object system-property(string)

Remarks

The argument must evaluate to a string that is a valid QName. The QName is expanded into a name using the namespace declarations in scope for the expression. The system-property function returns an object representing the value of the system property identified by the name. If there is no such system property, the empty string is returned.

Since these properties are strings representing QNames, a prefix must correspond to a namespace declaration in scope for "http://www.w3.org/1999/XSL/Transform" for the xsl:version, xsl:vendor, and xsl:vendor-url properties. A prefix corresponding to a namespace declaration "urn:schemas-microsoft-com:xslt" must be in scope for the msxsl:version property.

System property | Description
xsl:version | A number giving the version of XSLT implemented by the processor; for XSLT processors implementing the version of XSLT specified by this document, the number 1.0.
xsl:vendor | Returns the string "Microsoft".
xsl:vendor-url | A string containing the URL "http://www.Microsoft.com".
msxsl:version | A number giving the version of MSXML, 2.6 for this release.

Example

The following function call returns 1.0:

system-property('xsl:version')

See Also

XML Data Types Reference


format-number Function

Converts the first argument to a string using the format pattern string specified by the second argument.

string format-number(number, string, string?)

Remarks

For the "format pattern" string, the following characters are supported "."; ","; "#"; and "0". The "#" character is used to describe integers. The "." and "," characters are used to describe the location and type of punctuation. The "0" character is used enforce digits before and after the "." character.

When truncating digits to the right of the decimal points, the number is rounded. The number to the left of the decimal point is never truncated.

Both parameters must be present or an error will be thrown. If the first parameter cannot be coerced to a number, the function should return NaN. If the second parameter is not a valid pattern string, the number that is the first parameter is returned as is.

Examples

The following function call returns "5,351":

format-number(5351,"#,###")

The following function call returns "5351.00":

format-number(5351, "#.00")

The following function call returns "53.5100":

format-number(53.51, "#.0000")

The following function call returns "0053.5100":

format-number(53.51, "0000.0000")

The following function call returns "0053.51":

format-number(53.51, "0000.####")

The following function call returns "53.6":

format-number(53.56, "0.0")

See Also

XML Data Types Reference


current Function

Returns a node-set that has the current node as its only member.

node-set current()

Remarks

The function returns a node-set that has the current node as its only member. For an outermost expression (an expression not occurring within another expression), the current node is always the same as the context node. Thus,

<xsl:value-of select="current()"/>

is the same as

<xsl:value-of select="."/>

However, within square brackets, the current node is usually different from the context node. For example,

<xsl:apply-templates select="//glossary/item[@name=current()/@ref]"/>

processes all <item> elements that have a <glossary> parent element and a name attribute with value equal to the value of the current node's ref attribute. This is different from

<xsl:apply-templates select="//glossary/item[@name=./@ref]"/>

which means the same as

<xsl:apply-templates select="//glossary/item[@name=@ref]"/>

and so would process all <item> elements that have a <glossary> parent element and that have a name attribute and a ref attribute with the same value.

See Also

XML Data Types Reference


element-available Function

Returns True if and only if the expanded-name is the name of an instruction.

boolean element-available(string)

Remarks

The argument must evaluate to a string that is a QName. The QName is expanded into an expanded-name using the namespace declarations in scope for the expression. This function returns True for all XSLT elements supported in this release and False for each XSLT element not supported in this release. In addition, it returns True for all extension elements supported in this release, currently only msxsl:script. Outside of XSLT, it will always return False.

Example

The following function call returns True (provided the xsl prefix is mapped to the XSLT namespace):

element-available("xsl:template")

See Also

XML Data Types Reference


generate-id Function

Returns a string that uniquely identifies the node in the argument node-set that is first in document order.

string generate-id(node-set?)

Remarks

The unique identifier must consist of ASCII alphanumeric characters and must start with an alphabetic character. Thus the string is syntactically an XML name. There is no guarantee that a generated unique identifier will be distinct from any unique IDs specified in the source document. If the argument node-set is empty, the empty string is returned. If the argument is omitted, it defaults to the context node.

See Also

XML Data Types Reference


function-available Function

Returns True if the function is in the function library.

boolean function-available(string)

Remarks

The argument must evaluate to a string that is a QName. The QName is expanded into an expanded-name using the namespace declarations in scope for the expression. The function-available function returns True if and only if the expanded-name is the name of a function in the function library. If the expanded-name has a non-null namespace URI, it refers to an extension function; otherwise, it refers to a function defined by XPath or XSLT.

Example

The following function call returns True:

function-available("format-number")

See Also

XML Data Types Reference


Sample Data

This topic offers the sample data used by examples throughout this documentation.

<?xml version="1.0"?>
<!-- This file represents a fragment of a bookstore inventory database -->
<bookstore specialty="novel">
  <book style="autobiography">
    <author>
      <first-name>Joe</first-name>
      <last-name>Bob</last-name>
      <award>Trenton Literary Review Honorable Mention</award>
    </author>
    <price>12</price>
  </book>
  <book style="textbook">
    <author>
      <first-name>Mary</first-name>
      <last-name>Bob</last-name>
      <publication>Selected Short Stories of
        <first-name>Mary</first-name>
        <last-name>Bob</last-name>
      </publication>
    </author>
    <price>55</price>
  </book>
  <magazine style="glossy" frequency="monthly">
    <price>2.50</price>
    <subscription price="24" per="year"/>
  </magazine>
  <book style="novel" id="myfave">
    <author>
      <first-name>Toni</first-name>
      <last-name>Bob</last-name>
      <degree from="Trenton U">B.A.</degree>
      <degree from="Harvard">Ph.D.</degree>
      <award>Pulitzer</award>
      <publication>Still in Trenton</publication>
      <publication>Trenton Forever</publication>
    </author>
    <price intl="Canada" exchange="0.7">6.50</price>
    <excerpt>
      <p>It was a dark and stormy night.</p>
      <p>But then all nights in Trenton seem dark and
      stormy to someone who has gone through what
      <emph>I</emph> have.</p>
      <definition-list>
        <term>Trenton</term>
        <definition>misery</definition>
      </definition-list>
    </excerpt>
  </book>
  <my:book xmlns:my="uri:mynamespace" style="leather" price="29.50">
    <my:title>Who's Who in Trenton</my:title>
    <my:author>Robert Bob</my:author>
  </my:book>
</bookstore>

See Also

XPath Examples | XPath Syntax


XPath Examples

This topic recaps the examples that appear throughout this documentation. All are based on the provided Sample Data.

Find all author elements within the current context:

./author

Note that this is equivalent to:

author

Find all first.name elements:

first.name

Find the document element (bookstore) of this document:

/bookstore

Find all author elements that are children of the current context node (e.g. /bookstore/book):

//author

Find all bookstores where the value of the specialty attribute is equal to "textbooks":

/bookstore[@specialty = "textbooks"]

Find all books where the value of the style attribute on the book is equal to the value of the specialty attribute of the bookstore element at the root of the document:

book[/bookstore/@specialty = @style]

Find all first-name elements within an author element. Note that the author children of the current context are found, and then first-name children are found relative to the context of the author elements.

author/first-name

Find all title elements one or more levels deep in the bookstore (arbitrary descendants):

bookstore//title

Note that this is different from the following pattern, which finds all title elements that are grandchildren of bookstore elements:

bookstore/*/title

Find emph elements anywhere inside book excerpts, anywhere inside the bookstore:

bookstore//book/excerpt//emph

Find all titles one or more levels deep in the current context. Note that this situation is essentially the only one in which the period notation is required.

.//title

Find all element children of author elements:

author/*

Find all last names that are grandchildren of books:

book/*/last-name

Find the grandchildren elements of the current context:

*/*

Find the book element from the "my" namespace:

my:book

Find all elements from the "my" namespace:

my:*

Find all elements with the specialty attribute.

*[@specialty]

Find the style attribute of the current element context:

@style

Find the exchange attribute on price elements within the current context:

price/@exchange

The following example does not return anything, since attributes do not contain element children." It is allowed by the XPath grammar, but not strictly invalid:

price/@exchange/total

Find all books with style attributes:

book[@style]

Find the style attribute for all book elements:

book/@style

Find all attributes of the current element context:

@*

Find all attributes from the "my" namespace. This does not include unqualified attributes on elements from the "my" namespace.

@my:*

Find all first-name elements. These examples are equivalent:

./first-name
first-name

Find all unqualified book elements:

book

For example, the following finds the first author element:

author[1]

The following finds the third author element that has a first-name:

author[first-name][3]

Note that indexes are relative to the parent. In other words, consider the following data:

<x>
  <y/>
  <y/>
</x>
<x>
  <y/>
  <y/>
</x>

Find the first y from each x:

x/y[1]
x/y[position() = 1]

Find the first y from the entire set of y elements within x elements:

(x/y)[1]

Find the first y from the first x:

x[1]/y[2]

Find the last book:

book[last()]

Find the last author for each book:

book/author[last()]

Find the last author from the entire set of authors of books:

(book/author)[last()]

Find all books that contain at least one excerpt element:

book[excerpt]

Find all titles of books that contain at least one excerpt element:

book[excerpt]/title

Find all authors of books where the book contains at least one excerpt and the author has at least one degree:

book[excerpt]/author[degree]

Find all books that have authors with at least one degree:

book[author/degree]

Find all books that have an excerpt and a title:

book[excerpt][title]

Find all author elements that contain at least one degree and one award:

author[degree and award]

Find all author elements that contain at least one degree or award and at least one publication:

author[(degree or award) and publication]

Find all author elements that contain at least one degree element and that contain no publication elements:

author[degree and not(publication)]

Find all author elements that contain publication elements but do not contain either degree elements or award elements:

author[not(degree or award) and publication]

Find all author elements that contain a last-name element with the value Bob:

author[last-name = "Bob"]

Find all author elements where the first last-name is Bob:

author[last-name[1] = "Bob"]
author[last-name = "Bob"]

Find all authors where the from attribute is not equal to "Harvard":

degree[@from != "Harvard"]

Find all authors where the last name is the same as the /guest/last-name element (note that this assumes there is only one last-name; see Set Operations):

author[last-name = /guest/last-name]

Find all authors whose text is "Matthew Bob":

author[. = "Matthew Bob"]

Find all author elements whose last name is "Bob" and whose price is > 50 (note that this assumes there is only one last-name and price for an author; see Set Operations):

author[last-name = "Bob" and price > 50]

Find all authors whose last name begins with "M" or greater:

author[last-name > "M"]

When an author can have several last names in the schema (such as Clemens and Twain), use the following patterns:

author[all last-name >= "M"]

Find the first three books (1, 2, 3):

book[index() <= 3]

Find all author elements where any one of the last names is Bob:

author[last-name = "Bob"]

Find all author elements where none of the last-name elements are Bob:

author[not(last-name = "Bob")]

Find all author elements containing a first-name child whose text is "Bob" (note that this and following examples assume there is only one first-name child for an author):

author[first-name = "Bob"]

Find all author elements containing any child element whose text is "Bob":

author[* = "Bob"]

Find author elements equal to "Joe Bob":

author[last-name = "Bob" and first-name = "Joe"]

Find the intl attribute equal to "Canada":

price[@intl = "Canada"]

Find the first 3 degrees:

degree[position() < 3]

Find the second text node in each p element in the current context:

p/text()[2]

Find the nearest book ancestor of the current element:

ancestor::book[1]

Find the nearest ancestor author element that is contained in a book element:

ancestor::book[author][1]

To illustrate unions, consider the following data:

<root>
  <x/> <!-- green -->
  <y>
    <x/> <!-- blue -->
    <x/> <!-- blue -->
  </y>
  <z>
    <x/>
    <x/>
  </z>
  <x/> <!-- green -->
</root>

Find both green and blue nodes:

x | y/x

See Also

Sample Data


Qualified Names

A qualified name (QName) is composed of a prefix and a local part. The prefix provides the namespace prefix part of the qualified name, and must be associated with a namespace URI.

Note that the prefix functions only as a placeholder for a namespace name. Applications should use the namespace name, not the prefix, in constructing names whose scope extends beyond the containing document.


NaN Values

The term NaN, which stands for "not a number," means a symbolic entity that represents a value not otherwise available in floating-point format.

There are two kinds of NaNs:

  • Quiet NaNs represent unknown or uninitialized values.
  • Signaling NaNs represent symbolic values and values that are too big or too precise for the format. Signaling NaNs raise an invalid operation exception whenever an operation is attempted on them.


Expressions

Expressions are used in XSLT for a variety of purposes including:

  • Selecting nodes for processing
  • Specifying conditions for different ways of processing a node
  • Generating text to be inserted in the result tree

Expressions occur as the value of certain attributes on XSLT-defined elements and within curly braces in attribute value templates.

Expression evaluation occurs with respect to a context. In XSLT, an outermost expression (that is, an expression that is not part of another expression) gets its context as follows:

  • The context node comes from the current node.
  • The context position comes from the position of the current node in the current node list; the first position is 1.
  • The context size comes from the size of the current node list.
  • The variable bindings are the bindings in scope on the element that has the attribute in which the expression occurs.
  • The set of namespace declarations are those in scope on the element that has the attribute in which the expression occurs.
  • The function library consists of those found at XPath Functions; it is an error for an expression to include a call to any other function.

See Also

Patterns


Patterns

A pattern is a type of expression that returns a set of nodes based on a set of conditions for a node. Nodes that satisfy the conditions match the pattern. The syntax for patterns is a subset of the syntax for expressions. In particular, Location Paths that meet certain restrictions can be used as patterns. An expression that is also a pattern always evaluates to an object of type node-set. A node matches a pattern if the node is a member of the result of evaluating the pattern as an expression with respect to some possible context; the possible contexts are those whose context node is the node being matched or one of its ancestors.

The following table lists examples of patterns.

Pattern | Matches
para | any para element
* | any element
chapter|appendix | any chapter element and any appendix element
olist/item | any item element with an olist parent
appendix//para | any para element with an appendix ancestor element
/ | the root node
text() | any text node
processing-instruction() | any processing instruction
node() | any node other than an attribute node and the root node
id("W11") | the element with unique ID W11
para[1] | any para element that is the first para child element of its parent
*[position()=1 and self::para] | any para element that is the first child element of its parent
para[last()=1] | any para element that is the only para child element of its parent
items/item[position()>1] | any item element that has a items parent and that is not the first item child of its parent
item[position() mod 2 = 1] | any item element that is an odd-numbered item child of its parent
div[@class="appendix"]//p | any p element with a div ancestor element that has a class attribute with value appendix
@class | any class attribute (not any element that has a class attribute)
@* | any attribute


XML and XSL Samples

The XML SDK contains several sample applications that demonstrate usage of XML and XSL code. For more information about these samples, see XML Sample Applications.

The XML documentation also contains code examples in multiple languages, which are included as topics within this online help. For more information about these examples, see XML Code Examples.

The following XML and XSL samples also are available for download on MSDN Online:

  • XML Samples
  • XSL Samples

    For XSLT versions of these samples, see the MSDN Code Center.

XML Samples | XSL Samples
ADO 2.5 Streams and XML | ASP Error Message Demo (No Errors)
ADO, ASP and XML | ASP Error Message Demo (XML Source Parsing Error)
Browsing XML | ASP Error Message Demo (XSL Run-time Error)
Class Schedule | ASP Error Message Demo (XSL Script Error)
MFC Tree Control | ASP Error Message Demo (XSL Style Sheet Parsing Error)
Server-side XML Demos | Breakfast Menu Demo (Active Server Pages)
SQL Server XML Technology Preview | Breakfast Menu Demo (Data Islands)
VBOrder | Downloadable Default Style Sheet
XML and C++ Samples | Enhanced Grocery List Sample
XML Data Files | Error Message Demo (No Errors)
XML Data Islands | Error Message Demo (XML Source Parsing Error)
XML Data Source Object | Error Message Demo (XSL Run-time Error)
XML HTTP | Error Message Demo (XSL Script Error)
XML Order | Error Message Demo (XSL Style Sheet Parsing Error)
XML RDF Viewer | Grocery List Sample
XML Shopping Cart | Invoice Sample
XML Tree Viewer (Script) | Ledger Display Sample
XML Tree Viewer (VB) | Portfolio Sample
XML Validator | Portfolio Sample (Ascending Sort)
XML/XSL Viewer | Portfolio Sample (Attributes)
XSL Debugger | Portfolio Sample (Descending Sort)
XSL Pattern Matching | Portfolio Sample (Embedded HTML)
XSL Visual Pattern Builder | Portfolio Sample (Show Exchange)
 | Portfolio Sample (Show Price Range)
 | Product List (Commas) Sample
 | Product List (Table) Sample
 | Review Error Sample (Mismatched Case)
 | Review Sample (CSS)
 | Review Sample (Embedded CSS)
 | Review Sample (XSL)
 | Schema Translation Demo
 | Simple XSL Pattern Demo
 | Stock Sorter Sample
 | Stock Sorter Sample (VB)
 | Style Sheet Converter
 | To The Pole Sample
 | To The Pole Sample (with Chapter Numbers)
 | To The Pole Sample (with Subsections)
 | To The Pole Sample (with TOC)
 | Transformation Viewer
 | XML Element Renaming Demo
 | XML Sorting Demo
 | XML/XSL Viewer
 | XSL Working Draft Sample
 | XSL Working Draft Table of Contents Sample


XML Sample Applications

This version of the XML SDK includes the Merge XML Fragments sample application.


Merge XML Fragments

The following sample appends nodes from an XML fragment to another XML fragment.

<XML ID="file1">
   <COLLECTION xmlns:dt="urn:schemas-microsoft-com:datatypes">
     <DATE dt:dt="datetime">1998-10-13T15:56:00</DATE>
     <BOOK>
       <TITLE>Cosmos</TITLE>
       <AUTHOR>Carl Sagan</AUTHOR>
       <PUBLISHER>Ballantine Books</PUBLISHER>
    </BOOK>
    <BOOK>
       <TITLE>Catwings</TITLE>
       <AUTHOR>Ursula K. Le Guin</AUTHOR>
       <PUBLISHER>Scholastic</PUBLISHER>
    </BOOK>
   </COLLECTION>
</XML>
<xml id="file2">
   <COLLECTION>
    <BOOK>
       <TITLE>Inside MSXML</TITLE>
       <AUTHOR>Microsoft</AUTHOR>
       <PUBLISHER>The MS Press</PUBLISHER>
    </BOOK>
    <BOOK>
       <TITLE>Home Town</TITLE>
       <AUTHOR>Tracy Kidder</AUTHOR>
       <PUBLISHER>Random House</PUBLISHER>
    </BOOK>
   </COLLECTION>
</xml>
<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
<TITLE></TITLE>
</HEAD>
<script language="vbscript" id="mergeFunction">
Dim xmldoc
Dim xmldoc2

' First document.
Set xmldoc = file1.XMLDocument
' Second document.
Set xmldoc2 = file2.XMLDocument

function merge_xml()
   Dim iIndex
   Dim xmlNode
   Dim NodeList

   Set NodeList = xmldoc2.selectNodes("*/BOOK")
   ' Merge.

   For iIndex = 0 To NodeList.length - 1
      Set xmlNode = NodeList.Item(iIndex).cloneNode(True)
      xmldoc.documentElement.appendChild xmlNode
   Next
end function
</script>
<script language="vbscript">
function mergeAndShow()
   merge_xml()
   showDoc(1)
   Showfile1.value="Show Merged File"
end function

function showDoc(iIndex)
   select case iIndex
      case 1 GeneralDisplayBox.value=xmldoc.xml
      case 2 GeneralDisplayBox.value=xmldoc2.xml
      case else GeneralDisplayBox.value=""
   end select

end function
</script>
<BODY>
<PRE>
<script language="vbscript">
   document.writeln document.scripts.item("mergefunction").text
</script>
</PRE>
<TABLE border='2'>
   <TR>
      <TD>
         <TEXTAREA title='General Display' id='GeneralDisplayBox' rows='20' cols='60' readonly></TEXTAREA>
      </TD>
      <TD width='90'>
         <TABLE>
            <TR align=center>
               <TD>
                  <input id='Showfile1' type='button' value="Show File1" onclick="showDoc(1)" />
               </TD>
            </TR>
            <TR align=center>
               <TD>
                  <input type='button' value="Show File2" onclick="showDoc(2)" />
               </TD>
            </TR>
            <TR align=center>
               <TD>
                  <input type='button' value="MergeFiles" onclick="mergeAndShow()"/>
               </TD>
            </TR>
         </TABLE>
      </TD>
   </TR>
</TABLE>
<P>&nbsp;</P>
</BODY>
</HTML>


XML Code Examples

Use the following code examples to learn how to use the XML objects, properties, methods, and events.

  • Code Examples in Microsoft JScript


XML Code Examples in Microsoft JScript

Microsoft XML SDK 2.6 - XML and XSL Samples

Code Examples in Microsoft JScript

Use the following code examples to learn how to use XML objects and interfaces when writing in Microsoft® JScript®.

  • XSLTemplate Examples
  • IXMLDOMSelection Examples


XSLTemplate Examples

This topic has several examples that demonstrate how to use the XSLTemplate object.

Typical Caching Scenario

The initial setup of the XSL template goes into an ASP Application state:

<%
var xsldoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument");
xsldoc.async = false;
xsldoc.load("someStylesheet.xsl");
var myTemplate.stylesheet = xsldoc;
Application("template") = myTemplate;
%>

Each client user ASP (which could be hundreds of simultaneous users) reuses the previously loaded template using rental-model objects, as follows:

<%
var xmldoc = new ActiveXObject("Msxml2.DOMDocument");
xmldoc.async = false;
xmldoc.load("someData.xml");

var myProc = Application("template").createProcessor();
myProc.input = xmldoc;
myProc.output = Response; //write response to client
myProc.transform();
%>

Then, a client administrator ASP can also safely do a "live" update of the style sheet while the user ASPs are running, as follows:

<%
var newxsldoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument");
newxsldoc.async = false;
newxsldoc.load("SomeNewStyleStyleSheet.xsl");
var myTemplate = Application("template");
myTemplate.stylesheet = newxsldoc;
%>

ADO

This example connects an ADO Recordset with a cached XSL template and sends the result directly down the wire:

<%
var xslprocessor = Application("xsltemplate").createProcessor();
xslprocessor.input = recordset; //take input from ADO recordset
xslprocessor.output = Response; //send result to the client
xslprocessor.transform();
%>

Asynchronous Transformations

The following example shows how to accomplish an asynchronous transformation:

var xmldoc; //XMLDOM document for downloading data
var XSLProcessor; //XSL transformation engine
var XSLOutput; //HTML window to write results to. This function
// will kick off the data download and wire up the async callbacks
// that Msxml makes during the download. XSLTemplate is the compiled
// stylesheet.

function NewXMLWindow(dataURL, XSLTemplate)
{
xmldoc = new ActiveXObject("Msxml2.DOMDocument");
xmldoc.ondataavailable = TransfromChunk;
xmldoc.onreadystatechange = TransformChunk;
XSLProcessor = XSLTemplate.createProcessor();
XSLProcessor.input = xmldoc;
xmldoc.async = true;
xmldoc.load(dataUrl);

XSLOutput = window.open("XSLOutput","","resizable,scrollbars");
XSLOutput.title = "XSL Output";
XSLOutput.document.open();
return XSLOutput;
}
// This is the async callback. Now you can transform the next chunk
// (as much as is possible, based on how much data was downloaded).
// You can then get the chunk of transformed output and write it to
// an HTML frame that incrementally displays the results.

function TransformChunk()
{
XSLProcessor.transform();
var chunk = XSLProcessor.output;
XSLOutput.document.write(chunk);
// If you're done downloading the data, close
// the HTML document so that it knows you are done.
if (xmldoc.readyState == 4)
   XSLOutput.document.close();
}

Passing a Parameter

Using a variable in XSL:

<xsl:stylesheet xmlns:xsl="...">
<xsl:param name="myBaseName" />
...
<xsl:value-of select="$myBaseName" />
...
</xsl:stylesheet>

Using a variable in JScript:

var myVariable = 5;
//...load XML, XSL, and create xslTemplate here
var myProc = myTemplate.createProcessor():
myProc.input = xmldoc;
myProc.addParameter(myVariable, "myBaseName");
//now you can perform the transformation.

Passing Objects

Passing an object in XSL:

<xsl:stylesheet xmlns:xsl="..." xmlns:myObjNSPrefix="myObjURI">
...
<xsl:value-of select="myObjNSPrefix:someMethod()" />
<xsl:value-of select="myObjNSPrefix:get-someProperty()" />
...
<msxsl:script implements-prefix="foo">
   myObjNSPrefix.someMethod();
   myObjNSPrefix.someProperty = "bar";
<msxsl:script>
</xsl:stylesheet>

Passing an object in script:

function someMethod()
{
...
}

var myObj = new Object;
myObj.someMethod = someMethod;
myObj.someProperty = 123;

//...load XML, XSL, and create xslTemplate
var myProc = myTemplate.createProcessor();
myProc.input = xmldoc;
myProc.addObject(myObj, "myObjURI");
//...now do the transformation...


IXMLDOMSelection Examples

Typical XPath Caching Scenario

Set up the compiled XPath expression object:

xmldoc.setProperty("SelectionLanguage", "XPath")
var xpath = xmldoc.selectNodes("foo/morefoo/evenmorefoo");

Next, load an XML document:

xmldoc.async = false;
xmldoc.load ("http://server/someXML.xml");

Finally, apply the XPath selection to the entire document tree and walk the matching nodes:

xpath.context = xmldoc; //this is redundant in this case
var node;
while ((node = xpath.nextNode()) != NULL)
{
   //do something with each node here
}


About Advanced Full-Text Search

Advanced full-text search allows you to search using Boolean, wildcard, and nested expressions. You can also limit the search to previous results, match similar words, or search topic titles only.

Click the following links to learn more about full-text search:

  • Focusing Your Search
  • Searching for Help Topics
  • Finding Information Using Advanced Full-Text Search
  • Searching for Words or Phrases
  • Defining Search Terms
  • Using Nested Expressions When Searching

Focusing Your Search

When an entire collection of documents is accessible through HTML Help (as evidenced by the Define Subset option on the View menu), searching can produce too many results. To restrict the index and search results, define a subset to your current collection.

To define a subset:

  1. On the View menu, choose Define Subset. The Define Subset dialog box appears.
  2. From the list of available items presented in the left hand pane, select those that you want to be included in the new subset. Use the Add and Remove buttons to build the subset.
  3. When your subset of help topics is complete, type in a name in the Save new subset as box and click Save.
  4. Return to HTML Help and select the subset in the Active Subset box.

You can modify a subset at anytime by using the Define Subset menu item.

Searching for Help Topics

A basic search consists of the word or phrase you want to find. You can use wildcard expressions, nested expressions, Boolean operators, similar word matches, a previous results list, or topic titles to further define your search.

The basic rules for formulating queries are as follows:

  • Searches are not case-sensitive, so you can type your search in uppercase or lowercase characters.
  • You can search for any combination of letters (a-z) and numbers (0-9).
  • Punctuation marks such as the period, colon, semicolon, and comma are ignored during a search.
  • Hyphens used in the search string are treated as a word break and return results differently than other punctuation remarks. For example, the search phrase "forward-only" will return results matching "forward-only" and "only" but not "forward". The phrase "-only" returns "-only" and "only".
  • The period, when used in a numeric string, is treated as a decimal and the number is returned. For example "2.6.2345.2", will return the instances of the number, and "version 2.6" will return only the quoted string. When mixing characters and numbers without quotation marks, all combinations are returned.
  • Group the elements of your search using double quotes or parentheses to set apart each element. You cannot search for quotation marks.

    Note If you are searching for a file name with an extension, you should group the entire string in double quotes, like so: "filename.ext". Otherwise, the period will break the file name into two separate terms.

Return to top of page

Finding Information Using Advanced Full-Text Search

  1. Click the Search tab, and then type the word or phrase you want to find.
  2. Click the arrow button to add a Boolean operator (AND, OR, NEAR, NOT) to your search.
  3. Click List Topics and then double-click the topic you want to view, or select the desired topic and click Display.
  4. To sort the topic list, click the Title, Location, or Rank column heading.

Notes

  • You can precisely define a search by using wildcard expressions, nested expressions, and Boolean operators.
  • You can request similar word matches, search only the topic titles, or search the results of a previous search.

Return to top of page

Searching for Words or Phrases

You can search for words or phrases and use wildcard expressions. These expressions allow you to search for one or more characters by using a question mark (?) or asterisk (*). The following table describes the results of different kinds of searches.

Search | Example | Results
For a single word | select | Topics that contain the word "select." (You will also find grammatical variations, such as "selector" and "selection.")
For a phrase | "new operator" -or- new operator | Topics that contain the literal phrase "new operator" and all its grammatical variations. Without the quotation marks, the query is equivalent to specifying "new AND operator," which will find topics containing both of the individual words, instead of the designated phrase.
Using wildcard expressions | esc* -or- 80?86 | Topics that contain the terms "ESC," "escape," "escalation," and so on. The asterisk cannot be the only character in the term. Topics that contain the terms "80186," "80286," "80386," and so on. The question mark cannot be the only character in the term.

Note Select the Match similar words check box at the bottom of the pane to return minor grammatical variations of a phrase.

Return to top of page

Defining Search Terms

The AND, OR, NOT, and NEAR operators enable you to precisely define your search by creating a relationship between search terms. The following table shows how you can use each of these operators. If no operator is specified, AND is used. For example, the query "spacing border printing" (typed without quotes) is equivalent to "spacing AND border AND printing."

Search for | Example | Results
Both terms in the same topic | dib AND palette | Topics containing both the words "dib" and "palette."
Either term in a topic | raster OR vector | Topics containing either the word "raster" or the word "vector" or both.
The first term without the second term | ole NOT dde | Topics containing the term "OLE," but not the term "DDE."
Both terms in the same topic, close together | user NEAR kernel | Topics containing the word "user" within eight words of the word "kernel."

Note The |, &, and ! characters don't work as Boolean operators. You must use OR, AND, and NOT.

Return to top of page

Using Nested Expressions When Searching

Nested expressions allow you to create complex searches for information. For example, "control AND ((active OR dde) NEAR window)" finds topics containing the word "control" along with the words "active" and "window" close together, or containing "control" along with the terms "DDE" and "window" close together.

The basic rules for searching help topics using nested expressions are as follows:

  • You can use parentheses to nest expressions within a query. The expressions in parentheses are evaluated before the rest of the query.
  • If a query does not contain a nested expression, it is evaluated from left to right. For example, "control NOT active OR dde" finds topics containing the word "control" without the word "active," or finds topics containing the term "DDE." On the other hand, "control NOT (active OR dde)" finds topics containing the word "control" without either of the terms "active" or "DDE."
  • You cannot nest expressions more than five levels deep.

Return to top of page


absolute URL <definition>

ADO Glossary

absolute URL

A fully qualified URL that specifies the location of a resource that resides on the Internet or an intranet. See also URL and relative URL.


ActiveX control <definition>

ADO Glossary

ActiveX control

Self-registering, in-process COM component that often has a visual element either at design time or run time. ActiveX controls also have the ability to communicate with an Active Document container, such as Microsoft Internet Explorer.


ADISAPI <definition>

ADO Glossary

ADISAPI (Advanced Data Internet Server Application Programming Interface)

An ISAPI DLL that provides parsing, Automation control, Recordset marshaling, and MIME packaging. The ADISAPI component works through the API provided by Internet Information Services (IIS). See also ISAPI.


aggregate function <definition>

ADO Glossary

aggregate function

In a query, a function such as COUNT, AVG, or STDEV that calculates a value using all the rows in a column of a table. In writing expressions and in programming, you can use SQL aggregate functions (including the three listed above) and domain aggregate functions to determine various statistics.


alias <definition>

ADO Glossary

alias

An alternate name you give to a column or expression in an SQL SELECT statement, often shorter or more meaningful. For example, BobSales is the alias in the following SELECT statement: "Select wr-Sales as BobSales from SalesDB". An alias can be used to dynamically assign columns to control bindings on the DataControl object.


apartment thread <definition>

ADO Glossary

apartment threading

A COM threading model where all calls to an object occur on one thread. In apartment threading, COM synchronizes and marshals calls. See also COM.


asynchronous operation <definition>

ADO Glossary

asynchronous operation

An operation that returns control to the calling program without waiting for the operation to complete. Before the operation is complete, code execution continues. See also synchronous operation.


binding entry <definition>

ADO Glossary

binding entry

A mapping between a field in a table and a variable. In the ADO Visual C++ extensions, Recordset fields are mapped to C/C++ variables.


bitmask <definition>

ADO Glossary

bitmask

A numeric value intended for a bit-by-bit value comparison with other numeric values, typically to flag options in parameter or return values. Usually this comparison is done with bitwise logical operators, such as And and Or in Visual Basic, & and | in C++.

For example, the ADO FieldAttributeEnum values can be used as bitmasks to determine the attributes of a field. Suppose you wanted to determine if a field was updatable. You could test for this with the following expression in Visual Basic:

Field.Attributes AND adFldUpdatable

If the result is TRUE, then the field is updatable.


bookmark <definition>

ADO Glossary

bookmark

A marker that uniquely identifies a row within a set of rows so that a user can quickly navigate to it.


business object <definition>

ADO Glossary

business object

An object that performs a defined set of operations, such as data validation or business rule logic. Business objects usually reside on the middle tier.


business rule <definition>

ADO Glossary

business rule

The combination of validation edits, logon verifications, database lookups, policies, and algorithmic transformations that constitute an enterprise's way of doing business. Also known as business logic.


calculated expression <definition>

ADO Glossary

calculated expression

An expression that is not constant, but whose value depends upon other values. To be evaluated, a calculated expression must obtain and compute values from other sources, typically in other fields or rows.


chapter <definition>

ADO Glossary

chapter

A reference to a range of rows from a data source. In ADO, a chapter is typically a reference to another Recordset.

Chapter columns make it possible to define a parent-child relationship where the parent is the Recordset containing the chapter column and the child is the Recordset represented by the chapter.


chapter-alias <definition>

ADO Glossary

chapter-alias

An alias that refers to the column appended to the parent.


character set <definition>

ADO Glossary

character set

A mapping of a set of characters to their numeric values. For example, Unicode is a 16-bit character set capable of encoding all known characters and used as a worldwide character-encoding standard.


child <definition>

ADO Glossary

child

The dependant side of a hierarchical relationship. A child is a node in a hierarchical stucture that has another node above it (closer to the root). See also child-alias, parent-child relationship, parent.


child-alias <definition>

ADO Glossary

child-alias

An alias that refers to the child. See also alias, child.


CLSID (class identifier) <definition>

ADO Glossary

CLSID (class identifier)

A universally unique identifier (UUID) that identifies a COM component. Each COM component has its CLSID in the Windows Registry so that it can be loaded by other applications. See also ProgID, COM.


client tier <definition>

ADO Glossary

client tier

A logical layer of a distributed system that typically presents data to and processes input from the user, sometimes referred to as the front end. Usually, the client tier requests data from a server based on input, and then formats and displays the result. See also middle tier, data source tier, distributed application.


COM (Component Object Model) <definition>

ADO Glossary

COM (Component Object Model)

A binary standard that enables objects to interoperate in a networked environment regardless of the language in which they were developed or on which computers they reside. COM-based technologies include ActiveX Controls, Automation, and object linking and embedding (OLE). COM allows an object to expose its functionality to other components and to host applications. It defines both how the object exposes itself and how this exposure works across processes and across networks. COM also defines the object's life cycle.


COM Component <definition>

ADO Glossary

COM component

Binary file — such as .dll, .ocx, and some .exe files — that supports the COM standard for providing objects. Such a file contains code for one or more class factories, COM classes, registry-entry mechanisms, loading code, and so on.


comparison operator <definition>

ADO Glossary

comparison operator

An operator that compares two expressions and returns a Boolean value.

A criteria parameter that may be expressed as ">" (greater than), "<" (less than), "=" (equal), ">=" (greater than or equal), "<=" (less than or equal), "<>" (not equal), or "like" (pattern matching).


component <definition>

ADO Glossary

component

An object that encapsulates both data and code, and provides a well-specified set of publicly available services.


compound file <definition>

ADO Glossary

compound file

An implementation of COM structured storage for files. A compound file stores separate objects in a single, structured file consisting of two main elements: storage objects and stream objects. Together, they function like a file system within a file. For more information, see Compound Files in the Microsoft® Platform SDK.

A number of individual files bound together in one physical file. Each individual file in a compound file can be accessed as if it were a single physical file.


constant <definition>

ADO Glossary

constant

A numeric or string value that does not change. Named ADO enumerations (enumerated constants) can be used in your code instead of actual values, for example, adUseClient is a constant whose value is 3. (Const adUseClient = 3). See also enumeration.


cursor <definition>

ADO Glossary

cursor

A database element that controls record navigation, updatability of data, and the visibility of changes made to the database by other users.


data binding <definition>

ADO Glossary

data binding

The process of associating the objects or controls of an application to a data source. A control associated with a data source is called a data-bound control.

The contents of a data-bound control are associated with values from a database. For example, a grid control that is bound to a Recordset object can be updated when the rows in the Recordset are updated. When new values are retrieved by the Recordset, new values are displayed in the grid.


data provider <definition>

ADO Glossary

data provider

Software that exposes data to an ADO application either directly or via a service provider. See also service provider.


data shaping <definition>

ADO Glossary

data shaping

A technique which makes use of a formalized syntax (called Shape language) to define a specialized Recordset object (called a shaped Recordset) that contains not just data, but also references to other Recordset objects and/or computed values based on those other Recordset objects.


DCOM <definition>

ADO Glossary

DCOM

A wire protocol that enables COM components to communicate directly with each other across a network. See also COM, component.


DDL (Data Definition Language) <definition>

ADO Glossary

DDL (Data Definition Language)

Those statements in SQL that define, as opposed to manipulate, data. The schema of a database is created or modified with DDL. For example, CREATE TABLE, CREATE INDEX, GRANT, and REVOKE are SQL DDL statements.


default stream <definition>

ADO Glossary

default stream

A text or binary stream (represented by a Stream object) that is associated with Record or Recordset objects when using certain OLE DB providers, such as the Microsoft OLE DB Provider for Internet Publishing. The default stream typically contains the contents of a file such as the HTML code for the root of a Web site.


disconnected recordset <definition>

ADO Glossary

disconnected Recordset

A Recordset object in a client cache that no longer has a live connection to the server. If the original data source needs to be accessed again for some reason, such as updating data, the connection must be re-established. However, the collections, properties, and methods of a disconnected Recordset can still be accessed.


distributed application <definition>

ADO Glossary

distributed application

A program written so that the processing can be divided across multiple computers over a network. Typically, a distributed application is divided into presentation, business logic, and data store layers, or tiers. See also client tier, middle tier, data source tier.


DLL (dynamic-link library) <definition>

ADO Glossary

DLL (dynamic-link library)

A file that contains one or more functions that are compiled, linked, and stored separately from the processes that use them. The operating system maps the DLLs into the address space of the calling process when the process is starting, or while it is running.


DML (Data Manipulation Language) <definition>

ADO Glossary

DML (Data Manipulation Language)

Those statements in SQL that manipulate, as opposed to define, data. The values in a database are selected and modified with DML. For example, INSERT, UPDATE, DELETE, and SELECT are SQL DML statements.


document source provider <definition>

ADO Glossary

document source provider

A special class of providers that manage folders and documents. When a document is represented by a Record object, or a folder of documents is represented by a Recordset object, the document source provider populates those objects with a unique set of fields that describe characteristics of the document, instead of the actual document itself. See also resource record.


DSN (data source name) <definition>

ADO Glossary

DSN (data source name)

The collection of information used to connect your application to a particular ODBC database. The ODBC Driver Manager uses this information to create a connection to the database. A DSN can be stored in a file (a file DSN) or in the Windows Registry (a machine DSN).


data source tier <definition>

ADO Glossary

data source tier

A logical layer of a distributed system that represents a computer running a DBMS, such as an SQL Server database. See also client tier, middle tier, distributed application.


dynamic property <definition>

ADO Glossary

dynamic property

A property specific to a data provider or the cursor service. The Properties collection of an object is populated with these automatically ("dynamically"). An object has no dynamic properties until it is connected to a data source through a particular data provider. See also data provider, cursor.


enumeration <definition>

ADO Glossary

enumeration

A list of named constants. Enumerated values need not be unique. However the name of each value must be unique within the scope where the enumeration is defined. In ADO, enumerations are used for numeric parameter and return values, to add meaning to ADO code and to shield the developer from the numeric values (which may change from version to version). For example, to open a static Recordset, use the adOpenStatic enumerated value:

Recordset.Open ,,adOpenStatic

Also referred to as enumerated constant. See also constant.


event <definition>

ADO Glossary

event

An action recognized by an object, for which you can write code to respond. Events can be generated by command execution, transaction completion, recordset navigation, and data updates, among other actions. See also event handler.


event handler <definition>

ADO Glossary

event handler

An event handler is the code that is executed when an event occurs. See also event.


handler <definition>

ADO Glossary

handler

A routine that manages a common and relatively simple condition or operation, such as error recovery or data management.


hierarchical Recordset <definition>

ADO Glossary

hierarchical Recordset

A Recordset that contains another Recordset. See also data shaping, chapter.

For more information, see Accessing Rows in a Hierarchical Recordset


hierarchy <definition>

ADO Glossary

hierarchy

In general, a hierarchy is a ranked structure with a top level and subordinate levels. In ADO, hierarchical Recordsets are used to represent the parent-child relationship between a record and a chapter. Also in ADO, Record and Stream objects can be used to access hierarchical tree structures such as a folder and documents. ADO MD also includes Hierarchy objects to represent a relationship between the levels of a dimension in an OLAP cube. See also hierarchical Recordsets, parent-child relationship, chapter, tree.


ISAPI <definition>

ADO Glossary

ISAPI (Internet Server Application Programming Interface)

A set of functions for Internet servers, such as a Windows NT® Server/Windows 2000 Server running Microsoft® Internet Information Services (IIS).


key <definition>

ADO Glossary

key

A column or columns in a table that uniquely identify a row; often used to index a table.


marshaling <definition>

ADO Glossary

marshaling

The process of packaging, sending, and unpackaging interface method parameters across thread or process boundaries.


middle tier <definition>

ADO Glossary

middle tier

The logical layer in a distributed system between a user interface or Web client and the database. This is typically where business objects are instantiated. The middle tier is a collection of business rules and functions that generate and operate upon receiving information. They accomplish this through business rules, which can change frequently, and are thus encapsulated into components that are physically separate from the application logic itself. Also known as application server tier. See also distributed application, client tier, data source tier.


MIME <definition>

ADO Glossary

MIME (Multi-purpose Internet Mail Extension)

An Internet protocol originally developed to allow exchange of electronic mail messages with rich content across heterogeneous network, machine, and e-mail environments. In practice, MIME has also been adopted and extended by non-mail applications.

MIME is a standard that allows binary data to be published and read on the Internet. The header of a file with binary data contains the MIME type of the data; this informs client programs (Web browsers and mail packages, for instance) that they will need to handle the data in a different way than they handle straight text. For example, the header of a Web document containing a JPEG graphic contains the MIME type specific to the JPEG file format. This allows a browser to display the file with its JPEG viewer, if one is present.


node <definition>

ADO Glossary

node

An element in a hierarchical tree structure. A node may be the root, or the child of another node. A node can also be the parent of multiple children. See also hierarchy, tree, root, child, parent.


object variable <definition>

ADO Glossary

object variable

A variable that contains a reference to an object. For example, objCustomObject is a variable that points to an object of type CustomObject:

Set objCustomObject = CreateObject(adodb.Recordset)


ODBC <definition>

ADO Glossary

ODBC (Open Database Connectivity)

A standard programming language interface used to connect to a variety of data sources. This is usually accessed through Control Panel, where data source names (DSNs) can be assigned to use specific ODBC drivers.


OLE DB <definition>

ADO Glossary

OLE DB

A set of interfaces that expose data from a variety of sources using 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. See also COM.


optimistic locking <definition>

ADO Glossary

optimistic locking

A type of locking in which the data page containing one or more records, including the record being edited, is unavailable to other users only while the record is being updated by the Update method, but is available before and after the call to Update.

Optimistic locking is used when the Recordset object is opened with the LockType parameter or property set to adLockOptimistic or adLockBatchOptimistic. See also pessimistic locking.


ordinal value <definition>

ADO Glossary

ordinal value

The numeric location of an item within an order. In an ADO collection, the ordinal value of the first item is zero (0). The next item is one (1), and so on.


parameterized command <definition>

ADO Glossary

parameterized command

A query or command that allows you to set parameter values before the command is executed. For example, a SQL string can be parameterized by embedding parameter markers in the SQL string (designated by the '?' character). The application then specifies values for each parameter and executes the command.


parent <definition>

ADO Glossary

parent

The controlling side of a hierarchical relationship. In a hierarchical structure, a parent has one or more child nodes directly beneath it in the hierarchy. See also parent-alias, parent-child relationship, child.


parent-alias <definition>

ADO Glossary

parent-alias

An alias that refers to the parent. See also alias, parent.


parent-child relationship <definition>

ADO Glossary

parent-child relationship

A relationship in a hierarchical structure in which the parent is one level higher and directly associated with one or more children. A child is one level lower and must have one parent. See also parent, child.


persist <definition>

ADO Glossary

persist

To save data in a permanent state, such as saving a Recordset to a file.


pessimistic locking <definition>

ADO Glossary

pessimistic locking

A type of locking in which the page containing one or more records, including the record being edited, is unavailable to other users to ensure that an update will be made. Pessimistic locking behavior is defined by the OLE DB provider. Typically, records are locked upon editing and remain unavailable until the Update method has completed.

Pessimistic locking is enabled when the Recordset object is opened with the LockType parameter or property set to adLockPessimistic. See also optimistic locking.


pooling <definition>

ADO Glossary

pooling

A performance optimization based on using collections of pre-allocated resources, such as objects or database connections. It is more efficient to draw an existing resource from the pool than to create a new resource.


Prog ID (programmatic identifier) <definition>

ADO Glossary

ProgID (programmatic identifier)

A unique name mapped to the Windows registry by a COM application. The ProgID for an ADO Connection is "ADODB.Connection". See also CLSID, COM.


proxy <definition>

ADO Glossary

proxy

An interface-specific object that provides the parameter marshaling and communication required for a client to call an application object that is running in a different execution environment, such as on a different thread or in another process. The proxy is located with the client and communicates with a corresponding stub that is located with the application object that is being called. See also stub.


relative URL <definition>

ADO Glossary

relative URL

A partially qualified URL that specifies a resource on the Internet or an intranet whose location is relative to a starting point specified by an absolute URL or equivalent ADO Connection object. In effect, the concatenated absolute and relative URLs consitute a complete URL. See also URL and absolute URL.


remote data source <definition>

ADO Glossary

remote data source

A data source that exists on a another computer, rather than on the local system (where the client application runs).


resource record <definition>

ADO Glossary

resource record

A record from a document source provider that countains fields for the definition and description of a folder or document. The document itself is not contained in the resource record but typically can be accessed by the default stream or a field in the resource record containing a URL. See also document source provider, default stream, URL.


root <definition>

ADO Glossary

root

The top level in a hierarchical tree structure. The root node has no parents, but may have children. See also hierarchy, tree, parent, child.


rowset <definition>

ADO Glossary

rowset

A set of rows from a data source, all having the same field schema. A rowset can represent all or some fields from a table. A rowset can also represent a virtual table, created by a query or a join of two or more tables. In ADO, rowsets are represented by Recordset objects.


schema <definition>

ADO Glossary

schema

A description of a database to the database management system (DBMS), typically generated using the data definition language provided by the DBMS. A schema defines attributes of the database, such as tables, columns, and properties.


scope <definition>

ADO Glossary

scope

The range of reference for an object or variable or a range of records in a view or table. For example, local variables can be referenced only within the procedure in which they were defined. Public variables are accessible from anywhere in the application. Objects, such as the current database, are in scope if they are in the defined search path. Record ranges can be specified with a Scope clause in many commands.


service provider <definition>

ADO Glossary

service provider

Sofware that encapsulates a service by producing and consuming data, augmenting features in your ADO applications. It is a provider that does not directly expose data, rather it provides a service, such as query processing. The service provider may process data provided by a data provider. See also data provider.


shaped Recordset <definition>

ADO Glossary

shaped Recordset

A Recordset whose columns have been specifically defined to contain not just data, but also references (called chapters) to other Recordset objects and/or computed values based on other Recordset objects.


sibling <definition>

ADO Glossary

sibling

Any two or more nodes in a hierarchical structure that are on the same level in the hierarchy. The root node in a hierarchy has no siblings.


stored procedure <definition>

ADO Glossary

stored procedure

A precompiled collection of code such as SQL statements and optional control-of-flow statements stored under a name and processed as a unit. Stored procedures are stored within a database; they can be executed with one call from an application and allow user-declared variables, conditional execution, and other powerful programming features.


stub <definition>

ADO Glossary

stub

An interface-specific object that provides the parameter marshaling and communication required for an application object to receive calls from a client that is running in a different execution environment, such as on a different thread or in another process. The stub is located with the application object and communicates with a corresponding proxy that is located with the client that calls it. See also proxy.


sub-node <definition>

ADO Glossary

sub-node

See child.


synchronous operation <definition>

ADO Glossary

synchronous operation

An operation initiated by code that completes before the next operation may start. See also asynchronous operation.


tree <definition>

ADO Glossary

tree

A structure representing a hierarchical relationship between elements (nodes). There is one node at the top level of a tree (the root). Underneath the root, there can be multiple children. Each child may in turn be the parent of other children, thus branching like a tree. A folder containing documents and other folders is a typical example of a tree structure. See also hierarchy, node, root, child, parent.


URL (Uniform Resource Locator) <definition>

ADO Glossary

URL (Uniform Resource Locator)

Specifies the location of a resource residing on the Internet or an intranet. A complete URL consists of a scheme (such as FTP, HTTP, mailto, file, and so on), followed by a colon, a server name, and the full path of a resource (such as a document, graphic, or other file). Some examples of URLs are:

http://www.domain.com/default.html
ftp://ftp.server.somewhere/ftp.file
file://Server/Share/File.doc

See also absolute URL and relative URL.


Web server <definition>

ADO Glossary

Web server

A computer that provides Web services and pages to intranet and Internet users.


About the Code Examples

The code examples in this guide are designed for illustration purposes only. Because they are written primarily to demonstrate ODBC 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-ODBC functions (those functions whose names do not start with "SQL") and most error handling.

All code examples use ANSI strings and the same database schema, which is shown at the start of Chapter 7: Catalog Functions.


Audience

The ODBC interface is designed for use with the C programming language. Use of the ODBC interface spans three areas: SQL statements, ODBC function calls, and C programming. This guide assumes:

  • A working knowledge of the C programming language.
  • General DBMS knowledge and a familiarity with SQL.


Document Conventions

This guide uses the following typographic conventions.

Format | Usage
SELECT | Uppercase letters indicate SQL statements, macro names, and terms used at the operating-system command level.
SQLRETURNCODE SQLFetch(hdbc) | Monospace font is used for sample command lines and program code.
fOption | Italicized words indicate programmatic arguments, information that the user or the application must provide, or word emphasis.
SQLEndTran | Bold type indicates that syntax must be typed exactly as shown, including function names.
| | A vertical bar separates two mutually exclusive choices in a syntax line.
... | An ellipsis indicates that arguments can be repeated several times.
. . . | A column of three dots indicates continuation of previous lines of code.


ODBC Drivers

SQL_ALTER_TABLE

SQL_AT_ADD_COLUMN |
SQL_AT_DROP_COLUMN


ODBC Drivers

SQL_BOOKMARK_PERSISTENCE

SQL_BP_SCROLL |
SQL_BP_UPDATE[1]

[1] Bookmarks persist after a commit but do not persist after a rollback.


ODBC Drivers

SQL_CATALOG_NAME_SEPARATOR

"\" (Excel 3.0/4.0)

"." (Excel 5.0/7.0/97)


ODBC Drivers

SQL_CATALOG_TERM

"Directory" (Excel 3.0/4.0)

"Workbook" (Excel 5.0/7.0/97)


ODBC Drivers

SQL_CATALOG_USAGE

SQL_QU_DML_STATEMENTS |
SQL_QU_TABLE_DEFINITION |
SQL_QU_INDEX_DEFINITION |
SQL_QU_PROCEDURE_INVOCATION


ODBC Drivers

SQL_CATALOG_USAGE

SQL_QU_DML_STATEMENTS |
SQL_QU_TABLE_DEFINITION |
SQL_QU_INDEX_DEFINITION


ODBC Drivers

SQL_CATALOG_USAGE

SQL_QU_DML_STATEMENTS |
SQL_QU_TABLE_DEFINITION


ODBC Drivers

SQL_CONVERT_BINARY

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_CONVERT_CHAR

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_CONVERT_DATE

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_CONVERT_DOUBLE

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_CONVERT_FLOAT

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_CONVERT_INTEGER

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_CONVERT_LONGVARBINARY

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_CONVERT_LONGVARCHAR

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_CONVERT_NUMERIC

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_CONVERT_REAL

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_CONVERT_SMALLINT

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_CONVERT_TIME

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_CONVERT_TIMESTAMP

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_CONVERT_TINYINT

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_CONVERT_VARBINARY

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_CONVERT_VARCHAR

SQL_CVT_DOUBLE |
SQL_CVT_FLOAT |
SQL_CVT_INTEGER |
SQL_CVT_NUMERIC |
SQL_CVT_REAL |
SQL_CVT_SMALLINT |
SQL_CVT_VARCHAR |
SQL_CVT_WVARCHAR


ODBC Drivers

SQL_DBMS_VER

ISAM | Version | Format of version numbers
Microsoft Access | 2.0 | 02.00.0000
 | 3.0 | 03.00.0000
 | 3.5 | 03.50.0000
 | 4.0 | 04.00.0000

Note Versions 1.0 and 1.1 are not supported. Also, there is no difference in the data format in Microsoft Access versions 3.0, 7.0, and 97.


ODBC Drivers

SQL_DBMS_VER

ISAM | Version | Format of version numbers
DBASE | 3.0 | 03.00.0000
 | 4.0 | 04.00.0000
 | 5.0 | 05.00.0000


ODBC Drivers

SQL_DBMS_VER

ISAM | Version | Format of version numbers
Microsoft Excel | 3.0 | 03.00.0000
 | 4.0 | 04.00.0000
 | 5.0/7.0 | 05.00.0000
 | 97/2000 | 08.00.0000


ODBC Drivers

SQL_DBMS_VER

ISAM | Version | Format of version numbers
Paradox | 3.x | 03.00.0000
 | 4.x | 04.00.0000
 | 5.x | 05.00.0000


ODBC Drivers

SQL_DBMS_VER

ISAM | Version | Format of version numbers
Text | 1.0 | 01.00.0000


ODBC Drivers

SQL_DDL_INDEX

SQL_DL_CREATE_INDEX

SQL_DL_DROP_INDEX


ODBC Drivers

SQL_FILE_USAGE

SQL_FILE_TABLE (Excel 3.0/4.0)

SQL_FILE_CATALOG (Excel 5.0/7.0)


ODBC Drivers

SQL_GETDATA_EXTENSIONS

SQL_GD_ANY_ORDER |
SQL_GD_ANY_COLUMN |
SQL_GD_BLOCK |
SQL_GD_BOUND


ODBC Drivers

SQL_KEYWORDS

ALPHANUMERIC

AUTOINCREMENT

BINARY

BOOLEAN

BYTE

COUNTER

CURRENCY

DATABASE

DATABASENAME

DATETIME

DISALLOW

DISTINCTROW

DOUBLEFLOAT

FLOAT4

FLOAT8

GENERAL

IEEEDOUBLE

IEEESINGLE

IGNORE

IMAGE

INTEGER1

INTEGER2

INTEGER4

LOGICAL

LOGICAL1

LONG

LONGBINARY

LONGCHAR

LONGTEXT

MEMO

MONEY

NOTE

NUMBER

OLEOBJECT

OWNERACCESS

PARAMETERS

PERCENT

PIVOT

SHORT

SINGLE

SINGLEFLOAT

STDEV

STDEVP

STRING

TABLEID

TEXT

TOP

TRANSFORM

UNSIGNEDBYTE

VAR

VARBINARY

VARP

YESNO


ODBC Drivers

SQL_MAX_CHAR_LITERAL_LEN

255 (Excel 3.0/4.0/5.0/7.0)

65535 (Excel 97)


ODBC Drivers

SQL_MAX_COLUMN_NAME_LEN

30 (Excel 3.0/4.0)

64 (Excel 5.0/7.0/97)


ODBC Drivers

SQL_MAX_TABLE_NAME_LEN

12 (Excel 3.0/4.0)

31 (Excel 5.0/7.0/97)


ODBC Drivers

SQL_NUMERIC_FUNCTIONS

SQL_FN_NUM_ABS |
SQL_FN_NUM_ATAN |
SQL_FN_NUM_CEILING |
SQL_FN_NUM_COS |
SQL_FN_NUM_EXP |
SQL_FN_NUM_FLOOR |
SQL_FN_NUM_LOG |
SQL_FN_NUM_MOD |
SQL_FN_NUM_POWER |
SQL_FN_NUM_RAND |
SQL_FN_NUM_SIGN |
SQL_FN_NUM_SIN |
SQL_FN_NUM_SQRT |
SQL_FN_NUM_TAN


ODBC Drivers

SQL_OJ_CAPABILITIES

SQL_OJ_LEFT
SQL_OJ_RIGHT
SQL_OJ_NOT_ORDERED
SQL_OJ_INNER
SQL_OJ_ALL_COMPARISON_OPS


ODBC Drivers

SQL_SCROLL_OPTIONS

SQL_SO_FORWARD_ONLY |
SQL_SO_STATIC |
SQL_SO_KEYSET_DRIVEN


ODBC Drivers

SQL_STRING_FUNCTIONS

SQL_FN_STR_ASCII |
SQL_FN_STR_CHAR |
SQL_FN_STR_CONCAT |
SQL_FN_STR_LCASE |
SQL_FN_STR_LEFT |
SQL_FN_STR_LENGTH |
SQL_FN_STR_LOCATE |
SQL_FN_STR_LOCATE_2
SQL_FN_STR_LTRIM |
SQL_FN_STR_RIGHT |
SQL_FN_STR_RTRIM |
SQL_FN_STR_SPACE |
SQL_FN_STR_SUBSTRING |
SQL_FN_STR_UCASE


ODBC Drivers

SQL_SUBQUERIES

SQL_SQ_COMPARISON |
SQL_SQ_EXISTS |
SQL_SQ_IN |
SQL_SQ_QUANTIFIED |
SQL_SQ_CORRELATED_SUBQUERIES


ODBC Drivers

SQL_TIMEDATE_FUNCTIONS

SQL_FN_TD_CURDATE |
SQL_FN_TD_CURTIME |
SQL_FN_TD_DAYOFMONTH |
SQL_FN_TD_DAYOFWEEK |
SQL_FN_TD_DAYOFYEAR |
SQL_FN_TD_HOUR |
SQL_FN_TD_MINUTE |
SQL_FN_TD_MONTH |
SQL_FN_TD_NOW |
SQL_FN_TD_SECOND |
SQL_FN_TD_WEEK |
SQL_FN_TD_YEAR


ODBC Drivers

SQL_TIMEDATE_FUNCTIONS

SQL_FN_TD_DAYOFMONTH |
SQL_FN_TD_DAYOFWEEK |
SQL_FN_TD_DAYOFYEAR |
SQL_FN_TD_HOUR |
SQL_FN_TD_MINUTE |
SQL_FN_TD_MONTH |
SQL_FN_TD_SECOND |
SQL_FN_TD_WEEK |
SQL_FN_TD_YEAR


ODBC Drivers

SQL_UNION

SQL_U_UNION_ALL |
SQL_U_UNION


OLE DB Provider for ODBC

ODBC Provider 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 list, click the data type you want to convert from to view the supported conversions:

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. Some conversions will fail, due to incompatibility, and some will succeed, depending on the data.


Recommended Reading

For more information about SQL, the following standards are available:

  • Database Language—SQL with Integrity Enhancement, ANSI, 1989 ANSI X3.135-1989.
  • Database Language—SQL: ANSI X3H2 and ISO/IEC JTC1/SC21/WG3 9075:1992 (SQL-92).
  • X/Open CAE Specification, Structured Query Language (SQL), C201 (X/Open Company Ltd., U.K., 1992).

In addition to standards and vendor-specific SQL guides, many books describe SQL, including:

  • Date, C. J., with Darwen, Hugh: A Guide to the SQL Standard (Addison-Wesley, 1993).
  • Emerson, Sandra L., Darnovsky, Marcy, and Bowman, Judith S.: The Practical SQL Handbook (Addison-Wesley, 1989).
  • Groff, James R. and Weinberg, Paul N.: Using SQL (Osborne McGraw-Hill, 1990).
  • Gruber, Martin: Understanding SQL (Sybex, 1990).
  • Hursch, Jack L. and Carolyn J.: SQL, The Structured Query Language (TAB Books, 1988).
  • Melton, Jim, and Simon, Alan R.: Understanding the New SQL: A Complete Guide (Morgan Kaufmann Publishers, 1993).
  • Pascal, Fabian: SQL and Relational Basics (M & T Books, 1990).
  • Trimble, J. Harvey, Jr. and Chappell, David: A Visual Introduction to SQL (Wiley, 1989).
  • Van der Lans, Rick F.: Introduction to SQL (Addison-Wesley, 1988).
  • Vang, Soren: SQL and Relational Databases (Microtrend Books, 1990).
  • Viescas, John: Quick Reference Guide to SQL (Microsoft Corp., 1989).

For additional information about transaction processing, see:

  • Gray, J. N. and Reuter, Andreas: Transaction Processing: Concepts and Techniques (Morgan Kaufmann Publishers, 1993).
  • Hackathorn, Richard D.: Enterprise Database Connectivity (Wiley & Sons, 1993).

For more information about Call-Level Interfaces, the following standards are available:

  • X/Open CAE Specification, Data Management: SQL Call Level Interface (CLI), C451 (X/Open Company Ltd., U.K., 1995).
  • ISO/IEC 9075-3:1995, Call-Level Interface (SQL/CLI).

For additional information about ODBC, a number of books are available, including:

  • Geiger, Kyle: Inside ODBC (Microsoft Press®, 1995).
  • Gryphon, Robert, Charpentier, Luc, Oelschlager, Jon, Shoemaker, Andrew, Cross, Jim, and Lilley, Albert W.: Using ODBC 2 (Que, 1994).
  • Johnston, Tom and Osborne, Mark: ODBC Developers Guide (Howard W. Sams & Company, 1994).
  • North, Ken: Windows Multi-DBMS Programming: Using C++, Visual Basic, ODBC, OLE 2 and Tools for DBMS Projects (John Wiley & Sons, Inc., 1995).
  • Stegman, Michael O., Signore, Robert, and Creamer, John: The ODBC Solution, Open Database Connectivity in Distributed Environments (McGraw-Hill, 1995).
  • Welch, Keith: Using ODBC 2 (Que, 1994).
  • Whiting, Bill: Teach Yourself ODBC in Twenty-One Days (Howard W. Sams & Company, 1994).


ODBC Drivers

block cursor

A forward-scrolling, read-only result set returned to the client, who is responsible for maintaining storage for the data.


ODBC Drivers

database

In Visual FoxPro, a database file has a .dbc extension and can contain one or more tables.


ODBC Drivers

database table

In Visual FoxPro, a table that is associated with a database. Contrast free table.


ODBC Drivers

free table

In Visual FoxPro, a table that is not associated with a database.

A .dbf file created in FoxPro version 2.x is a free table unless it is converted to a Visual FoxPro table and added to a Visual FoxPro database. Contrast database table.


ODBC Drivers

locale

The set of information that corresponds to a given language and country. A locale indicates specific settings such as decimal separators, date and time formats, and character-sorting order.


ODBC Drivers

preparable SQL statement

A SQL statement that has not already been processed by SQLPrepare.


ODBC Drivers

read-only concurrency

The cursor cannot be updated.


ODBC Drivers

rowset

Blocks of data stored in a cursor, representing rows retrieved from a data source.


ODBC Drivers

row versioning

Essentially timestamp support, in which row versions are compared at update time.


ODBC Drivers

sort order

Sort orders incorporate the sorting rules of different locales, allowing you to sort data in those languages correctly. In Visual FoxPro, the current sort order determines the results of character expression comparisons and the order in which the records appear in indexed or sorted tables.


ODBC Drivers

static cursor

A snapshot of a data set defined by the query. Static cursors do not reflect real-time changes of the underlying data by other users. The cursor's memory buffer is maintained by the ODBC cursor library, which allows forward and backward scrolling.


ODBC Drivers

table

In Visual FoxPro, records are stored in a table. Each row of a table represents a record, and the columns of the table represent the fields of the record. Each Visual FoxPro table is stored in its own file with a .dbf extension. Visual FoxPro tables can be associated with a database.

FoxPro version 2.x tables are not associated with a database.